Module 6

Typography & Visual Design

Fonts • Text Styling • Backgrounds • Gradients • Shadows

✍️ From Plain to Professional

In Module 5 you learned the mechanics of CSS — selectors, specificity, the box model, units, and colors. Now it is time to put them to work. Typography and visual design are what separate a page that works from a page that looks professional.

Typography alone accounts for roughly 95% of web design — most of what users see is text. The remaining 5% is the visual polish: backgrounds, gradients, borders, and shadows that create depth and hierarchy. Master these, and your pages will look as good as any modern website.

💡
The Magazine Analogy: HTML is the manuscript — raw text on a page. Module 5 (CSS fundamentals) gave you a ruler and a color palette. This module gives you the designer's full toolkit: typefaces, text effects, background imagery, and depth effects. The same content, but now it looks like a polished magazine spread.

🎯 By the end of this module, you will:

  • Build robust font stacks with system fonts and web fonts
  • Load Google Fonts and self-hosted fonts with @font-face
  • Control line height, letter spacing, word spacing, and text alignment
  • Style text with decoration, transform, shadow, and overflow
  • Set background colors, images, and control their size and position
  • Create linear and radial gradients for modern UI effects
  • Use border-radius, box-shadow, and text-shadow for visual depth

🔤 Font Families & Web Fonts

The font-family property controls which typeface the browser uses. You provide a font stack — an ordered list of fonts. The browser tries the first font; if it is not available, it moves to the next, and so on until it finds one that works.

Font Stacks

Always end your stack with a generic family — a keyword that tells the browser to pick any available font of that category. The five generic families are: serif, sans-serif, monospace, cursive, and fantasy.

Generic Font Families

1 serifClassic typefaces with small strokes at letter edges. Elegant, traditional. Example: Times New Roman, Georgia.
2 sans-serifClean typefaces without strokes. Modern, readable on screens. Example: Arial, Helvetica.
3 monospaceEvery character has the same width. Used for code. Example: Courier New, Consolas.
4 cursiveHandwriting-style fonts. Decorative, less readable at small sizes.
5 fantasyDecorative, artistic fonts. Rarely used in production — results vary wildly across browsers.
CSS
Try it →
/* Font stack: specific → generic fallback */

body {
  font-family: "Inter", "Segoe UI", system-ui, sans-serif;
}

h1, h2, h3 {
  font-family: "Playfair Display", Georgia, serif;
}

code, pre {
  font-family: "JetBrains Mono", "Fira Code", monospace;
}

System Font Stack

The system-ui keyword tells the browser to use the operating system's default UI font. On macOS that is San Francisco, on Windows it is Segoe UI, on Android it is Roboto. Zero download, instant rendering, and native look.

CSS
Try it →
/* Modern system font stack — no downloads needed */

body {
  font-family: system-ui, -apple-system, "Segoe UI",
    Roboto, "Helvetica Neue", Arial, sans-serif;
}

Loading Web Fonts (Google Fonts)

Google Fonts hosts 1,500+ free fonts. Add a <link> element in your HTML <head> and the font becomes available in your CSS. Only load the weights you actually use — each weight adds to page load time.

HTML
<!-- In <head> — Load Inter (weights 400, 600, 700) -->
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600;700&display=swap"
      rel="stylesheet">
ℹ️
What does display=swap do? It tells the browser to show text immediately in a fallback font while the web font loads. Without it, text may be invisible for seconds on slow connections (FOIT — Flash of Invisible Text). With swap, users see a brief font change instead (FOUT — Flash of Unstyled Text), which is much better.

Self-hosting with @font-face

Instead of relying on Google's servers, you can host font files yourself. Download the .woff2 file (the most efficient format) and declare it with @font-face. This gives you full control, better privacy, and works offline.

CSS
/* Self-host a font */

@font-face {
  font-family: "MyCustomFont";
  src: url("fonts/my-font.woff2") format("woff2");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}
