접근성 | CoUI
LogoCoUI

접근성

CoUI 가 모든 컴포넌트에 걸쳐 보장하는 것과, 각 컴포넌트가 따로 말하는 것

접근성#

CoUI 의 접근성은 두 층이다. 전역 축은 컴포넌트가 무엇이든 상관없이 한 번 켜지고 전부에 적용된다. 컴포넌트별 동작은 그 컴포넌트만의 키보드 조작·역할·포커스 관리이고, 각 컴포넌트 문서의 "접근성 (Accessibility)" 절에 있다.

이 페이지는 전역 축만 다룬다. 컴포넌트 문서에서 반복하지 않는 이유는 단순하다 — 축이 하나 바뀌면 139개 문서가 동시에 낡는다.

동작 줄이기 (reduced motion)#

사용자가 OS 에서 "동작 줄이기"를 켜면 CoUI 는 무언가를 다른 자리로 옮기는 모션을 끈다. 컴포넌트 코드는 이 설정을 읽지 않는다 — duration 토큰 자신이 어느 tier 인지에 따라 갈린다.

무엇켜졌을 때
전환열림/닫힘, 슬라이드, 페이드, hover 반응즉시 (0ms)
표시 스피너, 무한 진행률, 토스트 표시 시간, 캐러셀 자동재생 간격 그대로

표시 tier 를 끄지 않는 이유: 멈춘 스피너는 더 이상 "아직 작업 중"이라고 말하지 않고, 얼어붙은 진행률 표시는 앱이 죽은 것으로 읽힌다. 장식을 지우는 것과 정보를 지우는 것은 다르다.

  • FlutterMediaQuery.disableAnimations 를 읽는다. iOS/Android/macOS 의 시스템 설정이 그대로 흐른다.
  • Web@media (prefers-reduced-motion: reduce).

알려진 한계: Web 에서 호출자가 토큰이 아닌 임의 duration 을 넘겨 inline 으로 나가는 경로는 CSS 변수를 거치지 않아 이 오버라이드가 닿지 않는다. 토큰을 쓰면 전부 닿는다.

고대비 (high contrast)#

사용자가 고대비를 켜면 같은 62개 시맨틱 색 슬롯이 더 강한 값으로 해석된다. 컴포넌트는 이 축의 존재를 모른다 — 색이 해석되는 지점 한 곳에서만 갈린다.

  • FlutterMediaQuery.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)" 절에 있다. 비어 있는 문서는 없다 — 그 절이 없는 문서가 있으면 테스트가 깨진다. 컴포넌트가 키보드를 하나도 다루지 않으면 "없음"이라고 적혀 있고, 그것도 정보다.