Charts
Categorical palette
Six hues telling the series of a chart apart. The one place in the library where colour does not encode a state — and therefore the one place with rules of its own.
- Series 1
- Series 2
- Series 3
- Series 4
- Series 5
- Series 6
Markup
<ul class="inst-legend">
<li class="inst-legend-item"><span class="inst-legend-swatch" style="--series:var(--chart-1)"></span>Series 1</li>
<li class="inst-legend-item"><span class="inst-legend-swatch" style="--series:var(--chart-2)"></span>Series 2</li>
<li class="inst-legend-item"><span class="inst-legend-swatch" style="--series:var(--chart-3)"></span>Series 3</li>
<li class="inst-legend-item"><span class="inst-legend-swatch" style="--series:var(--chart-4)"></span>Series 4</li>
<li class="inst-legend-item"><span class="inst-legend-swatch" style="--series:var(--chart-5)"></span>Series 5</li>
<li class="inst-legend-item"><span class="inst-legend-swatch" style="--series:var(--chart-6)"></span>Series 6</li>
</ul>
The first law asks that colour encode a state, a category or a polarity. A series of a chart is a category, so the palette does not break the law but refines it. The exception is named in the design principles, and along with it are named five rules that exist nowhere else in the library.
Contract
A series is assigned through --series, inline, as a channel of data: the
number of a series comes with the data and cannot be known to static CSS.
Markup
<svg class="inst-sparkline" viewBox="0 0 120 32" preserveAspectRatio="none" aria-hidden="true" style="--series:var(--chart-3)">
<path class="inst-sparkline-area" d="M0,20 24,14 48,18 72,9 96,12 120,5 120,32 0,32Z"/>
<path class="inst-sparkline-line" d="M0,20 24,14 48,18 72,9 96,12 120,5"/>
</svg>
| What | Required | Why |
|---|---|---|
A --series on the root of a series |
yes | Once on the <svg> or on the mark of a legend — every node of the series inherits it |
The hue through --series rather than directly |
yes | A component reads the receiver rather than the hue of a series: otherwise reassigning a series takes editing the markup of every node |
| A legend at two series and more | yes | With no key colour reports nothing |
A --series at one series |
no | The default is --accent-mark: one series is not a category, and there is nothing to tell apart |
The components that read --series:
| Component | The nodes |
|---|---|
| The legend | inst-legend-swatch |
| The sparkline | inst-sparkline-line · inst-sparkline-area · inst-sparkline-last |
| The avatar | The hue of a group: an avatar tells entities apart rather than reporting a state — the same work a series does |
The ring and the meter do not read --series:
they show one quantity and take --tone-mark.
/* A named series of the application — the number tied to a measure in one place */
.chart-render { --series: var(--chart-1); }
.chart-physics { --series: var(--chart-2); }
Such a tie is the cheapest way to keep the first rule: a measure gets its number once rather than in every template.
Accessibility
| Colour is not the only carrier | A series is always named in words in the legend. The palette tells series apart but reports nothing about them — the label does |
| Colour blindness | The differing lightness within the palette was chosen for this: when hue is lost the series stay distinguishable by brightness |
| Black-and-white print | The same mechanism. Six hues of one lightness would merge into one grey |
| Contrast | Every hue holds 3:1 against the surface in five themes: the line and the mark of a series are load-bearing signs |
| Labels | A categorical hue is not used for text: 3:1 is enough for a sign but not for a label. Text takes --text-primary or --tone-ink |
| Forced-colours mode | The categorical hues are reset by the system, and that is right: in such a mode it is the legend with its words that has to tell the series apart |
Variants
| Token | Hue | Lightness (light theme) | Lightness (dark) |
|---|---|---|---|
--chart-1 |
292 | 0.520 | 0.700 |
--chart-2 |
320 | 0.600 | 0.760 |
--chart-3 |
355 | 0.550 | 0.720 |
--chart-4 |
55 | 0.650 | 0.800 |
--chart-5 |
115 | 0.580 | 0.740 |
--chart-6 |
190 | 0.620 | 0.780 |
The values are written in OKLCH through light-dark(): the hue and the
saturation hold while the lightness turns over with the theme. The palette
stands at least 25° from the status hues: the corridors 25 (error), 85
(warning) and 150 (success) are reserved for good, and a series landing in
them reads as "it failed" or "done".
Series no. 3 stands at 355, that is 30° from the error hue at 25: that is
the closest approach to a status hue in the whole palette, and widening it
further is impossible not only for the sake of telling them apart but because
there are no free corridors left.
Against the accent there is no such guarantee and there can be none. The
accent is a knob with four positions: petrol
215, graphite 250, indigo 275, clay 45. Four positions cover too much
of the circle: clay stands 10° from series no. 4 (55). In place of an angular
guarantee what works here is a division of TERRITORY — the accent does not step
inside a chart, a categorical colour does not step outside. The one place they
meet on one screen is the legend of a multi-series chart beside the interface,
and what tells them apart there is lightness.
API
| Name | Value | Where from |
|---|---|---|
| variable | ||
--seriesThe receiver of the hue. A component of a series reads it rather than the hue directly | --accent-mark | the application sets it |
| token | ||
--chart-1The hue of series no. 1. The order is part of the contract: series no. 1 is always this hue | light-dark(oklch(0.520 0.150 292), oklch(0.700 0.130 292)) | one across every cell |
--chart-2The hue of series no. 2. The order is part of the contract: series no. 2 is always this hue | light-dark(oklch(0.600 0.150 320), oklch(0.760 0.120 320)) | one across every cell |
--chart-3The hue of series no. 3. The order is part of the contract: series no. 3 is always this hue | light-dark(oklch(0.550 0.140 355), oklch(0.720 0.120 355)) | one across every cell |
--chart-4The hue of series no. 4. The order is part of the contract: series no. 4 is always this hue | light-dark(oklch(0.650 0.120 55), oklch(0.800 0.110 55)) | one across every cell |
--chart-5The hue of series no. 5. The order is part of the contract: series no. 5 is always this hue | light-dark(oklch(0.580 0.130 115), oklch(0.740 0.120 115)) | one across every cell |
--chart-6The hue of series no. 6. The order is part of the contract: series no. 6 is always this hue | light-dark(oklch(0.620 0.110 190), oklch(0.780 0.100 190)) | one across every cell |
Why the kit works this way — the constitution · Open source under MIT, github.com/keshon/instrument