color-mode
Reactive color mode with persistence and DOM application.
| Package | @vizejs/composable/color-mode |
| Own the source | vize lib pull composable:color-mode |
| Runtime exports | useColorMode, useDark |
| Gzip budget | 3840 B |
Minimal setup
Use a Vue 3.5+ project with Vue SFC compilation configured (see Vite+ integration).
vp install @vizejs/composable
Import the entry directly. Call lifecycle-bound utilities inside <script setup> so cleanup follows the component scope; consult the runtime contract for this entry's SSR and hydration behavior.
import { useColorMode, useDark } from "@vizejs/composable/color-mode";
Try it
Choose an actual guide color preference or follow the system
Apply actual light, dark, automatic, or configured contrast modes while distinguishing the selected preference, system preference, and resolved mode.
What to observe: Choose Dark or Light with the native select to override system changes. Automatic follows the real browser color-scheme preference. Contrast applies its configured custom mode to the sample card. Follow system preference restores Automatic. When storage is available, reloading reads the previous selected preference after hydration.
useColorMode observes the actual prefers-color-scheme media query, applies data-guide-mode to this preview's own documentElement, and uses its actual origin's localStorage with a dedicated key. The source-owned CSS styles the sample card from that real attribute. mode is the writable selected preference; state resolves auto to system. stored means the value read at initialization/mount, not an immediately updated save receipt. Persistence is best-effort, so an in-tab mode is never labelled saved. SSR initially renders auto with a light system fallback; the component reads storage after mounting. The example disables transition suppression and supplies no fake storage/media host. Scope disposal removes its watchers/listeners. The parent site's theme is unaffected.
Compare the initial and interacted states


Copy the example
Save this complete SFC in your Vue project. Its public imports and source are the same ones executed in the preview.
<script setup lang="ts">
import { useId } from "vue";
import { useColorMode } from "@vizejs/composable/color-mode";
const choiceId = useId();
const { mode, system, state, stored } = useColorMode<"contrast">({
attribute: "data-guide-mode",
storageKey: "vize:docs:color-mode",
modes: { contrast: "contrast" },
disableTransition: false,
});
</script>
<template>
<div class="composable-example">
<label :for="choiceId">Guide color preference</label>
<select :id="choiceId" v-model="mode">
<option value="auto">Automatic</option>
<option value="dark">Dark</option>
<option value="light">Light</option>
<option value="contrast">Contrast</option>
</select>
<section class="mode-card" aria-label="Color preference sample">
<h3>Read the guide in your chosen mode</h3>
<p>The automatic choice follows this browser's actual system preference.</p>
</section>
<output aria-live="polite"
>Chosen: {{ mode }} · System: {{ system }} · Applied: {{ state }}</output
>
<p>Preference read at mount: {{ stored }}.</p>
<button type="button" @click="() => (mode = 'auto')">Follow system preference</button>
<p>
A manual choice overrides later system changes. Contrast is an explicitly configured custom
mode. This example applies an attribute to this preview's own HTML element; it does not change
the parent documentation. The in-tab choice still applies if browser storage cannot persist
it. Server rendering starts with Automatic and a Light system fallback, then the client reads
the actual preference after mounting.
</p>
</div>
</template>
<style scoped>
.mode-card {
--guide-sample-light-background: #ffffff;
--guide-sample-light-foreground: #182833;
--guide-sample-dark-background: #182833;
--guide-sample-dark-foreground: #fff9ee;
--guide-sample-contrast-background: #000000;
--guide-sample-contrast-foreground: #fff37c;
border: 1px solid currentColor;
border-radius: 8px;
padding: 12px;
background: var(--guide-sample-light-background);
color: var(--guide-sample-light-foreground);
& h3 {
margin-top: 0;
}
}
:global(html[data-guide-mode="dark"]) {
& .mode-card {
background: var(--guide-sample-dark-background);
color: var(--guide-sample-dark-foreground);
}
}
:global(html[data-guide-mode="contrast"]) {
& .mode-card {
background: var(--guide-sample-contrast-background);
color: var(--guide-sample-contrast-foreground);
}
}
</style>
Runtime contract
| Utility | Category | Stability | SSR | Hydration | Cleanup | Targets | Host globals | Uses |
|---|---|---|---|---|---|---|---|---|
useColorMode |
media | experimental | deterministic-fallback | caller-managed | reactive-scope | web, desktop | document, window |
useMediaQuery, resolveElement, tryOnScopeDispose |
useDark |
media | experimental | deterministic-fallback | caller-managed | reactive-scope | web, desktop | document, window |
useColorMode |
API
useColorMode
Reactive color mode with persistence and DOM application. The stored mode is read synchronously during setup through the injected storage, so a cookie adapter yields identical server and client renders. On the client the resolved mode is written to the target's class list (or attribute) after each change, optionally with transitions suppressed. Nothing touches the DOM during server rendering.
function useColorMode<const Custom extends string = never>( options: UseColorModeOptions<Custom> = {}, ): ColorModeControls<Custom>
useDark
Boolean dark-mode switch built on useColorMode. Setting the ref to the current system preference stores "auto", so the page keeps following the system until the user picks the opposite mode.
function useDark(options: UseDarkOptions = {}): WritableComputedRef<boolean>
Types
UseColorModeOptions
Options for useColorMode.
| Member | Type | Description |
|---|---|---|
modes? |
Readonly<Partial<Record<BasicColorMode, string>> & Record<Custom, string>> |
Class name (or attribute value) written for each mode. Custom modes must be listed here. |
attribute? |
string |
"class" toggles class names; any other value is used as an attribute name (e.g. "data-theme"). |
target? |
MaybeElementTarget |
Element that receives the class/attribute. |
initialValue? |
ColorModeValue<Custom> |
Mode used when nothing is stored. |
storageKey? |
string | null |
Storage key. null disables persistence. |
storage? |
ColorModeStorage | null |
Persistence adapter. null disables persistence. |
ssrSystem? |
BasicColorMode |
System preference assumed while no media capability exists (server rendering). |
disableTransition? |
boolean |
Suppress CSS transitions for one frame while switching modes. |
host? |
MaybeRefOrGetter<MediaQueryHost | null | undefined> |
Media capability used to read prefers-color-scheme. |
ColorModeControls
Reactive color mode returned by useColorMode.
| Member | Type | Description |
|---|---|---|
mode |
WritableComputedRef<ColorModeValue<Custom>> |
Selected mode, including "auto". Assigning persists and applies it. |
system |
ComputedRef<BasicColorMode> |
System preference from prefers-color-scheme. |
state |
ComputedRef<BasicColorMode | Custom> |
Resolved mode ("auto" replaced by the system preference). |
stored |
ComputedRef<ColorModeValue<Custom>> |
Value read from storage at creation (or the initial value). |
UseDarkOptions
Options for useDark.
| Member | Type | Description |
|---|---|---|
valueDark? |
string |
Class/attribute value applied in dark mode. |
valueLight? |
string |
Class/attribute value applied in light mode. |