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) */
}
(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;
}
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";
}
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
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");
}
Variable fonts give you:
- one file instead of 4 - 6 separate weight files;
- smaller total size: significant smaller than multiple static fonts;
- smooth weight transitions; you can use
font-weight: 450(instead of the fixed increments of 100) or animate between weights (fancy!); - 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. */
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;
}
}
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;
}
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 (or Rails-based SSG)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. đ
đŹ Over to youâŚ
What did you like about this article? Learned something knew? Found something is missing or even broken? 𫣠Let me (and others) know!
Comments are powered by Chirp Form
{{comment}}