색상 대비 검사기
전경색과 배경색 간의 WCAG AA 및 AAA 대비 비율 검사
전경(텍스트) 색상
배경 색상
큰 텍스트 샘플 (18px 굵게)
일반 텍스트 샘플 — 선택하신 배경 색상에서 본문 텍스트가 이렇게 보입니다.
대비 비율
14.63:1
WCAG 준수 여부
통과
일반 AA
통과
일반 AAA
통과
큰 텍스트 AA
통과
큰 텍스트 AAA
WCAG AA — 일반 텍스트: 4.5:1, 큰 텍스트 (18px 이상 또는 14px 굵게): 3:1
WCAG AAA — 일반 텍스트: 7:1, 큰 텍스트 (18px 이상 또는 14px 굵게): 4.5:1
색상 대비 검사란?
색상 대비 검사는 전경색(일반적으로 텍스트)과 배경색 사이의 휘도 차이를 측정하여 그 결과를 비율로 나타냅니다. 1:1 비율은 두 색상이 동일함을 의미하며, 21:1은 흰색 바탕의 검정 또는 그 반대를 나타내는 최댓값입니다. W3C가 발행한 웹 콘텐츠 접근성 지침(WCAG)은 저시력자나 색각 이상이 있는 사람들이 텍스트를 읽을 수 있도록 텍스트가 충족해야 하는 최소 대비 비율을 정의합니다.
대비 비율 공식은 WCAG 2.x에서 도출되었으며 상대 휘도에 의존합니다. 상대 휘도는 색상이 사람 눈에 얼마나 밝게 보이는지를 측정하는 지표입니다. 상대 휘도는 각 sRGB 채널을 선형화(감마 제거)하고 ITU-R BT.709 계수로 채널에 가중치를 적용하여 계산합니다. 계수는 빨간색 0.2126, 녹색 0.7152, 파란색 0.0722입니다. 사람의 눈이 녹색 빛에 가장 민감하기 때문에 녹색이 가장 큰 기여를 합니다. 비율은 (L1 + 0.05) / (L2 + 0.05)이며, 여기서 L1은 더 밝은 색상의 휘도입니다.
WCAG는 두 가지 적합성 수준을 정의합니다. Level AA는 일반 크기 텍스트에 최소 4.5:1, 큰 텍스트(18px 이상 또는 14px 굵게)에 3:1의 대비 비율을 요구합니다. Level AAA는 기준을 각각 7:1과 4.5:1로 높입니다. WCAG 2.1은 또한 테두리, 아이콘, 포커스 표시기와 같은 비텍스트 UI 구성 요소에 3:1 비율을 요구하는 성공 기준 1.4.11을 도입했습니다.
이 대비 검사기를 사용하는 이유
눈으로 대비를 확인하는 것은 신뢰할 수 없습니다. 보정된 디스플레이에서 뚜렷해 보이는 색상이 저품질 노트북 화면, 직사광선 아래, 또는 적녹 색각 이상자에게는 구분하기 어려울 수 있습니다. 수치 비율은 추측을 제거하고 WCAG 표준에 대한 통과/실패 판정을 제공합니다.
대비 검사기 활용 사례
WCAG 대비 비율 요구 사항
아래 표는 WCAG 2.1에서 다양한 콘텐츠 유형과 적합성 수준에 대해 요구하는 최소 대비 비율을 요약합니다. 큰 텍스트는 일반 굵기에서 18px(24 CSS 픽셀) 이상, 또는 굵은 굵기에서 14px(18.66 CSS 픽셀) 이상으로 정의됩니다.
| 수준 | 최소 비율 | 적용 대상 | 참고 |
|---|---|---|---|
| AA Normal text | 4.5 : 1 | Body text, paragraphs, labels | Minimum for most UI text |
| AA Large text | 3.0 : 1 | Text >= 18px, or >= 14px bold | Minimum for headings |
| AA UI components | 3.0 : 1 | Borders, icons, focus indicators | Non-text contrast (WCAG 1.4.11) |
| AAA Normal text | 7.0 : 1 | Body text at highest standard | Enhanced readability |
| AAA Large text | 4.5 : 1 | Large text at highest standard | Enhanced for headings |
휘도, AA, AAA 설명
대비 비율 계산은 세 단계로 이루어집니다: 각 색상의 휘도 계산, 비율 도출, WCAG 기준과 비교.
코드 예제
WCAG 대비 비율을 프로그래밍 방식으로 계산하세요. 각 예제는 WCAG 2.x의 상대 휘도 공식과 대비 비율 계산을 구현합니다. 비교를 위해 동일한 흰색 바탕의 검정과 흰색 바탕의 인디고 조합을 테스트합니다.
// Calculate relative luminance per WCAG 2.x (sRGB)
function luminance(r, g, b) {
const [rs, gs, bs] = [r, g, b].map(c => {
c /= 255
return c <= 0.04045 ? c / 12.92 : Math.pow((c + 0.055) / 1.055, 2.4)
})
return 0.2126 * rs + 0.7152 * gs + 0.0722 * bs
}
// Contrast ratio between two RGB colors
function contrastRatio(fg, bg) {
const l1 = luminance(...fg)
const l2 = luminance(...bg)
const lighter = Math.max(l1, l2)
const darker = Math.min(l1, l2)
return (lighter + 0.05) / (darker + 0.05)
}
contrastRatio([0, 0, 0], [255, 255, 255]) // -> 21.0
contrastRatio([99, 102, 241], [255, 255, 255]) // -> 3.95
contrastRatio([29, 78, 216], [255, 255, 255]) // -> 6.06 (AA pass)def luminance(r: int, g: int, b: int) -> float:
"""Relative luminance per WCAG 2.x, ITU-R BT.709 coefficients."""
channels = []
for c in (r, g, b):
c /= 255
channels.append(c / 12.92 if c <= 0.04045 else ((c + 0.055) / 1.055) ** 2.4)
return 0.2126 * channels[0] + 0.7152 * channels[1] + 0.0722 * channels[2]
def contrast_ratio(fg: tuple, bg: tuple) -> float:
l1 = luminance(*fg)
l2 = luminance(*bg)
lighter, darker = max(l1, l2), min(l1, l2)
return (lighter + 0.05) / (darker + 0.05)
print(f"{contrast_ratio((0, 0, 0), (255, 255, 255)):.2f}") # -> 21.00
print(f"{contrast_ratio((99, 102, 241), (255, 255, 255)):.2f}") # -> 3.95
# Check WCAG AA for normal text
ratio = contrast_ratio((29, 78, 216), (255, 255, 255))
print(f"{ratio:.2f} — {'AA pass' if ratio >= 4.5 else 'AA fail'}") # -> 6.06 — AA passpackage main
import (
"fmt"
"math"
)
func linearize(c float64) float64 {
c /= 255
if c <= 0.04045 {
return c / 12.92
}
return math.Pow((c+0.055)/1.055, 2.4)
}
func luminance(r, g, b int) float64 {
return 0.2126*linearize(float64(r)) +
0.7152*linearize(float64(g)) +
0.0722*linearize(float64(b))
}
func contrastRatio(fgR, fgG, fgB, bgR, bgG, bgB int) float64 {
l1 := luminance(fgR, fgG, fgB)
l2 := luminance(bgR, bgG, bgB)
lighter := math.Max(l1, l2)
darker := math.Min(l1, l2)
return (lighter + 0.05) / (darker + 0.05)
}
func main() {
ratio := contrastRatio(0, 0, 0, 255, 255, 255)
fmt.Printf("%.2f\n", ratio) // -> 21.00
ratio = contrastRatio(29, 78, 216, 255, 255, 255)
fmt.Printf("%.2f\n", ratio) // -> 6.06 (AA pass for normal text)
}/* WCAG-safe color pairs — tested contrast ratios */
/* 12.63:1 — passes AAA normal text */
.high-contrast {
color: #1e293b; /* slate-800 */
background: #f8fafc; /* slate-50 */
}
/* 7.07:1 — passes AAA normal text */
.dark-theme-text {
color: #e2e8f0; /* slate-200 */
background: #0f172a; /* slate-900 */
}
/* 4.57:1 — passes AA normal, fails AAA */
.accent-on-white {
color: #1d4ed8; /* blue-700 */
background: #ffffff;
}
/* 2.14:1 — fails AA for text, but passes 3:1 for large text */
.muted-heading {
color: #94a3b8; /* slate-400 */
background: #ffffff;
}