Ray Book
웹 접근성

색상, 대비, 모션

색상만으로 정보를 전달하고 있지 않은가? 대비 비율, 색맹 고려, prefers-reduced-motion을 시각화합니다

accessibilitycontrastcolor-blindmotionwcag

색상만으로 정보를 전달하고 있지 않은가?

이전 글에서 스크린 리더와 키보드 내비게이션을 다뤘습니다. 하지만 접근성은 시각 장애에만 국한되지 않습니다. 저시력, 색맹, 전정 장애 등 시각적 요소와 관련된 접근성 문제도 중요합니다.

이번 글에서는 세 가지 질문을 던집니다.

  1. 텍스트를 읽을 수 있는가?, 대비 비율
  2. 색을 구분하지 못해도 정보를 얻을 수 있는가?, 색맹 고려
  3. 움직이는 요소가 불편하지 않은가?, 모션 감소

색 대비 기준: WCAG AA와 AAA

WCAG (Web Content Accessibility Guidelines) 는 텍스트와 배경 사이의 대비 비율 (contrast ratio) 을 구체적인 숫자로 규정합니다.

수준일반 텍스트큰 텍스트 (24px+ 또는 18.66px 볼드)UI 컴포넌트
AA4.5 : 13 : 13 : 1
AAA7 : 14.5 : 1-

대비 비율의 범위는 1:1 (동일 색상) 에서 21:1 (흰색 위 검정) 까지입니다. 비율이 높을수록 읽기 쉽습니다.

대비 비율 = (밝은 색의 상대 휘도 + 0.05) / (어두운 색의 상대 휘도 + 0.05)

