Skip to content

Pick and customise a theme

In this guide you choose a look for a deck and make it yours: you try the themes live, install a theme package, set its options, override a colour and a font, give one slide a dark background, and finally copy a theme into the deck to edit it freely. It takes about ten minutes.

You need a deck that runs with npm run devpnpm devyarn devbun run dev. If you don’t have one yet, follow Your first deck in 5 minutes first.

With the deck open in the browser, press T (or the palette button in the bottom-left corner). The theme menu lists the deck’s themes and a light/dark switch; pick one and every slide redraws in it. D flips between light and dark directly.

The menu changes only what this screen shows, and remembers it per deck in this browser. Your deck.mdx is untouched, and the projector or a viewer’s phone keeps its own choice. A new deck offers the three themes that ship with the engine:

Theme Look
minimal The default: black on white, a wide sans serif, one accent.
rla Zinc-950 with a blue to violet ramp and dot-matrix headings, made for the animated backgrounds.
stack An emerald accent on warm neutrals, Inter, product-like.

There are 16 themes in all: these three, none (no styles at all, for a deck that brings its own CSS) and 13 theme packages. The theme gallery shows every one in light and dark.

Every theme in the gallery ships inside @kadal/deck, so there is nothing to install. Pick one and name it in the headmatter, the YAML block at the top of deck.mdx. This guide uses ocean:

---
title: Tide tables
theme: ocean
---

Save, and the running deck switches to the new theme; there is nothing to import in src/main.tsx. The theme brings its stylesheet, its fonts, its code colours and its own layouts (ocean adds waves and depth, and a <Wave /> component). Each theme’s README, linked from its card in the gallery, lists what it adds.

theme: ocean looks for a built-in theme first, then an installed @kadal/theme-ocean, then kadal-theme-ocean, then a package called ocean, and finally the copy of ocean inside @kadal/deck. So installing a theme’s own package is only for pinning it to a version apart from the engine’s, or for building on it:

Terminal window
npm install @kadal/theme-ocean
Terminal window
pnpm add @kadal/theme-ocean
Terminal window
yarn add @kadal/theme-ocean
Terminal window
bun add @kadal/theme-ocean

A theme from someone else (kadal-theme-<name>, or any package) is installed like any other dependency. When a name matches nothing, kadal-deck dev and kadal-deck build stop with the names they tried and the command that installs the likeliest one.

themes: puts those first in the menu; the other first-party themes follow them:

---
title: Tide tables
theme: ocean
themes: [ocean, minimal, brand]
---

The deck opens in theme:; viewers can switch to any theme in themes:. Each theme picked from the menu brings its own layouts, fonts and code colours, and the deck’s own theme’s components stay available, so no slide breaks. Code blocks recolour too: the build highlights each token once for every theme in the menu.

4. Set the theme’s options with themeConfig

Section titled “4. Set the theme’s options with themeConfig”

Most themes have a few knobs. ocean takes an accent colour for each scheme and can hide the waves on its cover:

---
theme: ocean
themeConfig:
accent: "#0b7285" # links, bullets and the progress bar, light scheme
accentDark: "#5ff7ff" # the same in the dark scheme
waves: true # false hides the swells along the cover
---

brand is a theme made of options: your colours, logo and footer, with no CSS to write. Put the logo files in public/:

---
theme: brand
themeConfig:
primary: "#2453d4" # the cover, links, numbers, the accent
secondary: "#0f9d8a" # gradients and shapes on the cover
logo: ./logo.svg # from public/; without one the company name is set as a wordmark
logoDark: ./logo-dark.svg # the logo on dark slides
company: Harbor Labs # the wordmark, the logo's alt text, the footer's default
footer: "Harbor Labs · Q3 review · Confidential" # false hides the footer
logoPosition: top-right # top-left, bottom-right or none
---

Each key becomes a CSS custom property on the deck root (accentDark becomes --theme-accent-dark), so your own CSS can read the same values. A theme’s README lists the keys it reads; keys it doesn’t read do nothing.

src/styles.css loads after the theme, so a token you set there wins. Every theme is built from the same custom properties: --deck-bg, --deck-fg, --deck-muted, --deck-accent, --deck-accent-2, --deck-surface and a few more (the full list is in Writing a theme). Set the light and the dark value:

src/styles.css
.theme-ocean {
--deck-accent: #c2410c;
}
.theme-ocean[data-color-scheme="dark"] {
--deck-accent: #fb923c;
}

Scope the rule to the theme’s class, .theme-<name>, as above. A rule on :root or html is overridden by the theme, which sets its tokens on the deck’s own element.

fonts: in the headmatter loads families from Google Fonts and points the theme’s font tokens at them. It replaces the theme’s own choice:

---
theme: ocean
fonts:
sans: Inter # body text, and headings unless `heading` is set
heading: Fraunces # headings only
mono: JetBrains Mono # code
---

weights (default 300,400,600,700), italic, serif, provider (google, bunny, or none for fonts already on the machine) and local are covered in Themes and backgrounds. Exports wait for the fonts before they take pictures.

A slide can force its own colour scheme, so a dark picture or an animated background under a light theme still has readable text. Set it in that slide’s frontmatter:

---
colorScheme: dark
background: { component: topo, color: "rgba(94,234,212,0.5)", fill: "rgba(94,234,212,0.10)" }
---
## A dark slide in a light deck
The text, the code and the cards on this slide use the theme's dark tokens.

background: takes a colour, a gradient, an image or video from public/, a web page, or one of 36 animated background components such as aurora, topo, globe or starfield. The full list is in Themes and backgrounds, and defaults: in the headmatter can give every slide the same one.

When options and a few variables are not enough, copy the theme into the deck and edit it there:

Terminal window
npx kadal-deck theme eject
Terminal window
pnpm exec kadal-deck theme eject
Terminal window
yarn kadal-deck theme eject
Terminal window
bunx kadal-deck theme eject
Theme "ocean" ejected to /path/to/deck/theme; the headmatter now says theme: ./theme

You get theme/style.css, its layouts in theme/index.tsx, its package.json and README. The headmatter now says theme: ./theme, nothing else in deck.mdx changes, and the deck looks exactly as before. Edit theme/style.css and the deck follows. If themes: lists the package (ocean), list ./theme there instead so the menu offers your copy.

--dir ./my-theme picks another folder, --theme seriph ejects a theme other than the deck’s, and --force replaces a folder that exists. The built-in themes eject too.

Send feedback