TechLead

Typography and Text Styling

Fonts, sizing, spacing, and readable text

CSS typography controls everything about how text looks and feels. Good typographic choices are the foundation of readable, professional interfaces — and CSS gives you fine-grained control over fonts, spacing, sizing, and rendering.

Loading Custom Fonts with @font-face

@font-face {
  font-family: 'Inter';
  src:
    url('/fonts/Inter.woff2') format('woff2'),
    url('/fonts/Inter.woff')  format('woff');
  font-weight: 100 900;   /* variable font weight range */
  font-style: normal;
  font-display: swap;      /* show fallback immediately; swap when loaded */
}

font-display: swap prevents invisible text during load (FOIT). optional is even more conservative — it never causes a layout shift but may not show the custom font on slow connections.

Variable Fonts

A variable font encodes a whole family in a single file with axes you can animate smoothly in CSS — weight, width, slant, optical size, and custom axes.

body { font-family: 'Inter', system-ui, sans-serif; }

.heading { font-variation-settings: 'wght' 700, 'slnt' -5; }

/* Animate weight on hover */
.link {
  font-weight: 400;
  transition: font-weight 0.2s ease;
}
.link:hover { font-weight: 600; }

Fluid Typography with clamp()

clamp(min, preferred, max) creates type that scales fluidly with the viewport — no media query breakpoints needed for font sizes.

/* Scales from 1rem at 320px to 1.5rem at 1200px */
h1 { font-size: clamp(1.75rem, 4vw + 0.5rem, 3rem); }
p  { font-size: clamp(1rem,    1vw + 0.75rem, 1.25rem); }

/* Line height should scale with font size */
body { line-height: clamp(1.4, 1.2 + 0.5vw, 1.7); }

The Core Text Properties

.article {
  font-size: 1.125rem;       /* ~18px — more readable than 16px for long text */
  line-height: 1.7;          /* 1.5–1.8 for body text */
  font-weight: 400;
  letter-spacing: -0.01em;   /* slightly tighter for large headings */
  word-spacing: 0.05em;
  max-width: 65ch;           /* ch unit = width of '0', ideal for line length */
  text-wrap: pretty;         /* Chrome 117+ — avoids orphaned last words */
}

.heading {
  letter-spacing: -0.03em;
  text-wrap: balance;        /* balances line breaks in headings */
}

Responsive Text Truncation

/* Single-line truncation */
.title {
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* Multi-line clamp (WebKit, now widely supported) */
.excerpt {
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 3;
  overflow: hidden;
}

Font Stack Best Practices

/* System font stack — no network request, native feel */
body {
  font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI',
    Roboto, Helvetica, Arial, sans-serif;
}

/* Monospace for code */
code, pre {
  font-family: 'JetBrains Mono', ui-monospace, 'Cascadia Code',
    'Source Code Pro', Menlo, monospace;
  font-feature-settings: 'liga' 1, 'calt' 1; /* enable ligatures */
}

OpenType Features

.price {
  font-variant-numeric: tabular-nums;  /* columns align correctly */
  font-feature-settings: 'tnum' 1;
}

.small-caps {
  font-variant-caps: small-caps;       /* true small caps, not scaled */
}

.ordinal {
  font-variant-numeric: ordinal;       /* 1st, 2nd, 3rd */
}

Best Practices

  • Limit custom font weights to those you actually use — each is a separate network request
  • Preload critical fonts: <link rel="preload" as="font" crossorigin>
  • Use rem for font sizes so users can scale with browser zoom
  • Target 45–75 characters per line (max-width: 65ch) for optimal readability
  • Ensure body text colour meets WCAG AA contrast (4.5:1) against the background

Continue Learning