Theme customiser
To open the customiser, select the palette button in the header. It works on any page. The whole site changes as you choose, so there is no separate preview.
Start with three choices:
- Brand colour: any Tailwind colour, or your own hex code.
- Neutral: the grey used for backgrounds, borders and text.
- Corners: how round they are.
The brand colour and the neutral generate every colour for light and dark mode. The result always passes the contrast checks below.
Advanced shows every colour token as a hex code and as OKLCH values. When you edit tokens by hand, a colour pair can fail, so the customiser checks your theme as you type. It uses the same colour pairs and minimum ratios as scripts/check-contrast.ts, the script that tests this project.
Your theme is saved in your browser. It appears as one more preset, Custom, until you select Reset.
Contrast
The default theme, checked with the same colour pairs and minimum ratios as scripts/check-contrast.ts.
light
| Pair | Ratio | Result |
|---|---|---|
| Body text | 19.75:1 (min 4.5) | Passes |
| Secondary text | 7.64:1 (min 4.5) | Passes |
| Secondary text on muted backgrounds | 7.01:1 (min 4.5) | Passes |
| Card text | 19.75:1 (min 4.5) | Passes |
| Popover text | 19.75:1 (min 4.5) | Passes |
| Primary button label | 5.50:1 (min 4.5) | Passes |
| Secondary button label | 16.04:1 (min 4.5) | Passes |
| Text on accent backgrounds | 6.41:1 (min 4.5) | Passes |
| Destructive button label | 4.56:1 (min 4.5) | Passes |
| Primary colour as a fill or icon | 3.59:1 (min 3) | Passes |
| Focus ring | 3.59:1 (min 3) | Passes |
| Focus ring on cards | 3.59:1 (min 3) | Passes |
| Form field border | 3.07:1 (min 3) | Passes |
| Form field border on cards | 3.07:1 (min 3) | Passes |
dark
| Pair | Ratio | Result |
|---|---|---|
| Body text | 18.92:1 (min 4.5) | Passes |
| Secondary text | 7.63:1 (min 4.5) | Passes |
| Secondary text on muted backgrounds | 5.87:1 (min 4.5) | Passes |
| Card text | 16.76:1 (min 4.5) | Passes |
| Popover text | 14.56:1 (min 4.5) | Passes |
| Primary button label | 6.83:1 (min 4.5) | Passes |
| Secondary button label | 14.56:1 (min 4.5) | Passes |
| Text on accent backgrounds | 9.20:1 (min 4.5) | Passes |
| Destructive button label | 6.83:1 (min 4.5) | Passes |
| Primary colour as a fill or icon | 6.83:1 (min 3) | Passes |
| Focus ring | 6.83:1 (min 3) | Passes |
| Focus ring on cards | 6.05:1 (min 3) | Passes |
| Form field border | 4.11:1 (min 3) | Passes |
| Form field border on cards | 3.64:1 (min 3) | Passes |
Export
Paste this CSS into your stylesheet, after the Facade tokens. Then set data-facade-theme="custom" on <html>.
[data-facade-theme="custom"] {
--background: oklch(1 0 0);
--foreground: oklch(0.147 0.004 49.25);
--card: oklch(1 0 0);
--card-foreground: oklch(0.147 0.004 49.25);
--popover: oklch(1 0 0);
--popover-foreground: oklch(0.147 0.004 49.25);
--primary: oklch(0.646 0.222 41.116);
--primary-foreground: oklch(0.147 0.004 49.25);
--secondary: oklch(0.97 0.001 106.424);
--secondary-foreground: oklch(0.216 0.006 56.043);
--muted: oklch(0.97 0.001 106.424);
--muted-foreground: oklch(0.444 0.011 73.639);
--accent: oklch(0.954 0.038 75.164);
--accent-foreground: oklch(0.47 0.157 37.304);
--destructive: oklch(0.577 0.245 27.325);
--destructive-foreground: oklch(0.985 0.001 106.423);
--border: oklch(0.923 0.003 48.717);
--input: oklch(0.664 0.01 56.259);
--ring: oklch(0.646 0.222 41.116);
}
.dark[data-facade-theme="custom"],
.dark [data-facade-theme="custom"],
[data-facade-theme="custom"] .dark {
--background: oklch(0.147 0.004 49.25);
--foreground: oklch(0.985 0.001 106.423);
--card: oklch(0.216 0.006 56.043);
--card-foreground: oklch(0.985 0.001 106.423);
--popover: oklch(0.268 0.007 34.298);
--popover-foreground: oklch(0.985 0.001 106.423);
--primary: oklch(0.705 0.213 47.604);
--primary-foreground: oklch(0.147 0.004 49.25);
--secondary: oklch(0.268 0.007 34.298);
--secondary-foreground: oklch(0.985 0.001 106.423);
--muted: oklch(0.268 0.007 34.298);
--muted-foreground: oklch(0.709 0.01 56.259);
--accent: oklch(0.266 0.079 36.259);
--accent-foreground: oklch(0.837 0.128 66.29);
--destructive: oklch(0.704 0.191 22.216);
--destructive-foreground: oklch(0.147 0.004 49.25);
--border: oklch(1 0 0 / 12%);
--input: oklch(0.553 0.013 58.071);
--ring: oklch(0.705 0.213 47.604);
}