Showcase

Dark Mode Dashboard

A marketing dashboard behind one Light, Dark and System switch. Charts, a campaign table and a visitor-journey flow all follow it, though each of the three products takes a theme in a different way.

Dark mode dashboard: ApexCharts, ApexGrid and ApexSankey behind one theme switchOpen in new tab

Built with ApexCharts.js, ApexGrid, ApexSankey

To put an ApexCharts chart in dark mode, set theme.mode: 'dark', and set chart.background: 'transparent' alongside it so the chart sits on your page's surface instead of painting its own. That is the whole answer for one chart.

The dashboard above is the harder case: a page of three products, ApexCharts.js for the charts, ApexGrid for the campaign table and ApexSankey for the visitor journey, behind one Light, Dark and System switch. The three take a theme in three different ways, one of them only at construction time, and that is the part no single product's documentation covers.

How do you enable dark mode in ApexCharts?

const chart = new ApexCharts(el, {
  chart: { type: 'area', background: 'transparent' },
  theme: { mode: 'dark' },
  // ...
})

// switch at runtime, no re-create needed
chart.updateOptions({ theme: { mode: 'light' } }, false, false)

The background line matters more than it looks. With chart.background unset, the mode picks a background for you: 'dark' paints the SVG #343A3F and 'light' paints it #fff. On a designed dark page that is a grey rectangle inside every panel. 'transparent' hands the background back to your CSS in both modes.

To follow the operating system with no script at all, use theme: { follow: 'os' }. On a page of charts alone that is the best option. On this page it is the wrong one, for the reason in the next section.

theme.mode also leaves two things alone. Series colours are yours, so pick a palette that holds contrast on both surfaces (this dashboard uses one palette for both modes). And the radialBar track keeps its light grey ring on a dark page unless you set plotOptions.radialBar.track.background per mode.

The dark mode guide covers React, Next.js and export.

How do you switch a page of different products to dark mode?

One resolver, three mechanisms. Measured on ApexCharts 7.8.0, apex-grid 3.5.0 and ApexSankey 1.12.2:

ProductHow it takes a themeFollows the OS by itself?Paints its own surface?
ApexChartsruntime option, updateOptions({ theme: { mode } })with theme.follow: 'os'yes, #343A3F, unless chart.background is set
ApexGridattribute, theme="dark" on the elementnoyes, its slate palette, #0f172a
ApexSankeyconstructor option only, theme: 'dark'noyes, #0e1a23, unless canvasStyle overrides it

So "System" cannot be delegated to each product. If the charts follow the OS on their own while the grid and the Sankey do not, an OS switch at sunset turns a third of the page dark. The page resolves the choice once and tells every product the answer:

const osDark = matchMedia('(prefers-color-scheme: dark)')
const resolve = () => (choice === 'system' ? (osDark.matches ? 'dark' : 'light') : choice)

function applyTheme() {
  const mode = resolve()
  document.documentElement.dataset.theme = mode            // the page's own CSS tokens

  for (const c of charts) c.updateOptions(chartTheme(mode), false, false)

  if (mode === 'dark') grid.setAttribute('theme', 'dark')
  else grid.removeAttribute('theme')

  if (sankeyMode !== mode) buildSankey(mode)               // rebuild: see below
}

// Only an explicit "System" listens. A reader who chose Dark keeps Dark.
osDark.addEventListener('change', () => { if (choice === 'system') applyTheme() })

The page's own tokens are matched to ApexGrid's built-in dark palette, whose host paints #0f172a, so the table reads as the same material as the panels around it rather than a slightly different dark.

Why does ApexSankey need a rebuild to change theme?

Because the theme is read once, in the constructor. Calling sankey.update() with theme: 'dark' in its options leaves the diagram as it was: the labels stay #212121 and sankey.options.theme still reads 'light' afterwards. A theme change is a destroy and a new instance:

function buildSankey(mode) {
  if (sankey) sankey.destroy()
  journeyEl.replaceChildren()
  const width = Math.max(320, journeyEl.clientWidth)
  sankey = new ApexSankey(journeyEl, {
    theme: mode,
    width,
    height: 300,
    viewPortWidth: width,     // draw at 1:1, see below
    viewPortHeight: 300,
    canvasStyle: 'background: transparent; border: 0;', // beats the theme's surface
  })
  sankey.render({ nodes, edges, options: sankey.options })
  sankey.on('node:click', ({ id }) => setChannel(id))
}

Two things make that rebuild cheap to live with. It runs only when the resolved mode actually changes, not on every click of the switch. And the same function is the resize handler, because size is a construction-time input too: the diagram is laid out in an 800 by 500 viewBox by default and scaled to fit, so in a wide, short panel it shrinks to a third of the width with tiny labels unless viewPortWidth and viewPortHeight are the real size.

Pass the width as a number. A '100%' width makes ApexSankey apply the same unit to the height, so height: 300 becomes 300%.

What breaks first

A light flash on load. If the charts are created with default options and then switched by applyTheme(), a dark page shows white charts for a frame. Create them with the resolved theme already in their options, then call applyTheme() for the other two products.

Channel colours that change with the theme. The Sankey's dark theme brings its own node palette. Setting color on the four channel nodes pins them to the same colours as the sessions chart, so Search is the same blue in the chart, the journey and the table in both modes.

A theme preference that cannot be stored. The switch remembers the choice in localStorage, inside a try, because storage throws in some private windows and embedded contexts. When it does, the page falls back to its default instead of failing to start.

Which plan covers this?

ApexCharts.js and ApexGrid are included on every plan, including Community, which is free for organizations under $2M in annual revenue, budget or funding. ApexSankey is included from the Pro plan upward; without a license key it renders with a watermark, so the dashboard can be rebuilt on your own data before buying. Redistributing the libraries inside software you sell or host is covered by the OEM plan.

Every theming option in ApexCharts

See the pieces running

Reference documentation

Frequently Asked Questions

How do I enable dark mode in ApexCharts?

Set theme.mode to 'dark', and set chart.background to 'transparent' alongside it. Without the background line the chart paints its own #343A3F surface, which shows as a grey box on any other dark page. Switch at runtime with chart.updateOptions({ theme: { mode: 'light' } }); there is no need to re-create the chart. To follow the operating system with no script, use theme.follow: 'os'.

Why is there a grey box behind my dark ApexCharts chart?

That is the chart's default dark background. When chart.background is unset, theme.mode 'dark' paints the SVG #343A3F (and 'light' paints it #fff). Set chart.background to 'transparent' to let your container's colour show through.

Does ApexGrid follow the operating system dark mode?

No. Measured on apex-grid 3.5.0, the grid stays light under a dark OS preference until the theme="dark" attribute is set on the element. Resolve the preference in your own code and set the attribute.

Can ApexSankey change theme without being re-created?

No. ApexSankey 1.12.2 reads its theme in the constructor, and calling update() with a different theme in the options leaves the diagram unchanged. Destroy the instance and build a new one with the new theme, and only when the resolved mode actually changed.

Which plans include ApexCharts, ApexGrid and ApexSankey together?

ApexCharts.js and ApexGrid are on every plan, including Community, which is free for organizations under $2M in annual revenue, budget or funding. ApexSankey is included from the Pro plan upward, and renders with a watermark without a license key, so the dashboard can be rebuilt on your own data first. Redistributing them inside software you sell or host requires the OEM plan.

Related

Theme a dashboard of your own

ApexCharts and ApexGrid are on every plan; ApexSankey is Pro and above.

Get started