Set IBM Plex Sans and Plex Mono for every brand, self-hosted and subset by rule, on a documented scale
Context and Problem Statement
The sites run five font stacks (Inter + Manrope, IBM Plex + Fraunces, IBM Plex, Source Serif
- Source Sans, Archivo + JetBrains Mono), each loaded its own way. The 0.2 draft chose IBM Plex over Inter, Geist and Public Sans and declared a type scale “a major third from 16” whose small sizes did not follow it; the fit between the typeface and the wordmarks was never tested. On 3 Oct 2026 Demian set Plex Sans and Plex Mono as the hypothesis, to be confirmed with trilingual technical specimens and the fit test, and swapped only if it failed. What does the family read in, how is it delivered so that the twelve sites and the documents share one typography, and does it sit well beside the marks?
The pass conditions were written before measuring: (1) every character of Spanish, Portuguese and English text and of technical notation (± µ ° Ω ‰ ≤ ≥ × ² ³ → — « » ✓) is in the font; (2) figures align in columns; (3) the register is that of a precise instruments maker; (4) an open licence; (5) installable for Office and print; (6) a weight the brand uses whose stem, at the mark’s x-height, is within 10 % of the mark’s stem, so a heading or a label can sit beside a mark without one reading thinner by accident.
Decision Drivers
Criteria 1 (trust of a regulated buyer), 2 (one coherent, expandable family), 3 (register), 4 (accessibility), 5 (works beyond the screen), 7 (three languages), 8 (open licences) and 9 (generated and tested) of ADR-0009.
Considered Options
- IBM Plex Sans (variable) and IBM Plex Mono, self-hosted and subset in the build
- Inter and JetBrains Mono
- Source Sans 3 and Source Code Pro
- A system font stack only, no files of our own
Decision Outcome
Proposed: IBM Plex Sans and IBM Plex Mono, self-hosted from this repository, subset by
rule, on the scale below. Every pass condition holds, measured in the build
(brand/src/type.ts, brand/test/type.test.ts) from brand/type.yaml; the evidence is in
brand/evidence/type-fit.png and type-specimen.png, and live on the Typography topic.
-
Two families, one for all ten brands. Plex Sans for text, headings, interface and data; Plex Mono for code, identifiers, file names and small uppercase labels. No unit has a typeface of its own: its identity is its wordmark and its colour. Plex is a grotesque drawn for an engineering company, with a double-storey a and plain stems; the wordmarks are geometric. The constructions differ, and that is wanted: a typeface that imitated the marks would make any typed name look like a mark, which the Wordmarks rule forbids.
-
Web files, generated. From the npm packages at pinned versions (
@ibm/plex-sans-variable@0.2.0,@ibm/plex-mono@2.5.0), HarfBuzz subsets each face to the rangestype.yamldeclares: Latin, Latin Extended-A, Greek, typographic punctuation, superscripts and subscripts, fractions, arrows, mathematical operators, ✓. The variable Sans keeps its weight axis from 300 to 700 and drops the width axis (pinned at 100). Four files: Sans roman 77 KB and italic 83 KB (480 code points, 534 glyphs each), Mono 400 and 500 at 21 KB (432 code points; Plex Mono has no Greek, which falls back to the system mono). Each is named by its content hash and cached for a year;/tokens/type.csscarries the@font-facerules with exactunicode-ranges, and other AKRIBIS sites load both cross-origin. The build fails if a declared symbol is missing from the Sans, if the site’s own text uses a character the subset lacks, or if an axis, a feature or a size is not what the file says. -
Weights. Regular 400 for text; Medium 500 for interface (labels, buttons, table headers, emphasis at small sizes); SemiBold 600 for headings and strong; Bold 700 for display. Light 300 travels in the file without a job.
-
The scale, in rem (16 px at the browser default). Above the base a major third, 1.25: 1.25 · 1.5625 · 1.9531 · 2.4414 · 3.0518. Below it, arithmetic, 2 px a step: 0.875 and 0.75, and nothing smaller, because text sizes need small, even differences and a 12 px floor, while display sizes need proportional ones. Steps 3 to 5 are fluid: one step smaller on a 20 rem viewport, nominal from 80 rem, as
clamp(). Line height per step: 1.5 for text (steps −2 to 0), then 1.4 · 1.3 · 1.2 · 1.15 · 1.1. Measure 68ch (45ch narrow). Tracking: none at text sizes; −0.01em from step 3; +0.06em on uppercase labels. The test asserts the ratios, the floor and the fluid bounds. -
Figures. Plex’s figures are lining and tabular by default (every digit 0.6 em in the Sans; there is no
tnumbecause none is needed), so tables align with nothing set. Slashed zero (font-variant-numeric: slashed-zero) in codes and serials where 0 and O meet. The subset keepskern,liga,frac,sups,subs,zeroand the stylistic sets. -
Roles, as
font: var(--type-<role>): display (5, semibold), title (4, semibold), heading (2, semibold), subheading (1, semibold), lede (1), body (0), small (−1), caption (−2), label (−2, mono, medium, uppercase), code (−1, mono), data (0, tabular). A site invents no sizes; it uses the steps (--size-N), the weights (--weight-*), the leadings and the trackings. The same roles are in/tokens.jsonas DTCGtypographytokens, with the metrics of the faces (x-height 0.516, cap height 0.698, ascent 1.025, descent 0.275 of the em) so a mark can be aligned to text by x-height. -
The fit test, measured on the static instances. The mark’s stem is the ring module, 131.59 grid units on an x-height of 500: 0.263 of the x-height. The plain stem of Plex Sans is the i (the l has a tail, the I serifs). At the same x-height:
Instance Stem / x-height Against the mark Round o Ascender Descender Wordmark 0.263 0.921 1.293 0.421 Light 300 0.152 −42.1 % 0.871 1.445 0.391 Regular 400 0.190 −27.9 % 0.903 1.434 0.388 Medium 500 0.248 −5.7 % 0.919 1.423 0.385 SemiBold 600 0.287 +9.2 % 0.929 1.418 0.383 Bold 700 0.328 +24.5 % 0.937 1.410 0.381 Medium, the interface weight, weighs like the mark and its round letters are the same width (0.919 against 0.921); SemiBold headings weigh 9 % more, so a heading reads as text and the mark as a mark. Plex’s ascenders are about a tenth taller and its descenders a tenth shorter than the mark’s at equal x-height, which is why a mark is aligned to text by x-height, not by ascender. Condition (6) holds with margin.
-
Office and print. The complete static instances as IBM ships them, converted from WOFF2 to TTF without touching the font: Sans Regular, Italic, Medium, Medium Italic, SemiBold, SemiBold Italic, Bold, Bold Italic and Mono Regular, Medium, in a zip named by the font version (
akribis-fonts-office-plex-3.005.zip, 894 KB) with both licences and a README that says which weight does what. The Office templates (topic 4.1) will use them. -
Licence. SIL Open Font License 1.1 for both families: no per-seat cost, embeddable in documents, for every unit, site and template.
-
Brand wears the brand. This site links
type.css, preloads the roman Sans and the medium Mono, and styles every text with the roles; its own figures and tables now use the steps. The remaining system-font stacks of the other sites are replaced when they are rebuilt to the standard.
Consequences
- Good, because one
type.cssand four files give twelve sites the same letters, sizes and rhythm, and the build proves what the files contain. - Good, because the type scale is now what it claims, and a heading beside a mark has a measured relationship instead of an eyeballed one.
- Good, because the fonts for documents are the same design at the same weights, under a licence that costs nothing.
- Open: metric fallbacks (
size-adjust) so text does not jump before the font loads; whether dark mode wants a lighter weight; other scripts if a unit needs them (Mono’s Greek falls back today); a serif for long documents, not planned. The figure annotations on this site (9 to 10 px in the ramp swatches) sit below the text floor on purpose: they are chart labels, not text. - Inter, Source Sans and the system stack were not drawn up as specimens: Plex met every condition, and the mandate was to swap it only on failure.