with one click
frontend-tokens
// Design token management — color, typography, spacing, motion, and interaction tokens. Use with /frontend-tokens to create, update, or manage design tokens.
// Design token management — color, typography, spacing, motion, and interaction tokens. Use with /frontend-tokens to create, update, or manage design tokens.
[HINT] Download the complete skill directory including SKILL.md and all related files
| name | frontend-tokens |
| description | Design token management — color, typography, spacing, motion, and interaction tokens. Use with /frontend-tokens to create, update, or manage design tokens. |
| reads | ["devinfo.handoff"] |
| writes | ["devinfo.handoff","devinfo.tokenDrift"] |
| metadata | {"author":"mileszeilstra","version":"3.3.0","category":"frontend"} |
Beheert het project design systeem: design tokens aanmaken, bekijken, updaten, en dark/light mode configuratie. Inclusief motion tokens (durations, easings) en interaction tokens (focus ring, hover, active states).
Keywords: design tokens, theme, colors, typography, spacing, breakpoints, dark mode, light mode, tailwind, css variables, design system, brand colors, font families, motion, animation, easing, transitions, interactions, focus ring, hover states
Dit command beheert de theme sectie in .project/project.json die design tokens bevat (colors, typography, spacing, breakpoints, borderRadius, shadows, modes, cssVars). Het kan tokens automatisch extraheren uit bestaande Tailwind of CSS configuratie.
Output locatie: .project/project.json → theme sectie
Referenties:
skills/frontend-tokens/references/THEME_TEMPLATE.md — Token categorieën en naming conventionsskills/shared/DESIGN.md — Anti-patterns, OKLCH kleuradvies, typography, motion, interaction statesDe theme sectie in project.json volgt dit schema:
{
"colors": {
"main": [{ "token": "dark", "value": "#hex", "usage": "description" }],
"accent": [
{ "token": "accent-primary", "value": "#hex", "usage": "description" }
],
"semantic": [
{ "token": "success", "value": "#hex", "usage": "description" }
]
},
"typography": {
"families": {
"heading": "Font, fallback",
"body": "Font, fallback",
"mono": "Font, fallback"
},
"sizes": [
{
"token": "text-display",
"size": "3rem",
"lineHeight": "1.1",
"usage": "Hero headings"
},
{
"token": "text-title-l",
"size": "2rem",
"lineHeight": "1.2",
"usage": "Page titles"
},
{
"token": "text-body-m",
"size": "1rem",
"lineHeight": "1.5",
"usage": "Body text"
}
]
},
"spacing": {
"base": "4px",
"scale": [{ "token": "spacing-4", "value": "16px", "usage": "description" }]
},
"breakpoints": [
{ "token": "screen-md", "value": "768px", "target": "Tablets" }
],
"borderRadius": [
{ "token": "rounded-md", "value": "0.375rem", "usage": "description" }
],
"shadows": [{ "token": "shadow-md", "value": "...", "usage": "Cards" }],
"motion": {
"durations": [
{
"token": "duration-fast",
"value": "200ms",
"usage": "Tooltip, hover state"
}
],
"easings": [
{
"token": "ease-out",
"value": "cubic-bezier(0.25, 1, 0.5, 1)",
"usage": "Elements entering"
}
]
},
"interactions": {
"focusRing": {
"width": "2px",
"style": "solid",
"color": "var(--color-accent-primary)",
"offset": "2px"
},
"hover": {
"transition": "var(--duration-fast) var(--ease-out)",
"transform": "none"
},
"active": { "transform": "scale(0.98)" }
},
"modes": { "light": ":root { css }", "dark": ".dark { css }" },
"cssVars": ":root { full css vars export }"
}
Zie shared/DASHBOARD.md voor het volledige project.json schema met alle secties.
.project/project.jsontheme sectie (kan leeg/undefined zijn).project/project.json (of maak nieuw met leeg schema als niet bestaat)theme sectie (NIET andere secties overschrijven)JSON.stringify(data, null, 2)Als .project/project.json niet bestaat, maak aan met het lege schema uit shared/DASHBOARD.md:
{
"concept": {
"name": "",
"pitch": "",
"conceptFile": "project-concept.md",
"content": ""
},
"theme": {},
"stack": {
"framework": "",
"language": "",
"styling": "",
"db": "",
"auth": "",
"hosting": "",
"packages": []
},
"data": { "entities": [] },
"endpoints": [],
"decisions": []
}
Vul vervolgens de theme sectie met de gegenereerde tokens.
stateDiagram-v2
[*] --> PREFLIGHT: /theme invoked
PREFLIGHT --> ACTION_SELECT: validation pass
PREFLIGHT --> ERROR: validation fail
ACTION_SELECT --> CREATE: "Aanmaken"
ACTION_SELECT --> VIEW: "Bekijken"
ACTION_SELECT --> UPDATE: "Updaten"
ACTION_SELECT --> EXTRACT: "Extraheren"
ACTION_SELECT --> MODES: "Modes"
ACTION_SELECT --> DELETE: "Verwijderen"
CREATE --> CONFIRM: all steps complete
VIEW --> [*]: display only (no state change)
UPDATE --> CONFIRM: changes ready
EXTRACT --> CONFIRM: tokens parsed
MODES --> CONFIRM: mode configured
DELETE --> CONFIRM: user confirmed
CONFIRM --> POSTFLIGHT: user confirms "Ja"
CONFIRM --> ACTION_SELECT: user selects "Aanpassen"
CONFIRM --> [*]: user selects "Annuleren"
POSTFLIGHT --> COMPLETE: validation pass
POSTFLIGHT --> RECOVER: validation fail
COMPLETE --> [*]
ERROR --> RECOVER
RECOVER --> PREFLIGHT: retry
RECOVER --> [*]: abort
State Descriptions:
Voer deze checks uit VOORDAT de workflow begint.
PRE-FLIGHT CHECK
════════════════════════════════════════════════
1. Directory Check
# Verify .project/ exists or can be created
Directory: [✓|✗] .project/ - [exists|created|error]
2. Session Check
# Check .project/session/devinfo.json
Session: [✓|✗] [New session | Continuing from {skill}]
Handoff: [✓|✗] [data available | not applicable]
3. Conflict Check (voor Create/Update)
# Read .project/project.json → check of theme sectie al gevuld is
Conflicts: [✓|✗] project.json theme - [empty | has data (will warn) | file missing]
Pre-flight Samenvatting:
════════════════════════════════════════════════
PRE-FLIGHT RESULT
════════════════════════════════════════════════
Directory: [✓ PASS | ✗ FAIL]
Session: [✓ PASS | ✗ FAIL]
Conflicts: [✓ PASS | ⚠ WARNING | ✗ FAIL]
Status: [→ Ready to proceed | ⚠ Warning: {issue} | ✗ Cannot proceed]
════════════════════════════════════════════════
On Failure:
AskUserQuestion:
header: "Pre-flight Failed"
question: "Pre-flight check mislukt: {reason}. Hoe wil je doorgaan?"
options:
- label: "Fix en retry (Recommended)", description: "Los probleem op en probeer opnieuw"
- label: "Doorgaan anyway", description: "Negeer warning en ga door"
- label: "Annuleren", description: "Stop workflow"
multiSelect: false
Check eerst of project.json een gevulde theme sectie bevat:
# Read .project/project.json → parse JSON → check theme sectie
Design Principles Context (optional):
Check .project/project.json → design.principles. If principles exist, show them as context before action selection:
DESIGN PRINCIPES BESCHIKBAAR
════════════════════════════════════════════════════════════════
- {principle.name}: {principle.description}
- {principle.name}: {principle.description}
════════════════════════════════════════════════════════════════
Deze principes worden meegenomen als suggestie bij token keuzes.
Principles are advisory — use them to inform suggestions (e.g., if "Mobile-first" exists, suggest mobile-optimized breakpoints), but don't enforce.
Als theme sectie DATA bevat (niet leeg):
Completeness check — voer uit voordat het actiemenu getoond wordt:
Check welke van de 10 verwachte secties aanwezig zijn in theme. Een sectie telt als "aanwezig" als de key bestaat EN niet een leeg object {}, leeg array [], of lege string "" is.
THEME STATUS
════════════════════════════════════════════════
[✓|✗] colors
[✓|✗] typography
[✓|✗] spacing
[✓|✗] breakpoints
[✓|✗] borderRadius
[✓|✗] shadows
[✓|✗] motion
[✓|✗] interactions
[✓|✗] modes
[✓|✗] cssVars
Compleet: {N}/10 secties
════════════════════════════════════════════════
Als alle secties aanwezig (N = 10):
AskUserQuestion:
header: "Theme"
question: "Wat wil je doen?"
options:
- label: "Bekijken", description: "Toon huidige design tokens"
- label: "Updaten", description: "Wijzig bestaande tokens"
- label: "Modes", description: "Dark/light mode beheren"
- label: "Verwijderen", description: "Theme data verwijderen"
multiSelect: false
Als secties ontbreken (N < 10):
AskUserQuestion:
header: "Theme"
question: "Wat wil je doen? (⚠ {10-N} secties ontbreken: {missing_list})"
options:
- label: "Aanvullen (Recommended)", description: "Vul ontbrekende secties aan: {missing_list}"
- label: "Bekijken", description: "Toon huidige design tokens"
- label: "Updaten", description: "Wijzig bestaande tokens"
- label: "Modes", description: "Dark/light mode beheren"
multiSelect: false
Als theme sectie LEEG is of project.json niet bestaat:
AskUserQuestion:
header: "Theme"
question: "Geen theme gevonden. Wat wil je doen?"
options:
- label: "Aanmaken (Recommended)", description: "Nieuwe theme met guided setup"
- label: "Extraheren", description: "Tokens ophalen uit bestaande Tailwind/CSS"
- label: "Explain question", description: "Leg opties uit"
multiSelect: false
Targets alleen de ontbrekende secties. Voor elke ontbrekende sectie, voer de bijbehorende stap uit de Aanmaken-route uit:
| Ontbrekende Sectie | → Voer Stap Uit |
|---|---|
| colors | Stap 1: Kleuren |
| typography | Stap 2: Typography |
| spacing | Stap 3: Spacing |
| breakpoints | Stap 4: Breakpoints |
| modes | Stap 5: Dark Mode |
| motion | Stap 6: Motion |
| interactions | Stap 7: Interactions |
| borderRadius | Genereer defaults (0.125rem, 0.25rem, 0.375rem, 0.5rem, 0.75rem, 9999px) |
| shadows | Genereer defaults (sm, md, lg, xl + glow met accent kleur) |
| cssVars | Auto-genereer uit alle aanwezige token data |
Sla al-aanwezige secties over. Na het aanvullen van alle ontbrekende secties:
cssVars om nieuw toegevoegde tokens te bevattenStap 1: Kleuren
AskUserQuestion:
header: "Colors"
question: "Hoe wil je kleuren definiëren?"
options:
- label: "Genereer voor mij (Recommended)", description: "Beschrijf wat je bouwt, Claude kiest passende kleuren"
- label: "Handmatig invoeren", description: "Ik geef hex values op"
- label: "Extraheren uit config", description: "Haal uit Tailwind/CSS"
- label: "Explain question", description: "Leg uit wat design tokens zijn"
multiSelect: false
Als "Genereer voor mij":
Beschrijf kort wat je bouwt:
→ Voorbeeld: "Healthcare dashboard voor artsen"
→ Voorbeeld: "E-commerce voor luxe horloges"
→ Voorbeeld: "SaaS landing page voor projectmanagement"
Op basis van de beschrijving genereert Claude een contextbewust kleurenpalet:
Print 8 blank lines as whitespace buffer (keeps the palette table above visible when the modal panel opens).
header: "Kleurpalet"
question: "Klopt dit kleurpalet?"
options:
- label: "Ja, ga door (Recommended)", description: "Ga naar Stap 2"
- label: "Aanpassen", description: "Wijzig kleuren"
multiSelect: false
Token Layer Toelichting (na kleurbevestiging)
De gegenereerde kleuren volgen een drielaags structuur:
main en accent groepen): directe hex/OKLCH waarden.
CSS output: --color-dark: #1a1a2e, --color-accent-primary: #3B82F6semantic groep): verwijzen via var() naar een primitive.
CSS output: --color-success: var(--color-accent-primary) (niet: --color-success: #10B981)
Reden: als accent-primary wijzigt, updaten semantic tokens automatisch mee.Houd deze structuur aan bij het opbouwen van cssVars in Stap 8.
Kleurformaat
Detecteer Tailwind versie uit package.json vóór kleurengeneratie:
"tailwindcss": "^4.*" → gebruik OKLCH (oklch(L C H), bijv. oklch(0.15 0.02 260))#RRGGBB)OKLCH voordeel: L-component direct aanpasbaar voor dark mode (zelfde C/H = zelfde kleur, alleen helderheid wisselt). Hex heeft dit niet.
Als "Handmatig invoeren":
Geef je primaire kleuren (hex values):
1. Primary (hoofdkleur voor acties/buttons)
→ Voorbeeld: #3B82F6
2. Secondary (ondersteunende kleur)
→ Voorbeeld: #10B981
3. Neutral (grijs voor tekst/borders)
→ Voorbeeld: #6B7280
Als "Extraheren": → Spring naar Route: Extraheren
Stap 2: Typography
AskUserQuestion:
header: "Typography"
question: "Welke fonts gebruik je?"
options:
- label: "System fonts (Recommended)", description: "system-ui, sans-serif"
- label: "Custom fonts", description: "Eigen font families opgeven"
- label: "Extraheren", description: "Haal uit bestaande CSS"
multiSelect: false
Als "Custom fonts":
Geef je font families:
1. Headings font
→ Voorbeeld: "Inter", "Poppins"
2. Body font
→ Voorbeeld: "Inter", system-ui
3. Mono font (optioneel, voor code)
→ Voorbeeld: "Fira Code", monospace
Type 's' voor populaire combinaties
Stap 3: Spacing
AskUserQuestion:
header: "Spacing"
question: "Spacing scale voorkeur?"
options:
- label: "4px base (Recommended)", description: "4, 8, 12, 16, 20, 24, 32, 48, 64"
- label: "8px base", description: "8, 16, 24, 32, 40, 48, 64, 80, 96"
- label: "Custom", description: "Eigen spacing scale"
- label: "Explain question", description: "Leg uit wat een spacing scale is"
multiSelect: false
Stap 4: Breakpoints
AskUserQuestion:
header: "Breakpoints"
question: "Responsive breakpoints?"
options:
- label: "Tailwind defaults (Recommended)", description: "sm:640, md:768, lg:1024, xl:1280"
- label: "Bootstrap style", description: "sm:576, md:768, lg:992, xl:1200"
- label: "Custom", description: "Eigen breakpoints"
- label: "Explain question", description: "Leg uit hoe breakpoints werken"
multiSelect: false
Stap 5: Dark Mode
AskUserQuestion:
header: "Dark Mode"
question: "Wil je dark mode toevoegen aan je theme?"
options:
- label: "Ja, auto-generate (Recommended)", description: "Genereer dark kleuren automatisch op basis van je light palette"
- label: "Ja, handmatig", description: "Zelf dark mode kleuren opgeven"
- label: "Nee, alleen light mode", description: "Sla dark mode over (later toe te voegen via Modes)"
multiSelect: false
Als "Ja, auto-generate":
dark <> lightmid-gray en light-gray aan voor dark contextoklch(0.15 0.02 260) → oklch(0.90 0.02 260) voor background-inversion); C en H blijven gelijk voor kleurconsistentie.dark CSS block naast :rootAls "Ja, handmatig":
Geef je dark mode kleuren (hex values):
1. Background (donkere achtergrond)
→ Voorbeeld: #1a1a2e
2. Foreground (lichte tekst op donker)
→ Voorbeeld: #f5f5f5
3. Card background (iets lichter dan background)
→ Voorbeeld: #2d2d44
4. Border kleur
→ Voorbeeld: #3d3d5c
Als "Nee":
modes bevat alleen light keyStap 6: Motion
AskUserQuestion:
header: "Motion"
question: "Motion tokens voor animaties en transities?"
options:
- label: "Defaults (Recommended)", description: "Standaard durations (100/200/300/500ms) + smooth easings"
- label: "Custom", description: "Eigen durations en easing curves opgeven"
- label: "Geen motion tokens", description: "Sla over (later toe te voegen via Update)"
- label: "Explain question", description: "Leg uit wat motion tokens zijn"
multiSelect: false
Als "Defaults":
Genereer standaard motion tokens op basis van shared/DESIGN.md:
duration-instant (100ms), duration-fast (200ms), duration-normal (300ms), duration-slow (500ms)ease-out (cubic-bezier(0.25, 1, 0.5, 1)), ease-in (cubic-bezier(0.7, 0, 0.84, 0)), ease-in-out (cubic-bezier(0.65, 0, 0.35, 1))Print 8 blank lines as whitespace buffer (keeps the motion token table above visible when the modal panel opens).
header: "Motion Tokens"
question: "Kloppen deze motion tokens?"
options:
- label: "Ja, ga door (Recommended)", description: "Ga naar Stap 7"
- label: "Custom", description: "Geef eigen durations/easings op"
multiSelect: false
Als "Custom":
Geef je motion preferences:
1. Snelheid voorkeur?
→ "snappy" (kortere durations: 75/150/200/350ms)
→ "smooth" (standaard: 100/200/300/500ms)
→ "relaxed" (langere durations: 150/250/400/600ms)
2. Easing stijl?
→ "smooth" (ease-out-quart — default)
→ "snappy" (ease-out-expo — confident, direct)
→ "custom" (eigen cubic-bezier waarden)
Als "Geen motion tokens":
motion sectie wordt leeg objectStap 7: Interactions
AskUserQuestion:
header: "Interactions"
question: "Interaction tokens voor hover, focus en active states?"
options:
- label: "Defaults (Recommended)", description: "Focus ring (2px accent), subtle hover transition, scale active"
- label: "Custom", description: "Eigen interaction styles opgeven"
- label: "Geen interaction tokens", description: "Sla over (later toe te voegen via Update)"
- label: "Explain question", description: "Leg uit wat interaction tokens zijn"
multiSelect: false
Als "Defaults":
Genereer standaard interaction tokens:
width: 2px, style: solid, color: var(--color-accent-primary), offset: 2pxtransition: var(--duration-fast) var(--ease-out), transform: nonetransform: scale(0.98)Print 8 blank lines as whitespace buffer (keeps the interaction token table above visible when the modal panel opens).
header: "Interaction Tokens"
question: "Kloppen deze interaction tokens?"
options:
- label: "Ja, ga door (Recommended)", description: "Ga naar Stap 8"
- label: "Custom", description: "Geef eigen focus/hover/active stijlen op"
multiSelect: false
Als "Custom":
Geef je interaction preferences:
1. Focus ring
→ Kleur: accent-primary / custom
→ Breedte: 2px / custom
→ Offset: 2px / custom
2. Hover effect
→ Transition speed: instant / fast / normal
→ Transform: none / translateY(-1px) / scale(1.02)
3. Active/press effect
→ Transform: scale(0.98) / scale(0.95) / none
Als "Geen interaction tokens":
interactions sectie wordt leeg objectCHECKPOINT: Design Tokens Samenvatting
Presenteer alle verzamelde tokens als overzicht voordat ze worden opgeslagen:
Stap 8: Bevestiging
THEME SAMENVATTING
| Categorie | Waarde |
|-----------|--------|
| **Primary** | {color} |
| **Secondary** | {color} |
| **Neutral** | {color} |
| **Headings** | {font} |
| **Body** | {font} |
| **Spacing** | {scale} |
| **Breakpoints** | {list} |
| **Dark Mode** | {Ja (auto) / Ja (custom) / Nee} |
| **Motion** | {Defaults / Custom / Geen} |
| **Interactions** | {Defaults / Custom / Geen} |
Print 8 blank lines as whitespace buffer (keeps the THEME SAMENVATTING table above visible when the modal panel opens). AskUserQuestion:
header: "Confirm"
question: "Theme aanmaken met deze settings?"
options:
- label: "Ja, aanmaken (Recommended)", description: "Schrijf naar .project/project.json (theme sectie)"
- label: "Aanpassen", description: "Terug om wijzigingen te maken"
- label: "Annuleren", description: "Stop zonder aanmaken"
multiSelect: false
Als "Ja":
skills/frontend-tokens/references/THEME_TEMPLATE.md voor token categorien en naming conventionscolors (main, accent, semantic) met structured token objectstypography met families en sizes. Gebruik semantische namen voor sizes:
text-display (grootste heading), text-title-l/m/s, text-headline-l/m/s, text-body-l/m/s, text-code
i.p.v. size-based namen als text-xs/sm/base/lg. Wijs elke naam een concrete rem-waarde toe passend bij het project.spacing met base en scalebreakpoints, borderRadius, shadowsmodes met zowel light als dark CSS stringsmodes met alleen light keymotion met durations en easingsinteractions met focusRing, hover, activecssVars — volledige CSS variables string (alle tokens incl. motion/interaction als :root { ... }).project/project.json (of maak nieuw met leeg schema)theme sectie → Write terug als formatted JSON.project/project.json → parse theme sectieHUIDIGE THEME
## Colors
| Token | Value | Preview |
|-------|-------|---------|
| primary-500 | #3B82F6 | |
| secondary-500 | #10B981 | |
| ... | ... | ... |
## Typography
| Element | Font |
|---------|------|
| Headings | Inter |
| Body | system-ui |
## Spacing
| Token | Value |
|-------|-------|
| spacing-1 | 4px |
| spacing-2 | 8px |
| ... | ... |
## Breakpoints
| Name | Value |
|------|-------|
| sm | 640px |
| md | 768px |
| ... | ... |
⚠ ONTBREKENDE SECTIES: {missing_list}
Gebruik "Aanvullen" om ontbrekende secties toe te voegen.
AskUserQuestion:
Als alle secties aanwezig:
header: "Action"
question: "Wat wil je doen?"
options:
- label: "Klaar", description: "Terug naar conversation"
- label: "Updaten", description: "Wijzigingen maken"
- label: "Exporteren", description: "Toon als CSS variables"
- label: "Visual Preview", description: "Open theme preview in browser"
multiSelect: false
Als secties ontbreken — voeg "Aanvullen" toe:
header: "Action"
question: "Wat wil je doen? (⚠ {N} secties ontbreken)"
options:
- label: "Aanvullen (Recommended)", description: "Vul ontbrekende secties aan: {missing_list}"
- label: "Klaar", description: "Terug naar conversation"
- label: "Updaten", description: "Wijzigingen maken"
- label: "Exporteren", description: "Toon als CSS variables"
multiSelect: false
If "Visual Preview":
THEME PREVIEW
═════════════
Generating preview page...
1. Create temporary preview HTML:
- Inject CSS variables from project.json theme.cssVars
- Include color swatches, typography samples, spacing demo
2. Open in default browser:
start [temp-path]/theme-preview.html
(If dark mode configured: include toggle button in preview)
Gebruik vóór elke Write op theme.colors, theme.typography, of theme.spacing waarbij een bestaande key een andere waarde krijgt (niet puur additief).
Additief = geen drift-risk (geen check nodig):
colors (bv. colors.brand-accent)typography.sizesDrift-risk = bestaande key wijzigt (run drift check):
colors.primary waarde wijzigt of wordt verwijderd/hernoemdtypography.fontFamily wijzigtspacing schaal significant wijzigtDrift-check stappen:
backlog.html → filter op type === "PAGE" && (status === "DOING" || status === "DONE")TOKEN DRIFT WARNING
Wijziging: {token-pad} {oude waarde} → {nieuwe waarde}
Affected: {N} PAGE-features (DOING/DONE)
- {page-naam} ({status}, gebouwd {datum})
- ...
Pages die bg-{token} / text-{token} gebruiken kunnen visueel breken.
header: "Token drift gedetecteerd"
question: "Hoe verder?"
options:
- label: "Doorgaan + log drift (Recommended)", description: "Tokens schrijven, drift gelogd in devinfo.tokenDrift voor latere re-render"
- label: "Eerst pages bijwerken", description: "Stop nu — draai /frontend-convert {page} patch op affected pages"
- label: "Annuleren", description: "Geen wijziging schrijven"
multiSelect: false
Bij "Doorgaan + log drift": schrijf naar devinfo.tokenDrift:
{
"tokenDrift": {
"changedAt": "{ISO-timestamp}",
"changes": [
{
"path": "{token-pad}",
"from": "{oude waarde}",
"to": "{nieuwe waarde}"
}
],
"affectedFeatures": ["{page-naam-1}", "{page-naam-2}"],
"resolved": false
}
}
Toon na write:
Aanbevolen vervolg per affected page:
/frontend-convert {page} patch (re-render met nieuwe tokens)
Bij "Eerst pages bijwerken" of "Annuleren": stop zonder Write.
AskUserQuestion:
header: "Update"
question: "Welke sectie wil je updaten?"
options:
- label: "Colors", description: "Kleuren aanpassen"
- label: "Typography", description: "Fonts aanpassen"
- label: "Spacing", description: "Spacing scale aanpassen"
- label: "Breakpoints", description: "Breakpoints aanpassen"
- label: "Motion", description: "Durations en easings aanpassen"
- label: "Interactions", description: "Focus ring, hover, active states aanpassen"
- label: "Alles", description: "Volledige herconfig"
multiSelect: true
Per geselecteerde sectie:
project.json → theme sectieStap 1: Detectie
# Zoek configuratie files
# - tailwind.config.js/ts/mjs
# - CSS files met :root variables
# - globals.css, variables.css, etc.
Output:
DETECTIE RESULTAAT
| Bron | Status | Tokens |
|------|--------|--------|
| tailwind.config.js | ✓ Gevonden | ~{N} colors, spacing |
| src/styles/globals.css | ✓ Gevonden | ~{N} CSS variables |
| src/index.css | ✗ Geen tokens | - |
AskUserQuestion:
header: "Extract"
question: "Uit welke bronnen extraheren?"
options:
- label: "Alle bronnen (Recommended)", description: "Combineer alle gevonden tokens"
- label: "Alleen Tailwind", description: "Alleen uit tailwind config"
- label: "Alleen CSS", description: "Alleen :root variables"
multiSelect: false
Stap 2: Extractie uitvoeren
colors/typography/spacing keys overschrijftAskUserQuestion:
header: "Modes"
question: "Theme mode actie?"
options:
- label: "Dark mode toevoegen (Recommended)", description: "Voeg dark variant toe aan huidige theme"
- label: "Light mode toevoegen", description: "Voeg light variant toe"
- label: "Mode verwijderen", description: "Verwijder een bestaande mode"
- label: "Mode switchen", description: "Wissel default mode"
- label: "Explain question", description: "Leg uit hoe modes werken"
multiSelect: false
Als "Dark mode toevoegen":
AskUserQuestion:
header: "Dark Mode"
question: "Hoe dark mode kleuren genereren?"
options:
- label: "Auto-generate (Recommended)", description: "Inverteer/adjust automatisch"
- label: "Handmatig", description: "Zelf dark kleuren opgeven"
- label: "Extraheren", description: "Haal uit bestaande dark theme CSS"
- label: "Explain question", description: "Tips voor dark mode kleuren"
multiSelect: false
Als "Auto-generate":
After mode configuration, show side-by-side comparison:
MODE COMPARISON
═══════════════
1. Generate comparison HTML:
- Left panel: Light mode
- Right panel: Dark mode
- Same content in both
2. Open in default browser:
start [temp-path]/mode-comparison.html
Layout: [Light Mode] | [Dark Mode] side-by-side
Output:
MODE COMPARISON READY
─────────────────────
Colors compared:
Background: #ffffff ↔ #1a1a2e
Foreground: #1a1a2e ↔ #f5f5f5
Primary: #3B82F6 ↔ #60A5FA
...
Contrast check:
Primary on background: 4.8:1 ✓ (AA pass)
Text on background: 7.2:1 ✓ (AAA pass)
Opening comparison in browser...
AskUserQuestion (after preview opens):
header: "Mode Preview"
question: "Bekijk de light/dark vergelijking in browser. Tevreden?"
options:
- label: "Ja, opslaan (Recommended)", description: "Bevestig mode configuratie"
- label: "Aanpassen", description: "Wijzig kleuren"
AskUserQuestion:
header: "Delete"
question: "Weet je zeker dat je de theme wilt verwijderen?"
options:
- label: "Ja, verwijderen", description: "Verwijder theme sectie uit project.json"
- label: "Nee, annuleren (Recommended)", description: "Behoud theme"
multiSelect: false
Als "Ja":
.project/project.jsontheme sectie naar leeg object {}Voer deze checks uit NA elke write operatie (Create/Update/Extract/Modes).
POST-FLIGHT CHECK
════════════════════════════════════════════════
1. File Validation
File: [✓|✗] .project/project.json - [exists|missing|empty]
Theme: [✓|✗] theme sectie - [populated|empty|missing]
Format: [✓|✗] JSON - [valid|corrupt]
2. Content Validation
Sections:
[✓|✗] colors - [present|missing] (main, accent, semantic)
[✓|✗] typography - [present|missing] (families, sizes)
[✓|✗] spacing - [present|missing] (base, scale)
[✓|✗] breakpoints - [present|missing]
[✓|✗] borderRadius - [present|missing]
[✓|✗] shadows - [present|missing]
[✓|✗] motion - [present|skipped|missing] (durations, easings)
[✓|✗] interactions - [present|skipped|missing] (focusRing, hover, active)
[✓|✗] modes - [light only|light+dark|missing]
[✓|✗] cssVars - [present|missing]
3. Value Validation
Colors:
[✓|✗] All color values valid (#RRGGBB hex or oklch(L C H) format)
[✓|✗] No empty values
[✓|✗] Each color has token, value, usage
[✓|⚠] Semantic tokens use var() refs — not raw hex (warning only: existing setups may have raw values)
[✓|⚠] Semantic completeness — success, warning, error, info gedefinieerd en onderling distinct (⚠ als één ontbreekt of als twee dezelfde primitive ref gebruiken)
Typography:
[✓|✗] Font families have fallbacks
[✓|✗] Sizes have token, size, lineHeight
Spacing:
[✓|✗] All values numeric with unit
[✓|✗] Scale entries have token, value, usage
Modes:
[✓|✗] Light mode :root CSS present and valid
[✓|✗] Dark mode .dark CSS present (if configured)
[✓|✗] Dark mode contrast ratios acceptable (AA minimum)
[✓|✗] No unfilled placeholders in mode CSS blocks
Motion (if configured):
[✓|✗] Duration values end with 'ms' or 's'
[✓|✗] Each duration has token, value, usage
[✓|✗] Easing values are valid cubic-bezier or keyword
Interactions (if configured):
[✓|✗] Focus ring has width, color, offset
[✓|✗] Hover transition references valid motion tokens
[✓|✗] Active transform is valid CSS transform
4. Export Validation
CSS Export:
[✓|✗] cssVars field present and non-empty
[✓|✗] :root block syntax valid
[✓|✗] Matches structured token data
[✓|✗] All variables populated (no {placeholders})
5. JSON Integrity
Integrity:
[✓|✗] project.json is valid JSON
[✓|✗] Other secties ongewijzigd (concept, stack, data, endpoints, decisions)
[✓|✗] Theme sectie matches schema
Post-flight Samenvatting:
════════════════════════════════════════════════
POST-FLIGHT RESULT
════════════════════════════════════════════════
File: [✓ PASS | ✗ FAIL]
Content: [✓ PASS | ✗ FAIL] - {N}/{M} sections
Values: [✓ PASS | ⚠ WARNINGS | ✗ FAIL]
Modes: [✓ PASS | ⚠ Light only | ✗ FAIL] - {light|light+dark}
Motion: [✓ PASS | ⚠ Skipped | ✗ FAIL]
Interactions: [✓ PASS | ⚠ Skipped | ✗ FAIL]
Export: [✓ PASS | ✗ FAIL]
Integrity: [✓ PASS | ✗ FAIL]
Status: [→ Complete | ⚠ Warnings: {list} | ✗ Recovery needed]
════════════════════════════════════════════════
On Failure:
AskUserQuestion:
header: "Post-flight Failed"
question: "Validatie vond problemen: {issues}. Wat nu?"
options:
- label: "Auto-fix (Recommended)", description: "Probeer automatisch te repareren"
- label: "Handmatig fixen", description: "Bekijk en fix problemen"
- label: "Negeren", description: "Accepteer output ondanks problemen"
multiSelect: false
Always runs after successful post-flight validation. Ensures theme tokens are available in the project's styling infrastructure, not just in project.json.
Bij Updates: diff de oude token waarden (voor de update) tegen de nieuwe waarden. Gebruik dit diff om:
Styling approach detectie:
Detect styling approach from package.json + CSS files:
tailwindcss present → check voor Tailwind 4 CSS-first OF klassieke config (zie stap 2)Tailwind project:
@theme inline. Als gevonden: update de :root CSS variables in dat bestand direct — dit IS de Tailwind config in v4
tailwind.config.{js,ts,mjs} als er geen @theme inline iscolors: map color tokens to Tailwind color keysspacing: map custom spacing tokens (skip if standard 4px scale)borderRadius: map radius tokensboxShadow: map shadow tokensfontFamily: map typography familiesNon-Tailwind project:
src/styles/theme.css) from theme.cssVars:root { ... }:
--color-dark: #1a1a2e;
--color-accent-primary: #3B82F6;--color-success: var(--color-accent-primary);var(--color-{meest-passende-primitive}) — match op kleurgroep of gebruikersbedoelingNo project detected (no package.json, no source files):
"Geen project gedetecteerd — theme alleen opgeslagen in project.json."THEME INFRASTRUCTURE
════════════════════════════════════════════════
Approach: {Tailwind | CSS Variables | Skipped (no project)}
Config: {tailwind.config updated | theme.css generated | —}
Tokens: {N} color, {M} spacing, {P} typography, {Q} motion, {R} interaction tokens synced
════════════════════════════════════════════════
After post-flight validation, check if existing website code uses the theme.
Skip this phase entirely for View, Delete, or when no website code exists.
# Glob for frontend source files
# src/**/*.{tsx,jsx,astro,vue}, app/**/*.{tsx,jsx}, *.html
"Geen website code gevonden — theme opgeslagen." → proceed to Output FormaatScan the codebase for theme integration:
tailwind.config for custom theme extensions matching project.json tokens:root blocks with CSS custom properties#hex, rgb(), hsl(), bg-[#, text-[#bg-primary, text-accent, var(--, theme class referencesp-[16px], gap-[24px], arbitrary Tailwind values#C89B3C naar #0AC8B9, scan dan voor resterende #C89B3C referenties in component files en arbitrary Tailwind values (bg-[#C89B3C], shadow-[...rgba(200,155,60,...)], etc.)Tally results:
WEBSITE SYNC CHECK
════════════════════════════════════════════════
Bestanden gescand: {N}
Theme integratie: {Tailwind config | CSS vars | Geen}
Hardcoded kleuren: {N} bestanden, {M} waarden
Oude waarden gevonden: {N} bestanden, {M} referenties (bij updates)
Theme token gebruik: {N} bestanden, {M} referenties
════════════════════════════════════════════════
If code already uses theme correctly (hardcoded count ≤ 3 AND theme tokens present):
✓ Theme in sync — website gebruikt al design tokens.
→ Proceed to Output Formaat.
If code has hardcoded values (hardcoded count > 3 OR no theme token usage):
AskUserQuestion:
header: "Website Sync"
question: "Er zijn {N} bestanden met hardcoded kleuren/styling die het theme niet gebruiken. Wil je restylen?"
options:
- label: "Ja, restyle alles (Recommended)", description: "Vervang hardcoded waarden door theme tokens in alle {N} bestanden"
- label: "Extract als theme", description: "Formaliseer bestaande kleuren/waarden als theme tokens (reverse sync)"
- label: "Toon bestanden", description: "Bekijk welke bestanden geraakt worden voordat je beslist"
- label: "Nee, alleen theme opslaan", description: "Sla over — handmatig later"
multiSelect: false
If "Extract als theme": Run the Extraheren route (FASE 2 → Route: Extraheren) to parse existing hardcoded color/spacing values from component files as theme tokens. After extraction, merge into project.json#theme (existing tokens take priority, extracted values fill gaps), re-run Theme Infrastructure Sync (X.6). This formalizes existing design choices rather than overwriting them.
If "Toon bestanden": Show file list with hardcoded value count per file, then re-ask with "Ja, restyle alles" / "Selectief kiezen" / "Nee" options.
Step 1: Replace hardcoded values
Per component file, replace hardcoded values with theme tokens:
| Hardcoded | → Theme Token |
|---|---|
bg-[#3B82F6] | bg-primary |
text-[#1a1a2e] | text-foreground |
p-[16px] | p-4 |
gap-[32px] | gap-8 |
#hex in inline style | var(--color-primary) |
Map each hardcoded value to the closest theme token by color distance / value match.
Step 2: Verification
After restyle, quick scan for remaining hardcoded values. Report count.
WEBSITE SYNC
════════════════════════════════════════════════
Bestanden gescand: {N}
Bestanden gerestyled: {M}
Vervangingen: {X} hardcoded waarden → theme tokens
Gewijzigde bestanden:
✓ {file} — {N} kleuren gerestyled
✓ {file} — {N} kleuren + spacing
✓ tailwind.config.ts — theme extension toegevoegd/bijgewerkt
Resterend: {R} hardcoded waarden (handmatige review aanbevolen)
════════════════════════════════════════════════
Na succesvolle actie:
THEME [AANGEMAAKT/BIJGEWERKT/VERWIJDERD]
Locatie: .project/project.json (theme sectie)
| Categorie | Tokens |
|-----------|--------|
| Colors | {N} (main: {n}, accent: {n}, semantic: {n}) |
| Typography | {N} (families: {n}, sizes: {n}) |
| Spacing | {N} |
| Breakpoints | {N} |
| Border Radius | {N} |
| Shadows | {N} |
| Motion | {N} (durations: {n}, easings: {n}) |
| Interactions | {N} (focusRing, hover, active) |
| Modes | {light/dark/both} |
| CSS Vars | {present/missing} |
Theme tokens ready in project.json voor downstream consumption.
Next steps:
1. /frontend-design {page} → bouw een pagina met deze tokens
2. /frontend-convert → converteer een design met deze tokens
3. /frontend-tokens → bekijk of update tokens later
4. /frontend-check → check performance en SEO
5. /frontend-check --scope=a11y → accessibility audit
Zie ook:
skills/shared/VALIDATION.mdvoor algemene recovery patterns.
| Error | Recovery |
|---|---|
| Config file niet gevonden | Offer manual path input |
| Parse error in config | Toon raw content, vraag format hint |
| Geen tokens gevonden | Offer defaults + manual input |
| Tailwind v3 vs v4 verschil | Detecteer versie, pas parser aan |
| Error | Recovery |
|---|---|
| Permission denied | Suggest alternative path |
| Disk full | Warn, suggest cleanup |
| Directory niet creeerbaar | Offer manual creation instructions |
| JSON parse error | Backup corrupt file, maak nieuw |
| Error | Auto-fix | Manual |
|---|---|---|
| Invalid hex code | Suggest closest valid | Show invalid, ask correction |
| Missing section | Add with defaults | Ask for values |
| Empty value | Use default | Ask for value |
| CSS syntax error | Re-generate cssVars | Show error location |
| Invalid JSON | Re-generate file | Show parse error |
Note: Rollback wordt afgehandeld door Claude Code's ingebouwde "Rewind" functie.
Zie ook:
skills/shared/DEVINFO.mdvoor volledige specificatie.
Bij skill start:
{
"currentSkill": {
"name": "frontend-tokens",
"phase": "PREFLIGHT",
"startedAt": "ISO timestamp"
}
}
Update devinfo bij elke fase transitie:
PREFLIGHT → ACTION_SELECTACTION_SELECT → CREATE|UPDATE|EXTRACT|MODES|DELETECONFIRM → POSTFLIGHTPOSTFLIGHT → COMPLETEUpdate .project/project.json → design.principles with concrete design system decisions:
project.json → design.principles (skip if design section doesn't exist){ "name": "{base}px spacing scale", "description": "Base unit {base}px, scale: {scale values}" }{ "name": "Font: {heading} + {body}", "description": "Headings: {heading family}, Body: {body family}" }{ "name": "Color palette: {scheme}", "description": "Primary: {main color}, Accent: {accent color}" }name — add new principles, never overwrite existing user-defined principlesproject.json (only mutate design.principles)Bij succesvolle completion:
{
"handoff": {
"from": "frontend-tokens",
"to": null,
"data": {
"themeLocation": ".project/project.json#theme",
"preset": "Anthropic Style | Custom",
"tokens": {
"colors": 12,
"typography": 3,
"spacing": 9
},
"modes": ["light", "dark"],
"cssVarsPresent": true
}
}
}
Deze skill garandeert bij completion:
.project/project.json bevat een gevulde theme sectietheme bevat valid sections: colors, typography, spacing, breakpoints, borderRadius, shadows, modes, cssVarstheme.cssVars bevat syntactisch valide CSS variables stringAndere skills consumeren theme data als volgt:
project.json → theme.cssVarsproject.json → theme.colors, theme.typography, etc.project.json → theme.modes.light / theme.modes.darkskills/frontend-tokens/references/THEME_TEMPLATE.md - Referentie voor token categorien en naming conventionsskills/shared/DASHBOARD.md - project.json schema en merge-strategieskills/shared/VALIDATION.md - Pre/post-flight validation templatesskills/shared/DEVINFO.md - Session state trackingDit command moet NOOIT:
theme muteren)Dit command moet ALTIJD: