| name | fira-sans-typography |
| description | Typography system using Fira Sans. Apply when building web UIs, design systems, or any artifact requiring consistent type. Covers font loading, typescale, spacing rules, and HTML element mapping. |
Fira Sans Typography
Font Loading
Source: Mozilla Fira Sans — use .otf or .woff2 files from the repo for self-hosting.
Priority order: Self-hosted (own CDN/server/GitHub) → Google Fonts fallback
Self-hosted (preferred — full weight control + OpenType features)
Download .otf or .woff2 files from the GitHub repo and serve from your own CDN or server.
@font-face {
font-family: 'Fira Sans';
src: url('/fonts/FiraSans-Regular.otf') format('opentype');
font-weight: 400;
font-display: swap;
}
@font-face {
font-family: 'Fira Sans';
src: url('/fonts/FiraSans-Medium.otf') format('opentype');
font-weight: 500;
font-display: swap;
}
@font-face {
font-family: 'Fira Sans';
src: url('/fonts/FiraSans-SemiBold.otf') format('opentype');
font-weight: 600;
font-display: swap;
}
Load 400 and 500 by default. Add 300 (light) for lead text if needed, or 600 (semibold) for strong emphasis. Avoid loading more than 3 weights.
Google Fonts (fallback — limited OpenType control)
<link rel="preconnect" href="https://fonts.googleapis.com">
<link href="https://fonts.googleapis.com/css2?family=Fira+Sans:wght@400;500&display=swap" rel="stylesheet">
Fallback stack: 'Fira Sans', ui-sans-serif, system-ui, -apple-system, sans-serif
Typescale & Element Mapping
Scale is defined in rem with calc() for fluid adjustments. Base 1rem = 16px assumed. Mobile-first — larger screens adjust up.
:root {
--font-sans: 'Fira Sans', ui-sans-serif, system-ui, -apple-system, sans-serif;
--text-xs: 0.75rem;
--text-sm: 0.875rem;
--text-base: 1rem;
--text-md: 1.125rem;
--text-lg: 1.25rem;
--text-xl: 1.5rem;
--text-2xl: 2rem;
--text-3xl: 2.5rem;
--text-4xl: 2.75rem;
--weight-light: 300;
--weight-regular: 400;
--weight-medium: 500;
--weight-semibold: 600;
}
body {
font-family: var(--font-sans);
font-size: var(--text-base);
font-weight: var(--weight-regular);
line-height: 1.5em;
letter-spacing: 0;
}
@media (min-width: 640px) { body { line-height: 1.55em; } }
@media (min-width: 1024px) { body { line-height: 1.6em; } }
h1 {
font-size: var(--text-2xl);
font-weight: var(--weight-medium);
line-height: 1.2em;
letter-spacing: -0.0175em;
}
@media (min-width: 768px) {
h1 { font-size: var(--text-4xl); letter-spacing: -0.0175em; }
}
h2 {
font-size: 1.75rem;
font-weight: var(--weight-medium);
line-height: 1.2em;
letter-spacing: 0;
}
@media (min-width: 768px) {
h2 { font-size: var(--text-3xl); }
}
h3 {
font-size: var(--text-lg);
font-weight: var(--weight-medium);
line-height: 1.3em;
letter-spacing: 0.0035em;
}
@media (min-width: 768px) {
h3 { font-size: var(--text-xl); }
}
h4 {
font-size: var(--text-md);
font-weight: var(--weight-medium);
line-height: 1.35em;
letter-spacing: 0;
}
h5 {
font-size: var(--text-base);
font-weight: var(--weight-medium);
line-height: 1.4em;
letter-spacing: 0;
}
h6 {
font-size: var(--text-sm);
font-weight: var(--weight-medium);
line-height: 1.4em;
letter-spacing: 0;
text-transform: uppercase;
letter-spacing: 0.04em;
}
p {
font-size: var(--text-base);
line-height: 1.5em;
letter-spacing: 0;
}
.lead {
font-size: var(--text-md);
font-weight: var(--weight-light);
line-height: 1.5em;
}
@media (min-width: 768px) {
.lead { font-size: var(--text-lg); }
}
small {
font-size: var(--text-sm);
line-height: 1.4em;
}
figcaption {
font-size: var(--text-xs);
line-height: 1.4em;
opacity: 0.7;
}
button, .btn {
font-family: var(--font-sans);
font-size: var(--text-base);
font-weight: var(--weight-medium);
line-height: 1.6em;
letter-spacing: 0;
}
input, select, textarea {
font-family: var(--font-sans);
font-size: var(--text-base);
font-weight: var(--weight-regular);
line-height: 1.4em;
letter-spacing: 0;
}
label {
font-size: var(--text-sm);
font-weight: var(--weight-regular);
line-height: 1.2em;
letter-spacing: 0.042em;
text-transform: uppercase;
}
@media (max-width: 767px) {
label { font-size: var(--text-xs); }
}
.overline, .preheader {
font-size: var(--text-base);
font-weight: var(--weight-regular);
line-height: 1.6em;
letter-spacing: 0.109em;
text-transform: uppercase;
}
@media (max-width: 767px) {
.overline, .preheader { font-size: var(--text-sm); }
}
code, pre {
font-family: 'Fira Mono', ui-monospace, monospace;
font-size: var(--text-sm);
}
Rules (quick reference)
| Concern | Rule |
|---|
| Body range | 16px base; never below on mobile for core reading text |
| Minimum size | 12px (0.75rem) — for labels/captions only |
| Letter-spacing | Zero for most text; slight negative (-0.0175em) for large headlines (44px); positive (0.04–0.11em) for uppercase labels/overlines |
| Line-height unit | Use em — tracks element's own font-size |
| Line-height range | 1.2em (single-line headings/UI) → 1.6em (paragraphs on wide viewports) |
| All caps | Use sparingly; when needed, add letter-spacing: 0.04–0.11em depending on size |
| Default weights | Load 400 and 500 only; add 300 for lead text or 600 for strong emphasis if design requires |
| Weight mixing | Regular (400) for body; medium (500) for headings/buttons; light (300) for lead paragraphs |
| Headlines | Medium (500) by default; avoid mixing medium + semibold at same hierarchy level |
| Mobile-first | Base = mobile (16px body); override up at 640px, 768px, 1024px |
| Fira Sans character | Humanist sans with excellent legibility; slightly wider letterforms than geometric sans — comfortable at smaller sizes |
For full desktop + mobile scale table with exact values → TYPESCALE.md
For OpenType features and numeric styles → OPENTYPE.md
For Tailwind configuration and examples → TAILWIND.md