CSS is Awesome

Authoring a theme

A theme is a single CSS file declaring the 123 tokens in the contract. The validator guarantees a clean one-file swap.

Overview

The token contract

The machine-readable source of truth is scripts/theme-contract.json. It lists every CSS custom property a theme must declare. The validator reads this file — if you add a token to the base library, you add it to the contract, and every theme then has to declare it.

The tokens fall into these categories:

See /docs/tokens for the full gallery with live swatches and current values for each shipped theme.

File structure

A theme file is a font @import (optional) plus a single :root block that sets every required token. Preserve the commented section headers — they make the file scannable and keep the contract visually grouped.

/* ============================================================
   THEME — My Brand
   ============================================================ */

@import url('https://fonts.googleapis.com/css2?family=Inter:wght@300..700&display=swap');

:root {
  /* Surfaces */
  --paper:        #FFFFFF;
  --paper-raised: #F7F7F5;
  --paper-sunk:   #EEEEEA;
  --paper-glass:  rgba(255,255,255,0.80);

  /* Ink */
  --ink:       #0A0A0A;
  --ink-soft:  #4A4A4A;
  --ink-faint: #8A8A8A;

  /* Lines, Primary, Seal, Accent, Code, Type, */
  /* Radius, Shadow, Blur, Glow, Motion... */

  /* Semantic aliases — bridge to library names */
  --surface-default: var(--paper);
  --surface-raised:  var(--paper-raised);
  --text-primary:    var(--ink);
  --border-default:  var(--hair-soft);
}

If you are contributing your theme to the shipped consolidated file instead, the body stays identical — wrap it in a [data-theme="<name>"] selector:

[data-theme="my-brand"] {
  /* same 123 token declarations */
  --paper: #FFFFFF;
  /* ... */
}

Step by step

  1. Copy public/themes/press/theme.css (or any shipped theme) as a starting point. Press is a good editorial baseline; Cupertino is a good rounded/soft baseline; Terminal is a good dark-mode baseline.
  2. Rename the file path to public/themes/<your-theme>/theme.css.
  3. Change every token value — leave every token name. Work category by category: start with surfaces (--paper, --paper-raised, --paper-sunk), then ink (--ink, --ink-soft, --ink-faint), then the rest of the palette, then type, then radii and shadows.
  4. Keep the semantic aliases intact: --surface-default: var(--paper), --text-primary: var(--ink), etc. These bridge library names to your native palette. Change the right-hand side only if your mood genuinely requires a different mapping (e.g. you want --surface-default to resolve to --paper-raised).
  5. Run the validator against your file:
    $ node scripts/theme-validator.js public/themes/<your-theme>/theme.css
    It prints every missing token. Fix them and re-run until the output reads OK.
  6. Once the single file validates, register the theme in the picker. Add an entry (id + label) to the THEMES array in src/components/ThemePicker/ThemePicker.tsx, and add the id to the VALID_THEMES set in src/app/layout.tsx.
  7. If you are contributing upstream, also add a [data-theme="<name>"] block to the consolidated public/theme.css with the same body. The per-theme file is kept for standalone deploys; the consolidated file powers the docs site picker.

The validator

scripts/theme-validator.js is zero-dependency Node. It auto-detects per-file vs consolidated input and reports missing tokens with a non-zero exit code on failure.

Exit codes:

Wire npm run validate-themes into your pre-commit or CI step and authorship becomes a closed loop: if it passes, it ships.

Design guidance

Testing

Shipping

For a standalone deploy, drop your theme file in your own public/themes/<your-theme>/theme.css and link it from your HTML before cia.css. No registration required — the base library reads tokens from whatever you put on :root.

To contribute your theme upstream, open a PR using the Theme Submission template at .github/ISSUE_TEMPLATE/theme_submission.yml. See the full checklist in CONTRIBUTING-THEMES.md at the repo root — it covers validator output, screenshots per page, contrast notes and the picker registration diff.

Theme