Media & Preferences
Overview
Media and preference modifiers let you apply utility styles conditionally based on user preferences and media conditions. They wrap utility declarations in CSS @media queries like prefers-color-scheme: dark and prefers-reduced-motion, generating 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 user's system prefers a dark color scheme.
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 {
@media (prefers-color-scheme: dark) {
background-color: #1a1a2e;
}
}
._dark\:text-color\:body-light {
@media (prefers-color-scheme: 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 to dark mode automatically.</p>
</body>
CSS Selector
| Modifier Name | Media Query |
|---|---|
dark | @media (prefers-color-scheme: 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 | @media (prefers-color-scheme: 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] generates _dark:property:value, _hover:property:value, and _dark:hover:property:value. This lets you create different hover effects for light and dark modes.useDarkModifier uses @media (prefers-color-scheme: dark) which responds to the operating system preference. For manual theme toggling via data-theme, use Styleframe's theme() API instead.