들어가며

“빨강과 초록으로 구분했으니까 괜찮을 거야.”

개발하다 보면 한 번쯤 이런 생각을 해보게 됩니다. 그런데 생각보다 많은 사람들이 이 두 색을 잘 구분하지 못합니다. 북유럽 혈통 기준 통계에서는 남성 약 1/12, 여성 약 1/200이 적색-녹색 색각이상을 가지고 있다고 알려져 있어요. 지역과 유전적 배경에 따라 비율은 달라질 수 있지만, 빨강-초록 구분이 어려운 사용자가 꾸준히 존재한다는 사실은 변하지 않습니다.

그리고 이게 전부가 아닙니다. 저시력 사용자, 나이가 든 사용자, 밝은 햇빛 아래서 화면을 보는 사용자들도 모두 색상 대비에 영향을 받아요.

색상 접근성은 단순히 “색맹인 사람도 볼 수 있게"하는 것이 아닙니다. 모든 사용자가 정보를 명확하게 인식하고, 색상이 정보 전달의 유일한 수단이 아니도록 설계하는 거죠.

이번 글에서는 색상 접근성의 이론부터 실무 팁, 검사 도구까지 정리했습니다. 현업에서 디자이너와 이 주제를 어떻게 다룰지도 함께 살펴볼게요.

색상과 접근성 - 색상은 접근성에서 굉장히 중요한 영역 중 하나입니다.
색상과 접근성 - 색상은 접근성에서 굉장히 중요한 영역 중 하나입니다.
제작: Nanobanana
색상 접근성의 다양한 측면을 시각화. 왼쪽에는 일반적인 시각으로 보는 웹페이지, 오른쪽에는 색각이상자가 보는 같은 페이지.
색상 접근성의 다양한 측면을 시각화. 왼쪽에는 일반적인 시각으로 보는 웹페이지, 오른쪽에는 색각이상자가 보는 같은 페이지.
제작: Nanobanana

색상 접근성이란?

정의

색상 접근성은 색상을 포함한 모든 사용자가 웹사이트의 정보를 정확하게 인식할 수 있도록 하는 설계 원칙입니다.

여기에는 다음이 포함됩니다:

  1. 색각이상 사용자: 특정 색상을 구분하기 어려운 사람들
  2. 저시력 사용자: 명도 대비가 낮으면 텍스트를 읽을 수 없는 사람들
  3. 노인: 나이가 들면서 색상 구분 능력이 저하되는 사람들
  4. 환경적 제약: 밝은 햇빛 아래나 저품질 모니터에서 보는 사용자들

표준과 요구사항

색상 접근성은 WCAG 2.2에서 명확히 다룹니다:

  • SC 1.4.3 (Contrast Minimum): 일반 텍스트 4.5:1, 큰 텍스트 3:1
  • SC 1.4.6 (Contrast Enhanced): AAA 수준 7:1 / 4.5:1
  • SC 1.4.11 (Non-text Contrast): 아이콘·입력 필드·포커스 표시 등 비텍스트 요소 3:1
  • SC 2.4.11 / 2.4.12: 포커스가 다른 요소에 가려지지 않도록 보장
  • SC 2.4.13 (Focus Appearance, AAA): 포커스 표시의 크기·대비 기준 제시

많은 조직과 규제가 WCAG 2.x를 참고하기 때문에, 이 기준을 이해하면 실무 적용이 훨씬 수월해집니다.

포용성 비즈니스 케이스

색상 접근성은 법적 의무만이 아닙니다:

  • 더 많은 사용자가 정보를 정확히 읽고 이해할 수 있음
  • 검색 최적화 (명확한 색상 대비는 텍스트 가독성 향상)
  • 브랜드 이미지 (접근성 있는 회사로의 인식)
  • 사용성 개선 (모든 사용자가 더 잘 읽을 수 있음)

색각이상 이해하기

색각이상의 유형

1. 적색 약시 (Protanomaly): 빨강을 볼 때 회색처럼 보임

  • 빨강 파장에 반응이 약함

