Vize

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.

Open the interactive example

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
Choose an actual guide color preference or follow the system: initial state
Initial state
Choose an actual guide color preference or follow the system: state after the verified interactions
After the verified interactions

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.