# 3 · Humans: how the people you design for actually behave

You know humans from text. You have never squinted at a tiny button, given up
on a form, or felt stupid because a label made no sense. The people using
your interface do these things every day.

This file translates what designers know about people into rules you can
apply. Each has a human reason. When no rule fits, reason from the **Why**.

---

## Part A · How people see a screen

### A1 · People scan, they don't read
**Rule:** Put the most important words first: in headings, labels, buttons
and list items. Headings must make sense without the paragraph below them.
Break text into short chunks with clear subheadings.
**Why:** People skim the left edge and the first words of each line, then
decide whether to read more. Anything buried mid-paragraph is unseen.
**Agent trap:** you write complete, polite sentences ("In this section you
can manage your notification preferences"). Write "Notifications" instead.
**Check:** read only the headings and button labels. Can you still tell what
the page does and what to do next?

### A2 · Hierarchy: one thing is most important
**Rule:** Each view has **one** primary action, styled as the strongest
element. Secondary actions are visibly quieter. Use size, weight and
position for hierarchy before you use colour.
**Why:** When everything shouts, people hesitate, because they can't tell what
matters. (Von Restorff effect: the one thing that differs is the one that's
noticed. Make it the right thing.)
**Agent trap:** every button you create is a filled accent button.
**Check:** blur the screenshot or squint. The first thing you see should be
the main content, then the primary action.

### A3 · Things close together are read as related
**Rule:** Space between groups must be clearly larger than space within
groups, at least 2× on your scale (for example, 8 inside, 24 between). A
label sits closer to its own input than to the one above.
**Why:** Gestalt proximity. People infer structure from spacing before they
read a word. Even spacing everywhere reads as one undifferentiated list.
**Agent trap:** you use `gap: 16px` everywhere, so nothing is grouped.
**Check:** list the spacing values between groups vs within groups. Between
must be larger.

### A4 · Things that look alike are read as alike
**Rule:** Same function, same appearance. Different function, visibly
different appearance. Don't style a link like a button or a label like a link.
**Why:** Gestalt similarity. People learn your visual language within seconds
and then trust it. Breaking it makes them stop and doubt.
**Check:** every clickable thing looks clickable; nothing non-clickable does.

### A5 · How a control looks is a promise about what it does
*Added in v0.2 after a designer's review: "What can members and admins do?"
was styled as a link (accent colour, underline on hover). The designer
expected a dialog or side panel. Instead it expanded inline and pushed the
whole page down.*

**Rule:** Before styling any interactive element, decide what it **does**,
then give it the look that promises exactly that:

| It does this | It must look like | Never |
|---|---|---|
| Goes to another page or place, **standalone** (a back link, "View all", a nav item) | A link: `<a href>`, text style, **no underline at rest, underline on hover and keyboard focus**. Its icon or position already says "link" | A permanent underline (visual noise); a button that navigates without saying so |
| Goes to another page or place, **inside a sentence** | A link: `<a href>`, **always underlined** | Colour alone: colour-blind readers can't find it in the text |
| Performs an action here | A button: `<button>`, filled, outlined or ghost | Plain text, or a link style |
| Expands or collapses content **in place** | An accordion row with a chevron that turns, the content opening inside the same container | A link style; a row with no arrow |
| Opens a dialog or side panel | A button, ideally with an ellipsis ("Edit…") or a panel icon | An inline expand |
| Shows a short hint | An info icon with a tooltip or popover that floats **over** the page | Inline text that pushes content down |

**Why:** People predict what will happen before they click, from how
something looks. When the result doesn't match the prediction, they feel
the interface did something wrong, even if it worked. Underlined, coloured
text means "take me somewhere" to almost everyone.

**Agent trap:** you pick the semantics correctly (`<details>`, the right
element for expanding) but then restyle it to "look clean", which removes
the arrow and makes it look like a link. The markup is right; the promise
is wrong.

**Second agent trap** *(designer review: "the underline only appears on
this type of button when we hover over it")*: you give every link one
style, always underlined. Inside a paragraph, that's correct. On a
standalone link like "← Back to tasks", the arrow already says "link", and
a permanent underline just adds noise.

**Check:** run `checks/affordance.js` on the rendered page. It flags things
underlined like links that aren't links, links that go nowhere, and
expanding rows with no arrow or chevron. Then fill in this table for every
interactive element: *looks like / does / match?*

### A6 · Don't move what people are looking at
**Rule:** Content may move only when the person caused it **and** the
control told them it would (a chevron, a "Show more" label). Otherwise,
show extra information **over** the page (popover, tooltip, dialog, side
panel) or reserve its space in advance. Never let loading content, late
images, banners or error messages push the page down under someone's
cursor or finger.
**Why:** Unexpected movement makes people lose their place, misclick on
whatever slid under the pointer, and distrust the interface.
**Agent trap:** you insert a message or panel into the page flow because it
was the easiest place in the code.
**Check:** click every expanding or revealing control. If anything below
it moved, did the control promise that movement? If not, change the
pattern.

---

## Part B · How people decide

### B1 · More choices, slower decisions (Hick's law)
**Rule:** Show only the choices needed for the current step. Put advanced
options behind a disclosure. Give a recommended default.
**Why:** Each extra option adds decision time and doubt. A good default
means most people don't have to decide at all.
**Agent trap:** the request lists 9 features, so you put 9 equal options on
the first screen.
**Check:** count the choices visible on first load. Could any move to a
later step or behind "More"?
**Don't over-apply it:** Hick's law is about *unrelated* choices competing
for attention. It is **not** a reason to hide the options a person is
actively choosing between. When the screen's job is a decision (which plan,
which shipping option), show the options side by side; see B5.

### B2 · People expect your product to work like the others (Jakob's law)
**Rule:** Use conventions: logo top-left links home, search has a magnifier,
settings are under the avatar or a gear, primary button on the right of a
dialog's button row (or follow the platform), destructive actions in red.
Be original in the content and brand, not in the controls.
**Why:** People spend most of their time in other products. That's where they
learned how interfaces work.
**Agent trap:** inventing a "creative" navigation pattern to look unique.

### B2b · Direction and place follow meaning
*Added in v0.2 after a designer's review: "Back to tasks" sat at the right
end of the global header. "These are always on the page, not on the
navigation header … always on the left side, not on the right side. We are
going back, not forward."*

**Rule:**
- **Back goes left; forward goes right.** In left-to-right languages,
  "back", "previous" and "cancel and return" sit on the left, with a
  left-pointing arrow. "Next", "continue" and "forward" sit on the right.
  In step flows: [Back] on the left, [Next] on the right. Mirror all of
  this for right-to-left languages.
- **Page navigation belongs to the page.** A back link or breadcrumb sits at
  the top-left of the page content, on its own row above the page title.
  The global header holds only global things: logo, product or workspace
  switcher, search, account.
- **Keep columns aligned.** When the page has columns, put the back link on
  a row above all of them, so the column titles still line up.

**Why:** People read direction spatially: left is where they came from,
right is where they're going (in left-to-right languages). A back link on
the right reads as "forward". Placed in the global header, it reads as
part of the app's chrome instead of "leave this page", and it's far from
the title it relates to.

**Agent trap:** the header has empty space on the right, so you put the
back link there to balance the layout. Balance is a visual reason; direction
is a meaning reason, and meaning wins.

**Check:** `checks/affordance.js` flags back controls in the right half of
the screen or inside the global header, and Back placed after Next.

### B5 · Give people what they need to decide, on the screen where they decide
*Added in v0.2 after a designer's review. The agent built Plan & billing as
"your plan + one upsell". The version built without the module showed all
plans side by side, seats used against the limit, and the next payment and
date, and the designer preferred it: "This type of thing helps the user make
the decision."*

**Rule:** When a screen asks the person to decide something (upgrade,
downgrade, choose a plan, pick an option, change a limit), show all of
this **at a glance**, without opening anything:

1. **Where they are now:** current option, clearly marked.
2. **Usage against limits:** "8 of 20 seats used" as a number **and** a bar,
   with plain words when it's nearly full or full.
3. **What happens next if they do nothing:** next payment, amount and date.
4. **Every option side by side,** in the same structure, so they can compare
   line by line (same order of price, total, features, action).
5. **The consequence for them,** not just the list price: "For your 6 people:
   $84.00 a month", "Too small for your 6 people".
6. **A recommended next step:** one filled button; the other options quieter.
7. **What's blocked, and how to unblock it:** "To switch, your team needs to
   be 3 people or fewer", rather than a disabled button with no reason.

**Why:** People don't decide from a sales pitch; they decide by comparing.
If the facts are spread across dialogs or hidden behind "Upgrade", they
either leave to work it out elsewhere or decide badly and regret it.
Showing cost *for their team* removes the maths they'd otherwise do in
their head.

**Agent trap:** other rules in this module ("show fewer choices", "avoid
identical cards", "don't build what wasn't asked for") can push you to
strip a decision screen down to one upsell. Those rules protect attention
on screens where people *aren't* choosing. On a decision screen, the
comparison **is** the job.

**Check:** for every decision screen, answer the seven points above from
the screen alone. Any you can't answer is missing.

### B3 · Recognition over recall
**Rule:** Show options; don't make people remember them. Keep context
visible (what step, what item, what was entered). Prefer selects, chips and
suggestions over blank inputs when the set of values is known.
**Why:** Working memory is small, about four items. Anything you make people
hold in their head is something they'll get wrong.

### B4 · Complexity has to live somewhere (Tesler's law)
**Rule:** When something is complicated, put the complexity in the system,
not on the person. Accept phone numbers with or without spaces. Infer the
city from the postcode. Pre-fill what you know.
**Why:** Every bit of complexity you don't handle, the human has to.
**Agent trap:** strict input validation because it was easier to code.
**Check:** for each input, ask: what formats would a person reasonably type?
Accept all of them (Postel's law).

---

## Part C · How people act

### C1 · Bigger and closer is easier to hit (Fitts's law)
**Rule:** Touch targets at least 44×44px; never under 24×24px. Put frequent
actions near where attention already is. On mobile, keep primary actions in
the lower half of the screen where thumbs reach. Leave space between
destructive and safe actions.
**Why:** Small or distant targets cause misses, especially for people with
tremors, older users, and anyone on a bus.
**Check:** measure the hit area of every interactive element.

### C2 · Show what's happening, fast
**Rule:**
- Under 0.1s: feels instant. Respond to every click within this time,
  even if only with a pressed state.
- Around 1s: people notice. Show a spinner or a pressed/loading state.
- Over 10s: people leave. Show progress and let them do something else.
Disable buttons while submitting, and say what's happening ("Saving…").
**Why:** Silence makes people click again (causing double submits) or assume
it broke.
**Agent trap:** an async button with no loading state, because the demo had
no network delay.

**How a loading button must look** *(added in v0.2 after a designer's review:
"It just shows 'Sending…', which is not good. It should show something
visual, like a spinner.")*
- Show a **spinner** inside the button. Text alone ("Sending…") doesn't read
  as activity: it reads as a label that changed.
- The button **keeps its exact size.** Stack the busy content over the
  original label (kept invisible) so the width can't change. If spinner +
  text doesn't fit, show the spinner alone and keep the text for screen
  readers.
- Drop the "…" from the loading label ("Sending"): the spinner already says
  "in progress", and the dots cost width.
- Keep the spinner turning even with reduced motion: it's essential feedback,
  not decoration.
- Mark the button `aria-busy="true"` and disabled while busy.
**Agent trap:** swapping the label text for "Saving…" changes the button's
width, so it jumps, shrinks or grows in the middle of the action. Measured in
experiment 01: the old button shrank 15px and shifted sideways.

### C2b · Put feedback where it won't move anything
*Added in v0.2 after a designer's review: the invite confirmation appeared as
text beside the Send button. When the message was long, it wrapped and pushed
the button onto a new line.*

**Rule:** Choose where a message goes by what it is:

| Message | Where it goes | Why |
|---|---|---|
| Success ("Invite sent") | A **toast** (corner, Undo) **or** a **confirmation dialog** (centred, with next steps). This is a fork: ask the human which they want (`1-brief.md` Step 3b) and record it in `DESIGN.md` | Both float over the page, so nothing moves. Which one fits depends on the product |
| Error about one field | **Next to that field** | They need to see what to fix, where to fix it |
| Error about the whole action (offline, server) | Inline, in a **reserved column beside the button** that wraps inside itself | It must stay until fixed; a toast would disappear |
| A state ("Unsaved changes") | The same reserved column | Short, and relevant to that form |

Wherever inline text sits next to a control, the control **must not move**
when the text gets longer: give the text its own column (`flex: 1;
min-width: 0`), never let the row wrap, and align the button to the top.

**Why:** A button that jumps after you press it makes people misclick,
or doubt that the action worked. Success messages are also short-lived
by nature; they don't need permanent space in the layout.

**Agent trap:** the easiest place in the code for a message is a `<p>` next
to the submit button, so that's where it goes. It looks fine with the short
demo text you tested, and breaks with a real email address.

**Check:** run `checks/stability.js` on every action with **long, realistic
content** (a 50-character email, a long error) at 375px and 1280px. The
control you used must never move or resize.

### C3 · Prevent errors before handling them
**Rule:** Constrain inputs to valid values where possible. Confirm
destructive actions **with specifics** ("Delete 'Q3 Report'? This can't be
undone."). Better: offer undo instead of a confirmation.
**Why:** People don't read generic "Are you sure?" dialogs; they click
through them. Undo respects them more and works better.

### C4 · Error messages: what happened, how to fix it
**Rule:** Put the message next to the problem, in plain words, saying how to
fix it. Keep what the person typed. Never blame them.
Bad: "Invalid input." Good: "Enter a date after today."
**Why:** An error is the moment people are most likely to give up.
**Check:** trigger every error. Does each say how to fix it?

---

## Part D · How people feel

### D1 · Pretty hides problems (aesthetic–usability effect)
**Rule:** Never let visual polish stand in for checking usability.
**Why:** People rate attractive interfaces as easier to use, even when they
aren't, so polished UI hides its flaws from the human reviewing it. This is
exactly how "looks good, broken inside" gets shipped.
**Agent trap:** this is your most important one. A good-looking screenshot
convinces you, and the human, that the work is done.

### D2 · People remember the peak and the end (peak–end rule)
**Rule:** Spend extra care on the moments that matter most (the first
success, the moment of payment, the error) and on the end of every flow:
the confirmation, the "you're done" state.
**Why:** People judge an experience by its most intense moment and how it
ended, not by the average.

### D3 · Progress motivates (goal gradient)
**Rule:** In multi-step flows, show where people are and how much is left.
Save progress so they can return.
**Why:** People speed up as they near a goal, and abandon tasks that feel
endless.

---

## Part E · People are not all like the default

Design for the range, not the average.

| Who | What they need | Rule |
|-----|---------------|------|
| Low vision | Contrast and zoom | Text contrast ≥ 4.5:1 (≥ 3:1 for text ≥ 24px, or ≥ 19px bold). UI boundaries and icons ≥ 3:1. Works at 200% zoom. |
| Colour blindness (~1 in 12 men) | Not colour alone | Pair colour with text, icon or pattern. Red/green alone means nothing to many people. |
| Keyboard users | Focus and order | Everything reachable by Tab in a logical order, with a clearly visible focus ring. |
| Screen reader users | Meaning in markup | Labels, alt text, landmarks, live regions for updates. Icon-only buttons need `aria-label`. |
| Motion sensitivity | Calm | Respect `prefers-reduced-motion`. No auto-playing or looping animation over 5s. |
| Older users | Size and clarity | 16px minimum body text; clear labels; no hover-only information. |
| Non-native readers | Plain language | Short sentences, common words, no idioms. Text can grow 30%+ in translation. |
| Slow connections and old devices | Speed | Show content before decoration. Don't make everything wait for a large script. |

**Agent trap:** you assume the user is a young, sighted, fluent English
speaker with a fast laptop and a mouse, because most examples you learned
from silently assume that.

---

## Checklist

- [ ] Headings and buttons make sense when skimmed alone.
- [ ] One primary action per view.
- [ ] Every control looks like what it does; `checks/affordance.js` is clean.
- [ ] Nothing moves unless the person caused it and the control promised it.
- [ ] Space between groups ≥ 2× space within groups.
- [ ] Only the needed choices shown; good defaults set.
- [ ] Targets ≥ 44×44px on touch.
- [ ] Every async action shows a spinner, and the button keeps its size.
- [ ] Success messages float (toast); errors sit next to the problem; nothing pushes a button.
- [ ] `checks/stability.js` passes on every action, with long content, at 375 and 1280.
- [ ] Destructive actions are specific, and undoable where possible.
- [ ] Every error says how to fix it.
- [ ] Contrast passes; colour is never the only signal; keyboard works.
