@layer theme { /* prefers-reduced-motion — a medical accommodation: vestibular disorders make sliding, fading and * spinning UI genuinely nauseating. Honoured globally rather than per component; every * transition and keyframe in this theme is decoration and none carries information. * * The !important here is the documented exception, and not the same kind as the ones that fight * an inline `style=`: a cascade layer cannot help, since styles/base sets transitions too and * !important INVERTS the layer order, so only an important declaration reaches all of them at * once. This is the canonical form as published by the WCAG working group. DO NOT COPY THE FLAG * OUT OF THIS BLOCK. * * .01ms rather than 0s: a zero duration means `transitionend`/`animationend` never fire, and JS * that waits for one (LuCI's modal teardown does) would hang. `scroll-behavior` is on the list * because smooth scrolling is motion too, and a jump is what this user asked for. */ @media (prefers-reduced-motion: reduce) { *, *::before, *::after { animation-duration: .01ms !important; animation-iteration-count: 1 !important; transition-duration: .01ms !important; scroll-behavior: auto !important; } } /* prefers-reduced-transparency — the user asked the OS for opaque surfaces. Every surface in the * theme is opaque already: no blur, an opaque bar, opaque pop surfaces. The one thing left is the * wash over an uploaded photo. */ @media (prefers-reduced-transparency: reduce) { :root { /* the uploaded background is a translucent wash over a photo — exactly the effect this * user asked the OS to drop. Make the scrim opaque so the canvas is flat, not a photo * ghosting through (16-login-bg.css lays it over the image as one background layer). */ --fs-photo-scrim: var(--fs-bg); } } /* prefers-contrast — the user asked the OS for MORE contrast but, unlike forced-colors, left the * palette to us. Same mechanism as reduced-transparency: re-state the tokens and every rule that * reads them follows. What moves and why: * - the hairlines: --fs-border is the most-read line in the theme and sits at ~20% of the * text's contrast, so it is pulled toward --fs-text. The role hairlines (-line/-line-hi, a * 40/55% tint of their role) go to the FULL role colour — a tint exists to be quiet, which * is the opposite of what this user asked for. * - secondary text: --fs-dim toward --fs-text, and --fs-faint most of the way there too * (10-11px eyebrow labels are where AA erodes first). Not all the way ONTO --fs-dim: the two * are the export tier's --text-color-medium and -low, so collapsing them republishes one * colour twice to every app reading the gradation. 82% is a window tools/export-tier.mjs * measures rather than takes on trust: below 80 the fainter ink drops under AA on footstrap * dark's --fs-panel2 (4.51:1 at 80), above 82 the high-to-low ramp falls under the 0.10 * spread the tier promises (0.098 at 84); 82 clears both, at 4.68:1 and 0.110. * - --fs-placeholder, in BOTH modes. It is the one token the theme ships deliberately under AA * (3.99-4.48:1 light, 3.43-6.05:1 dark): a hint has to move far enough not to read as a * typed value, and a dark palette has only 6.45:1 of ink to spend. A reader who asked the OS * for more contrast has said which of the two they want, so the mixes go back to the * AA-clearing 62% and 80% — 4.98:1 and 4.61:1, at 37% and 20% of the ink-to-field range * instead of 44% and 34%. Only --fs-placeholder-mix moves: the mix itself is spelt once, in * 03-palettes.css. `placeholder-ink` measures this pass as well. * - the focus ring: --fs-focus-ring is a tint of the accent (--fs-accent-soft, 10% light and * 15% dark), so at "more contrast" it carries the accent at 60% instead. Its sibling * --fs-focus-ring-solo is already the solid accent and needs no restatement here. */ @media (prefers-contrast: more) { :root { --fs-border: color-mix(in srgb, var(--fs-text) 45%, var(--fs-bg)); --fs-dim: color-mix(in srgb, var(--fs-text) 85%, var(--fs-bg)); --fs-faint: color-mix(in srgb, var(--fs-text) 82%, var(--fs-bg)); --fs-focus-ring: 0 0 0 3px color-mix(in srgb, var(--fs-accent) 60%, transparent); --fs-accent-line: var(--fs-accent); --fs-accent-line-hi: var(--fs-accent); --fs-good-line: var(--fs-good); --fs-good-line-hi: var(--fs-good); --fs-warn-line: var(--fs-warn); --fs-warn-line-hi: var(--fs-warn); --fs-danger-line: var(--fs-danger); --fs-danger-line-hi: var(--fs-danger); /* the placeholder is the one token shipped under AA (see the note above); this reader * asked for contrast, so both modes buy the AA ink back, at the percentages that clear * it. Light here, dark in the block below — per mode exactly as in 03-palettes.css. */ --fs-placeholder-mix: 62%; } :root[data-darkmode="true"] { --fs-placeholder-mix: 80%; } } /* forced-colors (Windows High Contrast) — the OS replaces every colour the theme picked, and * background-color on most elements is forced to the system canvas, so any state carried by * BACKGROUND alone vanishes: the active tab pill, the checked toggle and the selected dropdown * row all look exactly like their inactive siblings, which is a correctness bug rather than a * cosmetic one. Re-state those states with a border or outline, which forced-colors keeps, in * system colour keywords so they follow the user's scheme. */ @media (forced-colors: active) { /* Active tab, selected dropdown row, checked toggle: outline the state. * * `.ifacebox-head.active` and Apply join the list rather than getting a rule of their own, * being the same KIND of state — carried by a background, with no word to fall back on — and * the same repair. "This port is up" was indistinguishable from a dead port, and Save & Apply * did not read as a BUTTON at all: it is a .cbi-dropdown wrapper, so with its fill forced to * Canvas only the label inside the