2. 적색 맹 (Protanopia): 빨강을 완전히 못 봄

  • 세상이 파랑-노랑으로만 보임

3. 녹색 약시 (Deuteranomaly): 초록을 볼 때 회색처럼 보임

  • 가장 흔한 유형으로 알려짐

4. 녹색 맹 (Deuteranopia): 초록을 완전히 못 봄

5. 청색-황색 색각이상 (Tritanomaly/Tritanopia): 파랑과 노랑 구분 어려움

  • 매우 드문 유형으로 알려짐
다양한 색각이상 유형의 색상 인식 시뮬레이션. 같은 웹페이지가 6가지 다른 방식으로 표현됨: 일반 시각(Normal vision), 적색 약시(Protanomaly), 적색 맹(Protanopia), 녹색 약시(Deuteranomaly), 녹색 맹(Deuteranopia), 청색-황색 약시(Tritanomaly). 각 시각에서 색상이 어떻게 다르게 보이는지 명확히 확인해보세요.
다양한 색각이상 유형의 색상 인식 시뮬레이션. 같은 웹페이지가 6가지 다른 방식으로 표현됨: 일반 시각(Normal vision), 적색 약시(Protanomaly), 적색 맹(Protanopia), 녹색 약시(Deuteranomaly), 녹색 맹(Deuteranopia), 청색-황색 약시(Tritanomaly). 각 시각에서 색상이 어떻게 다르게 보이는지 명확히 확인해보세요.
제작: Nanobanana

명도 대비(Luminance Contrast)

WCAG 기준

WCAG 2.2에서는 텍스트와 배경의 명도 대비를 숫자로 정의합니다:

레벨일반 텍스트큰 텍스트
A(요구사항 없음)(요구사항 없음)
AA4.5:13:1
AAA7:14.5:1

큰 텍스트의 정의:

  • 18pt 이상, 또는
  • 14pt 이상이고 굵은 글씨 (700 이상)

추가로 확인할 것 (WCAG 2.2):

  • 비텍스트 대비 (SC 1.4.11): 아이콘, 테두리, 입력 필드 등 비텍스트 요소의 대비를 최소 3:1로 유지합니다.
  • 포커스 가림 방지 (SC 2.4.11/2.4.12): 포커스 표시가 다른 UI에 가려지지 않도록 보장합니다.
  • 포커스 표시 형태 (SC 2.4.13, AAA): 포커스 표시의 크기·대비 기준을 제시합니다. (높은 등급 목표 시 참고)

명도(Luminance) 계산

javascript
// RGB를 명도로 변환하는 공식 (WCAG)
function getLuminance(r, g, b) {
  // 0-255 범위를 0-1로 정규화
  r = r / 255;
  g = g / 255;
  b = b / 255;

  // 감마 보정
  if (r <= 0.03928) {
    r = r / 12.92;
  } else {
    r = Math.pow((r + 0.055) / 1.055, 2.4);
  }

  if (g <= 0.03928) {
    g = g / 12.92;
  } else {
    g = Math.pow((g + 0.055) / 1.055, 2.4);
  }

  if (b <= 0.03928) {
    b = b / 12.92;
  } else {
    b = Math.pow((b + 0.055) / 1.055, 2.4);
  }

  return 0.2126 * r + 0.7152 * g + 0.0722 * b;
}

function getContrast(foreground, background) {
  const l1 = getLuminance(...foreground); // RGB 배열
  const l2 = getLuminance(...background);

  const lighter = Math.max(l1, l2);
  const darker = Math.min(l1, l2);

  return (lighter + 0.05) / (darker + 0.05);
}

// 예시: 검은 텍스트 #000000 vs 흰 배경 #FFFFFF
const contrast = getContrast([0, 0, 0], [255, 255, 255]);
console.log(contrast); // 21 (아주 좋음!)

실용적인 대비 조합

반드시 확인하세요:

css
/* ✅ 좋은 예 - 4.5:1 이상 */
.good-contrast {
  color: #000000;        /* 검은색 */
  background: #FFFFFF;   /* 흰색 */
  /* 대비: 21:1 */
}

