UI Structure Standard
Interfaces become hard to use because of structure, not size. An app with 300 settings can be easy to navigate and an app with 30 can be a maze. The causes are always the same few: the same control living in several places, menus that mirror the code or the team, actions mixed in with preferences, unlabeled icons, invisible scope, and nothing ever being removed.
The fix is to do what the major platforms already do, the same way in every app, so that people can use what they already know:
- One familiar layout. A searchable list of categories on the left, the open page on the right, rows with the label on the left and the control on the right.
- One set of category names. Account, Notifications, Privacy and security, Accessibility, Appearance, Language and region, Data and storage, then the product's own subjects. Seven questions file any setting into exactly one.
- Fewer controls. The best setting is the one that never ships because a better default made it unnecessary.
- One home for each control that remains. Every setting, destination and command is a record in a registry, each record has exactly one home, and the interface is drawn from the records. When nobody places a control by hand, the structure cannot drift.
Pick the job
Find the request in this table and read only that section, plus the files it names.
| The request | Section | Also read |
|---|---|---|
| "Add a setting / menu item / button" | Adding one control | Nothing more, usually |
| "This UI is a mess", "review this", "users can't find things" | Auditing an existing interface | references/audit.md, references/rules.md |
| "Build / design / redesign a settings screen, sidebar or toolbar", or a list of features to turn into one | Building a new surface | references/categories.md, references/visual-design.md, references/record-schema.md |
| "Organize our settings", "what categories should we have" | The standard categories | references/categories.md |
| "Set this up for our project" | Adopting the registry | references/record-schema.md |
Whatever the job, respect the project you are in. If the codebase already has a settings registry, a component library or its own rules about markup, extend those; do not introduce a parallel system. The standard describes structure, not technology.
The evals/ folder holds test cases for this skill. It is not reference material; do not read it while doing a task.
Adding one control
Ask these five questions in order and stop at the first yes. Tell the user that decision and the reason; do not walk them through questions that no longer apply.
- Would a better default make it unnecessary? If the need can be met for everyone by changing the default, detecting the situation, or simply removing the behavior people dislike (a confirmation nobody needs, a prompt for something that autosaves), do that. There is no setting.
- Does the system or an administrator already decide it? Theme, text size, notification permission and similar belong to the layer below. Follow it. If the product has a real reason to offer its own choice, the choice includes "Use system setting" and that is the default; it is never a bare on/off switch.
- Is it done once, not a lasting choice? Then it is an action: a button, kept apart from toggles, in context or in a labeled group at the foot of its page.
- Is it changed weekly or more often? Then the control lives in context, beside the feature it changes, with a link to its home page. The home page still lists it.
- Would fewer than about 1 in 20 people ever change it? Then its tier is advanced. A page with three or more advanced items shows them in an Advanced fold; with fewer, it is an ordinary row placed last.
Otherwise it is a standard row on its subject page.
If the answer to question 1 or 2 was yes, say so plainly and propose that change instead of the setting. Every setting has a cost in testing, in support and in everyone else's ability to find things, so the user should hear the cheaper option first. They may still want the setting; that is their call. Offer it as the fallback in a few lines.
Choosing the home. Use the seven questions under The standard categories, mapped onto the pages that exist. If the control fits none, that is evidence of a missing subject: say so and propose one. Do not create a page called General, Misc, Other or Advanced. When the existing interface already has such a page, put the new control on the best-named specific page instead and mention the catch-all in one line as something worth fixing later. A request to add one control is not a request for an audit.
Controls that ask a question. A confirmation with "Don't ask again" is an in-context control for a setting (question 4): the checkbox and the settings row are the same record, so the choice can be found and reversed later. Never offer "Don't ask again" without a row somewhere that undoes it.
How much to show the user. If the project has a registry, write the full record (fields below) and check that the page it lands on is still within the limits. If it does not, or the user only asked for advice, answer in plain words: what to add, where, what it is called, what it defaults to and why, and whether it applies to this device or the whole account. Fields such as id, tier, owner and reviewed are bookkeeping; leave them out of a conversational answer. When you do not know something the record needs (whether settings sync between devices, which pages exist), state your assumption in a clause and carry on.
Auditing an existing interface
The goal is a list of concrete, fixable findings tied to rule numbers, and a proposed structure. Read references/rules.md first so findings can cite rules, and use the report template in references/audit.md.
An audit may cover settings, navigation, toolbars, or all three. Audit what the user asked about and what you can see. If the screen has no settings, skip the settings parts of the template; do not stretch them to fit.
- Inventory. List every place a person can change or trigger something: settings screens, account menus, context menus, per-item dialogs, navigation lists, toolbars, icon strips, command-line flags, config files. In a large codebase, delegate the sweep or search for the patterns (labels, toggles, menu builders); do not read giant files whole. For each control record its label as shown, its kind, where it lives and how it is reached. When a control has no label at all, say so, describe it by position, and mark any guess about what it does as a guess.
- Measure. Count what the rules limit: places to change things, top-level navigation entries, settings per page, depth, buttons per toolbar, icon-only controls, controls with no accessible name, search boxes, and how many distinct command sets objects of the same type show.
- Find the symptoms.
- the same setting or command reachable in more than one place, or two controls that stack or fight
- one subject scattered across several pages
- one thing with several names; a link whose label differs from its target's title; near-duplicate names side by side
- pages named General, Misc, Other, Advanced
- actions (export, delete, sign out) sitting among preferences or among destinations
- scope explained only in hint text, or not at all
- builder's vocabulary in labels
- groups with one item and lists longer than their limit
- icons with no visible words; one icon used for two meanings
- toolbars past five buttons; command sets that differ between objects of the same type
- no marker for the current location
- more than one search box on a screen
- show, hide or reorder controls offered as the cure for clutter
- Propose. For settings: the standard categories that apply plus up to two product subjects, at most nine in all, with every control mapped to exactly one. For navigation: at most nine top-level entries, the rest under labeled groups. For toolbars: one command set per object type, five on the bar, the rest under More. List what leaves the surface altogether and where it goes. Say that groupings are proposals to confirm with a card sort and a tree test (five people find the big problems; aim for 80% task success), because groupings must match how users think and that cannot be read from code.
- Order the changes so that the ones nobody has to relearn come first: words on icons and a current-location marker, then collapsing duplicates and unifying names, then consistent toolbars, then regrouping. Moving things comes late because it costs existing users something. If adopting a registry is warranted, it goes last and is offered, not pushed; the user asked what to change on screen.
Order findings by how much they hurt a new user. Credit what already works: an existing registry, search, reset or address mechanism is the foundation the fix builds on, and saying so prevents a rewrite nobody needs.
Building a new surface
- Run the five questions on every item first. A product wish-list always contains things that should be a better default, belong to the system, or are actions rather than preferences. Leave the cut items out of the registry and list them in your summary with the reason, so the user can overrule you.
- Write the records before any markup, following
references/record-schema.md. If a registry exists, add to it. - File every record with the seven questions (next section). Leave out categories that would be empty. A category with one or two settings is fine when people look for it by name (Notifications); otherwise fold it into its neighbor. Status about a subject stays on that subject's page; only app-wide information goes under About.
- Check the limits (below), by hand or with the checker.
- Draw the standard layout from the records, following
references/visual-design.mdfor arrangement, sizes, controls and states, andreferences/patterns.mdfor behavior. In short: category list beside the page on wide screens and before it on narrow ones; one column capped near 1,100 px; label left, control or current value right; related rows in contained groups; switches for on and off, radios for two to five options, no Save button; targets at least 24 px and 44 to 48 px for touch; text contrast 4.5:1; nothing shown by color alone. Each row shows its scope and, when it differs from the default, a changed marker with Reset. Search sits at the top. Every record has a stable address. Actions sit apart from preferences, destructive ones apart again. - Say what you assumed: defaults, scopes and owners you had to guess are the user's to confirm. Use
unassignedfor an unknown owner; do not invent team names.
The standard categories
Ask these in order and stop at the first yes. The order is the tie-breaker.
| # | The question | Category |
|---|---|---|
| 1 | Is it about who you are, how you get in, or what you pay? | Account |
| 2 | Does it decide when or how the product interrupts you? | Notifications |
| 3 | Does it control what other people, other apps or the service can see, keep or do? | Privacy and security |
| 4 | Does it exist so someone with a particular need can see, hear or operate the product? | Accessibility |
| 5 | Does it change only how things look? | Appearance |
| 6 | Is it about language, time or formats? | Language and region |
| 7 | Is it about where data is kept, how much, or over which network? | Data and storage |
| Otherwise: which feature does it change? | One of up to two product subjects (Messages, Editor, Playback) |
About (version, help, legal) is a link at the foot and is not counted. Settings an administrator makes for everyone are a separate surface. A small app with under about 15 settings uses one page with these names as group headings.
So a ringtone for calls is Notifications, not Calls; "reduce motion" is Accessibility, not Appearance. When a setting could sit in two, file it by the first yes and put the other category's words in its search words. references/categories.md covers large products, suites, naming and the reasoning.
These names are the ones operating systems already use. Start from them and change them only when the product's own users, in a card sort, group things differently.
The record
| Field | The question it answers | Allowed values |
|---|---|---|
id | What is its permanent name? | A dotted path such as chat.notifications.sound. Never reused or renamed. |
kind | What sort of thing is it? | preference, action, status, list, destination, command |
subject | What one noun is it about? | Exactly one subject. This is its home. |
scope | Who or what does it change? | item, device, account, organization |
tier | How many people need it? | everyday, standard, advanced |
label, summary | What is it called, and what does it do? | A plain label; one sentence about the outcome |
search | What would a person type to find it? | Synonyms, the old name, the jargon term |
value | What kind of value does it hold? | A type, with options or a range |
default, default_reason | What happens if nobody touches it? | The value most people would pick, and why |
risk | What breaks if it is changed? | safe, care (recoverable), destructive (lost for good) |
owner, reviewed | Who decides when it goes away? | An owner and the date it was last justified |
Destinations and commands use a shorter set of fields. The full schema is in references/record-schema.md, and assets/registry.example.json is a working example.
The limits
| Limit | Value | Where it comes from |
|---|---|---|
| Top-level settings subjects (About not counted) | 9 | Proposed starting value |
| Settings on one page | 15 | Android settings guidance |
| Levels of settings pages below the home screen | 2 | Nielsen Norman Group on progressive disclosure |
| Advanced items a page needs before it gets a fold | 3 | Android settings guidance |
| Top-level navigation entries, counting group headings; and destinations in one group | 9 | Proposed starting value |
| Toolbar commands before "More" (More itself not counted) | 5 | Proposed starting value |
| Share of accounts that changed a setting, below which it is reviewed | 2% | Proposed starting value |
A page past its limit splits by subject. It does not gain a third level or a "More settings" page. The numbers are starting values; a project may change them with test data, and should record why.
Labels
- Preferences, lists and destinations: noun first, plain words. "Notifications", not "Manage your notification preferences".
- Actions and commands: a verb and its object. "Export your data", "Reply".
- No negatives: "Block", not "Don't allow".
- No internal or technical term unless the summary defines it on the same line.
- The item that opens a page uses the page's exact title.
- One word per concept everywhere. Pick "Devices" or "Signed-in devices" and use it on every surface.
- Every navigation icon has a visible text label. In a toolbar only search, close, back and print may stand alone; everything else, including refresh, filter, sort, add and a row of dots, shows its word. A tooltip is not a label, because it does not exist on touch screens and costs a hover everywhere else. Every control still needs an accessible name.
- One icon, one meaning, across the whole product.
Adopting the registry
For a project with no registry, start small: one data file (JSON, or the project's own language) listing records, and one function that draws a page from it. Migrate one subject at a time; hand-written rows keep working until their subject moves. references/patterns.md ends with the order to migrate in.
scripts/check_registry.py validates a registry file against the rules a machine can check: required fields, unique ids, the limits, banned page names, duplicate labels, value types and ranges, defaults, destructive records that are not actions, icon-only commands, misspelled fields, and stale review dates.
python3 scripts/check_registry.py path/to/registry.json
It exits non-zero when a rule is broken, so it can run in a test suite. --limits prints the numeric limits and each can be overridden. If the project keeps its registry in another format, export to the JSON shape in references/record-schema.md or port the checks.
A passing check is structural only. It cannot tell whether a default is the one people want, whether a label is the word users say, or whether the subjects match how people think.
Do not add the checker, a new file format or a build step to a project that forbids such additions. Express the same checks in the project's existing test style instead.
Proving it works
Structure is tested before anything is built. references/testing.md describes how: a card sort to find the groupings, a tree test for findability, first-click tests on real screens, and the ongoing measures. Where a product must not collect usage data, tree tests and first-click tests with volunteers answer the same questions and collect nothing in production.
Reference files
references/categories.md— the standard categories in full: what goes in each, tie-breakers, how small apps, large apps and suites fit, names to use and avoid. Read when choosing or reviewing a structure.references/visual-design.md— the standard layout, the row, groups, which control to use, sizes and spacing with their sources, color and contrast, states, and a finishing checklist. Read when building or redesigning any surface.references/rules.md— all 27 rules in six groups, with the reason for each. Read for an audit, or when a decision needs a rule number.references/record-schema.md— every field per kind, value types, and the registry file shape. Read before writing records.references/patterns.md— how each part looks and behaves on screen: home screen, rows, scope badges, the Advanced fold, actions, search, addresses, navigation, toolbars, and the migration order. Read when building.references/audit.md— the audit report template and example findings.references/testing.md— card sorting, tree testing, measures and targets.references/evidence.md— the research and platform guidance behind the rules, with links.assets/registry.example.json— a small registry that passes the checker.scripts/check_registry.py— the automated check.