Skip to content

Design Principles

Chris edited this page Oct 7, 2026 · 1 revision

A reference, not a task: this collects the design principles, rules and decisions the site already follows, so new work stays consistent and nobody has to dig through old PRs, chat threads or audit files to find out why something looks or works the way it does. This page mirrors the GitHub issue "Design principles, rules and decisions" — update both together when a new principle or decision is made.

Part 1: The ten house principles

Every one of the 184 items in the UI audit traces back to one of these. Each has a quick test you can run on any page in a few seconds.

1. Hierarchy

Rule: on every screen the most important thing is the most visible thing, and the order of the page follows the order of importance. Why: people decide in a second or two whether a page is what they wanted. If the first screen is IDs, disclaimers or explanations, they scroll, guess or leave. Quick test: squint at the page — the title and the main action should stand out, nothing else should compete. Could a stranger say what the page is for and what to do, in 5 seconds? House rule: one page-title per page; card headings are smaller than it. The page's purpose comes first, identity elements (the beetle) are allowed to be big, explanations and IDs come later.

2. Progressive disclosure

Rule: show what most people need now; put the rest one tap away. Why: everything shown at once is everything competing at once. Rules, edge cases, empty fields and full histories are valuable, but only to some people some of the time. Quick test: does more than half of the visitors to this page need this block on this visit? If not, collapse it, summarise it, or move it to its own page. House rule: summaries before detail; <details>/<summary> for rules and long explanations; empty fields hidden with "Show all fields"; long lists show the first few and "Show all n".

3. Consistency

Rule: the same thing always looks, sounds and behaves the same; different things look different. Why: users learn a site once. Each time a button, name, icon or pattern changes, they have to work out again whether it means something new. Quick test: pick any element (a back link, a button, a page name, an icon) and find it on three other pages. Does it match? If two things look the same, do they do the same thing? House rule: three button looks chosen by role; one back-link pattern; one name per page in menu, title and links; one icon per destination; sentence case; one control per kind of switch.

4. Colour means something

Rule: colour is information, never decoration, and each colour has exactly one meaning. Why: the site is grey so colour can talk. The moment a colour is used for two things (green for "correct" and for "progress", blue for "AI" and for "your box"), the signal stops working everywhere. Quick test: for every coloured pixel, say in two words what it means. If you can't, it should be grey. If the answer differs between two places, one of them is wrong. House rule: green = done or correct; red = a problem; amber = waiting; the one worst-to-best scale for ratings (grey → red → orange → yellow → green, .scale-*, game_scale.py); blue = IBBI-AI; purple = the players' consensus. Progress bars and focus rings are grey, never a rating colour.

5. Accessibility

Rule: everyone can read it, reach it and operate it — by eye, by keyboard and by finger. Why: low contrast, tiny targets and colour-only signals don't only fail people with impairments; they fail everyone on a phone in sunlight. Quick test: text contrast at least 4.5:1 (3:1 for large text, icons and borders that carry meaning); touch targets at least 44×44px; tab through the page — can you see where you are and reach everything? Is every colour signal paired with a word or an icon? House rule: gray-500 is the lightest text colour anywhere (gray-400 only for disabled controls, dividers, decoration); 2px gray-600 focus ring with a white offset; 44px hit areas on touch; status always shown as colour + word; labels on every input.

6. Responsive

Rule: one design that rearranges itself for the screen — same components, same tokens, same hierarchy, different layout. Why: most visitors open the site on a phone at some point. A layout that only works at desktop width cuts off tables, pushes the page sideways and buries actions. Quick test: open every page at 390px — nothing scrolls sideways, nothing is clipped without a cue, the main action is reachable without hunting. Then at 1280px: is space used, or is it a stretched phone page? House rule: the lg (64rem) breakpoint changes structure (rail on screen vs. drawer, two columns vs. one); wide tables get a phone strategy (priority columns or cards); keyboard hints hide on touch screens.

7. Proximity

Rule: things that belong together sit together; a control sits next to the thing it acts on. Why: the eye groups by distance before it reads labels. A pager far from its image, or a consent box inside an unrelated card, is read as belonging to the wrong thing. Quick test: point at each control and ask what it acts on — is that thing the nearest thing to it? House rule: image controls live in a toolbar attached to the image; commit buttons sit at the end of the form they commit (sticky when the list is long); per-upload choices sit with the upload.

8. Affordance

Rule: things you can interact with look interactive, and things that look interactive are. Why: if a button looks like text, people don't find it. If plain text looks clickable, they tap it and think the site is broken. Quick test: cover the labels — can you still tell which things are buttons, links, toggles and plain text? House rule: links are underlined or clearly placed in navigation; buttons are one of the three button looks; counts that can filter are buttons; IDs come with a copy button.

9. Feedback and state

Rule: the interface always tells you what state things are in and what just happened. Why: locked vs. unplayed, empty vs. still loading, selected vs. not, disabled and why — when these look the same, people can't predict what will happen and stop trusting the controls. Quick test: for each element, list its states (empty, loading, error, disabled, selected, locked, done). Does each one look different and say why? House rule: a standard empty state (icon, sentence, action); disabled buttons say what's missing; locked things show a lock and when they unlock; the current ROI, page and row are always marked.

