/** * Generator for QDS UI State Variants * * This script generates Tailwind @custom-variant declarations for all ui attribute states. * Run this to generate the CSS, then copy it into your global.css file. * * Usage: * bun run docs/generate-ui-variants.ts */ type DataAttributeState = | "invalid" | "required" | "disabled" | "readonly" | "checked" | "selected" | "pressed" | "open" | "closed" | "highlighted" | "active" | "mixed" | "empty" | "horizontal" | "vertical"; /** * Generate positive and negative variant CSS for a given state * * @param state - The state name (e.g. "checked", "disabled") * @returns CSS string with @custom-variant declarations */ function generateVariant(state: DataAttributeState): string { const variantName = `ui-${state}`; const negatedVariantName = `not-ui-${state}`; const uiAttr = `ui-${state}`; return `/** * ${variantName}: Apply styles when nearest component scope has ${uiAttr} * Automatically scopes to prevent nested components from inheriting state */ @custom-variant ${variantName} ( /* Descendant of scope with ${uiAttr}, stops at nearest scope boundary */ [ui-qds-scope][${uiAttr}] > &, [ui-qds-scope][${uiAttr}] > :not([ui-qds-scope]) &, /* Direct match on element itself */ [${uiAttr}]& ); /** * ${negatedVariantName}: Apply styles when nearest component scope does NOT have ${uiAttr} * Automatically scopes to prevent nested components from inheriting state */ @custom-variant ${negatedVariantName} ( /* Descendant of scope without ${uiAttr}, stops at nearest scope boundary */ [ui-qds-scope]:not([${uiAttr}]) > &, [ui-qds-scope]:not([${uiAttr}]) > :not([ui-qds-scope]) &, /* Direct match on element itself ONLY if it's also a scope */ [ui-qds-scope]:not([${uiAttr}])& );`; } /** * Generate all variants */ export function generateAllVariants(): string { const states: DataAttributeState[] = [ "invalid", "required", "disabled", "readonly", "checked", "selected", "pressed", "open", "highlighted", "active", "mixed", "empty", "horizontal", "vertical", "closed" ]; const header = `/** * QDS UI State Variants * * These custom variants enable state-based styling that automatically scopes * to the nearest component scope, preventing style leakage in nested components. * * Usage: * ui-checked:bg-blue-500 * not-ui-disabled:opacity-100 * ui-open:rotate-180 * * Generated by: docs/generate-ui-variants.ts * DO NOT EDIT MANUALLY - Run the generator script to update */ `; const variants = states.map((state) => generateVariant(state)).join("\n\n"); return `${header}\n${variants}`; }