Skip to content
academia.sh

Lesson 21 / 25

Interface Text Guide

Turning an interface text guide into rules an auditor can check; a term glossary, tone rules, a per-class length limit, and the criterion that visible text be contained in the accessible name.

Contents

Every one of the seven patterns in the previous topic came down to text. A banner’s duration was computed from its message’s word count, an error message’s adequacy hinged on whether it named a recovery path, and a badge’s second channel was almost always a single word.

The Micro-States lesson measured interface text as a layout constraint: a label’s width, the overlap truncation produces, an error message’s line count. This lesson treats the same text as a system: how does an interface text guide stop being a list of personal style preferences and turn into rules an auditor can check?

What the Guide Solves and Where It Fails

An interface text guide keeps different people working on the same interface choosing the same words. The problem it solves is inconsistency: when one screen reads “borrow,” another “check out,” and a third “sign out,” the user assumes three separate actions.

The guide fails in three situations:

  • No example. “Write short and clear” is not a rule; it cannot be tested.
  • No counter-example. When the wrong thing is never written down, the rule leaves room for interpretation.
  • Nothing audits it. A guide read by hand stops being enforced as the catalog grows.

All three gaps point to the same fix: rules must be written so a machine can test them. The guide’s body can stay prose, but every rule needs an audit counterpart.

Four Rule Families

Term glossary. One word is chosen per concept; its synonyms are forbidden. The glossary is not a dictionary but a decision record, and the reason is written down too. If “record” is chosen, “material” and “item” are forbidden.

Tone. Three rules are enough: action labels are a short imperative, system messages never blame the user, and capitals and exclamation marks are never used for emphasis. The no-blame rule is about accuracy, not politeness — “you entered it wrong” gives the user no information, because it never names the shape the field expected.

Length. The limit changes with the text class: an action label is at most three words, an empty-state title six, help text fourteen, a message twenty. The numbers are arbitrary, chosen for that interface; what matters is that they can be audited.

Visible text versus name. The 2.5.3 Label in Name criterion requires the visible label to be contained in the accessible name. This looks like copy-editing, but it is a direct text decision: writing “Apply filter” on screen while the name stays “Apply” removes the criterion.

Auditing the Guide

The script below audits a fourteen-entry interface text catalog against the four rule families, then counts how many different words are used for each concept.

// text-audit.mjs — checking the interface text guide's four rules against the catalog

// Interface text catalog: each entry's class, visible text, and (for the checks) accessible name.
const CATALOG = [
  { key: "button.borrow",   class: "action",  text: "Borrow",                             name: "Borrow" },
  { key: "button.reserve",  class: "action",  text: "Reserve",                            name: "Reserve" },
  { key: "button.checkout", class: "action",  text: "Check out",                          name: "Check out" },
  { key: "button.filter",   class: "action",  text: "Refine results",                     name: "Apply" },
  { key: "button.clear",    class: "action",  text: "Clear filters",                      name: "Clear filters" },
  { key: "button.export",   class: "action",  text: "Export the selected records",        name: "Export the selected records" },
  { key: "title.empty",     class: "title",   text: "No records match the filters",       name: null },
  { key: "title.landing",   class: "title",   text: "SEARCH CATALOG RECORDS",             name: null },
  { key: "error.isbn",      class: "message", text: "ISBN entered wrong!",                name: null },
  { key: "error.date",      class: "message", text: "The return date must be after today. Pick a day from the calendar.", name: null },
  { key: "help.member",     class: "help",    text: "Your member number is on your card's back, below the barcode; enter without spaces.", name: null },
  { key: "help.material",   class: "help",    text: "You can narrow the results by selecting a different material type.", name: null },
  { key: "status.result",   class: "message", text: "128 records listed",                 name: null },
  { key: "button.remove",   class: "action",  text: "Remove",                             name: "Remove type tag" },
];

