Elements

Charts

Reactive SVG charts, from simple sparklines to live dashboards.


Setup

Chart styles are included in Manifest CSS or a standalone stylesheet, both referencing theme variables.

Chart functionality is included in manifest.js with all core plugins, or it can be selectively loaded.

<!-- Manifest CSS -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/mnfst@latest/lib/manifest.min.css" />

<!-- Manifest JS -->
<script src="https://cdn.jsdelivr.net/npm/mnfst@latest/lib/manifest.min.js"></script>

Chart Types

Declare a chart by passing a config object to x-chart on any container. The type selects the renderer; everything else is shared.

Line


<div x-chart="{
    type: 'line',
    labels: ['Jan','Feb','Mar','Apr','May','Jun'],
    series: [{ name: 'Visitors', data: [30, 80, 45, 90, 60, 120] }],
    height: 200
}"></div>

Lines interpolate smoothly by default. Set curve to change it: 'linear' for straight segments, 'step' for tiers, 'natural' for splines.


<div x-chart="{ type: 'line', curve: 'step', ... }"></div>

Area

Area charts are line charts with a translucent fill. Add stacked: true to pile series on top of each other.


<div x-chart="{
    type: 'area',
    stacked: true,
    labels: ['Q1','Q2','Q3','Q4'],
    series: [
        { name: 'Organic', data: [40, 55, 60, 80] },
        { name: 'Paid', data: [20, 30, 25, 45] }
    ],
    height: 200
}"></div>

Bar

Multiple series render as grouped bars; stacked: true stacks them instead.


<div x-chart="{
    type: 'bar',
    stacked: true,
    dataLabels: true,
    labels: ['Jan','Feb','Mar'],
    series: [
        { name: 'Revenue', data: [120, 200, 150] },
        { name: 'Costs', data: [80, 110, 95] }
    ],
    height: 200
}"></div>

Pie & Donut

Pie charts take a single series whose values become slices, colored from the theme palette in order. type: 'donut' hollows the center. Data also accepts [{ label, value }] rows.


<div x-chart="{
    type: 'donut',
    labels: ['Mobile','Desktop','Tablet'],
    series: [{ data: [55, 35, 10] }],
    height: 220
}"></div>

Scatter


<div x-chart="{
    type: 'scatter',
    labels: [1,2,3,4,5,6,7,8],
    series: [{ name: 'Sample', data: [12, 30, 18, 44, 25, 50, 33, 60] }],
    height: 200
}"></div>

Combo

type: 'combo' mixes renderers in one plot — give each series its own type (bar, line, or area). Series without one default to bars.


<div x-chart="{
    type: 'combo',
    labels: ['Jan','Feb','Mar','Apr','May','Jun'],
    series: [
        { name: 'Revenue', type: 'bar', data: [120, 200, 150, 280, 230, 320] },
        { name: 'Margin %', type: 'line', data: [20, 35, 28, 44, 40, 52] }
    ],
    height: 200
}"></div>

Candlestick

type: 'candlestick' (or 'ohlc') plots open–high–low–close rows, as { o, h, l, c } objects (long names open/high/low/close also work) or [o, h, l, c] arrays. Rising candles use the theme's positive color, falling ones the negative.


<div x-chart="{
    type: 'candlestick',
    labels: ['Mon','Tue','Wed','Thu','Fri'],
    series: [{ data: [
        { o: 30, h: 38, l: 28, c: 36 },
        { o: 36, h: 40, l: 33, c: 34 },
        { o: 34, h: 35, l: 26, c: 28 },
        { o: 28, h: 33, l: 27, c: 32 },
        { o: 32, h: 44, l: 31, c: 42 }
    ] }],
    height: 200
}"></div>

Gauge

type: 'gauge' plots a single value on a 180° dial — a compact KPI readout. Give it a value (or a one-number series/data); unit suffixes the centered readout.


<div x-chart="{ type: 'gauge', value: 73, unit: '%', height: 180 }"></div>

The dial runs 0–100 by default. Set min: <n> and max: <n> to rescale it — the end labels and the value fill follow the new range.


<div x-chart="{ type: 'gauge', value: 6.2, min: 0, max: 10, height: 180 }"></div>

Pass zones to paint threshold bands behind the value arc — an array of { to, color } segments, each running from the previous to (or min) up to its own. Useful for good/warning/critical ranges.


<div x-chart="{
    type: 'gauge',
    value: 88,
    unit: '%',
    zones: [
        { to: 50, color: 'var(--color-negative-content)' },
        { to: 80, color: 'var(--color-brand-content)' },
        { to: 100, color: 'var(--color-positive-content)' }
    ],
    height: 180
}"></div>