⚠️
Performance tip: limit your fonts. Two font families and three weights maximum is a good rule of thumb. Every font file adds 20–100KB to the page. Use woff2 format — it is 30% smaller than woff and supported by all modern browsers.

Font Size & Weight

font-size controls how large text appears. Use rem for predictable, accessible sizing (recall Module 5). font-weight controls boldness — values range from 100 (thin) to 900 (black), with 400 being normal and 700 being bold.

CSS
Try it →
h1 {
  font-size: 2.5rem;    /* 40px at default */
  font-weight: 700;      /* Bold */
}

h2 {
  font-size: 1.75rem;   /* 28px */
  font-weight: 600;      /* Semi-bold */
}

body {
  font-size: 1rem;       /* 16px — the browser default */
  font-weight: 400;      /* Normal */
}

.caption {
  font-size: 0.875rem;  /* 14px — small text */
  font-weight: 300;      /* Light */
}

📝 Text Styling

Beyond font family and size, CSS gives you fine-grained control over how text appears: spacing between lines and letters, alignment, decoration, transformation, and overflow handling.

Line Height

line-height controls the space between lines of text. A unitless value like 1.6 means 1.6 times the font size. This is the most important readability setting — too tight and text feels cramped, too loose and the eye loses track.

CSS
Try it →
body {
  line-height: 1.6;      /* Good for body text */
}

h1, h2, h3 {
  line-height: 1.2;      /* Tighter for headings */
}
💡
Use unitless line-height. Always set line-height as a unitless number (e.g., 1.6), not a fixed value like 24px. A unitless value scales proportionally when the font size changes. A fixed value does not — and creates overlap if the text is ever resized.

Letter & Word Spacing

letter-spacing adjusts the space between characters. word-spacing adjusts the space between words. Small values make a big visual difference — use them sparingly.

CSS
Try it →
.uppercase-heading {
  text-transform: uppercase;
  letter-spacing: 0.1em;   /* Spread uppercase letters */
  font-size: 0.875rem;
  font-weight: 600;
}

.tight-heading {
  letter-spacing: -0.02em;  /* Slightly tighter — common for large headings */
  font-size: 3rem;
}

Text Alignment

text-align controls horizontal alignment within a block element. The four values: left (default for LTR languages), right, center, and justify (stretches lines to fill the full width — use with caution, as it can create uneven spacing).

Text Decoration

text-decoration adds or removes lines on text. Links have underline by default. You can customize the line style, color, and thickness.

CSS
Try it →
/* Remove default link underline, add on hover */
a {
  text-decoration: none;
  color: #3b82f6;
}
a:hover {
  text-decoration: underline;
  text-underline-offset: 4px;
}

/* Strikethrough for deleted prices */
.original-price {
  text-decoration: line-through red;
  color: #999;
}

Text Transform

text-transform changes the capitalization of text without editing the HTML. This keeps content accessible (screen readers see the original) while changing the visual appearance.

text-transform Values

1 uppercaseALL LETTERS BECOME CAPITALS. Common for labels, badges, and small headings.
2 lowercaseall letters become lowercase.
3 capitalizeFirst Letter Of Each Word Is Capitalized.
4 noneRemoves any inherited transformation.

Text Overflow & Truncation

When text overflows its container, you can clip it with an ellipsis (...). This requires three properties working together:

CSS
Try it →
/* Single-line truncation with ellipsis */
.truncate {
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  max-width: 300px;
}

