Skip to main content

Theming

Theming

Customize colors, typography, and spacing in Classic Theme

Tags: theming customization design-tokens

Classic Theme uses CSS custom properties (variables) for theming, making it easy to customize colors, typography, and spacing.

How Theming Works​

Classic Theme's theming system is built on three layers:

  1. Design Tokens - Core values defined in @classic-homes/theme-tokens
  2. CSS Variables - Runtime customizable properties
  3. Tailwind Preset - Utility classes mapped to CSS variables

Customizing Colors​

Override CSS variables in your global stylesheet:

:root {
  /* Primary color */
  --primary: 220 90% 56%;
  --primary-foreground: 0 0% 100%;

  /* Background colors */
  --background: 0 0% 100%;
  --foreground: 222 47% 11%;

  /* Card colors */
  --card: 0 0% 100%;
  --card-foreground: 222 47% 11%;

  /* Muted colors */
  --muted: 210 40% 96%;
  --muted-foreground: 215 16% 47%;

  /* Border and input */
  --border: 214 32% 91%;
  --input: 214 32% 91%;
  --ring: 220 90% 56%;
}
HSL Format

Colors use HSL values without the hsl() wrapper. This allows Tailwind to add opacity modifiers like bg-primary/50.

Dark Mode​

Add dark mode support by defining variables for the dark color scheme:

@media (prefers-color-scheme: dark) {
  :root {
    --background: 222 47% 11%;
    --foreground: 210 40% 98%;

    --card: 222 47% 11%;
    --card-foreground: 210 40% 98%;

    --primary: 217 91% 60%;
    --primary-foreground: 222 47% 11%;

    --muted: 217 33% 17%;
    --muted-foreground: 215 20% 65%;

    --border: 217 33% 17%;
    --input: 217 33% 17%;
    --ring: 217 91% 60%;
  }
}

Or use a class-based approach:

.dark {
  --background: 222 47% 11%;
  --foreground: 210 40% 98%;
  /* ... other dark mode variables */
}

Available CSS Variables​

Semantic Colors​

Variable Description
--background Page background color
--foreground Default text color
--card Card background color
--card-foreground Card text color
--popover Popover background color
--popover-foreground Popover text color
--primary Primary action color
--primary-foreground Text on primary color
--secondary Secondary action color
--secondary-foreground Text on secondary color
--muted Muted/subtle background
--muted-foreground Muted text color
--accent Accent/highlight color
--accent-foreground Text on accent color
--destructive Destructive/error color
--destructive-foreground Text on destructive color

Status Colors​

Variable Description
--success Success state color
--success-foreground Text on success color
--warning Warning state color
--warning-foreground Text on warning color
--error Error state color
--error-foreground Text on error color
--info Info state color
--info-foreground Text on info color

UI Elements​

Variable Description
--border Default border color
--input Input border color
--ring Focus ring color
--radius Default border radius

Control colors​

Variable Description
--secondary Filled sage action color (Button variant="secondary")
--secondary-border 2px ring around filled sage controls (Toolbar overrides it)
--secondary-soft Lighter sage for icon rests and dividers (ViewToggle)
--outline Outline button border
--outline-hover Outline button border while hovered (over the primary fill)
--toolbar Toolbar background
--toolbar-foreground Toolbar text
--toolbar-border Ring for filled controls sitting on the toolbar

Typography​

The theme ships Figtree (sans) and Spectral (serif). h1 is set in Spectral 500 italic (2.5rem / 2rem); h2-h6 are Figtree 600.

The scale is applied twice on purpose. theme-styles/base.css sets it inside @layer classic-theme.base, and the Tailwind preset emits the same rules through addBase. The second copy matters in any app that runs @tailwind base: preflight's h1..h6 { font-size: inherit; font-weight: inherit } is unlayered, so it outranks the layered theme rule (@csstools/postcss-cascade-layers preserves that ordering with specificity boosts). Without the preset's copy, bare headings render at body size. The text-h1 … text-h6 utilities carry the same size / line-height / weight when you need the scale on another element.

Customize the families:

:root {
  --font-sans: 'Inter', system-ui, sans-serif;
  --font-serif: 'Georgia', serif;
  --font-mono: 'Fira Code', monospace;
}

Spacing and Sizing​

Adjust the border radius scale:

:root {
  --radius: 0.5rem; /* Base radius */
}

Components use this base value with modifiers:

  • rounded-sm = calc(var(--radius) - 0.25rem)
  • rounded-md = var(--radius)
  • rounded-lg = calc(var(--radius) + 0.25rem)

Using in Components​

Access theme values in your components with Tailwind utilities:

<div class="bg-background text-foreground">
  <h1 class="text-primary">Themed Heading</h1>
  <p class="text-muted-foreground">Muted text</p>
  <button class="bg-primary text-primary-foreground"> Primary Button </button>
</div>

Or use CSS variables directly:

<style>
  .custom-element {
    background-color: hsl(var(--primary));
    color: hsl(var(--primary-foreground));
  }
</style>

Brand Customization Example​

Here's a complete example customizing the theme for a brand:

:root {
  /* Custom brand colors */
  --primary: 142 76% 36%; /* Green primary */
  --primary-foreground: 0 0% 100%;

  --secondary: 142 76% 96%; /* Light green */
  --secondary-foreground: 142 76% 20%;

  --accent: 38 92% 50%; /* Orange accent */
  --accent-foreground: 0 0% 100%;

  /* Custom radius for softer look */
  --radius: 0.75rem;
}

Next Steps​

On this page