/* ❌ 나쁜 예 - 3:1 미만 */
.bad-contrast {
  color: #777777;        /* 회색 */
  background: #CCCCCC;   /* 밝은 회색 */
  /* 대비: 약 2.79:1 */
}

/* ✅ 다크 모드 예 */
@media (prefers-color-scheme: dark) {
  body {
    color: #EEEEEE;      /* 밝은 회색 텍스트 */
    background: #1a1a1a; /* 어두운 배경 */
    /* 대비: 약 15:1 */
  }
}
WCAG 대비 비율 가이드라인 - 마치 신호등처럼 빨강(실패) 에서 4.5:1을 준수한 노랑(AA 통과) 에서 7:1을 준수한 녹생(AAA 패스) 를 단계별로 비교합니다.
WCAG 대비 비율 가이드라인 - 마치 신호등처럼 빨강(실패) 에서 4.5:1을 준수한 노랑(AA 통과) 에서 7:1을 준수한 녹생(AAA 패스) 를 단계별로 비교합니다.
제작: Nanobanana

도구를 이용한 검사

  1. WebAIM Contrast Checker: https://webaim.org/resources/contrastchecker/
  2. Chrome DevTools: Elements > Inspect > 색상 선택기
  3. Contrast Ratio 웹사이트: https://contrast-ratio.com/

색상만으로 정보 전달하면 안 된다

문제 상황

html
<!-- ❌ 나쁜 예 -->
<div>
  <span style="color: red">오류</span>
  <span style="color: green">성공</span>
</div>

<!-- 색각이상자는 둘 다 같은 색으로 보임 -->

해결 방법

html
<!-- ✅ 좋은 예 1: 텍스트 포함 -->
<div>
  <span style="color: red">❌ 오류</span>
  <span style="color: green">✅ 성공</span>
</div>

<!-- ✅ 좋은 예 2: 아이콘 + 텍스트 -->
<div>
  <span style="color: red" aria-label="오류">
    <i class="icon-error"></i> 오류 메시지
  </span>
</div>

<!-- ✅ 좋은 예 3: 패턴 사용 -->
<svg width="100" height="100">
  <!-- 빨강 + 대각선 패턴 -->
  <rect fill="red" width="50" height="50"/>
  <pattern id="diag" patternUnits="userSpaceOnUse" width="8" height="8">
    <path d="M0,8 l8,-8 M-2,2 l4,-4 M6,10 l4,-4" stroke="black" stroke-width="1"/>
  </pattern>
  <rect fill="url(#diag)" width="50" height="50"/>
</svg>

폼 입력 검증 예제

html
<!-- ❌ 색상만 사용 -->
<input type="email" style="border: 2px solid red;">

<!-- ✅ 색상 + 아이콘 + 메시지 -->
<div class="form-group">
  <input
    type="email"
    class="form-input has-error"
    aria-describedby="email-error">
  <svg class="error-icon" aria-hidden="true">
    <use xlink:href="#icon-error"></use>
  </svg>
  <p id="email-error" class="error-message">
    유효한 이메일 주소를 입력하세요
  </p>
</div>
css
.form-input.has-error {
  border: 2px solid #d32f2f; /* 빨강 */
  background-color: #ffebee; /* 연한 빨강 */
}

.error-icon {
  color: #d32f2f; /* 빨강 */
}

.error-message {
  color: #d32f2f;
  /* 텍스트로 문제를 명확하게 설명 */
}

색상 선택 가이드

1단계: 먼저 명도로 설계

css
/* 색상 없이 흑백으로 먼저 설계 */
body {
  color: #333333; /* 어두운 회색 텍스트 */
  background: #ffffff; /* 흰 배경 */
}

/* 이 상태에서 이미 4.5:1 이상의 대비 */

2단계: 색상 추가

css
/* 명도를 유지하면서 색상 추가 */
.primary {
  color: #0962db; /* 파랑 - 명도 유지 */
  background: #ffffff;
  /* 명도 대비: 약 5.6:1 */
}

