재사용 가능한 UI 구성요소 (예: 배지 또는 버튼)를 빌드하거나 동적 테마를 지원할 때 예측할 수 없는 배경 색상에서 텍스트를 읽을 수 있도록 하는 것은 어려울 수 있습니다. contrast-color() CSS 함수는 black 또는 white로 변환되어 지정된 배경 색상과의 대비율을 극대화합니다.
최적의 텍스트 색상 결정
요소에 변수 또는 동적으로 삽입된 배경 색상이 있는 경우 브라우저를 사용하여 가장 안전한 텍스트 색상을 계산할 수 있습니다.
필수: contrast-color() 함수를 사용하여 color 속성을 설정하고 배경 색상 (또는 맞춤 속성)을 인수로 전달합니다.
.badge {
/* Define the background color as a custom property */
--badge-bg: #e02424;
background-color: var(--badge-bg);
/* MANDATORY: Dynamic contrasting color */
color: contrast-color(var(--badge-bg));
}
금지사항: background-color에 contrast-color()를 사용하지 마세요. 이 함수는 전경색 (예: 텍스트 또는 테두리)이 지정된 배경색과 대비되도록 특별히 설계되었습니다.
테마 설정과 통합
contrast-color()는 CSS 맞춤 속성을 포함한 모든 유효한 <color> 값을 허용하므로 디자인 시스템 내에서 특히 강력합니다. 배경 테마를 변수로 중앙 집중화하면 테마가 변경될 때마다(사용자가 지정한 색상으로 변경되는 경우에도) 전경 콘텐츠의 대비가 자동으로 업데이트됩니다.
권장사항: 애플리케이션 전체에서 동기화를 보장하려면 contrast-color() 내에서 공유 백그라운드 변수를 참조하세요.
.theme-card {
background-color: var(--theme-surface-color);
color: contrast-color(var(--theme-surface-color));
}
.theme-container[data-theme="light"] {
--theme-surface-color: #f4f4f4;
}
.theme-container[data-theme="dark"] {
--theme-surface-color: #1a1a1a;
}
.theme-container[data-theme="custom"] {
--theme-surface-color: var(--user-custom-theme);
}
하지 말아야 할 일: 배경이 동적인 경우 각 상호작용 상태에 대해 별도의 텍스트 색상을 하드 코딩하지 마세요. 이렇게 하면 contrast-color()가 방지하려는 불일치가 다시 발생합니다.
대체 전략
contrast-color()가 없는 브라우저를 지원하려면 대체 기능을 제공해야 합니다. 배경 색상이 알려져 있고 고정되어 있는 경우 하드 코딩된 대비 색상을 사용합니다. 색상을 알 수 없는 동적 배경의 경우 UI 요구사항과 브라우저 지원 타겟에 따라 아래 표에서 전략을 선택하세요.
| 전략 | 권장 분야 | 고려사항 |
|---|---|---|
| 상대 색상 문법 | 자동화된 고품질 CSS 전용 대비 계산 | 최고 품질의 CSS 전용 대체입니다. |
| 텍스트 그림자 | 어떤 배경에서도 가독성을 빠르게 높일 수 있습니다. | '지저분'하거나 '빛나는' 것처럼 보일 수 있으며 모든 디자인에 적합하지 않을 수 있습니다. |
| 텍스트 획 | 글꼴 두께를 유지하면서 가장자리 대비를 보장합니다. | 가능한 경우 paint-order을 사용하여 글꼴이 얇아지지 않도록 합니다. |
| 반투명 오버레이 | 텍스트 뒤에 최소 대비 영역을 확보합니다. | 텍스트 아래의 배경 색상 모양을 변경합니다. 별도의 텍스트 요소가 필요합니다. |
| SVG 필터 | 배경이 변경되면 업데이트되는 반응형 대비입니다. | 별도의 텍스트 요소가 필요하며 구현이 까다롭습니다. |
권장사항: 상대 색상 문법 (RCS)
RCS는 이를 지원하는 브라우저에서 동적 색상에 가장 강력한 자동 대체 기능을 제공합니다.
@supports (color: oklch(from red l c h)) {
.badge {
/* Highest threshold that passes WCAG. Higher values may be more legible. */
--threshold: 0.623;
--l: max(0, sign(var(--threshold) - l));
color: oklch(from var(--badge-bg) var(--l) 0 h);
}
}
@supports (color: contrast-color(red)) {
.badge {
color: contrast-color(var(--badge-bg));
}
}
대체 CSS 대체
권장사항: RCS가 충분하지 않거나 지원되지 않는 경우 디자인 제약 조건에 가장 적합한 대체 옵션을 선택합니다.
옵션 1: 텍스트 획
.badge--stroke {
-webkit-text-stroke: 4px black;
}
옵션 2: paint-order를 사용한 텍스트 획으로 글자 모양 유지
.badge--stroke {
-webkit-text-stroke: 4px black;
paint-order: stroke fill;
}
옵션 3: 반투명 배경 오버레이
.badge--overlay {
position: relative;
color: #fff;
overflow: hidden;
}
.badge--overlay::before {
content: "";
position: absolute;
inset: 0;
background-color: rgb(0 0 0 / 0.4);
z-index: 0;
}
.badge--overlay span {
position: relative;
z-index: 1;
}
옵션 4: 텍스트 그림자
.badge--shadow {
text-shadow: 0 1px 3px rgb(0 0 0 / 0.8);
}
옵션 5: SVG 필터
이 SVG 필터 정의를 로드합니다. 필터의 외부 및 데이터 URL 참조를 방지하는 이 WebKit 버그의 영향을 받는 Safari 버전을 지원해야 하는 경우 HTML 문서에 직접 추가해야 합니다. 그렇지 않으면 외부 파일(SVG 파일 또는 CSS에 인라인된 파일)에서 로드해야 합니다.
<!-- Technique from https://miunau.com/posts/dynamic-text-contrast-in-css/ --> <svg xmlns="http://www.w3.org/2000/svg" version="1.1" height="0" style="display: none;"> <defs> <filter id="contrast-filter" color-interpolation-filters="sRGB"> <feColorMatrix type="matrix" values="0.2126 0.7152 0.0722 0 0 0.2126 0.7152 0.0722 0 0 0.2126 0.7152 0.0722 0 0 0 0 0 1 0"/> <feMorphology operator="dilate" radius="2"/> <feComponentTransfer> <feFuncR type="linear" slope="-255" intercept="128"/> <feFuncG type="linear" slope="-255" intercept="128"/> <feFuncB type="linear" slope="-255" intercept="128"/> </feComponentTransfer> <feComposite operator="in" in2="SourceGraphic"/> </filter> </defs> </svg>텍스트를
<span>로 래핑합니다.<div class="badge--svg"> <span>Badge content</span> </div><span>에 필터를 적용합니다.
.badge--svg span{
color: var(--badge-bg);
filter: url(#contrast-filter);
}