Theming
The interface is styled with CSS variables set on its root element (.ar-root). There are three ways to set them.
1. The theme prop
Section titled “1. The theme prop”import { DARK_THEME, ReportViewer } from '@ariana-reports/react';
<ReportViewer theme={DARK_THEME} template={template} data={data} /><ReportViewer theme={{ primary: '#6750a4', radius: 12, fontFamily: 'Inter, sans-serif' }} template={template} data={data} /><ReportViewer :theme="DARK_THEME" :template="template" :data="data" /><ReportViewer :theme="{ primary: '#6750a4', radius: 12, fontFamily: 'Inter, sans-serif' }" :template="template" :data="data" />| Theme property | CSS variable | Used for |
|---|---|---|
primary, onPrimary |
--ar-primary, --ar-on-primary |
selection, focus rings, active buttons |
background |
--ar-bg |
workspace behind pages |
surface, surfaceVariant |
--ar-surface, --ar-surface-2 |
toolbars, panels, section headers |
inputBackground, border |
--ar-input-bg, --ar-border |
inputs and dividers |
text, textMuted, danger |
--ar-text, --ar-muted, --ar-danger |
text, labels, errors |
radius, fontFamily, fontSize |
--ar-radius, --ar-font, --ar-font-size |
shape and typography |
bandColor |
--ar-band-color |
default band caption color in the designer |
mode |
color-scheme |
light or dark: native scrollbars and pickers |
2. From a Material UI theme
Section titled “2. From a Material UI theme”themeFromMui(theme) converts a MUI theme, so the report UI follows your palette and dark mode. See Material UI.
3. Plain CSS
Section titled “3. Plain CSS”Set the variables in a rule for .ar-root. The defaults have zero specificity, so your rule wins whatever the order of the stylesheets:
.ar-root { --ar-primary: #6750a4; --ar-surface: #fffbfe;}
[data-theme='dark'] .ar-root { --ar-bg: #1c1b1f; --ar-surface: #2b2930; --ar-text: #e6e1e5; color-scheme: dark;}The variables must be set on .ar-root itself (not on a parent). All class names start with ar- if you want to override individual rules.
Report pages stay white
Section titled “Report pages stay white”Pages and the design surface are drawn like paper, with the template’s own colors, in every theme. Theming only changes the application chrome around them.
Try it
Section titled “Try it”import { useState } from 'react';import { DARK_THEME, ReportViewer, themeFromMui } from '@ariana-reports/react';import { salesData, salesTemplate } from './report-data';
// Any object shaped like a Material UI theme works, so in a MUI app pass useTheme() directly.const grape = themeFromMui({ palette: { mode: 'light', primary: { main: '#7b1fa2' }, background: { default: '#f3e5f5', paper: '#ffffff' } }, shape: { borderRadius: 14 },});
const themes = { default: undefined, dark: DARK_THEME, grape } as const;
export default function ViewerTheme() { const [name, setName] = useState<keyof typeof themes>('grape'); return ( <div style={{ display: 'flex', flexDirection: 'column', height: '100%' }}> <label style={{ padding: 8, background: '#fff', font: '13px system-ui' }}> Theme{' '} <select value={name} onChange={(e) => setName(e.target.value as keyof typeof themes)}> {Object.keys(themes).map((t) => ( <option key={t}>{t}</option> ))} </select> </label> <div style={{ flex: 1, minHeight: 0 }}> <ReportViewer template={salesTemplate} data={salesData} theme={themes[name]} zoomMode="fitWidth" showIssues /> </div> </div> );}<script setup lang="ts">import { computed, ref } from 'vue';import { DARK_THEME, ReportViewer, themeFromMui } from '@ariana-reports/vue';import { salesData, salesTemplate } from './report-data';
// Any object shaped like a Material UI theme works.const grape = themeFromMui({ palette: { mode: 'light', primary: { main: '#7b1fa2' }, background: { default: '#f3e5f5', paper: '#ffffff' } }, shape: { borderRadius: 14 },});
const themes = { default: undefined, dark: DARK_THEME, grape } as const;const name = ref<keyof typeof themes>('grape');const theme = computed(() => themes[name.value]);</script>
<template> <div style="display: flex; flex-direction: column; height: 100%"> <label style="padding: 8px; background: #fff; font: 13px system-ui"> Theme <select v-model="name"> <option v-for="t in Object.keys(themes)" :key="t">{{ t }}</option> </select> </label> <div style="flex: 1; min-height: 0"> <ReportViewer :template="salesTemplate" :data="salesData" :theme="theme" zoom-mode="fitWidth" show-issues /> </div> </div></template>{ "schemaVersion": 1, "name": "Sales by customer", "unit": "mm", "page": { "size": "A4", "orientation": "portrait", "margins": { "top": 12, "right": 12, "bottom": 12, "left": 12 } }, "dataSources": [ { "name": "orders" } ], "styles": { "title": { "fontSize": 18, "fontWeight": "bold", "color": "#0f172a" }, "muted": { "fontSize": 9, "color": "#64748b" }, "th": { "fontSize": 9, "fontWeight": "bold", "color": "#ffffff", "backgroundColor": "#334155", "padding": 1.5 }, "td": { "fontSize": 9, "padding": 1.5, "border": { "width": 0.4, "color": "#e2e8f0", "style": "solid", "sides": [ "bottom" ] } }, "num": { "textAlign": "right" }, "sub": { "fontSize": 9, "fontWeight": "bold", "backgroundColor": "#e2e8f0", "padding": 1.5 } }, "reportHeader": { "id": "rh", "height": 16, "components": [ { "id": "title", "type": "text", "x": 0, "y": 0, "width": 186, "height": 9, "text": "Sales by customer", "styleName": "title" }, { "id": "sub", "type": "text", "x": 0, "y": 10, "width": 186, "height": 5, "text": "{{ count(orders) }} orders from {{ distinct(orders, \"customer\").length }} customers", "styleName": "muted" } ] }, "pageFooter": { "id": "pf", "height": 8, "components": [ { "id": "pf_n", "type": "text", "x": 0, "y": 2, "width": 186, "height": 5, "text": "Page {{ page.number }} of {{ page.count }}", "style": { "fontSize": 8, "color": "#64748b", "textAlign": "right" } } ] }, "body": [ { "kind": "dataRegion", "id": "orders_region", "dataSource": "orders", "sort": [ { "expression": "row.date", "direction": "asc" } ], "groups": [ { "id": "by_customer", "expression": "row.customer", "header": { "id": "gh", "height": 8, "components": [ { "id": "gh1", "type": "text", "x": 0, "y": 1, "width": 186, "height": 6, "text": "{{ group.key }}", "style": { "fontSize": 11, "fontWeight": "bold", "color": "#1d4ed8", "padding": 1 } } ] }, "footer": { "id": "gf", "height": 8, "components": [ { "id": "gf1", "type": "text", "x": 0, "y": 1, "width": 134, "height": 6, "text": "Subtotal ({{ group.count }} orders)", "styleName": "sub" }, { "id": "gf2", "type": "text", "x": 134, "y": 1, "width": 52, "height": 6, "text": "{{ sum(group.rows, \"amount\") }}", "styleName": "sub", "style": { "textAlign": "right" }, "format": { "type": "currency", "currency": "USD" } } ] } } ], "header": { "id": "head", "height": 7, "components": [ { "id": "h1", "type": "text", "x": 0, "y": 0, "width": 22, "height": 7, "text": "Order", "styleName": "th" }, { "id": "h2", "type": "text", "x": 22, "y": 0, "width": 30, "height": 7, "text": "Date", "styleName": "th" }, { "id": "h3", "type": "text", "x": 52, "y": 0, "width": 82, "height": 7, "text": "Product", "styleName": "th" }, { "id": "h4", "type": "text", "x": 134, "y": 0, "width": 52, "height": 7, "text": "Amount", "styleName": "th", "style": { "textAlign": "right" } } ] }, "detail": { "id": "row", "height": 6.5, "components": [ { "id": "c1", "type": "text", "x": 0, "y": 0, "width": 22, "height": 6.5, "text": "#{{ row.id }}", "styleName": "td" }, { "id": "c2", "type": "text", "x": 22, "y": 0, "width": 30, "height": 6.5, "text": "{{ row.date }}", "styleName": "td", "format": { "type": "date", "pattern": "dd MMM yyyy" } }, { "id": "c3", "type": "text", "x": 52, "y": 0, "width": 82, "height": 6.5, "text": "{{ row.product }}", "styleName": "td" }, { "id": "c4", "type": "text", "x": 134, "y": 0, "width": 52, "height": 6.5, "text": "{{ row.amount }}", "styleName": "td", "style": { "textAlign": "right" }, "format": { "type": "currency", "currency": "USD" } } ] }, "footer": { "id": "foot", "height": 10, "components": [ { "id": "f1", "type": "text", "x": 0, "y": 2, "width": 134, "height": 7, "text": "Grand total", "style": { "fontSize": 11, "fontWeight": "bold", "backgroundColor": "#1e293b", "color": "#fff", "padding": 1.5 } }, { "id": "f2", "type": "text", "x": 134, "y": 2, "width": 52, "height": 7, "text": "{{ sum(rows, \"amount\") }}", "style": { "fontSize": 11, "fontWeight": "bold", "backgroundColor": "#1e293b", "color": "#fff", "padding": 1.5, "textAlign": "right" }, "format": { "type": "currency", "currency": "USD" } } ] } } ]}{ "orders": [ { "id": 1001, "customer": "Contoso Ltd", "date": "2026-01-08", "product": "Cloud backup", "amount": 480 }, { "id": 1002, "customer": "Fabrikam", "date": "2026-01-11", "product": "USB-C dock", "amount": 129.9 }, { "id": 1003, "customer": "Contoso Ltd", "date": "2026-01-15", "product": "Support plan", "amount": 1200 }, { "id": 1004, "customer": "Northwind", "date": "2026-01-19", "product": "Monitor stand", "amount": 54.5 }, { "id": 1005, "customer": "Fabrikam", "date": "2026-02-02", "product": "Headset", "amount": 89 }, { "id": 1006, "customer": "Northwind", "date": "2026-02-05", "product": "Webcam HD", "amount": 75.25 } ]}