Theming

Components read semantic roles, never raw colors. Override roles in your own CSS to retheme. See Colors for the full list.

:root {
  /* Remap a role and every component follows */
  --primary: var(--coral-500);
  --on-primary: var(--clay-950);
  --primary-container: var(--coral-100);
  --on-primary-container: var(--coral-800);

  /* Surfaces */
  --background: var(--cream);
  --surface: var(--paper);
  --surface-sunken: #f1e4d2;

  --radius: 1.6rem;
}

Rebranding

Swap a role to another tonal scale. Run npm run tokens afterwards to re-check contrast.

:root {
  /* Make primary blue for a different product */
  --primary: var(--blue-600);
  --on-primary: var(--paper);
  --primary-container: var(--blue-100);
  --on-primary-container: var(--blue-800);
}

Dark mode

Dark mode is a .dark class on html that remaps the same roles. Clay shadows are themeable too: highlights get dimmer and drops get deeper. Use next-themes to toggle it.

.dark {
  --background: #1d120d;      /* warm chocolate, not black */
  --surface: #2b1b14;         /* raised = lighter */
  --surface-sunken: #140b08;  /* wells = darker */
  --on-surface: var(--cream);

  --primary-container: #4a1c1a;
  --on-primary-container: var(--coral-200);

  /* Clay depth */
  --clay-hi: rgb(255 255 255 / 0.07);
  --clay-lo: rgb(0 0 0 / 0.35);
  --clay-drop: rgb(0 0 0 / 0.7);
}

Changing the palette

Edit the brand hexes in scripts/palette.mjs, then run npm run tokens. It regenerates app/tokens.css and fails if any role pair drops below 3:1.