ChartStyle
A stylesheet for charts drawn in CSS, so a measurement can live on a page as text you can read, search, correct and translate — instead of being baked into a PNG. Declare it on any page with [[style:ChartStyle]].
It rides on top of whatever skin the site is wearing and borrows that skin's own --ink, --bg, --hair and --quiet, so a chart is never a foreign object on the page and follows the lamplight palette into dark mode by itself.
A note for anyone editing this page. /style/ unwraps every fence it finds, and a fence it does not recognise still ends with three backticks that it reads as the start of one. So this page carries exactly one fenced block — the CSS at the bottom — and every example above is written as indented code instead. Add a second fence and the stylesheet breaks.
The heatmap
A grid of readings where the value paints its own cell. You author the real number twice — once in a custom property, once as the text — and the colour is computed from it, so the ink can never drift out of step with the figure.
<div class="heat" style="--cols:3">
<b>groove</b><b>Column B</b><b>Column C</b>
<i>row label</i>
<span style="--v:.95">0.95</span>
<span class="null" style="--v:.50">0.50</span>
<span style="--v:1">1.00</span>
</div>
The first <b> is the corner; the rest are column heads. Each <i> starts a row, and the <span>s after it are that row's readings.
--v is the reading itself. --lo (default .5) is the value that means nothing; --k (default 2) is one divided by the span from nothing to full, because calc() may not divide by a var(). The cell's ink is the reading's distance between them, and its text flips toward paper as its ground darkens. class="null" rings a cell to say it is indistinguishable from nothing at all.
For a scale that runs 0 → 1 rather than 0.5 → 1, set --lo:0 and --k:1 on the container.
The bar
An inline bar that fills its own width behind the number — <span class="bar" style="--v:.64">0.64</span>. Same --lo and --k.
/* ChartStyle — charts as markup, not pictures.
Everything is keyed off ONE custom property, --v, holding the real
reading; the ink is derived from it. Nothing here is a hard-coded
colour except the accent, which a page may override. */
.heat, .barlist {
--chart: #2d5c96; /* the accent a value is painted in */
--null: #9b3d28; /* the ring on "this reads as nothing" */
--lo: .5; /* the reading that means nothing */
--k: 2; /* 1 / (full - nothing) — calc() may not divide by a var() */
--cols: 3;
font-family: var(--sans, ui-sans-serif, system-ui, sans-serif);
font-feature-settings: "onum" 0, "lnum" 1, "tnum" 1;
font-variant-numeric: lining-nums tabular-nums;
}
:root[data-theme="dark"] .heat, :root[data-theme="dark"] .barlist { --chart: #6d9bd4; }
@media (prefers-color-scheme: dark) {
:root:not([data-theme="light"]) .heat,
:root:not([data-theme="light"]) .barlist { --chart: #6d9bd4; }
}
/* how far a reading sits between "nothing" and "full", clamped to 0..1 */
.heat > span, .bar {
--f: clamp(0, calc((var(--v) - var(--lo)) * var(--k)), 1);
}
.heat {
display: grid;
grid-template-columns: minmax(4.5rem, auto) repeat(var(--cols), 1fr);
gap: 3px;
margin: 1.6rem 0;
font-size: .82rem;
}
/* the column heads */
.heat > b {
font-weight: 600;
font-size: .69rem;
letter-spacing: .09em;
text-transform: uppercase;
color: var(--quiet);
text-align: center;
padding: 0 0 .3rem;
align-self: end;
}
.heat > b:first-child { text-align: right; padding-right: .7rem; }
/* the row labels */
.heat > i {
font-style: normal;
color: var(--quiet);
text-align: right;
padding-right: .7rem;
align-self: center;
white-space: nowrap;
}
/* a reading: its own ground, its own contrast */
.heat > span {
padding: .55rem .4rem;
text-align: center;
border-radius: 2px;
background: color-mix(in srgb,
var(--chart) calc(var(--f) * 92%),
color-mix(in srgb, var(--hair) 45%, transparent));
/* contrast must FLIP, not fade. A linear ink-to-paper ramp puts mid
readings at mid-grey on a mid-ground — the one place contrast is
worst, and exactly where 0.76 and 0.86 became unreadable. This
crosses over quickly around the middle instead. */
color: color-mix(in srgb,
var(--bg) clamp(0%, calc((var(--f) - .45) * 400%), 100%),
var(--ink));
}
.heat > span.null { box-shadow: inset 0 0 0 1.5px var(--null); }
/* an inline bar — the number sits on its own fill */
.bar {
display: inline-block;
min-width: 5.5rem;
padding: .12rem .5rem;
border-radius: 2px;
text-align: right;
background:
linear-gradient(to right,
color-mix(in srgb, var(--chart) 55%, transparent) calc(var(--f) * 100%),
color-mix(in srgb, var(--hair) 40%, transparent) 0);
}
@media (max-width: 640px) {
.heat { font-size: .74rem; gap: 2px; }
.heat > span { padding: .45rem .2rem; }
}