Theme generator

One key color in; a complete theme out. Surfaces, text, controls, status colors and chart colors for light and dark, every pair checked, as a UISet skin, shadcn/ui variables or design tokens.

Preview

Light

NorthwindPro plan

Project settings

Atlas, production

Shown in every URL and log line.

Region
Note: A new region, Sydney, is available.
Success: Deployed dpl-4821 to production.
Warning: The certificate for atlas.dev renews in 6 days.
Error: Build failed on fix-auth-redirect.
Build
Branch
Status
dpl-4821
main
Ready
dpl-4820
pricing-page
Building
dpl-4819
fix-auth-redirect
Failed
dpl-4818
docs-search
Queued
RequestsThousands, this week
Traffic sourcesLast 30 days

Dark

NorthwindPro plan

Project settings

Atlas, production

Shown in every URL and log line.

Region
Note: A new region, Sydney, is available.
Success: Deployed dpl-4821 to production.
Warning: The certificate for atlas.dev renews in 6 days.
Error: Build failed on fix-auth-redirect.
Build
Branch
Status
dpl-4821
main
Ready
dpl-4820
pricing-page
Building
dpl-4819
fix-auth-redirect
Failed
dpl-4818
docs-search
Queued
RequestsThousands, this week
Traffic sourcesLast 30 days

Checks

Every pair the base skin draws, measured on the colors the files contain. Contrast is WCAG 2; chart colors are told apart in OKLab (ΔE ×100), with full color vision and with protan and deutan vision simulated.

CheckLightDark
Text
Text on backgroundNeeds 4.5:117.47:1Passesui-foreground on ui-background17.02:1Passesui-foreground on ui-background
Text on muted surfacesNeeds 4.5:115.87:1Passesui-foreground on ui-muted14.85:1Passesui-foreground on ui-muted
Quiet text on backgroundNeeds 4.5:16.73:1Passesui-muted-foreground on ui-background7.99:1Passesui-muted-foreground on ui-background
Quiet text on muted surfacesNeeds 4.5:16.12:1Passesui-muted-foreground on ui-muted6.97:1Passesui-muted-foreground on ui-muted
Text on primaryNeeds 4.5:16.31:1Passesui-primary-foreground on ui-primary6.17:1Passesui-primary-foreground on ui-primary
Controls
Control edgesNeeds 3:13.89:1Passesui-input on ui-background4.11:1Passesui-input on ui-background
Focus ringNeeds 3:16.22:1Passesui-ring-border on ui-background6.50:1Passesui-ring-border on ui-background
Checked and selected controlsNeeds 3:16.22:1Passesui-primary on ui-background3.10:1Passesui-primary on ui-background
Status
Info as textNeeds 4.5:15.61:1Passestone-info on its badge10.89:1Passestone-info on its badge
Success as textNeeds 4.5:15.48:1Passestone-success on its badge11.51:1Passestone-success on its badge
Warning as textNeeds 4.5:15.24:1Passestone-warning on its badge11.72:1Passestone-warning on its badge
Danger as textNeeds 4.5:16.58:1Passestone-danger on its badge10.51:1Passestone-danger on its badge
Destructive as textNeeds 4.5:14.55:1Passesui-destructive on its button5.89:1Passesui-destructive on its button
Charts
Chart colors on backgroundNeeds 3:13.13:1Passeschart-6 on ui-background4.40:1Passeschart-4 on ui-background
Chart colors apart with protan and deutan visionNeeds ΔE 8ΔE 9.4Passeschart-2 and chart-4ΔE 10.3Passeschart-2 and chart-4
Chart colors apart with full color visionNeeds ΔE 15ΔE 17.6Passeschart-1 and chart-6ΔE 16.4Passeschart-7 and chart-8

Adjusted to pass

  • Destructive, light: moved to lightness 0.542 to hold 4.5:1.
  • Primary, dark: moved to lightness 0.505 to hold 3:1 on the background.

Export

A skin file: every variable as light-dark(), in UISet’s skin layer. Import it after UISet’s CSS and name it on the root.

import "./custom.css"

