Theme

Overview

Define a theme that controls colour, typography, spacing, and charts across every doc, grid, mail, and plot.

AI assisted, human approved — novem uses AI to review and keep our documentation up to date.

Often you want to create several different assets, styles and documents that all look similar. It's possible to do this hardcoded in code, but it's easier if this information can live in a separate structure and be re-used.

To facilitate this novem offers themes which let you define your visual identity once and reuse it everywhere. Set --novem-accent to your corporate blue and every callout border, link, footnote ref and chart accent follows along. No per-vis configuration is needed and the change travels into embedded plots automatically.

A custom JavaScript plot embedded in a doc can read the parent's variables both through CSS using var(--novem-text) and through a JavaScript object as render.theme.text. The plot behaves like a native part of the document.

Dark mode flips automatically as well. The engine keeps a parallel set of dark-mode defaults for every style variable, so a theme that doesn't explicitly handle dark mode still looks correct in both modes.

The theme leverages the Cascade in Cascading Style Sheets (CSS) by predefining a set of variables that are then user overridable.

By overriding the exiting css variables in your plot you can have a unique style when the chart is viewed alone, but inherit corporate styles when embedded into doc or an e-mail.

Novem styles compose top-down through three layers, where each layer can override the previous one.

1. Basline styles  structural layout + sane --novem-* defaults
                   (built into the engine, not user-editable)

2. Theme           brand: novem (default) | custom | +org/theme
                   overrides --novem-* values + selector refinements

3. Per-vis CSS     author overrides for one specific vis
                   same primitives, scoped to this document

Baseline styles is built into the engine and not user-editable. It sets the default values for every primitive. The theme sits on top, swapping in its own --novem-* values and adding selector refinements. Per-vis CSS layers further on top to fine-tune a single visualisation. You almost never need !important because specificity follows the cascade naturally.

Every doc, grid, plot or mail has a /config/theme value. The default is novem which gives you the built-in theme. You can also use custom to opt out of the built-in theme entirely and let your custom.css be the whole theme surface, or +org/yourorg to use an org theme that bundles CSS, fonts and assets authored once by your org admin.

You can set the theme via the CLI, the API or the editor.

# CLI
novem -p my-plot -w config/theme custom

# API
curl -X POST -d "custom" \
  https://api.novem.io/v1/vis/plots/my-plot/config/theme

When theme = custom, the only style sources are the engine skeleton plus your custom.css. When theme = novem, the built-in theme sits takes over and will override your custom.css values. This is how a chart, when embedded in a plot, will match the parent theme.

A handful of recurring patterns emerge once you've themed a few visualisations.

Always use the variables, not hardcoded values. A chart that hardcodes stroke: "#212529" looks invisible in dark mode. Reach for var(--novem-axis-color) in CSS, or render.theme.axisColor in JS, and the engine flips them for you.

Test in dark mode. The webapp toggles dark mode with the moon icon in the top-right. A theme that only handles light mode is half a theme.

Don't abuse !important. The cascade gives you all the layering you need: skeleton, then theme, then per-vis. If you find yourself reaching for !important, you're usually fighting the cascade in the wrong direction.

It's idiomatic to keep your brand colours as private variables like --brand-navy and re-route --novem-* to point at them. That way the brand has a single source of truth and the primitives keep their semantic meaning.

Variables propagate into iframes, so anything you set on .novem--doc--page shows up in embedded plots automatically. You don't need to repeat your theme inside each plot.