Skip to content

Theming & Styles

Style customization has four layers, from shallow to deep: CSS variables → classNames → slots → unstyled.

Every meter-* rule produced by the Tailwind plugin resolves --meter-* CSS variables first (falling back to the host Tailwind palette), so switching themes is just a variable override. The core package exports a full set of theme utilities:

import {
darkTheme,
lightTheme,
resolveTheme,
themeCssVariables,
themeStyles,
type MeterTheme,
type MeterThemeInput,
} from '@meterry/component'
  • themeCssVariables('dark') returns CSS text like :host, :root { --meter-surface: ...; };
  • themeStyles(theme) returns a complete <style> tag string;
  • MeterThemeInput can be 'light' | 'dark' or a partial override (colors / radius / chartColors / fontFamily / shadow / css).

Apply the dark theme to a local container:

import { themeCssVariables } from '@meterry/component'
const scopeVars = themeCssVariables('dark').replace(':host, :root', '.dark-scope')
<div className="dark-scope">
<style dangerouslySetInnerHTML={{ __html: scopeVars }} />
<MeterChart theme="dark" ... />
</div>

Also pass theme="dark" to the chart component so the ECharts palette follows.

Available CSS variables: --meter-surface, --meter-surface-muted, --meter-text, --meter-text-muted, --meter-text-subtle, --meter-border, --meter-border-strong, --meter-accent, --meter-accent-soft, --meter-focus, --meter-danger, --meter-danger-soft, --meter-radius-sm|md|lg, --meter-shadow, --meter-font-family, --meter-chart-1..n.

All components support className (root node) and classNames (stable slots). Available slot names:

root, card, header, caption, title, description, body, toolbar, filter, table, empty, loading, error, pagination, actions, button, chart, legend, legendItem, value, unit

// rows: fetched from the meter service (see Getting Started)
<MeterTable
className="w-full"
classNames={{
card: 'rounded-2xl border-slate-200 shadow-xl',
header: 'bg-slate-50 px-6 py-5',
title: 'text-slate-950',
table: 'overflow-hidden rounded-xl',
button: 'rounded-lg border-indigo-200 text-indigo-700',
}}
columns={columns}
data={rows}
/>

Tailwind classes passed via classNames coexist with the default semantic classes (defaults first, custom last), and conflicting properties resolve in favor of the custom ones.

Making Tailwind scan the meter-* class names

Section titled “Making Tailwind scan the meter-* class names”

The meter-* class names live as strings in the core package source, so Tailwind needs to scan them. With Tailwind v4, add in CSS:

/* when consuming the published package */
@source "../node_modules/@meterry/component/dist/index.js";

(Adjust the path relative to your page’s CSS file; the examples in the component library repository use a Vite alias and scan src/component.ts directly.)

3. slots: deep customization via render props

Section titled “3. slots: deep customization via render props”

The React/Vue MeterUsage, MeterChart, and MeterTable all support slots render props to replace the rendering of the header / value / chart / toolbar / cell / pagination areas while keeping the component’s data fetching and state management:

<MeterUsage
client={client}
slots={{
value: ({ total, formattedTotal, measure }) => (
<div className="my-value">{formattedTotal} {measure}</div>
),
header: ({ caption, title }) => <MyHeader caption={caption} title={title} />,
chart: ({ model, option }) => <MyChart option={option} />,
}}
/>

Context types for each slot: MeterHeaderSlotContext, MeterValueSlotContext, MeterChartSlotContext, MeterTableToolbarSlotContext, MeterTableCellSlotContext, MeterPaginationSlotContext (all exported from the core package).

// rows: fetched from the meter service (see Getting Started)
<MeterChart
unstyled
classNames={{
root: 'block',
card: 'rounded-xl bg-white p-6 shadow',
chart: 'mt-4',
}}
type="line"
data={rows}
x="bucket"
series={[{ key: 'tokens', label: 'Tokens', value: 'tokens' }]}
/>

unstyled drops all default meter-* classes and keeps only the classes you provide.

React renders charts with echarts-for-react and Vue 3 with vue-echarts:

  • classNames.chart still controls the chart container’s size, spacing, and look via Tailwind
  • component APIs like type, series, and theme stay unchanged

@meterry/component-tailwindcss does two things:

  1. registers the meter palette (meter.background, meter.accent, …);
  2. generates all meter-* component styles (card, table, filters, pagination, chart legend, etc.), with every rule resolving colors and radii through --meter-* variables for runtime reskinning.

The color fallback chain: var(--meter-accent, indigo.600) — the runtime CSS variable wins, then it falls back to the host Tailwind theme, so the plugin automatically blends into the host’s brand colors.