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.