Heatmap

type: 'heatmap' renders a matrix of tiles, where each series is a row, each datum a column aligned to labels, and the series name is the row label. Tile color interpolates across the value range using the heatmap ramp.


<div x-chart="{
    type: 'heatmap',
    labels: ['12a','4a','8a','12p','4p','8p'],
    series: [
        { name: 'Mon', data: [2, 1, 8, 14, 18, 9] },
        { name: 'Tue', data: [3, 1, 10, 16, 20, 11] },
        { name: 'Wed', data: [1, 0, 7, 13, 22, 14] }
    ],
    height: 200
}"></div>

Set dataLabels: true to print each value in its tile, and gap to change the tile gutter (px, default 1; use 0 for a seamless field).


<div x-chart="{ type: 'heatmap', dataLabels: true, gap: 0, ... }"></div>

Gantt

type: 'gantt' (alias timeline) lays out horizontal segments along a shared axis. Each series is a track (row); its data is an array of { from, to, status?, color? } segments. A single track is a status strip; multiple tracks are swimlanes — a Gantt chart. Height derives from the track count, so there's no height to set.


<div x-chart="{
    type: 'gantt',
    colors: { Online: 'var(--color-positive-content)', Away: 'var(--color-brand-content)', Offline: 'var(--color-surface-3)' },
    series: [
        { name: 'Bo-Chen', data: [
            { from: '2026-06-25T00:00', to: '2026-06-25T17:00', status: 'Online' },
            { from: '2026-06-25T17:00', to: '2026-06-25T21:00', status: 'Offline' } ] },
        { name: 'Ele', data: [
            { from: '2026-06-25T00:00', to: '2026-06-25T13:00', status: 'Offline' },
            { from: '2026-06-25T13:00', to: '2026-06-25T17:20', status: 'Online' },
            { from: '2026-06-25T17:20', to: '2026-06-25T21:00', status: 'Offline' } ] }
    ]
}"></div>

A segment with no to is a point (a milestone or event), drawn as a marker tick. markers: [{ at, label }] adds vertical reference lines (e.g. a "now" line), dataLabels: true prints each segment's label inside its bar, and rowHeight (px) sets the lane height.

Axis

The axis adapts to the segment values — no type-of-axis flag needed:

from / to values Axis
Numbers (0, 26.2) Numeric — a linear scale. unit suffixes the ticks (' mi', '%'); min/max pin the range.
Dates / ISO strings Time — a time scale, ticks formatted to the locale and span. min/max pin the window.
Other strings ('Plan', 'GA') Category — an ordinal scale over labels (or the distinct values); segments span between named stages.
<div x-chart="{ type: 'gantt', unit: ' mi', min: 0, max: 26.2,
    series: [{ name: 'Pace', data: [
        { from: 0, to: 8, status: 'Easy' },
        { from: 8, to: 18, status: 'Tempo' },
        { from: 18, to: 26.2, status: 'Hard' } ] }]
}"></div>

Coloring

Every chart resolves a segment's (or series', or slice's) color through the same cascade — the default is the theme palette, and an author override is any CSS color value:

  1. A per-segment color (or per-series color) — wins outright.
  2. For Gantt charts, the config colors map (status → color) — so the same status is one color across every track, with one legend entry.
  3. Otherwise the --color-chart-N palette, assigned in first-seen order.

Whatever you supply for color is passed straight to the SVG fill, so any CSS <color> or var() works — a fixed value, a chart palette variable, a theme token, or your own custom property:

<div x-chart="{ type: 'gantt', series: [{ name: 'Build', data: [
    { from: 0, to: 3, status: 'Spec',   color: '#f59e0b' },                  // fixed hex
    { from: 3, to: 6, status: 'Code',   color: 'var(--color-chart-3)' },     // chart palette var
    { from: 6, to: 8, status: 'Review', color: 'var(--color-brand-content)' }, // theme token
    { from: 8, to: 9, status: 'Ship',   color: 'var(--my-brand)' }           // custom property
] }] }"></div>

Leave color and colors unset and the palette colors the tracks for you.

Map

type: 'map' draws a world choropleth — countries shaded by value — plus optional location bubbles. Geometry and reference data (country shapes, ISO codes, continent membership) lazy-load from a CDN the first time a map scrolls into view.


<div x-chart="{
    type: 'map',
    data: { US: 320, CN: 410, IN: 380, BR: 140, 'United Kingdom': 240, DE: 260, ZA: 60 }
}"></div>

