The short answer
Yes, DirtyStack has a genuine, deliberate typography system — three typefaces, defined in one place, wired to shared CSS variables, and applied with real discipline. Across five different page types I inspected live, only those three fonts ever render; there are no stray Arials, Inters, or random typefaces leaking in. That is rarer and better than most sites this age.
The catch is in the details, not the foundation. There are a handful of specific, fixable gaps in how the system is applied — and, most relevant to the “consistency across our branding” conversation, every written document that looks like a font guide is out of date and names fonts that were never shipped. This page is meant to become the accurate one.
The three faces
These are the real values — the families, roles, weights, and the CSS token each one lives behind. Everything you are reading on this page is set in these three fonts, so the specimens below are the fonts themselves, not pictures of them.
Find your seat.
Aa Gg 0123456789 $1/$3
DM Sans
Body and interface copy across the whole site — legible at every size on the dark UI, with room to breathe.
12 TABLES LIVE
$1/$3 NL · OPEN NOW · 0123456789
Scale & weight
The sizes below are the ones actually rendering on the live site today. Headlines run in Archivo; body and titles in DM Sans; labels and data in Roboto Mono, uppercase with wide tracking.
On the weight ladder, DM Sans ships five cuts — 400 / 500 / 600 / 700 / 800. Two facts worth knowing: the heaviest loaded cut (800) is currently never used, and big headings ask for 900, which isn’t loaded at all. More on both in section 05.
How it’s applied
The intended rule is simple and, for the most part, followed live. This is what actually rendered across the home, room, leaderboard, tournament, and blog pages I audited.
| Where | Face | Weight | Verified live on |
|---|---|---|---|
| Hero & page headlines, article titles | Archivo | 900 | Home, Room, Leaderboards, The Rail |
| Section titles | Archivo | 800 | Mixed — some fall back to DM Sans (see 05) |
| Card / list-item titles | DM Sans | 700 | Leaderboards, Room, Search |
| Body & description copy | DM Sans | 400 / 500 | Every page |
| Buttons & chips | DM Sans | 600 | Every page |
| Eyebrows, stat labels, clock, blinds | Roboto Mono | 400–700 | Every page |
Where it drifts
Four application gaps, in rough priority order. None is cosmetic-catastrophe territory — each is a small, self-contained decision-and-fix.
The written “font guide” is stale
Fix firstThree documents describe an older, different font set that never shipped. Anyone — you, your partner, a new developer, or an AI assistant — who opens one of these believing it is the guide will be working from the wrong fonts. This is the single most important thing to correct for a “brand consistency” conversation.
• Handoff/Foundations.html (“Design System — Proposal v1”) → Bricolage Grotesque / Hanken Grotesk / Space Mono
• README.md → “DM Sans, Space Mono, Instrument Serif”
• .cursor/rules/coding-standards.mdc → mono = Space Mono, plus an Instrument Serif accent
Fix · Adopt this page as the canonical guide; retire or replace Foundations.html and correct the two lines in README + the Cursor rule so they read Archivo / DM Sans / Roboto Mono.
Headings ask for a weight that isn’t loaded
MediumThe font-black class (weight 900) is used ~77 times, but DM Sans is only loaded up to 800. On Archivo headings this is fine (it’s a variable font with the full range). But on the ~45 places where font-black sits on plain DM Sans, the browser has no 900 cut to use, so it synthesizes a faux weight — slightly heavier and blurrier than a real cut, and it renders a touch differently across browsers.
e.g. components/ui/SectionHeader.tsx, StatCell.tsx, profile/*, room/*, several page titles. Also contradicts the project’s own coding-standard, which lists 400–800.
Fix · Either switch those to font-extrabold (800, already loaded and currently unused), or add the 900 cut to the DM Sans load. Pick one and apply it everywhere.
Section headings are split between two faces
MediumMost hero and page H1s correctly use the Archivo display face. But the shared section-title component (SectionHeader.tsx) and a few page and panel headings render in plain DM Sans instead — so “which headings are display vs. body” isn’t a settled rule. It reads fine, but it’s the kind of inconsistency that grows over time.
DM-Sans headings: ui/SectionHeader.tsx, tournament detail H1, /regions page, 404 page, some card h2–h3.
Fix · Decide the rule — e.g. “Archivo for every H1–H2, DM Sans only for card titles” — then make SectionHeader conform. One component change fixes most of it.
An undefined serif slips onto The Rail & Stacked
MediumTwo editorial subtitles use a font-serif italic flourish (“Stories from the rail…”, “Bad beat stories from the felt…”). But there is no serif in the system, so this falls through to whatever serif the visitor’s device happens to have — Georgia on a Mac, Times or Cambria on Windows. The one place we reach for a serif renders differently for every visitor.
The Rail page subtitle + Stacked section subtitle (font-serif italic, ~19px / 14.5px).
Fix · Either commit to the flourish and add a real editorial serif as a fourth brand font (loaded like the others), or drop it and use DM Sans italic. Right now it’s neither.
A loaded weight is dead payload
LowDM Sans 800 is downloaded on every page load but used zero times — headings jump straight to the (unloaded) 900. Tidy this up as part of the weight decision above: either 800 becomes the heading weight, or it stops being loaded.
The browser-tab favicon and Apple touch icon render in system-ui, not a brand font. That’s an unavoidable limitation of how those tiny PNGs are generated at build time — the brand fonts can’t be used there. It doesn’t affect anything on the actual pages. Noted only so it doesn’t look like a miss.
Recommendations
In the order I’d tackle them. The first is a doc cleanup; the two marked Decide are genuine calls for the two of you to make together before anything gets changed in code.
Make this the single source of truth
Adopt this page as the canonical DSP font guide. Replace the stale Handoff/Foundations.html and fix the font lines in README.md and the Cursor coding-standard so nothing points at the old Bricolage / Hanken / Space Mono / Instrument Serif set again.
Decide the heading rule Decide together
Is Archivo the face for all headings, or only page-level H1s with DM Sans for section and card titles? Either is defensible — but pick one, then make the shared SectionHeader component match it.
Decide the serif question Decide together
Do we want an editorial serif voice on The Rail / Stacked at all? If yes, choose one and load it as a proper fourth brand font. If no, switch those subtitles to DM Sans italic. Today it renders as a random system serif — the one genuine cross-device inconsistency.
Settle the heading weight
Standardise heavy headings on a weight that’s actually loaded — move the font-black (900) usages on DM Sans to font-extrabold (800), or add the 900 cut to the load. This clears the faux-weight rendering and the unused-800 payload in one pass.
Copy-paste values
The exact strings, for handoff to anyone building against the brand.
/* Display — hero & page headlines, article titles */ --font-display: "Archivo", "Archivo Fallback", "DM Sans", ui-sans-serif, system-ui, sans-serif; // via .font-display-head → font-variation-settings: "wdth" 118; letter-spacing: -0.015em; /* Body & UI — the <body> default */ --font-sans: "DM Sans", "DM Sans Fallback", ui-sans-serif, system-ui, sans-serif; // loaded weights: 400, 500, 600, 700, 800 /* Data & labels — clock, blinds, stats, eyebrows */ --font-mono: "Roboto Mono", "Roboto Mono Fallback", ui-monospace, monospace; // loaded weights: 400, 500, 600, 700