<!-- Generated by tools/docs.php from docs/src/chart.html - edit the source, not this file. -->

# Charts

A chart is the table of its numbers, drawn: columns stacked by day, a sparkline of the last runs, a board of lanes over the next hours. The numbers stay in the page as text, so a screen reader reads the table; a small script only hands them to the stylesheet.

## A package of the release

Charts are linked apart, on the screens that show them, beside the two files: `css/redz-ui-charts.min.css` draws, and `js/redz-ui-charts.min.js` hands it the numbers - every `data-v`, `data-at`, `data-from`, `data-to`, `data-median` and `data-now` becomes a custom property through the CSSOM, which a strict CSP allows where it would refuse a `style` attribute. A number is a share, 0 to 1: a value over the largest, a time over the window.

```html
<link rel="stylesheet" href="redz-ui/css/redz-ui-charts.min.css">
<script type="module" src="redz-ui/js/redz-ui-charts.min.js"></script>
```

## Columns by day

`table class="chart"`: its `caption` is the title, the header cells after the first are the series and draw as the legend, and each row is a day - `th scope="row"` its label, each `td` a segment whose `data-v` is its share of the tallest day, stacked from the first up. The series take eight colors in order, `--redz-chart-1` to `-8`, chosen to stay apart for color-blind eyes in both themes; `data-tone` gives a segment a state's color instead. A row's `title` is its tooltip, a row with `data-empty` a stub on the axis, and a label wrapped in `visually-hidden` is read, not shown - label every few days when there are many. `--redz-chart-height` sets the height, 10rem by default; ninety days fit a card 700 px wide.

```html
<table class="chart">
	<caption>Messages sent per day</caption>
	<thead><tr><th scope="col">Day</th><th scope="col" data-tone="success">Free</th><th scope="col" data-tone="warning">Paid</th><th scope="col" data-tone="danger">Failed</th></tr></thead>
	<tbody>
		<tr title="Nov 1: 120 free, 30 paid, 4 failed"><th scope="row">Nov 1</th><td data-tone="success" data-v="0.615">120</td><td data-tone="warning" data-v="0.154">30</td><td data-tone="danger" data-v="0.021">4</td></tr>
		<tr title="Nov 2: 132 free, 28 paid, 2 failed"><th scope="row"><span class="visually-hidden">Nov 2</span></th><td data-tone="success" data-v="0.677">132</td><td data-tone="warning" data-v="0.144">28</td><td data-tone="danger" data-v="0.01">2</td></tr>
		<tr title="Nov 3: 98 free, 41 paid, 6 failed"><th scope="row"><span class="visually-hidden">Nov 3</span></th><td data-tone="success" data-v="0.503">98</td><td data-tone="warning" data-v="0.21">41</td><td data-tone="danger" data-v="0.031">6</td></tr>
		<tr data-empty title="Nov 4: none"><th scope="row">Nov 4</th><td>0</td><td>0</td><td>0</td></tr>
		<tr title="Nov 5: 144 free, 35 paid, 3 failed"><th scope="row"><span class="visually-hidden">Nov 5</span></th><td data-tone="success" data-v="0.738">144</td><td data-tone="warning" data-v="0.179">35</td><td data-tone="danger" data-v="0.015">3</td></tr>
		<tr title="Nov 6: 160 free, 22 paid, 1 failed"><th scope="row"><span class="visually-hidden">Nov 6</span></th><td data-tone="success" data-v="0.821">160</td><td data-tone="warning" data-v="0.113">22</td><td data-tone="danger" data-v="0.005">1</td></tr>
		<tr title="Nov 7: 110 free, 30 paid, 9 failed"><th scope="row">Nov 7</th><td data-tone="success" data-v="0.564">110</td><td data-tone="warning" data-v="0.154">30</td><td data-tone="danger" data-v="0.046">9</td></tr>
		<tr title="Nov 8: 90 free, 25 paid, 2 failed"><th scope="row"><span class="visually-hidden">Nov 8</span></th><td data-tone="success" data-v="0.462">90</td><td data-tone="warning" data-v="0.128">25</td><td data-tone="danger" data-v="0.01">2</td></tr>
		<tr data-empty title="Nov 9: none"><th scope="row"><span class="visually-hidden">Nov 9</span></th><td>0</td><td>0</td><td>0</td></tr>
		<tr title="Nov 10: 150 free, 40 paid, 5 failed"><th scope="row">Nov 10</th><td data-tone="success" data-v="0.769">150</td><td data-tone="warning" data-v="0.205">40</td><td data-tone="danger" data-v="0.026">5</td></tr>
		<tr title="Nov 11: 170 free, 12 paid, 0 failed"><th scope="row"><span class="visually-hidden">Nov 11</span></th><td data-tone="success" data-v="0.872">170</td><td data-tone="warning" data-v="0.062">12</td><td data-tone="danger" data-v="0">0</td></tr>
		<tr title="Nov 12: 125 free, 33 paid, 4 failed"><th scope="row"><span class="visually-hidden">Nov 12</span></th><td data-tone="success" data-v="0.641">125</td><td data-tone="warning" data-v="0.169">33</td><td data-tone="danger" data-v="0.021">4</td></tr>
		<tr title="Nov 13: 140 free, 29 paid, 3 failed"><th scope="row">Nov 13</th><td data-tone="success" data-v="0.718">140</td><td data-tone="warning" data-v="0.149">29</td><td data-tone="danger" data-v="0.015">3</td></tr>
		<tr title="Nov 14: 155 free, 31 paid, 2 failed"><th scope="row"><span class="visually-hidden">Nov 14</span></th><td data-tone="success" data-v="0.795">155</td><td data-tone="warning" data-v="0.159">31</td><td data-tone="danger" data-v="0.01">2</td></tr>
	</tbody>
</table>
```

