---
title: Microcopy
source: 'https://academia.sh/en/courses/interface-fundamentals/microcopy'
course: 'Fundamentals of Interface Design'
language: en
updated: '2026-08-17T18:11:56+00:00'
license: 'CC BY-SA 4.0'
---

# Microcopy

Computing interface text as a layout constraint, the width budget of action labels, the prefix collisions produced by title truncation, and the three parts of an error message.

The previous lesson showed that an icon does not replace text, and handed the
discussion over to text. Interface text — button labels, field names, empty-state
sentences, error messages — has appeared in every lesson of this course, but none of
them treated it as a design decision. Text is not a slot to be filled in once the
design is finished; it is one of the layout's hardest constraints.

**Microcopy** is the functional text of an interface: the short pieces that name an
action, report a state, or describe a field. This lesson takes up three questions: how
much space does a label take, what does truncation break, and what parts does an
error message consist of?

## A Label Is a Width Budget

A button's width derives from its text. Different phrasings that name the same action
produce different widths, and those widths have to fit the action strip.

```js
// labels.mjs — action label width and fitting the action strip

// Candidate family's advance widths (em = 1000 units); the table from the Line Length lesson.
const G = {
  " ": 260, a: 556, b: 574, c: 500, d: 574, e: 545, f: 340, g: 574,
  h: 560, i: 244, j: 244, k: 520, l: 244, m: 856, n: 560,
  o: 570, p: 574, q: 574, r: 366, s: 480, t: 358, u: 560,
  v: 508, w: 800, x: 500, y: 508, z: 470, A: 660, B: 660, C: 686,
  D: 720, E: 610, F: 610, G: 740, H: 730, I: 280, J: 280, K: 660,
  L: 610, M: 890, N: 730, O: 760, P: 610, R: 660, S: 620,
  T: 620, U: 720, V: 660, W: 890, X: 660, Y: 620, Z: 600,
  ",": 260, ".": 260, ":": 260, "-": 340, "'": 200,
};
const DEFAULT = 560;
const SIZE = 16, PADDING = 16, GAP = 8;

const width = (s) => ([...s].reduce((t, c) => t + (G[c] ?? DEFAULT), 0) / 1000) * SIZE;
const buttonWidth = (s) => width(s) + 2 * PADDING;

const CANDIDATES = [
  ["long", ["Borrow", "Add to List", "Create Reservation"]],
  ["short", ["Borrow", "Add to List", "Reserve"]],
  ["very short", ["Take", "Add", "Reserve"]],
];

const STRIP = 360; // width remaining for the record-detail column on a narrow screen

console.log("label set     labels                                        button widths              total  360 px");
for (const [name, labels] of CANDIDATES) {
  const widths = labels.map(buttonWidth);
  const total = widths.reduce((a, b) => a + b, 0) + GAP * (labels.length - 1);
  console.log(
    `${name.padEnd(14)} ${labels.join(" / ").padEnd(44)} ${widths.map((v) => v.toFixed(0)).join(" ").padStart(22)} ${total.toFixed(0).padStart(7)}  ${total <= STRIP ? "fits" : "OVERFLOWS"}`
  );
}

// Width difference between different phrasings of the same action
console.log("\naction label           text width  button width");
for (const e of ["Borrow", "Please Borrow", "Borrow This Item", "Create Reservation", "Reserve"]) {
  console.log(`${e.padEnd(22)} ${width(e).toFixed(1).padStart(11)} ${buttonWidth(e).toFixed(1).padStart(13)}`);
}

// If equal-width buttons are used: the widest label determines everyone
const set = CANDIDATES[0][1];
const widest = Math.max(...set.map(buttonWidth));
const equalTotal = widest * set.length + GAP * (set.length - 1);
console.log(`\nif equal-width buttons are chosen: each button ${widest.toFixed(0)} px, total ${equalTotal.toFixed(0)} px`);
console.log(`variable-width total: ${(set.map(buttonWidth).reduce((a, b) => a + b, 0) + GAP * 2).toFixed(0)} px`);
console.log(`cost of switching to equal width: ${(equalTotal - (set.map(buttonWidth).reduce((a, b) => a + b, 0) + GAP * 2)).toFixed(0)} px`);
```

