Skip to content

Instantly share code, notes, and snippets.

@andy0130tw
Last active September 18, 2025 17:09
Show Gist options
  • Save andy0130tw/96175ff6bcdc71bd5b6e902570626592 to your computer and use it in GitHub Desktop.
Save andy0130tw/96175ff6bcdc71bd5b6e902570626592 to your computer and use it in GitHub Desktop.
CodeMirror theme switcher based on the current color scheme of the containing window
const SELECTOR_PREFERS_DARK_COLOR_SCHEME = '(prefers-color-scheme: dark)'
export function autoColorScheme(options: {
dark?: Extension, light?: Extension, defaultDark?: boolean} = {}): Extension {
const {
dark: darkExt = [],
light: lightExt = [],
defaultDark = false,
} = options
const darkThemeCompartment = new Compartment()
function darkThemeExt(dark: boolean): Extension {
return dark ? darkExt : lightExt
}
const plugin = ViewPlugin.define((view) => {
function colorSchemeChangeHandler(evt: MediaQueryListEvent) {
dispatchDarkTheme(evt.matches)
}
function dispatchDarkTheme(dark: boolean) {
view.dispatch({
effects: darkThemeCompartment.reconfigure(darkThemeExt(dark)),
})
}
const win = view.dom.ownerDocument.defaultView
const supportsMatchMedia = typeof win?.matchMedia === 'function'
if (supportsMatchMedia) {
const query = win.matchMedia(SELECTOR_PREFERS_DARK_COLOR_SCHEME)
query.addEventListener('change', colorSchemeChangeHandler)
if (query.matches !== defaultDark) {
dispatchDarkTheme(query.matches)
}
return {
destroy() {
query.removeEventListener('change', colorSchemeChangeHandler)
}
}
}
// matchMedia is unsupported?
return {}
})
return [
plugin,
darkThemeCompartment.of(darkThemeExt(defaultDark)),
]
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment