// Pins :hover, :active and :focus-visible visuals for state Stories (Storybook only, never loaded by the app). // Every rule that uses :hover, :active or :focus-visible is cloned right after itself with the pseudo-class swapped for a // data attribute, so cascade order and specificity stay the same. No declaration or value is authored here. // 2026-11-13: :focus-visible → data-sb-focus, so a Focus specimen can show its ring without holding the page focus (only one // element can be focused; e.g. Selection Controls focuses the Checkbox and pins the Radio Group / Switch rings). const HOVER = "data-sb-hover" const ACTIVE = "data-sb-active" const FOCUS = "data-sb-focus" const PSEUDO = /:hover\b|:active\b|:focus-visible\b/ // A negated :focus-visible (:not(:focus-visible)) would match every element without the attribute, so such a rule keeps its // :focus-visible and only its :hover / :active (if any) are swapped. const NEGATED_FOCUS = /:not\([^)]*:focus-visible/ const mirrored = new WeakSet() function mirror(list: CSSStyleSheet | CSSGroupingRule) { for (let i = list.cssRules.length - 1; i >= 0; i--) { const rule = list.cssRules[i] if (rule instanceof CSSStyleRule) { if (PSEUDO.test(rule.selectorText)) { // `base` keeps :focus-visible (the pre-2026-11-13 clone), so real focus + data-sb-hover / data-sb-active still matches // a compound rule such as :focus-visible:active (Slider Focus + Drag). `full` also swaps :focus-visible. const base = rule.cssText.replace(/:hover\b/g, `[${HOVER}]`).replace(/:active\b/g, `[${ACTIVE}]`) const full = NEGATED_FOCUS.test(rule.selectorText) ? base : base.replace(/:focus-visible\b/g, `[${FOCUS}]`) if (full !== base) list.insertRule(full, i + 1) if (base !== rule.cssText) list.insertRule(base, i + 1) } } else if ("cssRules" in rule) { mirror(rule as CSSGroupingRule) } } } export function ensurePseudoStates() { for (const sheet of document.styleSheets) { if (mirrored.has(sheet)) continue mirrored.add(sheet) try { mirror(sheet) } catch { // Unreadable (cross-origin) sheet: nothing to mirror. } } }