The Solver
The Solver is the engine that powers the color system. It takes your high-level Intent and turns it into precise CSS Tokens.
You can interact with the Theme Builder in two ways:
- The UI: The interactive web interface (for exploration).
- The CLI: The
color-systemcommand line tool (for production).
Both use the exact same “Solver” logic under the hood.
The “Theme Builder” Model
To understand the solver, it helps to think about the controls you see in the Theme Builder UI. The solver is simply the code that runs every time you move a slider or add a surface.
1. Anchors: Defining the Playing Field
In the Theme Builder, you set the Anchors. These are the boundaries of your color system.
Page Anchors
Defines the lightness range for the "Page" polarity.
The solver takes these values and asks: “Can I fit readable text inside this range?” If the answer is “No” (and the anchor is adjustable), the solver moves the slider for you until the text is readable.
2. Surfaces: The “Steps”
In the Theme Builder, you add Surfaces to a list.
Surfaces
.surface-page
Passes
<!-- Surface Item 2 -->
<div class="surface-card docs-p-2 docs-rounded docs-border" style="display: flex; align-items: center; gap: 1rem;">
<span class="text-strong" style="flex: 1;">Card</span>
<code class="text-subtle">.surface-card</code>
<span style="font-size: 0.8em; background: #dcfce7; color: #166534; padding: 2px 6px; border-radius: 4px;">Passes</span>
</div>
<!-- Surface Item 3 -->
<div class="surface-card docs-p-2 docs-rounded docs-border" style="display: flex; align-items: center; gap: 1rem;">
<span class="text-strong" style="flex: 1;">Sidebar</span>
<code class="text-subtle">.surface-sidebar</code>
<span style="font-size: 0.8em; background: #dcfce7; color: #166534; padding: 2px 6px; border-radius: 4px;">Passes</span>
</div>
The solver’s job is to place these surfaces evenly between your Start and End anchors. It doesn’t just divide the lightness evenly (e.g., 10%, 20%, 30%). It divides the Contrast Space evenly. This ensures that the visual “step” from Page to Card looks the same as the step from Card to Sidebar.
Why Contrast Space?
If we just divided the lightness values evenly (Linear Lightness), the steps would look uneven to the human eye. Dark colors bunch up, and light colors spread out. By dividing by Contrast (Linear Perception), every step feels visually consistent.
3. The Result: Generated Tokens
Finally, the solver outputs the CSS tokens that the Theme Builder (and your app) uses.
Generated CSS
--lightness-surface-page: light-dark(0.98, 0.12);
--lightness-surface-card: light-dark(0.95, 0.15);
--lightness-surface-sidebar: light-dark(0.92, 0.18);
The Pipeline
When you run pnpm solve (or change a setting in the Builder), this pipeline executes:
- Hydrate: Read your config.
- Adjust Anchors: Ensure the range supports High Contrast text.
- Distribute: Calculate the target contrast for each surface.
- Solve Lightness: Use binary search to find the exact lightness value that hits that contrast target.
- Solve Text: Find the text colors that sit accessibly on top of those surfaces.
- Generate: Write the CSS.