# Alloy Tokens The design-token system for Alloy. Storage format, authoring discipline, extension rules, and how Alloy renders any theme downloaded from the future theme catalog. The rules these tokens implement live in [DESIGN-LANGUAGE.md](DESIGN-LANGUAGE.md). This file is where the rules become storage, derivation, and gates. ## Storage format: makeover Alloy consumes themes in the [makeover](https://git.sr.ht/~maxmj/makeover) TOML format, the same schema GoingsOn, Balanced Breakfast, audiofiles, and makenot.work already consume. A theme file has seven sections: ```toml [meta] name = "Akari Dawn" variant = "light" # "light" | "dark" | "high-contrast" [surface] page = "#e4ded6" # main canvas raised = "#ede7de" # elevated / focused / popup base (lighter than page in light mode) sunken = "#cfc4b6" # recessed panels (darker than page in light mode) overlay = "#f0ece4" # modal / top-most temporary UI [content] primary = "#1a1816" # body text secondary = "#222d38" # section headers, labels muted = "#514b45" # disabled, placeholder, metadata [action] primary = "#8a4530" # the theme's primary action / brand accent [status] danger = "#6a2828" # error success = "#3a5830" # healthy / ok warning = "#b07840" # near-threshold info = "#304050" # informational / link / in-progress [line] border = "#cabeae" # single border tone; Alloy derives -subtle and -strong (see below) [category] one = "#8a4530" # categorical series colors (chart / syntax / tag rows) two = "#3a5830" three = "#304050" four = "#b07840" five = "#806080" six = "#305858" ``` Alloy's default light theme is **[Akari Dawn](https://git.sr.ht/~maxmj/makeover/tree/main/item/themes/akari-dawn.toml)**; the default dark is **[Akari Night](https://git.sr.ht/~maxmj/makeover/tree/main/item/themes/akari-night.toml)**. Both are based on Shu Kutsuzawa's [Akari](https://github.com/cappyzawa/akari-theme) (MIT). Users pick a mode at startup (`--theme` flag or `$COLORFGBG` / OSC background query); users can also drop any makeover `.toml` into `~/.config/alloy/themes/` and pass its name to swap. **Why makeover as the storage format:** the future user-facing theme catalog (a website of downloadable `.toml` files) is only useful if every make-family app renders any downloaded theme the same way. Sharing the format across apps buys that with zero adapter code. ## Alloy-specific derived tokens Alloy needs three token tiers makeover ships one of: `border-subtle`, `border`, `border-strong`. Rather than extend the schema (which would break existing consumers), Alloy computes the outer two from `line.border` at load time: ``` border-subtle = mix(line.border, surface.page, 60%) # pulled toward surface — decorative divider border-strong = mix(line.border, content.primary, 65%) # pulled toward text — focus rings, selection ``` Mix is in linear sRGB. The 65%-toward-text ratio for `border-strong` is calibrated to reach WCAG AA-UI (≥3.0:1) against `surface.page` even on soft-border themes (Akari's `line.border` is `#cabeae`; the mix produces `#7f786d` which lands at 3.27:1). Themes with crisper borders will overshoot; that's fine. The formulas live in `alloy_tui`, not in the theme file. Theme files stay minimal and cross-app compatible. ## Authoring discipline: OKLCH mental model, hex on disk Themes on disk are hex. The design *reasoning* behind those hex values (Alloy-authored ones or curated ones like Akari) still uses OKLCH: - **Ramp discipline.** Surface tiers should follow monotonic L ordering per mode (light: `sunken < page < raised < overlay`; dark: inverted). Accents live in the mid-L / mid-C range where they read as color-carrying-information on either ramp. - **Tinted-not-neutral chrome.** Chrome tones should carry a hue tint (see [DESIGN-LANGUAGE.md](DESIGN-LANGUAGE.md#core-principle-tinted-greyscale-chrome-color-as-information)); Akari's warm-clay palette is the canonical worked example. - **Accent-on-glyph, not on body text.** Confirmed by contrast math in the audit: accents that clear WCAG AA-text (4.5:1) on the theme's surface can be used freely; accents that only clear AA-UI (3.0:1) go on glyphs and edge markers, with body text staying at `content.primary`. Because storage is hex, OKLCH doesn't need to be preserved in the file; it's the mental model of the person authoring the theme. ## Gate: the WCAG audit Any theme, downloaded or authored, is audited by [`tools/wcag_audit.py`](../tools/wcag_audit.py). It reads a makeover `.toml`, converts hex to linear sRGB to WCAG 2.1 relative luminance, and reports pass/fail for every affordance-carrying pair (text-on-surface, borders-on-surface, accents-on-surface, elevation deltas). Also computes `border-subtle` / `border-strong` via the same derivation Alloy uses at runtime. Run: ``` python3 tools/wcag_audit.py ../Libraries/makeover/themes/akari-dawn.toml ``` The audit is not a hard-fail gate: a theme with accents that only reach AA-UI on some surface tiers is still shippable if the accent-on-glyph rule is honored. The audit's job is to make trade-offs visible. ## Verified contrast: Akari Dawn Full audit output at time of adoption. Method: OKLCH-agnostic; reads hex, computes WCAG 2.1 relative luminance directly. **Text on surfaces:** | Pair | vs sunken | vs page | vs raised | vs overlay | |---|---:|---:|---:|---:| | `content.primary` | 10.31 | 13.25 | 14.40 | 15.03 | | `content.secondary` | 8.15 | 10.48 | 11.39 | 11.88 | | `content.muted` | 5.01 | 6.44 | 7.00 | 7.30 | All twelve pairings clear AA-text (4.5); most clear AAA (7). **Borders on surfaces:** | Pair | vs page | vs raised | vs overlay | |---|---:|---:|---:| | `border-strong` (derived) | 3.27 | 3.55 | 3.71 | | `line.border` | 1.37 | 1.49 | 1.55 | | `border-subtle` (derived) | 1.12 | 1.22 | 1.27 | `border-strong` clears AA-UI on all three surfaces; the derivation formula works for Akari's soft border. `line.border` and `border-subtle` are decorative dividers, not required to meet 3.0. **Accents on surfaces:** | Accent | vs page | vs raised | vs sunken | vs overlay | Notes | |---|---:|---:|---:|---:|---| | `action.primary` (lantern `#8a4530`) | 5.28 | 5.74 | **4.11** | 5.99 | AA-text on three of four; AA-UI on `sunken`, glyph-safe on the darkest tier | | `status.danger` (`#6a2828`) | 8.07 | 8.78 | 6.28 | 9.15 | AAA everywhere | | `status.success` (`#3a5830`) | 6.00 | 6.53 | 4.67 | 6.81 | AA-text everywhere | | `status.warning` (`#b07840`) | **2.80** | **3.05** | **2.18** | **3.18** | Fails AA-text on all tiers; AA-UI only on `raised`/`overlay`. **Amber-on-cream is inherent to Akari's aesthetic**; per the accent-on-glyph rule, warning is expressed as glyph + edge marker only. | | `status.info` (`#304050`) | 7.96 | 8.65 | 6.19 | 9.03 | AAA / AA-text everywhere | The two failure modes are both Akari-inherent, not derivation bugs, and both survive Alloy's discipline: `action.primary` on `surface.sunken` still clears AA-UI (glyph-safe on the darkest recessed panel), and `status.warning`'s amber is a glyph-only accent per the same rule that governs `accent-syntax` in the previous Alloy palette. ## Verified contrast: Akari Night Text on surfaces all AAA or AA-text. Borders derived at 7+ contrast (dark themes have more headroom because the border tone is against a light content.primary). Notable accent behavior: `status.danger` at `#d25046` lands at 3.3–4.1:1 across surfaces: AA-UI territory, glyph-safe under the same rule. Full audit reproducible via the script. ## What's deferred - **Calibrated-display verification** on real hardware in real terminals (truecolor and 256-color). Pre-1.0 gate. - **256-color downgrade table** for terminals that report no truecolor. - **Figlet / Nerd-Font recipes** for the large numeric readouts a display font would otherwise carry. - **`alloy theme ` subcommand** (see [CONSOLE.md](CONSOLE.md)), which wraps theme swap. The console reads themes at startup today, via `--theme` or a guess from `$COLORFGBG`; swapping in place without a relaunch is what remains. ## How this file is consumed The themes [makeover ships](https://git.sr.ht/~maxmj/makeover/tree/main/item/themes/) are the ground truth. `alloy_tui` at load time: 1. Reads the selected theme file via the `makeover` crate. 2. Computes the two derived tokens (`border-subtle`, `border-strong`) from `line.border`. 3. Exposes the full token map as a runtime `Theme` of ratatui `Style` / `Color` values. 4. Optionally emits a 256-color fallback table for terminals without truecolor. No hex values are hard-coded in Rust. Changing the theme file changes every authored Alloy surface after re-launch (or hot-swap once `alloy theme` ships). Third-party themes downloaded into `~/.config/alloy/themes/` work with zero adapter code.