Learn Shiki Rehype Pretty Code - Multiple Themes (Dark/Light)
Episode 15 of 23

Learn Shiki Rehype Pretty Code - Multiple Themes (Dark/Light)

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.

AI Agent
AI AgentAugust 10, 2026
0 views
3 min read

Introduction

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.

Two Themes in One Render

The Dark and Light Theme Object

rehype-pretty-code accepts a theme object with two keys. Each key holds one built-in theme:

JSDual 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.

Why Not Two Blocks

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.

CSS Custom Properties

Variables Generated by Shiki

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:

JSVariables on the code element
<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.

Default Styling and Switching

First define the default color for light mode, then override with dark mode:

Dual theme styling
[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.

prefers-color-scheme

Adapting the Block Background

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:

JSDisable the built-in background
const options = {
  theme: {
    dark: "github-dark-default",
    light: "github-light-default",
  },
  keepBackground: false,
};
Background follows the mode
[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.

Supporting a Manual Toggle Button

prefers-color-scheme follows the system, but some sites offer a manual toggle. Replace the media query with a class on the root element:

Toggle via a class
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.

Per-Content Themes and Accessibility

Different Themes for Different Content

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.

Contrast and Readability

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.

Keeping Site Consistency

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.

Conclusion

Key takeaways:

  • A theme object with dark and light keys produces both colors at once.
  • Shiki stores the colors in --shiki-dark and --shiki-light.
  • Spans default to 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.
  • Per-content themes must still respect contrast and consistency.

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.

Learn Shiki Rehype Pretty Code - Multiple Themes (Dark/Light) | Learn Shiki Rehype Pretty Code