/* Multi-line truncation (clamp to 3 lines) */
.line-clamp {
  display: -webkit-box;
  -webkit-line-clamp: 3;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

🖼️ Backgrounds

Every element has a background layer behind its content and padding. By default it is transparent, but you can fill it with a solid color, an image, or (as you will see in the next section) a gradient.

Background Color

The simplest background. It fills the entire content + padding area (but not the margin). You already know how to define colors from Module 5.

CSS
Try it →
.card {
  background-color: #f8fafc;
  padding: 24px;
}

.highlight {
  background-color: rgba(59, 130, 246, 0.1);  /* Semi-transparent blue */
  padding: 4px 8px;
  border-radius: 4px;
}

Background Image

background-image places an image behind the element's content. By default, the image tiles (repeats) to fill the space. You control this with related properties.

Property Purpose Common Values
background-image Sets the image url("photo.jpg")
background-size How the image scales cover, contain, 100% auto
background-position Where the image sits center, top right, 50% 50%
background-repeat Whether the image tiles no-repeat, repeat, repeat-x
background-attachment Scrolling behavior scroll, fixed
CSS
Try it →
.hero {
  background-image: url("hero-bg.jpg");
  background-size: cover;         /* Scale to fill, crop excess */
  background-position: center;     /* Keep the focal point centered */
  background-repeat: no-repeat;   /* Don't tile */
  min-height: 400px;
}
💡
cover vs contain: cover scales the image to fill the element completely, cropping any excess. contain scales the image to fit entirely inside the element, leaving empty space if the aspect ratios differ. Use cover for hero sections and full-bleed backgrounds; use contain for logos and diagrams where nothing should be cut off.

The background Shorthand

You can combine all background properties into one shorthand declaration. The order is flexible, but the common convention is: color image position/size repeat.

CSS
/* Shorthand — all in one line */
.hero {
  background: #1a1a2e url("hero.jpg") center/cover no-repeat;
}

🌈 Gradients

Gradients are CSS-generated images — smooth transitions between two or more colors. They are set with background-image (or the background shorthand) and require zero image files.

Linear Gradients

A linear-gradient() transitions colors along a straight line. You specify a direction (or angle) and two or more color stops.

CSS
Try it →
/* Top to bottom (default) */
.gradient-1 {
  background: linear-gradient(#3b82f6, #8b5cf6);
  padding: 40px;
  color: white;
  border-radius: 12px;
  margin-bottom: 16px;
}

/* Left to right */
.gradient-2 {
  background: linear-gradient(to right, #f59e0b, #ef4444);
  padding: 40px;
  color: white;
  border-radius: 12px;
  margin-bottom: 16px;
}

/* Diagonal with angle */
.gradient-3 {
  background: linear-gradient(135deg, #06b6d4, #3b82f6, #8b5cf6);
  padding: 40px;
  color: white;
  border-radius: 12px;
}

Gradient Direction Keywords

to bottomTop to bottom (default). Same as 180deg.
to rightLeft to right. Same as 90deg.
to top rightDiagonal to the top-right corner.
° 135degAny angle. 0deg is up, 90deg is right, 180deg is down.

Radial Gradients

A radial-gradient() radiates colors outward from a center point, creating circular or elliptical effects.

CSS
Try it →
.spotlight {
  background: radial-gradient(circle at center, #3b82f6, #1e1b4b);
  padding: 60px;
  color: white;
  text-align: center;
  border-radius: 12px;
}

Gradient + Image Overlay

A powerful technique: layer a semi-transparent gradient over a background image to ensure text remains readable. CSS allows multiple backgrounds stacked with commas — the first one is on top.

CSS
/* Dark overlay on top of a photo */
.hero {
  background:
    linear-gradient(rgba(0,0,0,0.6), rgba(0,0,0,0.6)),
    url("hero.jpg") center/cover no-repeat;
  color: white;
}

🔲 Borders & Rounded Corners

Borders are the visible lines between an element's padding and margin. border-radius rounds the corners — from subtle rounding to full circles.

Border Syntax

The border shorthand takes three values: width, style, color. You can also target individual sides with border-top, border-right, border-bottom, border-left.

CSS
Try it →
/* Shorthand: width style color */
.card {
  border: 1px solid #e2e8f0;
  padding: 24px;
  margin-bottom: 16px;
}

/* Bottom-only accent border */
.section-heading {
  border-bottom: 3px solid #3b82f6;
  padding-bottom: 8px;
}

/* Dashed and dotted styles */
.note {
  border: 2px dashed #f59e0b;
  padding: 16px;
}

Border Styles

solidA continuous line. The most common style.
- - dashedA series of short dashes.
··· dottedA series of dots.
doubleTwo parallel lines. The width must be at least 3px to see both lines.
noneNo border. Useful for removing default borders from buttons and inputs.

border-radius: Rounded Corners

border-radius rounds the corners of an element. One value rounds all four corners equally. You can also set each corner individually.

CSS
Try it →
/* Subtle rounding — modern UI standard */
.card {
  border-radius: 8px;
  background: #f1f5f9;
  padding: 24px;
  margin-bottom: 16px;
}

/* Pill-shaped button */
.pill {
  border-radius: 9999px;
  background: #3b82f6;
  color: white;
  padding: 8px 24px;
  display: inline-block;
  margin-bottom: 16px;
}

/* Perfect circle (equal width and height) */
.avatar {
  width: 80px;
  height: 80px;
  border-radius: 50%;
  background: #8b5cf6;
  color: white;
  display: flex;
  align-items: center;
  justify-content: center;
  font-weight: 700;
  font-size: 1.5rem;
}
ℹ️
50% makes a circle — but only if width equals height. If the element is rectangular, border-radius: 50% creates an ellipse, not a circle. For perfect circles, ensure both width and height are the same. For pill shapes on elements of varying width, use a very large value like 9999px.

outline vs border

outline looks like a border but does not take up space in the box model — it does not affect layout. It is drawn outside the border. The browser uses it for focus indicators on interactive elements. Never remove it without providing an alternative focus style.

CSS
Try it →
/* Custom focus style — never just "outline: none" */
button:focus-visible {
  outline: 2px solid #3b82f6;
  outline-offset: 2px;
}

🔮 Shadows

Shadows create depth and visual hierarchy. CSS has two shadow properties: box-shadow for elements and text-shadow for text.

box-shadow

box-shadow adds one or more shadows around an element's box. The syntax: offset-x offset-y blur spread color. Blur and spread are optional.

CSS
Try it →
/* Subtle elevation — great for cards */
.card-sm {
  box-shadow: 0 1px 3px rgba(0,0,0,0.12);
  padding: 24px;
  border-radius: 8px;
  background: white;
  margin-bottom: 24px;
}

/* Medium elevation */
.card-md {
  box-shadow: 0 4px 6px -1px rgba(0,0,0,0.1), 0 2px 4px -2px rgba(0,0,0,0.1);
  padding: 24px;
  border-radius: 8px;
  background: white;
  margin-bottom: 24px;
}

/* Large elevation — modals, popups */
.card-lg {
  box-shadow: 0 20px 25px -5px rgba(0,0,0,0.1), 0 8px 10px -6px rgba(0,0,0,0.1);
  padding: 24px;
  border-radius: 8px;
  background: white;
}

box-shadow Values

1 offset-xHorizontal offset. Positive = right, negative = left.
2 offset-yVertical offset. Positive = down, negative = up.
3 blurHow soft the shadow is. 0 = sharp edge. Larger = softer.
4 spreadExpands or contracts the shadow. Positive = bigger, negative = smaller.
5 colorShadow color. Use rgba() with low opacity for natural-looking shadows.
💡
Inset shadows: Add the inset keyword before the values to create an inner shadow: box-shadow: inset 0 2px 4px rgba(0,0,0,0.1). This creates a pressed or recessed effect — useful for input fields and toggle buttons.

text-shadow

text-shadow adds a shadow behind text. The syntax is simpler: offset-x offset-y blur color (no spread value). Use subtle shadows for readability on images, or decorative shadows for headings.

CSS
Try it →
/* Subtle readability shadow */
.hero-title {
  color: white;
  text-shadow: 0 2px 4px rgba(0,0,0,0.5);
  font-size: 3rem;
}

/* Decorative glow effect */
.neon {
  color: #06b6d4;
  text-shadow: 0 0 10px #06b6d4, 0 0 40px #06b6d4;
  font-size: 2rem;
}

✏️ Practice: Typography & Visual Design

Test your understanding of fonts, text styling, backgrounds, gradients, and shadows.

Exercise 1: Build a Font Stack

Write a CSS rule for the body element that: (1) uses Inter as the primary font, (2) falls back to the system UI font, (3) ends with the correct generic family.
Then write a second rule for headings that uses Playfair Display with a serif fallback.

Reveal Solution

body { font-family: "Inter", system-ui, sans-serif; } — Inter is the primary, system-ui is the native OS font, and sans-serif is the generic fallback.

h1, h2, h3 { font-family: "Playfair Display", Georgia, serif; } — Playfair Display is a serif web font, Georgia is a well-known system serif, and serif is the generic family.

Exercise 2: Typography Readability

This paragraph is hard to read. Identify all the problems and write the CSS to fix them:

CSS
p {
  font-size: 11px;
  line-height: 12px;
  letter-spacing: 0.2em;
  text-align: justify;
  font-family: fantasy;
}
Reveal Solution

Font size too small — 11px is below the recommended minimum of 16px for body text. Fix: font-size: 1rem;

Line height too tight and uses fixed units — 12px line-height on 11px text is barely any spacing. Fix: line-height: 1.6; (unitless, scales with font size).

Letter spacing too wide — 0.2em is extreme for body text. Fix: remove letter-spacing or use normal. Only use positive letter-spacing on uppercase labels (around 0.05–0.1em).

text-align: justify creates rivers — uneven word spacing reduces readability. Fix: text-align: left;

fantasy is unpredictable — every browser renders it differently. Fix: use a proper font stack like "Inter", system-ui, sans-serif.

Exercise 3: Create a Card with Visual Polish

Write CSS for a .card class that has: a white background, 24px padding, 8px rounded corners, a subtle box shadow, and a 1px solid light-gray border. Then add a :hover state that increases the shadow.

Reveal Solution

.card { background: white; padding: 24px; border-radius: 8px; box-shadow: 0 1px 3px rgba(0,0,0,0.12); border: 1px solid #e2e8f0; }

.card:hover { box-shadow: 0 10px 15px -3px rgba(0,0,0,0.1), 0 4px 6px -4px rgba(0,0,0,0.1); }

💡 The hover shadow is larger (more offset-y, more blur) to create the illusion that the card lifts off the page. This is a common interaction pattern in modern UI design.

Exercise 4: Gradient Hero Section

Write the CSS for a .hero section with a diagonal gradient from blue (#3b82f6) to purple (#8b5cf6), white centered text, and a minimum height of 400px.

Reveal Solution

.hero { background: linear-gradient(135deg, #3b82f6, #8b5cf6); color: white; text-align: center; min-height: 400px; display: flex; align-items: center; justify-content: center; }

💡 135deg creates a top-left to bottom-right diagonal. We use display: flex with centering to place the text in the middle of the hero. You will learn Flexbox in detail in Module 7.

📋 Module Summary

🔤
Font Families

Build font stacks ending with a generic family. Use system-ui for zero-download native fonts. Load web fonts with <link> or @font-face. Limit to 2 families and 3 weights max.

📝
Text Styling

Use unitless line-height (1.5–1.6 for body). letter-spacing for uppercase labels, negative values for large headings. text-overflow: ellipsis for truncation.

🖼️
Backgrounds

background-size: cover for hero images. contain for logos. Combine with background-position: center and no-repeat.

🌈
Gradients

linear-gradient() for straight transitions, radial-gradient() for circular effects. Layer gradients over images for text readability.

🔲
Borders

border: width style color. border-radius for rounded corners — 50% for circles, 9999px for pills. Never remove outline without a replacement.

🔮
Shadows

box-shadow for element depth (cards, modals). text-shadow for text readability over images. Use low-opacity rgba() for natural-looking shadows.

Next Module →

Module 7: Layout: Flexbox

Master one-dimensional layouts. Flex containers, items, alignment, wrapping, and real-world patterns: navbars, card rows, centering.