The chart ships with 37 themes. A theme styles everything inside the chart page: the candles and lines on the canvas, the grid and axis text, indicator panes, the toolbar and menus, and the page background.
Because the chart runs in an iframe, your app's CSS cannot reach inside it, and the chart's CSS cannot reach your app. You style the chart by choosing a theme, or by adding your own theme to the chart's source.
1. Pick a theme#
Theme ids: call listThemes() (or the bridge command of the same name). A few
of them: dark-blue (the default), light, midnight, graphite, ocean,
oled, mono.
| Where | How |
|---|---|
| React | <KandloChart theme="ocean" />. Changing the prop switches the theme. |
| Plain API | createKandloChart(el, { theme: 'ocean' }), later chart.setTheme('midnight') |
| postMessage | { kandlo: 1, id, type: 'setTheme', payload: { theme: 'midnight' } } |
| Chart page URL | ?embed=1&theme=ocean |
An unknown id is refused with unknown_theme (a URL value is ignored with a
console warning).
The themeChange event (onThemeChange in React) reports every change,
including one the user makes in the chart's own theme menu when the toolbar
is shown (chrome="full", the default).
In an embed the theme your page sets is not saved: the next load starts
from what your page passes again. The standalone chart page (opened directly,
not embedded) saves the user's choice in its origin's localStorage under
kandlo-theme, and starts from dark-blue when nothing is saved.
Follow the system light/dark setting#
import { useEffect, useState } from 'react';
function useSystemTheme(light = 'light', dark = 'dark-blue') {
const query = '(prefers-color-scheme: dark)';
const [isDark, setDark] = useState(() => window.matchMedia(query).matches);
useEffect(() => {
const m = window.matchMedia(query);
const on = (e) => setDark(e.matches);
m.addEventListener('change', on);
return () => m.removeEventListener('change', on);
}, []);
return isDark ? dark : light;
}
// <KandloChart theme={useSystemTheme()} ... />Around the chart#
chrome="minimal"(URLchrome=minimal) hides the chart's toolbar, zoom selector and tuning button, for pages that have their own controls.- The container
<div>the component renders (data-kandlo-chart) is yours to style: border, radius, shadow, size. Give it a background close to the theme's so the area does not flash while the chart page loads. Most themes declare their page background assurfaces.bginthemes/<id>.theme.js(#06131dforocean). The two base themes,dark-blueandlight, take theirs from the base stylesheet instead.
2. Make your own theme#
A theme is one file, themes/<id>.theme.js. The canvas palette, the
toolbar and menu styles, the theme picker entry, the stylesheet and the
background illustration are all generated from it.
cp themes/_example.theme.js themes/brand.theme.js # set id, constant, name, kind, cat, colours
# add "brand" to themes/_order.json, where it should sit in the theme picker
npm run themes # validate and regenerate
npm run build && npm run build:interactions # rebuild the bundles
npm run build:static # if you deploy the static siteidis what you pass astheme('brand'); it appears inlistThemes()after the rebuild.themes/_order.jsonfixes the picker order. A theme missing from it stopsnpm run themeswithThemes missing from themes/_order.json: brand.npm run themeschecks every theme before it writes anything, including a candle-contrast check: rising and falling candles must differ by at least 40 degrees of hue or enough brightness after the 3D shading (contrastWaiverturns a failure into a warning).npm run themes:checkruns the same checks without writing.- Canvas colours (
candles,grid,pane) are 6-digit hex without#; page colours (surfaces,backdrop,swatch) are CSS colours with#.
The full contract, field by field, is in
themes/_schema.md (themes/_schema.md). Do not edit the generated
regions in the sources (between // <<< generated: and // >>> generated:
markers) or apps/web/assets/styles/theme-<id>.css; they are rewritten from
themes/.
Offer only your themes#
The API accepts every theme that is built in. To present a short list in your
app, keep your own list of ids and pass only those; with chrome="minimal"
the chart's own theme menu is hidden, so users cannot pick others.