Media & Preferences Modifiers
useMediaPreferenceModifiers is included in the Modifiers Preset (useModifiersPreset) and you can configure it through the preset's mediaPreferences option. For most projects, applying it via the preset is the recommended approach.Overview
Media and preference modifiers let you apply utility styles conditionally based on user preferences and media conditions. Most wrap utility declarations in CSS @media queries like prefers-reduced-motion and prefers-contrast; the dark modifier is theme-driven and instead targets the .dark-theme / [data-theme="dark"] selector. Together they generate variant utility classes that adapt to the user's environment.
Why Use Media & Preference Modifiers?
Media and preference modifiers help you:
- Support dark mode: Automatically switch colors and styles based on the user's color scheme preference
- Respect motion preferences: Disable or reduce animations for users who prefer reduced motion
- Handle contrast preferences: Increase or decrease contrast for users who need it
- Target orientation: Adapt layouts for portrait and landscape orientations
- Support print styles: Apply print-specific styling without separate stylesheets
- Handle forced colors: Adapt to Windows High Contrast Mode and similar settings
useDarkModifier
The useDarkModifier() function creates a modifier that applies styles when the dark theme is active. It generates a single :is(.dark-theme, [data-theme="dark"]) class/attribute selector — the same selector the dark theme's token values use — and forwards variables and children through it.
To follow the operating-system preference, mirror it into data-theme from JavaScript (window.matchMedia('(prefers-color-scheme: dark)')); see the Theme Switcher Guide.
import { styleframe } from "styleframe";
import { useBackgroundColorUtility, useTextColorUtility } from "@styleframe/theme";
import { useDarkModifier } from "@styleframe/theme";
const s = styleframe();
const dark = useDarkModifier(s);
useBackgroundColorUtility(s, {
surface: '#ffffff',
'surface-dark': '#1a1a2e',
}, [dark]);
useTextColorUtility(s, {
body: '#212529',
'body-light': '#e2e8f0',
}, [dark]);
export default s;
._background-color\:surface { background-color: #ffffff; }
._background-color\:surface-dark { background-color: #1a1a2e; }
._text-color\:body { color: #212529; }
._text-color\:body-light { color: #e2e8f0; }
._dark\:background-color\:surface-dark {
:is(.dark-theme, [data-theme="dark"]) & {
background-color: #1a1a2e;
}
}
._dark\:text-color\:body-light {
:is(.dark-theme, [data-theme="dark"]) & {
color: #e2e8f0;
}
}
<body class="_background-color:surface _dark:background-color:surface-dark _text-color:body _dark:text-color:body-light">
<p>This text adapts when the dark theme is active (data-theme="dark").</p>
</body>
The dark modifier composes with other modifiers. When combined (e.g., [dark, hover]), modifiers nest inside-out — dark wraps hover:
._dark\:hover\:background-color\:primary {
:is(.dark-theme, [data-theme="dark"]) & {
&:hover {
background-color: var(--color--primary);
}
}
}
CSS Selector
| Modifier Name | Selectors |
|---|---|
dark | :is(.dark-theme, [data-theme="dark"]) & |
useMotionSafeModifier
The useMotionSafeModifier() function creates a modifier that applies styles only when the user has not requested reduced motion.
import { styleframe } from "styleframe";
import { useTransitionDurationUtility } from "@styleframe/theme";
import { useMotionSafeModifier } from "@styleframe/theme";
const s = styleframe();
const motionSafe = useMotionSafeModifier(s);
useTransitionDurationUtility(s, {
300: '300ms',
500: '500ms',
}, [motionSafe]);
export default s;
._transition-duration\:300 { transition-duration: 300ms; }
._motion-safe\:transition-duration\:300 {
@media (prefers-reduced-motion: no-preference) {
transition-duration: 300ms;
}
}
<!-- Only animate when motion is allowed -->
<div class="_motion-safe:transition-duration:300">
Animated content
</div>
CSS Selector
| Modifier Name | Media Query |
|---|---|
motion-safe | @media (prefers-reduced-motion: no-preference) |
useMotionReduceModifier
The useMotionReduceModifier() function creates a modifier that applies styles when the user prefers reduced motion.
import { styleframe } from "styleframe";
import { useTransitionDurationUtility, useAnimationUtility } from "@styleframe/theme";
import { useMotionReduceModifier } from "@styleframe/theme";
const s = styleframe();
const motionReduce = useMotionReduceModifier(s);
useTransitionDurationUtility(s, {
0: '0ms',
}, [motionReduce]);
useAnimationUtility(s, {
none: 'none',
}, [motionReduce]);
export default s;
._motion-reduce\:transition-duration\:0 {
@media (prefers-reduced-motion: reduce) {
transition-duration: 0ms;
}
}
._motion-reduce\:animation\:none {
@media (prefers-reduced-motion: reduce) {
animation: none;
}
}
<!-- Disable animation for users who prefer reduced motion -->
<div class="_motion-reduce:animation:none _motion-reduce:transition-duration:0">
Accessible content
</div>
CSS Selector
| Modifier Name | Media Query |
|---|---|
motion-reduce | @media (prefers-reduced-motion: reduce) |
useContrastMoreModifier
The useContrastMoreModifier() function creates a modifier that applies styles when the user prefers more contrast.
CSS Selector
| Modifier Name | Media Query |
|---|---|
contrast-more | @media (prefers-contrast: more) |
useContrastLessModifier
The useContrastLessModifier() function creates a modifier that applies styles when the user prefers less contrast.
CSS Selector
| Modifier Name | Media Query |
|---|---|
contrast-less | @media (prefers-contrast: less) |
usePortraitModifier
The usePortraitModifier() function creates a modifier that applies styles in portrait orientation.
CSS Selector
| Modifier Name | Media Query |
|---|---|
portrait | @media (orientation: portrait) |
useLandscapeModifier
The useLandscapeModifier() function creates a modifier that applies styles in landscape orientation.
CSS Selector
| Modifier Name | Media Query |
|---|---|
landscape | @media (orientation: landscape) |
usePrintModifier
The usePrintModifier() function creates a modifier that applies styles for print media.
import { styleframe } from "styleframe";
import { useDisplayUtility, useBackgroundColorUtility } from "@styleframe/theme";
import { usePrintModifier } from "@styleframe/theme";
const s = styleframe();
const print = usePrintModifier(s);
useDisplayUtility(s, {
none: 'none',
}, [print]);
useBackgroundColorUtility(s, {
white: '#ffffff',
}, [print]);
export default s;
._print\:display\:none {
@media print { display: none; }
}
._print\:background-color\:white {
@media print { background-color: #ffffff; }
}
<!-- Hide navigation when printing -->
<nav class="_print:display:none">Site Navigation</nav>
<!-- White background for print -->
<main class="_print:background-color:white">
Printable content
</main>
CSS Selector
| Modifier Name | Media Query |
|---|---|
print | @media print |
useForcedColorsModifier
The useForcedColorsModifier() function creates a modifier that applies styles when forced colors mode is active (e.g., Windows High Contrast Mode).
CSS Selector
| Modifier Name | Media Query |
|---|---|
forced-colors | @media (forced-colors: active) |
useMediaPreferenceModifiers
The useMediaPreferenceModifiers() function registers all media and preference modifiers at once and returns them as a destructurable object.
Returned Modifiers
| Key | Modifier Name | Media Query |
|---|---|---|
dark | dark | :is(.dark-theme, [data-theme="dark"]) & |
motionSafe | motion-safe | @media (prefers-reduced-motion: no-preference) |
motionReduce | motion-reduce | @media (prefers-reduced-motion: reduce) |
contrastMore | contrast-more | @media (prefers-contrast: more) |
contrastLess | contrast-less | @media (prefers-contrast: less) |
portrait | portrait | @media (orientation: portrait) |
landscape | landscape | @media (orientation: landscape) |
print | print | @media print |
forcedColors | forced-colors | @media (forced-colors: active) |
Examples
Dark Mode with Accessible Motion
import { styleframe } from "styleframe";
import {
useBackgroundColorUtility,
useTextColorUtility,
useTransitionDurationUtility,
} from "@styleframe/theme";
import { useMediaPreferenceModifiers } from "@styleframe/theme";
const s = styleframe();
const { dark, motionSafe, motionReduce } = useMediaPreferenceModifiers(s);
useBackgroundColorUtility(s, {
surface: '#ffffff',
'surface-dark': '#0f172a',
}, [dark]);
useTextColorUtility(s, {
body: '#1e293b',
'body-dark': '#e2e8f0',
}, [dark]);
useTransitionDurationUtility(s, {
200: '200ms',
0: '0ms',
}, [motionSafe, motionReduce]);
export default s;
<div class="_background-color:surface _dark:background-color:surface-dark _text-color:body _dark:text-color:body-dark _motion-safe:transition-duration:200 _motion-reduce:transition-duration:0">
Adaptive content
</div>
Print-Friendly Layout
import { styleframe } from "styleframe";
import {
useDisplayUtility,
useBackgroundColorUtility,
useTextColorUtility,
} from "@styleframe/theme";
import { usePrintModifier } from "@styleframe/theme";
const s = styleframe();
const print = usePrintModifier(s);
useDisplayUtility(s, {
none: 'none',
block: 'block',
}, [print]);
useBackgroundColorUtility(s, {
transparent: 'transparent',
}, [print]);
useTextColorUtility(s, {
black: '#000000',
}, [print]);
export default s;
<header class="_print:display:none">Site Header</header>
<nav class="_print:display:none">Navigation</nav>
<main class="_print:background-color:transparent _print:text-color:black">
<article>Printable article content</article>
</main>
<footer class="_print:display:none">Site Footer</footer>
Best Practices
- Use
darkfor color scheme adaptation: Provide alternative colors for dark mode without JavaScript - Always handle reduced motion: Use
motion-reduceto disable animations for users who need it - Choose between motion-safe and motion-reduce: Use
motion-safeto add animations (opt-in) ormotion-reduceto remove them (opt-out) - Test print styles: Use browser print preview to verify
printmodifier results - Consider contrast preferences: Use
contrast-moreandcontrast-lessfor users with visual needs - Use forced-colors sparingly: Only override forced colors mode when absolutely necessary for content comprehension
FAQ
motion-safe if you want animations to be opt-in (only animate when the user hasn't restricted motion). Use motion-reduce if you want to override specific animations when reduced motion is preferred. The motion-safe approach is generally safer as it starts without animation and adds it when allowed.[dark, hover] to generate a combined variant. The dark modifier wraps hover, producing nested selectors: :is(.dark-theme, [data-theme="dark"]) & { &:hover { ... } }. This lets you create different hover effects for dark mode.data-theme is exactly what it responds to. The useDarkModifier generates a single :is(.dark-theme, [data-theme="dark"]) & selector, matching the values set by the dark theme. It does not emit @media (prefers-color-scheme: dark); to follow the operating-system preference, mirror it into data-theme (see the Theme Switcher Guide).