// --- Rule 1: term glossary --------------------------------------------------
// One word per concept; the others go unused throughout the course.
const TERM = {
  "borrow":  ["check out", "rent", "sign out"],
  "record":  ["material", "item", "product"],
  "filter":  ["refine"],
  "member":  ["customer", "subscriber"],
};

// --- Rule 2: tone ------------------------------------------------------------
const BLAMING = ["entered wrong", "entered incorrectly", "you forgot", "you must", "did not enter"];
const ABBREVIATION = new Set(["ISBN", "QR"]);

// --- Rule 3: length ----------------------------------------------------------
const LIMIT = { action: 3, title: 6, message: 20, help: 14 };

const words = (m) => m.trim().split(/\s+/);
const lower = (m) => m.toLocaleLowerCase("en");
// A plain substring search finds "rent" inside "different" and produces a false
// positive: matching must anchor at the start of a word and leave the end open.
const matches = (text, word) =>
  new RegExp(`(^|[^\\p{L}])${lower(word)}`, "u").test(lower(text));

const findings = [];
for (const c of CATALOG) {
  // 1. term
  for (const [correct, forbidden] of Object.entries(TERM))
    for (const f of forbidden)
      if (matches(c.text, f))
        findings.push({ key: c.key, rule: "term", detail: `"${f}" instead of "${correct}"` });

  // 2. tone
  if (c.text.includes("!"))
    findings.push({ key: c.key, rule: "tone", detail: "exclamation mark" });
  for (const b of BLAMING)
    if (matches(c.text, b))
      findings.push({ key: c.key, rule: "tone", detail: `blaming pattern: "${b}"` });
  for (const w of words(c.text)) {
    const plain = w.replace(/[^\p{L}]/gu, "");
    if (plain.length >= 4 && plain === plain.toLocaleUpperCase("en") && !ABBREVIATION.has(plain))
      findings.push({ key: c.key, rule: "tone", detail: `all caps: "${plain}"` });
  }

  // 3. length
  const n = words(c.text).length;
  if (n > LIMIT[c.class])
    findings.push({ key: c.key, rule: "length", detail: `${n} words, limit ${LIMIT[c.class]}` });

  // 4. 2.5.3 — the visible text must be contained in the name
  if (c.name !== null && !lower(c.name).includes(lower(c.text)))
    findings.push({ key: c.key, rule: "2.5.3", detail: `visible "${c.text}", name "${c.name}"` });
}

console.log(`catalog: ${CATALOG.length} entries, findings: ${findings.length}\n`);
console.log("key                 rule      detail");
for (const f of findings)
  console.log(`${f.key.padEnd(20)}${f.rule.padEnd(10)}${f.detail}`);

const tally = {};
for (const f of findings) tally[f.rule] = (tally[f.rule] ?? 0) + 1;
console.log("\nby rule: " + Object.entries(tally).map(([k, v]) => `${k}=${v}`).join(", "));

// --- How many different words name the same concept -------------------------
console.log("\nconcept        words used");
for (const [correct, forbidden] of Object.entries(TERM)) {
  const seen = new Set();
  for (const c of CATALOG) {
    if (matches(c.text, correct.split(" ")[0])) seen.add(correct);
    for (const f of forbidden) if (matches(c.text, f)) seen.add(f);
  }
  console.log(`${correct.padEnd(14)}${seen.size ? [...seen].join(", ") : "—"}   (${seen.size} form${seen.size === 1 ? "" : "s"})`);
}
catalog: 14 entries, findings: 10

key                 rule      detail
button.checkout     term      "check out" instead of "borrow"
button.filter       term      "refine" instead of "filter"
button.filter       2.5.3     visible "Refine results", name "Apply"
button.export       length    4 words, limit 3
title.landing       tone      all caps: "SEARCH"
title.landing       tone      all caps: "CATALOG"
title.landing       tone      all caps: "RECORDS"
error.isbn          tone      exclamation mark
error.isbn          tone      blaming pattern: "entered wrong"
help.material       term      "material" instead of "record"

by rule: term=3, 2.5.3=1, length=1, tone=5

