From 0513d7b08108da7b008f5a89fcce29b8d9ac52a1 Mon Sep 17 00:00:00 2001 From: "gc.zhu" Date: Fri, 29 May 2026 19:08:28 +0800 Subject: [PATCH] [NO-JIRA] Add bpk-token-migrate skill for Figma-driven component token migration MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds a reusable Claude Code skill that migrates Backpack component styles to the new VDL token pipeline. Uses Figma as source of truth — reads design variables via get_variable_defs, converts paths to CSS custom property names via the kebabBpkName rule, and rewrites the component SCSS mixin directly. Includes naming rule references and eval test cases. Co-Authored-By: Claude Sonnet 4.6 --- .claude/skills/bpk-token-migrate/SKILL.md | 125 ++++++++++++++++++ .../skills/bpk-token-migrate/evals/evals.json | 19 +++ .../references/naming-rules.md | 72 ++++++++++ 3 files changed, 216 insertions(+) create mode 100644 .claude/skills/bpk-token-migrate/SKILL.md create mode 100644 .claude/skills/bpk-token-migrate/evals/evals.json create mode 100644 .claude/skills/bpk-token-migrate/references/naming-rules.md diff --git a/.claude/skills/bpk-token-migrate/SKILL.md b/.claude/skills/bpk-token-migrate/SKILL.md new file mode 100644 index 0000000000..b6689c7f91 --- /dev/null +++ b/.claude/skills/bpk-token-migrate/SKILL.md @@ -0,0 +1,125 @@ +--- +name: bpk-token-migrate +description: Re-implement a Backpack component's styles using Figma as the source of truth. Figma defines what each variant/state looks like and which design variables are used — the SCSS must match that exactly. Use this skill whenever the user mentions migrating a Backpack component to new VDL tokens, or types "/bpk-token-migrate ". This is NOT a mechanical variable rename — it is a Figma-driven style rewrite. +--- + +# bpk-token-migrate + +Re-implement a Backpack component's styles by reading the Figma design and writing SCSS that implements exactly what Figma specifies, using the new CSS custom properties from the token-sync pipeline. + +**Figma is the source of truth.** The current SCSS may be incomplete, semantically wrong, or missing states that now exist in the design. Don't map old code to new names — read the design and implement it. + +## Input + +Arguments: ` ` + +--- + +## Step 1 — Read the Figma design + +Call `mcp__figma-remote-mcp__get_variable_defs` with the `fileKey` and `nodeId` from the URL. This returns all Figma variables used by the component node in `{ "Figma/Path/name": value }` format — these are the source of truth. + +Also call `mcp__figma-remote-mcp__get_design_context` to get the screenshot and understand the visual structure (variants, states). + +Build the design spec **internally as reasoning** (do not output a long table). For each Figma variable, determine: +- Which CSS property it maps to (background, color, border, border-radius, etc.) +- Which variant/state uses it +- The resulting CSS custom property name (computed in Step 2) + +If a property has no Figma variable (hardcoded), note that for use in Step 4. + +--- + +## Step 2 — Convert Figma variable paths to CSS custom property names + +Apply the **kebabBpkName** rule (from `token-sync/src/style-dictionary-config.ts`): + +``` +Component/Chip/Colour/bg-default-on + → split: ["Component", "Chip", "Colour", "bg-default-on"] + → "Component" → "private": ["private", "Chip", "Colour", "bg-default-on"] + → prepend "bpk": ["bpk", "private", "Chip", "Colour", "bg-default-on"] + → kebab-case each, join: bpk-private-chip-colour-bg-default-on + → prefix --: --bpk-private-chip-colour-bg-default-on +``` + +See `references/naming-rules.md` for full rules and edge cases. + +Verify every computed name exists in: +- `token-sync/css/theme-backpack-light.css` +- `token-sync/css/theme-backpack-dark.css` +- `token-sync/css/primitives.css` + +Flag any name that doesn't exist — don't invent variables. + +--- + +## Step 3 — Read the current SCSS mixin + +File: `packages/backpack-web/src/bpk-mixins/_{component}.scss` + +Understand the current structure (selectors, pseudo-classes, nested modifiers) — you'll reuse the selector architecture. Note what the current code does differently from the Figma design. + +--- + +## Step 4 — Rewrite the SCSS + +Write the mixin from scratch based on what Figma says, not from what the old code says. + +**Core principle:** For each (variant, state, property) tuple in the Figma design spec, emit exactly the CSS that implements it using `var(--bpk-private-…)`. + +**Patterns to use:** + +```scss +// Color property → direct var() +background-color: var(--bpk-private-chip-colour-bg-default-on); +color: var(--bpk-private-chip-colour-text-on); + +// Border (Backpack uses inset box-shadow for borders): +box-shadow: 0 0 0 tokens.$bpk-border-size-sm var(--bpk-private-chip-colour-border-default-off) inset; + +// Dimension (radius, height, padding): +border-radius: var(--bpk-private-chip-dimension-radius); +height: var(--bpk-private-chip-dimension-min-height-width); + +// Generic primitive (spacing, not chip-specific): +padding: 0 var(--bpk-spacing-base); + +// No Figma variable (hardcoded in design): +cursor: pointer; +display: inline-flex; +``` + +**What NOT to do:** +- Don't use `bpk-themeable-property` — that was the old public theming API. The new tokens are private and should be referenced directly. +- Don't keep old `--bpk-chip-*` variable names anywhere. +- Don't keep old SCSS token variables (`tokens.$bpk-core-primary-day`, etc.) for anything that has a Figma-synced equivalent. +- Don't add fallback values to `var()` unless the Figma design explicitly has no token and you're using a hardcoded value. + +**Preserve what Figma doesn't specify:** +- Layout properties (display, align-items, flex, etc.) if not in the Figma spec — keep from current SCSS. +- Cursor styles, user-select, etc. — keep from current code. +- SCSS structure (BEM selectors, hover/focus/active pseudo-classes, RTL margin helpers) — keep the selector architecture, just update the property values. + +--- + +## Step 5 — Write the file and verify + +Write the updated mixin SCSS file directly — no confirmation step needed. + +Then run: +```bash +npm run lint +npm run jest -- --testPathPattern= +``` + +Fix any lint errors before reporting done. + +--- + +## Notes + +- **Dark mode is automatic**: `theme-backpack-dark.css` overrides the same CSS vars at `[data-theme="dark"]`. Once you reference `var(--bpk-private-chip-*)`, dark mode works without any extra SCSS. +- **Missing states in Figma**: If the design doesn't specify a state (e.g. no "bg-default-off"), that state has no fill — use `transparent` or omit the property. +- **Figma variables on non-colour properties**: Dimension variables (radius, padding, min-height) come from `Component/Chip/Dimension/*` → `--bpk-private-chip-dimension-*`. +- **Component name pluralisation in file paths**: chip → `_chips.scss`, badge → `_badges.scss` or `_badges-v2.scss`, button → `_buttons.scss`. diff --git a/.claude/skills/bpk-token-migrate/evals/evals.json b/.claude/skills/bpk-token-migrate/evals/evals.json new file mode 100644 index 0000000000..d8d9e993f9 --- /dev/null +++ b/.claude/skills/bpk-token-migrate/evals/evals.json @@ -0,0 +1,19 @@ +{ + "skill_name": "bpk-token-migrate", + "evals": [ + { + "id": 0, + "prompt": "/bpk-token-migrate chip https://www.figma.com/design/KXf2gHNLDe2cXWUoHl4cTX/Backpack%E2%80%A8Foundations---Components?node-id=10858-52134&m=dev\n\nWorking directory: /Users/gczhu/conductor/workspaces/backpack/buffalo\n\nPlease complete the migration task — show the mapping table and then update the SCSS file. Do NOT commit the changes.", + "expected_output": "1. A mapping table showing old CSS variable names (--bpk-chip-*) mapped to new CSS variable names (--bpk-private-chip-*). 2. The updated _chips.scss file with bpk-themeable-property calls replaced by direct var(--bpk-private-chip-*) declarations. 3. Lint and test run confirmation.", + "files": [], + "assertions": [] + }, + { + "id": 1, + "prompt": "I need to work on CLOV-1632 — the chip component token migration. I have the Figma link: https://www.figma.com/design/KXf2gHNLDe2cXWUoHl4cTX/Backpack%E2%80%A8Foundations---Components?node-id=10858-52134&m=dev\n\nWorking directory: /Users/gczhu/conductor/workspaces/backpack/buffalo\n\nCan you help me replace the old --bpk-chip-* CSS variable names with the new Figma token names in the chip mixin?", + "expected_output": "Same as eval 0 — the skill should trigger, build a mapping table, and apply the replacements to _chips.scss.", + "files": [], + "assertions": [] + } + ] +} diff --git a/.claude/skills/bpk-token-migrate/references/naming-rules.md b/.claude/skills/bpk-token-migrate/references/naming-rules.md new file mode 100644 index 0000000000..4a2c2d7db2 --- /dev/null +++ b/.claude/skills/bpk-token-migrate/references/naming-rules.md @@ -0,0 +1,72 @@ +# Token Naming Transformation Rules + +Source: `token-sync/src/style-dictionary-config.ts` — `kebabBpkName` and `kebabSegment` functions. + +## Algorithm + +Given a Figma variable path like `Component/Chip/Colour/bg-default-on`: + +1. **Split on `/`**: `["Component", "Chip", "Colour", "bg-default-on"]` +2. **Rename Component → private**: `["private", "Chip", "Colour", "bg-default-on"]` +3. **Prepend bpk**: `["bpk", "private", "Chip", "Colour", "bg-default-on"]` +4. **kebab-case each segment**: + - `bpk` → `bpk` + - `private` → `private` + - `Chip` → `chip` + - `Colour` → `colour` + - `bg-default-on` → `bg-default-on` (already kebab) +5. **Join with `-`**: `bpk-private-chip-colour-bg-default-on` +6. **Prepend `--`**: `--bpk-private-chip-colour-bg-default-on` + +## kebabSegment rules + +``` +"CamelCase" → "camel-case" (insert hyphen before uppercase) +"onDark" → "on-dark" +"OnContrast" → "on-contrast" +"spaces here" → "spaces-here" (spaces → hyphens) +"under_score" → "under-score" (underscores → hyphens) +"ALL CAPS" → "all-caps" +"(annotation)" → "annotation" (remove special chars) +"🚧 WIP" → "wip" (remove emoji) +``` + +## Non-Component tokens (no "private" prefix) + +Paths not starting with `Component`: + +| Figma path | CSS variable | +|---|---| +| `Spacing/md` | `--bpk-spacing-md` | +| `Spacing/base` | `--bpk-spacing-base` | +| `Spacing/xl` | `--bpk-spacing-xl` | +| `Radius/sm` | `--bpk-radius-sm` | +| `Radius/md` | `--bpk-radius-md` | +| `Canvas/Default` | `--bpk-canvas-default` | +| `Canvas/Contrast` | `--bpk-canvas-contrast` | +| `Text/Primary` | `--bpk-text-primary` | +| `Text/Secondary` | `--bpk-text-secondary` | +| `Text/Disabled` | `--bpk-text-disabled` | +| `Text/On Dark` | `--bpk-text-on-dark` | +| `Core/Primary` | `--bpk-core-primary` | +| `Line/Default` | `--bpk-line-default` | + +## Component tokens (with "private" prefix) + +| Figma path | CSS variable | +|---|---| +| `Component/Chip/Colour/bg-default-on` | `--bpk-private-chip-colour-bg-default-on` | +| `Component/Chip/Colour/bg-on-dark-off` | `--bpk-private-chip-colour-bg-on-dark-off` | +| `Component/Chip/Dimension/radius` | `--bpk-private-chip-dimension-radius` | +| `Component/Badge/Colour/bg-default` | `--bpk-private-badge-colour-bg-default` | +| `Component/Button/Dimension/padding-h` | `--bpk-private-button-dimension-padding-h` | + +## Where variables live + +- **Component colours**: `token-sync/css/theme-backpack-light.css` and `theme-backpack-dark.css` +- **Primitive spacing/radius**: `token-sync/css/primitives.css` +- **Semantic non-component tokens** (Canvas, Text, Core): `theme-backpack-light.css` + +## iOS/Android exclusion + +Paths with a standalone `ios` or `android` segment are excluded from CSS output. No CSS variable will exist for them.