.secondary {
  color: #1e7e34; /* 초록 - 명도 유지 */
  background: #ffffff;
  /* 명도 대비: 약 5.1:1 */
}

3단계: 색맹 시뮬레이션 테스트

javascript
// Deuteranopia (녹색 맹)를 위한 색상 변환
function simulateDeuteranopia(rgb) {
  const [r, g, b] = rgb;
  return [
    0.625 * r + 0.375 * g,
    0.7 * r + 0.3 * g,
    b
  ];
}

// 테스트
const primary = [9, 98, 219]; // 파랑 #0962db
const simulated = simulateDeuteranopia(primary);
// 결과: [42, 36, 219] - 파랑이 약간 다르게 보이지만 구분 가능
// 참고: 실제 색각이상 시뮬레이션은 LMS 색공간 변환이 필요합니다. 이 함수는 교육용 근사치입니다.

권장 색상 팔레트

css
/* 예시 팔레트 (AA 이상을 목표로 설계) */

/* 회색 톤 */
:root {
  --gray-900: #111827; /* 거의 검은색 */
  --gray-700: #374151; /* 어두운 회색 */
  --gray-500: #6b7280; /* 중간 회색 */
  --gray-100: #f3f4f6; /* 밝은 회색 */
  --gray-50: #f9fafb;  /* 거의 흰색 */
}

/* 기능색 (사용 맥락에 따라 대비 재검증 필요) */
:root {
  --success: #065f46;   /* 짙은 초록 */
  --danger: #7f1d1d;    /* 짙은 빨강 */
  --warning: #92400e;   /* 짙은 노랑/갈색 */
  --info: #0c4a6e;      /* 짙은 파랑 */
}

/* 흰 배경 위 사용 */
.success { color: var(--success); background: white; }
.danger { color: var(--danger); background: white; }
.warning { color: var(--warning); background: white; }
.info { color: var(--info); background: white; }
접근성 있는 색상 팔레트 비교. 왼쪽은 일반적인 밝은 색상 팔레트(접근성 부족), 오른쪽은 짙은 톤의 팔레트(접근성 우수). 각 색상의 명도 값과 WCAG 대비 비율을 함께 표시. 색각이상 시뮬레이션도 함께 확인할 수 있습니다.
접근성 있는 색상 팔레트 비교. 왼쪽은 일반적인 밝은 색상 팔레트(접근성 부족), 오른쪽은 짙은 톤의 팔레트(접근성 우수). 각 색상의 명도 값과 WCAG 대비 비율을 함께 표시. 색각이상 시뮬레이션도 함께 확인할 수 있습니다.
제작: Nanobanana

현업에서 자주 마주하는 상황: “이건 우리 브랜드 색이에요”

디자이너와 함께 일하다 보면 이런 순간이 찾아옵니다.

“로고에서 따온 색상이라 바꾸기 어려워요. 이미 브랜드 가이드에 들어가 있고, 마케팅팀이랑도 맞춰둔 거라서요.”

색상 대비 문제를 제기했더니 돌아온 답변입니다. 이 상황, 낯설지 않으시죠?

브랜드 색상은 오랜 시간을 거쳐 결정된 결과물인 경우가 많습니다. 디자이너 입장에서는 “접근성 때문에 바꾸자"는 말이 단순한 수정 요청이 아니라 브랜드 아이덴티티를 건드리는 것처럼 느껴질 수 있거든요.

구체적인 상황을 가정해봅시다

버튼 텍스트 색상이 브랜드 파랑 #4B9DFF이고, 배경은 흰색(#FFFFFF)입니다.

배경:  #FFFFFF (흰색)
텍스트: #4B9DFF (밝은 파랑)
대비:  약 3.0:1 → AA 실패 (일반 텍스트 기준 4.5:1 필요)

이 상황에서 “이 색은 안 됩니다"라고 말하면 대화가 막히는 경우가 많습니다. 대신 다음 단계로 접근하는 게 효과적입니다.

