You will set up two themes at once via a theme object, use the CSS custom properties --shiki-dark and --shiki-light, adapt colors via prefers-color-scheme, and prepare per-content themes with good contrast and accessibility.

Modern sites support dark and light modes. Without special handling, code blocks keep a single color and look odd in one of the modes. The solution isn't two renders, but a single render with two themes at once.
Episode 15 covers multiple themes: the dark and light theme object, the CSS custom properties --shiki-dark and --shiki-light, automatic switching via prefers-color-scheme, plus per-content theme and accessibility considerations. After this episode, your code blocks adapt to the mode without JavaScript.
rehype-pretty-code accepts a theme object with two keys. Each key holds one built-in theme:
const options = {
theme: {
dark: "github-dark-default",
light: "github-light-default",
},
};With this configuration, Shiki colors the code twice in a single call: once with the dark theme and once with the light theme. Both token color results are stored as CSS custom properties on the output element.
The two-separate-blocks approach doubles the markup and confuses screen readers. The CSS variables approach keeps one <code> element with two sets of colors. The browser only picks the active set based on the mode, so the content stays a single source of truth.
For each token, Shiki stores the dark color in --shiki-dark and the light color in --shiki-light. On the resulting <code> element, the style attribute contains both variables:
<code style="--shiki-dark: #a5d6ff; --shiki-light: #0550ae;">
<span style="color: var(--shiki-dark)">const</span>
</code>Span elements use color: var(--shiki-dark) by default. Your only task in CSS is to swap which variable is active when the mode changes.
First define the default color for light mode, then override with dark mode:
[data-rehype-pretty-code-figure] span {
color: var(--shiki-light);
}
@media (prefers-color-scheme: dark) {
[data-rehype-pretty-code-figure] span {
color: var(--shiki-dark);
}
}The prefers-color-scheme: dark media query switches all tokens to the dark palette automatically according to the system setting. Without JavaScript, a single code block adapts to the visitor's system mode.
Token colors are only half the work; the block background must change too. Turn off the built-in background with keepBackground: false and set it yourself in CSS:
const options = {
theme: {
dark: "github-dark-default",
light: "github-light-default",
},
keepBackground: false,
};[data-rehype-pretty-code-figure] pre {
background: #ffffff;
}
@media (prefers-color-scheme: dark) {
[data-rehype-pretty-code-figure] pre {
background: #0d1117;
}
}With keepBackground: false, the background is fully controlled by CSS. This makes it easy to match the code block background with the surrounding section color.
prefers-color-scheme follows the system, but some sites offer a manual toggle. Replace the media query with a class on the root element:
html.dark [data-rehype-pretty-code-figure] span {
color: var(--shiki-dark);
}A common combination of both: the default follows the system, while a dark or light class overrides when the user chooses manually.
Not all content has to use the same theme. You can pick a theme per page by storing the theme choice in frontmatter and passing it to the plugin options. This is useful for articles with code illustrations designed specifically.
Both chosen themes must have good contrast against their respective backgrounds. Test with a contrast checker for main scopes like keyword, string, and comment. Avoid two themes that are both dim, because one of the modes will be hard to read.
Match the code block palette with the overall site palette. If the site uses a certain accent, choose a Shiki theme that harmonizes. This consistency makes the dark and light transition feel unified, not like two different sites.
Key takeaways:
dark and light keys produces both colors at once.--shiki-dark and --shiki-light.var(--shiki-dark) and are overridden per mode in CSS.prefers-color-scheme switches modes automatically without JavaScript.keepBackground: false moves the background to CSS.In episode 16 you'll learn copy button and UI interactions: adding a code copy button on the client side using data attributes, preparing captions and responsive layouts, and ensuring accessibility via aria-label and keyboard navigation.