Skip to content

Accessibility (A11y) Standards + Test Plan

Status: Reference

Scope note: the primary accessibility reference is UI/UX & Accessibility. This doc is the focused WCAG 2.1 AA test-plan companion.

Production-readiness #12. Documents what TwoMore enforces mechanically, what requires manual device testing, and the test plan for screen-reader audits before public launch.

Standards we comply with

  • WCAG 2.1 Level AA — international baseline
  • Korean Web Accessibility Guidelines 2.1 — KCC-published, mostly WCAG-aligned with extra emphasis on screen-reader compatibility for the elderly user base (large in tennis demographics)

What's mechanically enforced

ConstraintEnforcement
Every Pressable has accessibilityLabelTypeScript-required on the @twomore/ui Pressable primitive
Touch targets ≥ 44ptPressable variants size their outer container correctly
Disabled state announces "busy"Pressable + Button set accessibilityState.disabled + busy
Color contrast on text + iconsTamagui token system — all $text / $textMuted etc. designed against $background for WCAG AA
Focus ring on webPressable web build adds outline: 2px solid $primary on :focus-visible
Korean copy in t()Every UI string lives in i18n — no hardcoded English to confuse TalkBack 한국어

What's NOT mechanically enforced (manual gaps)

  1. <Image> accessibility labels — RN's Image doesn't TS-enforce accessibilityLabel. Decorative images should be accessible={false}; content images need a label. Current state: 5 known content Images missing labels (post-detail, edit-profile avatar, club-members avatar, create-club logo preview, club-settings logo). Fixed in adjacent commits to this doc — but the gap isn't structurally prevented.

  2. accessibilityRole="header" on page/section titles — without it TalkBack reads the title as plain text instead of "Heading: 클럽 설정". Inconsistently applied across screens.

  3. Focus management on Sheet/Modal open — when ConfirmSheet or any Sheet opens, focus should move to the sheet's first focusable element. Currently relies on RN's default behavior which is inconsistent.

  4. Live region announcements — for realtime updates (new score posted, RSVP confirmed), TalkBack users don't hear the change unless they re-traverse. Should use accessibilityLiveRegion="polite" on the affected text.

  5. Dynamic type / large fonts — most Text uses fixed role sizes. Korean Pretendard font scales but layout may break at 200% font size.

Manual test plan

Run BEFORE public launch on a real device with screen reader enabled.

Top 5 screens to audit (priority order)

  1. Login + Onboarding — first impression; must be perfect

    • Can a TalkBack user complete signup?
    • Are the 3 PIPA consent checkboxes labeled + clearly required vs optional?
    • Does the welcome → consent → display-name → play-style flow announce each step?
  2. Home feed — the most-used screen

    • SessionCard chip strip: are 형식, 경쟁/친선, 진행중 chips all readable?
    • LiveSessionCard pulses: does the PulseDot get an accessibilityLabel ("실시간")?
    • SegmentedTabs (오늘 / 이번 주 / 다음 달): tab selection announced?
    • Bottom tabs (홈 / 클럽 / 경기 / 기록 / 프로필): selected state announced?
  3. Session detail — the action surface

    • RSVP button state announced (확정 → 취소)?
    • Participant card with avatars: does each avatar identify the player?
    • Admin section: are owner-only buttons clearly distinguishable?
  4. Spectator scorecard LIVE tab — most complex layout

    • "내 경기" chip: announced as part of the row, not skipped?
    • ScoreEditSheet: focus moves to the score input on open?
    • Pending-call callout: does it announce as an alert?
  5. Privacy + Settings — legal compliance + PIPA

    • 동의 항목 (required/optional) clearly distinguishable by TalkBack?
    • "내 데이터 받기" button: full action description, not just label?
    • "탈퇴" / 계정 삭제: warning state announced?

TalkBack-specific test scenarios

For each of the 5 screens:

  • [ ] Swipe right traverses elements in expected reading order
  • [ ] No element is skipped or announced twice
  • [ ] All buttons announce their purpose, not just label (e.g. "참가 확정 버튼")
  • [ ] Headings announced as "Heading"
  • [ ] Disabled buttons announce "사용 불가"
  • [ ] Loading states announce ("로딩 중...")
  • [ ] Errors announce as alerts, not silent rendering

VoiceOver-specific

iOS-specific tests when iOS preview build lands:

  • [ ] Rotor navigation works (Headings, Buttons, Form Controls)
  • [ ] Custom controls (chip groups, sheet drag handles) expose proper traits

Known fixes deferred

  • Image accessibilityLabel sweep — 5 files, fixable when device testing reveals the actual user impact (some images may turn out to be decorative duplicates of nearby text)
  • accessibilityRole="header" sweep — every <Text role="pageTitle"> could auto-set this; needs a UI primitive change
  • Live region announcements on score updates — depends on a device test to confirm what TalkBack users currently experience

Future enforcement (when device testing is available)

  • ESLint rule: <Image> without accessibilityLabel AND without accessible={false} is a lint error
  • ESLint rule: <Text role="pageTitle"> must also set accessibilityRole="header" (or have a UI primitive do it)
  • Storybook-style a11y addon if a visual catalog is built

Reference

Markdown remains the source of truth. Run yarn docs:check before handoff.