1단계: 데이터로 함께 확인하기

WebAIM Contrast Checker나 Chrome DevTools를 열고, 디자이너와 함께 실제 수치를 확인합니다. “규정 때문에 드리는 말씀이 아니라, 이 색상이 저시력 사용자에게 어떻게 보이는지 같이 확인해볼 수 있을까요?“라는 접근이 훨씬 잘 통합니다.

2단계: 대안 제시하기

“이 색상은 안 됩니다"보다 “이렇게 하면 브랜드 색감은 살리면서 기준도 맞출 수 있어요"가 협업의 문을 열어줍니다.

css
/* 기존 브랜드 색 - AA 실패 */
.btn {
  color: #4B9DFF;  /* 대비: 약 3.0:1 ❌ */
  background: #FFFFFF;
}

/* 방법 1: 같은 계열, 더 어둡게 */
.btn {
  color: #0062D1;  /* 대비: 약 5.1:1 ✅ */
  background: #FFFFFF;
}

/* 방법 2: 배경과 텍스트 반전 */
.btn {
  color: #FFFFFF;
  background: #0062D1;  /* 대비: 약 5.1:1 ✅ */
}

같은 파랑 계열에서 채도와 명도를 조정하면 브랜드 정체성을 크게 훼손하지 않으면서도 WCAG AA를 충족시킬 수 있는 경우가 많습니다.

3단계: 합의 내용 문서화하기

합의된 내용은 디자인 시스템이나 브랜드 가이드에 “접근성을 고려한 색상 사용 규칙"으로 함께 기록해 두는 게 좋습니다. 그래야 다음에 같은 대화를 반복하지 않아도 되거든요.

설득이 어려운 경우도 있습니다

가끔은 현실적인 제약이 앞을 막습니다. “브랜드 가이드라인 변경은 경영진 승인이 필요하다”, “이미 인쇄물까지 나간 색상이다” 같은 상황이죠.

이럴 때 현업에서 자주 사용하는 방법들이 있습니다. 브랜드 색상을 그대로 유지하면서도 가독성을 높이는 기법들입니다.

방법 1: text-shadow로 아웃라인 효과 (이미지·그라디언트 배경에 효과적)

밝은 색상 텍스트가 복잡한 배경 위에 올라갈 때 가장 자주 쓰는 방법입니다. 지도 레이블, 히어로 섹션 타이틀에서 흔히 볼 수 있는 패턴이에요.

css
.hero-title {
  color: #4B9DFF; /* 브랜드 파랑 */
  /* 어두운 아웃라인으로 배경과의 대비를 확보 */
  text-shadow:
    -1px -1px 0 rgba(0, 30, 80, 0.85),
     1px -1px 0 rgba(0, 30, 80, 0.85),
    -1px  1px 0 rgba(0, 30, 80, 0.85),
     1px  1px 0 rgba(0, 30, 80, 0.85);
}

주의할 점이 있습니다. text-shadow는 WCAG 명도 대비 계산에 포함되지 않습니다. 자동화 도구는 여전히 “실패"로 표시할 수 있지만, 실제 사용자 가독성은 크게 향상됩니다. 도구 점수보다 실제 사용성을 함께 평가하는 게 좋습니다.

방법 2: text-stroke + paint-order (헤딩·디스플레이 텍스트에 효과적)

Modern CSS로 텍스트에 스트로크를 추가하는 방법입니다. paint-order를 함께 쓰면 스트로크가 텍스트 안쪽을 침범하지 않아요.

css
.display-heading {
  color: #4B9DFF; /* 브랜드 파랑 */
  -webkit-text-stroke: 3px #003380; /* 어두운 파랑 스트로크 */
  paint-order: stroke fill; /* 스트로크를 fill 아래에 렌더링 */
}

폰트 크기가 클수록 효과적입니다. 본문 텍스트에는 어색해 보일 수 있으니 큰 제목이나 강조 텍스트에 사용하세요.

방법 3: 텍스트 배경 칩 (UI 컴포넌트·배지에 효과적)

