Skip to content

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:

  1. The UI: The interactive web interface (for exploration).
  2. The CLI: The axiomatic command line tool (for production).

Both use the exact same “Solver” logic under the hood.

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.

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.

Start (Background) 0.98
End (Elevated) 0.12

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.

In the Theme Builder, you add Surfaces to a list.

Surfaces

Page .surface-page Passes
Card .surface-card Passes
Sidebar .surface-sidebar Passes

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.

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.

Linear Lightness (Bad)

Step 1 (95%)
Step 2 (85%)
Step 3 (75%)
Step 4 (65%)
Step 5 (55%)

Linear Contrast (Good)

Page (Start)
Workspace
Card
Action (End)

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);

When you run npx axiomatic (or change a setting in the Builder), this pipeline executes:

  1. Hydrate: Read your color-config.json.
  2. Adjust Anchors: Ensure the range supports High Contrast text.
  3. Distribute: Calculate the target contrast for each surface based on the available range and any gapBefore settings.
  4. Solve Lightness: Use binary search to find the exact lightness value that hits that contrast target.
  5. Solve Text: Find the text colors that sit accessibly on top of those surfaces (APCA-compliant).
  6. Generate: Write the CSS tokens.

The solver runs a second pass to generate a High Contrast variant (@media (prefers-contrast: more)). In this pass:

  • Key Colors are removed (forced to grayscale).
  • Anchors are pushed to pure Black (0) and White (1).
  • Chroma is disabled.

This ensures that users who need maximum legibility get a strictly accessible, high-contrast version of your theme automatically.