Pointegrity
Journal

One type system for app and content: why we picked IBM Plex

Published · Back to journal

Typography is one of those decisions where the defaults are good enough and the temptation to overthink is strong. We overthought it. The reason was not aesthetic vanity — it was that pouch spans more content types than most SaaS products, and no single default typeface handled all of them well.

This post is set in IBM Plex Serif. The UI of pouch renders in Plex Sans. Code snippets throughout our docs are in Plex Mono. All three are designed together, and that turns out to matter more than it sounds.

The three contexts we had to serve

A pouch user encounters text in at least three registers:

A product that plans to grow into a content layer (we do) and a developer-friendly CLI ecosystem (we do) needs to answer all three questions coherently from the start.

The three options we considered

System fonts (system-ui / SF Pro / Segoe UI / Roboto)

System stacks are the boring right answer for a CSS library. Zero network cost, platform-native, no foundries to pay, no GDPR footnote about third-party font loading. We shipped the site on system-ui for a week and everything worked. But two problems emerged.

First: no brand signal. Pointegrity looked like every other site built on a default design system. For a product that positions itself on trust and sovereignty, being visually interchangeable with everyone else is a small but real loss.

Second: no coordinated serif or mono. When we wanted to render long-form prose (this journal), our only serif options were Georgia or Cambria — general-purpose system serifs that didn't sit alongside the sans we were using anywhere. The compositions felt like unrelated families stapled together.

Inter (+ JetBrains Mono)

Inter is the default indie-SaaS choice for good reason. Clean, modern, readable at all sizes, neutral. Paired with JetBrains Mono for code, you get a coherent enough developer-product voice. Linear, Vercel, Stripe, GitHub all use Inter or siblings.

The problem was Inter's lack of a matching serif. If we committed to Inter now, we'd need to pick a second typeface later when long-form content arrived — and pairing typefaces well is harder than picking one family. The deferred decision was more expensive than we wanted.

IBM Plex

Plex ships as a coordinated trio: Sans for UI, Serif for editorial, Mono for code. They were designed together, so mixing them on one page works without clashing. IBM released Plex under the SIL Open Font License in 2018, which removed the licensing uncertainty.

The practical win: we make one typography decision, once, and it answers all three contexts. The UI uses Plex Sans. A long post like this renders in Plex Serif (you're reading it now). A code snippet below —

async function promote(drop) {
  const res = await api.patch(`/api/items/${drop.id}`, {
    stream: 'kept',
  });
  if (res.ok) toast('moved to kept');
}

— picks up Plex Mono automatically, and you can tell from the letterforms that it belongs to the same family. No visual hiccup when the eye moves from prose to code.

What it cost us

Honestly: ~180 KB of woff2 files, self-hosted, loaded from our own static directory. We serve the Latin and Latin-Extended subsets for Sans (four weights), Serif (three weights), and Mono (two weights). Each weight is ~15-25 KB gzipped. The browser lazy-loads via unicode-range declarations in our @font-face rules, so most page loads only fetch the subset they need.

In concrete terms, the bundle cost is small enough that we consider it essentially free, and self-hosting means we don't send a Google Fonts request that would otherwise put a Google dependency on every Pointegrity page. For a privacy-positioned product, that's the right trade.

One thing we learned the hard way

Our logo wordmark is an SVG. Early versions used <text> elements with font-family: 'Inter', expecting the browser to pick the right font. This silently broke on any system without Inter installed — the text shifted by a few pixels, which pushed the red accent-dot off-center relative to the 'i' stem.

The fix was to extract the actual glyph paths from the woff2 file via fontTools and bake them into the SVG. No more font-family dependency at render time; the SVG is now self-contained. This is obvious in hindsight for any production-shipped logo, but it took a bug report to appreciate. If you're shipping SVG marks with live <text>, convert to paths before public release.

What this decision closes off

Picking Plex means we're not picking:

The one-line summary

Pick a type system that can handle every surface you'll ship in the next two years, not just the ones you have today.

Pouch will eventually have a CLI reference, a tutorial section, an API docs surface, and probably a blog bigger than this one. Having Plex Sans + Serif + Mono sitting there ready means those additions cost design time, not typography time. That's the compounding return.

← All posts