브랜드 색상을 장식 요소(테두리, 아이콘)에 유지하고, 텍스트에는 별도의 배경을 깔아 대비를 확보하는 방법입니다. 가장 안정적이고 WCAG 기준도 충족합니다.

css
.tag {
  /* 브랜드 색상은 테두리로 유지 */
  border: 2px solid #4B9DFF;
  border-radius: 4px;

  /* 텍스트는 충분한 대비 확보 */
  color: #003380;        /* 어두운 파랑: 흰 배경 대비 약 10:1 ✅ */
  background: #FFFFFF;
  padding: 4px 10px;
}

/* 또는 브랜드 색 배경 위에 흰 텍스트 */
.tag-filled {
  background: #4B9DFF;
  color: #FFFFFF;
  /* 대비: 약 3.0:1 → 큰 텍스트 기준은 충족, 본문 기준은 아직 부족 */
  /* 폰트를 14pt bold 이상으로 설정하면 AA 기준 통과 */
  font-size: 1rem;
  font-weight: 700;
}

방법 4: 반투명 스크림 (이미지 위 텍스트에 효과적)

사진이나 그라디언트 배경 위에 텍스트가 올라가는 경우, 배경과 텍스트 사이에 반투명 레이어를 추가합니다.

css
.card-text-area {
  /* 텍스트 영역에 반투명 어두운 배경 */
  background: linear-gradient(
    to top,
    rgba(0, 0, 0, 0.75) 0%,
    rgba(0, 0, 0, 0) 100%
  );
  padding: 24px 16px 16px;
}

.card-title {
  color: #FFFFFF; /* 흰 텍스트 */
  /* 스크림 덕분에 충분한 대비 확보 */
}

어떤 방법을 쓰든, 한 가지를 기억해 두면 좋습니다. 자동화 도구가 “실패"로 표시해도, 실제 사용자가 충분히 읽을 수 있다면 그것도 개선입니다. 접근성은 “완벽하거나 아무것도 아니거나"가 아닙니다. 할 수 있는 데서부터 시작하면 됩니다.


실무: 다크 모드와 색상

다크 모드의 색상 대비 이슈

css
/* ❌ 라이트 모드 색상을 다크 모드에 그대로 사용 */
@media (prefers-color-scheme: dark) {
  body {
    background: #1a1a1a;
    color: #0066cc; /* 라이트 모드의 파랑 */
    /* 대비: 약 3.1:1 - 일반 텍스트에는 부족 */
  }
}

/* ✅ 다크 모드에 맞게 조정 */
@media (prefers-color-scheme: dark) {
  body {
    background: #1a1a1a;
    color: #66b2ff; /* 밝은 파랑 */
    /* 대비: 약 7.8:1 - 충분함 */
  }
}

테마 변수로 관리

css
:root {
  /* 라이트 모드 */
  --text-primary: #111827;
  --text-secondary: #6b7280;
  --background-primary: #ffffff;
  --background-secondary: #f3f4f6;

  --accent-primary: #0962db;
  --accent-danger: #7f1d1d;
}

@media (prefers-color-scheme: dark) {
  :root {
    /* 다크 모드 - 명도를 고려해 조정 */
    --text-primary: #f3f4f6;
    --text-secondary: #d1d5db;
    --background-primary: #1a1a1a;
    --background-secondary: #374151;

    --accent-primary: #66b2ff;
    --accent-danger: #fca5a5;
  }
}

body {
  color: var(--text-primary);
  background: var(--background-primary);
}

.button-primary {
  color: white;
  background: var(--accent-primary);
}

검사와 테스트

수동 검사 체크리스트

markdown
## 색상 접근성 체크리스트

### 명도 대비
- [ ] 텍스트와 배경 대비가 4.5:1 이상?
- [ ] 큰 텍스트(18pt+)도 3:1 이상?
- [ ] 버튼과 배경 대비도 확인했나?

### 색상 의존성
- [ ] 색상만으로 정보를 표현하지 않았나?
- [ ] 형태/패턴/텍스트로도 구분되나?
- [ ] 에러 메시지가 색상+텍스트로 표현되나?

