CHART: two tiers, chosen by what the data does (DR-2026-14)
TIER 1: .tw-chart-bars — CSS comparison bars, no dependency
TIER 2: data-controller="chart" — Chart.js, lazy-loaded, the only sanctioned path

Compare a handful of labelled magnitudes → Tier 1. Change over time, multiple series, axes → Tier 2. One headline number → a Stat Tile, not a chart. A share of a whole → the usage donut. Series colours are computed, not chosen — see the palette section below.

Tier 1 — CSS comparison bars

Bars: pure CSS, no JavaScript. Width comes from tw:[--chart-value:64%] — an arbitrary-property utility, never an inline style. Values use tabular figures so digits line up down the column. Consolidates the bar treatments hand-rolled across 17 prototype files.

Where enquiries come from

Single series — no legend needed, the title names it.

Open days 76%
School website 52%
Word of mouth 38%
Local authority 24%
Other 11%

Applications by stage

Multiple series — the legend is mandatory from two.

Year 7 164
Year 12 122
In-year 68
Nursery 38
Year 7 Year 12 In-year Nursery
<div class="tw-chart-bar"><span class="tw-chart-bar-label">Open days</span><span class="tw-chart-bar-track"><span class="tw-chart-bar-fill tw-chart-series-1 tw:[--chart-value:76%]"></span></span><span class="tw-chart-bar-value">76%</span></div>

Sizes and stacked labels

Small

Submitted70%
In review45%

Large

Submitted70%
In review45%

Stacked labels — long names, narrow card

St Mary's Catholic Primary32
Brookfield Community School21

Tier 2 — the chart controller (Chart.js)

Plotted data: change over time, multiple series, axes. The only sanctioned path to Chart.js — no prototype imports it directly. Lazy-loaded on first reveal, so screens without charts pay nothing; colours resolve from --tw-color-* at runtime, so a token change repaints every chart. Omit color on a dataset and the ruled order is applied automatically, and the legend turns itself on from two series.

Applications received

Two series, smoothed, no explicit colours.

Offers by stage

Stacked bars, y axis shown.

<div data-controller="chart" data-chart-type-value="line" data-chart-labels-value='["Sep","Oct"]' data-chart-datasets-value='[{"label":"2026/27","data":[12,19]}]'><canvas data-chart-target="canvas" role="img" aria-label="…"></canvas></div>

Series palette — computed, not chosen

The obvious approach — brand tokens at their -500 steps in token order — fails: orange-500green-500 separate by only ΔE 4.0 for deutan viewers, and both fall below 3:1 contrast. Two consequences: the brand navy primary-500 is unusable as a series colour (too dark), and the series green is green-700, never the base lime.

blue-600
orange-700
magenta-700
green-700
primary-400
other

Colour alone carries identity to two series. blue-600 + orange-700 measure ΔE 33.3 apart. From three, the legend and direct labels are the identity channel — not an accessibility garnish. Assign in fixed order, never cycled; beyond five, fold the tail into .tw-chart-series-other or use small multiples. A sixth hue is never generated.