Skip to main content
Posts

Theming Demo

A working demo of the theming described in my writeup of the okLCH color system I created for the Unified Design Language project. While that design system doesn't use Tailwind, I've applied the same thinking to this site, so we end up with a pretty close approximation.

You can play around below. The demo is scoped to its container, so it won't affect the rest of the page, but you can always change the whole site's theme in the header if you land on something you like.

It seems LightningCSS (used by Tailwind) now converts oklch to okLab when compiled as of Tailwind ~v4.2, so inspected values may not match what's in the code. Shouldn't really matter though, since the two convert cleanly, but just FYI.
<Theme>
Settings
Colors
black
black
px
Preview
NeutralInfoSuccessWarningError
NeutralInfoSuccessWarningError
NeutralInfoSuccessWarningError
Neutral
Brand
Info
Success
Warning
Error

How it works

* just on this site, not the actual project

Colors are controlled by data-hue and data-neutral attributes on the <Theme> component, each of which assigns a given color ramp to the alias variables that feed into the theme. These will cascade down from any ancestor, so you can scope overrides to a section of the page.

                    
            ╔──────────────────╗            
            │    colors.css    │            
            ╚────────┬─────────╝            
                     │                        
      ╭──────────────┴──────────────╮
      │                             │
      ▼                             ▼
╔────────────────╗          ╔───────────────╗
│    hues.css    ├─ ─ ─ ─ ─►│  globals.css  │
╚────────────────╝          ╚──────┬────────╝
        │                          │
        │                       Tokens
        │                          │
        ╰──╮        ╭──────────────╯
           │        │ 
           │        ▼
  ╔╌╌╌╌╌╌╌╌│╌╌╌╌╌<Theme>╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╗
  ╎     ╭──▼───────╮ ╭──────────────╮     ╎
  ╠─────│ data-hue ├─┤ data-neutral │─────╣
  ╎  ╭──╯╌╌╌╌╌╌╌╌╌╌╰─╯╌╌╌╌╌╌╌╌╌╌╌╌╌╌╰──╮  ╎
  ╎  │         {children}              │  ╎
  ╎  ╰─────────────────────────────────╯  ╎
  ╚╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╝

Aliases

Raw oklch palettes are registered with Tailwind in colors.css, making them available as both CSS variables and utility classes (bg-ruby-500, text-sand-200, etc.).

Two alias scales, --hue-* and --neutral-*, help power theming beyond what the semantic tokens provide. This also lets us re-theme components without having to touch the semantic token definitions if we don't need to. By default, they both point to the sand palette, but setting data-hue or data-neutral on any ancestor swaps the entire scale for another color ramp.

File structure

hues.css

Semantic tokens

Semantic tokens like --primary, --background, and --ring are declared on :root, [data-theme] and reference these aliases. Because [data-theme] is in the selector, the tokens re-evaluate whenever hue or neutral variables are overridden on a descendant.

(I think they're too limited for practical use, but I'm using shadcn tokens on this site. It's mostly a thought experiment to see how far I can push them / test how my colors work with them; this isn't what's used in the actual project.)

globals.css

Radius

Just for fun, I made the base radius = 10 for easier math, and I also wanted to play with odd-numbered radii.

So you have the --radius base value with these odd multipliers. Instances of <Theme> will inject a new --radius CSS variable, so updated values cascade down and re-scale instances.

I also capped the values with some eyeballing to avoid overly large radii; I'd prefer to use a more precise approach, but this is a quick and dirty solution.

globals.css

PS — If you're wondering what the --ellipse-factoris for, it has nothing to do with theming. I made a utility class for squircle corners, and that'll offset the radius variable to get the desired effect.

globals.css