예) 흰색(#fff)과 회색(#767676)
  L_white = 1.0, L_gray = 0.176
  비율 = (1.0 + 0.05) / (0.176 + 0.05) = 4.54:1 → AA 통과

아래 시각화에서 각 대비 수준이 실제로 어떻게 보이는지 확인하세요. 마지막 단계에서는 색상만으로 정보를 전달할 때의 문제도 함께 보여줍니다.

FAIL낮은 대비 — WCAG 실패
이 텍스트는 읽기 어렵습니다
흰 배경 + 밝은 회색 텍스트2.3:1
배경과 구분이 안 됩니다
밝은 회색 배경 + 회색 텍스트1.6:1
대비 비율이 4.5:1 미만이면 저시력 사용자는 텍스트를 읽을 수 없습니다. 화면 밝기를 낮추거나 햇빛 아래서 보면 일반 사용자도 힘들어집니다.

색맹 고려

전 세계 남성의 약 8%, **여성의 약 0.5%**가 색각 이상이 있습니다. 가장 흔한 유형은 적록 색맹으로, 빨간색과 초록색을 구분하기 어렵습니다.

유형영향비율 (남성)
제1색맹 (Protanopia)빨간색 인식 불가~1%
제2색맹 (Deuteranopia)초록색 인식 불가~1%
제1색약 (Protanomaly)빨간색 인식 감소~1%
제2색약 (Deuteranomaly)초록색 인식 감소~5%

실무에서의 원칙:

  • 색상에만 의존하지 말 것, 아이콘, 패턴, 텍스트를 함께 사용
  • 성공/실패를 빨강/초록으로만 표시하지 말 것, ✓/✕ 아이콘 병행
  • 그래프에서 색 구분이 어려우면 패턴 (빗금, 점선) 을 추가
  • 링크를 색상만으로 구분하지 말 것, 밑줄을 함께 사용
/* ❌ 색상만으로 링크 구분 */
a { color: blue; text-decoration: none; }

/* ✅ 밑줄 + 색상으로 구분 */
a { color: blue; text-decoration: underline; }

대비 검사 도구

대비 비율을 직접 계산할 필요는 없습니다. 도구를 사용하세요.

Chrome DevTools, Elements 패널에서 색상 값을 클릭하면 대비 비율이 표시됩니다. AA/AAA 통과 여부도 함께 보여줍니다.

WebAIM Contrast Checker, 전경색과 배경색을 입력하면 즉시 대비 비율을 계산합니다. 가장 많이 사용되는 온라인 도구입니다.

Lighthouse, Chrome DevTools의 Lighthouse 탭에서 접근성 감사를 실행하면 대비 문제를 자동으로 찾아줍니다.

// 프로그래밍 방식으로 대비 비율 계산
function relativeLuminance(hex) {
  const [r, g, b] = [1, 3, 5].map((i) => {
    const c = parseInt(hex.slice(i, i + 2), 16) / 255;
    return c <= 0.04045
      ? c / 12.92
      : Math.pow((c + 0.055) / 1.055, 2.4);
  });
  return 0.2126 * r + 0.7152 * g + 0.0722 * b;
}

function contrastRatio(hex1, hex2) {
  const l1 = relativeLuminance(hex1);
  const l2 = relativeLuminance(hex2);
  const lighter = Math.max(l1, l2);
  const darker = Math.min(l1, l2);
  return (lighter + 0.05) / (darker + 0.05);
}

contrastRatio("#ffffff", "#767676"); // 4.54 → AA 통과

모션 감소: prefers-reduced-motion

일부 사용자는 화면의 움직임으로 인해 어지러움, 메스꺼움, 두통을 겪습니다. 전정 장애 (vestibular disorders) 를 가진 사용자가 대표적입니다.

OS에서 "모션 줄이기" 설정을 켜면 CSS 미디어 쿼리 prefers-reduced-motion으로 감지할 수 있습니다.

/* 기본: 애니메이션 적용 */
.card {
  transition: transform 0.3s ease;
}

.card:hover {
  transform: scale(1.05);
}

/* 모션 줄이기 설정 시: 애니메이션 제거 또는 대체 */
@media (prefers-reduced-motion: reduce) {
  .card {
    transition: none;
  }

  .card:hover {
    transform: none;
    /* 대신 opacity 변경 같은 부드러운 대안 사용 */
    opacity: 0.9;
  }
}

JavaScript에서도 확인할 수 있습니다.

// JS에서 모션 감소 설정 감지
const motionQuery = window.matchMedia(
  "(prefers-reduced-motion: reduce)"
);

if (motionQuery.matches) {
  // 애니메이션을 비활성화하거나 간소화
  document.documentElement.classList.add("reduce-motion");
}

// 설정 변경 감지
motionQuery.addEventListener("change", (e) => {
  if (e.matches) {
    // 모션 감소 활성화됨
    cancelAllAnimations();
  }
});

핵심 원칙:

  • 모든 애니메이션을 없애는 것이 아니라, 과도한 모션을 줄이는 것
  • 페이드인/페이드아웃은 유지해도 됨, 회전, 확대/축소, 슬라이드는 줄이거나 제거
  • 5초 이상 지속되는 애니메이션은 정지 버튼 제공
  • 자동 재생 캐러셀은 prefers-reduced-motion: reduce에서 정지

다크 모드와 접근성

다크 모드를 지원한다고 접근성이 자동으로 좋아지는 것은 아닙니다. 다크 모드에서도 대비 비율을 유지해야 합니다.

:root {
  --text: #1a1a1a;        /* 라이트: 거의 검정 */
  --bg: #ffffff;           /* 라이트: 흰색 */
  /* 대비 비율: 약 17.4:1 ✅ */
}

@media (prefers-color-scheme: dark) {
  :root {
    --text: #e5e5e5;       /* 다크: 밝은 회색 */
    --bg: #171717;         /* 다크: 거의 검정 */
    /* 대비 비율: 약 15.4:1 ✅ */
  }
}

/* ❌ 다크 모드에서 흔한 실수 */
@media (prefers-color-scheme: dark) {
  :root {
    --text: #888888;       /* 너무 어두운 텍스트 */
    --bg: #333333;         /* 너무 밝은 배경 */
    /* 대비 비율: 약 2.4:1 ❌ */
  }
}

텍스트 크기와 확대

WCAG 1.4.4는 텍스트를 200%까지 확대해도 콘텐츠가 유실되거나 기능이 깨지지 않아야 한다고 규정합니다.

  • px 대신 rem을 사용하면 사용자가 브라우저 기본 글꼴 크기를 변경할 때 함께 조정됨
  • 본문 텍스트는 최소 16px (1rem) 을 권장
  • max-widthch 단위로 설정하면 줄당 문자 수를 제한해 가독성 향상
/* ❌ 고정 px, 사용자 설정 무시 */
body { font-size: 14px; }

/* ✅ rem, 사용자 브라우저 설정에 반응 */
body { font-size: 1rem; } /* 기본 16px */
h1   { font-size: 2rem; } /* 32px */

/* 줄 길이 제한으로 가독성 향상 */
article { max-width: 70ch; }

시각 접근성 체크리스트

항목기준
텍스트 대비일반 4.5:1 이상, 큰 텍스트 (24px+) 3:1 이상 (AA)
UI 컴포넌트 대비3:1 이상
색상만으로 정보 전달❌ 아이콘/텍스트/패턴 병행
링크 구분색상 + 밑줄
prefers-reduced-motion과도한 모션 제거 또는 대체
다크 모드 대비라이트 모드와 동일한 기준 유지
텍스트 크기rem 사용, 200% 확대 대응
줄 길이70~80ch 이내 권장

다음 글: 폼과 에러 처리

시각적 접근성을 확보했다면, 이제 사용자가 실제로 상호작용하는 폼을 살펴볼 차례입니다. 다음 글에서는 label 연결, 에러 메시지 전달, aria-live를 다룹니다.