<UISet skin="custom">
  <App />
</UISet>
/*
  A UISet skin from the theme generator: key oklch(0.5 0.19 268), radius 0.5rem.
  Use it with <UISet skin="custom">. Light and dark follow the theme prop, or the device.
  Every pair checked in light and dark: text 4.5:1, control edges and focus 3:1,
  chart colors 3:1 and told apart with protan and deutan vision.
  Open it again: /themes/generator?key=0.5%2C0.19%2C268&tint=30&tintHue=key&radius=0.5&density=default&text=default&start=base&charts=key
*/
@layer ui-sdk, uiset.tokens, uiset.base, uiset.skin;

@layer uiset.skin {
  [data-uiset][data-skin="custom"] {
    --ui-background: light-dark(oklch(0.995 0.002 268), oklch(0.17 0.008 268));
    --ui-foreground: light-dark(oklch(0.21 0.012 268), oklch(0.96 0.004 268));
    --ui-muted: light-dark(oklch(0.962 0.006 268), oklch(0.235 0.01 268));
    --ui-muted-foreground: light-dark(oklch(0.47 0.016 268), oklch(0.73 0.012 268));
    --ui-border: light-dark(oklch(0.9 0.008 268), oklch(0.3 0.012 268));
    --ui-input: light-dark(oklch(0.6 0.014 268), oklch(0.56 0.014 268));
    --ui-primary: light-dark(oklch(0.5 0.19 268), oklch(0.505 0.19 268));
    --ui-primary-foreground: light-dark(oklch(1 0 0), oklch(1 0 0));
    --ui-ring: light-dark(oklch(0.5 0.19 268 / 0.3), oklch(0.505 0.19 268 / 0.35));
    --ui-ring-border: light-dark(oklch(0.5 0.19 268), oklch(0.6783 0.1235 268));
    --ui-destructive: light-dark(oklch(0.542 0.2201 25.5), oklch(0.712 0.1809 22.8));
    --tone-info: light-dark(oklch(0.48 0.15 255), oklch(0.86 0.0771 240));
    --tone-success: light-dark(oklch(0.47 0.12 150), oklch(0.87 0.11 150));
    --tone-warning: light-dark(oklch(0.5 0.1084 70), oklch(0.89 0.11 85));
    --tone-danger: light-dark(oklch(0.46 0.1876 27), oklch(0.86 0.0754 25));
    --chart-1: light-dark(oklch(0.5 0.19 268), oklch(0.72 0.1446 268));
    --chart-2: light-dark(oklch(0.56 0.1759 40), oklch(0.73 0.17 40));
    --chart-3: light-dark(oklch(0.56 0.1109 170), oklch(0.73 0.1445 170));
    --chart-4: light-dark(oklch(0.41 0.089 70), oklch(0.58 0.1202 80));
    --chart-5: light-dark(oklch(0.65 0.19 350), oklch(0.64 0.17 340));
    --chart-6: light-dark(oklch(0.65 0.1489 240), oklch(0.88 0.11 130));
    --chart-7: light-dark(oklch(0.56 0.19 300), oklch(0.73 0.17 310));
    --chart-8: light-dark(oklch(0.53 0.19 10), oklch(0.79 0.1248 10));
    --chart-seq-1: light-dark(oklch(0.905 0.0456 268), oklch(0.338 0.0925 268));
    --chart-seq-2: light-dark(oklch(0.812 0.0932 268), oklch(0.433 0.1149 268));
    --chart-seq-3: light-dark(oklch(0.717 0.1392 268), oklch(0.527 0.1347 268));
    --chart-seq-4: light-dark(oklch(0.622 0.19 268), oklch(0.622 0.1446 268));
    --chart-seq-5: light-dark(oklch(0.527 0.177 268), oklch(0.717 0.1059 268));
    --chart-seq-6: light-dark(oklch(0.433 0.151 268), oklch(0.812 0.0709 268));
    --chart-seq-7: light-dark(oklch(0.338 0.1215 268), oklch(0.905 0.0368 268));
    --ui-radius: 0.5rem;
    --ui-spacing: 0.25rem;
    --ui-font-size: 0.875rem;
    --ui-shadow:
      0 1px 2px light-dark(oklch(0.2 0.02 268 / 0.08), oklch(0 0 0 / 0.5)),
      0 12px 32px -12px light-dark(oklch(0.2 0.04 268 / 0.22), oklch(0 0 0 / 0.7));
  }
}