```
label set     labels                                        button widths              total  360 px
long           Borrow / Add to List / Create Reservation                85 111 171     384  OVERFLOWS
short          Borrow / Add to List / Reserve                            85 111 90     303  fits
very short     Take / Add / Reserve                                       68 61 90     235  fits

action label           text width  button width
Borrow                        53.3          85.3
Please Borrow                105.2         137.2
Borrow This Item             124.7         156.7
Create Reservation           139.3         171.3
Reserve                       58.4          90.4

if equal-width buttons are chosen: each button 171 px, total 530 px
variable-width total: 384 px
cost of switching to equal width: 146 px
```

The first table shows that text is a layout constraint. "Create Reservation" alone
produces a 171-pixel button; with three buttons placed side by side, the total rises to
384 pixels and does not fit the 360-pixel strip. The same three actions come down to
303 pixels with "Reserve" instead, and fit.

The conclusion that follows is that shortening is not a matter of style but a
measurement decision. The information lost in shortening is also accounted for:
"Create Reservation" and "Reserve" name the same action, but "Take" does not — the
information about what is being taken is lost.

The second table measures how far a courtesy word or the object of the action can
inflate a label. "Borrow" measures 53.3 pixels; the courteous form "Please Borrow"
comes to 105.2 pixels; the object-qualified form "Borrow This Item" comes to 124.7
pixels. Adding a word and naming the object can more than double the width. Action
labels take the short imperative form; this is not a matter of tone but a budget
decision of roughly seventy pixels.

The third block gives the cost of a layout habit. When equal-width buttons are chosen,
the widest label determines every button, and the total rises to 530 pixels — 1.4
times the variable-width total. This 146-pixel difference cannot be absorbed on a
narrow screen. Equal width is chosen only when the labels are already close in length
and the strip has room to spare.

## Truncation Can Destroy Distinctiveness

Record titles in the result list are truncated when they do not fit the column.
Truncation's visible cost is that part of the information disappears; its invisible
cost is that two different records become **identical** on screen.

```js
// truncation.mjs — title truncation, prefix collisions, and the three parts of an error message

const G = {
  " ": 260, a: 556, b: 574, c: 500, d: 574, e: 545, f: 340, g: 574,
  h: 560, i: 244, j: 244, k: 520, l: 244, m: 856, n: 560,
  o: 570, p: 574, q: 574, r: 366, s: 480, t: 358, u: 560,
  v: 508, w: 800, x: 500, y: 508, z: 470, A: 660, B: 660, C: 686,
  D: 720, E: 610, F: 610, G: 740, H: 730, I: 280, J: 280, K: 660,
  L: 610, M: 890, N: 730, O: 760, P: 610, R: 660, S: 620,
  T: 620, U: 720, V: 660, W: 890, X: 660, Y: 620, Z: 600,
  ",": 260, ".": 260, ":": 260, "-": 340, "'": 200, "…": 800,
};
const DEFAULT = 560;
const width = (s, px) => ([...s].reduce((t, c) => t + (G[c] ?? DEFAULT), 0) / 1000) * px;

function truncate(s, px, limit) {
  if (width(s, px) <= limit) return s;
  const ellipsis = width("…", px);
  let result = "";
  for (const c of s) {
    if (width(result + c, px) + ellipsis > limit) break;
    result += c;
  }
  return result + "…";
}

const TITLES = [
  "Light and Shadow",
  "Studies in the History of the Republic of Turkey Volume I",
  "Studies in the History of the Republic of Turkey Volume II",
  "Protocol Reference Manual",
];
const SIZE = 20; // record title size, scale step 1

console.log(`record title size: ${SIZE} px`);
for (const col of [180, 260, 340, 520]) {
  const truncated = TITLES.map((t) => truncate(t, SIZE, col));
  const unique = new Set(truncated);
  console.log(`\ncolumn ${col} px  (unique titles: ${unique.size} / ${TITLES.length})`);
  for (let i = 0; i < TITLES.length; i++) {
    const k = truncated[i];
    const colliding = truncated.filter((x) => x === k).length > 1;
    console.log(`  ${JSON.stringify(k).padEnd(52)} ${width(k, SIZE).toFixed(0).padStart(4)} px  ${colliding ? "COLLIDES" : k === TITLES[i] ? "full" : "truncated"}`);
  }
}

// The three parts of an error message: what happened, why, what to do
const PARTS = [
  { name: "what happened", text: "The ISBN number could not be validated" },
  { name: "why", text: "The last digit does not match the check digit" },
  { name: "what to do", text: "Retype the number from the back cover of the book" },
];
const MESSAGE_SIZE = 13, LINE_LIMIT = 525;
console.log("\npart           text width (13 px)  fits one line (525 px)");
let total = 0;
for (const p of PARTS) {
  const g = width(p.text, MESSAGE_SIZE);
  total += g;
  console.log(`${p.name.padEnd(14)} ${g.toFixed(1).padStart(19)}  ${g <= LINE_LIMIT ? "yes" : "no"}`);
}
const all = PARTS.map((p) => p.text).join(". ");
console.log(`all three combined: ${width(all, MESSAGE_SIZE).toFixed(1)} px, line count: ${Math.ceil(width(all, MESSAGE_SIZE) / LINE_LIMIT)}`);
```