### 색각이상 테스트
- [ ] 페이지를 색맹 시뮬레이터로 확인했나?
- [ ] 빨강-초록 구분이 중요한가?
- [ ] 다른 색상 조합도 테스트했나?

### 다크 모드
- [ ] 다크 모드에서도 대비가 충분한가?
- [ ] 색상이 자동으로 조정되나?

자동화 도구

javascript
// axe DevTools 검사 (브라우저 확장)
// https://www.deque.com/axe/devtools/

// 또는 프로그래매틱하게
async function checkContrast() {
  const { axe } = window;
  const results = await axe.run({
    rules: ['color-contrast']
  });

  results.violations.forEach(violation => {
    console.log('색상 대비 문제:', violation);
  });
}

권장 도구

색상 접근성 검사 도구들은 다양합니다. WebAIM Contrast Checker, 색맹 시뮬레이터, axe DevTools, Lighthouse, WCAG 가이드라인의 예시.
색상 접근성 검사 도구들은 다양합니다. WebAIM Contrast Checker, 색맹 시뮬레이터, axe DevTools, Lighthouse, WCAG 가이드라인의 예시.
제작: Nanobanana
  1. WebAIM Contrast Checker: 두 색상의 대비 검사
  2. Color Blindness Simulator: 색맹 시뮬레이션
  3. axe DevTools: 자동화된 접근성 검사
  4. Lighthouse: Chrome DevTools 내장 검사

자세한 사용방법은 추후 포스트에서 다뤄보겠습니다.


자주 실수하는 패턴

실수 1: 회색 텍스트로 부가 정보 표현

css
/* ❌ 회색 텍스트는 저시력 사용자가 못 읽을 수 있음 */
.secondary-text {
  color: #999999; /* 회색 */
  background: #ffffff;
  /* 대비: 약 2.85:1 - AA 미달 */
}

/* ✅ 어두운 회색 사용 */
.secondary-text {
  color: #666666; /* 어두운 회색 */
  background: #ffffff;
  /* 대비: 약 5.74:1 - AA 만족 */
}

실수 2: 포커스 스타일을 너무 옅게 설정

css
/* ❌ 포커스 아웃라인이 옅음 */
button:focus {
  outline: 1px solid #cccccc;
  outline-offset: 1px;
}

/* ✅ 명확한 포커스 스타일 */
button:focus-visible {
  outline: 3px solid #0962db;
  outline-offset: 2px;
}

실수 3: 라이트/다크 모드 색상을 동일하게 사용

css
/* ❌ 둘 다 파랑 #0066cc 사용 */
.primary-color {
  color: #0066cc;
}

/* ✅ 모드별로 조정 */
:root {
  --primary: #0066cc;
}

@media (prefers-color-scheme: dark) {
  :root {
    --primary: #66b2ff;
  }
}

.primary-color {
  color: var(--primary);
}

정리하며

색상 접근성은 모든 설계자와 개발자가 알아야 할 기본 원칙입니다. 그리고 디자이너와의 협업에서 알 수 있듯, 이건 혼자서 해결하는 문제가 아니라 팀이 함께 만들어가는 품질이기도 하죠.

체크리스트로 정리하면 이렇습니다.

  • ✅ 명도 대비 검사 (4.5:1 이상)
  • ✅ 색상만으로 정보 표현 금지
  • ✅ 색맹 시뮬레이터로 테스트
  • ✅ 다크 모드에서도 대비 확인
  • ✅ 포커스 스타일이 명확
  • ✅ 자동화 도구로 검사

이 항목들을 하나씩 챙기다 보면, 단지 색각이상 사용자만이 아니라 저시력 사용자, 노인, 그리고 평범한 사용자까지 모두가 더 쉽게 쓸 수 있는 웹이 됩니다. 작은 색상 하나가 누군가의 경험을 바꿀 수 있다는 것, 기억해 두셨으면 좋겠습니다.


참고 자료