Tokens

VariableLightDark
--ui-backgroundoklch(0.995 0.002 268)#fdfdffoklch(0.17 0.008 268)#0e0f13
--ui-foregroundoklch(0.21 0.012 268)#16181eoklch(0.96 0.004 268)#f0f2f4
--ui-mutedoklch(0.962 0.006 268)#f1f2f7oklch(0.235 0.01 268)#1c1e23
--ui-muted-foregroundoklch(0.47 0.016 268)#575b64oklch(0.73 0.012 268)#a4a8af
--ui-borderoklch(0.9 0.008 268)#dcdee3oklch(0.3 0.012 268)#2b2e34
--ui-inputoklch(0.6 0.014 268)#7c8089oklch(0.56 0.014 268)#71747d
--ui-primaryoklch(0.5 0.19 268)#3655cdoklch(0.505 0.19 268)#3856cf
--ui-primary-foregroundoklch(1 0 0)#ffffffoklch(1 0 0)#ffffff
--ui-ringoklch(0.5 0.19 268 / 0.3)#3655cdoklch(0.505 0.19 268 / 0.35)#3856cf
--ui-ring-borderoklch(0.5 0.19 268)#3655cdoklch(0.6783 0.1235 268)#7794e4
--ui-destructiveoklch(0.542 0.2201 25.5)#d1001foklch(0.712 0.1809 22.8)#ff6b6b
--tone-infooklch(0.48 0.15 255)#0e5cafoklch(0.86 0.0771 240)#a3d8ff
--tone-successoklch(0.47 0.12 150)#146d34oklch(0.87 0.11 150)#9fe9ae
--tone-warningoklch(0.5 0.1084 70)#8a5600oklch(0.89 0.11 85)#fcd583
--tone-dangeroklch(0.46 0.1876 27)#a8000foklch(0.86 0.0754 25)#ffbeb8
--chart-1oklch(0.5 0.19 268)#3655cdoklch(0.72 0.1446 268)#7e9fff
--chart-2oklch(0.56 0.1759 40)#c54300oklch(0.73 0.17 40)#fe7d4e
--chart-3oklch(0.56 0.1109 170)#00896aoklch(0.73 0.1445 170)#00c49a
--chart-4oklch(0.41 0.089 70)#684000oklch(0.58 0.1202 80)#9f7100
--chart-5oklch(0.65 0.19 350)#df539foklch(0.64 0.17 340)#cd5cac
--chart-6oklch(0.65 0.1489 240)#0099e0oklch(0.88 0.11 130)#c0e699
--chart-7oklch(0.56 0.19 300)#8851d1oklch(0.73 0.17 310)#c886f6
--chart-8oklch(0.53 0.19 10)#bf2352oklch(0.79 0.1248 10)#ff98a8
--chart-seq-1oklch(0.905 0.0456 268)#d3dfffoklch(0.338 0.0925 268)#233367
--chart-seq-2oklch(0.812 0.0932 268)#a8c0ffoklch(0.433 0.1149 268)#364b8f
--chart-seq-3oklch(0.717 0.1392 268)#7e9ffboklch(0.527 0.1347 268)#4a65b8
--chart-seq-4oklch(0.622 0.19 268)#567bf8oklch(0.622 0.1446 268)#6181dd
--chart-seq-5oklch(0.527 0.177 268)#405fcfoklch(0.717 0.1059 268)#87a1e6
--chart-seq-6oklch(0.433 0.151 268)#2e47a2oklch(0.812 0.0709 268)#aec1f0
--chart-seq-7oklch(0.338 0.1215 268)#1d3075oklch(0.905 0.0368 268)#d5dff9
--ui-radius0.5rem
--ui-spacing0.25rem
--ui-font-size0.875rem