refract v0.1 · beta
Delivery / Blast radius

Blast radius — refract diff

Every value in a refract theme is a { ref, fn, arg } graph, not a frozen literal — so refract can answer a question a plain token file can't: what will this change break, before I ship it? refract diff <candidate> builds a candidate theme against your config's targets and prints exactly what moves — tokens re-valued, recipe classes changed, and any contrast pairing that crossed a threshold. It's the most defensible thing refract does: nothing else in the category stores the reference graph, so nothing else can tell you the blast radius up front.

On the command line

Point it at a candidate theme — a theme.config.* or a DTCG document. With no thresholds it just reports the diff; add thresholds and any breach exits nonzero, turning it into a PR gate.

Terminal
refract diff candidate.ts                                    # print the blast radius (report only)
refract diff candidate.ts \
  --max-token-changes 20 --max-class-changes 5 --fail-below AA     # gate: fail the run on a breach

The gate flags: --max-token-changes <n> and --max-class-changes <n> fail when more than n tokens or recipe classes move; --fail-below <AAA|AA|AA-large> fails when any pairing drops below that WCAG-2 level. Point at a non-default config with --config <path>.

As a CI gate — GitHub Actions

Drop this in .github/workflows/theme-diff.yml. On every pull request it diffs the candidate theme against your committed config and fails the check if a change moves more than the thresholds allow or drops a colour pairing below AA — so a token tweak can't silently re-skin the app or break contrast.

.github/workflows/theme-diff.yml
name: theme-diff
on: pull_request
jobs:
  diff:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with: { node-version: 22 }
      - name: Theme blast-radius gate
        run: >
          npx -y @theme-registry/refract diff candidate.ts
          --max-token-changes 20 --max-class-changes 5 --fail-below AA
Pair it with the contrast audit. refract diff --fail-below gates changes; refract audit --strict gates the absolute contrast of the whole theme. Both exit nonzero, so both slot into the same job.

Programmatic

The same diff is a plain export for custom tooling or a dashboard: diffThemes(base, candidate) from @theme-registry/refract/build returns the { tokens, classes, contrast, summary } structure the CLI and the MCP diffTheme tool both render.

diff.ts
import { diffThemes } from "@theme-registry/refract/build";

const { tokens, classes, contrast, summary } = diffThemes(base, candidate);
// summary → { tokenChanges, classChanges, worstPairing } for your own gate
Also an agent tool. Exposed as the MCP diffTheme tool too, so an agent can check its own change's blast radius before applying it — plan-then-apply, not apply-then-discover. See MCP server.
Technical documentation for @theme-registry/refract · all output is compiled client-side by the real library (CSS adapter). The chrome is theme-aware; the render panes carry each preset's own world. · MIT licensed.