# TopBar — Figma alignment fix spec (X.4.2 batch)

## Inputs (✓ all read)

- figma cache: `figma-data/normalized/figma-mcp-cache/topbar.tsx` (171 lines, 2 tag variants × inner Menu sub-component variant axes)
- canonical: `src/components/TopBar/TopBar.vue` (66 lines, ~40 CSS lines — currently a thin shell)
- figma styles: `figma-styles.json` (Roboto/16 pop title + Roboto/18 headline 2)
- aliases: `token-aliases.ts`, `prop-aliases.md`, `icon-aliases.ts`
- variables: `variables.css`

Variant axes (from figma `TopBarProps`):

- `tag` axis: `"After Login" | "Before Login"`
- `showMenu` boolean (identity)
- `showSearchBox` boolean (identity)
- Sub-component `Menu` has axes `property1: "active" | "Normal"` + `icon: "No"` (single-value `icon` axis → skip per prop-aliases.md "Figma axis 处理规则 1")

Slots (figma slot properties → canonical):

- `menu` (figma) → `<slot name="menu">` (canonical) — currently canonical exposes `<slot name="center">` instead
- `rightContent` (figma) → `<slot name="right-content">` (canonical) — currently canonical exposes `<slot name="right">` instead

## Mismatches by dimension

### Spacing mismatches

- Figma root `gap-[80px]`; canonical `.topbar { gap: 80px }` — 80px is **off-scale** (no `--sp-*` token equals 80). → **Fix:** propose new domain token `--topbar-section-gap: 80px` (logo↔menu and menu↔right grouping anchor); or accept off-scale and add inline comment. **Recommend:** new domain token (matches figma intent precisely).
- Figma root padding `px-[var(--spacing/m,16px)] py-[var(--spacing/xs,8px)]` = `var(--sp-m)` horizontal, `var(--sp-xs)` vertical. Canonical `.topbar { padding: 0 var(--sp-m) }` — **MISMATCH:** missing vertical padding entirely (figma is 8px top + 8px bottom). → **Fix:** `padding: var(--sp-xs) var(--sp-m)`.
- Figma root `w-[1920px]` for both tag variants — canonical sets `width: 100%` (responsive). figma 1920 is the design canvas width; canonical responsive width is correct (decision: keep `width: 100%`). **Log only** — `1920px` figma = canvas, not a real width constraint.
- Figma `tag=After Login` root — has no fixed height; figma cache implies height = content + `py-[var(--sp-xs)]`. Canonical `.topbar--after-login { height: 56px }`. The 56px is **off-scale** (no token; `--sp-xxxl` = 56px exists). → **Fix:** `height: var(--sp-xxxl)` (semantic match).
- Figma `tag=Before Login` root explicitly `h-[64px]`; canonical `.topbar--before-login { height: 64px }` raw. 64px is off-scale (no scale token). → **Fix:** propose new domain token `--topbar-height-before-login: 64px` OR accept off-scale literal.
- Figma "Logo & Service Name" frame `gap-[var(--spacing/m,16px)]` = `var(--sp-m)`; canonical `.topbar-brand { gap: var(--sp-m) }` ✓.
- Figma `tag=After Login` middle frame (search + menu container) `gap-[var(--spacing/xxl,40px)]` = `var(--sp-xxl)`; canonical `.topbar-menu { gap: var(--sp-xxl) }` ✓.
- Figma `Menu` inner items `gap-[var(--spacing/m,16px)]` = `var(--sp-m)`; canonical menu container has no inner-items gap (uses single slot, delegates to consumer). **Coverage gap:** if canonical exposes a default rendering of menu items, it must use `gap: var(--sp-m)` between items. **Currently canonical is a thin shell** — defer to consumer responsibility, but document.
- Figma `Menu` item internal `px-[var(--spacing/xs,8px)]` (8px horizontal). Canonical does not render menu items internally (slot only). Document for consumer.
- Figma `tag=After Login` "Right_content" inner `Time Zone` frame `flex-col gap-[8px] pt-[3px] px-[8px]` = `var(--sp-xs)` gap, `var(--sp-xs)` horizontal. `pt-[3px]` is off-scale (3px). Canonical `.topbar-right { gap: var(--sp-xs) }` ✓ for right-side gap, but no internal Time Zone styling exists (slot delegate). Document for consumer.
- Figma `tag=After Login` Right_content vertical divider `h-[24px] w-px` inside `h-[40px] px-[4px] py-[10px]` frame — 4px = `--sp-xxs`, 10px off-scale, 40px = `--sp-xxl` height. Canonical doesn't render — slot delegate. Document for consumer.
- Figma `tag=After Login` search box `gap-[8px] h-[32px] px-[12px] py-[8px] rounded-[4px] w-[480px]` = `var(--sp-xs)` / 32 (off-scale `--sp-xl=32`) / `var(--sp-s)` / `var(--sp-xs)` / `var(--r-s)` / 480 off-scale. canonical doesn't render — should expose `<Input>` or similar via slot. Document.
- Figma `tag=Before Login` Right_content `gap-[var(--spacing/l,24px)]` = `var(--sp-l)`. Canonical right gap is `var(--sp-xs)` — **MISMATCH** for before-login. → **Fix:** apply theme-conditional gap: `.topbar--before-login .topbar-right { gap: var(--sp-l); }`.

