// Pins :hover and :active visuals for state Stories (Storybook only, never loaded by the app). // Every rule that uses :hover or :active 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. const HOVER = "data-sb-hover" const ACTIVE = "data-sb-active" 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 (/:hover\b|:active\b/.test(rule.selectorText)) { list.insertRule( rule.cssText.replace(/:hover\b/g, `[${HOVER}]`).replace(/:active\b/g, `[${ACTIVE}]`), 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. } } }