ux-design · git:20261027.b1054fc · 2026-10-27 · sha256 01c31aec1b244c05

ux-design git:20261027.b1054fcA

Immutable. This exact content never changes and is served at /api/v1/blob/01c31aec1b244c05.

---
name: ux-design
description: UX pass for one feature of a macOS app, covering names, flow, where it opens (inline, popover, sheet, window, alert), states, words, hover help, accessibility, and AI features. Use before coding a story that adds a screen, an action, or text, when user-stories or improve-loop call it, or when the user asks for a UX review, clearer wording, better names, or whether something should be a modal.
argument-hint: "[story ID or screen]"
---

# UX design

A feature that works can still confuse people. This pass decides what
things are called, how a person moves through the feature, where it opens,
and what it says, before any code exists.

Run it once per story that adds a screen, an action, or words. The output
is a `UX:` block in the story in `docs/STORIES.md`, plus new terms in
`docs/GLOSSARY.md`. To review a built screen, run the same steps against
its snapshot and list what fails, most serious first.

## 0. Read

Read `docs/BRIEF.md`, `docs/GLOSSARY.md` (create it if missing), the story,
and its reference screenshots.

## 1. Name it

Every thing in the app has exactly one name, and every name lives in the
glossary.

```markdown
| Term | Means | Not |
| --- | --- | --- |
| Task | One thing to do, with an optional due date | to-do, item, entry |
| Complete | Mark a task done; it leaves the list | check, finish, close |
```

- Name the things first (the nouns in the brief), then the actions on them.
- Use the user's words, not the code's: "Task", not "TaskEntity" or "record".
- Use Apple's words for system actions: Settings, Quit, Close, Undo, Move
  to Trash (when it can come back), Delete (when it cannot).
- One word per idea across buttons, menus, titles, tooltips, and errors.
- Name a feature for what it does for the person, not the technology. A
  toggle gets a verb phrase that says what and for whom ("Enhance
  Dialogue", not "Isolate Vocals"). Say the name in a sentence out loud.
  Method: [writing.md](writing.md#names).

Check: every noun and verb the story puts on screen is in the glossary,
and no glossary term has a second name anywhere.

## 2. Map the flow

Write the flow as one line, from every way in to a clear end.

```text
Command-N, toolbar +, File > New Task -> title field has focus -> Return saves, row appears -> focus stays for the next task
```

- List every entry point: menu command, shortcut, toolbar, context menu,
  drag and drop.
- Cut steps: fill in good defaults, ask only for what you cannot guess.
- Every step has a way back: Escape cancels, Command-Z undoes.
- The end is visible: the new row, a checkmark, the window closing.

Check: the flow fits on one line, and each step names its way back.

## 3. Pick the surface

Go down this list and use the first one that fits. Lighter is better.

| The feature | Use |
| --- | --- |
| Changes one value on something already on screen | Inline (field, toggle, menu in the row) |
| Shows details of the selection while people keep working | Inspector |
| A few related choices tied to one control | Popover |
| Input people repeat while they watch the result, or a tool kept open beside the work | Panel |
| One short task that must finish or cancel before going on | Sheet |
| A long or self-contained task, or one people keep open beside the main window | New window |
| A place people come back to, like a list or a collection | Sidebar page |
| A rare action that destroys data and cannot be undone | Confirmation dialog |
| A problem people must act on right now | Alert |

- Show one modal at a time. Never open a popover from a popover, or a
  sheet from a sheet.
- A modal's title names its task: "Rename Project", not "Edit".
- Return confirms, Escape cancels, and the confirm button names the action.
- Ask before closing only when people would lose what they typed.
- Information that needs no action goes inline, never in an alert.

Details and SwiftUI code for each surface: [presentation.md](presentation.md).

Check: the `UX:` block names the surface and the table row that picked it.

## 4. Cover the states

Every view the story touches has words and a look for each state:

- **Empty**: what it is for, plus the first action as a button.
- **Loading**: only when it can take longer than half a second. Show
  cached content first. A bar when the length is known, with words for
  what is happening ("Importing 12 photos"), and Cancel when it is safe.
- **Error**: inline, next to the problem, with the fix.
- **Too much**: long names truncate with the full text in a tooltip; a
  thousand rows still scroll smoothly.
- **Done**: visible feedback in the same frame, and Undo where it applies.

Check: each state has a snapshot page or seed data that shows it.

## 5. Write the words

- Buttons and menu items start with a verb: "Add Task", "Export…".
- Add an ellipsis when the command asks for more input before it acts.
- Title case for buttons, menu items, and window titles. Sentence case for
  labels, descriptions, and messages.
- Errors say what happened and how to fix it, without blame. Write
  "Unable to load tasks", never "We're having trouble".
- Confirmations repeat the action: "Delete 3 Tasks?" with buttons "Delete"
  and "Cancel", never "Yes" and "No".

Patterns and before and after examples: [writing.md](writing.md).

A feature that uses AI (a summary, generated text, a model's suggestion)
also follows [ai-features.md](ai-features.md).

Check: read every string aloud. Each one is short, uses glossary terms
only, and a new user could say what each button does.

## 6. Add hover help

- Every icon-only button gets `.help("Add a task to this list (⌘N)")`:
  what it does, starting with a verb, then its shortcut. Not its name
  again. Rules: [writing.md](writing.md#tooltips).
- Hover can reveal secondary actions on a row. The same actions are also in
  the context menu and on the keyboard.
- Hover feedback shows at once and never shifts the layout.
- Information people need is always visible. Hover only adds detail.

Check: hover every icon-only control on the snapshot build. Each one shows
a tooltip.

## 7. Check accessibility

- The whole flow works with the keyboard alone, in a logical focus order.
- VoiceOver reads every control with the same words as the screen.
- Meaning never rests on color alone: add an icon or a word.
- Text is 10 pt or larger and readable in Increase Contrast.
- Reduce Motion replaces slides and bounces with fades.
- Every custom control has a label, a value, actions, and feedback.

The audit steps and SwiftUI modifiers: [accessibility.md](accessibility.md).

Check: you ran the flow keyboard-only and with VoiceOver, and the
Accessibility Inspector audit shows no issues for the new views.

## 8. Write the UX block

Add the result under the story's acceptance lines:

```markdown
UX:
- Flow: Command-N, toolbar +, File > New Task -> title field -> Return saves -> focus stays
- Surface: inline row at the top of the list (changes one value on screen)
- States: empty "No Tasks" with "Add Task"; error "Give the task a title." under the field
- Words: "Add Task", tooltip "Add a task to this list (⌘N)", "Undo Add Task"
- Access: Return saves, Escape cancels; VoiceOver "New task title"
```

Done when the story has a `UX:` block, every term it uses is in
`docs/GLOSSARY.md`, and steps 1 to 7 pass on its snapshots.