# Theming

Turn on dark mode, scope a theme to a subtree, and override a single token value.

Every generated token file is a flat block of resolved literals under one
selector, and the selectors are ranked: a colour mode beats the base, a theme
beats a colour mode. Whichever order the files load in, the right one wins.

```css
[data-color-mode='dark'][data-color-mode] {
  --app-color-surface-bg-base: oklch(0.2 0.002 264);
  --app-color-primary-bg-solid: oklch(0.96 0.003 264);
}
```

The repeated attribute is the ranking. Each repetition adds one to the
selector’s specificity without changing what it matches, so the dark file
outranks `:root` and a theme file, with two repetitions, outranks both.

Nothing aliases anything at runtime, so cascade order is the whole of the
mechanism. We did look at a palette block with the semantic tokens pointing
into it through `var()`, and dropped it. When any link in a `var()` chain
resolves to something invalid, the declaration goes invalid at computed-value
time, and that falls back to the *inherited* value rather than to the previous
declaration in the cascade, so a component inside a themed container would take
its parent’s colour with nothing in the console to say so. A literal fails the
other way round, since a value the browser cannot parse is dropped at parse time
and the previous declaration wins.

Everything below describes the files the stock `mirror.config.ts` builds and the
selectors it gives them. A project that writes its own config decides both, so
if yours does, the file names and the attributes here are whatever you named
them. See [Configuration](/tokens/configuration).

## Files

List the base file and whatever themes you want to offer in the `css` array of
`nuxt.config.ts`, in any order. Without Nuxt, import the same paths at your
entry point instead.

```ts
css: [
  '~~/.maas/tokens/css/application.css',
  '~~/.maas/tokens/css/theme/dark/application.css',
  '~~/.maas/tokens/css/theme/mono/application.css',
]
```

Adding a file adds the theme and removing it takes it away again, with no
registration step in between.

## Dark mode

Dark mode ships as the second file in that list, and it selects on one
attribute.

```html
<html data-color-mode="dark">
```

Once the attribute is set, every Mirror class is already correct.
`bg-surface-base`, `text-surface-muted` and the rest resolve to different
literals underneath it, so neither a component nor a class name changes.

If you are using Nuxt, let `@nuxtjs/color-mode` write the attribute for you.
Set `dataValue` to `'color-mode'` in `nuxt.config.ts` and the module writes
`data-color-mode="dark"` onto `<html>`, which is the selector the dark file
already carries.

```ts
colorMode: {
  classSuffix: '',
  dataValue: 'color-mode',
}
```

There is a `dark:` variant too, bound to that attribute rather than to
`prefers-color-scheme`, so a user’s choice beats the system default. Reach for
it only when a mode needs genuinely different *tokens*, rather than different
values of the same token.

## Themes

`data-theme` is the second axis. Set it to the name of a theme you listed,
`mono` for `theme/mono/application.css`, and every property that file declares
applies from that element down.

::docs-table
---
columns:
  - label: Attribute
  - label: Selector
rows:
  - items:
      - label: data-color-mode
        description: Set by your colour-mode handling.
      - label: '[data-color-mode=''dark''][data-color-mode]'
  - items:
      - label: data-theme
        description: Set by you, wherever the theme starts.
      - label: '[data-theme=''mono''][data-theme][data-theme]'
---
::

Both are attribute selectors, so either one applies to a whole document or to
any subtree.

```vue
<template>
  <section data-theme="mono"></section>
</template>
```

::tip
The two axes are independent. A theme file overrides only the properties it
declares, so a theme that changes typography leaves the colour mode alone.
::

## Overriding one value

The custom properties are ordinary CSS and they inherit, so a single override
needs no build step and no config.

```css
.marketing-hero {
  --app-color-surface-bg-base: oklch(0.18 0.04 264);
}
```

Scope it to the smallest element that needs it, and everything inside picks it
up while everything outside keeps the base value.

## Further reading

- [Configuration](/tokens/configuration): add a build target so a whole theme
  compiles into its own file, and pick its selector from the ladder.
- [Overview](/tokens/overview): the two layers, and how to read a token path.
