# BreadcrumbItem fix spec — Phase X.4.2 batch C

Inputs:

- Figma cache: `figma-data/normalized/figma-mcp-cache/breadcrumbitem.tsx`
- Canonical: `src/components/Breadcrumb/BreadcrumbItem.vue` (note: dir `Breadcrumb`, not `BreadcrumbItem`)
- Token aliases: `src/design-system/translation/token-aliases.ts`
- Prop aliases: `src/design-system/translation/prop-aliases.md`
- Icon aliases: `src/design-system/translation/icon-aliases.ts`
- Variables: `src/tokens/variables.css`

Figma component summary (cache):

- Variant axis: `state: 'hover' | 'current' | 'disabled' | 'Default'`
- Boolean property: `showIcon: boolean` (default `true`) — registered in `prop-aliases.md` Boolean Property Aliases as exact match
- Wrapper: `flex gap-[var(--spacing/xxs,4px)] items-center px-[var(--spacing/xs,8px)] py-[var(--spacing/xxs,4px)]`
- Icon ref: `data-name="icon/Arrow/right"` size `16px` (per `icon-aliases.ts` 表 B → `<Icon name="arrow-right">`)
- Text style: `Roboto Regular 14 / 1.5` → `--text-style-body`

Per `icon-aliases.ts` 表 B: `icon/Arrow/right` → `<Icon name="arrow-right">`.
Per `prop-aliases.md`: BreadcrumbItem `showIcon` ↔ figma `showIcon` exact match; default `<slot>` ↔ figma `children`.

---

## Mismatches

### a. Spacing

| Aspect | Figma 真源 | Canonical 现状 | Fix |
| --- | --- | --- | --- |
| Wrapper gap (icon ↔ label) | `--sp-xxs` (4px) — `gap-[var(--spacing/xxs,4px)]` | `gap: 4px` literal | replace with `gap: var(--sp-xxs)` |
| Wrapper padding-x | `--sp-xs` (8px) — `px-[var(--spacing/xs,8px)]` | n/a — wrapper `<li>` has no padding; padding is on `.breadcrumb-item__label` (`padding: 4px 8px`) | semantic mismatch — figma has padding on the **whole item** wrapper (icon + label both inside), canonical puts padding on label-only. **Fix**: move padding to wrapper `.breadcrumb-item { padding: var(--sp-xxs) var(--sp-xs); }` and remove from `.breadcrumb-item__label`. Or keep label padding for hover affordance: alternative — extract `--breadcrumb-item-padding-x: var(--sp-xs); --breadcrumb-item-padding-y: var(--sp-xxs)` and apply to wrapper. |
| Wrapper padding-y | `--sp-xxs` (4px) — `py-[var(--spacing/xxs,4px)]` | n/a (see above) | as above |
| Icon size | `16px` | `width: 16px; height: 16px` on `.breadcrumb-item__separator` | OK (figma stroke icon is `16px`) |

### b. Color

| State | Element | Figma 真源 token | Canonical 现状 | Fix |
| --- | --- | --- | --- | --- |
| `Default` | label | `Color Type/Text/Text_2` → `--text-2` | `color: var(--text-2)` | OK |
| `Default` | icon | inferred neutral icon color (likely `--icon-default` or `--text-2` — cache uses raster `<img>`, no explicit color token captured) | `color: var(--text-2)` | acceptable; but figma uses neutral icon — recommend `var(--icon-default)` (semantic separator color). Trade-off: `--icon-default` is `#9e9e9e` dark, slightly different from `--text-2` `#cccccc`. **Need cross-ref via `get_design_context` on the SVG fill.** Default to `--text-2` (current canonical) and flag for verification. |
| `hover` | label | `Color Type/Text/Text_1` → `--text-body` | `color: var(--text-heading)` (= `#ffffff` dark / `#000000` light) | mismatch — canonical uses `--text-heading` (Heading & Button) but figma uses `--text-body` (Text_1) — `#f8f8f8` dark vs `#ffffff` dark. Fix: `.breadcrumb-item--hover .breadcrumb-item__label { color: var(--text-body); }` and `.breadcrumb-item--Default .breadcrumb-item__label:hover { color: var(--text-body); }` |
| `hover` | icon | inferred `--text-body` (matches text color shift) | `color: var(--text-heading)` | same fix — repoint to `var(--text-body)` |
| `current` | label | `UX/Brand/Brand` → `--brand` | `color: var(--brand)` | OK |
| `current` | icon | `--text-2` (icon stays neutral while label is brand) | `color: var(--text-2)` | OK |
| `disabled` | label | `Color Type/Text/Disable` → `--text-disabled` | `color: var(--text-disabled)` | OK |
| `disabled` | icon | inferred `--text-disabled` | `color: var(--text-disabled)` | OK |

### c. Typography

| Element | Figma 真源 | Canonical | Fix |
| --- | --- | --- | --- |
| Label | `Roboto/14 body` → `--text-style-body` | `font-family: var(--font-family-base); font-size: 14px; font-weight: 400; line-height: 1` | mismatch on `line-height` — figma is `1.5` (per cache `leading-[1.5]`), canonical is `1`. Fix: replace whole block with `font: var(--text-style-body);`. **Caveat**: `--text-style-body` resolves to `400 14px/21px var(--font-family-base)` — `21px / 14px = 1.5` ✅ matches figma. Use the shorthand. |

### d. Effects

No drop shadow / mask. N/A.

### e. Composition

