Charts - Theming

Style charts with CSS custom properties for SVG, or a plain JS theme object for Canvas.

#Usage

import { ChartCanvas, ChartHover, ChartSvg } from '@primeui/vue-chart';
<!-- SVG: override CSS custom properties -->
<ChartSvg>
    <!-- series and axes -->
</ChartSvg>

<!-- Canvas: pass a theme object -->
<ChartCanvas :theme="theme">
    <!-- series and axes -->
</ChartCanvas>

SVG charts read CSS custom properties natively. Override --p-chart-* variables at any scope and charts update without a JS re-render. Canvas charts stay DOM-free, so theming goes through a plain JS theme prop instead.

#Overview

PrimeUI Chart supports two renderers and they theme differently by design:

  • SVG: reads CSS custom properties natively. Override --p-chart-* variables at any scope and charts update without a JS re-render. Dark mode is handled by CSS.
  • Canvas: stays DOM-free so it works in Web Workers, OffscreenCanvas, and SSR. Theme is a plain JS object passed as a prop.

The theme prop works on both <ChartSvg> and <ChartCanvas>. CSS is SVG only.

Both renderers ship with matching default palettes so they look identical by default.

#Default Palette

Charts ship with a 14-color categorical palette. The order is designed for adjacent contrast in pies, donuts, stacked bars, and legends: cool and warm colors alternate early, while softer secondary colors extend dense series without making normal charts feel busy. Dark mode uses the same hue families with a modest lift, so marks stay readable without becoming neon.

SlotCSS variableLightDark
0--p-chart-color-0#5daeea#6bbbed
1--p-chart-color-1#ffad5a#ffb76d
2--p-chart-color-2#ffd166#ffdc7a
3--p-chart-color-3#4ecdc4#61d8cf
4--p-chart-color-4#7c8cff#909dff
5--p-chart-color-5#c084fc#cc99fd
6--p-chart-color-6#ff6fae#ff82ba
7--p-chart-color-7#9ccc3c#aad64c
8--p-chart-color-8#ff7a66#ff8b76
9--p-chart-color-9#36b7d6#4fc7df
10--p-chart-color-10#a78bfa#b79dfb
11--p-chart-color-11#5ccf9f#6fdaad
12--p-chart-color-12#fda4af#fdb1bb
13--p-chart-color-13#94a3b8#a8b5c6

For SVG, override these variables where the chart lives. Use light-dark() when the page owns color-scheme.

