Readability Is a Layout Problem, Not a Decoration
Most of what people do on your page is read it. Before reaching for anything decorative, three settings do more for a page than every other text property combined: the space between lines, the length of a line, and the contrast of the text against its background.
Line height is the vertical space each line occupies. Browsers default to somewhere around 1.2, which is fine for a short heading and too tight for a paragraph — the eye loses its place jumping back to the start of the next line. Body text wants roughly 1.5 to 1.7. Write it unitless (line-height: 1.6) so it scales with each element's own font size instead of being frozen at one value.
Line length matters just as much and is ignored far more often. A paragraph stretched across a wide monitor can run to two hundred characters, and the return sweep to the next line becomes tiring. Typographers have long recommended somewhere around 60 to 75 characters per line. CSS gives you a unit made for this: ch, the width of the character "0" in the current font. Setting max-width: 65ch on your text container is a one-line fix that instantly makes a page feel professional.
Headings are the opposite case. They are short, large and read in a glance, so they want tighter line height than body text — around 1.1 to 1.25 — otherwise a two-line heading looks like two unrelated lines.
/* The three settings that matter most */
body {
font-size: 16px;
line-height: 1.6; /* comfortable for reading */
color: #222222; /* strong contrast, softer than pure black */
}
.prose {
max-width: 65ch; /* about 65 characters per line */
}
h1, h2, h3 {
line-height: 1.2; /* headings want tighter spacing */
} line-height,color,font-familyandtext-alignare all inherited, so setting them once onbodyreaches the entire page.text-decoration,background-color, margins and padding are not inherited and must be set where you want them.
Aligning and Spacing a Block of Text
text-align takes four values you will actually use. left is the default for left-to-right languages and the right answer for nearly all body text. center suits headings, buttons and short captions, but a centred paragraph gives the eye a ragged left edge to hunt for on every line, so keep it to two or three lines at most. right is mostly for numbers in a table, where aligned decimal points are easier to compare.
justify is the one that looks tempting and usually disappoints. It stretches the spaces between words so both edges line up, which works in a newspaper because the columns are narrow and the text is hyphenated. On the web, hyphenation is off unless you ask for it, so a justified paragraph on a phone ends up with huge gaps between words and pale rivers of white running down the page. If you want it, turn on hyphenation with it — and set the language on your HTML, because the browser needs to know which language's rules to hyphenate by.
Three more properties fine-tune the spacing. letter-spacing adjusts the gap between characters; a small positive value (0.03em or so) can make short uppercase labels much easier to read, while body text is usually best left alone. word-spacing does the same between words and is rarely needed. text-indent indents only the first line of a block, which is the traditional way to mark paragraphs in print — on the web, a margin between paragraphs does the same job more clearly.
/* The everyday cases */
.article { text-align: left; }
.page-title { text-align: center; }
.amount { text-align: right; }
/* Justify, done properly */
.justified {
text-align: justify;
hyphens: auto; /* needs lang="en" (or the right language) on your HTML */
}
/* Small caps-style label */
.eyebrow {
text-transform: uppercase;
letter-spacing: 0.08em;
font-size: 12px;
}
/* First-line indent, print style */
.book-style p { text-indent: 2em; margin-bottom: 0; } text-align—left,right,center,justify; inherited by childrenline-height— unitless values scale with font size and are the safe choiceletter-spacing— useemso it scales; helpful on uppercase labels, harmful on body textword-spacing— adjusts the gap between words; rarely neededtext-indent— indents the first line only, not the whole blockwhite-space: pre-wrap— keeps the line breaks you typed instead of collapsing them
Case, Underlines, and What Links Need
text-transform changes how text is drawn without changing the text itself. uppercase on a button label leaves the underlying HTML as it was, so copying the text gives you the original wording, and a search on the page still matches it. That makes it far better than typing the label in capitals by hand — you can change your mind about the styling later without editing content, and assistive technology reads the real words.
text-decoration is a shorthand that can set the line, its colour, its style and its thickness. That is more control than it once had, and it makes the default underline much nicer to live with. text-underline-offset pushes the line further from the text so it stops slicing through the descenders of letters like g and y.
Which brings us to the most common mistake in this lesson: a { text-decoration: none; } written across the whole site. Inside a navigation bar or a button that is fine — the surrounding design already makes it obvious those are clickable. Inside a paragraph it is a genuine problem. The underline is the only signal that does not depend on colour, and a reader who cannot distinguish your link colour from the body text now has no way to find the link at all. Keep underlines in running text and style them properly instead of deleting them.
/* Style the label, keep the content */
.button { text-transform: uppercase; }
/* Modern underline control */
.article a {
color: #0a58ca;
text-decoration: underline;
text-decoration-thickness: 1px;
text-underline-offset: 3px;
}
.article a:hover {
text-decoration-thickness: 2px;
}
/* Removing the underline is fine HERE, where the design says "link" */
.site-nav a,
.btn {
text-decoration: none;
}
/* Other decoration values */
.sold-out { text-decoration: line-through; color: #999999; }
.typo { text-decoration: underline wavy #d1039e; } text-transform: capitalizecapitalises the first letter of every word, including words that should stay lowercase, and it cannot fix names like "iPhone". For anything where the exact wording matters, write the text the way it should appear and leave the property alone.
Long Words, Overflow and Truncation
Real content breaks layouts. A pasted URL, an email address or a long product code has no spaces in it, so the browser cannot wrap it, and it runs straight out of its container and sometimes off the side of the page. overflow-wrap: break-word tells the browser it may break inside a word, but only when that word would otherwise overflow. It is a safe thing to set on any container that shows text you did not write yourself, such as comments or user profiles.
The opposite need also comes up: forcing text to stay on one line. white-space: nowrap does that, and it is what you want on a table header or a button whose label must not split awkwardly across two lines.
Truncating with an ellipsis is where people get stuck, because text-overflow: ellipsis on its own does nothing at all. It needs three declarations working together: the text must be prevented from wrapping, the box must clip what does not fit, and only then does the property have anything to replace with dots. Miss any one of the three and you get either wrapped text or a hard cut with no ellipsis.
Trimming to a fixed number of lines instead of one is the single place in this course where a vendor-prefixed property is still the practical answer. The -webkit-line-clamp combination below is what card designs across the web use, and it works in current browsers despite the prefix in its name.
/* Let unbreakable strings break rather than overflow */
.comment {
overflow-wrap: break-word;
}
/* Keep a label on one line */
.table th { white-space: nowrap; }
/* One-line truncation needs all three declarations */
.filename {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
max-width: 240px;
}
/* Trim a card summary to three lines */
.card__summary {
display: -webkit-box;
-webkit-line-clamp: 3;
-webkit-box-orient: vertical;
overflow: hidden;
} overflow-wrap: break-word— break a long word only when it would otherwise overflowwhite-space: nowrap— never wrap this texttext-overflow: ellipsis— needsnowrapandoverflow: hiddenalongside it- The element also needs a width it can actually run out of, otherwise nothing is ever clipped
- Truncated text still exists in the HTML, so search engines and screen readers see all of it
- If the full text matters, do not truncate — shorten the content instead
