# Data Typography

> Typography for numbers and data-dense interfaces - tabular figures, alignment, precision and rounding, units, negative-number conventions, type scale, and the label/value pairing that makes a table or dashboard readable at a glance. Trigger on "typography", "font", "numbers don't line up", "type scale", "how many decimals", "format the numbers", "table formatting", "tabular figures", "text is hard to read".

- Skill: `lukehle/data-typography` (Agent Skill)
- Install (CLI): `npx skillmds@latest add lukehle/data-typography`
- Raw SKILL.md: https://api.skillmd.com/api/skills/lukehle/data-typography/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Design & Media
- Author: Lukehle (https://skillmd.com/u/lukehle)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/lukehle/data-typography

---


# Data typography

Most "this table is hard to read" problems are typographic, and most of them are three fixes:
tabular figures, right alignment, and consistent precision.

---

## Tabular figures — the single highest-value line of CSS

By default most fonts use proportional digits: a `1` is narrower than a `0`. In a column of numbers,
digits fail to stack and the eye cannot compare magnitudes by length.

```css
.num, td, th, .figure {
  font-variant-numeric: tabular-nums;
  font-feature-settings: "tnum" 1;   /* fallback for older engines */
}
```

Apply it to **every** numeric context: tables, KPI figures, axis ticks, data labels, tooltips.

---

## Alignment rules

| Content | Alignment | Why |
|---|---|---|
| Numbers | **Right** | Magnitude is comparable by digit position |
| Text labels | Left | Reading order |
| Column headers | Match their column | A left-aligned header over right-aligned numbers looks broken |
| Units and currency | Attached to the number, not a separate column | Splitting them doubles the eye's work |
| Dates | Left, in a fixed format | `2026-08-19` sorts and aligns; `Aug 19, 2026` does neither |

Align a numeric column on the **decimal point** by giving every cell the same decimal places. Ragged
precision within a column is the most common cause of a column that will not scan.

---

## Precision — decided per column, never per cell

State the rule once, apply it to the whole column:

| Context | Precision |
|---|---|
| Currency in a summary | Whole units (`$4,820,115`) |
| Currency in a reconciliation | Two decimals — cents matter, and the exact match is the point |
| Compact axis / KPI | One decimal (`$4.8M`) |
| Percentages | One decimal (`104.2%`); zero when the input is coarse |
| Ratios | One or two decimals, and only if the inputs support it |
| Counts | Zero, always |

**Never show more precision than the number has.** A figure derived from a rounded extract displayed
to the cent is a false accuracy claim. See `ui-antipatterns`.

**Pin the unit for a whole axis or column.** Mixing `$1.2M` and `$900K` forces unit arithmetic on the
reader. Choose `M` and accept `$0.9M`.

---

## Negatives

Two conventions. Pick per context; never mix within one view.

| Convention | Use in |
|---|---|
| **Parentheses** `(1,234.50)` | Tables, statements — the accounting convention your audience expects |
| **Minus sign** `−1,234.50` | Charts, labels, compact displays — readable at small sizes |

Use a true minus (`−`, U+2212) rather than a hyphen in display type; a hyphen is too short and sits
too high against digits.

Colour may **reinforce** sign but must never be the only encoding — it fails in greyscale and for
colourblind readers. The parenthesis or the minus is the encoding; colour is a bonus.

---

## Type scale

A small, fixed scale. Data pages need fewer sizes than editorial ones.

```css
--text-xs:   11px;  /* footnotes, provenance, axis ticks     */
--text-sm:   13px;  /* table body, secondary labels          */
--text-base: 15px;  /* body                                  */
--text-lg:   18px;  /* section headings                      */
--text-xl:   24px;  /* supporting figures                    */
--text-2xl:  36px;  /* the one headline figure               */
```

Two rules:
- **Weight before size.** Going 400 → 600 differentiates without disturbing the layout. Reach for
  size only when weight is not enough.
- **Two weights, maybe three.** 400 and 600 covers nearly everything; add 500 only if you need a
  middle step.

---

## The label/value pair

The atom of a data interface. Get it right once and reuse everywhere.

```
NET NEW ARR          ← label: small, muted, uppercase or sentence case, never bold
$210K                ← value: large, high contrast, tabular
▼ 27.6% vs plan      ← context: small, with direction encoded by glyph AND colour
```

- The **label is subordinate**. A bold uppercase label competing with its value is backwards.
- The **value carries the weight**. It is what the reader came for.
- The **context line is optional** and must add something — "vs plan", "vs prior", "TTM". A context
  line that just repeats the period is noise.
- Keep the vertical gap inside the pair tighter than the gap between pairs (`visual-hierarchy`).

---

## Fonts under a strict CSP

Web fonts require an external host and are blocked; embedding one as base64 costs real page budget.
A system stack costs nothing and renders natively everywhere:

```css
--font-sans: ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto,
             "Helvetica Neue", Arial, sans-serif;
--font-mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas,
             "Liberation Mono", monospace;
```

Use the mono stack for identifiers — transaction ids, account codes, hashes — never for figures.
Monospace digits are wider and less legible than a good sans with `tnum` enabled.

---

## Line length and leading

- Prose: **60-75 characters**. A full-width paragraph in a wide dashboard is unreadable.
- Body line-height: **1.5**. Table rows: **1.3-1.4** — tighter, since rows are short.
- Large figures: **1.1**. Default leading around a 36px number leaves an awkward gap.

---

## Related skills

- `visual-hierarchy` — where these elements sit
- `design-tokens` — the scale as tokens
- `financial-tables` — applying all of this to a real table
- `ui-antipatterns` — false precision and the failure modes
- `artifact-accessibility` — contrast and minimum sizes

