/** * `--color-border-control` against every surface a control actually paints itself. * * WHY THIS EXISTS AS ITS OWN FILE. This is the check that would have caught Question J, and * nothing in the suite did. The token was chosen by measuring it against the *page* — 4.78:1 * light, 3.73:1 dark — and it does clear 3:1 there. But a field's resting border sits against * the field's own fill, and on `--color-surface-secondary` the dark value measured **2.83:1**, * under WCAG 1.4.11's 3:1 floor, across all seven components that share the token. * * The bug was not a wrong number. It was measuring the right token against the wrong surface, * which no amount of care in a per-component test would have found, because every one of those * tests measured the pairing its own component happened to emit and none of them owned the * shared token. So this file asserts the property of the *token layer*: for every surface any * control pairs it with, in both themes, it clears the floor. * * The surfaces are derived from the components' own `cva` output rather than named here. Naming * them is the failure mode described in `docs/AGENTS.md`: a test that names its tokens asserts a * fact about the token layer and stays green after the component moves to something else. * * **`Input` AND `Select` ARE DELIBERATELY NOT IN THIS LIST ANY MORE, and the guard test is why * they could be removed safely.** Figma draws an atom for each — `.input-text` and * `.input-dropdown` — and both strokes are Figma's own: `--color-border-field-figma` (#38425e, * 1.40:1) and `--color-border-field-focus` (#3acefa, 7.58:1), the first shipping as a rule-1 * conflict recorded in `docs/FIGMA-EXCEPTIONS.md`. Measuring a logged conflict against a 3:1 floor * would assert the value this system has decided not to hold. * * What remains are the controls Figma draws NO atom for, which is exactly the gap * `--color-border-control` exists to fill — and it makes the role's scope legible for the first * time: this file is now a list of Strix's own controls. * * Had the first test in each block not existed, dropping those two boundaries would have left the * 3:1 checks green over empty pairing sets. That is the whole reason it is there. */ import { describe, expect, it } from 'vitest'; import { comboboxInputStyles } from '../atoms/combobox/combobox'; import { textareaStyles } from '../atoms/textarea/textarea'; import { checkPairings, colorTokensIn } from './contrast.test-utils'; /** The floor WCAG 1.4.11 sets for the visual information that identifies a control. */ const BOUNDARY_MINIMUM = 3; const CONTROL = '--color-border-control'; /** * Every surface token that appears anywhere in a control's own class output, paired with the * fact that the control also draws `--color-border-control`. Deliberately over-inclusive: a * surface the border can *ever* sit on has to clear the floor, and narrowing this to the * "resting" combination is what let 2.83:1 through in the first place. */ const controls = [ ['Combobox input', () => comboboxInputStyles({})], ['Textarea', () => textareaStyles({})], ] as const; describe('--color-border-control', () => { for (const [name, render] of controls) { describe(name, () => { const tokens = colorTokensIn(render()); it('draws the shared control boundary, so the surfaces below are the right ones to check', () => { expect(tokens.borders).toContain(CONTROL); }); it('clears 3:1 against every surface it paints itself, in both themes', () => { // `colorTokensIn` already buckets disabled-only tokens away, so this is the enabled set. expect( tokens.surfaces.length, 'no surfaces derived — the extraction, not the token, is broken' ).toBeGreaterThan(0); const { failures, unmeasurable } = checkPairings( tokens.surfaces.map((surface) => ({ name: `${CONTROL} on ${surface}`, ink: CONTROL, surface, minimum: BOUNDARY_MINIMUM, })) ); expect(failures).toEqual([]); expect(unmeasurable).toEqual([]); }); /** * WCAG 1.4.11 exempts inactive controls, so the faint disabled surface is the design and * not a defect. Asserted as a bucket so the exemption cannot quietly widen: anything that * lands in `surfaces` is checked above, and only what a `disabled:` prefix put in * `disabledOnly` is skipped. */ it('skips only what a disabled prefix put in its own bucket', () => { for (const token of tokens.surfaces) expect(token).not.toMatch(/disabled/); }); }); } });