utwordSystem v3.25.7
Components

Button

A rounded square you tap. One filled primary per screen, everything else in outline, and every button dips slightly under your thumb.

stable

Usage

Use the primary (.primary) for the one action most people on that screen will take, which isn't always the one listed first. Most players join a game rather than host one, so on Home Join a game is filled and Create game is an outline.

Use a secondary (.secondary) for every other action. There is no third style: a screen that seems to need one has too many actions.

Use an icon button (.home-icon-btn, .modal-x) where a glyph says it faster than a word: the header's key, mode, help and back, a sheet's close, the refresh on Public games.

Use a text button (.rec-link, .text-btn) for a quiet side path or a way out: "I already have a code", "Not now", "I'm new, let's play".

Don't make a button round, and don't make it a pill. Square = tap, pill = read, circle = rank. No button is ever circular, because circles are ranks.

Dark
Light
Play on Home one primary, two equal outlines under it, one label

Anatomy

Dark
Light
Every kind primary, secondary, icon button, icon and label, round action, text button
KindClassSizeFill and edgeType
Primary.primarymin-height 52px, padding 15px 18px, radius 12--fg fill, --ground ink, lift 0 2px 6px --shadow-lift700 16px; .big is full width at 17px
Secondary.secondarymin-height 52px, padding 13px 16px, radius 12transparent, 1.5px --rule edge, --fg text600 15px
Icon button.home-icon-btn, .modal-x44×44, radius 12transparent, 1px --rule edge, --fg-soft glyph20px glyph; the close X is 16px
Icon and label.icon-secondary44px tall, radius 121px --rule edge, --fg-soft600 14px, 6px between glyph and label
Round action.tv-btn (trivia's Hint and Skip)44px tall, radius 121.5px --fg edge, --fg text700 15px
Text button.rec-link, .text-btn.text-btn min-height 44pxtext only, --fg-soft500 14px (.rec-link), 600 14px (.text-btn)
Confirm pair.btn-confirmmin-height 52px, radius 12, two equal widthsCancel: 1.5px --rule outline. The action: .primary-confirm (filled) or .danger (rose edge and text on --danger-wash)600 15px

Every kind is a real <button>; Hint, once used, becomes a plain label. Glyphs are inline SVG strokes in currentColor, so they take the button's colour in every state.

States

Dark
Light
Rest, busy, soft, done, attention busy and soft are both primaries; done is an outline with a check; the key asks for attention with a dot
StateWhat the player seesApplies to
Restas in Anatomyevery kind
Hover (pointer only)glyph or text goes to --fg; the edge goes to --rule-strong (.home-icon-btn) or --fg-soft (icon and label).home-icon-btn, icon and label, text buttons
Pressedscale(0.98) and brightness(0.94) over 0.07sprimary, secondary, danger, icon buttons, icon and label, .text-btn, segments, room rows, standings
Focus2px ring, offset 2px: --fg on most buttons, --focus on .home-icon-btnevery kind
Disabledopacity 0.4 (primary, secondary, danger, icon and label); 0.45 (Public games refresh); 0.5 (Hint and Skip)
Busythe label says what's happening and the button disables: "Rolling…", "Looking…", "Setting up…"primary, Create game
Softopacity 0.55 and still tappable (.rc-soft)Find my account, until six characters are typed
Donean outline with a check and bold --fg text: "Saved to Photos", "Saved to Notes"the two save buttons
Attention1.5px --fg edge and a 12px dot; the dot pops (240ms) and rings twice (900ms) once per visit, then reststhe key, until the code is saved

Behaviour

  • The press-dip is shared: every button in the list above shrinks to 98% and dims slightly, over 0.07s. Standings rows press as one piece, card and all, through :has() (scale(0.99), brightness(0.97), 0.09s). Tiles never dip; see Tile.
  • Guide, don't block. Buttons stay enabled and point at what's missing. Find my account looks soft until six characters are in, but tapping it early focuses the code tiles and, if anything is typed, says "Your code has six characters."
  • No connection. Join a game, Create game and the Compete cards disable, the hint under them says why, and Play solo stays live: the board and the dictionary are on the phone.
  • Confirm in place. Taking a player out or making a new room code turns the row or button that asked into a question over two equal buttons: no modal, no layout shift. When taking a player out, Cancel is a bigger target than the X that opened the question.
  • Rose is for removal. "Take out" is the .danger action and fills solid rose only while pressed. "New code" makes something rather than destroying it, so its confirm is the filled primary.
  • No help button during a live round. The clock keeps running behind a sheet, so offering help mid-game is a trap: you either lose seconds or learn nothing.

Accessibility

PairDarkLight
--ground on --fg
the primary's label on its fill
12.70AAA12.70AAA
PairDarkLight
--fg-soft on --ground
icon buttons and text buttons
6.36AA4.78AA
  • Target size: 44px is the floor. Primaries, secondaries and confirms are 52px tall; icon buttons are 44×44. One button misses it; see Known issues.
  • Names: icon buttons carry an aria-label that says what happens: "Home", "Close", "Tutorial", "Check again", "Switch to light mode", and "Your code, not saved yet" while the key asks for attention.
  • Busy states are words, so a screen reader hears "Rolling…" or "Looking…", not a spinner.
  • Focus is always visible on :focus-visible: the --fg ring is 12.70:1 against the ground, --focus is 5.27:1 (dark) and 5.38:1 (light).
  • Reduced motion: no movement on press, only a dim to brightness(0.9). The key's dot appears without its pop or ring.

Content

  • A verb that says what happens: "Create game", "Join a game", "Find my account", "Take out", "Play again". Never "Submit".
  • Keep the name through the flow. The button that opens a step and the button that finishes it should agree (see Known issues for the one place they don't).
  • Sentence case. Busy labels end in an ellipsis.
  • Text buttons never carry icons. The check on "Saved to Photos" sits on an outlined secondary, not on a text link.

Why it's built this way

Known issues and gaps

  • One tap target under the floor. .text-btn.small ("Clear" in the word lookup's history) is 32px tall against the 44px rule. Fix: min-height: 44px with the same text. Planned for 3.23.7 (3.23.6 audit).
  • Two focus colours. Six focus rules use --gold (which resolves to --fg) and two use --focus; the spec says --focus. Planned for 3.23.7.
  • The spec still names a 36px close. The spec's button rules say the one icon-button exception is a 36px sheet close; the shipped .modal-x is 44×44, so there is no exception. The code wins.
  • Not every button dips. Hint and Skip (.tv-btn), Cancel and New code (.btn-confirm), the Compete cards (.cc-card) and the text links (.rec-link), among others, aren't in the shared press list, though the spec says every button except tiles dips.
  • Three disabled opacities: 0.4, 0.45 (the refresh) and 0.5 (Hint, Skip and the Compete cards).
  • Two primaries carry a glyph, and nothing says why. "Play again" (solo results) and "Next round" (room results) lead with a replay arrow (.primary.with-icon); every other primary in the game is text only. Everywhere else a glyph in a button carries something the label doesn't: a state (the check on "Saved to Photos"), or the round's own controls (the bulb on Hint, the skip mark). The replay arrow repeats the label. Proposed for 3.23.7: drop it, so primaries are text only.
  • One name breaks across a flow. Home's "Create game" opens a sheet whose button reads "Create room".
  • The button CSS still reads legacy names (--cta, --tray-line, --paper, --paper-dim, --gold). They resolve to the canonical roles; the rename is planned (see Color).
  • Tile: the one control that doesn't dip
  • Segmented toggle: choosing between options, not acting
  • Sheet: where the close button and footer primary live
  • Field: guide, don't block, from the field's side
  • Color: the tokens and the focus hue

Changelog

VersionChange
3.22.0Play on Home: one primary (Join a game), with Create game and Play solo side by side under it
3.21.2Find my account stays tappable and looks soft until all six characters are in
3.21.0Save to Photos (primary) and Save to Notes (outline) side by side; each becomes an outlined "Saved to …" with a check
3.11.3The Public games refresh moves from a 36px one-off to the 44×44 icon button
3.11.0Join, Create and the Compete boards disable with no connection; Play solo stays live