Nzinga mosaic in magenta, flame and Volt
Design
/
Accessibility

Accessibility

Nzinga is for people speaking their own languages on the phones they already have: in sunlight, on slow networks, with any level of reading or eyesight.

Phone App
Expo SDK 58

The rules below are measured, and where the app falls short today the gap is listed.

01

Contrast

Source
apps/mobile/utils/color-theme.ts · app/contexts/ThemeColors.tsx · docs/site/site.css

Ratios are computed with the WCAG 2 relative-luminance formula from the token values. Body text needs 4.5:1 (AA); large text (18pt, or 14pt bold) and the edges of controls need 3:1.

AAA 7:1 or more   AA 4.5:1 or more   Large or UI only 3:1 to 4.5:1   Fail under 3:1

App, Dark Theme (Default)

Pair
Colours
Ratio
Result
Text on background
#F7F7FA on #0B0B10
18.36
AAA
Text on secondary (cards)
#F7F7FA on #16161D
16.83
AAA
Subtext on background
#A8A8B3 on #0B0B10
8.34
AAA
Subtext on secondary
#A8A8B3 on #16161D
7.64
AAA
Accent (Volt) text on background
#C8FF2E on #0B0B10
16.64
AAA
Cyan text on background
#19E0E6 on #0B0B10
12.01
AAA
Flame text on background
#FF6A1F on #0B0B10
6.86
AA
Magenta text on background
#FF2D7A on #0B0B10
5.52
AA
Violet text on background
#7C5CFF on #0B0B10
4.52
AA
Input placeholder (40%) on secondary
#707075 on #16161D
3.65
Large or UI only

App, Light Theme

Pair
Colours
Ratio
Result
Text on background
#0B0B10 on #F7F7FA
18.36
AAA
Subtext on background
#55555F on #F7F7FA
6.89
AA
Subtext on secondary
#55555F on #FFFFFF
7.37
AAA
Accent text on background
#5C7A00 on #F7F7FA
4.63
AA
Violet text on background
#7C5CFF on #F7F7FA
4.06
Large or UI only
Magenta text on background
#FF2D7A on #F7F7FA
3.32
Large or UI only
Cyan text on background
#0FA9AE on #F7F7FA
2.69
Fail
Flame text on background
#FF6A1F on #F7F7FA
2.68
Fail
Volt text on background
#C8FF2E on #F7F7FA
1.10
Fail never used as text
Input placeholder (40%) on secondary
#9D9D9F on #FFFFFF
2.71
Fail

Content on Colour (Both Themes)

Pair
Black #0B0B10
White #F7F7FA
Rule
On Volt #C8FF2E
16.64 AAA
1.10 Fail
Always black.
On Cyan #19E0E6
12.01 AAA
1.53 Fail
Black.
On Flame #FF6A1F
6.86 AA
2.68 Fail
Black.
On Magenta #FF2D7A
5.52 AA
3.32 Large
Black for text; white only for large type.
On Violet #7C5CFF
4.52 AA
4.06 Large
Black. This is why session panels use black content.

This Site's Light Palette

Pair
Colours
Ratio
Result
Text on page
#0B0B10 on #FFFFFF
19.63
AAA
Text on panels and sidebar
#0B0B10 on #F7F7FA
18.36
AAA
Subtext on page
#55555F on #FFFFFF
7.37
AAA
Subtext on panels
#55555F on #F7F7FA
6.89
AA
Subtext on hover grey
#55555F on #ECECF1
6.26
AA
Accent text on page
#5C7A00 on #FFFFFF
4.95
AA
Accent text on panels
#5C7A00 on #F7F7FA
4.63
AA
Black on Volt fills (nav indicator, marks, tags)
#0B0B10 on #C8FF2E
16.64
AAA
Volt on white
#C8FF2E on #FFFFFF
1.18
Fail fills only, never text
Search field edge
#85858E on #FFFFFF
3.66
UI pass
Dividers
10% black on #FFFFFF
1.24
Decorative, not required

In dark mode the site uses the app's dark values: text 18.36, subtext 8.34 on the page and 8.17 on the sidebar.

