arXiv sets all text in the IBM Plex family with STIX Two Math for notation, self-hosted — no external font services.
The type family is IBM Plex, plus STIX Two Math for notation. Each face has a job.
IBM Plex Sans
Body text — paragraphs, author names, abstracts, UI.
font-family: var(--ds-font-sans);
IBM Plex Sans Condensed
Labels & captions — metadata labels, table headers, uppercase section labels.
font-family: var(--ds-font-condensed);
IBM Plex Mono
Code & identifiers — arXiv IDs, DOIs, BibTeX, code blocks.
font-family: var(--ds-font-mono);
<code> and <pre> already use it; see Code blocks below.IBM Plex Serif italic
Annotation voice — footnote text and figure alt-text in the reader margins. “The editor speaking quietly next to the body text.”
<p class="ds-annotation">Figure 2: the scalaron potential.</p>
--ds-font-serif, italic, 400, 0.8125rem, --ds-text-muted. It is a voice, not a layout role; where it sits is the consumer's decision..ds-annotation.IBM Plex Serif (blog only)
Headlines and pull quotes on the arXiv blog. The upright serif weights appear in no other context.
font-family: var(--ds-font-serif);
STIX Two Math
Mathematical notation in HTML papers, rendered from MathML.
font-family: "STIX Two Math", "Cambria Math", math;
font-display: auto per DESIGN-POLICIES.The text sizes design-system.css sets, in rem, so every one of them moves with the reader's base size. Headings have their own scale below.
inline code tracks whatever it sits inside, a heading included.<p>Body text.</p>
<small>Secondary text.</small>
<p class="ds-annotation">A margin footnote.</p>
<p class="ds-panel-label">Paper information</p>
<p>An identifier: <code>arXiv:2604.22725</code></p>
--ds-text-muted.Four levels, one ratio: each level is a quarter larger than the one below it, which
puts h1 at exactly twice body size. Sizes are rem; the px equivalents assume a 16px root and move with the
reader's setting. Every level is --ds-font-sans in --ds-text —
rank is carried by size and weight alone, never by a colour change or a different face.
<h1>Gauge-independent approach to inflation</h1>
<h2>Submission history</h2>
<h3>Gauge-invariant perturbations</h3>
<h4>Boundary conditions</h4>
--ds-space-4.--ds-space-8, below --ds-space-3.--ds-space-6, below --ds-space-2.--ds-space-4, below --ds-space-2.Links are typography with a job, so they live here. Color carries the state; the underline carries the meaning — and inline links in body text are always underlined (color alone fails WCAG 1.4.1 at our contrast levels). Standalone navigation links (headers, footers, button-like rows) may drop the underline.
Full state-by-state references (rest / hover / visited / focus / disabled, light and dark): Links. Link color tokens with ratios: Colors.
Reading text with <a href="/abs/2604.22725">an inline link</a> in it.
.ds-link and the --ds-link-* tokens: the same grammar, the same names and the same values. Inside a .ds-page a bare <a> gets the same treatment without the class.:focus-visible only.A code block is Plex Mono on the dark chrome ground, and it always carries a copy button — the familiar two-sheets icon, top right. Someone reading a pattern page is there to take the markup away with them, and asking them to select four lines of it by hand without missing the last character is asking them to make a mistake.
<button class="ds-btn ds-btn-primary" type="button">Save</button>
<!-- Write the block the way you already do -->
<pre><code><button class="ds-btn">Save</button></code></pre>
<!-- And include the script once, at the foot of the page -->
<script src="copy-code.js" defer></script>
<, > and & inside it. No wrapper and no button of your own.<pre> on the page and adds the control.<pre>, opts that block out — for a block that is an illustration of output rather than something to take away.The small uppercase Condensed label that names a panel, a column or
a group. It is not a heading level and stays out of the document outline — if a reader
needs to navigate to it, it is an h3 or an h4 at the sizes above.
Do not reach for it as a heading substitute. A label that a reader needs to navigate to is a heading, and belongs in the outline.
Paper information
The label sits closer to what it introduces than to what precedes it, so it bonds downward. That relationship is intrinsic to the thing, not something its context supplies — which matters because half the instances in these pages are paragraphs rather than headings.
<p class="ds-panel-label">Paper information</p>
--ds-text-muted. Use it wherever this treatment appears — panel
headings, column labels, the label above a group of controls — on whichever element
is semantically right. A component that owns its internal spacing takes the margin back..ds-acc-body is this label, and carries no class.<h1> per page, then
<h2>, <h3> and <h4> in order, without
skipping a level. Choose the level from the outline, not from the size you want; the scale gives
each level its size. A screen reader user moves through the page by these levels.em, so the box grows with its label.overflow: hidden unless there is another way
to read all of it. At 400% page zoom a 1280px screen is a 320px screen, and the page must not
scroll sideways there.alt text or next to it..ds-panel-label makes it so. Type it in sentence case, so
copy-and-paste, search and screen readers get the real words.One family, three contexts. Internal tools and the public site share the working set; outreach properties add the upright serif and the heaviest sans. A face without a mark is not used in that context.
| Face · weights | Internal tools | Public site | Outreach |
|---|---|---|---|
| IBM Plex Sans · 400 / 500 / 600 | ✓ all text | ✓ all text | ✓ all text |
| IBM Plex Sans · 700 + 700 italic | ✓ h1 only | ✓ h1 only | ✓ the one heading that must not read as an article |
| IBM Plex Sans Condensed · 500 / 600 | ✓ labels, table headers | ✓ labels | ✓ labels |
| IBM Plex Mono · 400 / 500 | ✓ IDs, code | ✓ arXiv IDs, code | ✓ code, plus a display register (wordmark, standfirst) |
| IBM Plex Serif · 400 italic | annotation voice only (.ds-annotation) | annotation voice only (.ds-annotation) | ✓ emphasis, bylines, prev/next |
| IBM Plex Serif · 400 / 600 upright | — | — | ✓ headlines to display size, pull quotes, drop caps |
| STIX Two Math · 400 | — | ✓ math in HTML papers | — |
800 either
falls back or gets faux-bolded, differently per browser. Ask for the weight that exists.Space is given as spacing tokens, not pixels, because that is
what design-system.css implements — --ds-space-8 is 2rem, --ds-space-6
is 1.5rem, and so on down the scale.
font-size inherits the browser default bold, which is how several pages ended
up with an h3 heavier than the h2 above it.clamp() between
rem bounds rather than a fixed 2rem — see "Fluid where titles meet phones" below.--ds-font-condensed and --ds-text-muted, it marks a panel or a column and
stays out of the document outline. If a reader needs to navigate to it, it is an h3 or an h4 at the size
above, not a label.copy-code.js walks every <pre> on the page, wraps it in
.ds-code and inserts the control, which is .ds-code-copy. A block added next year is covered without
anyone remembering, which a per-block wrapper would not be. Every page under
docs/ already loads the script.<pre> scrolls horizontally, so padding at its inline end does not hold the
visible right edge clear — a long line runs straight under a right-hand button. A lane
across the top works at every line length.aria-hidden and the button carries
aria-label="Copy code", which is what a screen reader announces. The
title is the pointer affordance only — assistive technology takes the
aria-label and ignores it.role="status" region says “Copied to clipboard”.
Both are needed: a tick is invisible to a screen reader, and the button’s own name does
not change because its action did not change — it is still the copy button. Both reset
after two seconds.file:// is not, and it rejects when the window is not focused.
Both cases fall back to execCommand, and if that fails too the status region says
so rather than the button quietly doing nothing.<pre data-no-copy> — for a
block that is an illustration of output rather than something to take away.A browser font-size setting has to reach arXiv content (accessibility-priorities.md,
cognitive/dyslexia track), so every type size is rem. But a label that grows
inside a box that does not is no better than a label that never grew: set a control's padding
and minimum width in em, against its own label, and the whole control follows the
reader. Absolute floors stay absolute — min-height: 24px is a WCAG target
size, not a type size.
No Google Fonts, no Typekit, no CDN fonts (DESIGN-POLICIES.md). Every font-family declaration carries system fallbacks; text fonts load with font-display: swap (readability first), STIX Two Math with font-display: auto (math must render in the correct face).
No fixed line-heights, no pinned letter-spacing, no !important on font-family in content areas. User stylesheets, reader-mode extensions, and OS settings win — "HTML gives you freedom: you can use software to change colors or typefaces" (research, dyslexia track).
Paper titles use clamp() between rem bounds (22px–32px equivalent) so a seven-word title does not wrap to five lines at 320px. Body text never scales fluidly — it tracks the user's base size exactly.
Everything above is the design. This is what an implementer needs: the tokens, the
files, the @font-face rules and the fallbacks. Placed last because it is reference, not reading.
Open source under the SIL Open Font License, so there is no licensing cost and no vendor dependency; self-hostable as woff2 from arXiv's own assets; one designer's family covering Sans, Condensed, Mono and Serif so nothing is mixed; broad language coverage including Latin Extended, Cyrillic, Greek, Arabic, Hebrew, Devanagari and Thai; and it pairs with STIX Two Math, which is built for the same technical context. The family was re-evaluated in June 2026 against Atkinson Hyperlegible Next, Source Sans 3, Inter and Public Sans, and kept — that comparison lives in planning/proposals, not here.
--ds-font-sans: "IBM Plex Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
--ds-font-condensed: "IBM Plex Sans Condensed", "IBM Plex Sans", sans-serif;
--ds-font-mono: "IBM Plex Mono", ui-monospace, "SF Mono", Menlo, monospace;
--ds-font-serif: "IBM Plex Serif", Georgia, "STIX Two Text", "Times New Roman", serif;
Every stack carries system fallbacks so text stays readable if the web fonts fail: sans falls to the OS system font, mono to the platform monospace. There is no math token — STIX Two Math is applied by the paper renderer on HTML paper pages, not by the design system, so the stylesheet does not declare one.
Every weight and style IBM Plex ships is self-hosted — Thin 100, ExtraLight 200,
Light 300, Regular 400, Text 450, Medium 500, SemiBold 600 and Bold 700, each with an italic,
across Sans, Sans Condensed, Mono and Serif. That is 64 files and about 4 MB in the repository,
and it costs a reader nothing: a @font-face rule downloads only when a page uses
that face.
Available is not the same as approved. Only the faces the design system uses are declared in fonts.css; the rest are present but commented out, so you can see what exists without going to look for it. Uncommenting one is how you turn it on — and it is a decision, not a formality.
| Weight | Use it for |
|---|---|
| 400 Regular | Body copy, and anything that is read rather than scanned. |
| 500 Medium | Navigation, table headers, the quieter emphasis. |
| 600 SemiBold | Headings, button labels, and the label half of a label/value pair. |
| 700 Bold | The h1, and nothing else — see the heading scale. |
| 100, 200, 300, 450 | Present, unused, and commented out. |
Light weights are an accessibility problem that contrast ratios do not catch
Thin, ExtraLight and Light are now reachable. At interface sizes their strokes are too fine for many readers to resolve even when the colour passes AA — the ratio measures the hue, not whether the letterform survives. Body and interface text use 400 or heavier. Anything lighter belongs at display sizes, if anywhere, and is worth testing before it ships.
Self-hosted woff2, from arXiv's static assets. No external font service — see DESIGN-POLICIES for why that one is a hard constraint.
/static/fonts/
IBMPlexSans-*.woff2 8 weights x roman/italic = 16 files
IBMPlexSansCondensed-*.woff2 8 weights x roman/italic = 16 files
IBMPlexMono-*.woff2 8 weights x roman/italic = 16 files
IBMPlexSerif-*.woff2 8 weights x roman/italic = 16 files
STIXTwoMath-Regular.woff2 ← HTML paper pages only
This repo's pattern pages serve the same files from docs/fonts/, declared in
fonts.css, which every page links ahead of the stylesheet. The
@font-face rules live in that separate file rather than in the design system itself, because
consumers bundle the design system with their own font paths. Files come from
IBM/plex (OFL).
@font-face {
font-family: "IBM Plex Sans";
font-weight: 400;
font-style: normal;
font-display: swap;
src: url("/static/fonts/IBMPlexSans-Regular.woff2") format("woff2");
}
/* …one per weight and style… */
@font-face {
font-family: "STIX Two Math";
font-weight: 400;
font-display: auto;
src: local("STIX Two Math"),
url("/static/fonts/STIXTwoMath-Regular.woff2") format("woff2");
}
font-display: swap for text: the fallback shows immediately and swaps when the real font
arrives. Readability beats visual stability.font-display: auto for STIX Two Math: maths in a fallback font can be actively wrong, so it
is better to wait briefly.local() for STIX Two Math skips the ~300KB download if the reader already has it.| Surface | Body | Labels | Code | Math |
|---|---|---|---|---|
| Abstract pages | Plex Sans | Plex Sans Condensed | Plex Mono | — |
| HTML paper pages | Plex Sans | Plex Sans Condensed | Plex Mono | STIX Two Math |
| Internal tools | Plex Sans | Plex Sans Condensed | Plex Mono | — |
| Homepage, listings, search | Plex Sans | Plex Sans Condensed | Plex Mono | — |
Not yet a formal scale — these are the sizes used consistently across the mockups, recorded so the scale can be settled from what exists rather than invented.
| Use | Size | Weight | Face |
|---|---|---|---|
| Paper title (abstract page) | 24px | 600 | Plex Sans |
| Section heading | 15–16px | 600 | Plex Sans |
| Body text | 15px | 400 | Plex Sans |
| Metadata labels, table headers | 11px uppercase | 600 | Plex Sans Condensed |
| Secondary text, captions | 13px | 400–500 | Plex Sans |
| Annotation (margin footnote, alt text) | 13px italic | 400 | Plex Serif |
| Code, identifiers | 12–13px | 400 | Plex Mono |
| Nav links | 13px | 500 | Plex Sans |