Theming
A Roots app has twelve colour tokens. roots.lisp sets them, the base styles, the UI kit and the colour utilities (bg-primary, text-muted, border-error) use them, and each is a CSS variable (--roots-primary) for your own styles.
:theme (:primary "#8a5200" :accent "#2c6a47" :bg "#f7f4ea" :surface "#efeadb" :text "#141c1a"
:dark (:primary "#f0a640" :accent "#93c3a0" :bg "#0f1716" :surface "#15201e" :text "#ebe6d8"))Those are the colours of this site. The build measures them: a text colour that is under 4.5:1 on its background is the warning B168, with the ratio.
Light and dark #
Dark mode follows the system until the app chooses. The choice is made with the theme API of roots/platform, remembered on the device, and applied before the first paint on the next visit.
You chose system; the page shows light.
theme-switch.vue
<script setup lang="ts">
import { theme } from "roots/platform"
const choices = ["system", "light", "dark"] as const
</script>
<template>
<Box class="theme-switch gap-3">
<Box class="flex-row gap-2" role="group" aria-label="Theme">
<Button v-for="choice in choices" :key="choice" :label="choice" :aria-pressed="theme.current.value === choice"
:class="{ 'theme-switch-on': theme.current.value === choice }" @click="theme.set(choice)" />
</Box>
<Box class="theme-switch-swatches flex-row gap-2">
<span class="theme-switch-swatch bg-primary"></span>
<span class="theme-switch-swatch bg-accent"></span>
<span class="theme-switch-swatch bg-surface"></span>
<span class="theme-switch-swatch bg-text"></span>
</Box>
<Text>You chose <strong>{{ theme.current.value }}</strong>; the page shows <strong>{{ theme.resolved.value }}</strong>.</Text>
</Box>
</template>
<style>
.theme-switch { max-width: 30rem; }
.theme-switch-swatches { flex-wrap: wrap; }
.theme-switch-on { outline: 2px solid var(--roots-primary); outline-offset: 1px; }
.theme-switch-swatch { width: 44px; height: 28px; border-radius: 6px; border: 1px solid var(--roots-border); }
</style>theme.set("dark" | "light" | "system")chooses.theme.currentis the choice,theme.resolvedwhat is shown now; both are refs.- The switch in this site's header is the same API. In a terminal the same calls switch the terminal app's colour scheme.
The details (the variables besides the tokens, how the stylesheet switches without JavaScript, theme.js) are in Building apps: Theme.