CSS Builder
Build a theme stylesheet at runtime in Rust, JavaScript / TypeScript, and Elixir.
Lumis ships one CSS file per theme. The CSS builder lets you scope selectors, change the container selector, and add container styles.
The builder works with the same theme data as the bundled files. With no options, it produces the same CSS shape as @lumis-sh/themes/css/<theme>.css.
Options
The npm API uses camelCase. Rust and Elixir use snake_case.
| Option | Default | Use it for |
|---|---|---|
scope | "" | Parent selector prepended to every generated selector. |
containerSelector / container_selector | ".lumis" | Set the selector for the code block container rule. |
containerStyle / container_style | none | Add declarations to the container selector. Use this for layout-related CSS such as padding, border-radius, or overflow-x. If a property already exists, the new value replaces it. |
enableItalic / enable_italic | true | Drop italic declarations when set to false. |
containerStyle / container_style is a list of (property, value) pairs. For example, background-color: var(--code-background) replaces the theme background, while padding: 1rem adds a new declaration.
Token selectors are fixed as l-* classes, matching the built-in htmlLinked formatter output. Line wrappers use l-line, highlighted lines use l-highlighted by default, and line-number themes use l-line-number plus l-line-number-highlighted on highlighted gutters.
Use all options together
import {buildCss} from '@lumis-sh/themes'
import frappe from '@lumis-sh/themes/catppuccin_frappe'
const css = buildCss(frappe, {
scope: '.docs-theme',
containerSelector: '.code-block',
containerStyle: [
['background-color', 'var(--code-background)'],
['border-radius', '0.5rem'],
['padding', '1rem'],
],
enableItalic: false,
})
The output starts like this:
/* catppuccin_frappe
* revision: e068ab5f8261f23f6f71ffd8791ae40315b77b9c
*/
.docs-theme .code-block {
color: #c6d0f5;
background-color: var(--code-background);
border-radius: 0.5rem;
padding: 1rem;
}
.docs-theme .l-keyword {
color: #ca9ee6;
}
Default output
import {buildCss} from '@lumis-sh/themes'
import frappe from '@lumis-sh/themes/catppuccin_frappe'
const css = buildCss(frappe)
The output starts with the theme name and revision, then the container .lumis rule, then token rules:
/* catppuccin_frappe
* revision: e068ab5f8261f23f6f71ffd8791ae40315b77b9c
*/
.lumis {
color: #c6d0f5;
background-color: #303446;
}
.l-keyword {
color: #ca9ee6;
}
.l-string {
color: #a6d189;
}
Scope a stylesheet
Use scope when the same page needs more than one theme, or when a code block should inherit theme styles only inside part of your app. Do not include a trailing space; Lumis inserts the descendant combinator for you.
import {buildCss} from '@lumis-sh/themes'
import frappe from '@lumis-sh/themes/catppuccin_frappe'
const css = buildCss(frappe, {
scope: 'html[data-theme="dark"]',
containerStyle: [
['background-color', 'var(--code-background)'],
['border-radius', '0.375rem'],
['padding', '1rem'],
],
})
That produces selectors like this:
html[data-theme="dark"] .lumis {
color: #c6d0f5;
background-color: var(--code-background);
border-radius: 0.375rem;
padding: 1rem;
}
html[data-theme="dark"] .l-keyword {
color: #ca9ee6;
}
Change the container selector
The container rule uses .lumis by default. Change it when your rendered HTML uses a different wrapper class.
const css = buildCss(frappe, {
containerSelector: '.code-block',
})
That changes only the container rule selector:
.code-block {
color: #c6d0f5;
background-color: #303446;
}
.l-keyword {
color: #ca9ee6;
}
Other runtimes
Java and the CLI do not expose the CSS builder yet.