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

## Inputs (✓ all read)

- figma cache: `figma-data/normalized/figma-mcp-cache/promptmessage.tsx` (236 lines, 4 status × 2 size = 8 variant branches)
- canonical: `src/components/PromptMessage/PromptMessage.vue` (159 lines, ~70 CSS lines)
- figma styles: `figma-styles.json` (Roboto/14 body)
- aliases: `token-aliases.ts`, `prop-aliases.md` (incl. `closable ↔ showCloseIcon` known alias), `icon-aliases.ts`
- variables: `variables.css` (incl. `--prompt-success-bg`, `--prompt-{status}-text` already defined)

Variant axes (figma `PromptMessageProps`):

- `status`: `"success" | "info" | "error" | "warning"` (4 values)
- `size`: `"M" | "L"` (2 values, capitalized)
- `showCloseIcon: boolean` ↔ canonical `closable` (approved alias per prop-aliases.md "Known Entries")

## Mismatches by dimension

### Spacing mismatches

- Figma root `gap-[8px]` = `var(--sp-xs)`; canonical `.prompt-message { gap: 8px }` raw → **Fix:** `gap: var(--sp-xs)`.
- Figma root `px-[16px] py-[12px]` = `var(--sp-m)` / `var(--sp-s)`; canonical `.prompt-message { padding: 12px 16px }` raw → **Fix:** `padding: var(--sp-s) var(--sp-m)`.
- Figma root `rounded-[4px]` = `var(--r-s)`; canonical `.prompt-message { border-radius: 4px }` raw → **Fix:** `border-radius: var(--r-s)`.
- Figma `size=L` `max-width: 480px`; canonical `.prompt-message--L { width: fit-content }` — **MISMATCH:** figma 真源 cap is `max-width: 480px` (content can grow up to 480 then wrap); canonical uses `fit-content` (shrink-to-content, no cap). → **Fix:** `.prompt-message--L { max-width: var(--prompt-message-l-max-width) }` (new domain token, value 480px). Drop `width: fit-content` (default block-flex behaviour will size to content + max-width cap).
- Figma `size=M` outer `max-width: 320px`; canonical `.prompt-message--M { width: 320px }` — **MISMATCH:** figma is `max-width` (responsive cap), canonical is fixed `width`. → **Fix:** `max-width: var(--prompt-message-m-max-width)` (new domain token, 320px). Drop fixed width.
- Figma `size=M` `Content` wrapper `w-[240px]` (inner copy width 240px = 320 - 16×2 padding - 16 icon - 8 gap - 16 close - 8 gap = 240, computed). Canonical no inner wrapper width — flex sizing handles via `flex: 1 1 auto`. ✓ acceptable (computed = same width).
- Figma `Content` wrapper `gap-[var(--spacing/m,0px)] gap-y-[16px]` — note inline fallback `0px` (figma 真源 `--spacing/m` = 16 but it's a single-line text in most cases so gap doesn't apply). `gap-y-[16px]` = `var(--sp-m)` for line-wrap. Canonical `.prompt-message__copy` has no gap (single `<p>`). ✓ acceptable.
- Figma `size=M` icon wrapper `py-[2px]` = 2px vertical (off-scale). This is a 2px offset to baseline-align icon to first text line. Canonical `.prompt-message__icon { margin-top: 2px }` ✓ off-scale literal, same purpose. Acceptable.
- Figma `size=M` close icon wrapper `py-[2px]` = 2px (same). Canonical `.prompt-message__close { width: 16px; height: 16px }` — has no top-padding/margin offset. **Minor mismatch:** for size=M, close icon should have +2px top padding to align with first text line. → **Fix:** add `.prompt-message--M .prompt-message__close { padding-top: 2px }` OR `align-self: flex-start` + small offset. Off-scale value; document.
- Figma close icon `size-[16px]`; canonical `.prompt-message__close { width: 16px; height: 16px }` ✓.

### Color mismatches

