Answer
Every Brew screen meets one bar in both themes, 4.5 to 1 text contrast, a visible focus ring, 44 px targets on phones, real labels and stillness under reduced motion, and this page records what is measured, where the live product falls short and how we check.
Today
Checked 26 October 2026. Contrast ratios below were computed from the token hex values with the WCAG 2 relative luminance formula; they are token pairs, not rendered pixels.
The docs site, docs.joinsquareup.com
| Fact | Source |
|---|---|
| The docs site audit reported text contrast of at least 9.2 to 1 | handbook brief, docs site audit 26 Oct; no audit file found in brand-directions or handbook |
| Recomputed from the docs tokens, ink on the page ground is 14.7 to 1 or more in every direction and theme | brand-directions/docs2/build.mjs:253-259 |
| Recomputed, muted text on the tinted second surface drops to 4.8 to 1 for Seeti light and 5.2 for Ocho light, so 9.2 holds for ink, not for muted text | brand-directions/docs2/build.mjs:253-259 |
| Tap targets are at least 44 px on phones: home mark, menu button, sub navigation, filters and track links | brand-directions/docs2/src/site.css:371-377 |
| Under reduced motion every animation and transition is off | brand-directions/docs2/src/site.css:302 |
| A skip to the page link and a 2 px focus outline exist | brand-directions/docs2/src/site.css:10, 14-15 |
The screen kit, tomorrow's product
| Fact | Source |
|---|---|
| The contract asks ink and muted text at 4.5 to 1 or more on bg and surface, both themes | brand-directions/_shared/kit-contract.md:36 |
| Every direction passes: ink 13.2 to 18.5, muted 5.6 to 8.4 on bg, surface and surface2 | brand-directions/docs2/data/dirs.json |
| Main button label on the hero colour: Fetch 11.8, Buzz 13.1, Scoop 6.8, Ocho 6.0, Seeti 5.1 to 1 | brand-directions/docs2/data/dirs.json |
| The hero colour against the light page is 1.4 for Fetch, 1.3 for Buzz, 2.4 for Scoop, 2.8 for Ocho and 3.2 for Seeti, below 3 to 1 for four of five | brand-directions/docs2/data/dirs.json |
| Focus colour against the page is 4.7 or more in every theme | brand-directions/docs2/data/dirs.json |
| Warn and good text sit at 5.3 to 1 on light surfaces and 7.6 or more on dark | brand-directions/_kit/kit.css:23, 27 |
| Focus ring: 2 px outline in the focus token, 2 px offset, on focus-visible | brand-directions/_kit/kit.css:33 |
| Main, quiet and icon buttons are 46 px; menu rows 44; inputs 48; the paste button 54 | brand-directions/_kit/kit.css:109, 116, 333, 348, 357 |
| Below 44 px: small buttons 36, Back 36, chips 34, Make it tools 34, segment buttons 34, page tabs 40, the switch 28 tall | brand-directions/_kit/kit.css:73, 120, 144, 305, 360, 365, 368 |
| The Sample tag and micro labels use 11.5 px type | brand-directions/_kit/kit.css:17, 55 |
| A reduced motion block turns off every animation and transition in the frames; each direction's character motion sits inside a no preference query | brand-directions/_kit/kit.css:564 brand-directions/_shared/kit-contract.md:25 |
| The character image is aria hidden with empty alt | brand-directions/_kit/render.js:54 |
| No screen reader pass has been run on the kit | brand-directions/book/content/d2.js:814 |
The live product, the gaps
| Gap | Source |
|---|---|
| The third text colour fails 4.5 to 1: #737373 on dark is 2.99 to 4.18, #808080 on light is 3.25 to 3.95 | brew origin/main public/feed-system.css:12, 46 |
| That colour is used 787 times across the live stylesheets, including every quiet page note | brew origin/main public/*.css brew origin/main public/quiet.css:7 |
| Icon buttons are 36 px, buttons 40 px, chips 30 px | brew origin/main public/feed-system.css:91, 97, 180 |
| 124 font size declarations at 11 px or below | brew origin/main public/*.css |
| 45 declarations remove the outline; 51 focus-visible rules exist to replace it | brew origin/main public/*.css |
| The html element has no lang attribute | brew origin/main public/index.html:1 |
| Sound plays unless the person turns it off | brew origin/main public/app.js:1470 |
| A reduced motion catch all exists on the app root: animations end almost at once | brew origin/main public/feed-system.css:295-298 |
| The loader holds still under reduced motion | brew origin/main public/feed-system.css:157 |
| Old first run ran a check page under a clock, which fails slow readers | brand-directions/book/content/d2.js:894-896 |
Tomorrow
| Rule | Detail | Version | Tag |
|---|---|---|---|
| Contrast | Ink and muted at 4.5 to 1 or more on bg, surface and surface2, both themes. The live third text colour goes | v1 | Changes |
| Hero as an indicator | Where the hero colour alone marks state on light, such as the unseen story ring and the switch, pair it with a 3 to 1 edge or a shape | v1 | New |
| Targets | 44 px or more on phones for anything you tap; small buttons and chips keep a 44 px hit area around a smaller look | v1 | Changes |
| Focus | 2 px ring in the focus token, 2 px offset, never removed without a replacement | v1 | Changes |
| Order | Top bar, tabs or rail, Need your yes, picks, See everything | v1 | New |
| Sheets | Focus goes to the title, stays inside, returns to the opener on Close; Esc and swipe down close | v1 | Changes |
| Keys on laptop | J, K, H, X, Enter, Esc, M; never needed on a phone; every gesture has a button | v1 | New |
| Labels | Icon buttons named in 3 words or fewer: Heart, Not for me, More. Tab counts read as words: Today, 2 need your yes | v1 | Changes |
| Alt words | Product photos say what is in them; the character is named only | v1 | Changes |
| Live regions | Toasts speak once, politely, one at a time. Each reading source is announced once | v1 | New |
| Status | Always a word, never colour alone | v1 | Stays |
| Motion | Character motion only inside the no preference query; nothing carries meaning through motion; celebrations under two seconds | v1 | Changes |
| Sound | Off on a first visit, one switch in Settings; every spoken line also shows as text | v1 | Changes |
| No timers | No countdowns, Skip always reachable | v1 | Changes |
| Language | lang set on the page | v1 | New |
| Text size | Nothing below 11.5 px; layouts survive 200 percent zoom | v1 | Changes |
| v2 posting | A scheduled post can be cancelled by keyboard and screen reader up to the moment it goes | v2 | New |
| v3 results | Charts carry the same numbers as text | v3 | New |
| v4 rules | Limits and approvals read as text beside each control | v4 | New |
brand-directions/book/content/d2.js:802-915 brand-directions/_shared/product-model.md:113, 250
How we do it
- Compute contrast for any new token pair before it lands:
python3 -c "
def L(h):
c=[int(h[i:i+2],16)/255 for i in (1,3,5)]
c=[x/12.92 if x<=.03928 else ((x+.055)/1.055)**2.4 for x in c]
return .2126*c[0]+.7152*c[1]+.0722*c[2]
import sys;a,b=sys.argv[1:3];x,y=sorted([L(a),L(b)]);print(round((y+.05)/(x+.05),2))
" '#a7ad9f' '#111410'
- Render every screen at 390 and 1280 wide, light and dark, and tab through it: the ring is visible, the order follows reading order, Esc closes, focus returns.
- Turn on reduced motion in the device settings: nothing animates.
- Measure tap targets in the browser at 390 wide: every tappable box 44 px or more.
- Run VoiceOver on iOS through first run, Today, a pick, Make it and Hand it off.
- Log each failure against the kit part, fix it in the kit, so every screen and every direction gets the fix.
Numbers
| Number | Definition | Today | Where it is measured | Status |
|---|---|---|---|---|
| Lowest text contrast | Worst ink or muted pair on any surface, both themes | live 2.99 to 1, kit 5.6 to 1 | Token script above | Measured |
| Targets under 44 px on phone | Count of tappable elements under 44 px at 390 wide | not measured live; 7 kit parts | Headless audit | Proposed |
| Unlabelled controls | Buttons or links with no accessible name | not measured | Headless audit | Proposed |
| Screen reader tasks passed | Of five core tasks, how many a VoiceOver user finishes | none run | Test session | Proposed |
| Motion under reduce | Animations still running with reduced motion on | 0 found in kit CSS | Headless audit | Proposed |
Open
- The 9.2 to 1 figure from the docs site audit has no file behind it, and recomputing the docs tokens gives 4.8 to 1 for muted text on Seeti light. Which elements did the audit measure? Proposal: rerun it, publish the script beside the docs and record ink and muted separately.
- Four directions' hero colours are below 3 to 1 on the light page. Is an edge or shape on hero indicators acceptable? Proposal: yes, add a 1 px ink edge to the unseen ring and the on switch in light themes.
- Should small buttons and chips grow to 44 px tall, or keep their look with a larger hit area? Proposal: a larger hit area, so rows stay quiet.
- Who runs the first screen reader pass and when? Proposal: Sai, on the chosen direction, before the restyle ships.