Keying

Each entry maps a region to a value — pass an object ({ region: value }) or an array of [region, value] / { id, value } rows. A region key resolves, in order, as:

Key form Example
ISO alpha-2 'US', 'JP'
ISO alpha-3 'USA', 'JPN'
ISO numeric 840, 392
Country name 'United Kingdom', 'Brazil'
Continent 'Europe', 'North America' — fills every member country

A continent key expands to all its countries, so you can shade whole regions in one line.

Sequential & categorical

The scale follows the value type. Numeric values shade along the heatmap ramp (low → high) with a gradient legend; string values are treated as categories, each taking a palette color with a swatch legend. Regions with no data fall back to a neutral surface fill.

Categorical — by continent
<div x-chart="{
    type: 'map',
    data: { Europe: 'EMEA', Africa: 'EMEA', Asia: 'APAC', Oceania: 'APAC',
        'North America': 'Americas', 'South America': 'Americas' }
}"></div>

Points

points overlays location bubbles, each { lat, lng, value?, label?, color? }. Numeric values scale the radius (a proportional-symbol map); without them each point is a fixed dot. Points layer over a choropleth or stand alone on a blank map.

To reference a place by name instead of coordinates, supply a places gazetteer ({ name: [lat, lng] }) — a point's name then resolves its position from it (a place can be anything — a city, office, venue, sensor). Unlike country names, there's no place dataset to lazy-load (comprehensive ones run to tens of MB), so you define just the handful you plot. The name doubles as the point's label unless you set label.


<div x-chart="{
    type: 'map',
    places: { Tokyo: [35.69, 139.69], London: [51.5, -0.13], 'New York': [40.71, -74.01] },
    points: [
        { name: 'Tokyo', value: 95 },
        { name: 'London', value: 71 },
        { name: 'New York', value: 84 }
    ]
}"></div>

Because the whole config is a live Alpine expression, any field — including name — accepts a $x binding ({ name: $x.site.label, value: $x.site.users }), so points can come straight from data. Point names are author text and aren't auto-translated (only region labels are); to localize them, bind to a locale-aware source.

Localization

Geographic labels translate automatically to ~30 languages, following the current locale. The translations are lazy-loaded on demand from the i18n-iso-countries dataset (via jsDelivr) — only the active locale is fetched, and only when a map is shown. English is the default.

Override or extend any name through the _ui.map block, exactly like the color and date pickers:

// in a locale data source
_ui: {
    map: {
        regions: { US: 'É.-U.', GB: 'R.-U.' },   // per-region name overrides
        cities:  { Paris: [48.85, 2.35] }        // place gazetteer
    }
}

_ui.map.regions keys by ISO code, numeric id, or English name; _ui.map.cities is an alternative to the inline places map.


Configuration

Key Type Description
type string line, area, bar, pie, donut, scatter, combo, candlestick, gauge, heatmap, gantt (alias timeline), map. Default line.
labels array X-axis categories, slice labels, heatmap columns, or a Gantt's category-axis stages.
series array { name, data, color?, type? } per series. color overrides the palette; type applies in combo charts; in a heatmap each series is a row; in a Gantt each series is a track and its data is { from, to, status?, color? } segments.
data array | object Shorthand for a single unnamed series; for a map, the region values as { region: value } or [region, value] rows.
height number Chart height in pixels. Default 240. Width tracks the container.
stacked boolean Stack bar/area series. Default false.
curve string Line interpolation — monotone (default), linear, step, natural.
legend boolean Show the legend (multi-series, pies, heatmap ramp). Default true.
axis / grid boolean Show axis labels / grid lines (heatmap: row & column labels). Default true.
tooltip boolean Hover tooltips on segments. Default true.
dataLabels boolean Static value labels on points, bars, slices, and heatmap tiles. Default false.
title string Accessible chart label (aria-label on the SVG).
value number Gauge — the plotted value (or use a one-number series/data).
min / max number | string Gauge range (default 0 / 100); or a Gantt's fixed axis window (numeric or time).
unit string Suffix on the gauge readout or a Gantt's numeric ticks (e.g. '%', ' mi').
zones array Gauge — threshold bands: { to, color } segments behind the value arc.
gap number Heatmap — tile gutter in px. Default 1; 0 for a seamless field.
rowHeight number Gantt — lane height in px. Default 28.
colors object Gantt — a status → color map, shared across tracks.
markers array Gantt — vertical reference lines: { at, label?, color? }.
points array Map — location bubbles: { lat, lng, value?, label?, color? } or a name resolved via places.
places object Map — name[lat, lng] gazetteer for positioning named points.

