Philosophy
The Color System is built on three core pillars: Math, Semantics, and Adaptability.
Math vs. Magic
Most color systems are built on Magic Numbers.
“Use
Blue-500for buttons andGray-100for cards.”
This forces you to be the calculator.
- Is it accessible? You have to know that
Blue-800is safe onBlue-200. IsBlue-500safe? You have to check a table. - What about Dark Mode?
Blue-800onBlue-200might work in Light Mode, but in Dark Mode, you need to invert it. Now you’re managing two sets of magic numbers. - Is it consistent? The math of perception is non-linear. A step of “100” in lightness looks different in dark mode than in light mode. To make them feel the same, you have to manually tweak the values.
This system is built on Math.
“I need a Card.”
The system translates this intent into a mathematical rule: Must have APCA 60 contrast against the background.
The Solver handles the complexity for you:
- It guarantees contrast: It picks the exact lightness to hit APCA 60.
- It handles polarity: It automatically flips for Dark Mode.
- It balances perception: It adjusts the lightness steps so that “contrast” feels the same in both modes.
- It shifts hue: It automatically warms up lighter colors and cools down darker colors (the “Bezold–Brücke effect”) so you don’t have to manually pick “warmer” grays.
You define the intent. The system solves the math.
1. Guarantees Contrast
All these buttons have different hues, but the system mathematically guarantees they meet the same contrast standard.
2. Handles Polarity & Perception
The system automatically inverts the colors. Notice how the “Card” feels equally distinct in both modes, even though the actual lightness values are different.
3. Shifts Hue (Bezold–Brücke Effect)
The system automatically rotates the hue as lightness changes, mimicking natural light and creating a richer, more vibrant palette.
Designing with Intent
Our semantic roles (like “Surface”, “Action”, “Link”) are not arbitrary choices. They are derived directly from the fundamental semantics of the web platform.
By aligning our taxonomy with these platform primitives, we ensure that accessibility is not an “add-on” or a “special case.” It is the foundation of the design. When you design with these concepts, you are designing with the grain of the web, ensuring your application feels native and works perfectly for every user, regardless of their device or settings.
For the curious: Under the hood, we map these roles to CSS System Colors (like
Canvas,ButtonFace,Highlight). This is how we support Windows High Contrast mode automatically. But you don’t need to know that to use the system—just use the semantic names.
The Surface Model
In this system, you don’t pick colors. You pick Surfaces.
A Surface is not just a background color. It is a Context Creator. When you place an element on a surface, that surface dictates how text, borders, and other elements should behave to remain accessible.
Polarity: The “Light Switch”
Before understanding specific surfaces, you must understand Polarity. Every surface has a polarity that determines how it reacts to Light and Dark mode.
- Page-Aligned: These surfaces follow the global mode. They are light in Light Mode and dark in Dark Mode. (e.g., Cards, Sidebars).
- Inverted: These surfaces flip the global mode. They are dark in Light Mode and light in Dark Mode. (e.g., Tooltips, Primary Buttons).
The Solver handles this automatically. You just ask for a spotlight (which is inverted), and it ensures it pops against the current theme.
The Categories
The system groups surfaces into four semantic categories based on their intent:
- Canvas: The foundation. The “floor” of your app.
- Container: Objects that hold content. They sit on the canvas.
- Action: Interactive elements. They sit on containers or the canvas.
- Spotlight: Attention-grabbing elements. They float above everything.
The Default Hierarchy
The system comes with a standard set of surfaces that map to these categories. While you can create custom surfaces in the Theme Builder, these defaults cover most UI patterns.
<div class="surface-card docs-p-4 docs-rounded">
<strong>Card (Container)</strong>
<div class="text-subtle docs-mb-4">Contained element</div>
<div class="surface-tinted docs-p-4 docs-rounded">
<strong>Tinted (Container)</strong>
<div class="text-subtle">Subtle grouping</div>
</div>
</div>
Interactors (Actions)
Actions are surfaces that invite user input. They are the “buttons” and “controls” of your interface.
Spotlights (Attention)
Spotlights are used to draw immediate attention. They use Inverted Polarity to create maximum contrast and visual separation from the page.
The Reactive Pipeline
How does this actually work in the browser?
We use a technique called the Reactive Pipeline. Instead of hardcoding hex values into classes, we use CSS Custom Properties (var(--...)) and the Relative Color Syntax (oklch(from ...)).
The Flow
-
Input Variables: You set high-level intent variables.
.hue-brand { --hue-brand: 250; } -
The Engine (
engine.css): The engine listens to these variables and recalculates the colors in real-time./* Simplified Engine Logic */ --computed-surface: oklch(from var(--surface-token) l c var(--hue-brand)); -
The Output: The browser renders the final color.
Why is this powerful?
- Instant Theming: Change
--hue-brandon the<body>, and the entire app updates instantly. No re-compiling CSS. - Scoped Theming: Change
--hue-brandon a specific<div>, and only that section changes color. - Animation: Because these are just numbers, you can animate them! The system handles the color interpolation for you.
Context Consumers
Text and borders are Context Consumers. They don’t have their own colors; they calculate their color based on the surface they are sitting on.
text-strong: “I need high contrast against whatever surface I am on.”text-subtle: “I need to be readable, but less prominent.”bordered: “I need a decorative border that is visible on this surface.”
This means you can copy-paste a component from a light card to a dark spotlight, and the text will automatically invert to remain readable.