- Figma root bg per status:
  - `info`: `var(--ux/blue/msg-bg,#e1eefd)` = `--blue-bg`. Canonical uses `var(--blue-bg)` via `paletteMap.info.background` ✓.
  - `error`: `var(--ux/red/msg-bg,#fff1f0)` = `--red-bg`. Canonical uses `var(--red-bg)` ✓.
  - `warning`: `var(--ux/orange/msg-bg,#fff3db)` = `--orange-bg`. Canonical uses `var(--orange-bg)` ✓.
  - `success`: `bg-[#f1fbf3]` raw hex. **No canonical alias for `#f1fbf3`** as a generic "green-bg" — but canonical has defined `--prompt-success-bg: #f1fbf3` already in `variables.css` ✓. Canonical uses `var(--prompt-success-bg)` ✓.
- Figma copy text color per status:
  - `info`: `var(--ux/blue/default,#3892f3)` = `--blue`. Canonical uses `var(--prompt-info-text)` (= `#3892f3`). **Value-correct but alias chain is custom (`--prompt-info-text` is a fixed semantic token defined in variables.css equal to `#3892f3`).** Canonical alias is theme-stable (light theme would normally flip `--blue` to `#0473d5` but PromptMessage is theme-stable per design). → **No fix needed.** Document the deliberate use of theme-stable `--prompt-{status}-text`.
  - `error`: `var(--ux/red/default,#ea4233)` = `--red`. Canonical `--prompt-error-text` = `#ea4233` ✓ (theme-stable).
  - `warning`: `var(--ux/orange/default,#f68512)` = `--orange`. Canonical `--prompt-warning-text` = `#f68512` ✓.
  - `success`: `var(--ux/brand/brand,#2fb54e)` = `--brand`. Canonical `--prompt-success-text` = `#2fb54e` ✓.
- Figma icon color = same as text color (figma renders icons as colored SVG). Canonical `.prompt-message__icon { color: var(--prompt-text) }` ✓ (where `--prompt-text` is per-status palette text token).
- Figma close icon color = via SVG (likely grey for visibility on the tinted bg). Canonical `.prompt-message__close { color: var(--icon-default) }`. **Verify:** figma close icon is rendered via `imgUnion` SVG asset with stroke baked in; visually appears grey. Canonical `--icon-default` = `#9e9e9e` (dark) / `#595959` (light). Close icon visible on tinted bg ✓ acceptable.

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

- Figma copy text `Roboto/14 body` (size 14, line-height 1.5 = 21, weight 400) = `--text-style-body`. Canonical `.prompt-message__copy { font-size: 14px; line-height: 21px }` hardcoded → **Fix:** `font: var(--text-style-body)` (drop hardcoded `font-size` + `line-height`).
- Canonical `.prompt-message__copy` does not specify `font-weight` → inherits 400 ✓.

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

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

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

- Figma status icons:
  - `data-name="icon/Message/Success 2"` → `<Icon name="status-success">` per icon-aliases.ts (`'status/success'` / `'status-success'` → figma `icon/Message/Success 2`); also alias `'message/success-2'` exists. Canonical uses `iconMap.success = 'message/success-2'` ✓.
  - `data-name="icon/Message/warning 2"` → canonical `iconMap.warning = 'message/warning-2'` ✓.
  - `data-name="icon/Message/Error 2"` → canonical `iconMap.error = 'message/error-2'` ✓.
  - `data-name="icon/Message/Info 2"` → canonical `iconMap.info = 'message/info-2'` ✓.
- Figma close icon `data-name="icon/Edit/Close"` → `<Icon name="close">` per icon-aliases.ts. Canonical `<Icon name="close">` ✓.
- Figma `data-name="Content"` (frame) → layout container, no canonical mapping (per prop-aliases.md 表 D).
- No BRIDGE-Tier3 deferred items (close button is a simple icon, not a Button).

### Variant coverage gaps (per axis value)

8 variants (4 status × 2 size):

