07 — Motion Accessibility (멀미·전정장애·윤리)
한 줄 답: 모션은 모두에게 멋진 것이 아니다. 인구의 약 35%가 vestibular(전정) 민감성을 가지며, 이들에게 패럴랙스·자동 캐러셀·큰 슬라이드 전환은 두통·메스꺼움·균형 상실을 유발한다.
@media (prefers-reduced-motion: reduce)는 디자인 선호가 아니라 WCAG 2.3 의무다.
Why — 왜 모션 챕터의 마지막에 이게 오는가
지금까지 챕터(01~06)는 어떻게 더 부드럽게 움직이게 할까를 다뤘다. 이 챕터는 어떻게 안 움직이게 할까를 다룬다.
이는 소수의 예외 처리가 아니다. WHO 추산 전 세계 vestibular dysfunction 유병률 ~35%. 그 외 ADHD·자폐 스펙트럼·편두통 환자·임산부도 모션에 강한 민감성을 가진다. 모션을 끌 수 없는 인터페이스는 그 자체로 결함이다.
How — 어떻게 적용하나
1) prefers-reduced-motion 미디어 쿼리
@media (prefers-reduced-motion: reduce) {
/* 모션을 꺼야 할 때의 스타일 */
}값 2가지:
no-preference(기본) — 사용자가 모션 감소를 선택하지 않음reduce— 사용자가 OS·브라우저 설정에서 모션 감소를 선택
OS 설정 경로:
- macOS — System Settings → Accessibility → Display → Reduce motion
- iOS — Settings → Accessibility → Motion → Reduce Motion
- Windows — Settings → Accessibility → Visual effects → Animation effects (off)
- Android — Settings → Accessibility → Remove animations
2) 두 가지 전략 — 끄기 vs 대체
Tailwind/MUI 등 라이브러리는 보통 전략 A를 전역 default로 깔고, 필요한 곳만 전략 B로 정밀 대체.
3) 전역 기본 (안전 망)
거의 모든 디자인 시스템의 첫 줄:
@media (prefers-reduced-motion: reduce) {
*, *::before, *::after {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
scroll-behavior: auto !important;
}
}왜 0.01ms인가? 0이면 애니메이션 자체가 발동 안 됨 → animationend 이벤트 미발생 → JS 콜백이 안 불림. 0.01ms는 실질적으로 즉시 끝나지만 이벤트는 발생. JS와 호환되는 안전한 값.
4) 정밀 대체 — 필수 의미 보존
전체를 끄면 어떤 변화가 일어났는지 사용자가 모를 수도 있다. 예: 모달이 열렸다는 신호조차 사라지면 안 됨.
.modal {
opacity: 0;
transform: translateY(20px);
transition: opacity 200ms, transform 200ms;
}
.modal.open {
opacity: 1;
transform: translateY(0);
}
@media (prefers-reduced-motion: reduce) {
.modal {
transform: none; /* 위치 이동은 꺼라 */
transition: opacity 100ms; /* 페이드는 짧게 유지 */
}
}원칙: 위치·크기 변화는 끄되, 의미 전달용 페이드는 유지.
5) JS API — matchMedia
JS 측에서도 동일하게 감지 가능:
const reduce = window.matchMedia('(prefers-reduced-motion: reduce)');
if (reduce.matches) {
/* GSAP, Framer Motion, Lottie 등에 비활성 옵션 전달 */
gsap.globalTimeline.timeScale(0);
}
/* 사용자가 설정을 바꿀 수도 */
reduce.addEventListener('change', () => { /* 재적용 */ });6) View Transitions·Scroll-driven에도 적용
@media (prefers-reduced-motion: reduce) {
::view-transition-group(*),
::view-transition-old(*),
::view-transition-new(*) {
animation: none !important;
}
* {
animation-timeline: auto !important;
}
}자세한 건 05-view-transitions, 06-scroll-driven.
What — 안전한 모션 가이드라인
A. WCAG 2.3 Animation from Interactions
| 기준 | 등급 | 내용 |
|---|---|---|
| 2.3.1 | A | 1초에 3회 이상 깜빡임 금지 (광민감성 발작) |
| 2.3.3 | AAA | 인터랙션으로 발생하는 모션은 비활성화 가능해야 함 |
미국 ADA 소송 사례: 2019년 Beyoncé.com이 자동재생 비디오와 패럴랙스로 시각·전정 장애인이 사용할 수 없다는 소송. 합의로 끝났고, 이후 프리미엄 브랜드 사이트는 reduced-motion 지원이 기본이 됨.
B. 위험 강도 매트릭스
C. 안전한 기본값
| 속성 | 안전 범위 | 비고 |
|---|---|---|
| transform translate | ≤ 20px | 모달·toast 진입 |
| transform scale | 0.95 ~ 1.05 | hover/press |
| transform rotate | ≤ 15deg | 토글 화살표 |
| transition-duration | 150~300ms | 표준 UI |
| animation iteration | 1 또는 reduce 시 1 | 무한 반복은 reduce 시 정지 |
| 패럴랙스 차이 속도 | reduce 시 0% | 끄기 |
D. 라이브러리별 통합
- Framer Motion:
MotionConfig reducedMotion="user"또는useReducedMotion()hook - GSAP:
gsap.matchMedia()안에 reduce 케이스 분기 - Lottie:
lottie-react에서loop={false},autoplay={false}로 fallback - Tailwind:
motion-safe:/motion-reduce:variant
<div class="motion-safe:animate-bounce">팝</div>motion-safe:animate-bounce는 reduced-motion이 아닌 사용자에게만 적용.
What-if — 잘못 만지면
1) prefers-reduced-motion을 전혀 처리 안 함
iOS Safari로 패럴랙스 사이트 방문 → 멀미 사용자가 5초 안에 페이지를 닫는다. 분석 도구에 이 사용자 세그먼트의 이탈률이 따로 잡히지 않으므로 디자이너는 자신의 사이트가 누구를 쫓아내는지 모른다. WCAG 2.3.3 AAA 위반.
2) animation: none !important을 모든 곳에 일괄
@media (prefers-reduced-motion: reduce) {
* { animation: none !important; } /* ❌ 너무 거침 */
}문제:
- 로딩 스피너까지 멈춤 → 사용자가 시스템이 죽은 줄 안다.
- JS animationend 콜백이 안 불려 모달이 안 열림.
해결: animation-duration: 0.01ms (위 § 3의 전역 기본).
3) “예외적으로 중요한” 애니메이션을 reduced에서도 살림
@media (prefers-reduced-motion: reduce) {
.super-important-spinner {
animation: spin 1s infinite; /* ❌ 사용자 의사 무시 */
}
}사용자가 명시적으로 모션을 꺼달라고 말했는데 *디자이너가 “이건 중요해서 켜둠”*은 윤리적으로 잘못. 정적 표시(progress bar 등)로 기능을 보존하되 모션은 끄는 것이 원칙.
4) Reduced에서 정보가 사라짐
.toast { animation: slide-in 200ms; }
@media (prefers-reduced-motion: reduce) {
.toast { animation: none; opacity: 0; } /* ❌ 안 보임 */
}모션을 꺼도 결과 상태는 보여야 함. forwards나 직접 최종 상태 적용:
@media (prefers-reduced-motion: reduce) {
.toast { animation: none; opacity: 1; }
}5) 자동 캐러셀 (autoplay)
자동으로 5초마다 회전하는 캐러셀은 멀미 + 인지부하. WCAG 2.2.2 (Pause, Stop, Hide)에 따라 5초 이상 자동 움직임은 일시정지 컨트롤 의무. reduced-motion일 땐 자동재생 자체를 꺼라.
const reduce = window.matchMedia('(prefers-reduced-motion: reduce)');
if (!reduce.matches) carousel.startAutoplay();6) 색 + 모션 둘 다에 의지한 의미 전달
.error { animation: shake 200ms; color: red; }색맹 사용자는 색을 못 보고, 모션 비활성 사용자는 흔들림을 못 본다. 둘 다 비활성이면 에러임을 인지 못함. 텍스트·아이콘 등 제3의 채널 필수.
Insight — 한 단락 이야기
“멀미는 디자인의 정직한 피드백이다.”
1990년대 VR 연구자들은 시각과 전정기관의 불일치가 멀미를 유발한다는 걸 정량적으로 밝혔다 (Cybersickness). 우리 뇌는 눈으로 본 움직임과 내이의 균형 감각이 일치할 거라 기대한다. 화면이 갑자기 옆으로 슬라이드하면, 내 눈은 움직이는데 내 몸은 가만히 있다 → 뇌는 독에 중독됐다고 판단해 구토 반사를 일으킨다. 패럴랙스가 위험한 이유는 원근 차이가 내 몸이 움직이는 신호로 오해되기 때문이다. 2014년 iOS 7의 큰 시차 효과가 출시되자 수십만 명의 사용자가 메스꺼움을 호소했고, Apple은 6개월 만에 Reduce Motion 설정을 추가했다. 그로부터 prefers-reduced-motion이 W3C 표준이 됐다 (2017). 흥미로운 점은 *이 설정을 켠 사용자가 평균 35%*라는 것 — 즉 멋진 모션은 최대 3분의 1의 사용자를 쫓아낼 수 있는 무기다. 모션 디자인의 윤리는 결국 단순하다: 사용자가 끄겠다고 하면 끈다. 그게 전부다.
요약 + Mermaid
- vestibular 민감성 유병률 ~35% — 모션은 소수의 예외가 아니라 큰 사용자 세그먼트의 문제.
@media (prefers-reduced-motion: reduce)는 WCAG 2.3.3 AAA 의무.- 전역 기본: 모든 animation·transition의
duration: 0.01ms로 안전 망. - 정밀 대체: 위치 이동은 끄되, 의미 전달 페이드는 짧게 유지.
- 위험 매트릭스: 패럴랙스·자동 캐러셀·큰 슬라이드·3D 회전은 무조건 끄기.
- View Transitions·Scroll-driven에도 동일하게 적용.
- 라이브러리(Framer/GSAP/Lottie)는 모두 reduce 옵션 지원.
- 자동재생 캐러셀은 WCAG 2.2.2 (Pause, Stop, Hide) 의무.
- 모션 + 색만으로 의미 전달 금지 — 제3의 채널 (텍스트·아이콘) 필수.
이 챕터로 모션 도메인이 마무리된다. 다음 도메인: 07-responsive — 이 모션이 어떤 뷰포트·컨텍스트에서 동작하는가.