concept        words used
borrow        borrow, check out   (2 forms)
record        record, material   (2 forms)
filter        refine, filter   (2 forms)
member        member   (1 form)

Ten findings come out of a small fourteen-entry catalog, and they fall into four kinds, each with a different cost.

Term drift shows up in three entries. The last table gives its total effect: two of the four concepts are named with two different words. For the user, that makes two concepts look like four. It is also a translation cost: every synonym produces a separate key and a separate decision in the translation catalog.

All caps produces three findings from a single title. Capitals are not a style choice: letter outlines converge, so reading slows down, and some screen readers treat an all-caps word as an abbreviation and spell it out letter by letter. Emphasis belongs to formatting decisions — weight, size, position.

The blaming pattern and the exclamation mark land on the same entry. “ISBN entered wrong!” does not say what was wrong, does not say what to do, and assigns the fault to the user. The same catalog’s error.date entry is the counter-example: “The return date must be after today. Pick a day from the calendar.” It gives the rule and the fix together, and trips no check.

The length limit is exceeded by exactly one entry. “Export the selected records” is four words, over the action limit; the width calculation in the Micro-States lesson shows where that limit comes from.

The 2.5.3 finding is the quietest defect. The button reading “Refine results” on screen has the name “Apply”; nothing looks wrong visually. Someone using voice control who says the word on screen never invokes the button. That the audit catches this is the direct payoff of writing the rule so a machine can test it.

The script’s matches function also avoids a general substring trap: a plain search finds “rent” inside “different” and would misfire. Matching instead anchors at the start of a word and leaves the end open — so “material” and “materials” are both caught, while “different” is not.

Decisions That Belong in the Guide

The audit enforces the rule but does not choose it. The guide’s prose section is where these decisions, and their reasons, get written down.

Who is being addressed. Interface text is written in the user’s language, not the system’s: “Create record” is the database’s business; “Add book” is the user’s.

Who is the subject. In system messages the subject is the system: “The request could not be sent.” In text describing the user’s own action, the subject is the user: “Items you have borrowed.” Mixing the two misplaces responsibility.

How numbers and dates are written. This decision belongs to the next lessons; the guide states it in a single line — never hand-formatted.

When abbreviations are allowed. Only if already in the user’s vocabulary; interface-internal abbreviations are forbidden.

Common Mistakes and How to Spot Them

A guide exists, but nothing audits it. How to spot it: scan the catalog against the forbidden-word list; a finding means the guide is not being followed.

The same key holds different text in two places. How to spot it: count how many words are used per concept; any count above one is a decision.

Visible text is not contained in the name. How to spot it: run the audit’s fourth rule; a visual review cannot catch this.

Text is embedded in code. With no catalog to audit, no rule can be enforced. How to spot it: check whether text is separated from code; the translation catalog set up in the Application Architecture: Routing, State and Data course is this audit’s prerequisite.

Summary

  • An interface text guide only holds if every rule has a counterpart a machine can test; rules with no example and no counter-example leave room for interpretation.
  • Four rule families are enough: a term glossary, tone, a per-class length limit, and the requirement that visible text be contained in the accessible name.
  • Term drift is not just inconsistency; it multiplies keys in the translation catalog — in a fourteen-entry catalog, two of four concepts had two different names.
  • All caps and exclamation marks are not emphasis tools; emphasis is carried by weight, size, and position.
  • The 2.5.3 criterion cannot be caught by visual review: the label shown on screen must be contained in the accessible name.
  • Word checks skip plain substring search; matching anchors at the start of the word and leaves the end open, catching inflected forms without false positives from coincidental substrings.

Next Step

The heaviest finding the audit caught was an error message, and its counter-example sat in the same catalog. The difference between the two messages is not style but structure: one says what is wrong, the other says what is right and how to fix it. The next lesson classifies error messages by their source, determines which parts are required for each class, and builds a rule set that audits messages against those parts.

To keep your progress and take notes, Log in

My notes

Log in to take notes.

Start typing to search.

↑↓ Esc navigate · open · close