Presets
Nine built-in palettes for light and dark, a picker for trying them live, and how to extend one with your own colours.
A preset is a complete set of the fifteen colour tokens for both light and dark. Four are gray palettes that keep the monochrome look and differ only in tint and contrast; five add a tinted surface scale and a real accent colour for links, active tabs and focus rings. Every preset supports light and dark, so the theme toggle keeps working whichever one you choose.
Try them#
The picker below applies a preset to this page and remembers it in local storage. Switch the theme with the toggle in the header to see both schemes.
Click mono to return to the default.
Choosing a preset#
Set theme.preset. It is applied before any colors overrides, so you can start from the closest palette and change only a few values.
theme: {
preset: "ocean",
}| Preset | Character | Accent |
|---|---|---|
mono | Neutral grays with no tint. The default, and the set of values baked into the stylesheet. | Same as the foreground |
slate | Cool, slightly blue-tinted grays. | Same as the foreground |
warm | Warm paper and ink grays. | Same as the foreground |
contrast | Pure white and black with firmer borders. | Same as the foreground |
ocean | Blue-tinted surfaces with a blue accent. | #2563eb / #60a5fa |
dusk | Violet-tinted surfaces with a violet accent. | #7c3aed / #a78bfa |
forest | Green-tinted surfaces with a green accent. | #15803d / #4ade80 |
ember | Warm sand surfaces with an orange accent. | #ea580c / #fb923c |
rose | Rose-tinted surfaces with a crimson accent. | #e11d48 / #fb7185 |
Because mono is already the stylesheet default, choosing it emits no colour declarations. Every other preset expands to all fifteen tokens for :root and for .dark.
The accent token#
--pd-accent colours prose links, the active header tab underline, active tab and code tab underlines, and --pd-accent-foreground is the text colour for anything placed on an accent background. In the gray presets the accent equals the foreground, which is why they stay strictly monochrome. The tinted presets also use the accent as the focus ring.
Letting readers pick#
ThemePresetPicker from pzzadocs/ui is the component used above. Drop it into any page or into a layout slot to let visitors choose their own palette; the selection is written as a <style> element after the config styles and stored under the storageKey.
import { ThemePresetPicker } from "pzzadocs/ui";
<ThemePresetPicker presets={["mono", "ocean", "dusk"]} defaultPreset="mono" storageKey="my-docs-preset" />| Name | Type | Default | Description |
|---|---|---|---|
presets | ThemePreset[] | all presets | Presets to offer, in display order. |
defaultPreset | ThemePreset | "mono" | The preset the site is configured with. Selecting it removes the override. |
storageKey | string | null | "pd-preset" | localStorage key for the choice. null forgets it on reload. |
onChange | (preset: ThemePreset) => void | - | Called after a preset is applied. |
The picker is applied after hydration, so a stored choice appears a moment after first paint. For a fixed site-wide palette use theme.preset in the config instead, which is rendered on the server.
mono#
Neutral grays with no tint.
| Key | Token | Light | Dark |
|---|---|---|---|
background | --pd-background | #fafafa | #0c0c0c |
surface | --pd-surface | #ffffff | #121212 |
surface2 | --pd-surface-2 | #f2f2f2 | #1a1a1a |
surface3 | --pd-surface-3 | #e9e9e9 | #232323 |
foreground | --pd-foreground | #111111 | #ececec |
foreground2 | --pd-foreground-2 | #3d3d3d | #c4c4c4 |
mutedForeground | --pd-muted-foreground | #707070 | #8a8a8a |
faintForeground | --pd-faint-foreground | #a3a3a3 | #5c5c5c |
border | --pd-border | #e4e4e4 | #242424 |
borderStrong | --pd-border-strong | #cfcfcf | #343434 |
sidebar | --pd-sidebar | #fafafa | #0c0c0c |
ring | --pd-ring | #111111 | #ececec |
overlay | --pd-overlay | rgba(0, 0, 0, 0.4) | rgba(0, 0, 0, 0.6) |
accent | --pd-accent | #111111 | #ececec |
accentForeground | --pd-accent-foreground | #fafafa | #0c0c0c |
slate#
Cool, slightly blue-tinted grays.
| Key | Token | Light | Dark |
|---|---|---|---|
background | --pd-background | #f8f9fb | #0b0e12 |
surface | --pd-surface | #ffffff | #11151b |
surface2 | --pd-surface-2 | #eff1f5 | #181d25 |
surface3 | --pd-surface-3 | #e4e7ed | #20262f |
foreground | --pd-foreground | #0f1419 | #e6eaf0 |
foreground2 | --pd-foreground-2 | #3b434f | #c0c7d1 |
mutedForeground | --pd-muted-foreground | #6b7380 | #8690a0 |
faintForeground | --pd-faint-foreground | #9aa3b0 | #596273 |
border | --pd-border | #e2e6ec | #222933 |
borderStrong | --pd-border-strong | #c9cfd8 | #323b47 |
sidebar | --pd-sidebar | #f8f9fb | #0b0e12 |
ring | --pd-ring | #0f1419 | #e6eaf0 |
overlay | --pd-overlay | rgba(10, 14, 20, 0.45) | rgba(0, 0, 0, 0.6) |
accent | --pd-accent | #0f1419 | #e6eaf0 |
accentForeground | --pd-accent-foreground | #f8f9fb | #0b0e12 |
warm#
Warm paper and ink grays.
| Key | Token | Light | Dark |
|---|---|---|---|
background | --pd-background | #fbfaf8 | #0f0e0d |
surface | --pd-surface | #ffffff | #161412 |
surface2 | --pd-surface-2 | #f3f1ed | #1f1c19 |
surface3 | --pd-surface-3 | #e9e6e0 | #282420 |
foreground | --pd-foreground | #1a1816 | #ebe6df |
foreground2 | --pd-foreground-2 | #44403b | #c6bfb6 |
mutedForeground | --pd-muted-foreground | #746e67 | #8f877e |
faintForeground | --pd-faint-foreground | #a69f97 | #5f5850 |
border | --pd-border | #e6e2db | #27231f |
borderStrong | --pd-border-strong | #d2ccc3 | #383229 |
sidebar | --pd-sidebar | #fbfaf8 | #0f0e0d |
ring | --pd-ring | #1a1816 | #ebe6df |
overlay | --pd-overlay | rgba(20, 16, 12, 0.45) | rgba(0, 0, 0, 0.6) |
accent | --pd-accent | #1a1816 | #ebe6df |
accentForeground | --pd-accent-foreground | #fbfaf8 | #0f0e0d |
contrast#
Pure white and black with firmer borders.
| Key | Token | Light | Dark |
|---|---|---|---|
background | --pd-background | #ffffff | #000000 |
surface | --pd-surface | #ffffff | #000000 |
surface2 | --pd-surface-2 | #f0f0f0 | #161616 |
surface3 | --pd-surface-3 | #e0e0e0 | #262626 |
foreground | --pd-foreground | #000000 | #ffffff |
foreground2 | --pd-foreground-2 | #1f1f1f | #e8e8e8 |
mutedForeground | --pd-muted-foreground | #4a4a4a | #b4b4b4 |
faintForeground | --pd-faint-foreground | #767676 | #8c8c8c |
border | --pd-border | #c8c8c8 | #3a3a3a |
borderStrong | --pd-border-strong | #8a8a8a | #6a6a6a |
sidebar | --pd-sidebar | #ffffff | #000000 |
ring | --pd-ring | #000000 | #ffffff |
overlay | --pd-overlay | rgba(0, 0, 0, 0.55) | rgba(0, 0, 0, 0.7) |
accent | --pd-accent | #000000 | #ffffff |
accentForeground | --pd-accent-foreground | #ffffff | #000000 |
ocean#
Blue-tinted surfaces with a blue accent.
| Key | Token | Light | Dark |
|---|---|---|---|
background | --pd-background | #f7f9fc | #0a0f17 |
surface | --pd-surface | #ffffff | #0f1621 |
surface2 | --pd-surface-2 | #edf2f8 | #161f2c |
surface3 | --pd-surface-3 | #e1e8f1 | #1e2937 |
foreground | --pd-foreground | #0e1620 | #e4ebf5 |
foreground2 | --pd-foreground-2 | #334155 | #b9c5d6 |
mutedForeground | --pd-muted-foreground | #64748b | #7f8ea3 |
faintForeground | --pd-faint-foreground | #94a3b8 | #52607a |
border | --pd-border | #e0e7ef | #1f2a3a |
borderStrong | --pd-border-strong | #c7d2df | #2d3b4f |
sidebar | --pd-sidebar | #f7f9fc | #0a0f17 |
ring | --pd-ring | #2563eb | #60a5fa |
overlay | --pd-overlay | rgba(8, 16, 30, 0.45) | rgba(0, 0, 0, 0.6) |
accent | --pd-accent | #2563eb | #60a5fa |
accentForeground | --pd-accent-foreground | #ffffff | #0a0f17 |
dusk#
Violet-tinted surfaces with a violet accent.
| Key | Token | Light | Dark |
|---|---|---|---|
background | --pd-background | #faf9fc | #0e0b14 |
surface | --pd-surface | #ffffff | #14101c |
surface2 | --pd-surface-2 | #f2f0f7 | #1c1726 |
surface3 | --pd-surface-3 | #e8e4f0 | #261f32 |
foreground | --pd-foreground | #17121f | #ece8f3 |
foreground2 | --pd-foreground-2 | #3f3650 | #c3bbd1 |
mutedForeground | --pd-muted-foreground | #6f6682 | #8d849e |
faintForeground | --pd-faint-foreground | #a097b2 | #5c536d |
border | --pd-border | #e6e2ee | #241e30 |
borderStrong | --pd-border-strong | #cfc8db | #352d44 |
sidebar | --pd-sidebar | #faf9fc | #0e0b14 |
ring | --pd-ring | #7c3aed | #a78bfa |
overlay | --pd-overlay | rgba(14, 10, 22, 0.45) | rgba(0, 0, 0, 0.6) |
accent | --pd-accent | #7c3aed | #a78bfa |
accentForeground | --pd-accent-foreground | #ffffff | #0e0b14 |
forest#
Green-tinted surfaces with a green accent.
| Key | Token | Light | Dark |
|---|---|---|---|
background | --pd-background | #f8faf8 | #0a100c |
surface | --pd-surface | #ffffff | #0f1711 |
surface2 | --pd-surface-2 | #eef3ee | #162018 |
surface3 | --pd-surface-3 | #e2eae3 | #1e2a21 |
foreground | --pd-foreground | #101a13 | #e5efe8 |
foreground2 | --pd-foreground-2 | #334238 | #b8c8bd |
mutedForeground | --pd-muted-foreground | #65736a | #7e9285 |
faintForeground | --pd-faint-foreground | #96a39a | #51625a |
border | --pd-border | #e0e8e2 | #1f2b23 |
borderStrong | --pd-border-strong | #c6d3c9 | #2e3d33 |
sidebar | --pd-sidebar | #f8faf8 | #0a100c |
ring | --pd-ring | #15803d | #4ade80 |
overlay | --pd-overlay | rgba(8, 18, 12, 0.45) | rgba(0, 0, 0, 0.6) |
accent | --pd-accent | #15803d | #4ade80 |
accentForeground | --pd-accent-foreground | #ffffff | #0a100c |
ember#
Warm sand surfaces with an orange accent.
| Key | Token | Light | Dark |
|---|---|---|---|
background | --pd-background | #fcfaf7 | #110d0a |
surface | --pd-surface | #ffffff | #17120e |
surface2 | --pd-surface-2 | #f6f1ea | #201913 |
surface3 | --pd-surface-3 | #ede5da | #2a2119 |
foreground | --pd-foreground | #1c1510 | #f0e9e2 |
foreground2 | --pd-foreground-2 | #4a3d33 | #cbc0b5 |
mutedForeground | --pd-muted-foreground | #7a6b5e | #978a7d |
faintForeground | --pd-faint-foreground | #ab9d90 | #635749 |
border | --pd-border | #ece4da | #2a221b |
borderStrong | --pd-border-strong | #d7cabb | #3c3129 |
sidebar | --pd-sidebar | #fcfaf7 | #110d0a |
ring | --pd-ring | #ea580c | #fb923c |
overlay | --pd-overlay | rgba(22, 14, 8, 0.45) | rgba(0, 0, 0, 0.6) |
accent | --pd-accent | #ea580c | #fb923c |
accentForeground | --pd-accent-foreground | #ffffff | #110d0a |
rose#
Rose-tinted surfaces with a crimson accent.
| Key | Token | Light | Dark |
|---|---|---|---|
background | --pd-background | #fcf9fa | #120b0e |
surface | --pd-surface | #ffffff | #181013 |
surface2 | --pd-surface-2 | #f6eff2 | #211719 |
surface3 | --pd-surface-3 | #ede3e7 | #2b1f23 |
foreground | --pd-foreground | #1c1116 | #f1e8eb |
foreground2 | --pd-foreground-2 | #4a3640 | #cdbec4 |
mutedForeground | --pd-muted-foreground | #7a6670 | #988790 |
faintForeground | --pd-faint-foreground | #ab979f | #645258 |
border | --pd-border | #ecdfe4 | #2c2025 |
borderStrong | --pd-border-strong | #d8c5cd | #3f2f36 |
sidebar | --pd-sidebar | #fcf9fa | #120b0e |
ring | --pd-ring | #e11d48 | #fb7185 |
overlay | --pd-overlay | rgba(22, 8, 14, 0.45) | rgba(0, 0, 0, 0.6) |
accent | --pd-accent | #e11d48 | #fb7185 |
accentForeground | --pd-accent-foreground | #ffffff | #120b0e |
Extending a preset#
colors is applied after the preset, per colour scheme, and only the keys you set are emitted. Start from the preset that is closest to what you want and override the rest:
theme: {
preset: "ocean",
colors: {
light: {
accent: "#0f766e",
ring: "#0f766e",
},
dark: {
accent: "#2dd4bf",
ring: "#2dd4bf",
background: "#000000",
sidebar: "#000000",
},
},
}Any CSS colour string is accepted, including rgb(), hsl(), oklch() and var() references to your own properties. The values land in the same <style> element as the preset, so the override is present on first paint. See the ThemeColors reference for what each key is used for.