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.
| Slot | CSS variable | Light | Dark |
|---|---|---|---|
| 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.
#Chrome (Axes, Grid, Labels)
Chrome elements read semantic aliases. Override an alias to restyle that element without changing the series palette.
| Token | Used by |
|---|---|
--p-chart-axis | Axis lines, tick marks, axis titles |
--p-chart-grid | Major grid lines |
--p-chart-grid-minor | Minor grid lines |
--p-chart-tick-label-color | Axis tick labels |
--p-chart-data-label-color | Bar/line/point data labels |
--p-chart-annotation-color | Annotations, reference labels |
--p-chart-title-color | Chart title |
--p-chart-caption-color | Chart caption |
--p-chart-band-fill | Alternate band fill |
#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.
#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.