# StepItem — Fix Spec (Phase X.4.2 batch D)

- Canonical: `src/components/Steps/StepItem.vue`
- Figma cache: `figma-data/normalized/figma-mcp-cache/stepitem.tsx`
- Figma node ids — Vertical/Number: `4943:7255` (pending) · `4943:7261` (active) · `4943:7267` (completed). Horizontal/Number: `4943:7230` (pending) · `4943:7237` (active) · `4943:7244` (completed). Horizontal/Icon: `4943:7277` (pending) · `4943:7288` (active) · `4943:7299` (completed).
- Figma axes per cache: `direction: horizontal|vertical` × `state: pending|active|completed` × `style: number|icon`. Vertical only supports number (matches canonical `resolvedStepStyle` which forces `number` when vertical). No `theme` axis.
- Composition expectations per `prop-aliases.md` 表 B: `icon/Edit/Selected` (Line 75 Stroke) → `<Icon name="selected">`; `icon/Feature/Recently used` → `<Icon name="recently-used">`; `icon/Feature/Demo` → `<Icon name="demo">`; `icon/Feature/AI frame interpolation` → `<Icon name="ai-frame-interpolation">` (or current canonical aliases).

## Mismatch matrix

### a. Spacing

| Element | Figma 真源 | Canonical 现状 | Token to use |
| --- | --- | --- | --- |
| Horizontal item gap (column gap inside `step-item--horizontal`) | `6px` raw in cache (no scale token; close to nothing in figma scale) | `gap: 6px` raw | keep `6px` literal — figma uses non-scale value; flag for designer confirmation. Alternative `var(--sp-xxs)` (4px) is closest scale value but a numeric drift. |
| Horizontal item width | `120px` raw | `width: 120px` raw | keep `120px` literal (preview width; no canonical token) |
| Vertical item gap | `12px` raw → matches `--spacing/s` | `gap: 12px` raw | `gap: var(--sp-s)` |
| Vertical item width | `140px` raw | `width: 140px` raw | keep `140px` literal (no canonical token) |
| Icon-style horizontal track gap | `--spacing/xxs` (4px) | `gap: 4px` raw | `gap: var(--sp-xxs)` |
| Marker number circle size | `28px` raw (circle), `--radius/14px` | `width: 28px; height: 28px; border-radius: 14px` raw | keep `28px` + `border-radius: 50%` (or literal `14px`); no canonical `--size-*` or matching `--r-*` token (`--r-xxl` is 100px in canonical; figma cache uses raw `rounded-[14px]`). Recommend `border-radius: 50%`. |
| Marker icon-style size | `32px` raw | `width: 32px; height: 32px` raw | keep `32px` literal |
| Connector horizontal width | `40px` raw | `width: 40px` raw | keep `40px` literal (no canonical token) |
| Connector vertical height | `40px` raw | `height: 40px` raw | keep `40px` literal |
| Connector thickness | `2px` | `height/width: 2px` raw | keep `2px` literal |

### b. Color