### Color mismatches

- Figma root bg `var(--color-type/background/top-bar,black)` = `--bg-topbar`; canonical `.topbar { background: var(--bg-topbar) }` ✓.
- Figma root border `border` (1px solid) `var(--color-type/line/deep-divider,#353535)` = `--line-deep`. Canonical `.topbar { border-bottom: 1px solid var(--line-deep) }` — **MISMATCH:** figma is full border (all 4 sides), canonical only `border-bottom`. → **Fix:** `border: 1px solid var(--line-deep)`. (In practice top-bar full border may be undesirable for stickier headers; flag as design review since figma 1920 mockup may differ from production. Recommend matching figma 真源.)
- Figma "Product Name" text color `var(--color-type/text/heading-&-button,white)` = `--text-heading`; canonical `.topbar-title { color: var(--text-heading) }` ✓.
- Figma `Menu/active` text color `var(--color-type/text/text_1,#f8f8f8)` = `--text-body`; figma `Menu/Normal` text color `var(--color-type/text/tips-&-button,#9e9e9e)` — **MISSING TOKEN ALIAS:** "Color Type/Text/Tips & Button" not in `token-aliases.ts`. The closest alias is `--text-tips` (`#9e9e9e`). → **Fix:** add alias `'Color Type/Text/Tips & Button': '--text-tips'` (token color value matches), OR add new alias `--text-tips-button`. Recommend reusing `--text-tips` (same color value).
- Figma search box border `var(--ux/grey/grey-8-#595959,#595959)` → `--color-grey-8` → maps to `--line-border` in dark. Canonical doesn't render search; defer to slot consumer with `<Input variant="line">` (which uses `--line-border`).
- Figma search placeholder `var(--ux/grey/grey-7-#7b7b7b,#7b7b7b)` → `--color-grey-7` → maps to `--text-placeholder`. Defer to consumer.
- Figma Time Zone label text `var(--color-type/text/placeholder-&-button,#7b7b7b)` = `--text-placeholder`. Defer to consumer.
- Figma Time Zone clock text `var(--ux/orange/time-zone,#d39f39)` = `--orange-tz`. Defer to consumer.
- Figma Account Name "T" letter color `var(--color-type/text/heading-&-button,white)` = `--text-heading`. Defer.
- Figma `Before Login` "Try it now" button bg `var(--ux/brand/brand,#2fb54e)` = `--brand`; text `var(--color-type/text/heading-&-button,white)` = `--text-heading`. Defer to slot consumer (figma data-name `Elements/Button/Big` is a BRIDGE-Tier3 button).
- Figma vertical divider in Right_content `var(--color-type/line/light-divider,#434343)` = `--line-light`. Defer to consumer.

### Typography mismatches (use `--text-style-*` composite tokens or atomic)