Series without a tone take the chart colors by their column.

```html
<table class="chart">
	<caption>Bookings by channel, this week</caption>
	<thead><tr><th scope="col">Day</th><th scope="col">Site</th><th scope="col">Partners</th><th scope="col">Walk-in</th></tr></thead>
	<tbody>
		<tr title="Mon: 12 site, 5 partners, 3 walk-in"><th scope="row">Mon</th><td data-v="0.25">12</td><td data-v="0.104">5</td><td data-v="0.062">3</td></tr>
		<tr title="Tue: 15 site, 6 partners, 2 walk-in"><th scope="row">Tue</th><td data-v="0.312">15</td><td data-v="0.125">6</td><td data-v="0.042">2</td></tr>
		<tr title="Wed: 9 site, 8 partners, 4 walk-in"><th scope="row">Wed</th><td data-v="0.188">9</td><td data-v="0.167">8</td><td data-v="0.083">4</td></tr>
		<tr title="Thu: 18 site, 4 partners, 1 walk-in"><th scope="row">Thu</th><td data-v="0.375">18</td><td data-v="0.083">4</td><td data-v="0.021">1</td></tr>
		<tr title="Fri: 22 site, 9 partners, 5 walk-in"><th scope="row">Fri</th><td data-v="0.458">22</td><td data-v="0.188">9</td><td data-v="0.104">5</td></tr>
		<tr title="Sat: 30 site, 12 partners, 6 walk-in"><th scope="row">Sat</th><td data-v="0.625">30</td><td data-v="0.25">12</td><td data-v="0.125">6</td></tr>
		<tr title="Sun: 26 site, 10 partners, 7 walk-in"><th scope="row">Sun</th><td data-v="0.542">26</td><td data-v="0.208">10</td><td data-v="0.146">7</td></tr>
	</tbody>
</table>
```

`chart-values` writes each column's number above it - for one series and a few columns, where the numbers are what a person reads.

```html
<table class="chart chart-values">
	<caption>Guests per month</caption>
	<thead><tr><th scope="col">Month</th><th scope="col">Guests</th></tr></thead>
	<tbody>
		<tr><th scope="row">Jul</th><td data-v="0.55">220</td></tr>
		<tr><th scope="row">Aug</th><td data-v="0.7">280</td></tr>
		<tr><th scope="row">Sep</th><td data-v="0.45">180</td></tr>
		<tr><th scope="row">Oct</th><td data-v="0.6">240</td></tr>
		<tr><th scope="row">Nov</th><td data-v="0.85">340</td></tr>
		<tr><th scope="row">Dec</th><td data-v="1">400</td></tr>
	</tbody>
</table>
```

## Shares of a whole

`chart-bar` with `chart` draws one row as a single bar across: each segment's `data-v` is its share of the whole, and the legend under it names the series - write the share in the legend's words too. The bar is 12 px thick; `--redz-chart-bar-height` sets another.

```html
<table class="chart chart-bar">
	<caption>Payments in November</caption>
	<thead><tr><th scope="col">Method</th><th scope="col">Card, 58%</th><th scope="col">PromptPay, 31%</th><th scope="col">Cash, 11%</th></tr></thead>
	<tbody>
		<tr><th scope="row">November</th><td data-v="0.58">58%</td><td data-v="0.31">31%</td><td data-v="0.11">11%</td></tr>
	</tbody>
</table>
```