Warning
To fix in the app's light theme. Cyan and Flame fail as text on the light ground, and Magenta and Violet pass only for large text. Today they colour small text in light mode: the In review and Not accepted badges, the Withdraw button's label, and input error lines. Give each a darkened text value for light mode, as accent already does for Volt, or keep these labels in the text colour with a coloured dot or icon. Input placeholders at 40% fail in light mode and pass only for large text in dark mode. Raise them to the subtext colour.
02

Touch Targets

Source
apps/mobile/CLAUDE.md · components/Header.tsx · components/ConsentOption.tsx §3b
  • Every tappable thing is at least 44 by 44 points. Buttons are 44pt tall (py-3), and the Welcome pills match.
  • Small icons get a larger hit area: header icons are 28pt drawn with an 8pt hitSlop on every side, which makes 44pt.
  • Choice rows are pressable across their full width, not only on the 24pt radio.
  • Keep at least 8pt between separate targets, as the chip and action rows do (gap-2).
03

Text Size

Source
components/ThemedText.tsx · tailwind.config.js
  • All copy goes through ThemedText, which follows the phone's text size setting up to 1.25 times (maxFontSizeMultiplier). Body text at 14pt grows to 17.5pt.
  • Layouts must hold at 1.25 times: titles wrap rather than truncate, rows grow in height, and nothing important sits in a fixed-height box.
  • The cap protects layouts but limits people who set very large text. Review it with readers who use the largest sizes before launch.
04

Screen Readers

Source
components/ConsentOption.tsx · components/RecordButton.tsx · components/AudioPlayerBar.tsx · app/(join)/welcome.tsx

The app sets roles and labels on its controls: buttons, headers, radio groups for the four choices, switches, tabs, a timer and an adjustable playback position. Examples from the code:

Element
Role
What is read
A choice of who can use it
radio, in a radiogroup
"Nzinga's models. suggested. Teaches Nzinga your language. Paid", with selected and disabled states
Record button
button
"Start recording" or "Stop recording"
Playback bar
adjustable
"Playback position"
Welcome slide
text
"1 of 3. Your language. Your voice. Your terms. Record voice notes…"
Screen titles
header
The title, so readers can jump between sections
Status badge
text
The word itself: "In review", "Accepted"
  • Every icon-only control has a label that says what it does, not what it shows: "Open menu", not "Three lines".
  • Read order follows the screen: title, then content, then the footer action.
  • Money is read as words the reader can say: "$0.42", never a bare "42".
05

Motion

Source
components/AnimatedView.tsx · components/RecordButton.tsx · docs/ds/nzinga-ds.css
  • Motion confirms an action; it never carries information on its own. The live recording state also says "Recording" in words and shows a timer.
  • When the phone asks for reduced motion, replace slides and flips with a plain fade or no animation, and stop the breathing record button.

Not built yet The app does not read the Reduce Motion setting today. In this site All transitions stop under prefers-reduced-motion.

06

Low Literacy and Low Bandwidth

Source
SPEC.md · apps/mobile/CLAUDE.md · lib/sessions.ts · lib/prompts.ts §3.3–3.4

Audio first

The product is speaking, not typing. The main action is one large button, ideas to record are short spoken-style prompts, and Record Well teaches in five one-line lessons.

Icons with words

Every icon comes with a word, every status is a word, and choice rows are one line with a short pay tag.

Small downloads

Audio stays on the phone; only small details are sent. Screens load a few small JSON answers. The spec plans low-bitrate Opus audio and uploads that can wait for Wi-Fi.

Works when the network does not

Every list has an error state with Try again. Recordings, progress and payout methods are kept on the phone, so nothing is lost when a request fails.

07

Colour-Blind Safe States

Source
components/StatusBadge.tsx · Timelines · components/ConsentOption.tsx in recording-detail.tsx

No state is shown by colour alone. Badges carry a word, timelines carry a check or a cross, and the selected choice carries a check as well as its Volt edge.

Example
Live component
Do
A word in every badge: "Accepted", "In review", "Not accepted", with a dot for states still moving.
Don't
A green dot for accepted and a red dot for rejected.
Indistinguishable for many readers, and meaningless to a screen reader.