Lesson 21 / 26
Text Formatting
Whitespace processing rules, alignment and indentation, text-transform's dependence on language, and controlling underline decoration.
Contents
The font has arrived and the size scale is built. Decisions on the text itself remain: what happens to whitespace in the source text, how lines get aligned, how letters get converted, how links get underlined.
The first of these decisions is the most surprising one. In the document written in the Web Fundamentals and HTML curriculum, the text was written indented, and the indentation did not show up on screen. This lesson shows the reason for that, with a rule and a calculation.
Whitespace Processing
In the default behavior, consecutive whitespace characters in the source text get collapsed into a single space, and line breaks count as a space character too. This is what makes the HTML document formattable: indenting, line-splitting, and alignment happen freely in the source text without affecting the output.
The rule gets changed with the white-space property. The program
below applies five values to the same source text.
// whitespace.mjs — how white-space values process the same source text const SOURCE = "North Slope\n Station\tmeasurement readings"; const BEHAVIOR = { "normal": { collapseSpaces: true, preserveBreaks: false, wrap: true }, "nowrap": { collapseSpaces: true, preserveBreaks: false, wrap: false }, "pre": { collapseSpaces: false, preserveBreaks: true, wrap: false }, "pre-wrap": { collapseSpaces: false, preserveBreaks: true, wrap: true }, "pre-line": { collapseSpaces: true, preserveBreaks: true, wrap: true }, }; function process(text, rule) { let s = text; if (rule.preserveBreaks) { if (rule.collapseSpaces) s = s.split("\n").map((p) => p.replace(/[ \t]+/g, " ").trim()).join("\n"); } else { s = s.replace(/\s+/g, " ").trim(); } return s; } console.log("source (with escape sequences):"); console.log(JSON.stringify(SOURCE)); console.log(""); for (const [name, rule] of Object.entries(BEHAVIOR)) { const result = process(SOURCE, rule); console.log(`${name.padEnd(9)} wrap=${String(rule.wrap).padEnd(5)} -> ${JSON.stringify(result)}`); } console.log("\n--- text-transform: the Turkish special case ---"); const sample = "istasyon ILIK ışık"; console.log(`source : ${sample}`); console.log(`uppercase (en) : ${sample.toLocaleUpperCase("en-US")}`); console.log(`uppercase (tr) : ${sample.toLocaleUpperCase("tr-TR")}`); console.log(`lowercase (en) : ${sample.toLocaleLowerCase("en-US")}`); console.log(`lowercase (tr) : ${sample.toLocaleLowerCase("tr-TR")}`); console.log("\n--- text-indent and alignment effect (60-character box) ---"); const BOX = 60; const line = "Station sits at 1840 meters above sea level"; for (const [name, align] of [["start", (s) => s.padEnd(BOX)], ["end", (s) => s.padStart(BOX)], ["center", (s) => s.padStart((BOX + s.length) / 2).padEnd(BOX)]]) { console.log(`${name.padEnd(7)}|${align(line)}|`); }
source (with escape sequences): "North Slope\n Station\tmeasurement readings" normal wrap=true -> "North Slope Station measurement readings" nowrap wrap=false -> "North Slope Station measurement readings" pre wrap=false -> "North Slope\n Station\tmeasurement readings" pre-wrap wrap=true -> "North Slope\n Station\tmeasurement readings" pre-line wrap=true -> "North Slope\nStation measurement readings" --- text-transform: the Turkish special case --- source : istasyon ILIK ışık uppercase (en) : ISTASYON ILIK IŞIK uppercase (tr) : İSTASYON ILIK IŞIK lowercase (en) : istasyon ilik ışık lowercase (tr) : istasyon ılık ışık --- text-indent and alignment effect (60-character box) --- start |Station sits at 1840 meters above sea level | end | Station sits at 1840 meters above sea level| center | Station sits at 1840 meters above sea level |
The output distinguishes two axes: whether whitespace gets collapsed, and whether line-splitting gets allowed.
normal — consecutive spaces collapse to one, line breaks count as a
space, the line gets split to fit the box. The three spaces and the
tab character in the source text turned into a single space.
nowrap — does the same whitespace processing but does not allow
line-splitting. The text overflows the box; the situation established
in the Overflow Management lesson shows up.
pre — changes nothing: whitespace and line breaks are kept exactly
as they are, and no automatic line-splitting happens. This is the
browser’s default style for the pre element.
pre-wrap — keeps whitespace and line breaks but splits lines that
do not fit the box. This value gets written instead of pre when
long lines need showing without overflowing.
pre-line — collapses whitespace but keeps line breaks. It gets
used to show user text coming from a text field with its paragraph
structure.
Letter Conversion Depends on Language
The output’s second block shows why the text-transform property
cannot be considered a purely visual conversion.
The word istasyon becomes ISTASYON when uppercased by English
rules, and İSTASYON when uppercased by Turkish rules. The word
ILIK becomes ilik when lowercased by English rules, and ılık by
Turkish rules — two different words.
The reason is that in the Turkish alphabet, i and ı are separate
letters, and their uppercase counterparts are İ and I. The code
point distinction from the How Computers Work curriculum turns into a
difference in meaning here.
This shows that writing text-transform depends on the document’s
lang declaration. In a document where lang="tr" has not been
declared on the root element, converting to uppercase can give a
wrong result.
A second warning: the conversion happens only in drawing. The text in the document does not change; copied text comes in its source form, and a screen reader reads its source form. If a heading needs to appear in uppercase, this is a visual decision and gets made in the style sheet; the text itself should stay in its ordinary spelling in the source.
Alignment
text-align takes five basic values: start, end, center,
justify, and their physical counterparts left and right. The
logical values follow the writing direction, and that is the form
used in this course.
The output’s third block shows three alignments in a fixed-width box. The decision gets made by three criteria.
Centering works for short text; in long paragraphs, each line’s starting point shifts, making it harder for the eye to find the start of a line. It suits headings and short captions, not body text.
Justification (justify) makes every line the same length by
widening the word spacing. In narrow columns, the spacing widens
excessively and vertical white streaks form inside the text. The
result is more balanced when used together with hyphens: auto, but
left alignment still gets preferred in narrow columns.
Indentation (text-indent) pulls paragraph starts inward. If
vertical spacing exists between paragraphs, indentation is
unnecessary; when both get used together, the paragraph break gets
marked twice. In print tradition, indentation stands in for spacing.
Text Decoration
text-decoration is a shorthand and sets four longhands: -line
(where the line sits), -color, -style, -thickness.
Links being underlined comes from the browser default and is not a visual decoration: marking a link within text with color alone makes the link invisible to a reader who cannot perceive color distinctions. The underline should not get removed unless another visual distinguisher gets put in its place.
The underline’s legibility gets adjusted with text-underline-offset
and text-decoration-skip-ink; the second ensures the line does not
cut through letters’ descenders.
/* station.css — step 20: text formatting */ .measurement-table th { text-transform: uppercase; letter-spacing: 0.04em; font-size: var(--font-small); } .measurement-raw-data { white-space: pre-wrap; font-family: ui-monospace, monospace; tab-size: 2; } .observation-note { white-space: pre-line; } main a { text-decoration-line: underline; text-underline-offset: 0.15em; text-decoration-thickness: 1px; text-decoration-skip-ink: auto; } main a:hover { text-decoration-thickness: 2px; }
The .measurement-raw-data class shows the raw measurement record
coming from the station: line breaks and alignment spaces carry
meaning, so they get preserved; writing pre-wrap instead of pre
makes long lines split instead of overflow.
The letter spacing given to the header cells is a standard correction for uppercase-transformed text: capital letters look more cramped than lowercase, and reading gets harder if the spacing does not widen.
Summary
- In the default whitespace processing, consecutive spaces collapse to one and line breaks count as a space; the source text’s indentability rests on this.
white-spacesets two axes at once: whether whitespace gets collapsed, and whether line-splitting gets allowed.pre-wrapkeeps whitespace and splits the line,pre-linecollapses whitespace and keeps line breaks.text-transformdepends on language; becauseiandıhave separate uppercase counterparts in Turkish, a wrong conversion can result without alangdeclaration.- The conversion happens only in drawing; the text in the document does not change, and comes in its source form when copied.
- A link’s underline is not a decoration, it is a color-independent distinguisher; if it gets removed, another visual marker has to get put in its place.
Next Step
The text decisions are complete. The back of the boxes has not been looked at yet: a box’s background can be not just a color, but a positionable and repeatable image too. The next lesson takes up background layers, positioning and repetition rules, and which region the background extends to.
To keep your progress and take notes, Log in
My notes
Log in to take notes.