.revenue-chart {
    --p-chart-color-0: light-dark(#5daeea, #6bbbed);
    --p-chart-color-1: light-dark(#ffad5a, #ffb76d);
    --p-chart-color-2: light-dark(#ffd166, #ffdc7a);
}

For Canvas, pass the same palette through theme.series.

const chartTheme = {
    series: ['#5daeea', '#ffad5a', '#ffd166', '#4ecdc4', '#7c8cff']
};

#SVG

SVG charts are class-driven: every visual element carries a stable class (p-chart-axis-line, p-chart-grid-line, p-chart-tick-label, p-chart-color-0 … p-chart-color-13, etc.) and reads its color from a CSS custom property.

Import the stylesheet once at the app root:

import '@primeui/chart-style/style.css'; // class rules
import '@primeui/chart-style/themes/default.css'; // tokens (or themes/primeone.css for PrimeUI integration)

Override any variable at any scope (:root, a section, or a single chart wrapper) and the chart picks it up without a JS re-render.

#Series Palette

Each series picks up --p-chart-color-N (14 slots by default, indexed by series order). Override a slot inline on any wrapper to retheme the charts inside it.

Loading Demo...

#Chrome (Axes, Grid, Labels)

Chrome elements read semantic aliases. Override an alias to restyle that element without changing the series palette.

TokenUsed by
--p-chart-axisAxis lines, tick marks, axis titles
--p-chart-gridMajor grid lines
--p-chart-grid-minorMinor grid lines
--p-chart-tick-label-colorAxis tick labels
--p-chart-data-label-colorBar/line/point data labels
--p-chart-annotation-colorAnnotations, reference labels
--p-chart-title-colorChart title
--p-chart-caption-colorChart caption
--p-chart-band-fillAlternate band fill
Loading Demo...

#Hover & Dim Effects

Hover and optional dimming are class-driven (.p-chart-point-hover, .p-chart-point-inactive, .p-chart-series-inactive) and read coefficients from --p-chart-hover-brightness (default 1.1) and --p-chart-dim-opacity (default 1). Set --p-chart-dim-opacity below 1 globally, or pass dimOpacity to ChartHover, when non-hovered marks should fade.

<ChartHover :brightness="1.2" :dim-opacity="0.4" />

#Light & Dark

Tokens use the CSS light-dark() function, so dark mode follows the page's color-scheme. With a PrimeUI library, color-scheme: dark on .p-dark flips the entire chart palette with no JS required. With the standalone default.css, set color-scheme on a parent element (for example color-scheme: light dark, switching to dark for dark mode), since the chart styles do not set it.

#More Than 14 Series Colors

By default colors cycle back to slot 0 after the 14th series. For charts with more than 14 series, define additional --p-chart-color-N slots in CSS:

.my-chart {
    --p-chart-color-14: #e879f9;
    --p-chart-color-15: #facc15;
    --p-chart-color-16: #38bdf8;
}

#Canvas

The canvas renderer doesn't read CSS variables, so theming is done through the theme prop on <ChartCanvas>. The prop accepts a partial ChartTheme object. All fields are optional and merge over the built-in defaults.

<ChartCanvas :theme="{ series: ['#5daeea', '#ffad5a'], axes: ['#64748b'] }" />

#Creating a Custom Theme

The library exports defaultLightTheme and defaultDarkTheme as a base. Spread and override only what is needed in a dedicated theme file.

// themes/my-theme.ts
import { defaultLightTheme, defaultDarkTheme } from '@primeui/vue-chart';

export const myLightTheme = {
    ...defaultLightTheme,
    series: ['#5daeea', '#ffad5a', '#ffd166', '#4ecdc4', '#7c8cff', '#c084fc', '#ff6fae', '#9ccc3c', '#ff7a66', '#36b7d6']
};

export const myDarkTheme = {
    ...defaultDarkTheme,
    series: ['#6bbbed', '#ffb76d', '#ffdc7a', '#61d8cf', '#909dff', '#cc99fd', '#ff82ba', '#aad64c', '#ff8b76', '#4fc7df']
};

Then bind it to the charts.

<ChartCanvas :theme="myLightTheme" />

#Canvas Palette Override

Canvas does not repaint drawn marks from CSS selectors. Pass a reactive theme object to ChartCanvas when the palette, grid, axes, tooltip, legend, or crosshair should change. The example below toggles between light and dark theme objects built from defaultLightTheme and defaultDarkTheme; the chart repaints because the bound theme prop changes.

Loading Demo...

#More Than 14 Series Colors

Pass a series array longer than 14. The canvas renderer cycles through all entries using modulo before wrapping.

const theme = {
    ...defaultLightTheme,
    series: ['#5daeea', '#ffad5a', '#ffd166', '#4ecdc4', '#7c8cff', '#c084fc', '#ff6fae', '#9ccc3c', '#ff7a66', '#36b7d6', '#a78bfa', '#5ccf9f', '#fda4af', '#94a3b8', '#e879f9', '#38bdf8'] // 16 colors
};

#Light & Dark

Because canvas charts are DOM-free, they don't react to CSS changes on their own. The correct theme object must be passed based on the active color scheme. The simplest approach is to detect dark mode in JS and swap between the light and dark themes reactively, similar to how Chart.js handles theming.

<script setup>
import { ref, onMounted, onBeforeUnmount } from 'vue';
import { myLightTheme, myDarkTheme } from './themes/my-theme';

const isDark = () => getComputedStyle(document.documentElement).colorScheme === 'dark';
const theme = ref(isDark() ? myDarkTheme : myLightTheme);

let observer;
onMounted(() => {
    observer = new MutationObserver(() => {
        theme.value = isDark() ? myDarkTheme : myLightTheme;
    });
    observer.observe(document.documentElement, {
        attributes: true,
        attributeFilter: ['class', 'data-theme']
    });
});
onBeforeUnmount(() => observer?.disconnect());
</script>

<template>
    <ChartCanvas :theme="theme" :height="400">...</ChartCanvas>
</template>

Since theme is a reactive ref, updating it is all that's needed. The chart re-renders on its own. This logic can be extracted into a composable and reused across canvas charts.