Typography and Text Styling

1. Font Properties and Web Font Loading

Property Values Description Default
font-family family-name | generic-family Font family stack (fallback chain) Browser default
font-size length | % Font size (absolute or relative) medium (16px)
font-weight 100-900 | normal | bold | lighter | bolder Font weight (thickness) 400 (normal)
font-style normal | italic | oblique [angle] Font style normal
font-variant normal | small-caps | all-small-caps Font variant (caps, ligatures, numerals) normal
font-stretch 50%-200% | ultra-condensed to ultra-expanded Font width (condensed/expanded) 100% (normal)
font style variant weight size/line-height family Shorthand for all font properties -
font-synthesis none | weight | style | small-caps Control browser font synthesis weight style
font-size-adjust number | none Preserve x-height across fallback fonts none
font-kerning auto | normal | none Control kerning (letter spacing pairs) auto
font-optical-sizing auto | none Optimize font rendering at different sizes auto
@font-face Descriptor Values Description Required
font-family name Name to reference font ✓ Yes
src url() format() Font file URL and format ✓ Yes
font-weight 100-900 | range Weight(s) this file supports Optional
font-style normal | italic | oblique Style this file supports Optional
font-display auto | block | swap | fallback | optional Loading behavior strategy Optional
unicode-range U+0-10FFFF Character range this font covers Optional
font-stretch 50%-200% Width range this file supports Optional

Example: Font family and @font-face

/* Font stack with fallbacks */
body {
    font-family: 
        "Inter", 
        -apple-system, 
        BlinkMacSystemFont, 
        "Segoe UI", 
        Roboto, 
        sans-serif;
}

/* Load custom web font */
@font-face {
    font-family: "Inter";
    src: url("/fonts/inter-var.woff2") format("woff2");
    font-weight: 100 900;  /* Variable font weight range */
    font-style: normal;
    font-display: swap;  /* Show fallback immediately */
}

/* Load specific font weight */
@font-face {
    font-family: "Roboto";
    src: url("/fonts/roboto-regular.woff2") format("woff2");
    font-weight: 400;
    font-style: normal;
}

@font-face {
    font-family: "Roboto";
    src: url("/fonts/roboto-bold.woff2") format("woff2");
    font-weight: 700;
    font-style: normal;
}

/* Unicode range for subsetting */
@font-face {
    font-family: "Icons";
    src: url("/fonts/icons.woff2") format("woff2");
    unicode-range: U+E000-U+F8FF;  /* Private Use Area */
}

Example: Font properties usage

/* Font shorthand */
h1 {
    /* font: style variant weight size/line-height family */
    font: italic small-caps 700 2rem/1.2 Georgia, serif;
}

/* Individual properties */
p {
    font-family: "Inter", sans-serif;
    font-size: 1rem;
    font-weight: 400;
    font-style: normal;
}

/* Variable font weights */
.light { font-weight: 300; }
.regular { font-weight: 400; }
.medium { font-weight: 500; }
.semibold { font-weight: 600; }
.bold { font-weight: 700; }

/* Font size adjustment for consistent x-height */
body {
    font-family: "Primary", "Fallback", sans-serif;
    font-size-adjust: 0.5;  /* Preserves x-height across fonts */
}

/* Disable synthetic bold/italic */
.no-synthesis {
    font-synthesis: none;  /* Don't fake bold/italic if not available */
}

2. Font Display Strategies and Performance

font-display Block Period Swap Period Behavior Use Case
auto Browser default Browser default Browser decides strategy Default (not recommended)
block ~3 seconds Infinite Block, then swap when loaded Icons, critical branding
swap ~0ms Infinite Show fallback immediately, swap when loaded Body text, most common
fallback ~100ms ~3 seconds Brief block, short swap period Balance performance/appearance
optional ~100ms None Use if cached, otherwise fallback Non-critical, fast-loading priority
Loading Issue Problem Solution Implementation
FOIT Flash of Invisible Text Use font-display: swap @font-face { font-display: swap; }
FOUT Flash of Unstyled Text Match fallback font metrics font-size-adjust: 0.5;
Layout Shift Content jumps when font loads Size-adjust descriptor, preload size-adjust: 95%;
Slow Loading Fonts take long to download Preload critical fonts <link rel="preload">
Multiple Requests Too many font files Use variable fonts Single file with weight range

