# HTML Multi-Themes Formatter (/formatters/html-multi-themes)

One HTML block that supports multiple themes via CSS custom properties. Useful for light/dark mode.

## Options

| Runtime | Options |
| --- | --- |
| Rust | `language`, `structure`, `themes`, `default_theme`, `css_variable_prefix`, `pre_class`, `pre_attrs`, `code_attrs`, `include_highlights`, `highlight_lines`, `line_numbers`, `header` |
| Elixir | `language`, `structure`, `themes`, `default_theme`, `css_variable_prefix`, `pre_class`, `pre_attrs`, `code_attrs`, `include_highlights`, `highlight_lines`, `line_numbers`, `header` |
| JavaScript / TypeScript | `language`, `structure`, `themes`, `defaultTheme`, `cssVariablePrefix`, `preClass`, `preAttrs`, `codeAttrs`, `includeHighlights`, `highlightLines`, `lineNumbers`, `header` |
| Java | Not currently supported in `lumis4j` |
| CLI | `--structure`, `--themes`, `--default-theme`, `--css-variable-prefix`, `--pre-class`, `--pre-attr`, `--no-pre-attr`, `--code-attr`, `--no-code-attr`, `--include-highlights`, `--highlight-lines`, `--highlight-lines-class`, `--highlight-lines-style`, `--line-numbers`, `--header-open`, `--header-close` |