| Element / state | Figma 真源 | Canonical 现状 | Token to use |
| --- | --- | --- | --- |
| Pending marker border | `Color Type/Icon/Placeholder` (#7b7b7b) | `border: 1.5px solid var(--icon-placeholder)` | already correct |
| Pending marker number text | `Color Type/Text/Placeholder & Button` (#7b7b7b) | `color: var(--text-tips)` (#9e9e9e) — drift | `color: var(--text-placeholder)` |
| Active marker border | `Color Type/Text/Heading & Button` (white) | `border: 2px solid var(--text-heading)` | already correct |
| Active marker number text | `Color Type/Text/Heading & Button` (white) | `color: var(--text-heading)` | already correct |
| Completed marker bg | `UX/Brand/Brand` (#2fb54e) | `background: var(--brand)` | already correct |
| Completed marker check icon | (img asset stroke; figma resolves at icon level) | `<Icon name="selected">` (canonical default color) | rely on `<Icon>` default; if needed `color: var(--text-primary-btn)` (white on brand bg) |
| Pending title text | `Color Type/Text/Placeholder & Button` (#7b7b7b) | `color: var(--text-tips)` (#9e9e9e) — drift | `color: var(--text-placeholder)` |
| Active title text | `Color Type/Text/Heading & Button` (white) | `color: var(--text-heading)` | already correct |
| Completed title text | `UX/Brand/Brand` | `color: var(--brand)` | already correct |
| Connector pending/active | `Color Type/Icon/Placeholder` (#7b7b7b) | `background: var(--icon-placeholder)` | already correct |
| Connector completed | `UX/Brand/Brand` | `background: var(--brand)` | already correct |
| Icon-style marker color (active state) | `Color Type/Text/Heading & Button` (white via `--icon-active` would be #f8f8f8 — close but not identical) | `color: var(--text-heading)` | keep `var(--text-heading)`; figma `Heading & Button` token resolves to canonical `--text-heading` (white) per `token-aliases.ts` |
| Icon-style marker color (pending) | `Color Type/Icon/Placeholder` | `color: var(--icon-placeholder)` | already correct |
| Icon-style marker color (completed) | `UX/Brand/Brand` | `color: var(--brand)` | already correct |
| Description text (canonical-only) | n/a | `color: var(--text-tips)` | runtime addition (figma has no `description` field) — document in `prop-aliases.md` runtime additions. Color choice is reasonable. |

### c. Typography

| Element | Figma 真源 | Canonical 现状 | Token to use |
| --- | --- | --- | --- |
| Marker number digit | `Roboto/16` SemiBold (cache uses `Roboto:SemiBold` 16) — matches `Roboto/16 pop title` (600 / 16 / lh 24) but cache renders inline `leading-[1.5]` = `1.5 * 16 = 24` — same as `--text-style-pop-title` | `font-size: 16px; font-weight: 600; line-height: 1.5` raw | `font: var(--text-style-pop-title)` |
| Title — pending | `Roboto/14 body` (400 / 14 / 21) | `font-size: 14px; font-weight: 400; line-height: 1.5` raw | `font: var(--text-style-body)` |
| Title — active | `Roboto:SemiBold` 14 + `leading-[normal]` (cache override — line-height not 1.5) | `font-size: 14px; font-weight: 600; line-height: 1.5` raw | partial-override: `font: var(--text-style-body)` + `font-weight: 600`; the `leading-[normal]` cache value is figma authoring drift (unscaled) — keep canonical `1.5` line-height (matches `--text-style-body`). Flag the cache `normal` line-height as figma-side inconsistency. |
| Title — completed | `Roboto/14 body` (400) | `font-size: 14px; font-weight: 400; line-height: 1.5` | `font: var(--text-style-body)` |
| Description (canonical-only) | n/a | `font-size: 12px; line-height: 1.4` raw | `font: var(--text-style-tips)` (12 / 16 — line-height becomes `16/12 = 1.33` not the current 1.4); document as runtime addition. |

### d. Effects

None — no shadows / masks anywhere in StepItem variants.

### e. Composition

| figma `data-name` | canonical | status |
| --- | --- | --- |
| `icon/Edit/Selected` (completed marker check) | `<Icon name="selected">` | implemented (`Icon name="selected"` already used) — verify alias entry in `icon-aliases.ts`. Currently `icon-aliases.ts` does not list `selected`; canonical relies on Icon component's own internal map. Add an `icon/Edit/Selected` → `selected` row to `icon-aliases.ts` if Icon names need explicit alias coverage. |
| `icon/Feature/Recently used` (horizontal/icon completed) | `<Icon name="recently-used">` | canonical uses `iconNameByState.completed = 'action/selected'` — **drift**. Should be `'recently-used'` (figma 真源 for completed in icon style). |
| `icon/Feature/Demo` (horizontal/icon active) | `<Icon name="demo">` | canonical uses `'action/loading'` — **drift**. Should be `'demo'`. |
| `icon/Feature/AI frame interpolation` (horizontal/icon pending) | `<Icon name="ai-frame-interpolation">` | canonical uses `'action/search'` — **drift**. Should be `'ai-frame-interpolation'` or accept user-supplied `iconName` (canonical already accepts `iconName` prop, but the default fallback table is wrong). |
| `Frame` (connector segments) | (canonical inline div) | ok — pure layout, no canonical mapping needed |

Required composition fixes:

1. Update `iconNameByState` defaults in `StepItem.vue` to: `completed: 'recently-used'`, `active: 'demo'`, `pending: 'ai-frame-interpolation'` (or whatever canonical Icon registry exposes those svgs as).
2. Add explicit alias rows to `icon-aliases.ts`: `'feature/recently-used'`, `'feature/demo'`, `'feature/ai-frame-interpolation'`, `'edit/selected'` mapping to figma `icon/Feature/Recently used`, `icon/Feature/Demo`, `icon/Feature/AI frame interpolation`, `icon/Edit/Selected`.
3. Vertical layout: cache renders the trailing connector (40px vertical line) **after** the marker, inside the same column, before the next item. Canonical does the same via `step-vertical__column` + `step-line--vertical`. ok.
4. Horizontal layout: cache renders `[leading connector] [marker] [trailing connector]` for `style=number`, but for `style=icon` the trailing `2px h-bar` connector follows the icon marker AND is **inside** the same flex track (no leading connector inside the same item — leading connector belongs to the previous item's trailing). Canonical currently always renders both `showLeadingConnector` and `showTrailingConnector` → fine as long as `<Steps>` parent suppresses leading on first item / trailing on last. Verify in `Steps.vue` (out of scope here but flag).

### f. Variant coverage

| Variant | Figma | Canonical | Status |
| --- | --- | --- | --- |
| `direction=horizontal × style=number × state=pending|active|completed` | yes | yes | ok |
| `direction=horizontal × style=icon × state=pending|active|completed` | yes | yes | ok (with icon name fix in e.1) |
| `direction=vertical × style=number × state=pending|active|completed` | yes | yes | ok |
| `direction=vertical × style=icon` | not in cache (figma forces number for vertical) | canonical also forces `number` when vertical (script line 50–52) | ok — canonical aligned |

`title` default `'Step Title'` matches cache placeholder. `description` is canonical-only (runtime addition).

## New tokens proposed

None. All figma values map to existing canonical tokens (text styles, color tokens, spacing). Several literals (28px / 32px / 40px marker / connector dimensions and 6px horizontal item gap) are kept as raw px because figma uses non-scale literals for them.

## Blockers / open questions

1. **`leading-[normal]` on active title in figma cache** — figma authoring uses unspecified line-height for active titles only (other states use 1.5). Spec recommends keeping canonical `1.5` (= `--text-style-body`) for visual stability and flagging cache value as a figma-side authoring drift.
2. **`6px` horizontal item gap** — not a `--sp-*` scale value (closest is `--sp-xxs` = 4px or `--sp-s` = 12px). Keep literal; flag for designer to either accept scale value or formally adopt 6px.
3. **Default icon names for `style=icon`** — canonical's current defaults (`action/selected` / `action/loading` / `action/search`) contradict figma. Fix per e.1 + add aliases per e.2. Verify that the canonical Icon registry has glyphs registered under `recently-used`, `demo`, `ai-frame-interpolation`; if not, add them or change the default to whatever the registry exposes.
4. **`description` is canonical-only** — figma cache has no description field. Document in `prop-aliases.md` runtime-additions section (currently only `error`, `readonly` are recorded there).
5. **Pending text color drift** — canonical uses `--text-tips` (#9e9e9e) but figma 真源 is `Color Type/Text/Placeholder & Button` (#7b7b7b) which maps to `--text-placeholder`. Same drift on title-pending and marker-number-pending. Apply per b.
6. Marker `border-radius: 14px` (= half of 28px) — use `border-radius: 50%` for clarity rather than literal 14px; behaves identically.

## Files (canonical fix scope)

- `src/components/Steps/StepItem.vue` (script: fix `iconNameByState` defaults; style: switch hard-coded numbers/typography to canonical tokens; fix pending color drift)
- `src/design-system/translation/icon-aliases.ts` (add `feature/recently-used`, `feature/demo`, `feature/ai-frame-interpolation`, `edit/selected` rows)
- `src/design-system/translation/prop-aliases.md` (add `description`, `iconName`, `index`, `showLeadingConnector`, `showTrailingConnector`, `stepStyle`, `style` runtime-additions / aliases as applicable)
