CSS에서 브라우저가 대조되는 색상을 자동으로 선택하는 방법
(webkit.org)- contrast-color() 함수를 사용하면 버튼 등 다양한 배경색에 맞춰 브라우저가 검정 또는 흰색 글꼴색을 자동으로 선택함
- 대규모 프로젝트에서도 텍스트 가독성 유지가 쉬워지고 유지보수 효율성을 높임
- 현재 Safari Technology Preview에서는 WCAG 2 공식 알고리듬을 사용하는데, 이는 실제 인간 인지와 어긋날 수 있음
- 차세대 APCA 알고리듬 도입 논의가 WCAG 3 개발 과정에서 진행 중이며, 더 나은 명도 대비 평가를 약속함
- 단순한 흑백 대비 이외에도 향후 다양한 색상 옵션과 접근성 개선 기능이 추가될 전망임
개요 및 contrast-color() 도입 배경
- 여러 버튼이나 인터페이스 구성 요소에 다양한 배경색이 사용되는 디자인에서는 글꼴색(텍스트 색상) 의 가독성이 중요함
- 이전까지는 개발자가 직접 배경색과 텍스트 색상을 일일이 잘 조합해야 했으나, 대규모 프로젝트에서는 관리의 복잡성과 오류 발생 위험이 높음
- contrast-color() CSS 함수를 사용하면 개발자는 배경색만 지정하면 되고, 브라우저가 자동으로 검정 또는 흰색 글꼴색 중 높은 대비를 선택함
- 이 방식으로 유지보수와 디자인 작업의 효율성이 크게 향상됨
- 간단히
color: contrast-color(색상);
처럼 선언하여 사용할 수 있음
contrast-color() 사용법 예시
- 버튼의 배경색 변수에 원하는 색상을 지정하고, 텍스트 색상은
contrast-color()
를 이용하여 자동으로 대조되는 흑백 중 하나가 선택됨 - 한 번에 한 색상만 관리하면 되므로, 디자인 정책 변경이나 다크/라이트 모드 지원 시 maintenance가 쉬움
button {
background-color: var(--button-color);
color: contrast-color(var(--button-color));
}
- Relative Color Syntax를 활용하면 hover 상태에 대해서도 배경색과 텍스트 색상을 일관성 있게 관리할 수 있음
접근성 고려사항 및 알고리듬 설명
-
contrast-color()
사용이 모든 접근성(Accessibility) 문제를 자동으로 해결하지는 않음 - 특정 중간 밝기의 배경색에서는 검정, 흰색 모두 필요 기준에 못 미치는 경우가 발생함
- 현재 Safari Technology Preview에서 사용되는 WCAG 2 알고리듬은 공식 웹 접근성 표준임
- 이 알고리듬은 대비 비율 기반으로 선택하지만 실제 눈에 보이는 명도 대비와 불일치하는 결과가 나오기도 함
- 예시로, #317CFF파란색 배경에는 기계적으로 검정색이 더 높은 대비로 계산되지만, 실제로는 흰색이 더 잘 읽힘
- 이에 대한 비판과 개선 요구에 따라, 차세대 접근성 표준(WCAG 3) 에서 더 뛰어난 APCA(Accessible Perceptual Contrast Algorithm) 도입 논의가 진행 중임
- APCA는 인간의 인지 특성을 반영하여 색상 대비를 계산하므로 실제 가독성을 더 잘 보장함
실제 환경에서 충분한 대비 제공하기
- CSS의
@media (prefers-contrast: more)
미디어 쿼리를 활용하면, 사용자 접근성 선호도에 따라 추가적인 고대비 스타일을 적용할 수 있음
@media (prefers-contrast: more) {
/* 더 높은 대조 스타일 정의 */
}
- 예를 들어, 브랜드 핵심색이 #2DAD4E와 같은 밝은 녹색인 경우,
contrast-color()
가 미래에는 흰색을 선택하더라도 작은 글씨에는 여전히 대비가 충분하지 않을 수 있음 - APCA 알고리듬을 적용하면 폰트 크기, 굵기에 따라 필요한 최소 대비 기준을 상세히 참고할 수 있어 실무 디자인 결정에 도움을 줌
- 실제로 24px/400굵기 글자에는 흰색 적용이 적합하지만, 더욱 얇은 FONT나 작은 글씨에는 더 진한 배경색 사용이 권장됨
- 디자인팀은 light/dark mode, prefers-contrast 선호도 등을 고려해 각 조건에 맞는 색상 팔레트를 쉽게 변수로 관리할 수 있음
--button-color: #2DAD4E;
@media (prefers-contrast: more) {
@media (prefers-color-scheme: light) {
--button-color: #419543;
}
@media (prefers-color-scheme: dark) {
--button-color: #77CA8B;
}
}
button {
background-color: var(--button-color);
color: contrast-color(var(--button-color));
font-size: 1.5rem;
font-weight: 500;
}
- 핵심적으로는
contrast-color()
덕분에 배경색을 중심으로 색상만 관리하면 되고, 텍스트 색상 대비 쌍은 브라우저가 자동 생성함
검정색과 흰색을 넘어
- 현재의
contrast-color()
는 흑백 2가지 중에서만 선택하지만, 초기 버전은 여러 색상 중 선택도 가능했음 - CSS Working Group은 향후 알고리듬 변화와 호환성을 위해 단순 버전(흑백만 선택)을 우선 제공하고, 미래에는 사용자 지정 색상 옵션 및 원하는 최소 대비 기준을 지정하는 등 확장도 계획함
- 단순한 필요에는 이미 충분히 유용함
- 본 함수는 배경색뿐만 아니라 테두리, 기타 시각 요소에도 다양하게 활용 가능함
결론 및 참고 정보
- 차세대 접근성 표준 반영 후,
contrast-color()
는 알고리듬을 교체해 더 좋은 대비 자동 선택을 지원할 것임 - 그때까지는 핵심 배경색이 명확히 밝거나 어두운 경우에 특히 유용함
- 텍스트가 아닌 다양한 UI 요소에도 폭넓게 적용 가능함
- APCA(Accessible Perceptual Contrast Algorithm) 같은 최신 접근성 알고리듬에도 계속 관심을 가지는 것이 바람직함
참고 자료
- APCA 공식 문서 및 APCA Contrast Calculator에서 다양한 예시와 평가 기준 확인 가능
- CSSWG의 contrast-color 함수 관련 표준화 논의 진행 중
- WebKit이나 관련 커뮤니티에서 의견 공유 및 피드백 참여 가능
Hacker News 의견
-
이 문제를 해결하기 위해 색상 쌍이 설계 단계에서 간단하고 예측 가능한 WCAG/ACPA 명암비를 갖도록 팔레트를 만드는 도구를 개발 중인 중임. https://www.inclusivecolors.com/ 사이트에서 데스크톱에서 더 많은 기능을 제공함. 한 방법으로 100(연한 색)에서 900(어두운 색)까지 등급별 색상 견본을 만들고, 예를 들어 700 등급의 색은 100 등급과, 800 등급은 200 등급과 명확한 대비가 나타나도록 밝기를 조정하는 설계임. 이렇게 하면 red-700 vs gray-100, green-800 vs yellow-200 같은 조합이 명도 체크 없이도 확실히 대비를 제공함을 알 수 있음. Contrast 메뉴에서는 WCAG와 비교해 APCA 알고리즘이 얼마나 엄격한지, 특히 밝은 바탕의 어두운 글자에서 얼마나 까다로운지도 탐색 가능함. 어두운 테마에는 WCAG를 사용하면 안되는 이유임. Examples 메뉴에서는 Tailwind 및 IBM Carbon 팔레트 사례를 보면, 각 등급이 채도와 색상(Hue)이 비선형적으로 변해서 단순히 흑백 중 최적 대비를 고르는 건 쉽지만, 브랜딩이 중요한 팔레트는 단순 밝기 조정만으로 색을 뽑을 수 없는 더 복잡한 문제임.
-
lch를 사용해서 비슷한 것을 할 수 있는 방법이 있음
--text: lch(from var(--bg) calc((49.44 - l) * infinity) 0 0);
소스: https://til.jakelazaroff.com/css/…
-
LCH도 멋지지만 OKLCH가 더 뛰어남. https://evilmartians.com/chronicles/oklch-in-css-why-quit-rgb-hsl/… 이 글이 내 생각을 완전히 바꾼 계기가 되었음. 정말 대단한 도구임. 놀랍게도 내 디자이너 친구들 중 OKLCH를 아는 사람이 없었음. 이 방식이 많은 문제를 해결함.
-
이렇게 콜백으로 매개변수를 받을 수 있는 CSS 함수는 처음 봄. 정말 흥미로운 개념임. 혹시 lch 외에 이런 스타일의 함수가 또 있을지 궁금함.
-
Lea Verou가 이와 비슷한 우회 방법에 대해 쓴 좋은 글이 있음. https://lea.verou.me/blog/2024/contrast-color/
-
-
이 글은 명암 자동 선택 방식의 장단점에 대한 훌륭한 개요임. 단순한 사이트를 만드는 경우엔 이 방법이 쉽고 간단하게 올바른 대비를 제공함.
하지만 대규모로 WCAG 컴플라이언스가 필요한 경우엔 피하고, 진짜 의미기반 서식 토큰 계층이 필요함. 의미 토큰은 개발 속도를 높여주고, 단순히 검정/흰색 전환보다 시각적으로 더 보기 좋은 대비를 보장할 수 있음. 의미 토큰 계층의 좋은 점은 테마 제작이 매우 쉬워서 라이트/다크 테마가 추가 비용 거의 없이 가능함. 만약 브랜드 색상이 WCAG2에 걸릴 때, 별도의 WCAG2/APCA용 테마를 따로 만들어서 컴플라이언스를 얻으면서 더 나은 대비를 제공할 수 있음.
나는 Figma에서 variables/tokens 스트림을 맡고 있고, Figma와 Atlassian의 다크 모드 구현에도 참여했음. 토큰, 테마, 접근성 색상 관련 궁금한 게 있으면 언제든 질문 환영함.-
의미 토큰이란게 구체적으로 어떤 것인지 궁금함. 이런 기능이 있기에 내가 작업한 큰 프로젝트에서 색상 상대 계산과 대비 색상을 위해 CSS-in-JS를 썼음. 이런 기술이 곧 널리 적용될 걸 기대함.
-
마지막 2/3 내용은 너무 장황해서 현학적으로 보임. 회사 사이트/앱에선 결과 색상이 예측 불가해서 이런 함수에 의존하면 위험함. WebKit이 버그를 고치면서 색상 결과가 바뀔 수도 있기 때문임.
-
-
대비 색상이 브라우저 업체의 판단에 따라 정해지는 게 항상 맞지도, 예측 가능하지도 않다는 데에 아직 동의하기 어려움. 모든 브라우저가 동일하게 결과를 내는 확정적 기준이 마련될지 궁금함. 이 함수는 실제론 UX팀의 디자인 단계 지원용 도구로 느껴짐.
-
기사에 따르면 표준에서 계산 방법을 명확히 지정하고 있다고 언급함
-
'선택한다(choose)'는 표현이 애매하게 느껴짐. 실제로는 알고리즘이 색상을 산출함
-
링크에 나온 APCA 예시 공식에서 오류나 혼동되는 부분을 제외하면 100% 정확히 작동함. 완벽하게 일관성 있게 하려면, 두 후보 색상(더 밝은 것/어두운 것 모두)이 동시에 허용될 때 배경색의 밝기(L*)를 기준으로 예를 들어 L* 60 이상이면 밝은 쪽을 택하면 됨. 그러면 100% 일관성 확보함.
-
-
대규모 프로젝트에서 버튼이 안보이는 색상(예: 어두운데 검정 글자)으로 바뀔까봐 따로 챙기는 게 어렵다는 데, 실제로 출시 전에 일일이 버튼을 다 점검하면 어떨까 하는 생각임. 아니면 팀 전체에 어두운 버튼엔 검정 텍스트 절대 안된다고 사전 규정하고 공유하는 방법도 있음. 인지적 대비와 수학적 대비의 차이가 흥미로웠음. 워크플로우에 적용할 예정임.
-
실제로 버튼 전수 점검이 가능은 하지만, 이런 식으로 하면 사전 회귀 테스트 기간이 몇 주, 심하면 몇 달 걸릴 수도 있음. 대규모 프로젝트면 버튼이 수천 개가 넘기 쉽고, 특정 옵션 조합이나 워크플로우에서만 보이는 버튼도 많음
-
APCA를 참고하면 지각(인지) 기반 대비 계산이 가능함
-
-
시스템 색상이 인기 있었을 때 버튼 스타일을 만들었던 경험이 있음. 보기엔 근사했지만 명암비가 어떨지 알 수 없어서, 누군가 자바스크립트로 getComputedStyle로 계산해 준 게 있음. 명암이 불량하면 두 번째 색상 후보를 쓰거나, 불가피할 땐 text-shadow로 글자 주변에 명암 효과를 강화했음. 계산 방식은 잊었는데, 3개 RGB 값을 평균내서 비교하면 될 것 같기도 함. 파란색에선 평균값이 낮을 테니 흰색 글자 우선을 줄 수 있을 듯함.
-
최소한 light/dark 테마에서 active, focus, hover, link, visited 같은 가상 클래스별로 좋은 색상 추천을 알려주면 좋겠음. material UI는 여기에 disabled, before, after 상태까지 추가함.
-
예전에 배경색에 따라 검정/흰색 텍스트를 고르는 방식으로 영상 튜토리얼을 만든 적 있음. 내 방식은 단순해서 색을 회색조로 바꿔서 검정/흰색 중 뭘 쓸지 정함. 재미있는 작업이었음. 영상 만드는 실력은 부족함.
https://youtu.be/tUJvE4xfTgo?si=vFlegFA_7lzijfSR (포르투갈어 주의)- 흥미롭게도 다른 댓글에서 딱 그 작업을 하는 색상 공간 공식을 소개했음.
https://news.ycombinator.com/item?id=44015990
영상도 괜찮아 보임. 코드는 좋아보이는데 내가 포르투갈어를 몰라서 내용을 평가하진 못하겠음.
- 흥미롭게도 다른 댓글에서 딱 그 작업을 하는 색상 공간 공식을 소개했음.
-
전체 색 구성표를 직접 고르는데, 왜 대비 버튼 텍스트 색 고르는 게 처음부터 그냥 직접 고르는 것보다 쉽다는 건지 의문임. 이 기능은 배경색은 임의로 제각각 고르면서 정작 전경색(버튼 텍스트 색상)은 못 고르는 상태가 되는, 매우 극단적인 상황에서만 필요해질 듯함. 정말 문제인 상황은 이미지나 다양한 배경 위의 글자처럼 항상 잘 보여야 하는 건데, 이 기능은 전혀 못 다룸. 그래서 이런 제한적인 상황에서만 도움이 될 법한 기능인데, 이를 위해 새 동사까지 만들고, 기능은 흑백 선택이 끝인데다 가장 안좋은 명암 선택 알고리즘(WCAG 2)을 썼다는 점이 아쉬움. 진짜 대단함.
-
어떤 도구라도 쓸 상황을 안 겪어봤다고 바로 무시하는 시각은 아쉬움. 웹사이트 중엔 사용자가 임의로 색을 고르거나, 업로드 자료에서 색을 추출하는 경우가 많음. 접근성 챙기는 사이트는 이런 경우를 대비해서 자동 대비 계산을 꼭 하게 됨. 이런 내장 CSS 기능이 나오면, 기본적인 접근성도 쉽게 챙길 수 있음. 물론 더 발전된 경험 만들고 싶은 개발자에겐 아무 제약도 안 됨. contrast-color npm 패키지처럼 더 커스터마이즈가 가능하면 더 좋겠지만, 블로그에 보면 일단 흑백만 선택하는 알고리즘을 첫 단계로 시작했고 향후 개선 계획이 있음.
예시: https://coolors.co/8fbfe0-7c77b9-1d8a99-0bc9cd-14fff7 -
명암 선택 알고리즘이 별로라는 지적에 대해, WCAG 2 알고리즘을 따르고 있으며 앞으로 WCAG 3이 표준화되면 해당 알고리즘으로 쉽게 갈아탈 수 있음을 분명히 밝힘
-
-
SASS, Tailwind 등에 적용 가능한 이런 기능의 빌드 타임 대안이 있는지 궁금함. 이 기능이 전면 도입되려면 시간이 필요할 것 같은데, 플랫폼마다 구현이 동일하게 될지 걱정도 있음