Tables, figures and the containers around them
The thinnest part of journeymro_site — the marketing pages never needed a real data grid, and the report screens each grew their own. This page is where that gets settled: one table idiom, one stat idiom, one chart container, all in the frame's own vocabulary.
Principles
tnum, lnum, ss01 automatically. Numeric columns are right-aligned so the eye can compare down them; text columns are left-aligned. Never centre either.aspect-ratio box before the data arrives. A chart that sizes itself after a library measures it reflows the page under the reader.Tables
Always wrap a table in .table-wrap. It is the only thing standing between a wide table and a horizontally scrolling page, and on a phone it is what lets the table keep its shape instead of collapsing into cards.
| Part | Description | Tier | On hand | ROP | Value | Status |
|---|---|---|---|---|---|---|
| 4400-118-22 | Bearing, roller, 220mm | A | 1,184 | 860 | $412,900 | Healthy |
| 4400-206-08 | Seal, mechanical, 80mm | A | 42 | 180 | $38,400 | Below ROP |
| 4400-311-14 | Filter element, hydraulic | B | 960 | 640 | $74,100 | Healthy |
| 4400-455-91 | Belt, drive, 4-rib | C | 18 | 24 | $2,140 | Low |
| 4400-502-33 | Coupling, flexible | B | 0 | 12 | $0 | Stockout |
| 5 of 14,203 items | 2,204 | 1,716 | $527,540 | |||
| PO | Vendor | Line | Promised | Actual | Drift |
|---|---|---|---|---|---|
| PO-88412 | Northline Bearings | 001 | 14d | 19d | +5 |
| PO-88412 | Northline Bearings | 002 | 14d | 14d | 0 |
| PO-88510 | Corveq Seals | 001 | 28d | 51d | +23 |
| PO-88533 | Arlon Filtration | 001 | 7d | 6d | −1 |
| PO-88533 | Arlon Filtration | 002 | 7d | 9d | +2 |
| PO-88604 | Corveq Seals | 001 | 28d | 44d | +16 |
| PO-88711 | Westgate Hose | 001 | 21d | 22d | +1 |
| PO-88742 | Northline Bearings | 001 | 14d | 31d | +17 |
th carries background: var(--bg) for exactly this reason — a transparent sticky head lets the rows scroll through it.In-cell graphics
A magnitude read without leaving the row. Keep it to one graphic column per table — two competing bars is a chart pretending to be a table.
| Vendor | Share of spend | Lead-time drift | 12-month trend | Lines |
|---|---|---|---|---|
| Northline Bearings | 412 | |||
| Corveq Seals | 288 | |||
| Arlon Filtration | 190 | |||
| Westgate Hose | 96 | |||
| Delta Fasteners | 54 |
Stats & meters
The lattice comes from borders on the cells, not from a wrapper, so .stat-row reflows from five across to two to one without the rules ever going wrong. auto-fit and minmax(160px, 1fr) do the whole job — resize this window.
Chart containers
The container is the design; the plot inside it is left alone. Every figure below is hand-drawn SVG using only the classes in journeymro.css — no charting library — which is the point: a library configured to these same values will land in the same place.
The forecast continues as a dashed ghost in --series-ghost, never as a second solid line. A reader should be able to tell measured from modelled without reading the legend.
Bars above the intervention threshold take --danger. Everything else is the accent — not a per-category colour.
The parity line is the ghost stroke. Points more than 40% late take --danger; the colour carries the finding, so no annotation is needed.
<div class="viz-panel">
<div class="viz-title">
<span>Fig. 01 — On-hand units against reorder point</span>
<span class="tag">52 weeks</span>
</div>
<!-- ratio-wide (16:9) · ratio-std (4:3) · ratio-sq (1:1)
reserves the height so the page cannot reflow under the reader.
Draw the viewBox at the SAME ratio — 800×450 here — or the plot is
stretched to fit and the stroke weights distort with it. -->
<div class="viz ratio-wide">
<svg viewBox="0 0 800 450">
<g class="grid"><line …/></g> <!-- horizontal only -->
<path class="fill-1" d="…"/>
<path class="series-1" d="…"/>
<path class="ghost" d="…"/> <!-- forecast, always dashed -->
<g class="axis"><line …/></g>
<text class="tick" …>700</text>
</svg>
</div>
<div class="viz-foot">
<div class="chart-legend"><span><span class="key"></span>On hand</span></div>
<span class="mono mono-sm">Source · issues ledger</span>
</div>
<p class="viz-note">One sentence saying what to notice.</p>
</div>Leave the panel's background transparent so the page grid shows through. That is what stops a figure reading as a pasted screenshot.
Give the chart a heading. The title is mono chrome — a chart is a figure inside a section, not a section of its own.
Draw gridlines horizontally only, in --hairline-faint. Vertical gridlines duplicate the page grid behind them.
Let a library pick colours. Read --series-1…4 off :root at runtime so the light theme follows.
Write a .viz-note saying what to notice. A figure without a claim is decoration.
Go past four series on one plot. Beyond that a line chart is the wrong chart — use small multiples.
Every charting library takes explicit colours. Take them from the stylesheet, not from a copy — then a theme switch is one redraw and not a second palette to maintain.
function palette() {
const s = getComputedStyle(document.documentElement);
const t = (n) => s.getPropertyValue(n).trim();
return {
series: [t('--series-1'), t('--series-2'), t('--series-3'), t('--series-4')],
ghost: t('--series-ghost'),
grid: t('--grid-line'),
axis: t('--axis-line'),
text: t('--axis-text'),
font: t('--font-mono'),
};
}
// journeymro.js fires this whenever the theme changes
window.addEventListener('jm:theme', () => redraw(palette()));Series palette
Ordered. Take them in sequence rather than picking by taste — the second series is deliberately a neutral, so it reads as the other one rather than as a rival hue.
Dashboard layout
Everything on this page, assembled the way a real screen would be: a toolbar on the frame, a stat row, a chart and a table sharing one hairline lattice. Note that nothing here is a floating "widget" with its own shadow — the lattice is the layout.
Stock position
| Part | Description | On hand | ROP | Cover | Status |
|---|---|---|---|---|---|
| 4400-206-08 | Seal, mechanical, 80mm | 42 | 180 | Below ROP | |
| 4400-502-33 | Coupling, flexible | 0 | 12 | Stockout | |
| 4400-455-91 | Belt, drive, 4-rib | 18 | 24 | Low | |
| 4400-118-22 | Bearing, roller, 220mm | 1,184 | 860 | Healthy |
margin-bottom: -1px), so the lattice stays 1px throughout instead of doubling where two components touch. Same trick as the notch lapping onto the rail.