```
record title size: 20 px

column 180 px  (unique titles: 3 / 4)
  "Light and Shadow"                                    165 px  full
  "Studies in the His…"                                 174 px  COLLIDES
  "Studies in the His…"                                 174 px  COLLIDES
  "Protocol Referenc…"                                  178 px  truncated

column 260 px  (unique titles: 3 / 4)
  "Light and Shadow"                                    165 px  full
  "Studies in the History of th…"                       257 px  COLLIDES
  "Studies in the History of th…"                       257 px  COLLIDES
  "Protocol Reference Manual"                           246 px  full

column 340 px  (unique titles: 3 / 4)
  "Light and Shadow"                                    165 px  full
  "Studies in the History of the Republ…"               336 px  COLLIDES
  "Studies in the History of the Republ…"               336 px  COLLIDES
  "Protocol Reference Manual"                           246 px  full

column 520 px  (unique titles: 4 / 4)
  "Light and Shadow"                                    165 px  full
  "Studies in the History of the Republic of Turkey Volume I"  510 px  full
  "Studies in the History of the Republic of Turkey Volume II"  516 px  full
  "Protocol Reference Manual"                           246 px  full

part           text width (13 px)  fits one line (525 px)
what happened                237.3  yes
why                          260.1  yes
what to do                   302.8  yes
all three combined: 813.7 px, line count: 2
```

Both volumes come down to the identical truncated title at all three narrower column
widths. Four records appear on screen as three distinct titles; the user cannot tell
from the list which volume they are borrowing. When the column widens to 520 pixels,
the collision disappears.

The fix for this finding is not to widen the column. Widening is not always possible,
and titles can always get longer still. The fix is to truncate in a way that preserves
the **distinguishing part**: the volume information is separated from the title and
moved to its own metadata field, shown in a field that is never truncated. The
precondition for a truncation decision is knowing which piece of information is
distinguishing.

The second rule is that the full form of the truncated text stays accessible.
Truncation is a presentation decision; the record's accessible name continues to carry
the untruncated title.

## An Error Message Has Three Parts

The last table measures the structure of an error message. A good message answers
three questions: what happened, why it happened, what to do. Each of the three stays
under the 525-pixel line limit on its own; combined into a single sentence, they rise
to 813.7 pixels and become two lines.

Two lines are not a flaw, but squeezing the parts into a single block of text is. Kept
separate, each part can be placed at a different typographic level: the "what
happened" part is brought forward with weight, while "why" and "what to do" stay at a
secondary text level. A user who recognizes the problem reads only the first part; one
who does not keeps reading.

