Site-Native Themes
Maintain theme token files in your own repo and validate them against the Turbo Themes token contract.
Site-Native Themes
A site-native theme is a theme token file that lives in your repo — not in
packages/core/src/themes — and is loaded as ordinary CSS:
[data-theme='acme'] {
--turbo-bg-base: #ffffff;
--turbo-text-primary: #1a1a2e;
/* … */
}
Use one when you want a bespoke palette to participate in the token system: every
component and integration that reads --turbo-* custom properties (--turbo-bg-base,
--turbo-state-success-text, --turbo-brand-primary, …) re-skins automatically. Use a
flavor instead when the theme should ship to everyone through the published packs.
The contract
Turbo Themes publishes the exact set of tokens a deep integration reads — the same set the CSS generator emits for every theme:
- Required (90 tokens): surfaces (
--turbo-bg-*), text inks, brand and state colors — including the audited--turbo-state-*-textinks — borders, links, headings, code, and table tokens. - Optional (18 tokens): spacing, elevation, animation, and opacity tokens that some themes customize. Declare them if you use them; consumers fall back gracefully.
The contract is generated from the CSS generator itself, so it can never drift from what components actually read.
Validating
CLI
npx turbo-themes validate-native src/styles/acme.css
✅ src/styles/acme.css: 90/90 contract tokens declared, all good.
# exits non-zero on failure, so CI gates work
The validator reports missing required tokens, unknown --turbo-* tokens
(likely typos), duplicate declarations, and empty values. It accepts CSS source
or a JSON map ({ "--turbo-bg-base": "#ffffff", … }), and --json emits a
machine-readable report.
Scaffold
npx turbo-themes init-native-theme acme --out src/styles/acme.css
writes a stub declaring every required token; replace the placeholders with your palette
and re-run validate-native.
In your test suite
import { assertNativeTheme } from '@lgtm-hq/turbo-themes/native-theme';
import { readFileSync } from 'node:fs';
test('acme theme satisfies the token contract', () => {
assertNativeTheme(readFileSync('src/styles/acme.css', 'utf8'), 'acme');
});
The node API is also importable directly:
import { validateNativeThemeCss } from '@lgtm-hq/turbo-themes/native-theme';
const result = validateNativeThemeCss(cssSource);
// { valid, missing, unknown, duplicates, empty, declaredCount, requiredCount }
What the contract does and does not guarantee
The validator checks presence, spelling, uniqueness, and placeholder values of
tokens, folding declarations from every selector and @media block into one set — keep
one canonical [data-theme="…"] block per theme file. It does not check that your
palette is readable — contrast is a property of your color choices. Published flavors
are build-time normalized to WCAG 2.2 AA (see the
accessibility guide); for native themes, run your own contrast
checks on the same fg/background pairs the toolkit audits (the pair list is importable
from the package as @lgtm-hq/turbo-themes/contrast-pairs.json).
See also
- Custom Themes — customizing published flavors.
- Advanced Theming — component-level tokens.