# Themes

> Style the site, and change the classes without touching the HTML.

## Two stylings

```js
theme: {
  framework: 'tailwind',
  darkMode: 'class',
},
```

`tailwind` compiles on demand only the classes actually used in the produced
pages. `custom` does without it entirely: a stylesheet written in the package
styles the site, with no styling dependency.

The templates are the same in both cases. Switching values requires touching
no page.

## The images of the site

Three images carry the identity of a project, and none of them is a stylesheet
matter — they are declared once, from the root of the project:

```js
logo: 'branding/logo.png',
favicon: 'branding/favicon.png',
socialImage: 'branding/social.jpg',
```

| Field         | Where it shows                 | What to give it                                                                       |
| ------------- | ------------------------------ | ------------------------------------------------------------------------------------- |
| `logo`        | In the header, beside the name | Any web image. A detailed logo reads badly at header size: crop the mark alone        |
| `favicon`     | In the browser tab             | `.ico`, `.png` or `.svg` — nothing else is shown by browsers                          |
| `socialImage` | The preview of a shared link   | A PNG or JPEG, 1200 × 630 pixels as a rule. Social networks read neither SVG nor AVIF |

A version may carry its own `logo` and `favicon`, which is how a beta tells
itself apart at a glance in the tab.

`socialImage` needs `siteUrl`, and the build refuses the pair when it is
missing rather than publish a preview nobody can fetch:

```
socialImage needs siteUrl.
Social networks only read an absolute address: set siteUrl, the public address of the site.
```

A preview is fetched by another machine, from the outside: a path that works in
your browser means nothing to it.

## The principle: slots, not classes

The templates **hard-code no class**. They ask the theme for the class of each
slot — the header, the menu, a navigation link — and the theme answers.

The fifty slots are listed in the [reference](/versions/latest/reference/theme/).

That is what lets a utility styling and a classic styling share the same HTML:
one answers `dp-nav-link`, the other a handful of utilities. The template
itself does not change.

Components follow the same rule. When the theme does not answer, they fall
back on a `dp-*` class that their stylesheet styles from the `--dp-*` tokens:
they therefore follow the active palette without knowing anything about it.

## Changing the colours

The tokens are listed in the [reference](/versions/latest/reference/theme/). They are
redefined from the configuration:

```js
theme: {
  framework: 'tailwind',
  tokens: {
    '--dp-accent': 'oklch(55% 0.2 250)',
    '--dp-radius': '0.75rem',
  },
},
```

| Token                                                           | What it sets                                                   |
| --------------------------------------------------------------- | -------------------------------------------------------------- |
| `--dp-bg`, `--dp-bg-soft`                                       | Backgrounds                                                    |
| `--dp-text`, `--dp-text-soft`                                   | Text                                                           |
| `--dp-border`, `--dp-rule`                                      | Borders and rules                                              |
| `--dp-accent`, `--dp-accent-soft`                               | Accent colour                                                  |
| `--dp-tip`, `--dp-attention`, `--dp-danger` (and their `-soft`) | Tones of the blocks set apart from the text                    |
| `--dp-radius`                                                   | Corner rounding                                                |
| `--dp-font`, `--dp-font-mono`                                   | Font families                                                  |
| `--dp-content-width`                                            | Reading width. `none` by default: the content fills its column |
| `--dp-sidebar-width`, `--dp-toc-width`                          | Side columns                                                   |

A redefined token propagates everywhere: components included, without any of
them having to know. It sets the **light** palette; the dark one is a set of
tokens of its own, redefined in the `theme/` folder — see below. A site held
dark by `darkMode: 'dark'` therefore shows nothing of a token set here.

## Adding CSS

```js
theme: {
  framework: 'tailwind',
  css: '.dp-article h2 { letter-spacing: -0.01em; }',
},
```

The content of `css` is appended to the produced stylesheet.

## Colour scheme

`theme.darkMode` decides which palette the reader gets. `'class'`, the default,
follows the reader's system; `'dark'` or `'light'` keeps one whatever the
system — the build sets it as a class on `<html>`.

The header also carries a button that switches between light and dark, and
remembers the reader's choice from page to page — a few hundred bytes of inline
script, the only one content pages carry. `theme.toggle: false` removes it.
Without JavaScript, the button does not show.

The dark palette is a set of tokens, the same under both themes. To change it,
redefine them in the `theme/` folder, for both ways of being dark — the
system's scheme, and the class that `darkMode: 'dark'` sets:

```css
@media (prefers-color-scheme: dark) {
  :root:not(.light) {
    --dp-bg: #060814;
  }
}

:root.dark {
  --dp-bg: #060814;
}
```

<ForTheme framework="tailwind">

The `dark:` utilities of your pages follow the same rule: they apply under the
system's dark scheme, and always once `darkMode: 'dark'` is set.

```mdx
<div className="bg-white dark:bg-slate-900">…</div>
```

</ForTheme>
<ForTheme framework="custom">

Your own classes follow it through the same two selectors:

```css
:root.dark .note {
  border-color: var(--dp-accent);
}
```

</ForTheme>

## Layer order

Under the `tailwind` theme, the stylesheet declares its layers in this order:

```css
@layer theme, base, components, utilities;
```

Component rules live in `components`, **below** the utilities. A `className`
set at use therefore always wins, whatever the place of the rule in the file:

```mdx
<Card className="border-0 shadow-none">…</Card>
```

Without this layer, a component rule written after a utility would beat it at
equal specificity, and the author's `className` would be ignored without a
word.

The `custom` theme has no utilities: a `className` there names classes of your
own. Write them in the `theme/` folder, at the root of the project: every `.css`
file in it is appended to the stylesheet, outside any layer, so they come
before the component rules. Under this theme, `init` starts the folder with
`theme/custom.css`.

## The stylesheet is compiled last

A utility theme only emits the rules of the classes actually used: it
therefore needs the rendered pages before it can compile. The build first
writes every page, collecting the classes along the way, then compiles the
stylesheet.

That is also why the slot classes are available **without** waiting for that
compilation: the templates need them to be rendered. The two things are kept
apart for this reason alone.