10. Content is design

Rule: words are part of the interface — short, plain, specific, in the reader's terms, and never repeated. Why: most of what people do on the site is read labels. Raw field names, duplicated paragraphs, cryptic numbers ("79/20") and over-precise values slow people down as much as a bad layout does. Quick test: read every label aloud to someone who hasn't seen the site — do they know what it means without asking? Is any sentence on the page twice? House rule: short plain text for players and visitors; human labels, not database names; numbers rounded to what matters and grouped in threes; one name for each thing.

Part 2: Where they live in the code

Colour classes

  • Green = done or correct. Red = a problem or a danger. Amber = waiting or needs attention.
  • Anything rated from worst to best (levels, streaks, badges, accuracy, challenge, expertise) uses one scale: grey (not yet) → red → orange → yellow → green (.scale-* classes, game_scale.py).
  • Blue = IBBI-AI. Purple = the players' consensus. Never used for a rating.
  • The one exception: badges. The hardest badges are blue, the strange or secret ones are purple (#608).
  • Levels 1–10 each get their own light, hazy tint stepping grey → red → red-orange → orange → amber → yellow → lime → light green → green; only level 10 is solid, filled green with white text, and glows (#606, .level-1….level-10 in input.css).
  • Source of truth: the house-style comment at the top of beetlesgallery/static/css/input.css, and game_scale.py.

Buttons: three looks, never black

  • .btn-main — light grey, dark bold text. What the page is for: Search, Classify image, Play. One per page.
  • .btn-primary — dark grey, white text. Work inside tools and forms: Save, Apply filters, Generate.
  • .btn-secondary — white with a grey outline. The quieter choice next to those: Undo, Reject (never a weaker-looking "No thanks").
  • Set only size, spacing and rounding per page (e.g. btn-main w-full text-2xl py-6); never write a one-off grey button.
  • No black buttons, anywhere.

Numbers and text

  • Numbers of four digits or more are grouped in threes (12 345): |digit_groups in templates, digitGroups()/digitGroupsText() in static/js/digit_groups.js.
  • Text for players and visitors is short and plain. No jargon without an explanation nearby.
  • Ticked boxes and radio buttons are dark grey, not blue or black.

Wording that must stay consistent

  • Game names: Naming, Similarity, Odd One Out, Find Them All. Never "Identification" or "Imposter" in anything players see (game_views.GAME_NAMES).
  • Expertise: a player is a Naming expert (Naming + Find Them All) or a Distinction expert (Similarity + Odd One Out) — never "Identification expert". Report tables use the same two headings.
  • IBBI-AI, never "the classifier" or "the model", in anything players see.
  • Where a name comes from, on the review card: Validated ID (green dot) for a checked name, AI ID (blue dot) for IBBI-AI, Player ID (purple dot) for the players' consensus, with both confidence columns shown side by side when both exist. ("Expert ID" is kept only for the internal identification-tier name curators use on the scoring/annotation/Data-Management pages — a different, more technical concept from what players see.)
  • Confetti colour matches whichever ID type earned the points: green/blue/purple as above; both colours together when AI and the players agree.
  • "Seen before" marks the exact photo or tile it applies to — never a single pill that doesn't say which one.

Loading and waiting

  • Never tell the player the game is slow. No "this is taking a while" wording.
  • Something is always visibly moving while the game waits (tile-lay animation, a thin moving bar) — never a frozen-looking screen.
  • The Next button is visible as soon as the review is in, even if the next beetle isn't ready yet (it shows a busy state instead of disappearing).
  • One loading message, not two. A spinner plus a single rotating line of text — never two separate rotating texts stacked together (see UI audit item play-loading-double, found after round 5 merged two different loading-screen ideas on top of each other).

Privacy and cookies (EU-compliant by design)

  • Nothing non-essential (i.e. no analytics) runs before a visitor chooses. Accept and Reject are the same size and style, with no box pre-ticked.
  • Withdrawing is as easy as consenting: a "Cookie settings" link in the footer (and reachable from inside the game) reopens the choice at any time.
  • A /privacy/ page names the controller, what's collected, Google as processor, retention, and how to withdraw or complain.
  • This is a best-effort technical pass, not a legal review, by the owner's own choice.

Engineering process

  • Docker only. The app never runs outside Docker; no Windows-specific code paths (#586).
  • One PR, one topic, with tests, and it closes an issue (Closes #N). New tests go in new files so PRs merge cleanly.
  • For a big batch of related changes, build one combined ("train") PR from the individual branches rather than handing over a long list of PRs to merge one by one.
  • Claude never merges, deploys, or runs anything against the live or staging server. PRs are opened for the owner, who reviews, merges and deploys.
  • Testing: run only the touched test module(s) locally, plus the linter. GitHub CI (parallel, ~6 minutes) is the one full-suite check — don't duplicate it with a local full run. Before combining several branches that touch a shared data shape, search the whole test suite for that shape first, rather than finding each stale assertion one slow CI round-trip at a time.

Last updated 2026-10-07. The full, itemised backlog these principles produced is the UI audit wiki page and its tracking issue.

Clone this wiki locally