- Figma "Product Name" `Roboto/16 pop title` (size 16, line-height 24, weight 600) = `--text-style-pop-title`. Canonical `.topbar-title { font-size: 16px; font-weight: 600 }` hardcoded — also missing `line-height: 24px`. → **Fix:** `font: var(--text-style-pop-title)`.
- Canonical `.topbar-title { letter-spacing: -0.01em }` is **invented** — figma 真源 has `letter-spacing: 0`. → **Fix:** drop `letter-spacing: -0.01em`.
- Figma `Menu` text `Roboto/18 headline 2` (size 18, line-height 27, weight 600) = `--text-style-headline-2`. Canonical doesn't render menu items — defer to consumer with documented requirement to use `--text-style-headline-2`.
- Figma search placeholder `Roboto/14 body` = `--text-style-body`. Defer.
- Figma Time Zone label `text-[12px]` weight 400 line-height `normal` ≈ `Roboto/12 tips` = `--text-style-tips` (lh 16 vs `normal` ≈ 16 — match). Defer to consumer (Time Zone is a sub-component candidate).
- Figma Time Zone clock `font-['Digital_Numbers:Regular']` `text-[16px]` — Digital Numbers is **not** in the design system font tokens. → **New domain token needed:** `--font-family-digital-numbers: 'Digital Numbers', monospace`. Defer use to consumer; flag as new font dependency.

### Effect mismatches (use `--shadow-l*` / `--mask-overlay`)

- Figma TopBar 真源 has **no shadow / overlay**. Canonical also has no shadow ✓. **No mismatch.**

### Composition mismatches (canonical import vs figma data-name)

