TanStack
Core Concepts

Themes

Themes

Themes are isolated color objects converted into CSS variables. They never change tokenization or duplicate highlighted markup.

Light and dark

ts
import { createThemeCss } from '@tanstack/highlight/theme'
import { githubDarkTheme } from '@tanstack/highlight/themes/github-dark'
import { githubLightTheme } from '@tanstack/highlight/themes/github-light'

const css = createThemeCss({
  light: githubLightTheme,
  dark: githubDarkTheme,
  lightSelector: ':root',
  darkSelector: '.dark',
})

The default selectors are :root for light and .dark for dark.

One theme

ts
import { draculaTheme } from '@tanstack/highlight/themes/dracula'

const css = createThemeCss({
  dark: draculaTheme,
  darkSelector: ':root',
})

At least one of light or dark is required in pair mode.

Multiple named themes

ts
const css = createThemeCss({
  themes: [
    { selector: '[data-code-theme="github"]', theme: githubLightTheme },
    { selector: '[data-code-theme="dracula"]', theme: draculaTheme },
  ],
})

themes mode is mutually exclusive with light and dark.

Try the shipped themes

The highlighted markup stays the same while the selected theme changes only its CSS variables.

/src/main.ts
import { createHighlighter } from '@tanstack/highlight/core'
import { ts } from '@tanstack/highlight/languages/ts'
import { createThemeCss } from '@tanstack/highlight/theme'
import { draculaTheme } from '@tanstack/highlight/themes/dracula'
import { githubDarkTheme } from '@tanstack/highlight/themes/github-dark'
import { githubLightTheme } from '@tanstack/highlight/themes/github-light'

const highlighter = createHighlighter({ languages: [ts] })
const result = highlighter.highlight(
  [
    'type Theme = "light" | "dark"',
    '',
    'export function selectTheme(theme: Theme) {',
    '  document.documentElement.dataset.theme = theme',
    '}',
  ].join('\n'),
  { lang: 'ts' },
)

const themeCss = createThemeCss({
  themes: [
    { selector: '[data-code-theme="github-light"]', theme: githubLightTheme },
    { selector: '[data-code-theme="github-dark"]', theme: githubDarkTheme },
    { selector: '[data-code-theme="dracula"]', theme: draculaTheme },
  ],
})

export default function render(output: HTMLElement) {
  const style = document.createElement('style')
  style.textContent = `${themeCss}
body { margin: 0; padding: 24px; font-family: ui-sans-serif, system-ui; }
.demo { overflow: hidden; border: 1px solid color-mix(in srgb, currentColor 16%, transparent); border-radius: 12px; background: var(--th-background); color: var(--th-token); }
.toolbar { display: flex; align-items: center; justify-content: space-between; gap: 12px; padding: 10px 12px; border-bottom: 1px solid color-mix(in srgb, currentColor 16%, transparent); }
.toolbar label { font-size: 13px; font-weight: 600; }
.toolbar select { padding: 6px 28px 6px 8px; border: 1px solid color-mix(in srgb, currentColor 22%, transparent); border-radius: 7px; background: var(--th-background); color: var(--th-token); }
pre.th-code { margin: 0; }
code { font-family: ui-monospace, SFMono-Regular, Consolas, monospace; font-size: 14px; line-height: 1.6; }`

  document.head.append(style)
  output.innerHTML = `<section class="demo" data-code-theme="github-light">
  <div class="toolbar">
    <label for="theme">Code theme</label>
    <select id="theme">
      <option value="github-light">GitHub Light</option>
      <option value="github-dark">GitHub Dark</option>
      <option value="dracula">Dracula</option>
    </select>
  </div>
  ${result.html}
</section>`

  const demo = output.querySelector('.demo')
  const select = output.querySelector('#theme')

  if (demo instanceof HTMLElement && select instanceof HTMLSelectElement) {
    select.addEventListener('change', () => {
      demo.dataset.codeTheme = select.value
    })
  }
}

Variables only

Set includeBaseStyles: false when the application already owns the token selectors:

ts
const variables = createThemeCss({
  light: githubLightTheme,
  includeBaseStyles: false,
})

You can also call createThemeRule(selector, theme) and createThemeBaseCss() separately.

Renderer-owned wrappers

When another renderer owns the outer code block, point the base rules at its classes:

ts
const css = createThemeCss({
  light: githubLightTheme,
  dark: githubDarkTheme,
  lightSelector: '.markdown-renderer',
  darkSelector: '.dark .markdown-renderer',
  codeBlockSelector: '.markdown-renderer pre.tm-code',
  lineNumbersSelector: '.markdown-renderer .tm-code--line-numbers',
})

The defaults remain pre.th-code and .th-code--line-numbers. Token classes stay th-* because Highlight owns the inner token markup.

Shipped theme matrix

ThemeExportTypeImport
Aurora XauroraXThemeDarkthemes/aurora-x
DraculadraculaThemeDarkthemes/dracula
GitHub DarkgithubDarkThemeDarkthemes/github-dark
GitHub LightgithubLightThemeLightthemes/github-light
MonokaimonokaiThemeDarkthemes/monokai
NordnordThemeDarkthemes/nord
One Dark ProoneDarkProThemeDarkthemes/one-dark-pro
Solarized DarksolarizedDarkThemeDarkthemes/solarized-dark
Solarized LightsolarizedLightThemeLightthemes/solarized-light

Prefix each import with @tanstack/highlight/.

Create a theme

ts
import type { HighlightTheme } from '@tanstack/highlight/theme'

export const docsTheme = {
  name: 'Docs Light',
  type: 'light',
  background: '#ffffff',
  foreground: '#24292f',
  tokens: {
    token: '#24292f',
    attr: '#953800',
    'code-inline': '#0550ae',
    command: '#8250df',
    comment: '#6e7781',
    deleted: '#cf222e',
    function: '#8250df',
    heading: '#0550ae',
    inserted: '#116329',
    keyword: '#cf222e',
    link: '#0969da',
    literal: '#0550ae',
    meta: '#6e7781',
    number: '#0550ae',
    operator: '#cf222e',
    property: '#953800',
    selector: '#116329',
    string: '#0a3069',
    tag: '#116329',
    type: '#953800',
    variable: '#953800',
  },
} satisfies HighlightTheme

The complete token record is intentional: missing semantic colors should be a type error rather than an implicit theme mismatch.

Theme objects do not include font weight, italics, contrast validation, or typography. Add those rules in CSS.