# When text runs out of room

> Decide per field whether text wraps, truncates at the end or the middle, or stays whole; stop long words breaking boxes; and hold the size and contrast floors.

Module: Typography · Level: intermediate · 7 min · https://school.agents.org.in/design/typography/when-text-runs-out

Three files, all named almost the same. Narrow the column with end-truncation on, and try to tell which is which.

*[Interactive demo: End, middle, or wrap. Tap a row to see the full name.]*

With end-truncation they all become `Q3-board-report-fi…`. Identical. Switch to middle-truncation and the part that differs, the end, survives.

## Truncation deletes information

Truncation (Cutting text off with an ellipsis (…) when it doesn't fit. Only allowed when there is a way to see the full text, such as a tooltip or detail view) means cutting text with an ellipsis (…). The skill treats it as deleting information, so it is only allowed **with a route back** to the whole string: a tooltip that also opens on keyboard focus, an expand control, or a detail view. A clipped ID with no way to read it is data loss wearing CSS.

Choose per field, never one rule for the whole app:

| Field | Do this |
|---|---|
| What identifies the thing (a name, a page title) | **Wrap.** Two lines is fine; four means the column is too narrow. |
| Secondary info whose start matters (a role, a message preview) | **End-truncate.** |
| Things that differ at the end (file names, paths, hashes, emails on one domain) | **Middle-truncate.** CSS can't; split the string in code and keep the tail. |
| Card previews | **Clamp at 2 lines** with `line-clamp` (CSS that cuts text after a set number of lines, adding an ellipsis. Used to keep card previews the same height) so cards stay one height. |
| Prices, counts, dates, IDs | **Never cut.** Widen the column or truncate the label beside them. |

The CSS for the two common cases:

```css
.one-line { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.preview  { display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2; line-clamp: 2; overflow: hidden; }
```

## Guard against overflow both ways

Two opposite failures, each with one line of CSS.

**A long word punches out of its box.** A URL, a hash or a user's 40-letter username has nowhere to break, so it pushes past the card edge. Add `overflow-wrap: anywhere` (CSS that lets a very long word, like a URL or a code, break onto the next line instead of punching out of its box) wherever user text, links or codes can appear.

*(Not `break-word`: it looks the same but leaves the element's minimum width alone, so a flex item still overflows.)*

**A short phrase breaks where it shouldn't.** A badge that wraps to "In / review", or a button label split over two lines, reads as a bug. Those get `white-space: nowrap`.

  **Before (avoid):**

A comment card with `https://example.com/projects/acme/issues/4821?view=board` running straight off its right edge.

  **After (do this):**

The same card with `overflow-wrap: anywhere` on its body: the URL breaks onto a second line inside the card.

## Casing and punctuation are presentation

- **Store copy in sentence case.** If a label should look uppercase, use `text-transform: uppercase`. ALL-CAPS in the source gets rewritten at the next redesign and some screen readers spell it out.
- **Type real punctuation.** Curly quotes in prose (“like this”), an en dash for ranges (Mon–Fri), an em dash for asides, the single ellipsis character …, and a non-breaking space between a number and its unit (`12&nbsp;MB`) so they never split. Code keeps straight quotes.

## Underlines that look designed

Default underlines often slice through descenders and sit at a random thickness. Take the metrics from the font:

```css
a {
  text-decoration-thickness: from-font;
  text-underline-position: from-font;
  text-decoration-skip-ink: auto;
}
```

A dotted underline means "there's more here", which is why the glossary terms in this course have one. Never underline something that isn't a link. And if you want an animated underline, only its *color* animates reliably; a growing line is a separate element.

## The floors

A few numbers you never go under:

- **Inputs at 16px or more on phones.** Below that, iPhone Safari zooms the whole page when you tap the field. Don't "fix" it with `maximum-scale=1`; that removes zoom for everyone, an accessibility failure.
- **Body 16px, dense controls 14px, captions 13px.** Under 12px needs an unusual excuse.
- **Contrast 4.5:1 for body, 3:1 from about 24px**, as in the surface module.
- **Direction-neutral CSS**: `text-align: start` and `padding-inline-start` instead of left, and a `lang` on the page, so the layout mirrors for right-to-left languages.

**A typography fix should be the smallest diff.** It never restructures markup unless an expandable truncation or an animated underline needs an element.

**Handle long and short text (paste into your agent):**

```text
Review how text fits in these components:
- Per field, choose: wrap (names, titles), end-truncate (secondary metadata), middle-truncate (file names, paths, hashes) or line-clamp: 2 (card previews). Never cut prices, counts, dates or IDs.
- Every truncation needs a way to the full string (tooltip that also opens on focus, expand, or detail view).
- overflow-wrap: anywhere wherever URLs or user text appear; white-space: nowrap on badges and button labels.
- Inputs at least 16px on mobile; never maximum-scale=1.
- Sentence case in source, text-transform for caps; real quotes, dashes and … in copy.
```

**Check yourself:** A list shows file paths that share a long common prefix. How should they be shortened?

- [ ] End-truncate with an ellipsis
- [x] Middle-truncate, keeping the end where they differ, with a way to see the full path
- [ ] Shrink the font until they fit
- [ ] Hide the paths

*Why:* End-truncation makes them identical. Middle-truncation keeps the distinguishing tail, and the full path stays reachable.

## In the skill

- https://school.agents.org.in/skill/typography/line-behavior.md
