> ## Documentation Index
> Fetch the complete documentation index at: https://substrate.docs.unknowncreatives.studio/llms.txt
> Use this file to discover all available pages before exploring further.

# APCA Contrast Solver: How Substrate Targets Lc Scores

> How Substrate's runtime solver derives foreground lightness with APCA: the fixed policy, contrastFactor scaling, shortfall reporting, and the accessibility gate.

Substrate uses APCA — the Accessible Perceptual Contrast Algorithm — to compute the lightness of every foreground color **at runtime, per surface**. The result is a system where every foreground is derived to be legible against the actual background it sits on. You never hand-pick a text color, and you never run a contrast checker manually — contrast is what the solver *solves for*.

## What Is APCA?

APCA was developed by Andrew Somers of Myndex Perception Research (attribution and links in [About APCA](#about-apca) below) and models how human vision perceives contrast between text or foreground elements and their background more accurately than the WCAG 2.x ratio formula. The legacy WCAG 2.x formula treats light and dark colors symmetrically and operates in sRGB luminance, which doesn't reflect how the eye actually adapts to luminance differences across the lightness range. APCA accounts for the spatial frequency of text (size and weight), the direction of polarity (light-on-dark vs. dark-on-light), and the non-linear response of human vision — producing contrast scores that align much more closely with perceived readability.

## Why Substrate Uses APCA

Substrate's token model separates hue and chroma (your brand intent) from lightness (a derived value). When you declare the `brand` intent with `hue: 277` and `chroma: 0.268`, you are not specifying how light or dark that color is. The solver asks: *what lightness value, at this hue and chroma, produces the required Lc score against the current surface?* The answer changes continuously across the scheme axis — your brand color is lighter near the dark end and darker near the light end — but the contrast guarantee is identical at every position.

Lc values are directional: positive for dark-on-light, negative for light-on-dark. Substrate compares absolute values against its targets, so polarity flips are handled transparently.

## The solver policy

The targets are a **fixed, named policy** in the kernel — not per-token numbers:

| Channel    | Target |
| ---------- | ------ |
| Foreground | Lc 75  |
| Border     | Lc 50  |
| Focus ring | Lc 60  |
| Ceiling    | Lc 106 |

Every target is then **scaled by the user's `contrastFactor`** (0.75 → 1.5, bounded per brand by `flexibility.contrast`). High contrast is not a special mode with its own targets — the `highContrast` preset simply sets `contrastFactor: 1.3`, which pushes the foreground target from Lc 75 to \~97.5, clamped by the Lc 106 ceiling.

The transform order matters and is fixed: **warmth → CVD → APCA**. Color is adjusted for the user's viewing conditions and color vision *before* any contrast math, so the solve is honest about what the user actually sees.

Substrate additionally consults a size/weight-aware Lc floor table so small or light text gets stricter minimums.

<Warning>
  The size/weight floor table is Substrate's **own construction**, not an official APCA artifact — the kernel's source says exactly that, and recommends validating against live APCA tooling before claiming conformance levels.
</Warning>

## How the solve works

For each intent and surface:

1. The intent's scheme track is evaluated at the current `scheme` position → hue and chroma.
2. Warmth and CVD transforms adjust the color for the user.
3. A binary search over OKLCH lightness (\~30 iterations, precision \~1e-9) finds the L that meets the scaled Lc target against the context surface (`--ctx-surface-l`). OKLCH's perceptual uniformity is what makes this search sound — L moves without dragging hue or chroma.
4. The results are written as `--ucs-{intent}-fg-l`, `--ucs-{intent}-border-l`, per-role `--ucs-{intent}-fg-l-{role}` values, and `--ucs-focus-ring`.

## When a target can't be met

Some hue/chroma/surface combinations cannot reach the target within gamut. The solver never silently passes: its solution carries an explicit **`unmetLc`** shortfall value alongside the clamped lightness. And at generation time, a **build-failing accessibility gate** sweeps the whole brand corpus — APCA and CVD checks together — and fails the build with per-case output rather than emitting a brand that undershoots its policy.

There are no per-token Lc comments in the generated CSS — with runtime solving against the actual surface, a baked Lc annotation would be meaningless. The gate and `unmetLc` are the verification surface.

<Note>
  APCA is the candidate contrast method for WCAG 3.0, which is still in development. Substrate adopts it now for its perceptual accuracy — but WCAG 3.0 is not final, and Substrate's own kernel avoids claiming conformance ahead of the standard. Pair APCA targets with the CVD caveats on the [Color Vision](/modes/color-vision) page.
</Note>

## About APCA

Substrate's contrast solving implements APCA (Accessible Perceptual Contrast Algorithm), created by Andrew Somers of Myndex Perception Research, base algorithm 0.0.98G-4g. APCA is the candidate contrast method for WCAG 3. For the reasoning behind the model, read [Why APCA](https://github.com/Myndex/SAPC-APCA/blob/master/documentation/WhyAPCA.md); the full documentation lives at the [APCA documentation hub](https://git.apcacontrast.com/).

<Note>
  APCA is a method for predicting text contrast on self-illuminated displays for web-based content. Some use-cases are prohibited by license, including the following: use in medical, clinical evaluation, human safety related, aerospace, transportation, automotive, military applications, are strictly prohibited without a specific license in writing granting such use.
</Note>