| status | size | Figma branch | Canonical handler | OK? |
|---|---|---|---|---|
| success | L | `isSuccessAndL` | `.prompt-message--success` + `--L` | ✓ |
| success | M | `isSuccessAndM` | same with `--M` | ✓ |
| info | L | `isInfoAndL` | `--info --L` | ✓ |
| info | M | `isInfoAndM` | `--info --M` | ✓ |
| warning | L | `isWarningAndL` | `--warning --L` | ✓ |
| warning | M | `isWarningAndM` | `--warning --M` | ✓ |
| error | L | `isErrorAndL` | `--error --L` | ✓ |
| error | M | `isErrorAndM` | `--error --M` | ✓ |

All 8 covered via `paletteMap[status]` + `--${size}` modifier.

- `showCloseIcon=true|false` ↔ canonical `closable` (approved alias) ✓ via `v-if="closable"`.
- `size=M` figma uses `items-start` (top-align) due to potentially-wrapping text; figma `size=L` uses `items-center` (single line). Canonical `.prompt-message { align-items: center }` default + `.prompt-message--M { align-items: flex-start }` ✓.

**Sub-axis differences:**

- Figma `size=M` shows `items-start` AND text wraps to 2 lines (240px width); canonical handles via `align-items: flex-start` ✓.
- Figma `size=L` shows `items-center` AND text is single-line (`flex flex-wrap` with `nowrap` effective via `whitespace`); canonical adds `.prompt-message--L .prompt-message__copy { white-space: nowrap }` ✓.

## Token usage summary

### Existing canonical tokens used

- Spacing: `--sp-xs`, `--sp-s`, `--sp-m`
- Color: `--blue-bg`, `--red-bg`, `--orange-bg`, `--prompt-success-bg`, `--prompt-{status}-text` (4×), `--icon-default`
- Radius: `--r-s`
- Typography: `--text-style-body`

### New domain tokens proposed

| Token | Value | Rationale |
| --- | --- | --- |
| `--prompt-message-l-max-width` | `480px` | figma `size=L` `max-width: 480px`. Off-scale; component-specific. |
| `--prompt-message-m-max-width` | `320px` | figma `size=M` `max-width: 320px`. Off-scale; component-specific. |

> Both should be added under existing "Derived aliases for component compatibility" section in `variables.css` (alongside existing `--prompt-success-bg` etc).

### BRIDGE-Tier3 deferred items

- None for PromptMessage.

## Refactor scope

### Template changes

- No structural template changes — variant rendering is already correct.
- (Optional) Add `align-self: flex-start` + `padding-top: 2px` for `.prompt-message--M .prompt-message__close` if py-[2px] icon-baseline alignment matters.

### CSS changes

- Replace raw spacing/radius values with token references (per dimensions above).
- Replace hardcoded `font-size: 14px; line-height: 21px` with `font: var(--text-style-body)`.
- Switch `.prompt-message--L { width: fit-content }` → `max-width: var(--prompt-message-l-max-width)` (drop `width: fit-content`).
- Switch `.prompt-message--M { width: 320px }` → `max-width: var(--prompt-message-m-max-width)`.
- (Optional) Add `.prompt-message--M .prompt-message__close { padding-top: 2px }` for icon baseline alignment.

### Estimated diff

- Template: 0 lines changed.
- CSS: ~8 lines changed (token swaps + max-width replacements), ~2 lines added (optional close icon offset for M).
- Tokens (variables.css): ~3 lines added (2 new domain tokens + comment).
- Total: **~10 lines added/changed, ~2 lines removed (hardcoded `font-size`/`line-height`/`width`)**.

## Blockers / open decisions

- New domain tokens (`--prompt-message-l-max-width`, `--prompt-message-m-max-width`) require plan-owner approval before adding to `variables.css`. Both are off-scale literals tracking figma component-specific widths.
- `.prompt-message--L { width: fit-content }` → `max-width` change might affect existing consumers depending on layout context (e.g., flex parent). Verify with sample/playground page after change.
- Off-scale 2px close-icon top-padding for `size=M` is a polish item; defer if not visually impactful.
- `--prompt-{status}-text` tokens are intentionally theme-stable (defined as fixed hex in variables.css, not theme-aware). This is **deliberate** per design: PromptMessage maintains the same vivid colors regardless of site theme. Document in spec; do not "improve" by aliasing to `--blue` / `--red` etc.
