@cf/ui

Colors

Semantic colour tokens on Radix Gray, with red and green for status, switched by a .dark class.

The palette

Almost everything is Radix Colors' Gray scale, used the way Radix's own guide describes: steps 1 to 2 for backgrounds, 3 to 5 for component surfaces, 6 to 8 for borders, 11 to 12 for text. Because gray.css and gray-dark.css already scope their variables under :root and .dark, a token like --foreground: var(--gray-12) resolves correctly in both modes with a single declaration.

  • --background

    Page surface.

  • --foreground

    Default text on the page.

  • Aa

    --card

    Raised panels; brighter than the page in both modes.

  • Aa

    --popover

    Menus, selects and other floating surfaces.

  • Aa

    --primary

    The strongest fill, used by the default button.

  • Aa

    --secondary

    Quiet filled controls.

  • Aa

    --muted

    Subdued backgrounds for secondary content.

  • Aa

    --accent

    Hover and highlighted states in menus and ghost buttons.

  • Aa

    --marker

    Highlighter yellow for emphasised text.

  • Aa

    --destructive

    Errors and dangerous actions.

  • Aa

    --success

    Confirmation and success states.

  • --border

    Default borders and dividers.

  • --input

    Borders on form controls.

  • --ring

    Focus rings.

Where it isn't automatic

A few tokens use a different step per mode, so they need an explicit .dark override:

  • Card and popover are pure white in light mode and gray-2 in dark. In both cases that is a step brighter than the page, which is the elevation convention I follow.
  • Destructive and success don't use Radix step 9. Step 9 is tuned for vividness rather than contrast, and against white text red and green fall below 4.5:1. Step 11 in light and step 8 in dark both clear AA, at roughly 4.7 to 5.4:1.
  • Marker is a hex yellow, #ffd34d in light and #6b5200 in dark. It is not a Radix step; no yellow scale is imported.

What's missing

There is no brand colour in the theme. The site's blue lives in styles/globals.css as a site token, so components stay neutral. Opacity modifiers like bg-destructive/10 are used for tinted states; they are not separate tokens.

Rules

The same rules ship in DESIGN.md and the MCP server, for the agents building with this.

  • must

    When you use a filled token as a background, set text with its -foreground pair, for example bg-destructive text-destructive-foreground.

    pair-fill-with-foreground

  • must

    Use destructive and success only to mean error or danger and success; do not use them as decoration.

    status-colours-for-status

  • must

    If you change --destructive or --success, keep white text on them at WCAG AA (4.5:1) in both modes.

    That is why they use Radix step 11 in light and step 8 in dark instead of step 9.

    keep-status-contrast

  • should

    Reserve bg-marker with text-marker-foreground for highlighter-style emphasis, not for fills or status.

    marker-is-highlight

  • should

    Use bg-card or bg-popover for anything that sits above the page, and bg-background for the page itself.

    surfaces-for-elevation