name: generating-css description: Generate production CSS from design tokens using Tailwind CSS or vanilla CSS. Maps tokens to Tailwind theme config and establishes CSS architecture.
Generating CSS
Goal
Transform design tokens into usable CSS — either as a Tailwind theme configuration or as vanilla CSS custom properties with utility classes. The output should be production-ready, connected to the token system, and free of redundant or dead styles.
When to Use
- After design tokens have been defined (via
designing-ui-system). - When scaffolding a new frontend project that needs its styling foundation.
- When migrating from ad-hoc styles to a token-based CSS architecture.
Instructions
1. Establish CSS Architecture Layers
CSS flows through four layers, each building on the previous:
Tokens → Utilities → Components → Layouts
- Tokens: raw design values (colors, spacing, type sizes) as CSS custom properties or Tailwind config.
- Utilities: single-purpose classes (Tailwind provides these; in vanilla CSS, define sparingly).
- Components: multi-property styles for repeated UI patterns (button, card, badge). Only extract when a pattern repeats 3+ times.
- Layouts: page-level grid and container styles.
2. Map Tokens to Tailwind Configuration
Extend the Tailwind theme — do not override defaults wholesale. This preserves useful defaults while adding project-specific tokens:
// tailwind.config.ts
import type { Config } from 'tailwindcss';
export default {
content: ['./src/**/*.{ts,tsx,html}'],
darkMode: 'class',
theme: {
extend: {
colors: {
// Map token names to HSL values from the design system
primary: {
50: 'hsl(var(--color-primary-50) / <alpha-value>)',
100: 'hsl(var(--color-primary-100) / <alpha-value>)',
200: 'hsl(var(--color-primary-200) / <alpha-value>)',
300: 'hsl(var(--color-primary-300) / <alpha-value>)',
400: 'hsl(var(--color-primary-400) / <alpha-value>)',
500: 'hsl(var(--color-primary-500) / <alpha-value>)',
600: 'hsl(var(--color-primary-600) / <alpha-value>)',
700: 'hsl(var(--color-primary-700) / <alpha-value>)',
800: 'hsl(var(--color-primary-800) / <alpha-value>)',
900: 'hsl(var(--color-primary-900) / <alpha-value>)',
},
// Repeat for secondary, neutral, semantic colors
},
fontFamily: {
sans: ['Source Sans 3', 'system-ui', 'sans-serif'],
heading: ['Fraunces', 'Georgia', 'serif'],
},
fontSize: {
xs: ['0.75rem', { lineHeight: '1.6' }],
sm: ['0.875rem', { lineHeight: '1.5' }],
base: ['1rem', { lineHeight: '1.6' }],
lg: ['1.25rem', { lineHeight: '1.4' }],
xl: ['1.563rem', { lineHeight: '1.3' }],
'2xl': ['1.953rem', { lineHeight: '1.2' }],
},
spacing: {
// Extend with project tokens if Tailwind defaults don't match
'4.5': '1.125rem', // 18px, if needed
},
borderRadius: {
sm: '4px',
md: '8px',
lg: '16px',
},
boxShadow: {
// Use brand-hue-tinted shadows, not pure black
subtle: '0 1px 3px hsl(var(--color-primary-900) / 0.08)',
medium: '0 4px 12px hsl(var(--color-primary-900) / 0.12)',
strong: '0 8px 24px hsl(var(--color-primary-900) / 0.16)',
},
},
},
plugins: [],
} satisfies Config;
3. Store Token Values in CSS Custom Properties
Even with Tailwind, define tokens as CSS custom properties so they can be consumed by non-Tailwind contexts (third-party components, canvas rendering, emails):
/* tokens.css — imported before Tailwind layers */
:root {
--color-primary-50: 262 50% 95%;
--color-primary-100: 262 52% 90%;
/* ... full palette ... */
--color-surface: var(--color-neutral-50);
--color-on-surface: var(--color-neutral-900);
}
.dark {
--color-surface: var(--color-neutral-900);
--color-on-surface: var(--color-neutral-50);
/* Reduce saturation for dark mode primary */
--color-primary-500: 262 55% 60%;
}
4. Component Class Extraction
Only extract a component class when the same combination of utilities appears 3+ times AND the instances are semantically the same component:
/* components.css */
@layer components {
.btn-primary {
@apply inline-flex items-center justify-center rounded-md px-4 py-2
bg-primary-600 text-white font-medium text-sm
hover:bg-primary-700 focus-visible:outline-2
focus-visible:outline-offset-2 focus-visible:outline-primary-600
transition-colors;
}
}
Three buttons with the same style = extract. A button and a link that happen to share some padding = don't extract.
5. Dark Mode Implementation
Use the class strategy (not media) for dark mode in Tailwind. This allows programmatic toggling:
<!-- Toggle class on <html> or <body> -->
<html class="dark">
/* Tailwind handles dark: variants automatically */
/* For CSS custom properties, use the .dark selector */
.dark {
--color-surface: 262 8% 10%;
--color-on-surface: 262 7% 90%;
}
Dark mode rules:
- Swap the token values at the custom property level, not in individual components.
- Reduce color saturation by 10-20% for dark mode.
- Don't use pure black (#000) as dark background — use neutral-900 (slightly tinted).
- Shadows are ineffective on dark backgrounds; use subtle borders or elevation instead.
6. Responsive Utilities
For project-specific responsive patterns that Tailwind doesn't cover, add custom utilities:
@layer utilities {
.text-fluid-lg {
font-size: clamp(1.25rem, 1rem + 1vw, 1.563rem);
}
.container-prose {
max-width: 65ch;
margin-inline: auto;
padding-inline: var(--space-4);
}
}
Constraints
✅ Do
- Use design tokens as the single source of truth. Every color, size, and spacing value in CSS should trace back to a token.
- Extend the Tailwind theme rather than overriding it. Use
theme.extendso default utilities remain available. - Store token values as CSS custom properties so they work outside Tailwind contexts.
- Prefer Tailwind utility classes in markup over custom CSS for one-off styles.
- Extract component classes only when a utility pattern repeats 3+ times for the same semantic component.
- Use the class strategy for dark mode so it can be toggled programmatically.
- Include line-height in font-size token definitions so they travel together.
- Use HSL values without the
hsl()wrapper in custom properties so Tailwind's opacity modifier syntax works.
❌ Don't
- DO NOT write vanilla CSS that duplicates what Tailwind utilities already provide. If you're writing
.mt-4 { margin-top: 1rem; }alongside Tailwind, something has gone wrong. - DO NOT use
@applyfor everything. Extracting every element into an@applyclass defeats the purpose of utility-first CSS — you end up maintaining a parallel stylesheet with none of the benefits. - DO NOT generate CSS without connecting it to the design token system. Hardcoded hex values, pixel sizes, or magic numbers in CSS files mean the tokens are being bypassed.
- DO NOT use
!important. If specificity conflicts exist, fix the cascade order or layer structure instead. The only acceptable!importantis inside utility definitions (which Tailwind handles internally). - DO NOT create a component class for a pattern that appears only once or twice. Use utilities inline instead. Premature extraction creates classes no one remembers exist.
- DO NOT override Tailwind's default
theme(withoutextend). This removes useful defaults likeauto,full,screen, and fractional spacing values. - DO NOT generate a massive CSS file with "just in case" component classes. Every class should correspond to a component that exists in the UI right now.
- DO NOT mix design paradigms: pick either utility-first (Tailwind) or BEM/SMACSS (vanilla). Mixing both creates confusion about where styles live and which approach to use for new components.
- DO NOT use Tailwind's
@applywith responsive or state variants (@apply hover:bg-blue-500). This doesn't work as expected and creates brittle CSS. Apply state styles as separate declarations or use utilities in markup. - DO NOT ignore the CSS cascade order. Load styles in order: reset/base -> tokens -> utilities -> components -> layouts. Misordered layers cause specificity fights that lead to
!importanthacks.
Output Format
tailwind.config.ts(or.js) with theme extensions mapped to design tokens.tokens.csswith CSS custom properties for all design tokens.components.css(if needed) with extracted component classes using@layer components.- Brief notes explaining which tokens map to which Tailwind utilities.
Dependencies
designer/designing-ui-system/SKILL.md— provides the design tokens that this skill consumes.../../frontend/scaffolding-frontend/SKILL.md— provides the project structure where these CSS files live.