- Figma `data-name="icon/logo/TVU"` → `<Logo>` per icon-aliases.ts 表 B. Canonical exposes `<slot name="logo">` (consumer provides `<Logo>`). **Slot semantic match; canonical does NOT auto-import `<Logo>`** — this is by-design (top bar is a layout shell). Document expected consumer pattern.
- Figma `data-name="input box/line"` (search box) → `<Input variant="line">`. Canonical does not render — slot delegate. Document `showSearchBox=true` expected slot content uses `<Input variant="line" placeholder="Search">` with `<Icon name="search">` prefix.
- Figma `data-name="icon/Search/Search"` → `<Icon name="search">`. Defer.
- Figma `data-name="icon/menu/3"` → `<Icon name="menu">` per icon-aliases.ts 表 B. Defer to consumer.
- Figma sub-component `Menu` (item-level, with `property1: active|Normal`) → no canonical equivalent. **GAP:** Canonical exposes `<slot name="center">` (or `<slot name="menu">` per prop-aliases.md). Consumer must render menu items themselves. **Future:** consider extracting `<TopBarMenuItem>` sub-component once usage stabilises (not blocking).
- Figma `data-name="Time Zone"` (frame name) → not in composition aliases (per prop-aliases.md 表 D it's a layout container, not a composition target). **No canonical mapping required**, but a future TVU `<TimeZoneClock>` component could plug into the right slot.
- Figma `data-name="Account Name"` (frame name) → 表 D layout container. No mapping.
- Figma `data-name="Elements/Button/Big"` ("Try it now") → `<Button>` (BRIDGE-Tier3 deferred). Defer to consumer.
- **CRITICAL slot name mismatch:** prop-aliases.md "Slot Property Aliases" 表 declares `TopBar` slots:
  - `<slot name="menu">` ↔ figma `menu` slot property
  - `<slot name="right-content">` ↔ figma `rightContent` slot property
  - Canonical currently exposes `<slot name="center">` and `<slot name="right">` — **alias-mismatched**. → **Fix:** rename canonical slots to `menu` and `right-content` to align with prop-aliases.md ground-truth. (Add `left` slot? — figma has no `left` slot property; canonical's `<slot name="left">` is invented, recommend dropping or adding to prop-aliases.md if needed.)

### Variant coverage gaps (per axis value)

- `tag=After Login`: ✓ canonical class `.topbar--after-login` with height. **GAP:** canonical does not differentiate which slots are visible per tag. figma `After Login` shows search/menu/right-content; figma `Before Login` hides search and menu (only logo + before-login-menu + Try-it-now button). Canonical relies on consumer to conditionally render slot content. → **Decision:** acceptable (TopBar is a shell); document expected consumer pattern.
- `tag=Before Login`: ✓ canonical class. Height 64px ✓.
- `showMenu=true|false`: figma toggles `Menu` frame visibility. Canonical does not have a `showMenu` prop — slot is unconditionally rendered (empty if no slot content). **Coverage gap:** canonical lacks `showMenu` and `showSearchBox` boolean props (declared in prop-aliases.md "Boolean Property Aliases" as `exact match`). → **Fix:** add `showMenu?: boolean` (default `true`) and `showSearchBox?: boolean` (default `false`) props; gate slot rendering with `v-if`.
- Sub-component `Menu/property1`: canonical defers to consumer (no internal rendering). Document.

## Token usage summary

### Existing canonical tokens used

- Spacing: `--sp-xs`, `--sp-m`, `--sp-l`, `--sp-xxl`, `--sp-xxxl`
- Color: `--bg-topbar`, `--line-deep`, `--text-heading`, `--text-tips` (proposed alias addition)
- Typography: `--text-style-pop-title`, `--text-style-headline-2`

### New domain tokens proposed

| Token | Value | Rationale |
| --- | --- | --- |
| `--topbar-section-gap` | `80px` | figma root `gap-[80px]` between major sections (logo / menu / right). Off scale; topbar-specific. |
| `--topbar-height-before-login` | `64px` | figma `Before Login` 真源 fixed height 64px. Off scale (no `--sp-*` = 64). |
| `--font-family-digital-numbers` | `'Digital Numbers', monospace` | figma Time Zone clock uses `Digital_Numbers` font; not in design system. Optional — only if Time Zone is moved into TopBar later. |

> Could optionally re-use `--sp-xxxl` (= 56px) for `tag=After Login` height — recommend doing so (semantic match) instead of new token.

### Proposed alias addition (token-aliases.ts)

- `'Color Type/Text/Tips & Button': '--text-tips'` — figma uses this token name for inactive Menu items, currently unmapped. Same color value as `--text-tips` (`#9e9e9e`).

### BRIDGE-Tier3 deferred items

- "Try it now" button (`Elements/Button/Big`) — defer to consumer slot using `<Button>` once Tier3 ready.
- Menu items (per `Menu` sub-component) — defer or extract `<TopBarMenuItem>` later.

## Refactor scope

### Template changes

- Add `showMenu` and `showSearchBox` boolean props (declared in prop-aliases.md as exact-match). Default per figma: `showMenu=true`, `showSearchBox=false`.
- Add slot `<slot name="search">` gated by `v-if="showSearchBox"`.
- Rename `<slot name="center">` → `<slot name="menu">`, gated by `v-if="showMenu"`.
- Rename `<slot name="right">` → `<slot name="right-content">`.
- Drop or document `<slot name="left">` (no figma counterpart).

### CSS changes

- Add vertical padding `var(--sp-xs)` to `.topbar` rule.
- Switch `border-bottom` to full `border` (or document deviation if production calls for header-only border).
- Switch `.topbar--after-login { height: 56px }` → `height: var(--sp-xxxl)`.
- Switch `.topbar--before-login { height: 64px }` → `height: var(--topbar-height-before-login)` OR raw 64 (decision pending).
- Switch root `gap: 80px` → `gap: var(--topbar-section-gap)`.
- Switch `.topbar-title` to `font: var(--text-style-pop-title)`; drop hardcoded `font-size`/`font-weight` and invented `letter-spacing`.
- Add `.topbar--before-login .topbar-right { gap: var(--sp-l); }` to override the default `--sp-xs` for before-login.

### Estimated diff

- Template: ~6 lines added (props + 2 slot renames + showMenu/showSearchBox v-if), ~2 lines removed.
- CSS: ~10 lines added (token swaps + before-login gap + new height), ~4 lines removed (hardcoded values + letter-spacing).
- Tokens (variables.css): ~3 lines added (2 new domain tokens + comment).
- Aliases (token-aliases.ts): 2 lines added (forward + reverse for Tips & Button).
- Total: **~21 lines added, ~6 lines removed**.

## Blockers / open decisions

- Slot rename (`center`→`menu`, `right`→`right-content`) is a **breaking API change** for any current consumer using the old names. Plan owner must decide:
  1. Hard-rename + bump major
  2. Keep old names as deprecated aliases (Vue allows multiple slots with same content via fallthrough) — actually Vue does NOT support slot aliasing; would require explicit re-slot pattern.
- Full border vs `border-bottom`: figma 真源 = full border, but production typical = bottom only. **Needs design review.**
- `--topbar-height-before-login` (64px) — could just use raw 64; flag as off-scale.
- Sub-component `<TopBarMenuItem>` extraction is out of scope for this batch.
- Time Zone clock font `Digital_Numbers` introduces a new font family dependency — defer until Time Zone is brought into TopBar internals.
- Canonical `<slot name="left">` is invented (no figma counterpart). Decide: drop, or formalize via prop-aliases.md addition.