| Aspect | Figma 真源 | Canonical | Fix |
| --- | --- | --- | --- |
| Icon component | `data-name="icon/Arrow/right"` → `<Icon name="arrow-right">` per icon-aliases 表 B | `<Icon name="next" />` | **mismatch** with `icon-aliases.ts`. The alias table B has both `icon/Arrow/Next` → `next` and `icon/Arrow/right` → `arrow-right`. Figma cache uses `right`, canonical uses `next`. Fix: rename to `<Icon name="arrow-right">` to match figma 真源 alias entry. (Or register `next` as an additional alias for `icon/Arrow/right` in `icon-aliases.ts` — but cleaner to use the dedicated `arrow-right` name since figma has *both* `Next` (general arrow) and `right` (breadcrumb-specific) as separate icon variants.) |
| Wrapper element | `<div>` (figma) | `<li>` (canonical, semantic ordering inside `<ol>`) | acceptable — `<li>` is correct semantic upgrade for breadcrumb list. Document. |
| Label element | `<p>` (figma) | `<a>` or `<span>` (canonical, conditionally interactive) | acceptable — runtime semantic upgrade. Document. |
| Slot fallback | `children` defaults to `Label` text | `<slot />` (no fallback text) | minor gap: canonical doesn't render `Label` placeholder when slot empty. Acceptable for production (slot must always have content); document. |

### f. Variant coverage

| Figma variant | Canonical handled? | Notes |
| --- | --- | --- |
| `state=Default` | ✅ via `resolvedState='Default'` | OK |
| `state=hover` | ✅ via `resolvedState='hover'` (CSS class) | needs color token repoint |
| `state=current` | ✅ via `resolvedState='current'` (or `props.current`) | OK |
| `state=disabled` | ✅ via `resolvedState='disabled'` (or `props.disabled`) | OK |
| `showIcon=false` | ✅ via `:show-separator="false"` prop (canonical alias `showSeparator` ↔ figma `showIcon`) | **prop name diverges** — canonical uses `showSeparator`, figma uses `showIcon`. Per `prop-aliases.md` Boolean Property Aliases table, this is registered as `BreadcrumbItem.showIcon ↔ showIcon (exact match)`. The current canonical prop `showSeparator` is **not aligned** with the alias table. Fix options: (a) rename canonical to `showIcon` to match alias-table exact-match registration; (b) update alias-table to register `showSeparator ↔ showIcon` as approved alias with rationale (`canonical名称更精确：表达的是"分隔符箭头"而非"任意 icon"`). **Recommend (b)** — `showSeparator` is more semantic for breadcrumb context. Update `prop-aliases.md` row. |

Runtime additions (Vue ecosystem):

- `href?: string` — runtime addition, controls `<a>` vs `<span>` rendering
- `current?: boolean` — convenience boolean alternative to `state="current"`
- `disabled?: boolean` — convenience boolean alternative to `state="disabled"`

These are not in figma but are reasonable Vue-ecosystem additions. Document in `prop-aliases.md` Vue-ecosystem table:

| Component | Code Prop | Figma | Status | Notes |
| --- | --- | --- | --- | --- |
| `BreadcrumbItem` | `href` | `(no Figma counterpart)` | Vue ecosystem | Web link target |
| `BreadcrumbItem` | `current` | `(figma uses state=current)` | runtime convenience | Boolean alias for `state='current'` |
| `BreadcrumbItem` | `disabled` | `(figma uses state=disabled)` | runtime convenience | Boolean alias for `state='disabled'` |

---

## New domain tokens proposed

| Token | Value | Rationale |
| --- | --- | --- |
| `--breadcrumb-item-padding-x` | `var(--sp-xs)` | Alias for clarity (optional — direct `--sp-xs` works) |
| `--breadcrumb-item-padding-y` | `var(--sp-xxs)` | Alias for clarity (optional) |

Both are sugar — direct primitive token usage is fine. Not strictly required.

---

## Token repoint summary

| Current | Proposed | Location |
| --- | --- | --- |
| `gap: 4px` (`.breadcrumb-item`) | `gap: var(--sp-xxs)` | wrapper |
| n/a (wrapper has no padding) | `padding: var(--sp-xxs) var(--sp-xs)` | wrapper (move from `.breadcrumb-item__label`) |
| `padding: 4px 8px` (`.breadcrumb-item__label`) | remove (move to wrapper) | label |
| `font-family + font-size + font-weight + line-height` block | `font: var(--text-style-body)` | label |
| `line-height: 1` | (covered by shorthand above — figma 真源 is `1.5`) | label |
| `color: var(--text-heading)` (hover label, hover separator) | `color: var(--text-body)` | hover state (2 selectors) |
| `<Icon name="next" />` | `<Icon name="arrow-right" />` | template (1 instance) |
| `border-radius: 4px` for separator | n/a (no border-radius) | — |

---

## Open items / blockers

- **Verify figma `icon/Arrow/right` SVG fill color**: cache uses raster `<img>` masks (`imgLine98Stroke`, `imgLine98Stroke1`, `imgLine98Stroke2`) for default/hover/disabled — three different assets imply distinct fills. Confirm via `get_design_context` on the actual icon node, then map fills to `--text-2 / --text-body / --text-disabled` (currently inferred). Default state icon color may be `--icon-default` instead of `--text-2`.
- Decide canonical icon name: `next` (current canonical) vs `arrow-right` (figma 真源 + alias table). Recommend renaming to `arrow-right` for consistency with `icon-aliases.ts` 表 B; ensure SVG asset is registered under `arrow-right` key in icon registry.
- Decide `showSeparator` vs `showIcon` prop name: keep `showSeparator` and update `prop-aliases.md` Boolean Property Aliases row to register the rename, OR rename to `showIcon` for exact-match alignment.
