What is CSS Typography?
CSS typography refers to the art and science of styling text on the web using Cascading Style Sheets. It encompasses everything from font selection and sizing to line height, letter spacing, and text alignment. At its core, CSS typography is about using CSS properties to control how text appears on a webpage—making it readable, visually appealing, and accessible.
The fundamental CSS properties that govern typography include:
font-family— the typeface or font stack usedfont-size— the size of the textfont-weight— the thickness or boldness of charactersline-height— the vertical space between lines of textletter-spacing— the horizontal space between individual characterstext-align— the horizontal alignment of text blockscolor— the text color and its contrast against backgroundstext-transform— casing control like uppercase or lowercasefont-style— italic or oblique variants
Together, these properties form the foundation of web typography. Mastering them allows developers to create text that is not only beautiful but also functional and inclusive for all users.
Why CSS Typography Matters
🚀 Deploy your AI agent in 10 minutes
Managed Hermes hosting. Zero DevOps. 100M tokens/mo included.
Try it free →Typography is the backbone of web communication. The majority of web content is text—headlines, paragraphs, navigation labels, buttons, and form inputs. Poor typography can ruin even the most beautifully designed layout, while excellent typography elevates user experience in profound ways. Here's why it matters:
Readability and User Experience
Well-styled text reduces eye strain and cognitive load. Proper line height, adequate contrast, and comfortable font sizing allow users to read content effortlessly. Studies show that optimal line length (around 45–75 characters per line) and sufficient line spacing significantly improve reading speed and comprehension.
Accessibility
Typography directly impacts accessibility for users with visual impairments, dyslexia, or other reading difficulties. WCAG (Web Content Accessibility Guidelines) mandates minimum contrast ratios, and thoughtful typographic choices—like avoiding tiny font sizes and allowing text resizing—make content usable for everyone.
Branding and Visual Identity
Typography conveys personality. A serif font like Georgia feels traditional and authoritative; a geometric sans-serif like Futura feels modern and clean. Consistent typography reinforces brand identity across all touchpoints.
Performance and Scalability
Modern CSS typography techniques—like using system fonts or variable fonts—can reduce page weight while maintaining design fidelity. Efficient font loading strategies prevent layout shifts and improve perceived performance.
How to Use CSS Typography: Core Properties in Depth
1. Building a Robust Font Stack
The font-family property accepts a prioritized list of font names, ending with a generic family keyword. This ensures graceful fallback when a specified font isn't available on the user's system.
/* A classic font stack with fallbacks */
body {
font-family: "Inter", "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
}
/* A serif stack for long-form reading */
article {
font-family: Georgia, "Times New Roman", Times, serif;
}
/* A monospace stack for code blocks */
pre, code {
font-family: "JetBrains Mono", "Fira Code", "Cascadia Code", Consolas, monospace;
}
Best practice: Always end your font stack with a generic family keyword (serif, sans-serif, monospace, cursive, or fantasy). This guarantees the browser can render text even if all specified fonts fail to load.
2. Setting Font Size Responsibly
Font size can be set in pixels, ems, rems, percentages, or viewport units. Each unit serves different purposes.
/* Base size using a relative unit — preferred for accessibility */
html {
font-size: 100%; /* equals 16px in most browsers */
}
/* Body text scales relative to the root */
body {
font-size: 1rem; /* 16px */
}
/* Headings use a modular scale */
h1 { font-size: 2.5rem; } /* 40px */
h2 { font-size: 2rem; } /* 32px */
h3 { font-size: 1.75rem; } /* 28px */
h4 { font-size: 1.25rem; } /* 20px */
/* Small text for captions */
.caption {
font-size: 0.875rem; /* 14px */
}
Best practice: Use rem units for consistent scaling. Avoid pixel values for body text, as they prevent users from resizing text in some browsers. Set the root font-size as a percentage to respect user preferences.
3. Mastering Line Height
Line height is arguably the single most impactful property for readability. It controls the vertical rhythm of text and should be set without units for best inheritance behavior.
/* Unitless line-height is preferred — it multiplies the element's font-size */
body {
line-height: 1.6; /* 1.6 × font-size */
}
/* Tighter line-height for headings */
h1, h2, h3 {
line-height: 1.2;
}
/* Looser line-height for long-form reading */
.article-body p {
line-height: 1.75;
max-width: 65ch; /* limit line length to ~65 characters */
}
/* Very tight line-height for single-line UI elements */
.button {
line-height: 1;
}
Best practice: Always use unitless line-height values (e.g., 1.6, not 1.6em). This ensures child elements inherit a multiplier relative to their own font size, preventing awkward spacing when nested elements have different font sizes.
4. Font Weight and Style Control
Font weight ranges from thin (100) to black (900), with 400 being the default "normal" weight and 700 being "bold." Modern variable fonts support arbitrary weight values.
/* Standard weight declarations */
h1 {
font-weight: 700; /* bold */
}
.subtitle {
font-weight: 300; /* light */
}
/* Variable font weight — requires a variable font file */
@font-face {
font-family: 'InterVariable';
src: url('/fonts/InterVariable.woff2') format('woff2-variations');
font-weight: 100 900;
}
.variable-text {
font-family: 'InterVariable', sans-serif;
font-weight: 450; /* a custom weight between normal and bold */
font-style: normal;
}
Best practice: Map font weights to semantic roles: bold for emphasis, light for subtle secondary text, and regular for body copy. With variable fonts, you can fine-tune weight for perfect visual harmony.
5. Letter Spacing and Word Spacing
Adjusting spacing between characters and words can dramatically affect texture and legibility. Use sparingly—default spacing is usually optimal for body text.
/* Slightly open tracking for uppercase text */
.uppercase-heading {
text-transform: uppercase;
letter-spacing: 0.05em; /* 5% of font-size */
}
/* Tight tracking for large display text */
.hero-title {
font-size: 4rem;
letter-spacing: -0.02em;
}
/* Normal body text needs no adjustment */
p {
letter-spacing: normal; /* the default */
word-spacing: normal;
}
Best practice: Apply positive letter-spacing to uppercase text for improved legibility. Use negative letter-spacing only on very large headings where character spacing feels too loose. Always use em or rem units so spacing scales with font size.
6. Text Alignment and Justification
Alignment affects the visual flow of text blocks. Left-aligned text (the default in Western languages) provides a consistent reading edge. Centered text works for short headlines but hinders reading long paragraphs.
/* Default left alignment for body text */
p {
text-align: left;
}
/* Centered alignment for hero sections */
.hero h1 {
text-align: center;
}
/* Right alignment for data tables and numbers */
td.numeric {
text-align: right;
}
/* Justified text with careful hyphenation control */
article.justified {
text-align: justify;
hyphens: auto;
hyphenate-limit-chars: 8 4 4; /* min word length, min before/after break */
}
Best practice: Avoid justified text on the web unless you can control hyphenation and line length. Ragged-right (left-aligned) text is more forgiving of varying word lengths and prevents awkward "rivers" of whitespace.
Advanced CSS Typography Techniques
7. Web Font Loading Strategies
Custom web fonts enhance design but can cause performance issues. Use the @font-face rule with modern formats and implement a font loading strategy to prevent layout shifts.
/* Modern @font-face declaration with multiple formats */
@font-face {
font-family: 'CustomSerif';
src: url('/fonts/CustomSerif.woff2') format('woff2'),
url('/fonts/CustomSerif.woff') format('woff');
font-weight: 400;
font-style: normal;
font-display: swap; /* render fallback immediately, swap when font loads */
}
/* Apply with a fallback that matches metrics */
body {
font-family: 'CustomSerif', Georgia, serif;
}
/* Optional: fine-tune fallback metrics */
@font-face {
font-family: 'CustomSerifFallback';
src: local('Georgia');
size-adjust: 105%; /* adjust fallback to match custom font's x-height */
ascent-override: 90%;
}
Best practice: Use font-display: swap to show text immediately in a fallback font. Consider using size-adjust and metric overrides to minimize layout shift when the custom font loads. Prefer woff2 format for its superior compression.
8. Variable Fonts for Dynamic Typography
Variable fonts pack multiple axes (weight, width, slant, optical size) into a single font file, enabling fluid transitions and reduced file sizes.
/* Register a variable font with multiple axes */
@font-face {
font-family: 'Recursive';
src: url('/fonts/Recursive.woff2') format('woff2-variations');
font-weight: 300 900;
font-style: oblique 0deg 15deg;
font-display: swap;
}
/* Use the axes creatively */
.dynamic-heading {
font-family: 'Recursive', sans-serif;
font-weight: 750;
font-style: oblique 8deg;
font-variation-settings: 'CASL' 0.5, /* casual axis */
'CRSV' 1, /* cursive axis */
'MONO' 0; /* proportional spacing */
}
/* Fluid weight based on viewport */
.fluid-title {
font-family: 'Recursive', sans-serif;
font-weight: clamp(400, 5vw, 900);
}
Best practice: Explore variable fonts for creative flexibility. Use clamp() for fluid typography that responds to viewport changes. Always provide a reasonable fallback for browsers that don't support variable fonts.
9. Responsive Typography with clamp()
The clamp() function creates fluid typography that scales seamlessly between a minimum and maximum size based on viewport width—no media queries needed.
/* Fluid heading that scales between 2rem and 5rem */
h1 {
font-size: clamp(2rem, 1rem + 4vw, 5rem);
line-height: 1.1;
}
/* Fluid body text */
body {
font-size: clamp(1rem, 0.5rem + 1vw, 1.25rem);
}
/* Fluid line-height can also work */
p {
line-height: clamp(1.5, 1.2 + 0.5vw, 1.8);
}
/* Combine with container queries for component-level control */
@container (min-width: 400px) {
.card-title {
font-size: clamp(1.25rem, 0.5rem + 3cqi, 2rem);
}
}
Best practice: Use clamp() for headings and hero text where dramatic scaling adds visual impact. For body text, keep the range modest (e.g., 1rem to 1.25rem) to maintain readability across devices.
CSS Typography Best Practices: A Comprehensive Checklist
10. Establish a Type Scale
A consistent type scale creates visual hierarchy and rhythm. Define a set of font sizes based on a modular scale rather than arbitrary values.
/* A modular type scale (1.25 ratio — "Major Third") */
:root {
--text-xs: 0.75rem; /* 12px */
--text-sm: 0.875rem; /* 14px */
--text-base: 1rem; /* 16px */
--text-md: 1.25rem; /* 20px */
--text-lg: 1.563rem; /* 25px */
--text-xl: 1.953rem; /* 31px */
--text-2xl: 2.441rem; /* 39px */
--text-3xl: 3.052rem; /* 49px */
}
h1 { font-size: var(--text-3xl); }
h2 { font-size: var(--text-2xl); }
h3 { font-size: var(--text-xl); }
h4 { font-size: var(--text-lg); }
p { font-size: var(--text-base); }
small, .caption { font-size: var(--text-sm); }
11. Ensure Sufficient Color Contrast
WCAG AA requires a contrast ratio of at least 4.5:1 for normal text and 3:1 for large text (18px+ or 14px+ bold). Use tools like the WebAIM contrast checker to verify your color combinations.
/* High contrast body text */
body {
color: #1a1a1a; /* near-black */
background-color: #ffffff; /* white — contrast ratio ~17:1 */
}
/* Lower contrast for secondary text — still meets AA for large text */
.secondary-text {
color: #5a5a5a; /* medium gray on white — ratio ~5.5:1 */
font-size: 1.125rem; /* 18px qualifies as large text */
}
/* Danger: insufficient contrast — avoid this */
/* color: #999 on #fff = ratio ~2.6:1 — fails WCAG AA */
Best practice: Never rely solely on color to convey information. Pair color cues with underlines, icons, or bold text for users with color vision deficiencies.
12. Control Measure (Line Length)
The "measure" is the width of a text block. Optimal reading occurs at 45–75 characters per line. Use ch units or max-width to constrain text blocks.
/* Constrain paragraph width to optimal reading length */
p, li, blockquote {
max-width: 65ch; /* 65 characters wide */
}
/* Wider measure for larger text */
h1, h2 {
max-width: 40ch;
}
/* Full-width containers override the measure */
.full-width-text {
max-width: none;
}
/* Combine with responsive adjustments */
@media (min-width: 768px) {
p {
max-width: 70ch;
}
}
Best practice: Apply measure constraints to body text and list items. Headings can have shorter measures. Always test on real content—some languages have longer average word lengths.
13. Handle Text Overflow Gracefully
Long words, URLs, or narrow containers can break layouts. CSS provides overflow handling properties to manage these edge cases.
/* Truncate long single-line text with an ellipsis */
.truncate {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
max-width: 300px;
}
/* Allow breaking within long words */
.break-all {
word-break: break-all; /* breaks anywhere */
overflow-wrap: break-word; /* prefers whole-word breaks */
}
/* Prevent orphans (single words on last line) */
p {
text-wrap: pretty; /* newer property for balanced wrapping */
}
/* For URLs and code snippets */
.url-text {
word-break: break-all;
hyphens: none;
}
Best practice: Use overflow-wrap: break-word over word-break: break-all for general content—it preserves word integrity where possible. Reserve truncation for UI components like card titles or navigation items where space is fixed.
14. Respect User Preferences
Users may have system-wide font preferences for accessibility or comfort. CSS provides mechanisms to honor these choices.
/* Respect user's system font preferences */
body {
font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
}
/* Respond to user's contrast preference */
@media (prefers-contrast: high) {
body {
color: #000;
background: #fff;
}
a {
text-decoration: underline;
text-underline-offset: 3px;
}
}
/* Respond to reduced motion for text animations */
@media (prefers-reduced-motion: reduce) {
.animated-text {
animation: none;
transition: none;
}
}
Best practice: Use system-ui in your font stack to pick up the user's operating system font automatically. This provides a native feel and eliminates font downloads.
15. Combine Typography with Vertical Rhythm
Vertical rhythm aligns text to a consistent baseline grid, creating a harmonious flow down the page. Combine line-height, margin, and padding to maintain the rhythm.
/* Establish a baseline rhythm unit */
:root {
--baseline: 1.5rem; /* 24px */
}
body {
line-height: var(--baseline);
font-size: 1rem;
}
/* Margins align to the baseline grid */
h2 {
margin-top: calc(var(--baseline) * 2); /* 48px */
margin-bottom: var(--baseline); /* 24px */
}
p {
margin-bottom: var(--baseline);
}
/* Lists align with the grid */
ul {
margin-bottom: var(--baseline);
padding-left: 1.5rem;
}
Best practice: Use a baseline unit (typically 1.5rem or 24px) and ensure all vertical spacing—margins, padding, line-height—are multiples of that unit. This creates invisible horizontal lines that text consistently sits upon.
16. Optimize for Dark Mode
Typography must adapt when users switch to dark mode. Text that looks crisp on a light background can appear fuzzy or overly bright on a dark background.
/* Light mode defaults */
:root {
--text-color: #1a1a1a;
--text-secondary: #5a5a5a;
--bg-color: #ffffff;
}
/* Dark mode adjustments */
@media (prefers-color-scheme: dark) {
:root {
--text-color: #f0f0f0;
--text-secondary: #aaaaaa;
--bg-color: #1a1a1a;
}
/* Slightly reduce font-weight in dark mode — light text on dark feels bolder */
body {
font-weight: 400;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
/* Increase line-height slightly for dark mode readability */
p {
line-height: 1.65;
}
}
body {
color: var(--text-color);
background-color: var(--bg-color);
}
Best practice: In dark mode, consider reducing font-weight slightly (light text on dark backgrounds appears optically bolder), increasing line-height marginally, and using font-smoothing properties for sharper rendering on macOS.
Putting It All Together: A Complete Typography System
Here's a production-ready CSS typography foundation that incorporates all the best practices discussed above:
/* ============================================
Complete Typography System
============================================ */
/* 1. Root reset and base setup */
html {
font-size: 100%; /* respect user preferences */
-webkit-text-size-adjust: 100%;
}
/* 2. System font stack with custom font enhancement */
body {
font-family: "Inter", system-ui, -apple-system, BlinkMacSystemFont,
"Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
font-size: clamp(1rem, 0.5rem + 1vw, 1.125rem);
line-height: 1.6;
font-weight: 400;
color: #1a1a1a;
background-color: #ffffff;
text-rendering: optimizeLegibility;
-webkit-font-smoothing: antialiased;
}
/* 3. Modular type scale */
:root {
--text-xs: 0.75rem;
--text-sm: 0.875rem;
--text-base: 1rem;
--text-md: 1.25rem;
--text-lg: 1.563rem;
--text-xl: 1.953rem;
--text-2xl: 2.441rem;
--text-3xl: 3.052rem;
}
/* 4. Semantic heading hierarchy */
h1 { font-size: var(--text-3xl); font-weight: 700; line-height: 1.15; }
h2 { font-size: var(--text-2xl); font-weight: 600; line-height: 1.2; }
h3 { font-size: var(--text-xl); font-weight: 600; line-height: 1.25; }
h4 { font-size: var(--text-lg); font-weight: 500; line-height: 1.3; }
/* 5. Body text with optimal measure */
p, li, blockquote, dd, figcaption {
font-size: var(--text-base);
line-height: 1.65;
max-width: 65ch;
margin-bottom: 1rem;
}
/* 6. Small and caption text */
small, .caption, figcaption, .text-small {
font-size: var(--text-sm);
line-height: 1.5;
}
/* 7. Links with clear affordance */
a {
color: #0066cc;
text-decoration: underline;
text-underline-offset: 0.2em;
text-decoration-thickness: 0.06em;
text-underline-offset: 0.15em;
}
a:hover {
text-decoration-thickness: 0.12em;
}
/* 8. Code blocks */
pre, code {
font-family: "JetBrains Mono", "Fira Code", Consolas, "Liberation Mono", monospace;
font-size: 0.9em;
line-height: 1.5;
}
pre {
background: #f5f5f5;
padding: 1rem;
border-radius: 6px;
overflow-x: auto;
max-width: 100%;
}
/* 9. Blockquotes */
blockquote {
border-left: 4px solid #ccc;
padding-left: 1rem;
font-style: italic;
color: #555;
}
/* 10. Text overflow utilities */
.truncate {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.break-words {
overflow-wrap: break-word;
word-break: break-word;
}
/* 11. Dark mode adaptation */
@media (prefers-color-scheme: dark) {
body {
color: #e8e8e8;
background-color: #1a1a1a;
font-weight: 400;
}
a { color: #66b3ff; }
pre { background: #2a2a2a; }
blockquote {
border-left-color: #555;
color: #bbb;
}
}
/* 12. High contrast mode */
@media (prefers-contrast: high) {
body { color: #000; background: #fff; }
a { text-decoration: underline; }
}
/* 13. Reduced motion */
@media (prefers-reduced-motion: reduce) {
*, *::before, *::after {
animation-duration: 0.01ms !important;
transition-duration: 0.01ms !important;
}
}
/* 14. Print styles */
@media print {
body {
font-family: Georgia, "Times New Roman", serif;
font-size: 12pt;
line-height: 1.5;
color: #000;
background: #fff;
}
a { color: #000; text-decoration: underline; }
}
This system provides a solid foundation that you can customize for any project. It respects user preferences, scales responsively, and handles edge cases gracefully.
Conclusion
Mastering CSS typography is a continuous journey that blends technical precision with aesthetic sensibility. The web is fundamentally a text medium, and the care you invest in typography directly translates to better user experiences, improved accessibility, and stronger brand expression. Start with a solid type scale and a thoughtful font stack. Use relative units and unitless line heights to build resilience. Constrain your measure, check your contrast, and respect user preferences at every turn. Embrace modern capabilities like clamp(), variable fonts, and container queries to create typography that adapts fluidly to any screen or context. The complete typography system provided above gives you a production-ready starting point—adapt it, refine it, and let great typography become the invisible backbone of every project you build.