Declarative Series

For content-first authoring, set the type as a modifier and declare each series as a <data> child — handy when values come from local data bindings.

<figure x-chart.bar labels="['Q1','Q2','Q3','Q4']">
    <data series="Revenue" :values="$x.finance.revenue"></data>
    <data series="Costs" :values="$x.finance.costs" color="#f43f5e"></data>
</figure>

Dynamic Data

The config is a live Alpine expression: charts re-render whenever the data behind them changes. Bind series to local data, cloud data, or any reactive state — dashboards assemble themselves from whatever $x holds.

<!-- Re-draws whenever $x.sales loads or updates -->
<div x-chart="{
    type: 'bar',
    labels: $x.sales.labels,
    series: $x.sales.series,
    height: 240
}"></div>

Charts also re-render automatically on container resize and on locale switch (axis numbers use the active locale's compact notation).

Magic Method

Use $chart(id) to read or update a chart programmatically.

Member Description
$chart(id).type The active chart type.
$chart(id).series The normalized series array.
$chart(id).update(cfg) Replace the config and redraw.
$chart(id).redraw() Force a redraw with the current config.
<div id="sales" x-chart="{ type: 'bar', ... }"></div>
<button @click="$chart('sales').update({ type: 'line', labels: [...], series: [...] })">
    As line
</button>

Styles

Theme

Series cycle through the --color-chart-N color variable palette in order. By default there are nine colors, segment 10 wraps back to --color-chart-1. The palette size is read from CSS; define --color-chart-10, --color-chart-11, … in your theme (globally or scoped to one chart) and the cycle extends automatically.

Variable Purpose
--color-chart-1--color-chart-8 Series palette, applied in order.
--color-chart-grid Grid line color.
--color-chart-label Axis label color.
--color-positive-content / --color-negative-content Rising / falling candlesticks.
--color-chart-heat-low / --color-chart-heat-high Heatmap ramp endpoints (low → high value).
/* Extend the palette past 9 */
:root {
    --color-chart-10: oklch(60% 0.13 250);
    --color-chart-11: oklch(65% 0.15 320);
}

/* Rebrand one chart's palette */
#sales {
    --color-chart-1: var(--color-brand-content);
}

Per-series overrides also work inline through the config (series: [{ color: '#f43f5e', ... }]), which beats the palette.

The heatmap ramp is a single-hue sequential scale derived from --color-chart-1 by default. Override the two endpoints to recolor it — the tiles interpolate between them with color-mix:

:root {
    --color-chart-heat-low: var(--color-surface-2);
    --color-chart-heat-high: var(--color-brand-content);
}

Tailwind CSS

Charts size to their container, so layout is ordinary utility work.

<div class="grid md:grid-cols-3 gap-8">
    <div x-chart="{ type: 'line', ... }"></div>
    <div x-chart="{ type: 'bar', ... }"></div>
    <div x-chart="{ type: 'donut', ... }"></div>
</div>

Customization

Modify base styles with custom CSS targeting [x-chart] (or the .chart marker class the plugin stamps on every chart, which also covers modified directives like x-chart.bar). The SVG output uses plain element selectors:

Selector Part
svg line Grid lines (and candle wicks within their group)
svg text Axis labels
path.line Line series strokes
path.area Area fills
path.slice Pie / donut slices
rect Bars and candle bodies
rect.heat-cell Heatmap tiles (fill driven by the --heat percentage)
rect.gantt-segment / rect.gantt-point Gantt spans and point markers
path.map-region Map countries (fill driven by --color-chart-color)
circle.map-point Map location bubbles
circle Line points and scatter dots (.scatter)
g.positive / g.negative Rising / falling candles
path.gauge-track / path.gauge-value Gauge track and value arc
line.gantt-marker / text.gantt-track Gantt reference lines and track labels
text.value Data labels (.inverse when on a filled segment)
text.gauge-label Gauge centered readout
footer Legend — <span> items with <i> swatches (heatmap: footer.heat-legend gradient bar)
.tooltip Cursor tooltip — chrome shared with tooltips

Each series element carries its resolved color as --color-chart-color, so one rule can restyle any series-colored part.

/* Thicker lines and bigger points */
.chart path.line {
    stroke-width: 3;
}

.chart circle {
    r: 4;
}

/* Square legend swatches into dots */
.chart footer i {
    border-radius: 50%;
}