Syntax highlighting with Shiki on Astro, dual theme

Two themes, one build, no flash — the one config snippet you need for Shiki with a light/dark Astro blog.

· 1 min read
Configuring Shiki dual-theme syntax highlighting in an Astro blog: three lines of astro.config.mjs to emit both light and dark themes and five lines of CSS so code blocks switch with prefers-color-scheme

A short note for Astro devs wiring syntax highlighting on a blog that supports light and dark mode.

Astro ships with Shiki. Defaults work, but defaults emit one theme — so code blocks look great in light mode and terrible in dark (or vice versa). The fix is three lines in astro.config.mjs and five lines of CSS.

// astro.config.mjs
import { defineConfig } from "astro/config";

export default defineConfig({
  markdown: {
    shikiConfig: {
      themes: {
        light: "github-light",
        dark: "github-dark-dimmed",
      },
    },
  },
});

Astro emits both themes into the output, gated by CSS custom properties. Toggle between them by keying off [data-theme] (the attribute your theme switcher already sets):

html[data-theme="dark"] .astro-code,
html[data-theme="dark"] .astro-code span {
  color: var(--shiki-dark) !important;
  background-color: var(--shiki-dark-bg) !important;
}

No flash. No extra JS. Switches as fast as your theme toggle fires.

Takeaway: if you see a dual-theme Shiki setup that loads both themes as separate stylesheets or re-renders on toggle, the author didn’t know about this config. Save them 20 minutes.