Generates the static layer for a new evo-web component: canonical HTML structure for every variant (always) and SCSS when design tokens or Figma info is available (conditional). Always runs first in the pipeline — the HTML it establishes is the authoritative reference that /evo-static-storybook, /evo-marko-component, /evo-react-component, and /evo-docs-hookup all read from. Use this whenever the user says "generate the static layer", "create the static component", "build the CSS component", "write the skin layer", or when the evo-component orchestrator invokes Step 4.
Installation
Install with Codex or Claude Copy this prompt, paste it into Codex, Claude, or another assistant, and let it review the skill page and install it for you.
Generates the static layer for a new evo-web component: canonical HTML structure for every variant (always) and SCSS when design tokens or Figma info is available (conditional). Always runs first in the pipeline — the HTML it establishes is the authoritative reference that /evo-static-storybook, /evo-marko-component, /evo-react-component, and /evo-docs-hookup all read from. Use this whenever the user says "generate the static layer", "create the static component", "build the CSS component", "write the skin layer", or when the evo-component orchestrator invokes Step 4.
evo-static-component
You are generating the static (CSS) layer — the foundation that all other
layers wrap. This skill always runs and always produces HTML. SCSS is
conditional on token/Figma availability.
Two phases:
Phase 1 (always): Establish canonical HTML structure for every variant
Phase 2 (conditional): Generate SCSS when manifest.tokens or
figma.fileKey / figmaUrl is present
The HTML produced in Phase 1 is the authoritative source of truth.
Downstream skills read from this output — they do not re-derive structure
from the manifest independently.
This labelled output is what /evo-static-storybook, /evo-marko-component,
/evo-react-component, and /evo-docs-hookup consume directly.
Phase 2 — SCSS (conditional)
Skip if neither manifest.tokens nor any Figma reference is present:
⏭ SCSS deferred: no design tokens or Figma reference in manifest.
When the design spec is ready:
1. Ensure src/routes/_index/components/$COMPONENT/*.spec.json exists with a 'tokens' field
2. Re-run /evo-create-component-manifest $COMPONENT to update manifest.json
3. Re-run /evo-static-component $COMPONENT
Token resolution
Gap-filling priority order — follow this strictly:
manifest.tokens map — trust it; use these directly without question
get_variable_defs Figma call — use to catch tokens present in the design but missing from the manifest
get_design_context screenshot + annotations — use for spacing math, border widths, typographic scale, and edge-case state details
Token lookup fallback list (below) — reference for common properties when none of the above provides a value
STOP — missing token blocker: if all four levels above fail to provide a value, do not hardcode. Document every missing token (property name + raw value from Figma) and halt with the banner below.
Token lookup — two-stop check before declaring a token missing:
When a token referenced in the manifest or spec is not found in packages/skin/dist/tokens/, check node_modules/@ebay/design-tokens/dist/mixins/ before concluding it is absent. Evo themes inherit from that package without re-declaring every token, so a token can be valid and available even if it does not appear in the compiled skin token files.
Only after both checks come up empty should you treat the token as missing and fall back to the next priority level.
Figma validation (when figma.fileKey or figmaUrl present)
Determine Figma coordinates:
If figmaUrl is present: extract fileKey and nodeId directly from the URL.
URL format: https://www.figma.com/design/:fileKey/:fileName?node-id=1-2
Convert the node-id query param from - to : (e.g. 1-2 → 1:2).
If only figma.fileKey is present (no full URL): call search_design_system to locate the nodeId:
If a Figma variable is used in the design but absent from the manifest tokens map, add it and note it came from Figma validation
If a Figma value has no matching CSS custom property (e.g. a hardcoded hex), treat this as a missing token blocker — do not proceed to SCSS generation. Document it and halt (see missing token blocker below).
Missing token blocker
When all four priority levels above fail to resolve a value, do not generate SCSS. Instead halt with:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
🔴 Missing design tokens — SCSS generation blocked
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
The following values from the design have no corresponding
design token. The design team must add these tokens before
SCSS can be generated — do not hardcode these values.
<for each missing value>
Property: <CSS property, e.g. background-color>
Used on: <BEM selector, e.g. .badge--warning>
Raw value from Figma: <hex or literal>
Required token name (suggested): <e.g. --color-background-warning>
Resolution: Ask the design team to add the token(s) above to the
design token system, then update manifest.json and re-run this skill.
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Write status: "failed" with an error field listing the missing tokens to the pipeline state file (step '4'), then stop.
Token gap-filling reference
Use these only when manifest, Figma variable defs, and @ebay/design-tokens all fail to provide a value for layout, spacing, and typographic properties (not brand colors — those must have tokens):
For brand-critical color properties (background-color, color, border-color) on the
root block — where consumers may want component-level overrides — use the two-tier mixin:
Skip the mixin for layout properties (padding, margin, border-radius, font-size) — those go
as plain var(--).
Hardcoded values: Not permitted. If you find yourself reaching for a raw hex or pixel value with no token backing it, stop and trigger the missing token blocker above. Do not use $_ file-scoped variables.
ARIA attributes as styling hooks — prefer attribute selectors over modifier classes for state:
Nest BEM selectors (&--modifier inside the block is wrong — keep them flat)
Chain BEM modifiers (.avatar--fit.avatar--green is wrong)
Use presentational names (.btn--green is wrong; .btn--primary is right) — unless the modifier IS a color variant by design (e.g. avatar color variants are intentionally named by color)
Use .disabled class — use [disabled] or [aria-disabled="true"]
Add commented-out code
Write deep nesting beyond pseudo-selectors (:hover, ::before, :focus-visible)
Bundle registration
Add @use "../<block>/<block>"; to packages/skin/src/sass/bundles/skin-headless.scss
in alphabetical order among the existing @use lines.
Build verification
npm run build -w @ebay/skin
Fix any errors inline. Do not re-run the skill.
Common failure causes:
Typo in a var(--) token name — cross-check against the two-stop lookup
Missing or wrong @use path
Unclosed brace from a nested block that should be flat
Mandatory completion gate
⚠️ This skill is not done when you finish writing files.
It is done when every item below is explicitly checked off.
Returning control to the pipeline with unchecked items is a pipeline violation.
Do not print the output summary until this gate is fully passed.
Work through each item in order. Read the file on disk to verify each claim — do not
rely on memory of what you wrote. If any item fails, fix it before continuing.
HTML
HTML catalogue is written in the output and covers: Default, every modifier in manifest.bem.modifiers[], RTL, and every state that changes the HTML structure
SCSS
packages/skin/src/sass/<block>/<block>.scss exists on disk
.${block} base rule present in the SCSS
Every modifier in manifest.bem.modifiers[] has a .${block}--${modifier} rule — grep the file to confirm
Every element in manifest.bem.elements[] has a .${block}__${element} rule — grep the file to confirm
Token mixin used for background-color, color, border-color on root block; plain var(--) for layout properties
No BEM nesting (&--modifier inside block is forbidden — keep rules flat)
Dark mode token check — run this for every modifier that sets a background-color:
If the modifier introduces a light or warm background (yellow, white, cream, light-tinted):
Step 1 — Verify the token is defined in all four theme files.
The skin theming system has two parallel implementations that must both be updated:
evo-light-class.scss / evo-dark-class.scss — .evo-theme-scoped, class-based themes (dev server and .evo-theme wrapper in production)
All four files must define --color-foreground-on-<type>. Missing it in the class-based pair means the dev site silently inherits the wrong foreground color even when the production bundle is correct.
If any file is missing the definition, add --color-foreground-on-<type>: var(--color-neutral-800) to it. Never alias to --color-foreground-primary — that token resolves to #f7f7f7 (near-white) in dark mode and will silently fail contrast on any light background.
Step 2 — Verify the token resolves correctly in the browser.
File-level grep confirms the source exists but not that the browser receives it. The token scope must match what the page loads — evo-light-class.scss defines within .evo-theme, while evo-light.scss defines on :root. After rebuild, navigate to the CSS docs page and run:
getComputedStyle(
document.querySelector(".evo-theme") || document.documentElement,
)
.getPropertyValue("--color-foreground-on-<type>")
.trim();
// Must return a dark hex value (e.g. "#191919") — not empty string, not a light color
An empty string means the token is missing from the class-based file. A light color (e.g. #f7f7f7) means it is aliasing foreground-primary or another adaptive token.
If the modifier only uses dark backgrounds (attention, inverse, etc.), both sub-steps pass automatically
Foreground specificity check — run this when a modifier sets color-token on its root element:
If the new modifier sets text color via color-token on the root AND the block has existing .${block} a { color: ... } or .${block} button.fake-link { color: ... } rules:
The base .block a rule has specificity (0,1,1), which beats the modifier's root color (0,2,0) for a elements. The modifier's text color does not cascade to links without an explicit override — links will silently inherit the base fallback (often on-inverse / white), which fails WCAG AA on a light background.
Verify explicit link/button overrides exist for the modifier:
For light-background modifiers (warning, etc.), if both .modifier a and .modifier button.fake-link color rules are absent, this is a blocking WCAG 1.4.3 failure — add them before proceeding.
Stories (check only — do NOT write or edit the stories file)
Check whether packages/skin/src/sass/<block>/stories/<block>.stories.js exists.
If RTL, textSpacing, or modifier stories are absent, add a ⚠️ WARNING
in your output summary: "Stories incomplete — evo-static-storybook will add
missing exports in Step 5." Do NOT add them yourself. Story files are
evo-static-storybook's sole responsibility.
Docs
src/routes/_index/components/<name>/css+page.marko updated with a live demo block and <highlight-code> snippet for every new modifier or variant
Build
npm run build -w @ebay/skin passes with no errors
Grep packages/skin/dist/bundles/skin-default.css to confirm the new modifier selector is present in the compiled output
Only after every box is checked, print the output summary and return.
Output summary
## Static component: $COMPONENT
Phase 1 — HTML structure:
Variants: Default, [list]
Root element: <element>
BEM block: <block>
ARIA: [label strategy summary]
[Full HTML catalogue above]
Phase 2 — SCSS:
[✅ packages/skin/src/sass/<block>/<block>.scss written]
[✅ skin-headless.scss updated]
[⏭ Deferred — no tokens or Figma reference]
Completion gate: ✅ All items passed
The HTML catalogue above is the canonical reference for all downstream skills.
Completion record — mandatory final step
After the output summary is printed and all items in the mandatory completion gate
are checked off, write the completion record to the pipeline state file.
This is the signal the orchestrator reads to advance — it does not read your prose output.
Do not skip this step. Do not write status: "complete" until the output files are confirmed on disk.
Before running: Substitute the actual component name for $COMPONENT and the actual BEM block name for <BLOCK>.
If SCSS was deferred (no tokens/Figma): still write the completion record but set outputs to an empty array and add "note": "SCSS deferred — no tokens or Figma reference".
If any completion gate item failed: write status: "failed" with an error field describing which gate item failed. Do NOT write status: "complete".
Before running the failed variant: Substitute the actual component name for $COMPONENT and describe the failing gate item in place of <describe what gate item failed>.