Example: Font display strategies

/* Recommended: swap for body text */
@font-face {
    font-family: "Body";
    src: url("/fonts/body.woff2") format("woff2");
    font-display: swap;  /* Show fallback immediately */
}

/* Block for icons (prevent invisible icons) */
@font-face {
    font-family: "Icons";
    src: url("/fonts/icons.woff2") format("woff2");
    font-display: block;  /* Wait for icons to load */
}

/* Optional for non-critical decorative fonts */
@font-face {
    font-family: "Decorative";
    src: url("/fonts/decorative.woff2") format("woff2");
    font-display: optional;  /* Only use if already cached */
}

/* Fallback for balance */
@font-face {
    font-family: "Headings";
    src: url("/fonts/headings.woff2") format("woff2");
    font-display: fallback;  /* Brief wait, short swap */
}

Example: Optimize font loading

<!-- Preload critical fonts -->
<link rel="preload" 
      href="/fonts/inter-var.woff2" 
      as="font" 
      type="font/woff2" 
      crossorigin>

<!-- Font display CSS -->
<style>
/* Match fallback font metrics to reduce layout shift */
@font-face {
    font-family: "Inter";
    src: url("/fonts/inter-var.woff2") format("woff2");
    font-weight: 100 900;
    font-display: swap;
    /* Adjust fallback font size */
    size-adjust: 100%;
    ascent-override: 90%;
    descent-override: 22%;
    line-gap-override: 0%;
}

body {
    font-family: "Inter", 
                 -apple-system, 
                 BlinkMacSystemFont, 
                 sans-serif;
    /* Preserve x-height when fallback is shown */
    font-size-adjust: 0.5;
}

/* Loading class for progressive enhancement */
.fonts-loading body {
    font-family: -apple-system, BlinkMacSystemFont, sans-serif;
}
</style>
Note: font-display: swap is recommended for most cases to prevent FOIT. Preload critical fonts with <link rel="preload">. Use size-adjust descriptor to minimize layout shift.

3. Variable Fonts and Font Variations

Property Values Description Advantage
font-variation-settings "axis" value Low-level control of variable font axes Access custom axes
font-weight 100-900 Weight axis (high-level) Any value in range
font-stretch 50%-200% Width axis (high-level) Smooth transitions
font-style oblique [angle] Slant axis (high-level) Custom italic angles
font-optical-sizing auto | none Optical size axis (opsz) Size-specific optimization
Standard Axis Code Range Description
Weight wght 1-1000 Thickness of strokes
Width wdth 50-200 Condensed to expanded
Slant slnt -90 to 90 Angle of slant (oblique)
Optical Size opsz 6-72+ Optimize for font size
Italic ital 0-1 Roman to italic (binary)

Example: Variable font usage

/* Load variable font */
@font-face {
    font-family: "Inter";
    src: url("/fonts/inter-var.woff2") format("woff2");
    font-weight: 100 900;  /* Supports full weight range */
    font-stretch: 50% 200%;  /* Supports width range */
    font-style: oblique 0deg 10deg;  /* Supports slant range */
}

/* Use high-level properties (preferred) */
h1 {
    font-family: "Inter", sans-serif;
    font-weight: 350;  /* Any value 100-900 */
    font-stretch: 115%;  /* Slightly expanded */
}

h2 {
    font-weight: 650;  /* Custom weight between 600-700 */
}

