접근성#
CoUI 의 접근성은 두 층이다. 전역 축은 컴포넌트가 무엇이든 상관없이 한 번 켜지고 전부에 적용된다. 컴포넌트별 동작은 그 컴포넌트만의 키보드 조작·역할·포커스 관리이고, 각 컴포넌트 문서의 "접근성 (Accessibility)" 절에 있다.
이 페이지는 전역 축만 다룬다. 컴포넌트 문서에서 반복하지 않는 이유는 단순하다 — 축이 하나 바뀌면 139개 문서가 동시에 낡는다.
동작 줄이기 (reduced motion)#
사용자가 OS 에서 "동작 줄이기"를 켜면 CoUI 는 무언가를 다른 자리로 옮기는 모션을 끈다. 컴포넌트 코드는 이 설정을 읽지 않는다 — duration 토큰 자신이 어느 tier 인지에 따라 갈린다.
| 무엇 | 켜졌을 때 | |
|---|---|---|
| 전환 | 열림/닫힘, 슬라이드, 페이드, hover 반응 | 즉시 (0ms) |
| 표시 | 스피너, 무한 진행률, 토스트 표시 시간, 캐러셀 자동재생 간격 | 그대로 |
표시 tier 를 끄지 않는 이유: 멈춘 스피너는 더 이상 "아직 작업 중"이라고 말하지 않고, 얼어붙은 진행률 표시는 앱이 죽은 것으로 읽힌다. 장식을 지우는 것과 정보를 지우는 것은 다르다.
-
Flutter —
MediaQuery.disableAnimations를 읽는다. iOS/Android/macOS 의 시스템 설정이 그대로 흐른다. - Web —
@media (prefers-reduced-motion: reduce).
알려진 한계: Web 에서 호출자가 토큰이 아닌 임의 duration 을 넘겨 inline 으로 나가는 경로는 CSS 변수를 거치지 않아 이 오버라이드가 닿지 않는다. 토큰을 쓰면 전부 닿는다.
고대비 (high contrast)#
사용자가 고대비를 켜면 같은 62개 시맨틱 색 슬롯이 더 강한 값으로 해석된다. 컴포넌트는 이 축의 존재를 모른다 — 색이 해석되는 지점 한 곳에서만 갈린다.
-
Flutter —
MediaQuery.highContrastOf. 앱이 고대비 테마를 정의하지 않았으면 평상시 테마로 폴백한다(검토된 적 없는 팔레트를 렌더하는 것보다 낫다). - Web —
@media (prefers-contrast: more).
일부러 하지 않는 두 가지가 있다. surface 톤을 흰색으로 눕히지 않는다 — 카드·입력·팝오버는 배경 톤으로 구분되고 그중 일부만 보더를 그린다. 톤을 눕히면 그 경계가 사라지는데 대체할 것이 없고, 안 보이게 된 컴포넌트는 대비가 낮았던 것보다 나쁘다. 전경 극성을 역할마다 뒤집지 않는다 — 같은 컴포넌트가 어디선 밝은 글자, 어디선 어두운 글자로 읽히면 그 자체가 가독성 문제다.
색 강제 모드 (forced colors)#
Windows 고대비처럼 OS 가 색을 강제 치환하는 모드에서는 그림자가 사라진다 — 그 모드는 색을 바꾸지만 기하를 만들어주지 않는다. 그래서 포커스 표시가
box-shadow 에만 실려 있으면 통째로 없어진다.
CoUI 는 이 모드에서 포커스에 outline 을 보장하고, 반대로 평상시 상태의 예약된 투명 outline 은 제거한다. 후자를 안 하면 모든 요소가 상시 링을 두르게 되어
포커스를 구별할 수 없다 — 링이 사라지는 것보다 나쁘다.
시맨틱 역할#
Flutter 의 Semantics 와 Web 의 ARIA role 은 하나의 목록에서 나온다. 두 플랫폼 어댑터가 그 목록의 모든 값에 답해야 컴파일되므로, 역할을 한쪽에만 추가하는 경로가 구조적으로 없다.
역할에는 전제조건이 붙는다. 예를 들어 체크 상태를 말하지 않는 노드에 체크박스 역할을 붙이면 리더가 "체크 안 됨"이라고 지어낸다. CoUI 는 전제가 충족되지 않은 역할을 붙이는 대신 떨어뜨린다 — 아무 말도 하지 않는 편이 틀린 말보다 낫다.
일부 역할은 의도적으로 양쪽 다 내보내지 않는다. Flutter SDK 가 그 역할과 live region 을 동시에 주장하는 노드를 거부하는 경우가 있어, 실제로 통보되는 쪽(live region)만 내보낸다. 두 플랫폼이 다르게 답하지 않게 하려고 Web 도 같이 보류한다.
최소 터치 타겟#
포인터로 조작하는 요소는 최소 24×24 논리 픽셀의 히트 영역을 갖는다(WCAG 2.2 AA). 시각적으로 그보다 작은 컨트롤은 보이는 크기를 유지한 채 히트 영역만 넓힌다 — 아이콘이 커지는 게 아니라 누를 수 있는 범위가 커진다.
각 컴포넌트가 따로 말하는 것#
- ARIA role / Semantics 노출
- 키보드 조작
- 스크린 리더가 읽는 내용
- 포커스 관리 (진입 · 이탈 · 트랩)
- 알려진 제약
컴포넌트 문서의 "접근성 (Accessibility)" 절에 있다. 비어 있는 문서는 없다 — 그 절이 없는 문서가 있으면 테스트가 깨진다. 컴포넌트가 키보드를 하나도 다루지 않으면 "없음"이라고 적혀 있고, 그것도 정보다.