/** * ================================================================================================ * HeroUI Custom Tailwind CSS Variants * ================================================================================================ * * Custom variants with intelligent fallback: explicit controls override system preferences. * * Usage: * motion-reduce:opacity-0 - Reduced motion accessibility * motion-safe:animate-spin - Full motion effects * dark:bg-gray-900 - Dark mode styles * * Priority: Explicit data attributes/classes → System preferences (fallback) * ================================================================================================ */ /* ------------------------------------------------------------------------------------------------- * motion-reduce — Minimize animations for accessibility * Priority: data-reduce-motion="true" → prefers-reduced-motion: reduce * ------------------------------------------------------------------------------------------------- */ @custom-variant motion-reduce { /* Explicit: data-reduce-motion="true" on element or ancestor */ &:is([data-reduce-motion="true"], [data-reduce-motion="true"] *) { @slot; &::before, &::after { @slot; } } /* Fallback: system prefers-reduced-motion setting */ @media (prefers-reduced-motion: reduce) { &:not(:is([data-reduce-motion="true"], [data-reduce-motion="true"] *)) { @slot; &::before, &::after { @slot; } } } } /* ------------------------------------------------------------------------------------------------- * motion-safe — Enable full animations and transitions * Priority: data-reduce-motion="false" → prefers-reduced-motion: no-preference * ------------------------------------------------------------------------------------------------- */ @custom-variant motion-safe { /* Explicit: data-reduce-motion="false" on element or ancestor */ &:is([data-reduce-motion="false"], [data-reduce-motion="false"] *) { @slot; &::before, &::after { @slot; } } /* Fallback: system has no motion preference set */ @media (prefers-reduced-motion: no-preference) { &:not( :is( [data-reduce-motion="true"], [data-reduce-motion="true"] *, [data-reduce-motion="false"], [data-reduce-motion="false"] * ) ) { @slot; &::before, &::after { @slot; } } } } /* ------------------------------------------------------------------------------------------------- * dark — Dark mode styles * Priority: .dark class / data-theme="dark" → prefers-color-scheme: dark * ------------------------------------------------------------------------------------------------- */ @custom-variant dark { /* Explicit: .dark class or data-theme="dark" on element or ancestor */ &:is(.dark, .dark *, [data-theme="dark"], [data-theme="dark"] *) { @slot; &::before, &::after { @slot; } } /* Fallback: system prefers dark color scheme */ @media (prefers-color-scheme: dark) { &:not(:is(.dark, .dark *, [data-theme="dark"], [data-theme="dark"] *)) & { @slot; &::before, &::after { @slot; } } } }