같은 파란색을 적는 방법은 여러 갈래인데, 어떤 표기는 기계가 주고받기 좋고 어떤 표기는 사람이 조절하기 좋습니다. 코드로 보면 언제 무엇을 쓸지가 또렷해집니다. 아래 예제는 그대로 복사해 확인할 수 있습니다.

48.1 같은 색을 가리키는 세 표기

hex, rgb, hsl은 표기법만 다를 뿐 완전히 같은 색을 가리킵니다.


.a { color: #2563eb; } /* 16진수 */
.b { color: rgb(37, 99, 235); } /* 빨강 초록 파랑 */
.c { color: hsl(221, 83%, 53%); } /* 색상 채도 명도 */


세 줄 모두 똑같은 파랑으로 칠해집니다. hex는 짧고 정확해 도구와 코드 사이에서 흔들림이 없고, hsl은 값만 보고도 어떤 색인지 그려집니다.

48.2 RGB는 빛을 더하는 셈

화면의 색은 빛이라 빨강 초록 파랑을 더할수록 밝아집니다. 채널은 0에서 255까지, 모두 0이면 검정, 모두 255면 흰색입니다.


.black { background: rgb(0, 0, 0); } /* 빛 없음 */
.white { background: rgb(255, 255, 255); } /* 빛 가득 */
.red { background: rgb(255, 0, 0); }
.green { background: rgb(0, 255, 0); }
.blue { background: rgb(0, 0, 255); }


물감은 섞을수록 어두워지지만 빛은 다 켜면 흰색입니다. 빨강과 초록 빛을 함께 최대로 켜면 노랑이 나오는 것도 같은 이치입니다.


.yellow { background: rgb(255, 255, 0); } /* 빨강+초록 */
.gray { background: rgb(128, 128, 128); } /* 세 값이 같으면 무채색 */


세 채널이 같으면 색기 없는 회색이 되고, 값이 클수록 밝은 회색입니다.

48.3 HSL은 사람의 직관에 가깝다

hsl은 색을 색상 채도 명도 세 축으로 나눠 적습니다. 첫 값이 색상환의 각도, 둘째가 선명함, 셋째가 밝기입니다.


.h0 { background: hsl(0, 70%, 50%); } /* 빨강 */
.h120 { background: hsl(120, 70%, 50%); } /* 초록 */
.h240 { background: hsl(240, 70%, 50%); } /* 파랑 */
/* 채도만 낮추면 차분해짐 */
.dull { background: hsl(240, 20%, 50%); }
/* 명도만 올리면 밝아짐 */
.pale { background: hsl(240, 70%, 80%); }


색조는 그대로 둔 채 둘째 값만 내리면 차분해지고 셋째 값만 올리면 밝아집니다. 어느 축을 건드리는지가 이름에 적혀 있습니다.

48.4 공백 구분 문법과 알파

요즘 CSS는 쉼표 없이 공백으로 채널을 나누고 슬래시 뒤에 투명도를 붙입니다. 값은 그대로인데 형태만 바뀐 것입니다.


.old { color: rgb(37, 99, 235); } /* 옛 문법 */
.new { color: rgb(37 99 235); } /* 공백 구분 */
.half { color: rgb(37 99 235 / 0.5); } /* 알파 50% */
.hsl-half { color: hsl(221 83% 53% / 0.5); }


슬래시 뒤의 0.5는 절반만 비친다는 뜻입니다. 투명한 검정을 깔면 그 자리가 가라앉고 투명한 흰색을 얹으면 떠오르니, 오버레이나 그림자에 잘 맞습니다.


.overlay { background: rgb(0 0 0 / 0.45); } /* 어둡게 */
.glass { background: rgb(255 255 255 / 0.15); } /* 밝게 */

48.5 밝게 어둡게, HSL이 쉬운 이유

버튼의 호버 색처럼 같은 색을 살짝 어둡게 만들 일이 잦습니다. rgb로 하면 세 채널을 일일이 낮춰야 하는데, 얼마씩 낮춰야 색이 안 틀어지는지 감이 안 옵니다.


/* rgb: 세 값을 각각 손대야 하고 색조가 흔들리기 쉬움 */
.btn { background: rgb(37, 99, 235); }
.btn:hover { background: rgb(29, 78, 216); } /* 얼마가 맞는지 애매 */


hsl이라면 색상과 채도는 두고 명도 값 하나만 내리면 됩니다. 어두워지는 정도를 숫자로 예측할 수 있습니다.


:root {
--h: 221;
--s: 83%;
}
.btn { background: hsl(var(--h) var(--s) 53%); }
.btn:hover { background: hsl(var(--h) var(--s) 46%); }
.btn:active { background: hsl(var(--h) var(--s) 38%); }


명도만 한 칸씩 내려가니 색은 그대로인 채 눌리는 느낌만 또렷해집니다.

48.6 그라데이션에서 갈리는 보간

두 색을 그라데이션으로 이을 때, 어떤 표기로 적었는지에 따라 중간 색이 달라집니다. rgb는 채널을 직선으로 섞어서 중간이 탁하게 가라앉는 구간이 생기곤 합니다.


/* rgb 보간: 노랑과 파랑 사이가 칙칙한 회색을 지남 */
.grad-rgb {
background: linear-gradient(
to right,
rgb(255, 220, 0),
rgb(0, 90, 255)
);
}


hsl로 두 각도를 이으면 hue가 색상환을 따라 회전해 중간에 초록과 청록 같은 실제 색을 거칩니다.


/* hsl 보간: hue가 60도에서 220도로 돌며 초록을 통과 */
.grad-hsl {
background: linear-gradient(
to right,
hsl(60 100% 50%),
hsl(220 100% 50%)
);
}


같은 두 끝점인데 가운데 색이 다릅니다. 화사한 흐름은 hue가 도는 쪽이, 담담한 연결은 직선으로 섞이는 쪽이 어울립니다.

48.7 지각 균일을 노린 oklch

hsl에는 은근한 약점이 있습니다. 명도를 똑같이 줘도 노랑은 밝아 보이고 파랑은 어두워 보여, 나란히 놓으면 밝기가 들쭉날쭉합니다.


/* 셋 다 명도 50%인데 눈에는 밝기가 제각각 */
.y { background: hsl(60 100% 50%); } /* 너무 밝아 보임 */
.b { background: hsl(240 100% 50%); } /* 너무 어두워 보임 */


표준 CSS Color 4의 oklch()는 사람 눈이 느끼는 밝기를 기준으로 색을 배치합니다. 값은 밝기 채도 색상 순서이고, 첫 값만 바꾸면 어떤 색조든 비슷하게 밝아집니다.


/* 밝기 60%로 통일하면 눈에도 고르게 보임 */
.oy { background: oklch(60% 0.15 100); } /* 노랑 계열 */
.ob { background: oklch(60% 0.15 250); } /* 파랑 계열 */


여러 색조의 계단을 세워도 같은 단계끼리 밝기가 맞아 팔레트가 한 몸처럼 정돈됩니다. 아직 오래된 환경까지 지원되진 않아, 안전망으로 기존 값을 함께 둡니다.


.brand {
background: #2563eb; /* 지원 안 되면 이 값 */
background: oklch(0.62 0.19 255); /* 되면 이 값 */
}


같은 속성을 두 번 쓰면 브라우저는 아는 마지막 값을 씁니다. oklch를 모르는 곳은 앞의 hex에서 멈추니 색이 사라지지 않습니다.

48.8 언제 무엇을 쓰나

표기를 하나로 고집할 이유는 없습니다. 정확한 색을 주고받을 때는 짧은 hex가 편하고, 색을 조절하고 변형을 만들 때는 hsl이나 oklch가 손에 붙습니다.


:root {
/* 고정 브랜드색은 hex로 못 박아 둠 */
--brand: #2563eb;
/* 조절해서 파생시킬 색은 조절 축이 보이는 표기로 */
--brand-h: 221;
--brand-s: 83%;
}
.badge { background: var(--brand); }
.badge-soft { background: hsl(var(--brand-h) var(--brand-s) 92%); }
.badge-strong { background: hsl(var(--brand-h) var(--brand-s) 40%); }


색값을 화면 곳곳에 흩뿌리면 나중에 바꿀 때 고생합니다. 정하는 즉시 변수로 한곳에 모으면 표기법을 통째로 갈아도 손댈 자리가 한 군데뿐입니다.

48.9 정리

hex와 rgb와 hsl은 같은 색의 다른 언어입니다. rgb는 빛을 더하는 셈이라 기계에게 정확하고, hsl은 색상 채도 명도로 나뉘어 조절하기 좋으며, oklch는 지각의 균일함과 넓은 색 범위를 얹어 줍니다. 전달할 때와 조절할 때 다른 표기를 쥐고 색은 변수로 모아 두면, 색을 감이 아니라 의도로 다룰 수 있습니다.