Skip to content
InsightCharts

Search

Search guides and components

Core concepts

Theming

Theme tokens, the --ic-* CSS variables, presets and createTheme. Charts read shadcn/ui variables by default.

A theme is two sets of tokens, one for light and one for dark. Any colour token may be a literal (#2a78d6, oklch(...)) or a CSS variable reference (var(--brand, #2a78d6)). Variables are resolved against the chart's own DOM node, so scoped overrides work and the canvas always receives concrete colours.

Try the presets and accents. Everything below re-themes through one InsightProvider. Some presets are designed for a background colour of their own, and the demo paints it. The last eight names are the poster themes. See presets.

Revenue, Q4 ($m)
$97
+12.8% vs Q3
Subscriptions were 65% of the total

Subscription revenue rose from $42m in Q1 to $63m in Q4

Revenue by line of business

$ millions per quarter

Illustrative data

How the default theme follows your page

With no configuration, the chrome follows the host page. The default theme reads shadcn/ui's variables when they exist and falls back to neutral values when they do not:

TokenReadsFallback (light / dark)
surface--background#fcfcfb / #1a1a19
ink--foreground#0b0b0b / #f0efec
muted--muted-foreground#898781
fontFamilyinherited from the pagesystem UI font

Series colours do not come from the page by default. The default categorical order is checked for separation between neighbours under colour-vision deficiency in both modes, so keep the order if you re-skin it.

CSS variables

Every token of the default theme can be overridden with an --ic-* variable, set anywhere above the chart: on :root, on a section, or on a single wrapper.

Default tokens

Scoped --ic-* overrides

VariableTokenUsed for
--ic-surfacesurfaceWhat sits behind the marks; the gaps and rings that separate them.
--ic-inkinkTitles and primary values.
--ic-ink-secondaryinkSecondarySubtitles, legends, direct labels.
--ic-mutedmutedAxis labels and source lines.
--ic-gridgridHairline gridlines.
--ic-axisaxisBaselines and axis lines.
--ic-accentaccentThe highlighted series, and a single series.
--ic-contextcontextDe-emphasised series behind a highlight.
--ic-positive, --ic-negativepositive, negativeUp and down.
--ic-series-1 to --ic-series-8categoricalIdentity colours, in order.
--ic-fontfontFamilyChart and chrome text.
--ic-font-displaydisplayFamilyTitles and big numbers.
:root {
  --ic-accent: #0f766e;
  --ic-font-display: "Fraunces", Georgia, serif;
}
.dark {
  --ic-accent: #2dd4bf;
}

The sequential and diverging ramps, and the numeric tokens fontSize, radius and lineWidth, have no variables. Set them with a theme object.

Theme objects and createTheme

The theme prop on any chart, or on InsightProvider, accepts a full theme or a partial one. Tokens at the top level apply to both modes; light and dark override one mode. A partial theme is merged over the surrounding theme, so providers and charts only state what differs.

import { BarChart, InsightProvider } from "insightcharts";

<InsightProvider theme={{ accent: "#7c3aed", radius: 2, dark: { accent: "#a78bfa" } }}>
  <BarChart theme={{ lineWidth: 3 }} ... />
</InsightProvider>

createTheme(input, base?) does the same merge ahead of time and returns a complete theme you can export and reuse. base defaults to the default theme.

import { createTheme, themes } from "insightcharts/themes";

export const brand = createTheme({
  name: "brand",
  accent: "var(--brand, #7c3aed)",
  categorical: ["#7c3aed", "#0891b2", "#ca8a04", "#be123c"],
  light: { grid: "#ebe9f5" },
  dark: { grid: "#2a2740" },
});

// Or start from a preset.
export const compact = createTheme({ fontSize: 12, lineWidth: 1.5 }, themes.shadcn);

Tokens

TokenTypePurpose
surface, ink, inkSecondary, muted, grid, axiscolourChrome, from background to baselines.
accent, contextcolourThe highlighted series, and the grey used for every other series.
positive, negativecolourDirection. Always paired with a non-colour cue.
categoricalcolour[]Identity colours, assigned to entities in fixed order.
sequentialcolour[]One hue, light to dark, for magnitude.
divergingcolour[]Negative pole, neutral midpoint, positive pole.
fontFamily, displayFamilystringFonts for body text and for titles.
fontSizenumberBase chart text size in pixels. Default 12.
radiusnumberCorner radius of bar ends in pixels. Default 4.
lineWidthnumberLine thickness in pixels. Default 2.

Presets

themes from insightcharts/themes is a record of every theme by name. It holds the eight presets in this table and the eight poster themes. Each preset is also a named export (editorialTheme, ledgerTheme, monoTheme, earthTheme, scienceTheme, posterTheme, shadcnTheme). This table is drawn from the record itself; the preview toolbar on every component page lists the same presets.

PresetLight, on its surfaceDark, on its surfaceDesigned for
themes.insightthe pagethe pageThe default theme. It reads shadcn/ui variables for surface and ink.
themes.shadcnthe pagethe pageReads the page variables and takes series colours from --chart-1 to --chart-5.
themes.editorial#f8f7f4#15171aNewspaper graphics. Off-white surface, blue-black ink, serif titles, carmine accent.
themes.ledger#fff1e5#201c1aFinancial reports. Salmon surface, dark blue accent, claret and teal series.
themes.mono#ffffff#0b0b0cHigh contrast. Black and white text and axes, square marks, a thick baseline.
themes.earth#f4f1ea#1b1a17Environmental reports. Warm grey surface, green accent, blue, ochre and clay series.
themes.science#f6f8fa#0e151cJournal figures. Blue-grey surface, thin lines, small corner radius, teal accent.
themes.poster#f6f3fb#16112ePosters and social cards. Saturated magenta accent, thick lines, round bar ends.

Presets and their surfaces

insight and shadcn follow the page they are on. The other six are designed for a specific background colour in each mode, and its separators, marker rings and sticky table headers are drawn in that colour. On any other background they show as off-colour seams, and the contrast the palette was checked for no longer holds.

themeSurfaces[name].light and .dark are those backgrounds. Paint the section, card or page the charts sit in, and force the mode to match it:

Air fares, index (2019 = 100)
131
+3.1% vs 2023

Buses carried 3.6bn trips

The air fare index rose from 97 in 2021 to 131 in 2024

Fare index, 2019 = 100

Illustrative data
themeSurfaces.editorial.light = #f8f7f4
import { InsightProvider } from "insightcharts";
import { ledgerTheme, themeSurfaces } from "insightcharts/themes";

<section style={{ background: themeSurfaces.ledger.light }}>
  <InsightProvider theme={ledgerTheme} mode="light">{children}</InsightProvider>
</section>

To follow the page's mode instead of forcing one, read it with useColorMode and pick the matching surface, as the first demo on this page does.

The shadcn preset

The shadcn preset reads more of the page's variables than the default theme does. Series colours come from --chart-1 to --chart-5 and the accent from --chart-1. Use it when charts must match an existing shadcn palette. shadcn's stock chart colours are not ordered for colour-vision-deficiency separation, so keep direct labels or legends on.

import { InsightProvider } from "insightcharts";
import { themes } from "insightcharts/themes";

<InsightProvider theme={themes.shadcn}>{children}</InsightProvider>

Poster themes

A poster theme is a theme for the infographics and the poster frame. It has a strong painted ground, type colours for that ground and four or five mark colours. posterThemes from insightcharts/themes holds the eight by name, and the same eight are in the themes record. Each is also a named export: noirTheme, magentaTheme, duskTheme, paperTheme, oceanTheme, forestTheme, blueprintTheme and sunriseTheme.

noirone look

Near-black sheet, white type, yellow and sky blue marks.

#0d0d0f

magentaone look

Saturated magenta sheet, pale pink blocks, cyan accent.

#c2006b

duskone look

Violet gradient, marks from orange to yellow.

linear-gradient(160deg, #24104f 0%, #3d1663 55%, #5e2067 100%)

paperlight and dark

Warm off-white stock, black ink, one red.

#f3ecdf

dark: #1d1b18

oceanone look

Navy to teal gradient, aqua and coral marks.

linear-gradient(180deg, #041c3a 0%, #06365a 55%, #084c5c 100%)

forestone look

Dark green sheet, lime and cream marks.

#0f3d2a

blueprintone look

Cobalt sheet, white linework, yellow accent.

#1447c8

sunriseone look

Cream to peach gradient, plum and tangerine marks.

linear-gradient(180deg, #fff1e0 0%, #ffdfc6 100%)

A poster theme differs from a preset in three ways.

  • One look. noir, magenta, dusk, ocean, forest, blueprint and sunrise have the same tokens in light and dark. The poster has the same colours on a light page and on a dark page. paper has a second variant: its dark tokens are the same design on charcoal stock.
  • A painted ground. posterBackgrounds[name].light and .dark are the CSS background of each theme. dusk, ocean and sunrise are gradients, so the value is a CSS background and not a single colour. The theme's surface token is the solid colour closest to the ground. Charts draw the gaps between touching marks in surface.
  • A small palette. A poster theme has four or five categorical colours, not eight. Fold further entities into "Other" or shade by value with the sequential ramp.

The poster frame and every infographic template take the name as theme and paint the ground themselves:

import { Poster } from "insightcharts";

<Poster theme="ocean" title="..." panels={panels} />

To use a poster theme without the frame, paint the ground on a container and force one mode inside it:

import { InsightProvider } from "insightcharts";
import { posterBackgrounds, posterThemes } from "insightcharts/themes";

<section style={{ background: posterBackgrounds.ocean.light }}>
  <InsightProvider theme={posterThemes.ocean} mode="light">{children}</InsightProvider>
</section>

The text colours of each theme have a contrast of at least 4.5:1 against every colour stop of its ground. The categorical colours and the accent have at least 3:1. The display faces are system font stacks. Set --ic-font-display on an ancestor to use your own face.

Restyling the chrome

Titles, legends and source lines are real HTML. Each element carries a data-slot attribute (ic-figure, ic-title, ic-subtitle, ic-legend, ic-plot, ic-source, ic-table, ic-actions), so you can restyle them from your own CSS.

[data-slot="ic-title"] {
  font-size: 1.25rem;
  letter-spacing: -0.02em;
}