/* Use font-variation-settings for custom axes */
.custom {
    font-variation-settings: 
        "wght" 450,  /* Weight */
        "wdth" 100,  /* Width */
        "slnt" -5,   /* Slant */
        "GRAD" 50;   /* Custom axis (Grade) */
}

/* Animate weight on hover */
.button {
    font-weight: 400;
    transition: font-weight 0.3s;
}

.button:hover {
    font-weight: 600;  /* Smooth weight transition */
}

Example: Advanced variable font techniques

/* Responsive typography with variable fonts */
h1 {
    font-family: "Recursive", sans-serif;
    /* Lighter weight at larger sizes */
    font-weight: clamp(300, 900 - 20vw, 700);
    /* More condensed at smaller viewports */
    font-stretch: clamp(75%, 50% + 10vw, 125%);
}

/* Optical sizing (automatic) */
.auto-optical {
    font-optical-sizing: auto;  /* Adjusts opsz based on font-size */
}

/* Manual optical sizing */
.manual-optical {
    font-variation-settings: "opsz" 12;  /* Optimized for 12px */
    font-size: 12px;
}

/* Custom axes (font-specific) */
.roboto-flex {
    font-family: "Roboto Flex", sans-serif;
    font-variation-settings:
        "GRAD" 0,    /* Grade (stroke contrast) */
        "slnt" -10,  /* Slant */
        "XTRA" 468,  /* X-transparent */
        "XOPQ" 96,   /* X-opaque */
        "YOPQ" 79,   /* Y-opaque */
        "YTLC" 514,  /* Y-transparent lowercase */
        "YTUC" 712,  /* Y-transparent uppercase */
        "YTAS" 750,  /* Y-transparent ascender */
        "YTDE" -203, /* Y-transparent descender */
        "YTFI" 738;  /* Y-transparent figure height */
}
Note: Variable fonts reduce HTTP requests (single file for multiple weights/widths). Use high-level properties (font-weight, font-stretch) instead of font-variation-settings when possible for better browser support.

4. Text Layout and Spacing Controls

Property Values Description Default
line-height number | length | % Vertical spacing between lines normal (~1.2)
letter-spacing normal | length Horizontal space between characters normal
word-spacing normal | length Space between words normal
text-indent length | % First line indentation 0
text-align left | right | center | justify | start | end Horizontal text alignment start
text-align-last auto | left | right | center | justify Alignment of last line auto
vertical-align baseline | top | middle | bottom | length | % Vertical alignment in inline context baseline
white-space normal | nowrap | pre | pre-wrap | pre-line Whitespace and line break handling normal
tab-size number | length Width of tab character 8
word-break normal | break-all | keep-all | break-word Line breaking rules for words normal
overflow-wrap normal | anywhere | break-word Break long words to prevent overflow normal
hyphens none | manual | auto Hyphenation for line breaks manual
text-transform none | uppercase | lowercase | capitalize Text case transformation none
text-decoration line | style | color | thickness Underline, overline, line-through none

Example: Text spacing and layout

/* Optimal body text readability */
body {
    font-size: 1rem;
    line-height: 1.6;  /* Unitless preferred (relative to font-size) */
    letter-spacing: 0.01em;  /* Slight tracking */
}

/* Headings with tighter spacing */
h1 {
    font-size: 3rem;
    line-height: 1.2;  /* Tighter for large text */
    letter-spacing: -0.02em;  /* Negative tracking for large sizes */
}

/* Paragraph first-line indent */
p + p {
    text-indent: 2em;
}

/* Justified text with hyphenation */
.justified {
    text-align: justify;
    hyphens: auto;  /* Requires lang attribute */
    text-justify: inter-word;
}

/* Prevent text wrapping */
.nowrap {
    white-space: nowrap;
}

/* Break long URLs */
.url {
    word-break: break-all;
    overflow-wrap: break-word;
}

Example: White-space control

/* white-space values comparison */
.normal {
    white-space: normal;  /* Collapse spaces, wrap lines */
}

