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.