DEV Community

Rails Designer
Rails Designer

Posted on • Originally published at railsdesiger.com

Typography for Rails developers

This article was originally published on Rails Designer


Using professional typography in your Rails app makes the difference between something looking okay to really professional. There is lots of info about this topic around, but less so specifically for Rails developers building apps or Rails-based SSG.

But first: while most people say “fonts”, the correct term is actually “typeface”. A typeface is the design (like Inter or Helvetica), while a font is a specific implementation (like Inter Bold 16px).

Difference between font and typeface

A typeface is the overall design of a set of characters, like a family name. Examples include: Helvetica, Inter and San Francisco.

A font is a specific implementation or variation of a typeface, like size (12px), weight (bold) and style (italic). Examples would be Helvetica Bold 12px, Inter italic 14px and San Francisco Extra bold 18px.

Most people (including designers) will understand what you mean when using fonts. But if you want to impress your designer friends, know when to use either term for extra points! 🥇

Basics of font files

Modern web fonts come in several formats, but most are legacy formats you shouldn't use anymore:

  • TTF/OTF - Desktop formats, too large for web use
  • WOFF - Compressed web format, but outdated
  • WOFF2 - Modern compressed format, ~30% smaller than WOFF

Use WOFF2 exclusively. All modern browsers support it, and the file size savings are significant. As Jono Alderson explains (fun article as he goes over some history I have fond memories off), you're likely loading fonts wrong if you're not using WOFF2 with proper optimization.

Professional typefaces like Inter, Source Sans Pro, and system fonts support OpenType features through CSS font-feature-settings. These unlock typography features like ligatures, kerning, and tabular numbers:

body {
  font-feature-settings:
    "liga" 1,    /* ligatures (fi, fl combinations) */
    "kern" 1,    /* kerning (letter spacing) */
    "tnum" 1;    /* tabular numbers (same width) */
}
Enter fullscreen mode Exit fullscreen mode

(let me know if you want me to cover typography terms like ligatures and kerning in another article!)

Default stack

Before diving into custom fonts, let's talk about system fonts. The modern approach uses CSS keywords that map to each platform's default fonts:

body {
  font-family: system-ui, sans-serif;
}
Enter fullscreen mode Exit fullscreen mode

The system-ui keyword automatically uses:

  • San Francisco on macOS/iOS;
  • Segoe UI on Windows;
  • Roboto on Android;
  • Ubuntu on Linux.

For more control, you can use the full system font stack:

body {
  font-family: ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
}
Enter fullscreen mode Exit fullscreen mode

The newer CSS keywords are:

  • ui-sans-serif: system's default sans-serif UI font;
  • ui-serif: system's default serif UI font;
  • ui-monospace: system's default monospace UI font.

These provide excellent performance since they're already installed, but limit your design flexibility.

Picking a professional typeface

When system fonts aren't enough, Google Fonts (included some filtering options for you!) is the obvious starting point. For privacy-conscious developers, Bunny Fonts (use the same filtering options as I added to the Google Fonts link for professional font files) provides the same fonts without Google tracking.

What to look for in a professional typeface:

  • Larger x-height: the height of lowercase letters relative to capitals. Fonts with larger x-heights (like Inter, Open Sans) are more readable at smaller sizes, especially on screens;
  • Sans-serif for UI: serif fonts work great for long-form reading, but sans-serif fonts are cleaner for interfaces, buttons, and navigation;
  • Multiple weights: professional fonts offer 400 (regular), 500 (medium), 600 (semi-bold), and 700 (bold). This gives you hierarchy without changing typefaces;
  • Combining fonts: stick to one typeface family for UI elements. If you need contrast, like for a blog, pair a sans-serif (headings/UI) with a serif (body text), but keep it simple. More fonts = more loading time and visual chaos.

Popular professional choices:

  • Inter;
  • Source Sans Pro;
  • Poppins;
  • Lato.

Use a custom typeface in Rails

Rails makes font loading straightforward. Place your WOFF2 files in app/assets/fonts/:

app/assets/fonts/
├── inter-regular.woff2
├── inter-medium.woff2
├── inter-semibold.woff2
└── inter-bold.woff2
Enter fullscreen mode Exit fullscreen mode

Don't use CDNs for fonts. Self-hosting eliminates external requests and gives you full control over loading behavior. But if you need to, do not use Google.

Variable fonts are the modern approach. Instead of multiple static files, use one variable font file that contains all weights:

@font-face {
  font-family: "Inter";
  font-style: normal;
  font-display: swap;
  font-weight: 100 900;
  src: font-url("Inter-VariableFont_wght.woff2") format("woff2-variations");
}

@font-face {
  font-family: "Inter";
  font-style: italic;
  font-display: swap;
  font-weight: 100 900;
  src: font-url("Inter-Italic-VariableFont_wght.woff2") format("woff2-variations");
}
Enter fullscreen mode Exit fullscreen mode

Variable fonts give you:

  • One file instead of 4-6 separate weight files
  • Smaller total size - Often 50-70% smaller than multiple static fonts
  • Smooth weight transitions - You can use font-weight: 450 or animate between weights
  • Better performance - Fewer HTTP requests and faster loading

Check out this page how that works for the Inter font.

If variable fonts aren't available, fall back to static fonts with proper weight mapping:

@font-face {
  font-family: 'Inter';
  src: url('inter-regular.woff2') format('woff2');
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}

@font-face {
  font-family: 'Inter';
  src: url('inter-medium.woff2') format('woff2');
  font-weight: 500;
  font-style: normal;
  font-display: swap;
}

/* etc. */
Enter fullscreen mode Exit fullscreen mode

If you're using Tailwind CSS, the cleanest approach uses CSS custom properties:

@layer base {
  :root {
    --font-sans: "Inter", ui-sans-serif, system-ui, sans-serif;
    --font-serif: "Crimson Pro", ui-serif, Georgia, serif;
    --font-mono: "JetBrains Mono", ui-monospace, monospace;
  }
}
Enter fullscreen mode Exit fullscreen mode

This automatically maps to Tailwind's font-sans, font-serif, and font-mono classes without additional configuration.

For vanilla CSS, apply your font with fallbacks:

body {
  font-family: 'Inter', ui-sans-serif, system-ui, sans-serif;
}
Enter fullscreen mode Exit fullscreen mode

Use font-display: swap for most cases. This shows fallback text immediately, then swaps to your custom font when loaded. Avoid font-display: block which creates invisible text during loading.

For advanced optimization, reference this article for using size-adjust, ascent-override and descent-override to minimize layout shift when fonts swap.

And there you have it. Professional typography that makes your Rails app look polished and trustworthy. The difference between system fonts and a well-chosen custom typeface is immediately noticeable to users, even if they can't articulate why your app suddenly feels more premium. 😊

Top comments (0)