Not all three parts are always necessary. The "why" part is written when the user
cannot guess the reason on their own; the ISBN's check digit is such a case. The "what
to do" part, by the rule set in the Error and Warning States lesson, is mandatory: if
the error is recoverable, the path to recovery has to be in the message.

## The Unchanging Rules of Text

The microcopy decisions accumulated along the course's through-line come together in a
few rules.

**The action label names the action.** Generic words like "OK" and "Submit" do not
tell the user, in a confirmation dialog, what they are confirming. The pair "Borrow"
and "Cancel" lets the user read what each button does straight from the text itself.
This is also the only setup in which the user can decide without reading the
surrounding message.

**A label is written in the user's language, not the interface's.** "Create Record" is
a database operation; "Add Book" is the task the user is doing. The catalog
interface's vocabulary is the library's vocabulary.

**The same thing is called by the same name everywhere.** If one place says
"borrowing," another says "checkout," and a third says "loan," the user assumes there
are three separate operations. The rule set for visuals in the Repetition and
Consistency lesson applies to text as well.

**Placeholder text is not a label.** The Contrast and Accessibility lesson computed
this: placeholder text cannot do a label's job because it fails to clear the contrast
threshold. A field's name stands above the field; the placeholder only shows a format
example.

## Summary

- Microcopy is a layout constraint; a label's width is computed and compared against
  the action strip's budget.
- Adding a courtesy word or naming the object noticeably lengthens a label; the short
  imperative form in action labels is a measurement decision.
- Equal-width buttons lock to the widest label and noticeably increase the total
  width; a narrow strip cannot absorb it.
- Truncation does not just cut information — it can make two records identical on
  screen; the distinguishing information is pulled out of the truncated field, and the
  full form of the truncated text stays accessible.
- An error message consists of what happened, why it happened, and what to do; the
  parts are kept separate and placed at separate typographic levels.
- A label names the action, is written in the user's language, and the same thing is
  called by the same name everywhere.

## Course Wrap-Up

This course opened with a single claim: every visual decision carries a function, and
it is design if it can be justified. Across twenty-two lessons, the library catalog
interface was where this claim was tested.

Four topics built on one another. **Visual Principles** established how attention is
ordered, how elements are grouped, and how consistency produces learnability.
**Layout and Spacing** set these principles into a skeleton: the grid, the spacing
scale, density decisions, and the constraints screen size and input type bring.
**Typography and Color** made the text and surface that fill the skeleton measurable —
type sizes, the typographic scale, line length, color roles, contrast thresholds, and
the theme transformation. **Component States** turned the static image into an
interface that behaves over time: interaction states, waiting, emptiness, error,
icons, and text.

Three rules recurred throughout the course, resurfacing in every topic. First, **a
critical distinction is expressed on at least two channels**; this rule showed up as
size and weight in hierarchy, as color and text in semantic colors, and as surface
and position in component states. Second, **once a decision is tied to a number,
argument turns into verification**; thresholds did their job because they were
checkable, even when chosen somewhat arbitrarily. Third, **the cheapest fix for a
problem is most often not adding an element but removing one**; reducing the number of
levels, dropping a border, writing the reason instead of disabling a button were all
examples of this rule.

There is something this course did not cover, and the boundary needs to be drawn
there. Every decision made here was the **visual** answer to "what should the
interface do." Which tasks the interface should support, in what order the user
performs those tasks, and when they need which piece of information stayed outside
this course. In the catalog interface, the "search field, result list, record detail,
borrow" flow was given from the start; why it was that flow was never asked.

M15/K02 User Experience and Behavior Design opens with this question. It builds the
chain from user research to design decision: it covers when observation, interview,
and survey methods are chosen, how user profiles grounded in real data are built, and
how information architecture and flow design make task completion easier. What this
course measured was the inside of a screen; what the next course measures will be the
path between screens.
