Lumis Docs
Recipes

Rainbow brackets

Color nested brackets by depth in any Lumis formatter.

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 are a formatter option, so they 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 build the formatter:

import {highlight} from '@lumis-sh/lumis'
import {htmlInline} from '@lumis-sh/lumis/formatters'
import javascript from '@lumis-sh/lumis/langs/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})
)

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.

On this page