.nowrap {
    white-space: nowrap;  /* Collapse spaces, no wrap */
}

.pre {
    white-space: pre;  /* Preserve spaces, no wrap */
}

.pre-wrap {
    white-space: pre-wrap;  /* Preserve spaces, wrap lines */
}

.pre-line {
    white-space: pre-line;  /* Collapse spaces, preserve line breaks */
}

/* Code block formatting */
pre code {
    white-space: pre;
    tab-size: 4;  /* Tab width: 4 spaces */
}

/* Text overflow with ellipsis */
.truncate {
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

5. Advanced Typography (text-wrap, text-spacing) NEW

Property Values Description Browser Support
text-wrap wrap | nowrap | balance | pretty Advanced text wrapping modes Chrome 114+, Safari 17.5+
balance text-wrap: balance; Balanced line lengths (headings) Chrome 114+
pretty text-wrap: pretty; Avoid orphans/widows (paragraphs) Chrome 117+
hanging-punctuation none | first | last | force-end | allow-end Hang punctuation outside text box Safari only
text-spacing-trim normal | space-all | trim-start CJK punctuation spacing Experimental
text-autospace normal | no-autospace Spacing between CJK and Latin Experimental

Example: Text-wrap balance and pretty

/* Balanced headings (prevents single word on last line) */
h1, h2, h3 {
    text-wrap: balance;  /* Max 4 lines for performance */
    max-inline-size: 40ch;  /* Limit width for readability */
}

/* Pretty paragraphs (prevents orphans/widows) */
p {
    text-wrap: pretty;  /* Avoid single word on last line */
}

/* Traditional approach (still works) */
.traditional {
    text-align: justify;
    hyphens: auto;
    orphans: 3;  /* Min lines at bottom of page */
    widows: 3;   /* Min lines at top of next page */
}

/* Hanging punctuation (Safari) */
blockquote {
    hanging-punctuation: first last;  /* Hang quotes outside */
}

/* Combined for optimal typography */
article p {
    max-inline-size: 65ch;  /* Optimal reading width */
    text-wrap: pretty;
    hyphens: auto;
    hanging-punctuation: first last;
}

Example: Advanced text control

/* Multi-line truncation with balanced text */
.card-title {
    display: -webkit-box;
    -webkit-line-clamp: 3;
    -webkit-box-orient: vertical;
    overflow: hidden;
    text-wrap: balance;  /* Balance truncated text */
}

/* Optimal heading layout */
.heading {
    /* Limit width for readability */
    max-inline-size: 30ch;
    /* Balance line lengths */
    text-wrap: balance;
    /* Tighter line height */
    line-height: 1.1;
    /* Negative tracking for large text */
    letter-spacing: -0.02em;
    /* Hang punctuation outside */
    hanging-punctuation: first last;
}

/* Body text optimization */
.body-text {
    max-inline-size: 65ch;
    text-wrap: pretty;  /* Avoid orphans */
    line-height: 1.6;
    hyphens: auto;
}

/* Preserve manual line breaks */
.poem {
    white-space: pre-line;  /* Preserve breaks, wrap long lines */
    text-wrap: balance;
}
Note: text-wrap: balance works best for headings (≤4 lines). text-wrap: pretty optimizes paragraphs to avoid orphans/widows. Limited browser support currently.

6. International Typography Support

Property Values Description Use Case
writing-mode horizontal-tb | vertical-rl | vertical-lr Text flow direction (horizontal/vertical) CJK vertical text, Mongolian
text-orientation mixed | upright | sideways Character orientation in vertical text CJK vertical layout
direction ltr | rtl Text direction (left-to-right or right-to-left) Arabic, Hebrew
unicode-bidi normal | embed | isolate | bidi-override Bidirectional text algorithm control Mixed LTR/RTL content
text-combine-upright none | all | digits [n] Combine characters in vertical text (tate-chu-yoko) Japanese vertical numbers
font-language-override normal | string Override language-specific glyphs Use specific OpenType features
lang attribute lang="en" lang="ar" lang="ja" HTML language declaration Required for hyphenation, fonts

Example: Writing modes and text orientation

/* Vertical text (Japanese, Chinese, Korean) */
.vertical-text {
    writing-mode: vertical-rl;  /* Right-to-left vertical */
    text-orientation: mixed;  /* Rotate Latin chars, upright CJK */
}

.vertical-text-lr {
    writing-mode: vertical-lr;  /* Left-to-right vertical (Mongolian) */
}

/* All characters upright in vertical mode */
.upright {
    writing-mode: vertical-rl;
    text-orientation: upright;
}

/* Combine numbers in vertical text (tate-chu-yoko) */
.vertical-numbers {
    writing-mode: vertical-rl;
}

.number {
    text-combine-upright: all;  /* Horizontal numbers in vertical text */
}

/* Horizontal text (default) */
.horizontal {
    writing-mode: horizontal-tb;  /* Top-to-bottom horizontal */
}

Example: RTL and bidirectional text

<!-- HTML lang attribute for proper text handling -->
<html lang="ar" dir="rtl">  <!-- Arabic, right-to-left -->

/* RTL layout */
body {
    direction: rtl;  /* Right-to-left text flow */
    text-align: start;  /* Aligns right in RTL */
}

/* LTR content within RTL page */
.ltr-content {
    direction: ltr;
    unicode-bidi: embed;  /* Isolate from parent direction */
}

/* Bidirectional override (force direction) */
.force-ltr {
    unicode-bidi: bidi-override;
    direction: ltr;
}

/* Isolate mixed content */
.isolated {
    unicode-bidi: isolate;  /* Prevent direction contamination */
}

/* Logical properties work with writing modes */
.international {
    /* Instead of: margin-left, margin-right */
    margin-inline-start: 1rem;  /* Adapts to LTR/RTL */
    margin-inline-end: 1rem;
    
    /* Instead of: margin-top, margin-bottom */
    margin-block-start: 1rem;  /* Adapts to writing mode */
    margin-block-end: 1rem;
}

Example: Language-specific typography

/* Language-specific font stacks */
:lang(en) {
    font-family: "Inter", -apple-system, sans-serif;
}

:lang(ja) {
    font-family: "Noto Sans JP", "Yu Gothic", "Hiragino Sans", sans-serif;
}

:lang(zh) {
    font-family: "Noto Sans SC", "PingFang SC", "Microsoft YaHei", sans-serif;
}

:lang(ar) {
    font-family: "Noto Sans Arabic", "Arial", sans-serif;
    direction: rtl;
}

:lang(ko) {
    font-family: "Noto Sans KR", "Malgun Gothic", sans-serif;
}

/* Auto hyphenation requires lang attribute */
p {
    hyphens: auto;  /* Only works with proper lang attribute */
}

/* OpenType features for specific languages */
.turkish {
    font-language-override: "TRK";  /* Use Turkish-specific glyphs */
}

/* CJK punctuation adjustment */
:lang(ja), :lang(zh) {
    text-spacing-trim: space-all;  /* Trim CJK punctuation spacing */
}

Typography Best Practices

  • Use unitless line-height (1.6) for scalability
  • Set font-display: swap to prevent invisible text (FOIT)
  • Preload critical fonts with <link rel="preload">
  • Use variable fonts to reduce HTTP requests
  • Limit text width to 60-75 characters (45-75ch) for readability
  • Use text-wrap: balance for headings to avoid orphans
  • Add hyphens: auto with proper lang attribute
  • Use logical properties for international layouts
  • Match fallback font metrics with font-size-adjust to reduce layout shift
  • Use system font stacks for performance: -apple-system, BlinkMacSystemFont, "Segoe UI"