# Rainbow brackets (/recipes/rainbow-brackets)

Rainbow brackets color a language's bracket pairs by nesting depth. The pairs aren't a fixed set: they come from each language's Tree-sitter `brackets.scm` query, or a shared default when a language doesn't define one. Rainbow brackets work with every built-in formatter: the HTML and terminal formatters color the brackets, and BBCode scoped emits them as tags.

Turn it on when you highlight:

### JavaScript / TypeScript

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

const html = await highlight(
  'foo(bar([1, 2], {a: 3}))',
  htmlInline({language: javascript, theme: frappe}),
  {rainbowBrackets: true},
)
```

### Rust

```rust
use lumis::{
    highlight_with_options, languages::Language, themes,
    HighlightOptions, HtmlInlineBuilder,
};

let formatter = HtmlInlineBuilder::new()
    .language(Language::JavaScript)
    .theme(Some(
        themes::get("catppuccin_frappe").unwrap(),
    ))
    .build()
    .unwrap();

let html = highlight_with_options(
    "foo(bar([1, 2], {a: 3}))",
    formatter,
    HighlightOptions::new().rainbow_brackets(true),
);
```

### Elixir

```elixir
Lumis.highlight!(
  "foo(bar([1, 2], {a: 3}))",
  rainbow_brackets: true,
  formatter:
    {:html_inline,
     language: "javascript",
     theme: "catppuccin_frappe"}
)
```

The example needs `{:lumis_wasm_javascript, "~> 0.26.0"}` in `mix.exs`.

### CLI

```bash
lumis highlight main.js --rainbow-brackets
```


## How depth maps to color

The shared default matches `()`, `[]`, and `{}`. A language's own `brackets.scm` usually keeps those and adds more; Rust, for example, also colors generics (`<>`). Pairs tagged with `(#set! rainbow.exclude)` are matched but stay uncolored, such as string and template delimiters or JSX angle brackets.

Built-in formatters map each depth to one of six theme scopes,
`punctuation.bracket.rainbow.1` through `punctuation.bracket.rainbow.6`, and wrap
back to `1` after the sixth level. A theme can define these scopes; when it
doesn't, Lumis falls back to six built-in colors.

The nested event API does not discard that information. It emits a
`rainbowBracket` decoration carrying the real zero-based `depth`, even at level
six and beyond. Flat token iterators continue to report the six cycling scopes,
so existing scope-based formatters keep their output.