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

```ts group=highlight-themes file=/src/main.ts entry env=client
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

| Theme | Export | Type | Import |
| --- | --- | --- | --- |
| Aurora X | `auroraXTheme` | Dark | `themes/aurora-x` |
| Dracula | `draculaTheme` | Dark | `themes/dracula` |
| GitHub Dark | `githubDarkTheme` | Dark | `themes/github-dark` |
| GitHub Light | `githubLightTheme` | Light | `themes/github-light` |
| Monokai | `monokaiTheme` | Dark | `themes/monokai` |
| Nord | `nordTheme` | Dark | `themes/nord` |
| One Dark Pro | `oneDarkProTheme` | Dark | `themes/one-dark-pro` |
| Solarized Dark | `solarizedDarkTheme` | Dark | `themes/solarized-dark` |
| Solarized Light | `solarizedLightTheme` | Light | `themes/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.
