Dark Mode

Add light, dark, and system theme switching to HareUI.

HareUI dark mode is CSS-driven. Components read theme variables from the root element, so there's no provider component to mount. Add the dark class or data-theme="dark" to <html> and every component picks up the dark theme automatically.

<html class="dark" data-theme="dark">
  <body class="bg-background text-foreground">
    <!-- Your app -->
  </body>
</html>

Keep the bg-background and text-foreground classes on your app shell so the page canvas changes with the active theme.

HareUI's built-in light and dark themes respond to both .light/.dark classes and data-theme="light"/data-theme="dark" attributes. If you set both manually, keep the values in sync.

A Toggle Composable

Since HareUI has no theme provider, write a small composable that toggles the dark class and persists the choice:

import { ref, watchEffect } from 'vue'

type Theme = 'light' | 'dark' | 'system'

const theme = ref<Theme>((localStorage.getItem('theme') as Theme) ?? 'system')

function resolve(value: Theme): 'light' | 'dark' {
  if (value === 'system') {
    return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'
  }
  return value
}

watchEffect(() => {
  const resolved = resolve(theme.value)
  document.documentElement.classList.toggle('dark', resolved === 'dark')
  document.documentElement.dataset.theme = resolved
  localStorage.setItem('theme', theme.value)
})

export function useDark() {
  return { theme, resolvedTheme: () => resolve(theme.value), setTheme: (value: Theme) => (theme.value = value) }
}
<script setup lang="ts">
import { Button } from '@hareui/vue'
import { useDark } from '../composables/useDark'

const { theme, setTheme } = useDark()
</script>

<template>
  <div class="flex items-center gap-2">
    <Button :variant="theme === 'light' ? 'primary' : 'secondary'" @click="setTheme('light')">
      Light
    </Button>
    <Button :variant="theme === 'dark' ? 'primary' : 'secondary'" @click="setTheme('dark')">
      Dark
    </Button>
    <Button :variant="theme === 'system' ? 'primary' : 'secondary'" @click="setTheme('system')">
      System
    </Button>
  </div>
</template>

If your app already uses VueUse, its useDark composable covers the same prefers-color-scheme detection, localStorage persistence, and class toggling — pass { selector: 'html', valueDark: 'dark' } and skip writing your own.

Styling for Both Themes

Theme-aware utilities work automatically because they read CSS variables:

<template>
  <main class="min-h-screen bg-background text-foreground">
    <section class="bg-surface text-surface-foreground shadow-surface">
      Theme-aware content
    </section>
  </main>
</template>

Use the dark: variant for one-off changes that only apply in dark mode:

<div class="bg-background text-foreground dark:border-default">
  Custom dark-mode adjustment
</div>

See Colors for every token's light and dark value, and Theming to override them.

On this page

No Headings