Heading Scale
Switzer 500–700 — display down to h4
Two self-hosted faces. Switzer is the whole interface AND the reading layer — one grotesque separated by weight and size rather than a display/text pair, and it sets the quotations too; Roboto Mono is the data voice, and it is shipped rather than borrowed from the OS because it sets every index, duration, tag and status on every screen. Switzer is an Indian Type Foundry cut from Fontshare, Roboto Mono is under the SIL OFL — 67 KB for the pair, licences in fonts/. Reading copy is 14 px: this is a product with an app inside it, and the compact scale is after openclaw/carapace. This page is the whole typographic contract: the scale, the reading weight, the effects, the voice, and the accessibility floor. Open the full specimen ↗
The pairing
The fourth voice — and the one font this system does not ship
The hairline is the structure. Everything else is negotiable.
Display speaks, Text explains, Mono keeps the records — and Serif quotes. A pull-quote, a drop cap and a section quotation are the places a page stops arguing and starts citing; set in the same grotesque as the argument, they never quite read as a different speaker, they read as a bigger heading. Compare the two above.
No font file is shipped for it. The N&M family is self-hosted because it is the
brand; a face used on three devices per page does not earn another download, and this system ships
no licence for one. --font-serif resolves to the reader’s own Georgia — a genuinely
good screen serif present on essentially every device — and a product that has licensed a
display serif overrides exactly one token to get it. The pattern is borrowed from
openclaw/carapace, which ships font stacks and no
font files at all.
The three voices
The hairline is the structure. Everything else is negotiable.
Mono is the material Principle 2 is made of — every index, duration, timestamp and status runs through it — and it is still the one face this system does not ship. Those runs are short, tracked and uppercase, and the reader’s own console face is not merely adequate there, it is more familiar than anything we could send them. The consequence to know: mono is SF Mono on macOS, Consolas on Windows, and whatever the distro sets on Linux, so a mono run is not an identical width across platforms. The tabular-nums in tokens/base.css is what keeps digits aligned.
Why body copy is 400
The previous family was a Nunito-derived cut whose true Regular rendered grey rather than black, so this system set reading copy at 450 (“Book”) — an interpolation step invented to fix that face’s problem. Switzer does not have it: its Regular is properly fitted, and carrying 450 across would have been cargo — half a step heavier than the designer drew, for no reason anyone could still state. Body is 400. Switzer also has a real 500, which the old family lacked, so the ramp is 400 / 500 / 600 / 700 and every step is a weight that was actually drawn.
The scale
--size-display | clamp(2rem, 1.55rem + 2.2vw, 3.25rem) | Namaste Salesforce |
--size-h1 | 2.5rem | Namaste Salesforce |
--size-h2 | 2rem | Namaste Salesforce |
--size-h3 | 1.5rem | Namaste Salesforce |
--size-h4 | 1.25rem | Namaste Salesforce |
--size-body-lg | 1.0625rem | Namaste Salesforce |
--size-body | 0.875rem | Namaste Salesforce |
--size-small | 0.8125rem | Namaste Salesforce |
--size-fine | 0.75rem | Namaste Salesforce |
--size-label | 0.6875rem | Namaste Salesforce |
--size-mono | 0.8125rem | Namaste Salesforce |
--size-mega is left out of this table on purpose — at clamp(2.75rem, 1.9rem + 4.2vw, 5.5rem) its preview row would be taller than everything above it put together. It is shown in place under Display & poster typography below, which is the only context it belongs in.
Compact, after carapace: 12 / 13 / 14 / 17 / 20 / 24 / 32, then the ratio opens back up above 32 because a hero is a different job from a heading. Reading copy is 14 px because this is a product with an app inside it — at 16 the player, admin and tables all had to fight the base size with --size-small, which is the tell that the base was wrong for most of the screens being built. Only the top two steps clamp: a hero has to survive a 360 px phone, a paragraph does not. Everything is rem, because one px font-size is one piece of text that stops responding to browser zoom.
Weight, leading, tracking
--tracking-label | var(--ns-tracking-label) | |
--tracking-tight | var(--ns-tracking-tight) | |
--tracking-mega | var(--ns-tracking-mega) | |
--tracking-wide | var(--ns-tracking-wide) | |
--weight-label | 700 | |
--weight-heading | 700 | |
--weight-semibold | 600 | |
--weight-medium | 500 | |
--weight-body | 400 | |
--weight-regular | 400 | |
--weight-body-strong | 600 | |
--leading-tight | var(--ns-leading-tight) | |
--leading-heading | var(--ns-leading-heading) | |
--leading-body | var(--ns-leading-body) | |
--leading-mega | var(--ns-leading-mega) |
Measure — the most common typographic mistake in a product
The reading measure is a character count, not a width — the constraint IS characters per line, which makes ch the one honest unit for it. Past roughly eighty characters the eye starts losing the line return, and reading speed measurably drops.
The reading measure is a character count, not a width — the constraint IS characters per line, which makes ch the one honest unit for it. Past roughly eighty characters the eye starts losing the line return, and reading speed measurably drops. This paragraph is set with no cap at all, and the difference is not subtle at this width.
Short-measure text can afford a larger size, because the line is short enough to return from.
Numerals — data is never proportional
Alignment in a column of figures is a typographic property, not a table one — so time, output, .ns-num and anything carrying [data-numeric] get tabular-nums in the base layer, everywhere, automatically.
Switzer 500–700 — display down to h4
Switzer 400 at 14px — the reading weight and size
Section eyebrows render as an Apex-style code comment, not a pastel pill
System mono in the Salesforce-navy console chrome
Text effects — the three rules
Everything below is decoration over text that already reads correctly without it. 1. The effect is never the meaning — struck text is a real <s>, a highlight is a real <mark>, a citation is a real link to a footnote that exists. 2. It draws once — these fire when scrolled into view and then stop; a thing that loops forever mid-paragraph is an ad. 3. It collapses under reduced motion — each effect's final state is the correct rendering, so flattening leaves a drawn highlight rather than an invisible one. Source: components/css/type-fx.css + assets/js/type-fx.js.
Highlight marks a phrase, never a paragraph — and four tints exist so one page can mark two different things: shipped, deprecated, removed, or filled solid.
One record at a time collections, always — the correction pattern: struck text goes muted so the eye lands on the live value.
Bulkify every trigger — one circled phrase per screen.
Governor limits RunLocalTests optional
Developer Console
Metadata deploy complete
<mark class="ns-mark ns-mark--animate">a highlighter marks a phrase</mark>
<s class="ns-strike ns-strike--animate ns-strike--muted">one record at a time</s>
<span class="ns-circle ns-circle--animate">every trigger<svg viewBox="0 0 220 60" preserveAspectRatio="none" aria-hidden="true"><path d="M28,10 C90,-2 200,2 210,22 …"/></svg></span>
<span class="ns-frame ns-frame--animate">Governor limits</span>
<h2 class="ns-scan ns-scan--lines">Developer Console</h2>
<h2 class="ns-scramble">Metadata deploy complete</h2>Responsive shift — why the scramble exists
The scrambler's real job is not the entrance; it is the break. When a breakpoint re-wraps a heading, the text normally teleports to its new position. assets/js/type-fx.js listens to the breakpoint media queries — not to resize, which would fire on every frame — and re-settles the characters, so a reflow reads as deliberate rather than as a jump. For the layout half of the same problem, .ns-shift-group hands the browser interpolate-size and transitions on font-size, line-height, letter-spacing and max-inline-size, and .ns-shift takes a view-transition-name. Both degrade to nothing where unsupported, which is the right failure mode for a nicety. Resize this page across 40rem or 64rem to watch the heading above re-settle.
Highlight, strike, circle, frame, scan and scramble — each drawn once, over text that already reads without it
Display and poster typography
Type at --size-mega is a graphic element, not a heading: it sets solid (--leading-mega, 0.94), tightens to --tracking-mega because tracking scales with the size it is set at, and is laid out against the viewport rather than a container.
Build onthe platform
One signal colour, two steps of it
The hairline is the structure. Everything else is negotiable.
Poster scale, outline, gradient, stacked, drop cap, pull-quote and the kinetic strip
Circular text, links, citations, anchors
Type on a curve, the three link treatments, footnote citations and heading anchors
Content design — how the product speaks
Voice is a typographic decision, which is why it lives here rather than on a page of its own: plain English, encouraging, practical; explain why, not just what; second person for instruction, third for the product. Sentence case everywhere except uppercase mono kickers and lesson-type badges — and uppercase is a two-or-three-word device, because a tracked uppercase sentence is measurably slower to read. No emoji in product copy. Numbers only when they are concrete.
Do
Don't
Voice, casing, and the exact words for errors, empty states and buttons
Accessibility — the typographic floor
Weight, measure and leading are accessibility properties before they are aesthetic ones. Reading copy is --weight-body (450) and never lighter; body leading is 1.65, above the 1.5 that WCAG 1.4.12 needs to survive a reader's own text-spacing overrides; every size is rem so browser zoom works; secondary ink clears 6.87:1 rather than sitting at the 4.5:1 line. The general component contract — focus, targets, motion, status colour — is on the Accessibility page.
Reading weight, measure, contrast, zoom, and what an effect may never be the only signal for