Skip to content

Custom Themes

How to author a custom Cynosure theme by overriding semantic CSS tokens for a data-theme selector.

A Cynosure theme is a CSS file that overrides the semantic tokens for a data-theme selector. That’s the whole API.

Create src/themes/brand.css:

[data-theme='brand'] {
/* Accent family — buttons, links, focus rings. */
--cynosure-color-accent-solid: #0f766e;
--cynosure-color-accent-solid-hover: #115e59;
--cynosure-color-accent-soft: rgba(15 118 110 / 0.12);
--cynosure-color-accent-soft-hover: rgba(15 118 110 / 0.2);
--cynosure-color-accent-fg: #0f766e;
/* Surface family — backgrounds, cards, inputs. */
--cynosure-color-surface-default: #ffffff;
--cynosure-color-surface-subtle: #f6fbfa;
--cynosure-color-surface-muted: #ecf5f3;
/* Foreground family — body text, headings, borders. */
--cynosure-color-fg-default: #042f2e;
--cynosure-color-fg-muted: #134e4a;
--cynosure-color-fg-subtle: #5eead4;
--cynosure-color-border-default: #d6e4e1;
/* Optional: signal the browser colour-scheme so native controls match. */
color-scheme: light;
}

Only override what you need. Unset tokens inherit from base.css.

If you’re authoring a theme package:

{
"name": "my-brand-theme",
"exports": {
"./cynosure": "./dist/cynosure.css"
},
"sideEffects": ["./dist/*.css"]
}

Consumers import it once:

import 'my-brand-theme/cynosure';

Add the theme name to your ThemeProvider:

<ThemeProvider themes={['light', 'dark', 'brand']} defaultTheme="brand">
{children}
</ThemeProvider>

ThemeProvider sets data-theme="brand" on <html>, and the override CSS takes effect.

Minimum tokens you probably want to override:

FamilyTokens
Accent*-solid, *-solid-hover, *-soft, *-soft-hover, *-fg
Surface*-default, *-subtle, *-muted
Foreground*-default, *-muted, *-subtle
Border*-default, *-muted
Feedbacksuccess.solid, warning.solid, danger.solid
Focusshadow-focus-ring

Cynosure ships a contrast audit script that walks compiled CSS and asserts WCAG 2.1 AA pairs. Add your theme to scripts/audit-contrast.mjs to run the check against CI:

Terminal window
pnpm audit:contrast

Sometimes you want a theme only inside a specific container (for example a “preview” area). Set data-theme on an element other than <html>:

<div data-theme="brand">
{/* children here render with the brand palette */}
</div>

All Cynosure components read CSS custom properties through normal cascade, so scoping “just works”.