색은 화면을 예쁘게 칠하는 물감이 아니라 의미를 나누고 시선을 이끄는 신호입니다. 신호등이 색만 보고도 통하듯, CSS에서 색을 기능으로 다루는 방법을 코드로 하나씩 살펴보겠습니다.

08.1 색을 이름으로 묶어 두기

색 값을 화면 곳곳에 직접 박아 두면 나중에 한 곳만 바꾸려 해도 전부 뒤져야 합니다. 쓰임에 따라 이름을 붙여 한자리에 모읍니다.


.btn {
background: #2563eb;
}
.link {
color: #2563eb;
}


같은 파랑이 여러 곳에 흩어져 있으면 브랜드 색을 바꿀 때 누락이 생깁니다. 변수로 묶으면 값 하나만 고쳐도 전체가 따라옵니다.


:root {
--color-accent: #2563eb;
}
.btn {
background: var(--color-accent);
}
.link {
color: var(--color-accent);
}


이제 강조색은 --color-accent 하나가 관장합니다. 색을 취향이 아니라 역할로 부르기 시작하는 첫걸음입니다.

08.2 의미를 색으로 나누기

성공과 경고와 오류는 사용자가 색만 보고도 구별하길 바라는 상태입니다. 상태마다 색을 정해 이름을 붙여 둡니다.


:root {
--color-success: #16a34a;
--color-warning: #d97706;
--color-danger: #dc2626;
}
.alert-danger {
color: var(--color-danger);
background: #fef2f2;
}


붉은 글자에 아주 옅은 붉은 배경을 깔면 경고가 화면에서 스스로 두드러집니다. 상태 색이 정해져 있으면 어느 화면에서든 같은 뜻으로 읽힙니다.

08.3 밝기 단계로 폭을 넓히기

색 하나라도 밝기를 여러 단계로 나눠 두면 색을 늘리지 않고도 표현이 풍부해집니다. 옅은 것은 배경, 진한 것은 강조로 씁니다.


:root {
--blue-50: #eff6ff;
--blue-100: #dbeafe;
--blue-500: #3b82f6;
--blue-700: #1d4ed8;
}
.tag {
background: var(--blue-50);
color: var(--blue-700);
}


옅은 배경 위에 진한 글씨를 얹으니 같은 파랑 가족 안에서 대비가 생깁니다. 한 색의 계단만으로도 배경과 강조를 모두 감당합니다.

08.4 강조색은 아껴야 강조된다

눈에 띄게 하려고 여러 요소를 강조색으로 칠하면 어느 것도 특별해 보이지 않습니다. 차분한 바탕 위에 강조색은 한두 곳에만 얹습니다.


.btn-secondary {
background: #f3f4f6;
color: #374151;
}
.btn-primary {
background: var(--color-accent);
color: #fff;
}


회색 버튼이 물러서 있으니 파란 버튼 하나가 자석처럼 시선을 끕니다. 강조는 넓은 무채색 위에서만 강조로 살아납니다.

08.5 배경과의 대비를 지키기

색이 아무리 예뻐도 배경과 밝기 차이가 부족하면 그 위의 글씨가 흐릿해집니다. 옅은 색 위에는 충분히 진한 글씨를 얹습니다.


.badge {
background: #fde68a;
color: #78350f;
}


노란 배경에 밝은 회색 글씨를 얹으면 눈이 피로하지만, 짙은 갈색 글씨를 얹으면 또렷이 읽힙니다. 색의 아름다움과 글씨의 읽힘은 따로 확인해야 합니다.

08.6 색만으로 정보를 전하지 않기

붉은색과 초록색을 비슷하게 느끼는 사용자가 적지 않아, 색 하나에만 뜻을 실으면 누군가는 아무것도 알 수 없습니다. 색에 다른 신호를 겹칩니다.


.status--ok::before {
content: "\2713 ";
color: var(--color-success);
}
.status--err::before {
content: "\2715 ";
color: var(--color-danger);
}


성공에는 체크, 오류에는 가위표 기호를 색과 함께 붙이면 색을 구별하기 어려운 사용자도 모양으로 상태를 읽습니다. 색은 훌륭한 조수이지만 혼자 두면 누군가를 소외시킵니다.

08.7 밝은 화면과 어두운 화면 함께 쓰기

색에 이름을 붙여 두면 환경에 따라 값만 바꿔 끼울 수 있습니다. 같은 이름이 밝을 때와 어두울 때 다른 값을 가리키게 합니다.


:root {
--bg: #ffffff;
--text: #111827;
}
@media (prefers-color-scheme: dark) {
:root {
--bg: #0b0f19;
--text: #e5e7eb;
}
}
body {
background: var(--bg);
color: var(--text);
}


사용자의 화면 설정이 어두우면 배경과 글씨가 통째로 뒤바뀝니다. 요소마다 색을 다시 칠할 필요 없이 변수 몇 개가 두 모드를 감당합니다.

08.8 배경에 은은한 층 만들기

바탕과 그 위에 떠오른 영역을 아주 미세하게 다른 밝기로 나누면, 선 없이도 앞뒤가 느껴집니다. 밝기 차이는 옅게 둡니다.


.page {
background: #f9fafb;
}
.panel {
background: #ffffff;
box-shadow: 0 1px 3px rgba(0, 0, 0, .08);
}


패널이 바탕보다 살짝 밝고 옅은 그림자가 얹히니, 테두리를 긋지 않아도 위에 떠 있는 느낌이 납니다. 은은한 층이 화면에 조용한 깊이를 줍니다.

08.9 중심 색과 기능 색을 구별하기

브랜드를 드러내는 중심 색이 오류 색과 같은 계열이면 사용자가 강조인지 경고인지 헷갈립니다. 둘의 자리를 미리 나눠 둡니다.


:root {
--color-brand: #7c3aed;
--color-danger: #dc2626;
}
.btn-primary {
background: var(--color-brand);
}
.btn-delete {
background: var(--color-danger);
}


보라 계열이 브랜드를 맡고 붉은 계열은 위험만 맡으니, 삭제 버튼이 평범한 버튼과 확실히 갈립니다. 각 색의 역할이 겹치지 않아야 신호가 신호로 남습니다.

08.10 상호작용에도 색으로 응답하기

마우스를 올리거나 누를 때 색이 미세하게 변하면 여기가 살아 있는 자리라는 신호가 됩니다. 강조색의 진하기를 한 단계씩 옮깁니다.


.btn-primary {
background: var(--blue-500);
transition: background .15s;
}
.btn-primary:hover {
background: var(--blue-700);
}
.btn-primary:disabled {
background: var(--blue-100);
cursor: not-allowed;
}


손을 올리면 파랑이 짙어지고 못 누를 때는 옅어집니다. 밝기 단계를 미리 나눠 둔 덕에 상태마다 새 색을 만들지 않고 계단만 오르내리면 됩니다.

08.11 정리

색은 예쁜가가 아니라 무슨 일을 하는가로 골라야 합니다. 역할에 따라 이름을 붙이고, 밝기 단계로 폭을 넓히고, 강조는 아끼고, 색만으로 뜻을 싣지 않는 이 규칙들만 지켜도 화면은 알록달록함을 벗고 또렷해집니다. 색이 적어진 화면이 오히려 힘이 세지는 것을 코드로 확인할 수 있습니다.