`table class="chart-rows"` breaks a whole into rows, longest first: each `tr` a label, its count and its share in words, and a bar under them whose length is the row's `data-v`. The header cells name the columns for a screen reader and are hidden from sight. The bars take the first chart color; a row's `data-tone` gives it a state's.

```html
<table class="chart-rows">
	<caption>Where bookings came from</caption>
	<thead><tr><th scope="col">Source</th><th scope="col">Bookings</th><th scope="col">Share</th></tr></thead>
	<tbody>
		<tr data-v="0.52"><th scope="row">Search</th><td>182</td><td>52%</td></tr>
		<tr data-v="0.27"><th scope="row">Partners</th><td>94</td><td>27%</td></tr>
		<tr data-v="0.14"><th scope="row">Social networks</th><td>49</td><td>14%</td></tr>
		<tr data-v="0.07" data-tone="secondary"><th scope="row">Other</th><td>25</td><td>7%</td></tr>
	</tbody>
</table>
```

## Sparklines

`span class="spark"` with `role="img"` and its summary in `aria-label` draws one bar per run, oldest first: `data-v` its share of the longest, `data-state` - `ok`, `running`, `timeout`, `failed` - its color, `title` its tooltip. 18 px high in a row, `spark-md` 34 px, `spark-lg` 64 px; `spark-median` with `data-median` draws the median as a dashed line.

```html
<table class="table" aria-label="Agents">
	<thead><tr><th scope="col">Agent</th><th scope="col">Last 20 runs</th></tr></thead>
	<tbody>
		<tr><td class="font-monospace">backup.nightly</td><td><span class="spark" role="img" aria-label="20 runs: 18 OK, 1 timeout, 1 failed"><i data-state="ok" data-v="0.31" title="OK · 1.2 s"></i><i data-state="ok" data-v="0.28" title="OK · 1.1 s"></i><i data-state="ok" data-v="0.36" title="OK · 1.4 s"></i><i data-state="ok" data-v="0.33" title="OK · 1.3 s"></i><i data-state="timeout" data-v="1" title="Timeout · 3.9 s"></i><i data-state="ok" data-v="0.31" title="OK · 1.2 s"></i><i data-state="ok" data-v="0.26" title="OK · 1 s"></i><i data-state="ok" data-v="0.38" title="OK · 1.5 s"></i><i data-state="ok" data-v="0.31" title="OK · 1.2 s"></i><i data-state="failed" data-v="0.72" title="Failed · 2.8 s"></i><i data-state="ok" data-v="0.33" title="OK · 1.3 s"></i><i data-state="ok" data-v="0.28" title="OK · 1.1 s"></i><i data-state="ok" data-v="0.31" title="OK · 1.2 s"></i><i data-state="ok" data-v="0.41" title="OK · 1.6 s"></i><i data-state="ok" data-v="0.31" title="OK · 1.2 s"></i><i data-state="ok" data-v="0.33" title="OK · 1.3 s"></i><i data-state="ok" data-v="0.28" title="OK · 1.1 s"></i><i data-state="ok" data-v="0.36" title="OK · 1.4 s"></i><i data-state="ok" data-v="0.31" title="OK · 1.2 s"></i><i data-state="running" data-v="0.15" title="Running · 0.6 s"></i></span></td></tr>
	</tbody>
</table>
<figure class="vstack gap-4">
	<span class="spark spark-lg spark-median" role="img" aria-label="20 runs, median 1.2 s, longest 3.9 s" data-median="0.31"><i data-state="ok" data-v="0.31" title="Nov 3, 02:00 · 1.2 s"></i><i data-state="ok" data-v="0.28" title="Nov 3, 03:00 · 1.1 s"></i><i data-state="ok" data-v="0.36" title="Nov 3, 04:00 · 1.4 s"></i><i data-state="ok" data-v="0.33" title="Nov 3, 05:00 · 1.3 s"></i><i data-state="timeout" data-v="1" title="Nov 3, 06:00 · 3.9 s"></i><i data-state="ok" data-v="0.31" title="Nov 3, 07:00 · 1.2 s"></i><i data-state="ok" data-v="0.26" title="Nov 3, 08:00 · 1 s"></i><i data-state="ok" data-v="0.38" title="Nov 3, 09:00 · 1.5 s"></i><i data-state="ok" data-v="0.31" title="Nov 3, 10:00 · 1.2 s"></i><i data-state="failed" data-v="0.72" title="Nov 3, 11:00 · 2.8 s"></i><i data-state="ok" data-v="0.33" title="Nov 3, 12:00 · 1.3 s"></i><i data-state="ok" data-v="0.28" title="Nov 3, 13:00 · 1.1 s"></i><i data-state="ok" data-v="0.31" title="Nov 3, 14:00 · 1.2 s"></i><i data-state="ok" data-v="0.41" title="Nov 3, 15:00 · 1.6 s"></i><i data-state="ok" data-v="0.31" title="Nov 3, 16:00 · 1.2 s"></i><i data-state="ok" data-v="0.33" title="Nov 3, 17:00 · 1.3 s"></i><i data-state="ok" data-v="0.28" title="Nov 3, 18:00 · 1.1 s"></i><i data-state="ok" data-v="0.36" title="Nov 3, 19:00 · 1.4 s"></i><i data-state="ok" data-v="0.31" title="Nov 3, 20:00 · 1.2 s"></i><i data-state="running" data-v="0.15" title="Nov 3, 21:00 · 0.6 s"></i></span>
	<figcaption class="fs-12 text-muted tabular-nums">Median 1.2 s · longest 3.9 s</figcaption>
</figure>
```

