Typography, backgrounds, borders and shadows
Most of a website is text, so typography is where design wins or loses. In this lesson you will learn how to choose and set fonts, how to make long text pleasant to read, and how to add visual depth with backgrounds, rounded borders and shadows. By the end, your portfolio will start to look like a deliberate design.
Font families
The font-family property takes a list. The browser uses the first font available on the visitor’s device and falls back to the next ones:
body {
font-family: system-ui, -apple-system, "Segoe UI", Roboto, Arial, sans-serif;
}
h1, h2, h3 {
font-family: Georgia, "Times New Roman", serif;
}
Always end the list with a generic family: serif (with small strokes at letter ends), sans-serif (without), monospace (all letters equally wide, for code), cursive or system-ui. A font name with spaces goes in quotes.
System fonts and web fonts
System fonts are already installed on the visitor’s device. They load instantly and cost nothing, but they differ from one system to another. The system-ui keyword requests the interface font of the operating system, which is an excellent and fast default.
Web fonts are font files that you provide with your site, using @font-face:
@font-face {
font-family: "Portfolio Serif";
src: url("../fonts/portfolio-serif.woff2") format("woff2");
font-weight: 400 700;
font-display: swap;
}
The font-display: swap declaration shows text immediately in a fallback font and swaps when the custom font arrives, which avoids invisible text. Keep your web fonts to a minimum: one or two families, few weights. Each file adds to page load time, and always check the font’s license before using it. Many fonts under open licenses are available for free. Self-hosting font files, rather than loading them from a third party, is also friendlier to visitors’ privacy.
Size, weight and style
p { font-size: 1rem; }
h1 { font-size: 2.5rem; }
strong { font-weight: 700; } /* 400 normal, 700 bold */
em { font-style: italic; }
A type scale keeps sizes harmonious. One simple approach is a base of 1rem and larger steps, for example 1.25rem, 1.5rem, 2rem, 2.5rem. Do not make body text smaller than the browser default: it hurts readability.
Making text readable
These properties have a huge effect on comfort:
line-height: around 1.5 to 1.7 for paragraphs, a little tighter for large headings.max-widthinchunits: limit a text block to roughly 60 to 75 characters per line. Longer lines are tiring to follow.text-align: left-aligned (or right in right-to-left languages) is the most readable for long text. Avoidjustify, which can create uneven gaps, and avoid centering long paragraphs.letter-spacing: slight positive spacing can suit uppercase labels.text-transform:uppercase,lowercaseorcapitalizechange case without changing the HTML.text-decoration: underline, none or line-through.
.prose {
max-width: 65ch;
line-height: 1.7;
}
.eyebrow {
text-transform: uppercase;
letter-spacing: 0.08em;
font-size: 0.875rem;
}
Warning. Removing the underline from links makes them harder to recognize. If you remove it, offer another clear indicator such as a different color with enough contrast, bold weight or an underline on hover and focus. In running text, underlined links are the safest choice.
Backgrounds
You already know background-color. The background family can do much more:
.hero {
background-color: #0f766e;
background-image: url("../images/hero.jpg");
background-size: cover;
background-position: center;
background-repeat: no-repeat;
}
background-size: coverscales the image to fill the box, cropping if needed;containfits the entire image, leaving empty areas.background-positionchooses which part stays visible.background-repeatcontrols tiling.
CSS can also draw gradients without an image file:
.banner {
background: linear-gradient(135deg, #0f766e, #134e4a);
}
If text sits on top of an image, make sure it stays readable. A trick is to layer a semi-transparent gradient above the picture:
.hero {
background-image:
linear-gradient(rgb(0 0 0 / 55%), rgb(0 0 0 / 55%)),
url("../images/hero.jpg");
background-size: cover;
color: #ffffff;
}
A background image is decoration, and has no alternative text. If an image carries information, use an img element with alt instead.
Borders and rounded corners
.button {
border: 2px solid #0f766e;
border-radius: 999px; /* pill shape */
}
.avatar {
border-radius: 50%; /* circle, on a square image */
}
.note {
border-left: 4px solid #f59e0b;
}
Individual sides (border-left, border-bottom) are great for accents and dividers. The outline property draws a line outside the border and is used for keyboard focus indicators; you will treat it carefully in the accessibility lesson.
Shadows
A shadow gives depth. The box-shadow property takes horizontal offset, vertical offset, blur radius and a color:
.project-card {
box-shadow: 0 2px 8px rgb(0 0 0 / 12%);
}
.project-card:hover {
box-shadow: 0 6px 18px rgb(0 0 0 / 18%);
}
Subtle works best: low opacity and moderate blur. For text, text-shadow works the same way without the spread, but use it rarely, since it can reduce legibility. To shadow the outline of a transparent image, filter: drop-shadow() follows the shape of the content.
Styling lists and links as a menu
With what you know, you can turn the plain list of links in your nav into a tidy menu:
.site-nav ul {
list-style: none;
margin: 0;
padding: 0;
}
.site-nav a {
display: inline-block;
padding: 0.5rem 1rem;
color: #134e4a;
text-decoration: none;
border-radius: 0.375rem;
}
.site-nav a:hover {
background-color: #ccfbf1;
}
The list items still stack vertically; to place them on one line you will use Flexbox in the next chapter. Notice how display: inline-block lets padding apply to links.
Tip. Squint at your page. If you can still tell what is a heading, what is body text and what is secondary, your typographic hierarchy works. Hierarchy comes from size, weight, color and spacing, used with restraint.
Your turn
- Choose a font stack for body text and a different one for headings, each ending with a generic family.
- Define a type scale for
h1,h2,h3and paragraphs. Limit paragraph width to about65ch. - Give the home page intro a background (a gradient or an image) with readable white text.
- Round the corners of your project cards and add a subtle shadow that gets stronger on hover.
- Turn the menu list into a tidy menu with hover states.
See a solution
body {
font-family: system-ui, "Segoe UI", Arial, sans-serif;
line-height: 1.6;
}
h1, h2, h3 {
font-family: Georgia, "Times New Roman", serif;
line-height: 1.2;
}
h1 { font-size: 2.5rem; }
h2 { font-size: 1.75rem; }
h3 { font-size: 1.25rem; }
p { max-width: 65ch; }
.intro-block {
background: linear-gradient(135deg, #0f766e, #134e4a);
color: #ffffff;
padding: 3rem 1.5rem;
}
article {
border-radius: 0.75rem;
box-shadow: 0 2px 8px rgb(0 0 0 / 12%);
}
article:hover {
box-shadow: 0 6px 18px rgb(0 0 0 / 18%);
}
Check that the white text on the gradient is comfortably readable, and that the card shadow stays subtle. Add the menu rules from the lesson for the navigation.
Key takeaways
- Font stacks end with a generic family; system fonts are fast and free, web fonts need
@font-face, care with weight and a check of the license. - Readable text means a generous
line-height, a limited line length (around 65ch) and left alignment. - Backgrounds can be colors, images or gradients; keep text readable over images and never rely on a background image for information.
border-radiusrounds corners;box-shadowadds depth when used subtly.- Typographic hierarchy comes from size, weight, color and spacing.