| name | create-db-component |
| description | Creates a new DB UX Design System component with Mitosis source, SCSS, typed model, and Playwright tests. |
| triggers | ["create a new component","add component","generate component","new db component"] |
| inputs | [{"name":"component_slug","type":"string","required":true,"description":"Component directory name in kebab-case (e.g. 'navigation-item', 'tooltip')"},{"name":"component_name","type":"string","required":false,"description":"Optional PascalCase symbol name (e.g. 'NavigationItem'). If omitted, derive it from component_slug."},{"name":"figma_file_key","type":"string","required":true,"description":"Figma file key (e.g. from https://www.figma.com/file/<fileKey>/...)."},{"name":"figma_node_id","type":"string","required":true,"description":"Figma node ID of the target component/frame (e.g. 1234-5678)."},{"name":"figma_url","type":"string","required":false,"description":"Optional full Figma URL. Use only as fallback when file key and node ID are not provided separately."}] |
| requires | [{"context":"context/architecture.md","autoLoad":true}] |
| tools | ["db-ux/list_components","db-ux/get_component_props","db-ux/get_component_details","db-ux/get_example_code","db-ux/get_design_tokens","db-ux/list_design_token_categories","db-ux/list_icons","db-ux/docs_search","figma/get_figma_data","figma/download_figma_images"] |
| outputs | ["packages/components/src/components/{component_slug}/"] |
| on_error | {"max_retries":3,"actions":[{"log":"Review the shell output (lint/test/build) and fix reported errors before retrying."},{"fallback":"If errors persist after 3 retries, report to user with full error output."}]} |
Skill: Create Deutsche Bahn (DB) Component
Variable Convention
Throughout this skill:
{component_slug} = kebab-case directory/file name (e.g. navigation-item)
{component_name} = PascalCase symbol name derived from {component_slug} (e.g. navigation-item -> NavigationItem)
DB{component_name} = full component class name (e.g. DBNavigationItem)
.db-{component_slug} = CSS class (e.g. .db-navigation-item)
Pre-Conditions
context/architecture.md IS in context.
- MCP (
@db-ux/mcp-server) IS connected.
- Figma MCP IS connected.
component_slug IS provided by user. Derive component_name from component_slug unless explicitly provided.
figma_file_key and figma_node_id ARE provided by user.
- If either is missing, use
figma_url only as a fallback source to extract them. If still unresolved: ABORT.
- Call
list_components to verify the component does NOT already exist.
Execution
Step 0: Design Spec Extraction and Token Resolution
Phase 0.1: Figma Specs (Figma MCP)
- Use
figma_file_key and figma_node_id provided by the user as the primary source of truth.
- If one of them is missing, extract the missing value from
figma_url as fallback.
- URL format:
https://www.figma.com/file/<fileKey>/...?node-id=<nodeId>
- Call
get_figma_data with fileKey and nodeId to retrieve the component frame and its design data.
- Extract from the Figma response:
- Spacing: padding, gap, margin values from Auto Layout properties.
- Sizing: width, height constraints.
- Colors: fill colors, stroke colors, text colors (as Figma variable references or hex values).
- Typography: font family, size, weight, line-height.
- Border radius: corner radius values.
- Variants: all variant properties defined in the Figma component set.
- Variable references and styles are included in the
get_figma_data response — extract them from the returned data structure.
- Document ALL extracted values. These are the ground truth for implementation.
Phase 0.2: DB UX Token Mapping (@db-ux/mcp-server node package)
- Call
list_components. ABORT if component already exists.
- Call
list_design_token_categories to discover available categories. Then call get_design_tokens with the categories identified in Phase 0.1 (spacing, sizing, colors, radius).
- Map EVERY Figma value to its corresponding SCSS variable (
variables.$db-*) or CSS custom property (var(--db-*)) as fallback:
- Figma padding 8px: find matching
--db-spacing-fixed-* token.
- Figma color #EC0016 (background): find matching
--db-*-bg-* token.
- Figma color #000000 (text): find matching
--db-*-on-bg-* token.
- Figma color #ccc (border): find matching
--db-*-border-* token.
- Figma height 48px: find matching
--db-sizing-* token.
- Figma border-radius 4px: find matching
--db-border-radius-* token.
- If a Figma value has NO matching token: FLAG it. Do NOT hardcode. Ask user for guidance.
- Call
list_icons if the component uses icons in Figma.
- Call
get_component_details on a structurally similar existing component (same interaction family: form-control, container, navigation or feedback). If no similar component exists, state that explicitly and skip.
- Store ALL mapped token results. Reference them in SCSS implementation.
Step 1: RED - Write Failing Tests
Create packages/components/src/components/{component_slug}/{component_slug}.spec.tsx:
import AxeBuilder from '@axe-core/playwright';
import { expect, test } from '@playwright/experimental-ct-react';
import { DB{component_name} } from './index';
import { DEFAULT_VIEWPORT } from '../../shared/constants.ts';
const comp: any = <DB{component_name}>Content</DB{component_name}>;
const testComponent = () => {
test('should contain text', async ({ mount }) => {
const component = await mount(comp);
await expect(component).toContainText('Content');
});
test('should match screenshot', async ({ mount }) => {
const component = await mount(comp);
await expect(component).toHaveScreenshot();
});
// AI-REPLACE: Inline the real variant list from Figma Phase 0.1 here.
// AI-NOTE: Do NOT import from model.ts — it does not exist yet in RED phase.
for (const variant of ['variant1', 'variant2' /* AI-REPLACE: use real Figma variants */]) {
const variantComp: any = <DB{component_name} variant={variant}>Content</DB{component_name}>;
test(`should contain text for variant ${variant}`, async ({ mount }) => {
const component = await mount(variantComp);
await expect(component).toContainText('Content');
});
test(`should match screenshot for variant ${variant}`, async ({ mount }) => {
const component = await mount(variantComp);
await expect(component).toHaveScreenshot();
});
}
};
const testA11y = () => {
test('should have same aria-snapshot', async ({ mount }, testInfo) => {
const component = await mount(comp);
const snapshot = await component.ariaSnapshot();
expect(snapshot).toMatchSnapshot(`${testInfo.testId}.yaml`);
});
test('should not have any A11y issues', async ({ page, mount }) => {
await mount(comp);
const accessibilityScanResults = await new AxeBuilder({ page })
.include('.db-{component_slug}')
.analyze();
expect(accessibilityScanResults.violations).toEqual([]);
});
};
test.describe('DB{component_name}', () => {
test.use({ viewport: DEFAULT_VIEWPORT });
testComponent();
testA11y();
});
After writing the spec, build the project, generate outputs, and run the component tests from output/react:
pnpm run build && pnpm run build-outputs &&
cd output/react && pnpm run test:components
The RED phase is only complete if:
- The command exits non-zero.
- The failing test names are captured in the output.
- The failure is caused by missing or incomplete implementation (e.g. missing module, missing export), NOT by syntax errors in the spec itself.
If the spec has syntax errors, fix them first and re-run until you get clean "missing implementation" failures.
Step 2: GREEN - Implement
2a: Create model.ts
import { GlobalProps, GlobalState } from '../../shared/model';
export const {component_name}VariantList = [] as const;
export type {component_name}VariantType = (typeof {component_name}VariantList)[number];
export type DB{component_name}DefaultProps = {
variant?: {component_name}VariantType;
};
export type DB{component_name}Props = DB{component_name}DefaultProps & GlobalProps;
export type DB{component_name}DefaultState = {};
export type DB{component_name}State = DB{component_name}DefaultState & GlobalState;
Rules:
- EVERY prop gets a JSDoc comment.
- Variant list MUST match Figma Phase 0.1 variants.
- Use composition:
DefaultProps & GlobalProps.
2b: Create {component_slug}.lite.tsx
import { useDefaultProps, useMetadata, useRef, useStore } from '@builder.io/mitosis';
import { cls } from '../../utils';
import type { DB{component_name}Props, DB{component_name}State } from './model';
useMetadata({});
useDefaultProps<DB{component_name}Props>({});
export default function DB{component_name}(props: DB{component_name}Props) {
const _ref = useRef<HTMLElement | any>(null);
const state = useStore<DB{component_name}State>({});
return (
<div
ref={_ref}
id={props.id ?? props.propOverrides?.id}
class={cls('db-{component_slug}', props.className)}
data-variant={props.variant}
>
{props.children}
</div>
);
}
Rules:
- NEVER use inline styles in
.lite.tsx components.
- ID binding MUST be
id={props.id ?? props.propOverrides?.id}.
2c: Create {component_slug}.scss
@use "@db-ux/core-foundations/build/styles/variables";
.db-{component_slug} {
padding: variables.$db-spacing-fixed-sm;
block-size: variables.$db-sizing-md;
&[data-variant="..."] {
}
}
Rules: Line 1 = @use. Root = .db-{component_slug}. NO hardcoded values. Keep nesting shallow — prefer extracting shared/internal styles over deeply nested selectors.
2d: Create index.ts
export { default as DB{component_name} } from './{component_slug}';
Do NOT re-export from ./model. Do NOT use .lite suffix.
2f: Create Documentation, Examples and Showcase
- docs/: Create
Angular.md, HTML.md, React.md, Vue.md, Migration.md.
- examples/: Create
default.example.lite.tsx, variant.example.lite.tsx, _{component_slug}.arg.types.ts.
- showcase/{component_slug}.showcase.lite.tsx:
import { PatternhubProps } from '../../../shared/model';
import CardWrapperShowcase from '../../../shared/showcase/card-wrapper.showcase.lite';
import ContainerWrapperShowcase from '../../../shared/showcase/container-wrapper.showcase.lite';
import LinkWrapperShowcase from '../../../shared/showcase/link-wrapper.showcase.lite';
import VariantExample from '../examples/variant.example.lite';
export default function {component_name}Showcase(props: PatternhubProps) {
return (
<ContainerWrapperShowcase title="DB{component_name}" isPatternhub={props.isPatternhub}>
<LinkWrapperShowcase exampleName="Variant">
<CardWrapperShowcase>
<VariantExample />
</CardWrapperShowcase>
</LinkWrapperShowcase>
</ContainerWrapperShowcase>
);
}
Step 3: QUALITY CHECK and REFACTOR
- Run
pnpm run test. ALL MUST PASS.
- Run
pnpm run build. MUST SUCCEED.
- Run
pnpm run build-showcases. MUST SUCCEED.
- Run
pnpm run build-storybooks. MUST SUCCEED.
- Cross-check SCSS against Phase 0.2 mapping.
- Verify docs/, examples/, showcase/ are complete.
- Run tests again after refactor.
Step 4: Agent Examples
Create agent/{component_slug}.agent.lite.tsx with usage examples.
Step 5: Governance and Framework Outputs
-
Build framework outputs:
pnpm run build-outputs
This MUST succeed.
-
Create changeset:
bash pnpm changeset
Select @db-ux/core-components (only if the changes also affect styling: SCSS/CSS) and all JavaScript framework output packages (@db-ux/ngx-core-components, @db-ux/react-core-components, @db-ux/wc-core-components, @db-ux/v-core-components). Bump: minor.
As a changeset message, describe why we made a change and what changes to the developers. In most cases, we don't need to describe what we have changed internally, as the users are most curious about what changes for them.
Output Checklist
Red Flags
| Thought | Response |
|---|
| "Figma link is missing" | STOP. ABORT. Demand URL. |
| "I'll write tests later" | STOP. Step 1 is FIRST. |
| "I don't need model.ts" | STOP. Every component gets typed props. |
| "A quick hardcoded color" | STOP. Use SCSS variables (variables.$db-*). |
| "I'll skip MCP query" | STOP. Step 0 is mandatory. |
| "Icon is probably 'close'" | STOP. Call list_icons. |
| "Edit React output directly" | STOP. .lite.tsx ONLY. |
| "I'll use px" | STOP. Use tokens. |
| "Export from .lite" | STOP. No .lite suffix. |
| "Skip changeset" | STOP. Governance requires it. |
| "build-outputs is optional" | STOP. It is mandatory. |