## A board of lanes

`table class="lanes"` lays a time window across: a header cell `lanes-scale` holds the captions, each at its `data-at`, and each row is a lane - its name in the `th`, which may hold a button, and its track in the `td`. In a track `lanes-tick` marks a run at `data-at`, `lanes-tick-once` a single one, and `lanes-span` from `data-from` to `data-to` a run that goes on, hatched, with its caption. The red line is now, at the table's `data-now` - 0, the start, by default; the grid has `--redz-lanes-steps` parts, 8. A caption that would touch the one before it hides, in any language and at any width. Elements that share a [`data-highlight`](table.md#table-highlight) key light together: a lane, its track included, and its row in a table.

```html
<table class="lanes">
	<caption>The next 24 hours</caption>
	<thead><tr><td></td><th class="lanes-scale" scope="col"><span data-at="0">now</span><span data-at="0.125">18:00</span><span data-at="0.25">21:00</span><span data-at="0.375">00:00</span><span data-at="0.5">03:00</span><span data-at="0.625">06:00</span><span data-at="0.75">09:00</span><span data-at="0.875">12:00</span><span data-at="1">15:00</span></th></tr></thead>
	<tbody>
		<tr data-highlight="mail.queue"><th scope="row">mail.queue</th><td><span class="lanes-span" data-from="0" data-to="1" title="mail.queue - every minute">continuous</span></td></tr>
		<tr data-highlight="backup.nightly"><th scope="row">backup.nightly</th><td><i class="lanes-tick-once lanes-tick" data-at="0.458" title="backup.nightly - 02:00"></i></td></tr>
		<tr data-highlight="fx.rates"><th scope="row">fx.rates</th><td><i class="lanes-tick" data-at="0.04" title="fx.rates - 16:00"></i><i class="lanes-tick" data-at="0.207" title="fx.rates - 20:00"></i><i class="lanes-tick" data-at="0.373" title="fx.rates - 00:00"></i><i class="lanes-tick" data-at="0.54" title="fx.rates - 04:00"></i><i class="lanes-tick" data-at="0.707" title="fx.rates - 08:00"></i><i class="lanes-tick" data-at="0.873" title="fx.rates - 12:00"></i></td></tr>
	</tbody>
</table>
<table class="table" aria-label="Agents">
	<thead><tr><th scope="col">Agent</th><th scope="col">Next run</th></tr></thead>
	<tbody>
		<tr data-highlight="mail.queue"><td class="font-monospace">mail.queue</td><td>Every minute</td></tr>
		<tr data-highlight="backup.nightly"><td class="font-monospace">backup.nightly</td><td>02:00</td></tr>
		<tr data-highlight="fx.rates"><td class="font-monospace">fx.rates</td><td>16:00</td></tr>
	</tbody>
</table>
```

## Accessibility

- A chart is its table: the caption names it, the header cells name the series, each row says its day and its numbers. `charts.js` gives a `chart` or `chart-rows` table the table's roles, since WebKit drops them once CSS lays the table out otherwise.
- A share bar and breakdown rows say their shares in words - the legend's, the rows' - and the bar repeats them.
- A color never speaks alone: the legend names every series, a state's segment is named by its header cell, and a sparkline's summary is its name.
- Three of the light theme's series colors are under 3:1 against the page: the legend and the table are what tell them apart.
- In forced colors every bar is drawn in the text color; the numbers are in the table.
