Media Preferences
SSR-safe user media preferences (motion, transparency, forced colors, contrast, color scheme).
| Package | @vizejs/ui/media-preferences |
| Maturity | stable |
| Own the source | vize lib pull media-preferences |
| Requires | context |
| Aliases | reduced motion, reduced transparency, forced colors, high contrast, prefers-contrast, color scheme |
| Covers | CSS prefers-reduced-motion, CSS prefers-reduced-transparency, CSS forced-colors, CSS prefers-contrast, CSS prefers-color-scheme, User-Agent Client Hints |
Minimal setup
Use a Vue 3.5+ project with Vue SFC compilation configured (see Vite+ integration).
vp install @vizejs/ui
Add the public import below to <script setup lang="ts">. Components provide behavior without styles; the preview uses optional Paper styles.
import { MediaPreferencesProvider } from "@vizejs/ui/media-preferences";
Or copy the source into your project with vize lib pull media-preferences (see Source Distribution).
Try it
This is the basic example maintained with the component source, using the public package imports. The preview adds the optional Paper styles.
Open the interactive example · Styling these components
Copy the example
<!-- App settings that override the operating system's reduced-motion preference. -->
<script setup lang="ts">
import { computed, ref, useId } from "vue";
import { MediaPreferencesProvider } from "@vizejs/ui/media-preferences";
const reduceMotion = ref(false);
const checkboxId = useId();
const force = computed(() => (reduceMotion.value ? { reducedMotion: true } : {}));
</script>
<template>
<MediaPreferencesProvider :force>
<input :id="checkboxId" v-model="reduceMotion" type="checkbox" />
<label :for="checkboxId">Reduce motion in this app</label>
</MediaPreferencesProvider>
</template>
API
MediaPreferencesProvider
Source: src/families/accessibility/media-preferences/media-preferences-provider.vue
Props
| Prop | Type | Default | Description |
|---|---|---|---|
initial |
MediaPreferencesOverrides |
undefined |
Preferences assumed on the server and until the client detects them, for example from Sec-CH-Prefers-Reduced-Motion client hints or a cookie. |
force |
MediaPreferencesOverrides |
undefined |
Preferences that always win over detection, for example an in-app "reduce motion" setting. |
Slots
| Slot | Slot props | Description |
|---|---|---|
default |
MediaPreferencesSlotState |
Subtree that adapts to the effective preferences. |
Exposed
| Member | Type | Description |
|---|---|---|
reducedMotion |
boolean |
prefers-reduced-motion: reduce. |
reducedTransparency |
boolean |
prefers-reduced-transparency: reduce. |
forcedColors |
boolean |
forced-colors: active (Windows High Contrast and similar modes). |
contrast |
ContrastPreference |
prefers-contrast value. |
colorScheme |
ColorSchemePreference |
prefers-color-scheme value. |
element |
HTMLDivElement | null |
Rendered provider element. |
Behavior
Normative behavior for @vizejs/ui/media-preferences. media-preferences-provider.vue
(MediaPreferencesProvider) and the composables expose prefers-reduced-motion,
prefers-reduced-transparency, forced-colors, prefers-contrast, and prefers-color-scheme
without touching window before mount, so server and hydration renders agree. Every row is
proven by the named test.
| State x input | Observable outcome | Proven by |
|---|---|---|
| mounted provider, matching media queries | Root publishes data-reduced-motion, data-reduced-transparency, data-forced-colors (present when true), data-prefers-contrast, data-color-scheme; the slot receives the snapshot. |
provider publishes detected preferences as data attributes after mount |
media query change events |
Every preference is re-read and attributes update; unmount removes every listener. | provider follows media query change events and cleans up listeners |
force |
Forced values always win over detection; clearing force restores detected values. |
force overrides detection and initial is only a pre-detection hint |
initial |
Hints apply before detection only; detected values replace them after mount. | force overrides detection and initial is only a pre-detection hint |
| composables inside a provider | usePrefersReducedMotion, usePrefersReducedTransparency, useForcedColors, usePrefersContrast, usePrefersColorScheme read the provider's effective values. |
composables read the provider context |
| standalone composables | In components they return defaults during setup and subscribe after mount; in bare effect scopes they subscribe immediately; outside a scope they throw VIZE_UI_MEDIA_PREFERENCES_SETUP. |
standalone composables subscribe after mount and in bare effect scopes |
| tracker helpers | readMediaPreferences, mergeMediaPreferences, and the tracker's start/stop/dispose lifecycle behave as documented. |
tracker helpers read, merge, stop, and dispose |
| provider expose | Exposes each effective preference and the root element. | provider exposes effective preferences |
| SSR | Server markup is byte-identical and built from defaults plus initial hints. |
renders byte-identical markup from defaults and initial hints |
| hydration | Hydration matches the server snapshot with zero warnings, then detected preferences apply. | hydrates with the server snapshot, then applies detected preferences |
| public types | Contrast, color scheme, overrides, and composable return types are closed contracts. | src/families/accessibility/media-preferences/media-preferences.types.test-d.ts |
| DOM/SSR/Vapor | media-preferences-provider.vue compiles in every renderer lane. |
scripts/check-renderers.ts |
| Target | Public contract |
|---|---|
| Root | part="root", data-vize-ui="media-preferences-provider", data-reduced-motion, data-reduced-transparency, data-forced-colors, data-prefers-contrast, data-color-scheme |
MediaPreferences ships no stylesheet.