UI/UX Design Module 1 – UX foundations: the field, the process and the people
Memory, decisions and movement behind UI rules
What working memory, choice time, pointing and waiting mean for screens, with Hick-Hyman, Fitts's law, 24-pixel targets and the 0.1, 1 and 10 second limits.
What you will learn
- Design for recognition rather than recall, and cut the working-memory load of multi-step tasks
- Apply the Hick-Hyman law and the choice-overload evidence when deciding how many options to show
- Calculate Fitts's index of difficulty and use it, with WCAG 2.2's minimum target size, to size and place targets
- Match feedback to the response-time limits of 0.1, 1 and 10 seconds and to the 200 ms INP threshold
Before you start
What you’ll learn · Before you start
What you will learn
- Design for recognition rather than recall, and cut the working-memory load of multi-step tasks
- Apply the Hick-Hyman law and the choice-overload evidence when deciding how many options to show
- Calculate Fitts's index of difficulty and use it, with WCAG 2.2's minimum target size, to size and place targets
- Match feedback to the response-time limits of 0.1, 1 and 10 seconds and to the 200 ms INP threshold
Before you start
On this page
On the last screen of a checkout, a MealBox customer may have to type a coupon code from last week’s email, pick one of fourteen delivery slots and reach a Pay now button 600 pixels below the field they just filled. Each request leans on a limited part of the person: a working memory that holds about four chunks, choice times that grow with the options, and a hand that slows down as targets get smaller and further away.
Interface rules such as “recognition rather than recall” or “make important buttons big” come from that research. Here each rule gets its evidence and its limits: what memory research says about menus, what the Hick-Hyman law and choice overload mean for how many options to show, how to rate a target’s index of difficulty and its WCAG 2.2 size, and how to match feedback to the wait.
Working memory holds about four chunks, so show what people need
To use last week’s coupon, a customer finds the email, holds a code such as MB-LUNCH-4417 in mind, switches back to
the app and types it. Holding it is the job of working memory: The small store that holds what a task needs right now, such as a code being copied or two prices being compared. Its central limit is about four chunks., the small store for
what a task needs right now.
George Miller’s 1956 paper is quoted as “seven, plus or minus two”, but it described two limits. Judging one dimension absolutely, such as which of several tones was played, tops out at about 2.6 bits, roughly 6.5 categories. The span of immediate memory is about seven items and counts chunks, not bits: people stretch it by chunking: Recoding several items into one larger familiar unit, such as reading 4417 as one number, so that memory holds more information in the same few chunks., recoding input into larger familiar units. Miller called them “quite different kinds of limitations”. In 2001 Nelson Cowan examined studies in which people could not rehearse or recode and argued for a central limit of about four chunks, reading Miller’s seven as “a rough estimate and a rhetorical device”.
Neither number limits a menu: a menu is on the screen, so people scan its items instead of holding them. The limit warns against making people carry facts from one place to another. Jakob Nielsen’s sixth usability heuristic, recognition rather than recall: A usability heuristic: make elements, actions and options visible, so that people pick what they need from the screen instead of having to remember it., asks for “making elements, actions, and options visible”, and Ben Shneiderman’s eighth golden rule warns against making people remember information from one display to use it on another. Here is a first draft of MealBox’s account pages beside its redesign:
| The draft asks people to recall | The redesign lets them recognise |
|---|---|
| The coupon code from last week’s email | The coupons that apply, each with what it saves |
| The plan code in the welcome message | Each plan by name, price and lunches a week |
| Which days they paused last month | Last month’s calendar, paused days marked |
Each row moves a fact from the person’s head onto the screen; recall stays for secrets such as passwords.
Capping every menu at seven items
Wrong: “Miller showed people handle seven things, so a menu may hold seven items at most.” Right: a menu is read, not remembered. Size it by how quickly people find and compare its items.
Checkpoint
Check: Recognition over recall and the four-chunk limit
In multi-step tasks, remove the load the design adds
Changing a MealBox lunch plan takes four decisions: which plan, which days, which address, and whether to pay. A design can pile more on top: a price shown on one step and needed on the next, an address the service knows typed in again, a label nobody understands.
John Sweller’s 1988 study of problem solving showed that solving by Solving a problem by comparing where you are with the goal, again and again, and choosing each step to shrink the difference. took so much processing capacity that little was left for learning. cognitive load: The mental work a task takes. Intrinsic load comes from the task itself; extraneous load is added by the way it is presented, and design should remove it. theory, which grew from that work, separates the load the material brings from the load its presentation adds. In design terms, as Nielsen Norman Group puts it, intrinsic load is the effort of taking in new information and tracking one’s goals, while extraneous load uses mental resources without helping. Designers cannot remove the first; they should remove the second, for example by showing again what people entered before and by setting a smart default.
Step through a draft of the plan change and its redesign. The decisions stay; watch the remembering:
The drawing shows the whole scene; its 6 steps are listed below.
Every step as text
- Four steps, four decisions. A MealBox customer changes their lunch plan in four steps. Each step holds one real decision: which plan, which days, where to deliver, whether to pay. That is the load the task itself needs, and no design can remove it.
- A price held in mind. The draft shows the price per lunch only on step 1. To judge what five days a week will cost, the customer must carry that price to step 2 in working memory and multiply in their head: load the design adds.
- A trip back to check. Customers who forget the price go back to step 1 to read it again. Every trip back costs time and is a moment to give up, and none of it helps them decide.
- Typing what is on file. Step 3 is an empty address form, although MealBox delivers to this customer already. Retyping a known address is extraneous load and a fresh chance for a typing error.
- The redesign carries it. The redesign keeps a summary of the plan, the days and the weekly total on every step, and offers the saved address with a link to change it. Nobody needs the trip back any more.
- Review is recognition. The last step lists every choice in one place, so checking means recognising what is shown rather than recalling it. The customer makes the same four decisions with far less to hold in mind.
The redesign removed the price held in mind, the trip back to step 1 and the retyped address, and kept every decision. Steps help when each holds one decision and earlier answers travel along: Shneiderman’s fourth rule asks for groups of actions with a beginning, a middle and an end, and feedback when each group is done.
Key idea
For each step, list what it makes people remember from earlier steps or other places: each item is load the design adds, and putting it on the screen removes it without removing a decision.
Checkpoint
Check: Load the task needs and load the design adds
Choice time grows with the log of the options people know
A worker at a MealBox kitchen taps a station button for each new order and knows by heart which station each dish goes to. With eight stations instead of two the choice takes longer, but how much longer?
In 1952 W. E. Hick timed choice reactions with up to ten alternatives and found information taken in at a roughly constant rate, of the order of five bits a second; in 1953 Ray Hyman showed that the time follows the information the signal carries, also for alternatives that are not equally likely. The Hick-Hyman law: Choice time grows in a straight line with the information in a choice: T = a + b log2(n + 1) for n equally likely options that the person already knows. is usually written for n equally likely options as:
T is the time to choose, a the time any response takes and b the time each extra bit adds; the 1 is usually explained as one more alternative, that no signal comes at all. With the first-day fit below, a = 201 ms and b = 149 ms per bit, eight options take about 674 ms and sixteen take 811 ms: doubling the options added 137 ms.
The program fits the law by least squares to invented mean times of one person who knows the options, on the first day and after a week of practice:
"""Fitting the Hick-Hyman law, T = a + b log2(n + 1), to choice times.
The mean times are invented for this lesson: one person choosing among n
equally likely options whose meaning and place they already know, on the
first day and after a week of practice. A least-squares line through the
points (log2(n + 1), T) gives a, the time that does not depend on how many
options there are, and b, the time each extra bit of choice adds.
"""
from math import log2
OPTIONS = [1, 2, 4, 8]
MEAN_TIMES_MS = {
"first day": [352, 431, 556, 671],
"after practice": [257, 293, 345, 398],
}
def fit_line(xs, ys):
"""Least squares: the a and b that make a + b * x closest to ys."""
n = len(xs)
mx, my = sum(xs) / n, sum(ys) / n
b = sum((x - mx) * (y - my) for x, y in zip(xs, ys)) / sum((x - mx) ** 2 for x in xs)
return my - b * mx, b
bits = [log2(n + 1) for n in OPTIONS]
print("options n: " + "".join(f"{n:>7}" for n in OPTIONS))
print("log2(n + 1): " + "".join(f"{h:>7.2f}" for h in bits))
for label, times in MEAN_TIMES_MS.items():
a, b = fit_line(bits, times)
print()
print(f"{label}: T = {a:.0f} ms + {b:.0f} ms per bit")
print(" measured ms: " + "".join(f"{t:>7}" for t in times))
print(" fitted ms: " + "".join(f"{a + b * h:>7.0f}" for h in bits))
for n in (16, 32):
print(f" predicted for {n} options: {a + b * log2(n + 1):.0f} ms") Output
options n: 1 2 4 8 log2(n + 1): 1.00 1.58 2.32 3.17 first day: T = 201 ms + 149 ms per bit measured ms: 352 431 556 671 fitted ms: 350 438 548 674 predicted for 16 options: 811 ms predicted for 32 options: 954 ms after practice: T = 191 ms + 66 ms per bit measured ms: 257 293 345 398 fitted ms: 256 295 343 399 predicted for 16 options: 459 ms predicted for 32 options: 522 ms
Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 hick_fit.py
Runs on this device, in your browser. The first run downloads Python (about 13.5 MB), which is kept for the next runs.
Your run, in this browser
After practice the slope falls from 149 to 66 ms per bit, and 32 options take 522 ms instead of 954. Real data agree: in a 2018 experiment the extra time for a larger set of responses fell by 43 % with practice, although Hyman had still found sizeable effects after 15,000 trials.
The law describes people choosing among options they know. A newcomer facing an unfamiliar list must first search it, reading until the right item turns up: Andy Cockburn, Carl Gutwin and Saul Greenberg’s model of menu performance treats a novice’s time as visual search and only an expert’s as a Hick-Hyman decision. The logarithm is good news for familiar options; on a first visit, finding the item fast is what counts.
Change the data
Press Edit, change the first day’s 671 to 900, as if a few slow trials had pulled that mean up, and run it: b jumps from 149 to 249 ms per bit and a falls to 57 ms.
Checkpoint
Check: The Hick-Hyman law and when it applies
Long lists do not paralyse people on average
If every option costs time, should screens show as few as possible? choice overload: The claim that more options make people less willing to choose, or less happy with their choice. A meta-analysis of 50 experiments found its mean effect to be about zero. is the claim that more options can make people less willing to choose, or less happy with their choice. Some studies found strong effects in the lab and in the field; others found none, or found that more choice helped.
In 2010 Benjamin Scheibehenne, Rainer Greifeneder and Peter Todd pooled 63 conditions from 50 published and unpublished experiments, with 5,036 participants. The mean effect was “virtually zero”, with considerable variance between studies, and they found possible preconditions but no conditions that reliably produced overload. Options do cost something when people must compare them, because comparing means holding options in mind; Nielsen Norman Group counts comparison tables as external memory for that reason. Four claims heard in design critiques, against this lesson’s research:
| What is claimed | What the research supports |
|---|---|
| “Seven items at most, because of Miller” | Miller’s limits concern judging and remembering |
| “Every extra option slows the choice a lot” | For known options, each doubling adds about the same time |
| “More choice always paralyses” | Across 50 experiments the mean effect was about zero |
| “So show everything, always” | No: remove what nobody needs and help people compare the rest |
Structure is what helps finding and comparing: a sensible order, labelled groups, a smart default where one choice fits most people, and side-by-side comparison where options differ in several ways.
Key idea
None of the three famous findings sets a number of options: decide by how easily people find, compare and choose, and check it with the people who will use the screen.
Checkpoint
Check: What the choice-overload research shows
Distance and width set how hard a target is to hit
On MealBox’s review screen the thumb rests on the Delivery note field, the last one filled. Pay now is a wide button across the bottom, 600 pixels away; Remove item is a small icon at the right edge. Which is harder to hit?
Paul Fitts showed in 1954 that pointing time grows with the distance to a target and falls with its size. Scott MacKenzie’s 1992 work argues for the Shannon form of Fitts's law: Pointing time grows in a straight line with a target's index of difficulty, MT = a + b × ID, so targets that are far away and small take longer to hit., which gives a movement’s index of difficulty: How hard a pointing movement is, in bits: log2(D/W + 1), with D the distance to the target's centre and W the target's width along the movement. in bits:
D is the distance from the start to the target’s centre and W the target’s width along the line of movement. For Pay now, 600 px away and 48 px tall, ID = log2(600 ÷ 48 + 1) = log2(13.5) = 3.75 bits. Time then rises in a straight line, MT = a + b × ID, with a and b measured for a device and its users: MacKenzie and Bill Buxton’s mouse study fitted 230 ms plus 166 ms per bit. The 1 keeps ID at zero or above even when the start lies inside a large target, where Fitts’s original form goes negative.
For a rectangle, W measured along the approach is the extent a moving finger crosses; MacKenzie and Buxton found that taking the smaller of width and height predicted times as well, and both beat always taking the horizontal width. This lesson takes the smaller side.
Taking the long side as W
Wrong: “Pay now spans the screen, so it is easy to hit”: with its 328 px width as W it rates 1.50 bits. Right: a thumb moving down meets its 48 px height first, so W is 48 and it rates 3.75 bits. Make it taller or bring it closer; wider alone does not help.
The drawing shows the screen at half scale, with a dashed line from the start to each action’s centre:
Four actions on the review screen, measured from the last field filled
Text description of the diagram
The figure draws the review step of the MealBox checkout, a 360 by 800 CSS pixel phone screen, at half its size. Near the top is the Delivery note field, drawn with a dashed outline; a dot at its centre is labelled "start", because it is the last field a customer fills before acting. Four buttons are drawn with a blue fill and a thick outline, and a dashed line runs from the start dot to the centre of each one, so the length of each line is the distance D.
Change address is a 120 by 32 button just below the field: W is 32 and its index of difficulty is 2.67 bits. Remove item is a 24 by 24 icon at the right edge, partway down: W is 24 and it rates 3.70 bits. Apply coupon is an 80 by 40 button lower down on the right: W is 40 and it rates 3.64 bits. Pay now is a 328 by 48 button across the bottom of the screen, 600 pixels from the start: W is its height, 48, and it rates 3.75 bits, the hardest of the four to reach although it is the widest.
The program measures the same four actions, then tries Pay now at other sizes and distances:
"""Fitts's index of difficulty for the actions on one MealBox checkout screen.
The layout is drawn for this lesson: the review step of the checkout at
360 x 800 CSS px, origin at the top left, every box (left, top, width, height).
D is the distance from the start point to the centre of a target, W is the
smaller of its width and height (MacKenzie and Buxton, 1992), and
ID = log2(D / W + 1), the Shannon form (MacKenzie, 1992).
"""
from math import dist, log2
START = (180, 140) # centre of the "Delivery note" field, the last one filled
TARGETS = {
"Pay now": (16, 716, 328, 48),
"Apply coupon": (264, 560, 80, 40),
"Change address": (16, 260, 120, 32),
"Remove item": (308, 380, 24, 24),
}
def centre(box):
left, top, width, height = box
return (left + width / 2, top + height / 2)
def index_of_difficulty(d, w):
return log2(d / w + 1)
print(f"Start point {START}: the centre of the last field filled")
print(f"{'action':<16}{'D (px)':>8}{'W (px)':>8}{'ID (bits)':>11}")
for name, box in TARGETS.items():
d = dist(START, centre(box))
w = min(box[2], box[3])
print(f"{name:<16}{d:>8.1f}{w:>8}{index_of_difficulty(d, w):>11.2f}")
print()
print("What if: the Pay now button, reached from the same start point")
CASES = [
("as drawn: 48 px tall, 600 px away", 600, 48),
("half as tall: 24 px", 600, 24),
("its 328 px long side taken as W", 600, 328),
("moved up: 200 px away", 200, 48),
]
for label, d, w in CASES:
print(f" {label:<34} D {d:>3}, W {w:>3}: {index_of_difficulty(d, w):.2f} bits") Output
Start point (180, 140): the centre of the last field filled action D (px) W (px) ID (bits) Pay now 600.0 48 3.75 Apply coupon 457.1 40 3.64 Change address 171.2 32 2.67 Remove item 288.3 24 3.70 What if: the Pay now button, reached from the same start point as drawn: 48 px tall, 600 px away D 600, W 48: 3.75 bits half as tall: 24 px D 600, W 24: 4.70 bits its 328 px long side taken as W D 600, W 328: 1.50 bits moved up: 200 px away D 200, W 48: 2.37 bits
Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 fitts_id.py
Runs on this device, in your browser. The first run downloads Python (about 13.5 MB), which is kept for the next runs.
Your run, in this browser
Halving Pay now’s height to 24 px raises it to 4.70 bits, and moving it to 200 px from the field lowers it to 2.37: doubling W or halving D takes off about one bit whenever D is many times W.
Change the layout
Press Edit and make Remove item 44 by 44 pixels, (308, 380, 44, 44): its distance grows to 301.9 px, but W almost
doubles and its index of difficulty falls from 3.70 to 2.97 bits.
Checkpoint
Check: The index of difficulty of a target
WCAG 2.2 sets 24 by 24 CSS pixels as the floor
Fitts’s law says bigger is faster but sets no minimum. WCAG 2.2 does, for another reason: people with tremors or limited dexterity, and anyone with an imprecise pointer, hit the wrong control when small targets sit close together. Success Criterion 2.5.8, Target Size (Minimum), at level AA, requires a target size: The area of a control that a pointer or finger can activate. WCAG 2.2 Success Criterion 2.5.8 asks for at least 24 by 24 CSS pixels, with five exceptions. of at least 24 by 24 CSS pixels for pointer input: a 24-pixel square must fit inside the target. Five exceptions apply:
- Spacing: an undersized target is fine if a 24 px circle centred on its bounding box meets no other target and no other undersized target’s circle.
- Equivalent: another control on the page that meets the criterion does the same thing.
- Inline: the target is in a sentence, or the line height of the text around it sets its size.
- User agent control: the browser sets the size and the author has not changed it.
- Essential: the presentation is essential to the information, or legally required.
Success Criterion 2.5.5, at level AAA, asks for 44 by 44 CSS pixels, and the W3C’s Understanding document recommends the 24-pixel size even where spacing would allow less, and 2.5.5 for important controls. The program runs both tests on the three rows of six icons from that document’s example, then on a 16 px tall MealBox link under a button:
"""Checking WCAG 2.2 SC 2.5.8 Target Size (Minimum) on rows of buttons.
A target meets the size rule when a 24 by 24 CSS px square fits inside it. An
undersized target can still pass by spacing: a circle 24 px across, centred on
it, must not intersect another target or another undersized target's circle.
The first three rows follow the W3C Understanding document's example; the
MealBox rows are drawn for this lesson. Boxes are (left, top, width, height).
"""
from math import dist
def row(size, gap, count=6):
return [(i * (size + gap), 0, size, size) for i in range(count)]
def big_enough(box):
return box[2] >= 24 and box[3] >= 24
def centre(box):
return (box[0] + box[2] / 2, box[1] + box[3] / 2)
def circle_hits(point, box):
"""True when the 24 px circle round point overlaps box (touching is fine)."""
nearest = (min(max(point[0], box[0]), box[0] + box[2]), min(max(point[1], box[1]), box[1] + box[3]))
return dist(point, nearest) < 12
def failing(targets):
bad = []
for i, box in enumerate(targets):
if big_enough(box):
continue
others = [t for j, t in enumerate(targets) if j != i]
if any(circle_hits(centre(box), t) or (not big_enough(t) and dist(centre(box), centre(t)) < 24) for t in others):
bad.append(i)
return bad
ROWS = {
"six 24 x 24 icons, no gap": row(24, 0),
"six 20 x 20 icons, 4 px gaps": row(20, 4),
"six 20 x 20 icons, no gap": row(20, 0),
"MealBox: 16 px link 2 px under a button": [(16, 0, 328, 48), (16, 50, 64, 16)],
"MealBox: the same link 4 px under it": [(16, 0, 328, 48), (16, 52, 64, 16)],
}
print(f"{'row':<42}{'small':>6}{'failing':>9} result")
for name, targets in ROWS.items():
small = sum(not big_enough(t) for t in targets)
bad = failing(targets)
print(f"{name:<42}{small:>6}{len(bad):>9} {'fail' if bad else 'pass'}") Output
row small failing result six 24 x 24 icons, no gap 0 0 pass six 20 x 20 icons, 4 px gaps 6 0 pass six 20 x 20 icons, no gap 6 6 fail MealBox: 16 px link 2 px under a button 1 1 fail MealBox: the same link 4 px under it 1 0 pass
Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 target_spacing.py
Runs on this device, in your browser. The first run downloads Python (about 13.5 MB), which is kept for the next runs.
Your run, in this browser
The 20 px icons meet the criterion with 4 px gaps, where their centres are 24 px apart and the circles only touch, and fail with none. The link fails 2 px below the button and meets it 4 px below: its circle reaches 12 px from its centre, 4 px beyond its 8 px half height.
Treating the spacing exception as a goal
Wrong: “the icon is only 16 px, but it has room around it, so it is fine.” Right: give every control you design at least 24 by 24 CSS pixels, and primary actions far more.
Checkpoint
Check: WCAG 2.2 minimum target size
Feedback has to match the wait: 0.1, 1 and 10 seconds
Adding a dish should show at once; next week’s menu may take a second; a year’s invoices may take half a minute. In 1968 Robert Miller asked “response time to what?”, and Jakob Nielsen’s 1993 summary gives three response-time limits: About 0.1 second feels instant, about 1 second keeps the flow of thought, and about 10 seconds is the limit for keeping attention on a task.: about 0.1 second feels instantaneous, about 1 second keeps the flow of thought although the delay is noticed, and about 10 seconds is the limit for keeping attention on the task.
Nielsen Norman Group’s guidance on progress indicators turns them into choices: give some feedback at once, show an indicator for anything over about 1 second, keep looped animations for about 2 to 10 seconds, use a percent-done indicator from 10 seconds, and never leave a static “Loading…” alone. Past 10 seconds, Nielsen adds a clearly signposted way to interrupt. Choose for an action you know:
Answer the questions above: the table then marks what fits your case and says why the rest does not.
| Feedback | Under 0.1 s | 0.1 to 1 s | 1 to 10 s | Over 10 s |
|---|---|---|---|---|
| Result only Shows: the result itself, nothing before it | Good the result arrives within the limit for feeling instant | Mixed the delay is noticed, but the flow of thought holds | Poor nothing shows the action was received, so people try again | Poor attention leaves the screen with no sign of progress |
| Pressed state Shows: the control changes the moment it is used | Good the change itself is the feedback | Good says the input landed while the result follows | Mixed needed at once, but not enough on its own after about 1 s | Mixed needed at the start, then something that shows progress |
| Looped indicator Shows: a spinner or looping bar, with a label | Poor a flash too short to read | Poor under 1 s a looping animation distracts more than it tells | Good suits 2 to 10 s: it shows the work goes on | Poor past 10 s it gives no sense of how much is left |
| Percent-done bar Shows: how much is done, time left, a way to stop | Poor far too heavy for a change this quick | Poor it is gone before anyone can read it | Mixed fits a few seconds when the work has countable parts | Good shows how much is done and roughly how long is left |
| Notify when done Shows: a message once the result is ready | Poor a message about an instant change is noise | Poor people are still looking at the screen | Poor too short a wait to switch to another task | Good frees people to do other work, with a way back to the result |
How the answers decide
- “How long does the action take at the slow end, on the devices and networks your users have?” Under 0.1 s: rules out Looped indicator, Percent-done bar and Notify when done. Under 0.1 s the result is the feedback: anything that animates or reports progress would only flash past.
- “How long does the action take at the slow end, on the devices and networks your users have?” 0.1 to 1 s: rules out Looped indicator, Percent-done bar and Notify when done. Between 0.1 and 1 s people notice the delay but keep their train of thought; a looping animation that short only distracts.
- “How long does the action take at the slow end, on the devices and networks your users have?” 0.1 to 1 s: adds a caveat to Result only. The result alone can feel sluggish this close to 1 s: let the control react at once, then show the result.
- “How long does the action take at the slow end, on the devices and networks your users have?” 1 to 10 s: rules out Result only and Notify when done. After about 1 s a screen that shows nothing looks broken, and a few seconds is too short a wait to send people off to other work.
- “How long does the action take at the slow end, on the devices and networks your users have?” 1 to 10 s: adds a caveat to Pressed state. React at once, then add an indicator that says what is happening: the pressed state alone stops being enough after about 1 s.
- “How long does the action take at the slow end, on the devices and networks your users have?” 1 to 10 s and “Is the work made of parts you can count, such as 3 of 50 addresses done?” No, or the total is unknown: rules out Percent-done bar. For a few seconds of work with no parts to count, a percent-done bar is overkill and its numbers would be guesses.
- “How long does the action take at the slow end, on the devices and networks your users have?” 1 to 10 s and “Is the work made of parts you can count, such as 3 of 50 addresses done?” Yes, it has countable parts: adds a caveat to Percent-done bar. A count such as "address 3 of 50" suits a few seconds of work made of parts that people understand.
- “How long does the action take at the slow end, on the devices and networks your users have?” Over 10 s: rules out Result only and Looped indicator. Past 10 s attention leaves the screen: show how much is done, roughly how long is left and a way to stop.
- “How long does the action take at the slow end, on the devices and networks your users have?” Over 10 s: adds a caveat to Pressed state. The pressed state still confirms the start, but it cannot carry a long wait on its own.
- “How long does the action take at the slow end, on the devices and networks your users have?” Over 10 s and “Is the work made of parts you can count, such as 3 of 50 addresses done?” No, or the total is unknown: adds a caveat to Percent-done bar. With no known total, report the work done so far in absolute terms, such as each record as it is processed, rather than a percentage.
In short: the result itself under 0.1 s, an instant reaction up to 1 s, an indicator from 1 to 10 s, and beyond that progress with a way to stop, or a message when it is done.
INP checks the first limit in the field
Interaction to Next Paint (INP): A web metric: the slowest click, tap or key press of a visit, from the input to the next painted frame. 200 ms or less at the 75th percentile is good., INP, watches every click, tap and key press of a visit and reports the slowest, ignoring one in every 50, each measured from the input to the next frame painted. web.dev calls 200 ms or less good, up to 500 ms “needs improvement” and more than that poor, read at the 75th percentile of page loads in the field, phones and desktops apart. Research points to about 100 ms; 200 ms was chosen because most sites can reach it, and in 2022 data 56 % of phone origins met it, against 12 % at 100 ms. Step through one tap and add up what INP counts:
The drawing shows the whole scene; its 6 steps are listed below.
Every step as text
- The tap. A customer taps the Veg only filter on the MealBox menu page. The clock of the interaction starts at the tap itself, not when the code starts. The times in this scene are invented.
- Input delay: 120 ms. The main thread is still busy with a script that started before the tap, so the tap waits 120 ms before any of its event handlers can run. That wait counts too.
- Event handlers: 90 ms. The filter's event handlers run for 90 ms: they drop the dishes with meat and rebuild the list of dishes on the page.
- Next frame: 50 ms. The browser works out the new layout and paints the next frame, 50 ms later. Only now does the customer see the filtered menu.
- One interaction: 260 ms. From the tap to the next paint took 120 + 90 + 50 = 260 ms, more than the 200 ms that web.dev counts as good. All three parts counted, including the wait before any of the filter's code ran.
- From one tap to a page's INP. A page's INP is its slowest interaction, ignoring one for every 50. Field data then reads the 75th percentile of page loads, separately for phones and desktops: the page is good when that value is 200 ms or less.
The tap waited, ran its code and painted: 260 ms in all. Because INP ends at the next paint, a control that changes at once keeps it low even when the work behind it takes seconds; that slower work then needs the feedback of the table above.
Checkpoint
Check: Feedback for the wait, and INP
Practise: bring three checkout actions within reach
The exercise gives you the delivery step of the MealBox checkout as Python data. Fix the function that computes the index of difficulty, then move and resize three actions until each is 3.0 bits or less from the last field, with no overlaps and nothing under 24 by 24 CSS pixels:
Exercise · Easy · Python
Size and place three checkout actions with Fitts's law
The delivery step of the MealBox checkout is a 360 x 800 CSS px screen, written as Python data in checkout_layout.py: every target is a box (left, top, width, height), and START is the centre of the last field a customer fills, "Flat and building". From there they must reach three primary actions, which sit far away and are small. Change two things in the file:
- Fix
index_of_difficulty(start, box). It must returnlog2(D / W + 1), where D is the distance fromstartto the centre of the box and W is the smaller of the box's width and height, as the lesson does for rectangles. The starter takes the width, so it rates a wide, short button as easy. - Move and resize the three boxes in
PRIMARY(save-address,choose-slot,continue) so that each has an index of difficulty of 3.0 bits or less fromSTART.
Keep to the rules of a real screen: every target is at least 24 x 24 CSS px (WCAG 2.2 SC 2.5.8, without relying on its spacing exception), no two targets overlap, every box stays inside the screen, and each primary action is at most 64 px tall, a button rather than a panel. START, SCREEN and the eight targets in FIXED stay exactly as they are.
The sample tests work out every action's index of difficulty with their own formula, then check the sizes, the overlaps and the edges of the screen.
Starter code · checkout_layout.py
"""The delivery step of the MealBox checkout, 360 x 800 CSS px (a layout drawn for this exercise).
Every box is (left, top, width, height) in CSS px, with the origin at the top left of the screen.
"""
from math import dist, log2
SCREEN = (360, 800)
START = (180, 408) # the centre of the "Flat and building" field, the last one filled
# Targets that stay where they are.
FIXED = {
"back": (8, 8, 44, 44),
"help": (308, 8, 44, 44),
"name": (16, 128, 328, 48),
"phone": (16, 192, 328, 48),
"street": (16, 256, 328, 48),
"landmark": (16, 320, 328, 48),
"flat": (16, 384, 328, 48),
"terms": (16, 712, 160, 24),
}
# The three primary actions: move and resize these.
PRIMARY = {
"save-address": (296, 64, 48, 20),
"choose-slot": (16, 600, 100, 22),
"continue": (16, 748, 328, 36),
}
def index_of_difficulty(start, box):
"""Fitts's index of difficulty, in bits, of moving from start to box: log2(D / W + 1)."""
left, top, width, height = box
d = dist(start, (left + width / 2, top + height / 2))
w = width
return log2(d / w + 1) The sample tests · test_checkout_layout.py
from math import dist, log2
from checkout_layout import FIXED, PRIMARY, SCREEN, START, index_of_difficulty
FIXED_AS_GIVEN = {
"back": (8, 8, 44, 44),
"help": (308, 8, 44, 44),
"name": (16, 128, 328, 48),
"phone": (16, 192, 328, 48),
"street": (16, 256, 328, 48),
"landmark": (16, 320, 328, 48),
"flat": (16, 384, 328, 48),
"terms": (16, 712, 160, 24),
}
def bits(start, box):
left, top, width, height = box
return log2(dist(start, (left + width / 2, top + height / 2)) / min(width, height) + 1)
def overlap(a, b):
return a[0] < b[0] + b[2] and b[0] < a[0] + a[2] and a[1] < b[1] + b[3] and b[1] < a[1] + a[3]
def test_index_of_difficulty():
"""index_of_difficulty takes W as the smaller side of the box"""
cases = [
("a 328 x 48 button 600 px away", (180, 140), (16, 716, 328, 48), 3.75),
("a 24 x 200 bar 300 px away", (12, 400), (0, 0, 24, 200), log2(13.5)),
("a start point at the target's centre", (50, 50), (40, 40, 20, 20), 0.0),
]
for label, start, box, expected in cases:
got = index_of_difficulty(start, box)
assert abs(got - expected) < 0.01, f"{label}: got {got:.2f} bits, expected {expected:.2f}"
def test_start_and_fixed_targets_unchanged():
"""the start point, the screen and the other eight targets stay as given"""
assert START == (180, 408)
assert SCREEN == (360, 800)
assert FIXED == FIXED_AS_GIVEN
def test_three_primary_actions():
"""the three primary actions are still there, and only they"""
assert sorted(PRIMARY) == ["choose-slot", "continue", "save-address"]
def test_each_action_at_most_3_bits():
"""every primary action has an ID of 3.0 bits or less from the start point"""
for name, box in PRIMARY.items():
assert bits(START, box) <= 3.0, f"{name}: {bits(START, box):.2f} bits"
def test_every_target_at_least_24_px():
"""every target is at least 24 x 24 CSS px, and every action at most 64 px tall"""
for name, box in {**FIXED, **PRIMARY}.items():
assert box[2] >= 24 and box[3] >= 24, f"{name} is {box[2]} x {box[3]}"
for name, box in PRIMARY.items():
assert box[3] <= 64, f"{name} is {box[3]} px tall"
def test_no_overlaps_and_on_screen():
"""no two targets overlap, and every target is inside the 360 x 800 screen"""
boxes = {**FIXED, **PRIMARY}
names = sorted(boxes)
for i, a in enumerate(names):
box = boxes[a]
assert box[0] >= 0 and box[1] >= 0, f"{a} starts outside the screen"
assert box[0] + box[2] <= SCREEN[0] and box[1] + box[3] <= SCREEN[1], f"{a} ends outside the screen"
for b in names[i + 1 :]:
assert not overlap(box, boxes[b]), f"{a} overlaps {b}" A hint
At most 3.0 bits means D / W may be at most 2³ − 1 = 7. Change W first: for a wide button W is its height, so making it wider changes nothing, while making it 48 to 64 px tall raises W. Then change D: a box whose centre lies within 7 × W of START, for example in the empty band between the last field (it ends at y = 432) and the terms link (it starts at y = 712), meets the limit. In the function, min(width, height) gives W.
Results of the sample tests
| Test | Result | Details |
|---|
What your code printed
The sample tests run on this device, in your browser (Pyodide): nothing is sent to mysmartcopilot.com. The first run downloads Python (about 13.5 MB), which is kept for the next runs. A check in your browser is feedback for you, not proof that the code is right for every input.
When its tests pass, try the self-check, which starts with the ideas you have missed most:
Practice that adapts to you
Practise this lesson
15 questions about 7 ideas of this lesson. Each answer is explained at once, and what you get right is kept in this browser.
Interview questions
Warm-up (fresher to 2–5 years): is “seven, plus or minus two” a limit on how many items a menu should hold?
No. Miller’s 1956 paper described two limits that share the number seven: absolute judgement of one dimension, about 2.6 bits or six to seven categories, and the span of immediate memory, about seven chunks. Cowan’s 2001 review puts the central short-term limit at about four chunks. A menu is visible, so people recognise its items instead of holding them, and neither limit says how many it may list; the memory limit warns against making people carry facts between screens. Say what you would judge instead: how quickly people find and compare the items.
Warm-up (fresher to 2–5 years): what does a target’s index of difficulty measure, and how do you bring it down?
It measures how hard a pointing movement is, in bits: ID = log2(D/W + 1), with D the distance to the target’s centre and W its width along the movement, in practice a rectangle’s smaller side. A 48 px tall button 600 px away rates 3.75 bits, and movement time grows in a straight line with ID, MT = a + b × ID, with a and b measured for a device. To bring it down, move the target closer or make it bigger in the direction of approach: doubling W or halving D takes off about one bit when D is many times W. WCAG 2.2 adds a floor of 24 by 24 CSS pixels.
Two harder questions follow, on how many options to show and on a form that is slow to respond. Decide how you would open each answer, then hold it against the first step on the question’s page:
Model answers open with any pass: ₹79 for 1 week ($2.99), one payment that never renews.
-
Mid to senior
Ward Desk's complaint form takes 4 to 12 seconds to send on slow phone connections, and its Submit button shows nothing until the server answers. Some residents tap again and file the same complaint twice. Field data shows an INP of 380 ms at the 75th percentile on phones. What do you change, and in what order?
Flashcards · 8 cards
Recall it, then check
Say the answer to yourself before you look: recalling it is what makes it stick.
-
About how many chunks does working memory hold, and what follows for a screen?
About four, by Nelson Cowan's 2001 review. Show people what they need, such as the coupons that apply or last month's calendar with paused days marked, instead of asking them to recall it: recognition beats recall.
-
Should a menu be capped at seven items because of Miller's "seven plus or minus two"?
No. Miller's limits concern judging and remembering, and a menu is read, not remembered. Size it by how quickly people find and compare its items.
-
What does the Hick-Hyman law say about the time to choose among known options?
For n equally likely options, T = a + b log2(n + 1), so each doubling adds about the same time. When a is 201 ms and b is 149 ms per bit, the first day's fit, eight options take about 674 ms and sixteen 811 ms. A newcomer facing an unfamiliar list searches it instead.
-
What did the 2010 choice-overload meta-analysis find?
Across 50 experiments (63 conditions, 5,036 participants), the meta-analysis by Benjamin Scheibehenne, Rainer Greifeneder and Peter Todd found a mean effect of virtually zero, with considerable variance, and no condition that reliably produced overload. Remove what nobody needs and help people compare the rest.
-
How do you work out Fitts's index of difficulty for a button, and which side is W?
ID = log2(D/W + 1), with D the distance to the target's centre and W its width along the movement, in practice the rectangle's smaller side. A 48 px tall button 600 px away rates log2(13.5) = 3.75 bits; making it taller or closer lowers that, making it wider alone does not.
-
What does WCAG 2.2 criterion 2.5.8, Target Size (Minimum), require?
Level AA asks for pointer targets no smaller than 24 by 24 CSS pixels, with five exceptions: spacing, an equivalent control, inline targets, user agent control and essential presentation. Treat the spacing exception as a fallback, not a goal, and give primary actions far more.
-
What are the three response-time limits, and what feedback fits each wait?
About 0.1 s feels instantaneous, about 1 s keeps the flow of thought, and about 10 s is the limit for keeping attention on the task. Show the result itself under 0.1 s, an instant reaction up to 1 s, an indicator from 1 to 10 s, and beyond that progress with a way to stop.
-
Which INP counts as good, and how is it measured?
200 ms or less is good, up to 500 ms needs improvement and more is poor, read at the 75th percentile of page loads in the field, phones and desktops apart. INP reports the slowest click, tap or key press of a visit, ignoring one in every 50, from the input to the next frame painted.
Key takeaways
- Working memory holds about four chunks, so show people what they need instead of asking them to remember it, and keep “seven plus or minus two” out of decisions about menus.
- In multi-step tasks, keep the decisions the task needs and remove the load the design adds by carrying earlier answers forward.
- Choosing among known options takes about the same extra time for each doubling, newcomers search instead, and choice overload averages about zero, so options are structured for finding and comparing rather than cut to a fixed count.
- Fitts’s index of difficulty, log2(D/W + 1) with W the smaller side, rises with distance and falls with size, and WCAG 2.2 sets a floor of 24 by 24 CSS pixels with five exceptions.
- Feedback matches the wait, from the result itself under 0.1 s to progress and a way out past 10 s, and an INP of 200 ms or less at the 75th percentile checks the first limit.
Terms in this lesson
- choice overload
- The claim that more options make people less willing to choose, or less happy with their choice. A meta-analysis of 50 experiments found its mean effect to be about zero.
- chunking
- Recoding several items into one larger familiar unit, such as reading 4417 as one number, so that memory holds more information in the same few chunks.
- cognitive load
- The mental work a task takes. Intrinsic load comes from the task itself; extraneous load is added by the way it is presented, and design should remove it.
- Fitts's law
- Pointing time grows in a straight line with a target's index of difficulty, MT = a + b × ID, so targets that are far away and small take longer to hit.
- Hick-Hyman law
- Choice time grows in a straight line with the information in a choice: T = a + b log2(n + 1) for n equally likely options that the person already knows.
- index of difficulty
- How hard a pointing movement is, in bits: log2(D/W + 1), with D the distance to the target's centre and W the target's width along the movement.
- Interaction to Next Paint (INP)
- A web metric: the slowest click, tap or key press of a visit, from the input to the next painted frame. 200 ms or less at the 75th percentile is good.
- recognition rather than recall
- A usability heuristic: make elements, actions and options visible, so that people pick what they need from the screen instead of having to remember it.
- response-time limits
- About 0.1 second feels instant, about 1 second keeps the flow of thought, and about 10 seconds is the limit for keeping attention on a task.
- target size
- The area of a control that a pointer or finger can activate. WCAG 2.2 Success Criterion 2.5.8 asks for at least 24 by 24 CSS pixels, with five exceptions.
- working memory
- The small store that holds what a task needs right now, such as a code being copied or two prices being compared. Its central limit is about four chunks.
References
- The magical number seven, plus or minus two: some limits on our capacity for processing information (American Psychological Association (Psychological Review))
- The magical number 4 in short-term memory: a reconsideration of mental storage capacity (Cambridge University Press (Behavioral and Brain Sciences))
- 10 usability heuristics for user interface design (Nielsen Norman Group)
- Memory recognition and recall in user interfaces (Nielsen Norman Group)
- The eight golden rules of interface design (Designing the User Interface, sixth edition) (Ben Shneiderman, University of Maryland)
- Cognitive load during problem solving: effects on learning (Wiley (Cognitive Science))
- Minimize cognitive load to maximize usability (Nielsen Norman Group)
- Working memory and external memory (Nielsen Norman Group)
- On the rate of gain of information (SAGE (Quarterly Journal of Experimental Psychology))
- Stimulus information as a determinant of reaction time (American Psychological Association (Journal of Experimental Psychology))
- Quantifying motor task performance by bounded rational decision theory (Frontiers in Neuroscience)
- Similarity and number of alternatives in the random-dot motion paradigm (Springer (Attention, Perception, and Psychophysics))
- The contribution of stimulus frequency and recency to set-size effects (Springer (Psychonomic Bulletin and Review))
- A predictive model of menu performance (ACM (CHI 2007))
- Can there ever be too many options? A meta-analytic review of choice overload (Oxford University Press (Journal of Consumer Research))
- The information capacity of the human motor system in controlling the amplitude of movement (American Psychological Association (Journal of Experimental Psychology))
- Fitts' law as a research and design tool in human-computer interaction (Taylor and Francis (Human-Computer Interaction))
- Extending Fitts' law to two-dimensional tasks (ACM (CHI 1992))
- Web Content Accessibility Guidelines (WCAG) 2.2 (W3C)
- Understanding Success Criterion 2.5.8: Target Size (Minimum) (W3C Web Accessibility Initiative)
- Response time in man-computer conversational transactions (ACM (AFIPS Fall Joint Computer Conference))
- Response times: the 3 important limits (Nielsen Norman Group)
- Progress indicators make a slow system less insufferable (Nielsen Norman Group)
- Interaction to Next Paint (INP) (Google (web.dev))
- How the Core Web Vitals metrics thresholds were defined (Google (web.dev))
Report a problem with this lesson
Kept only in this browser. Your Learn progress