tokencheck

package
v0.17.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 24, 2026 License: Apache-2.0 Imports: 8 Imported by: 0

Documentation

Overview

Package tokencheck validates user color-token overrides (GDK-769 wave, GDK-785) with the same color math tools/theme-check.mjs asserts the shipped palettes with at build time. User colors arrive at runtime, where the build gate cannot see them; this package is the write-time refusal point and the load-time warning source for that surface.

The two implementations are pinned together by golden vectors: tools/theme-check.mjs --emit-vectors writes testdata/token-vectors.json from the exact formulas below, and the tests consume the full file. A formula that drifts here shows up as a vector mismatch, not as a silently different acceptance boundary.

Tier policy (single source: the embedded catalog.json, generated by tools/token-catalog.mjs from web/src/app.css — GDK-787):

locked     10 tokens — grounds (bg-*), text tiers, search-match, shell.
           Overrides are refused; palette-level opening is the
           custom-palette scope (GDK-789).
validated  12 tokens — accent 4, border 2, status inks 5, focus-ring.
           Open for override, checked at write time by the rules below.
free       22 tokens — lozenge/avatar/dept chips, scrim, scrollbar-hover.
           Any valid hex passes.

Rule derivations — every floor is a current theme-check assertion, carried over verbatim (no floor is invented in this package):

status-pair               pairwise status-ink ΔEok ≥ 0.05
                          (theme-check.mjs PAIR_N, checkPairwise)
status-pair-deuteranopia  the same pairs after the Machado 2009
                          severity-1.0 simulation, ΔEok ≥ 0.04
                          (theme-check.mjs PAIR_D, DEUT_M — the matrix
                          must not be substituted or transposed)
status-role-floor         overridden status ink on each ground:
                          ≥ 4.5 on bg-base/panel/elevated (text role),
                          ≥ 3 on bg-hover/active (dot role), compared at
                          two-decimal rounding like theme-check cr()
                          ("status ink floors" + "per-role ink contrast
                          floors" sections)
accent-text-contrast      accent-text on accent-subtle and on each
                          ground ≥ 4.5 — the text-role floor theme-check
                          applies to ink-on-ground pairs, applied to the
                          surfaces accent-text is actually painted on
                          (badges on accent-subtle, keys and links on the
                          grounds; measured: every shipped palette clears
                          it on all six, minimum 5.27)

Deliberate narrowing (three-part justification, per the GDK-769 investigation Q2 recommendation adopted 2026-08-24): theme-check also holds status-ink loudness RANK invariant across palettes (its "rank" section). The runtime does not require rank of an override — rank is taste, not legibility; the build assertion stays in force for app.css palettes. Same formulas and same thresholds are kept everywhere rank IS legibility.

For accent, accent-hover, accent-subtle, border-subtle, border-strong and focus-ring, theme-check carries no named assertion today, so no floor is carried for them either — their carried rule is hex validity only, which is what their empty extra rule lists in catalog.json record. Adding a real floor means theme-check gains the assertion first, then the catalog rules lists and this package grow together (GDK-786 hand-off).

Input defense (GDK-769 axis 3): unknown token names are IGNORED with a warn-severity violation — a stale schema (a token renamed or removed in a newer catalog) must never panic or brick a config write. Malicious or corrupt values (huge strings, non-color payloads, truncated hex) are rejected as invalid hex; echoed values are truncated so an error message cannot be turned into a paste bomb.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func CatalogPalettes

func CatalogPalettes() []string

CatalogPalettes returns the palette axis (light first, then data-theme blocks in sorted order).

func CatalogValue

func CatalogValue(name, palette string) (string, bool)

CatalogValue returns the catalog value of a token in one palette.

func Contrast

func Contrast(a, b string) float64

Contrast is the WCAG relative-luminance contrast ratio; the lighter side is numerator regardless of argument order, as in theme-check.

func DEok

func DEok(a, b string) float64

DEok is the OKLab Euclidean distance between two hex colors.

func Deut

func Deut(hex string) string

Deut maps a hex color through the deuteranopia simulation and returns the resulting hex (channels clamped and rounded, like theme-check rgb2hex).

func TierOf

func TierOf(name string) (tier string, ok bool)

TierOf reports the tier of a token, by bare name ("accent") or CSS variable name ("--color-accent"). ok is false for names the catalog does not carry.

func ValidHex

func ValidHex(s string) bool

ValidHex reports whether s is an acceptable override value.

Types

type CatalogToken

type CatalogToken struct {
	Name        string            `json:"name"`
	CSSVar      string            `json:"cssVar"`
	Tier        string            `json:"tier"`
	Description string            `json:"description"`
	Rules       []string          `json:"rules"`
	Values      map[string]string `json:"values"`
}

CatalogToken is one entry of the generated token catalog.

func CatalogTokens

func CatalogTokens() []CatalogToken

CatalogTokens returns the embedded catalog in file (app.css) order.

type Severity

type Severity string

Severity of a violation. Reject means the write must be refused; warn means the input is carried but the user must be told.

const (
	SeverityReject Severity = "reject"
	SeverityWarn   Severity = "warn"
)

type Violation

type Violation struct {
	Token    string   `json:"token"`
	Rule     string   `json:"rule"`
	Severity Severity `json:"severity"`
	Measured string   `json:"measured,omitempty"`
	Floor    string   `json:"floor,omitempty"`
	Message  string   `json:"message"`
}

Violation is one rule outcome. Measured and Floor carry the real numbers so an agent can fix its own color without a round trip.

func ValidateTokens

func ValidateTokens(overrides, base map[string]string) []Violation

ValidateTokens checks a partial set of color-token overrides against the tier policy. Keys are bare token names ("accent") or CSS variable names ("--color-accent"); values are hex colors. base is the palette the overrides will be applied to (name → raw value, hex for everything the rules read); tokens the caller did not override are filled from it, so a one-token override is checked in the context it will actually render in.

The result is deterministic for identical inputs: per-token checks run in sorted name order, group rules in STATS/GROUNDS order. Duplicate keys that normalize to the same name (both "--color-x" and "x") resolve last-wins in sorted key order, like CSS. Unknown names never panic: they produce a warn violation and are ignored.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL