Skip to content
This page has been auto-translated and may contain errors.View in English

색상과 배경

docs.scrimba.com

버튼을 특정한 톤의 파란색으로 만들고 싶다고 해봅시다. 디자인 파일에서 그 색을 찾아 #1b2130을 스타일시트에 붙여 넣으면 잘 작동하지만, 그 문자들이 무슨 뜻인지, 이 색을 조금 더 밝게 만들려면 어떻게 해야 하는지는 전혀 알 수가 없습니다. CSS의 색상은 그 밑바탕에 있는 몇 가지 체계를 배우기 전까지는 알 수 없는 코드 더미처럼 보입니다. 색상을 읽고 배경에 칠할 수 있게 되면, 페이지의 시각적 개성 대부분을 여러분 손으로 다룰 수 있게 됩니다.

색상 이름 붙이기

CSS에서는 같은 색을 여러 방식으로 쓸 수 있습니다. 가장 간단한 방법은 **키워드**로, 브라우저가 이미 알고 있는 평범한 영어 이름을 쓰는 것입니다.

css
.tag {
  color: rebeccapurple;      /* 이름이 붙은 색상, 코드가 필요 없음 */
}

이런 이름은 약 140개 정도 있는데, 빠르게 작업할 때는 편리하지만 실제 디자인에 쓰기에는 턱없이 부족합니다. 다른 방식들은 어떤 색이든 표현할 수 있게 해줍니다. hex 코드# 뒤에 여섯 글자를 붙인 것이고, rgb()는 빨강, 초록, 파랑을 양으로 섞은 것입니다. 이들은 모두 정확한 색을 가리키며, 키워드는 그중 흔히 쓰이는 색을 위한 지름길일 뿐입니다.

css
.tag {
  color: #1b2130;            /* 같은 다크 네이비, hex 코드로 표현 */
}

CSS에서 색상을 쓰는 방법은 네 가지이며, 서로 다른 체계로 같은 색을 표현합니다. rebeccapurple 같은 **키워드**는 고정된 이름입니다. #1b2130 같은 hex 코드는 빨강, 초록, 파랑을 여섯 글자에 담습니다. rgb() 함수는 같은 세 채널을 0에서 255까지의 숫자로 지정합니다. 그리고 hsl()은 색상을 색조(hue), 채도(saturation), 명도(lightness)로 표현합니다.

css
.badge {
  color: #1b2130;                 /* hex: 빨강, 초록, 파랑 각각 두 글자씩 */
  color: rgb(27, 33, 48);         /* 같은 색을 빨강, 초록, 파랑 양으로 표현 */
  color: hsl(219, 28%, 15%);      /* 같은 색을 색조, 채도, 명도로 표현 */
}

이 세 줄은 모두 똑같은 다크 네이비를 칠합니다. 색을 직접 골라야 할 때는 **hsl()**을 쓰세요. hsl은 사람이 직접 판단할 수 있는 설명처럼 읽히는 반면, hex와 rgb는 대부분 그대로 복사해서 붙여 넣는 숫자에 가깝습니다.

CSS는 키워드, hex 코드, rgb(), hsl() 중 어떤 형태로든 색상을 받아들이는데, 이 중 앞의 세 가지는 모두 서로 다른 표기법으로 sRGB 공간(수십 년간 화면이 써온 표준 색상 공간)을 가리킵니다. hex 코드는 16진수입니다. #1b2130은 빨강 1b, 초록 21, 파랑 30이며, 각 쌍은 00부터 ff까지(10진수로는 0에서 255까지) 값을 가집니다. rgb()는 같은 세 채널을 그냥 10진수로 씁니다. 둘 다 사람이 머릿속으로 판단하기 좋은 형태는 아닙니다. 색을 "조금 더 밝게" 바꾸려면 명확한 방향 없이 세 숫자를 동시에 조정해야 하기 때문입니다.

css
.badge {
  color: #1b2130;               /* 16진수: 빨강 1b, 초록 21, 파랑 30 */
  color: rgb(27 33 48);         /* 같은 채널, 10진수, 최신 공백 구분 문법 */
  color: hsl(219 28% 15%);      /* 색조 219도, 채도 28%, 명도 15% */
}

hsl()을 우선적으로 쓰세요. 세 축 각각이 독립적으로 조절할 수 있는 하나의 값에 대응합니다. 색조는 색상환 위 0에서 360까지의 위치이고, 채도는 색이 얼마나 선명한지, 명도는 얼마나 밝은지를 나타냅니다. 호버 상태를 한 톤 더 어둡게 만들고 싶다면? 명도만 몇 퍼센트 낮추고 나머지 둘은 그대로 두면 됩니다. 이렇게 하나의 축이 하나의 의미를 읽기 쉽게 대응하기 때문에, hsl()은 팔레트를 만들 때 쓰는 커스텀 속성과 깔끔하게 어울립니다. 이때 색조 하나를 유지한 채 명도만 바꿔가며 여러 톤의 세트를 만들 수 있습니다.

Juno색상 이름 붙이기 같은 색이라도 rebeccapurple 같은 키워드, #1b2130 같은 hex 코드, 또는 rgb()로 쓸 수 있습니다. 키워드는 빠르지만 약 140개뿐이라서 실제 작업 대부분은 코드를 씁니다. 아직 hex 코드를 눈으로 읽으려고 하지 마세요. 지금 단계에서는 정확한 값을 그대로 복사하는 것이 완전히 정상입니다.
Juno색상 이름 붙이기 키워드, hex, rgb(), hsl()은 모두 서로 다른 체계로 같은 색을 이름 붙입니다. 색을 직접 고를 때는 hsl()을 쓰세요. 색조, 채도, 명도는 실제로 판단할 수 있는 값이기 때문입니다. hex와 rgb()는 직접 조정하기보다 디자인에서 그대로 복사해오는 값에 적합합니다.
Juno색상 이름 붙이기 키워드, hex, rgb()는 모두 서로 다른 표기법으로 sRGB를 가리키며, hsl()은 같은 색을 한 번에 하나씩 조절할 수 있는 세 축으로 재구성합니다. 이것이 hsl을 우선하는 이유입니다. 더 어두운 호버 상태를 만들 때 세 좌표를 조정할 필요 없이 명도 하나만 바꾸면 됩니다. 커스텀 속성으로 팔레트를 만들고 명도를 단계별로 바꿀 때 이 장점이 확실히 드러납니다.

투명도

때로는 색을 부분적으로 투명하게 만들어서 뒤에 있는 것이 조금 보이게 하고 싶을 때가 있습니다. 이것이 **투명도**이며, alpha라는 네 번째 값을 추가해서 만듭니다. 0은 완전히 투명, 1은 완전히 불투명입니다.

css
.overlay {
  background-color: rgb(27 33 48 / 0.5);   /* 네이비, 하지만 절반만 투명하게 보임 */
}

끝에 붙은 / 0.5는 "불투명도 50%"라는 뜻입니다. 엘리먼트 전체를, 텍스트까지 포함해서 한꺼번에 희미하게 만드는 opacity라는 속성도 있습니다.

css
.faded {
  opacity: 0.5;              /* 특정 색상이 아니라 엘리먼트 전체를 희미하게 만듦 */
}

이 차이는 중요합니다. alpha는 그 색상 하나에만 영향을 주지만, opacity는 엘리먼트에 관한 모든 것을 희미하게 만듭니다.

색을 투명하게 만들려면 alpha 값을 추가하면 됩니다. 0은 보이지 않음, 1은 불투명함을 뜻합니다. rgb()hsl() 둘 다 슬래시 뒤에 이 값을 받고, hex 코드는 끝에 두 글자를 더 붙여서 나타낼 수 있습니다.

css
.overlay {
  background-color: rgb(27 33 48 / 0.5);    /* 절반 투명한 네이비 */
  background-color: hsl(219 28% 15% / 0.5); /* hsl로 표현한 같은 색 */
  background-color: #1b213080;              /* 다시 같은 색: 80은 대략 alpha 50%에 해당 */
}

alpha와 opacity 속성 사이에는 중요한 차이가 있습니다. alpha는 딱 그 색 하나만 부분적으로 투명하게 만듭니다. opacity는 텍스트, 테두리, 자식 엘리먼트까지 포함해서 엘리먼트 전체를 하나의 단위로 희미하게 만듭니다. 색 하나를 희미하게 만들 때는 alpha를, 엘리먼트 전체를 희미하게 만들 때는 opacity를 쓰세요. 배경만 살짝 부드럽게 하려다가 텍스트까지 흐릿해졌다면, alpha를 써야 할 자리에 opacity를 쓴 것입니다.

**alpha**는 색상의 네 번째 채널로, 0(완전 투명)부터 1(완전 불투명)까지의 값을 가지며 오직 그 색상 값 하나에 속합니다. 최신 rgb()hsl()은 슬래시 뒤에 이 값을 받고, hex는 네 번째 바이트로 받아들이므로, #1b21308080/ff, 대략 50%의 네이비가 됩니다. alpha가 색상에 속하는 값이기 때문에, 배경을 반투명하게 만들면서도 텍스트, 테두리, 나머지 모든 것은 완전히 불투명한 상태로 유지할 수 있습니다.

css
.overlay {
  background-color: hsl(219 28% 15% / 0.5);  /* 이 채우기만 반투명함 */
  color: white;                              /* 텍스트는 완전히 불투명 상태 유지 */
}

opacity는 비슷해 보이지만 사실은 다른 도구입니다. 엘리먼트 전체의 투명도를 설정하는데, 그 엘리먼트와 모든 자손 엘리먼트를 한 그룹으로 합성해서 처리합니다. 여기서 alpha에는 없는 두 가지 결과가 생깁니다. 첫째, 1보다 작은 opacity는 새로운 스태킹 컨텍스트(겹쳐진 엘리먼트들의 순서를 정하는 독립된 레이어)를 만들어내는데, 이는 어떤 요소가 어떤 요소 위에 놓이는지를 바꿔놓을 수 있습니다. 둘째, 희미해진 부모 안에 있는 자식 엘리먼트를 다시 불투명하게 되돌릴 수 없습니다. 희미하게 처리되는 것이 완성된 그룹 전체이기 때문입니다. 하나의 채우기나 테두리만 투명하게 하고 싶을 때는 그 색상에 alpha를 쓰고 opacity는 그대로 두세요.

Juno투명도 색상에 네 번째 값을 추가하면 투명하게 만들 수 있습니다. 0은 보이지 않음, 1은 불투명, / 0.5 같은 값은 그 중간입니다. 이 alpha는 그 색 하나에만 적용됩니다. 별도의 opacity 속성은 텍스트까지 포함해서 엘리먼트 전체를 희미하게 만드므로, 배경만 부드럽게 하고 싶을 때는 alpha를 선택하세요.
Juno투명도 alpha는 색상 하나의 투명도로, rgb()hsl()에서 슬래시 뒤에 쓰거나 hex 끝에 두 글자를 더 붙여서 표현합니다. opacity 속성은 이보다 무딘 도구로, 엘리먼트와 그 자식 전체를 한 번에 희미하게 만듭니다. 텍스트가 뜻하지 않게 흐릿해졌다면, alpha를 써야 할 자리에 opacity를 쓴 것입니다.
Juno투명도 alpha는 색상에 속하는 값이므로, 반투명한 채우기를 만들어도 텍스트와 테두리는 그대로 불투명하게 남습니다. opacity는 엘리먼트와 그 자손 전체를 한 그룹으로 합성하기 때문에, 희미해진 부모 안의 자식은 불투명하게 남을 수 없고, 조용히 새로운 스태킹 컨텍스트가 생겨납니다. 엘리먼트 전체를 함께 희미하게 만들고 싶은 게 아니라면 alpha를 쓰세요.

배경 색상과 이미지

background-color 속성은 엘리먼트의 테두리까지 그 뒤쪽 영역을 채웁니다. 이름을 붙일 수 있는 색이라면 어떤 것이든 쓸 수 있습니다.

css
.hero {
  background-color: hsl(219 28% 15%);   /* 콘텐츠 뒤에 놓인 단색 네이비 */
}

background-image로 엘리먼트 뒤에 이미지를 넣을 수도 있으며, url(...)로 파일을 가리킵니다.

css
.hero {
  background-image: url("mountains.jpg");
  background-size: cover;               /* 전체 영역을 채우도록 이미지를 확대 */
}

background-size: cover는 넘치는 부분을 잘라내면서 이미지가 상자를 완전히 채우도록 확대합니다. 빈 공간이 생기지 않도록 하려면 대부분의 경우 이 설정을 쓰면 됩니다.

background-color는 콘텐츠 뒤쪽 영역, 즉 패딩 박스를 채워서 테두리 안쪽까지 도달합니다. 그 색 위에 background-imageurl(...)로 이미지를 겹쳐 놓을 수 있습니다. 이미지가 그 위에 겹쳐지는 방식이기 때문에, 이미지 아래에 배경색을 깔아두면 투명한 부분이나 이미지가 아직 로딩되는 동안에도 그 색이 보입니다.

css
.hero {
  background-color: hsl(219 28% 15%);   /* 투명한 부분에 비치고, 먼저 로딩됨 */
  background-image: url("mountains.jpg");
  background-size: cover;               /* 상자를 채우고, 넘치는 부분은 잘라냄 */
  background-position: center;          /* 이미지의 중앙이 계속 보이도록 유지 */
  background-repeat: no-repeat;         /* 한 장만, 타일링하지 않음 */
}

이미지가 자리잡는 방식은 세 가지 속성이 결정합니다. background-size는 보통 cover(상자를 채우고 넘치는 부분을 잘라냄) 또는 contain(이미지 전체를 안에 맞추며, 빈 공간이 생길 수 있음)을 씁니다. background-position은 이미지가 잘려나갈 때 어느 부분이 계속 보일지를 결정하는데, center가 안전한 기본값입니다. background-repeat: no-repeat은 작은 이미지가 상자 안에서 타일처럼 반복되는 것을 막습니다.

background-color는 엘리먼트의 배경 영역을 채우는데, 기본값은 패딩 박스이므로 이 색은 패딩의 바깥쪽 끝까지 도달하고 테두리에서 멈춥니다. background-image는 이 색 위에 겹쳐지며, 사진 같은 이미지 아래에는 단색 background-color를 깔아두는 것이 좋습니다. 이미지가 로딩되는 동안이나 요청이 실패했을 때 보여지는 대체 화면이 되고, 투명한 영역을 통해서도 비쳐 보이기 때문에, 이미지의 톤과 가까운 색을 고르면 빈 페이지가 잠깐 번쩍이는 현상을 막을 수 있습니다.

css
.hero {
  background-color: hsl(219 28% 15%);   /* 대체 화면 및 로딩 중 채우기 */
  background-image: url("mountains.jpg");
  background-size: cover;               /* cover: 상자를 채우고 넘치는 부분을 잘라냄 */
  background-position: center;          /* 크롭의 기준을 중앙으로 고정 */
  background-repeat: no-repeat;
}

크기 설정은 실제로 트레이드오프입니다. cover는 이미지를 양쪽 축 모두 상자를 채우도록 확대하고 넘치는 부분을 잘라내므로, 빈 공간은 없지만 이미지 일부를 잃게 됩니다. contain은 이미지 전체가 들어맞을 때까지 확대하므로 잘려나가는 부분은 없지만, 상자에 이미지가 없는 빈 띠가 생길 수 있습니다(이 빈 부분은 background-color가 채웁니다). background-positioncover를 쓸 때 특히 중요한데, 크롭 후에 어느 영역이 남을지를 이 속성이 결정하기 때문입니다. center는 안전하지만, 인물 사진이라면 얼굴이 화면 안에 남도록 top을 쓰고 싶을 수 있습니다. 사진이 아닌 선명한 채우기를 원한다면, 그라디언트도 background-image의 한 종류라는 점을 기억하세요. 다음 절에서 이를 다룹니다.

Juno배경 색상과 이미지background-color는 엘리먼트 뒤쪽 공간을 채우고, background-image: url(...)는 그 자리에 대신 그림을 넣습니다. 이미지에 background-size: cover를 함께 써서 빈 공간 없이 상자 전체를 채우세요. 이 조합이면 초반에 필요한 대부분의 경우를 다룰 수 있습니다.
Juno배경 색상과 이미지 배경 이미지는 배경색 위에 겹쳐지므로, 이미지가 로딩되는 동안을 위해 그 아래에 색을 깔아두세요. cover는 상자를 채우고 넘치는 부분을 잘라내고, contain은 이미지 전체를 맞추며 빈 공간이 생길 수 있고, background-position은 잘려나갈 때 어느 부분이 남을지를 정합니다. 이미지가 타일처럼 반복되는 걸 원하지 않는다면 no-repeat을 추가하세요.
Juno배경 색상과 이미지 사진 아래에는 항상 background-color를 깔아두세요. 로딩 중 대체 화면이 되고 투명한 영역에도 비쳐 보입니다. covercontain은 각각 채우고-자르기와 맞추고-빈공간-생길 수도-있기의 관계이고, background-positioncover일 때만 진짜 쓸모가 있는데, 크롭 후에 무엇이 남을지를 이것이 결정하기 때문입니다. 기본은 center로 두고, 이미지의 특정 부분이 반드시 화면에 남아야 할 때만 옮기세요.

그라디언트

**그라디언트**는 한 색에서 다른 색으로 부드럽게 이어지는 색 변화입니다. CSS는 이것을 이미지의 한 종류로 취급하므로, background-color가 아니라 background-image에 씁니다.

css
.banner {
  background-image: linear-gradient(hsl(219 28% 15%), hsl(219 40% 35%));
}

이 코드는 위쪽의 첫 번째 색에서 아래쪽의 두 번째 색으로 이어지며 색이 섞입니다. 색을 두 개보다 더 나열할 수도 있고, 방향을 바꿀 수도 있습니다.

css
.banner {
  background-image: linear-gradient(to right, hsl(340 80% 55%), hsl(30 90% 60%));
  /* 위에서 아래가 아니라 왼쪽에서 오른쪽으로 색이 섞임 */
}

이미지 파일이 필요 없습니다. 브라우저가 색 변화를 대신 그려줍니다.

**그라디언트**는 생성되는 이미지이기 때문에, linear-gradient()background-color가 아니라 background-image에 씁니다. 첫 번째 인수는 방향이고, 그 뒤에 섞을 색상 목록이 옵니다. 방향을 생략하면 위에서 아래로 진행됩니다.

css
.banner {
  background-image: linear-gradient(
    to right,                    /* 방향: 왼쪽 끝에서 오른쪽 끝으로 */
    hsl(340 80% 55%),            /* 시작 색상 */
    hsl(30 90% 60%)              /* 끝 색상 */
  );
}

radial-gradient()도 방식은 비슷하지만, 직선이 아니라 중심점에서 원형으로 바깥쪽을 향해 색을 섞습니다. 부드러운 빛 번짐이나 스포트라이트 효과를 낼 때 쓰는 도구입니다.

css
.spotlight {
  background-image: radial-gradient(hsl(0 0% 100%), hsl(219 28% 15%));
  /* 중앙은 밝고 가장자리로 갈수록 어두워짐 */
}

그라디언트는 배경 이미지이므로, 같은 background-image 속성 아래에서 콤마로 구분해서 실제 이미지나 다른 그라디언트와 함께 겹쳐 쌓을 수 있습니다.

그라디언트는 생성되는 이미지이므로 background-image에 위치하며, url(...)과 똑같은 크기 조절 및 레이어링 규칙을 따릅니다. linear-gradient()는 선택적으로 방향을 먼저 받고(to right135deg 같은 각도), 그 뒤에 색을 섞을 색상 정지점(color stop) 목록을 받습니다. 정지점에 명확한 위치를 지정하면 색이 정확히 어디에서 나타날지 조절할 수 있고, 두 정지점을 같은 지점에서 만나게 하면 그라디언트가 딱 잘린 줄무늬로 바뀝니다.

css
.banner {
  background-image: linear-gradient(
    135deg,                      /* 각도: 0도는 위쪽, 90도는 오른쪽 */
    hsl(340 80% 55%) 0%,
    hsl(30 90% 60%) 100%
  );
}

radial-gradient()는 중심에서 바깥쪽으로 색을 섞으며, 정지점 앞에 선택적으로 모양과 크기를 받습니다. 둘 다 어느 의미로 보아도 이미지이므로, 콤마로 구분한 하나의 background-image 아래에서 겹쳐 쌓이며, 앞에 나온 그라디언트가 뒤에 나온 것 위에 그려집니다. 이 방식을 이용하면 반투명한 그라디언트를 사진 위에 색조로 얹을 수 있습니다.

css
.hero {
  background-image:
    linear-gradient(hsl(219 28% 15% / 0.6), hsl(219 28% 15% / 0.6)),  /* 위에 얹는 어두운 색조 */
    url("mountains.jpg");                                             /* 그 아래의 사진 */
  background-size: cover;
}

hsl()은 명도를 하나의 값으로 직접 드러내므로, 같은 색조에서 명도 두 값 사이를 오가는 두 지점짜리 그라디언트는 깔끔하고 조절하기 쉬운 페이드를 만들어냅니다. hex보다 hsl을 선택하는 것이 또 한 번 빛을 발하는 지점입니다.

Juno그라디언트 그라디언트는 색들 사이의 부드러운 색 변화이며, CSS는 이것을 이미지로 취급하므로 background-image에 씁니다. linear-gradient()는 기본적으로 위에서 아래로 직선 방향을 따라 색이 섞이며, 방향을 바꾸려면 to right 등을 추가하면 됩니다. 파일이 필요 없고, 브라우저가 직접 색 변화를 그려줍니다.
Juno그라디언트linear-gradient()radial-gradient()는 둘 다 생성되는 이미지이므로 background-image에 씁니다. 선형(linear)은 방향을 정한 직선을 따라 색이 섞이고, 방사형(radial)은 중심에서 원형으로 바깥쪽을 향해 섞이며 빛 번짐 효과에 좋습니다. 이미지이기 때문에, 하나의 콤마로 구분한 background-image 아래에 여러 개를 겹쳐 쌓을 수 있습니다.
Juno그라디언트 그라디언트는 이미지이므로 위치를 지정할 수 있고, 하나의 background-image 아래에 겹쳐 쌓이며, background-size 규칙을 따릅니다. 색상 정지점에 명확한 위치를 지정하면 정밀한 페이드나 딱 잘린 줄무늬를 만들 수 있고, 반투명한 그라디언트를 url(...) 위에 겹쳐 쌓으면 별도의 엘리먼트 없이도 사진에 색조를 입힐 수 있습니다. 같은 색조에서 명도 두 값을 오가는 두 지점짜리 hsl() 페이드가 가장 다루기 깔끔한 방식입니다.

상황에 맞게 변하면서 읽기 쉬운 색상

유용한 키워드로 currentColor가 있습니다. 이것은 엘리먼트의 텍스트 색을 대신 가리키는 값이므로, 테두리나 아이콘이 텍스트 색과 자동으로 맞춰지게 만들 수 있습니다.

css
.chip {
  color: hsl(219 28% 15%);
  border: 2px solid currentColor;   /* 테두리가 텍스트 색과 일치함 */
}

나중에 텍스트 색을 바꾸면 테두리도 별도의 수정 없이 저절로 따라갑니다.

한 가지 더 기억할 점은, 텍스트가 배경과 확실히 구별되게 만들어야 한다는 것입니다. 어두운 배경에 어두운 텍스트는 읽기 어렵고, 흰 배경에 흐린 회색도 눈이 피로해집니다. 둘 사이에 밝기 차이가 뚜렷하게 나도록 하세요.

currentColor는 엘리먼트 자신의 color 값을 그대로 가져오는 키워드이므로, 테두리, 외곽선, SVG 아이콘이 값을 반복하지 않고도 텍스트 색을 따라갈 수 있습니다. 텍스트 색을 한 곳에서 설정하면 currentColor를 참조하는 모든 것이 함께 바뀝니다.

css
.chip {
  color: hsl(219 28% 15%);
  border: 2px solid currentColor;   /* color를 따라가며, 값을 중복해서 쓰지 않음 */
}

이와 함께 눈여겨봐야 할 속성이 **대비(contrast)**입니다. 텍스트와 배경 사이의 밝기 차이를 뜻합니다. 대비가 낮으면 읽기 어렵고, 접근성 가이드라인은 본문 텍스트에 대해 최소 비율을 정해 놓았습니다. 여기서 hsl()이 다시 한번 빛을 발합니다. 명도가 독립적인 축이기 때문에, 색조는 유지한 채 텍스트와 배경의 명도를 충분히 멀리 떨어뜨려 놓으면 세 채널을 한꺼번에 짐작할 필요 없이 가독성을 확보할 수 있습니다.

currentColor는 엘리먼트의 계산된 color 값을 가리키므로, 테두리, 외곽선, box shadow, SVG의 fill을 텍스트 색에 가볍게 묶어둘 수 있는 방법이 됩니다. 다른 값들처럼 상속되므로, 컨테이너에 color를 설정하면 하나의 근원 색이 currentColor를 참조하는 모든 자손 엘리먼트로 캐스케이드되어 내려가는데, 이는 하나의 선언만으로 컴포넌트 전체에 테마를 입히는 깔끔한 패턴입니다.

더 까다로운 제약은 **대비(contrast)**입니다. 전경과 배경 사이의 상대 휘도(relative luminance) 차이를 측정한 값입니다. 접근성 표준인 WCAG(웹 콘텐츠 접근성 가이드라인)는 일반 본문 텍스트에 최소 4.5대 1, 큰 텍스트에 최소 3대 1의 비율을 요구하며, 이 기준에 못 미치는 텍스트는 저시력자뿐만 아니라 많은 사람에게 읽기 어렵습니다. hsl()은 이 기준을 맞추는 데 도움이 됩니다. 명도가 독립된 축이므로 색조와 채도는 그대로 유지한 채 두 명도 값만 서로 충분히 멀어질 때까지 움직여서 기준을 통과시킬 수 있고, 여섯 개 채널을 눈치껏 조정할 필요가 없습니다. 다만 이는 판단을 돕는 도구일 뿐 보장은 아닙니다. 명도가 같은 색이라도 실제 휘도 계산에서는 기준을 통과하지 못할 수 있으므로, 최종 비율은 반드시 체커로 검증하세요.

이 수준에서 알아두면 좋은 것이 두 가지 더 있습니다. background 축약 속성은 여러 배경 관련 속성을 한꺼번에 설정하는데, 한 가지 규칙은 background-sizebackground-position 뒤에 슬래시로 구분되어 온다는 것입니다: background: url("bg.jpg") center / cover no-repeat. 그리고 CSS에는 이제 hsl()을 넘어서는 색상 함수, 예를 들어 oklch() 같은 것이 있어서, 더 넓은 감마(gamut)(화면이 표현할 수 있는 색상의 범위)를 지원하는 디스플레이에서 sRGB 공간 바깥의 색까지 표현할 수 있습니다. 아직 이런 함수를 쓸 필요는 없지만, 이런 최신 함수가 존재한다는 것을 알아두면 디자인 시스템이 sRGB로는 표현할 수 없는 더 선명한 색을 요구할 때 그것을 알아볼 수 있습니다. 여기에 읽기 좋은 타이포그래피를 더하면 텍스트가 모든 독자에게 그 의미를 제대로 전달하게 됩니다.

Juno상황에 맞게 변하면서 읽기 쉬운 색상currentColor는 엘리먼트의 텍스트 색을 그대로 복사해오는 키워드이므로, 테두리를 텍스트와 맞추고 나중에 텍스트 색을 바꾸면 그것도 따라가게 만들 수 있습니다. 그 외에는 텍스트가 읽기 쉽도록 유지하세요. 뒤쪽 배경보다 확실히 더 밝거나 더 어둡게 만드세요. 낮은 대비는 페이지가 읽기 어려워지는 가장 흔한 원인입니다.
Juno상황에 맞게 변하면서 읽기 쉬운 색상currentColor는 테두리, 외곽선, 아이콘을 텍스트 색에 묶어두므로 한 곳에서만 설정하면 됩니다. 대비, 즉 텍스트와 배경 사이의 밝기 차이를 눈여겨보세요. 접근성 규칙이 본문 텍스트에 최소 기준을 정해두고 있기 때문입니다. hsl()에서 명도 값을 서로 떨어뜨리는 것이 그 차이를 충분히 벌려두는 가장 확실한 방법입니다.
Juno상황에 맞게 변하면서 읽기 쉬운 색상currentColor는 상속되므로, 컨테이너에 설정한 color 하나가 이를 참조하는 모든 자손에 테마를 입힙니다. WCAG는 본문 텍스트에 4.5대 1을 요구하고, hsl()은 명도만 움직여서 그 기준에 다다르게 해주지만, 명도가 같아도 실제 휘도 검사에서는 실패할 수 있으니 도구로 검증하세요. background 축약 속성은 크기를 위치 뒤에 슬래시로 붙인다는 것을 기억하고, oklch() 같은 최신 함수는 디자인에 더 넓은 감마가 필요할 때를 위해 존재한다는 것도 알아두세요.