Use `pre_attrs` / `preAttrs` to override the block background, including
backgrounds from a named default or `light-dark()`. See
[Override the block background](/formatters#override-the-block-background).

The override leaves theme variables and token styles intact. If your
[theme-switching CSS](/recipes/light-dark) sets the `<pre>` background with
`!important`, remove that root background rule so the attribute can take effect.

## Basic example

### JavaScript / TypeScript

```js
import {highlight} from '@lumis-sh/lumis'
import {htmlMultiThemes} from '@lumis-sh/lumis/formatters'
import javascript from '@lumis-sh/wasm-javascript'
import latte from '@lumis-sh/themes/catppuccin_latte'
import frappe from '@lumis-sh/themes/catppuccin_frappe'

const html = await highlight(
  'const x = 1',
  htmlMultiThemes({
    language: javascript,
    themes: {light: latte, dark: frappe},
    defaultTheme: 'light-dark()',
  })
)
```

### Rust

```rust
use lumis::{
    highlight, languages::Language, themes,
    HtmlMultiThemesBuilder,
};
use std::collections::HashMap;

let mut theme_map = HashMap::new();
theme_map.insert(
    "light".to_string(),
    themes::get("catppuccin_latte").unwrap(),
);
theme_map.insert(
    "dark".to_string(),
    themes::get("catppuccin_frappe").unwrap(),
);

let html = highlight(
    "const x = 1",
    HtmlMultiThemesBuilder::new()
        .language(Language::JavaScript)
        .themes(theme_map)
        .default_theme("light-dark()")
        .build()
        .unwrap(),
);
```

### Elixir

```elixir
Lumis.highlight!(
  "const x = 1",
  formatter:
    {:html_multi_themes,
     language: "javascript",
     themes: [
       light: "catppuccin_latte",
       dark: "catppuccin_frappe"
     ],
     default_theme: "light-dark()"}
)
```

### Java

HTML Multi-Themes is not currently supported in `lumis4j`.

### CLI

```bash
lumis highlight main.rs \
  --formatter html-multi-themes \
  --themes light:catppuccin_latte \
  --themes dark:catppuccin_frappe \
  --default-theme light-dark()
```


## `cssVariablePrefix`

By default, CSS variables are prefixed with `--lumis` (e.g., `--lumis-light`, `--lumis-dark-bg`). Override the prefix if it conflicts with your CSS. Produces variables like `--code-light`, `--code-dark-bg`, etc.

### JavaScript / TypeScript

```js
const html = await highlight(
  'const x = 1',
  htmlMultiThemes({
    language: javascript,
    themes: {light: latte, dark: frappe},
    cssVariablePrefix: '--code',
  })
)
```

### Rust

```rust
let formatter = HtmlMultiThemesBuilder::new()
    .language(Language::JavaScript)
    .themes(theme_map)
    .css_variable_prefix("--code")
    .build()
    .unwrap();

let html = highlight("const x = 1", formatter);
```

### Elixir

```elixir
Lumis.highlight!(
  "const x = 1",
  formatter:
    {:html_multi_themes,
     language: "javascript",
     themes: [
       light: "catppuccin_latte",
       dark: "catppuccin_frappe"
     ],
     css_variable_prefix: "--code"}
)
```

### Java

HTML Multi-Themes is not currently supported in `lumis4j`.

### CLI

```bash
lumis highlight index.js \
  --formatter html-multi-themes \
  --themes light:catppuccin_latte \
  --themes dark:catppuccin_frappe \
  --css-variable-prefix "--code"
```


## `defaultTheme`

Controls what color values appear inline (not just as CSS variables):

| Value | Behavior |
| --- | --- |
| omitted / `null` | CSS variables only, no inline colors |
| `"light"` | Inline colors from the `light` theme, CSS variables for all themes |
| `"light-dark()"` | Uses CSS `light-dark()` to select colors from the element's `color-scheme` |

Lumis leaves `color-scheme` unset so the output follows the page's theme. Use `light dark` on the page to follow the OS, or `light` / `dark` to select one scheme. To opt just a block into OS switching, set `preAttrs: {style: 'color-scheme: light dark;'}`; this overrides an inherited light-only or dark-only choice. See [Light/Dark Mode](/recipes/light-dark) for all runtimes and inline code.

### What `light-dark()` covers

[CSS `light-dark()`](https://drafts.csswg.org/css-color-5/#light-dark) is a color function, so only `color` and `background-color` use it. For `font-weight`, `font-style` and `text-decoration`, a value both themes share is written as an ordinary declaration; a value they disagree on is written as `--lumis-light-*` and `--lumis-dark-*` variables with no declaration, since only a rule of your own can switch it. Leaving it out of the style attribute is what keeps that rule from needing `!important`, see [Light/Dark Mode](/recipes/light-dark).

## Output format

HTML Multi-Themes wraps each line in a span. Tokens carry CSS variables for each configured theme:

```html
<pre class="lumis lumis-themes catppuccin_frappe catppuccin_latte" style="--lumis-light-bg:#eff1f5; --lumis-dark-bg:#303446;"><code class="language-javascript" translate="no" tabindex="0"><span class="l-line" data-line="1"><span style="--lumis-dark:#ca9ee6; --lumis-light:#8839ef;">const</span></span></code></pre>
```

- `<pre>` carries `lumis`, `lumis-themes`, and the included themes' names as classes. Theme keys such as `light` and `dark` name the CSS variables; they are not added as classes. Custom `pre_class` / `preClass` values are appended as usual.
- `<code class="language-*">` identifies the language and keeps the block focusable
- `<span class="l-line" data-line="N">` wraps only a line's content
- `<span class="l-line-number">` opens the line when `line_numbers` is on, see [Line Numbers](/recipes/line-numbers)
- Lines are separated by `\n` text nodes; nothing follows the last line element
- A final source newline ends its line rather than adding an empty numbered line
- `<span style="--lumis-...">` carries per-theme token colors and font styles

With `structure: "inline"` only the token spans are written, for a `<code>` or
other element of your own. See [Inline code](/formatters#inline-code).

Line layout and copy protection use inline styles, as in HTML Inline, including
when colors are supplied only through variables. See
[Line layout](/themes/css-files#line-layout) for the rules if you supply custom
CSS.

For CSS or formatters written for line divs, see
[HTML line markup migration](/migrate/html-lines).

## Good uses

- light/dark mode
- user-selectable theme switching
- one HTML block shared across themes