탐색

디자인 기초

색상

적응형 역할로 인터페이스 색상을 구성하고 반지름과 그림자로 표면을 돋보이게 하며 밝은 모드와 어두운 모드를 선택하세요.

이 페이지의 내용

#개요

표면, 선, 텍스트 역할을 바탕으로 인터페이스 색상을 구성하세요. 이들의 클래스는 밝은 모드와 어두운 모드에서 그대로 유지됩니다. 일러스트나 브랜드 강조색처럼 색 자체가 목적일 때는 팔레트 단계를 사용하세요. 색상 변화는 역할, 상태 또는 독자가 구별해야 하는 차이에 연결하세요.

색 구성표가 바뀌어도 역할은 그대로

역할은 한 번만 선택하세요. light-dark() 값은 다른 색 구성표 안의 로컬 영역을 포함해 요소의 색 구성표를 따릅니다.

텍스트 설명
  1. 역할 유지. 두 영역은 같은 표면, 구분선, 강조 텍스트, 보조 텍스트 클래스를 사용합니다.
  2. 영역별 해석. 역할 값은 light-dark()를 사용합니다. 각 영역의 color-scheme이 클래스를 바꾸지 않고 밝은 색 또는 어두운 색을 선택합니다.

#역할 또는 팔레트 단계 선택

목적사용 대상예제
페이지 구조와 텍스트 계층색상 역할bg-surface-raised, b-line-divider, fg-text-muted
특정 색상팔레트 단계bg-blue-5, fg-blue-60

하나의 구성, 두 모드

라이트
다크
두 문서는 동일한 클래스를 사용합니다. 활성 모드가 표면, 선, 텍스트 값을 제공합니다.
<section class="p-md r-lg b(1) b-solid b-line-divider bg-surface-raised fg-text-body">  <h2 class="m(0) text-lg font-regular fg-text-strong">Project updates</h2>  <p class="mt-xs mb-md text-sm fg-text-muted">Three milestones changed this week.</p>  <a class="fg-text-link underline" href="/updates">Continue</a></section>
생성된 CSS
@layer theme {  :root,  :host {    --color-zinc-40: oklch(68.6% .0028 265);    --color-zinc-60: oklch(48.6% .003 265);    --color-zinc-90: oklch(24.6% .0022 265)  }  :root,  :host {    --color-surface-raised: light-dark(var(--color-white), var(--color-zinc-90));    --color-line-divider: light-dark(oklch(0% 0 none / .12), oklch(100% 0 none / .12));    --color-text-muted: light-dark(var(--color-zinc-60), var(--color-zinc-40))  }  :root,  :host {    --color-white: oklch(100% 0 none)  }}@layer utilities {  .bg-surface-raised {    background-color: var(--color-surface-raised)  }  .b-line-divider {    border-color: var(--color-line-divider)  }  .fg-text-muted {    color: var(--color-text-muted)  }}

역할은 색상 키의 일부입니다. bg-surface-raised는 --color-surface-raised를, b-line-divider는 --color-line-divider를 읽습니다. 선 역할은 색상만 설정하므로 테두리 너비와 스타일은 별도로 지정하세요. 모든 역할은 색상 역할, 팔레트는 색상 팔레트를 참고하세요.

선 색상에는 너비와 스타일이 필요

모든 상자는 border-style: none으로 시작합니다. border-box 크기는 같고 나열된 테두리 선언만 달라집니다.

텍스트 설명
  1. b-line-divider는 border-color를, b(2)는 border-width를 제공합니다. 둘 다 none인 테두리 스타일을 바꾸지 않습니다.
  2. b-solid를 추가하면 2px 테두리가 그려집니다. 다른 필수 설정이 컴포넌트에 이미 있을 수 있으므로 색상 클래스만 보일 때는 전체 스타일을 검사하세요.

고정 색상 단계와 의미적 역할 구분

특정 색에는 팔레트 단계를, 맥락에 맞춰 바뀌어야 하는 결정에는 역할을 선택하세요.

텍스트 설명

두 경로가 고정 팔레트 단계와 밝은 값 또는 어두운 값을 선택하는 역할을 구분합니다.

#고정 및 적응형 색상 별칭

fg-yellow 같은 색상 별칭은 모든 모드에서 같은 값을 갖는 고정 색상입니다. fg-text-yellow 같은 텍스트 색상 별칭은 각 모드에서 텍스트 전경에 적합한 단계를 선택합니다. 실제 배경에서 대비를 확인하세요. 적응형 별칭도 모든 조합을 보장하지는 않습니다.

<svg class="fg-yellow" fill="currentColor" aria-hidden="true">…</svg><p class="fg-text-yellow">Pending review</p>
생성된 CSS
@layer theme {  :root,  :host {    --color-yellow-40: oklch(91.5% .1909 101.1);    --color-yellow-90: oklch(52.17% .1199 62.2)  }  :root,  :host {    --color-yellow: var(--color-yellow-40);    --color-text-yellow: light-dark(var(--color-yellow-90), var(--color-yellow-40))  }}@layer utilities {  .fg-text-yellow {    color: var(--color-text-yellow)  }  .fg-yellow {    color: var(--color-yellow)  }}

색상 라벨에는 텍스트 색상 별칭을 사용하세요. 본문, 강조, 링크에는 텍스트 역할을 유지하세요.


#돌출 표면

돌출 표면은 표면 역할, 반지름 토큰, 그림자 토큰을 조합합니다. 상호작용 가능한 표면에 마우스를 올리거나 포커스하면 그림자를 한 단계 높이세요. 일반 카드와 컨트롤에는 작은 반지름을 사용하고, pill은 아래 배지처럼 완전히 둥근 모양이 필요할 때 사용하세요.

돌출 표면

라이트
다크
두 문서는 같은 클래스를 사용합니다. 표면, 텍스트, 그림자 색상은 활성 모드가 제공하고 그림자 형태는 유지됩니다.
<a class="block p-md r-lg bg-surface-raised fg-text-body shadow-sm shadow-md:hover shadow-md:focus-visible" href="/notes">  <span class="flex items-center justify-between gap-sm">    <span class="fg-text-strong">Field notes</span>    <span class="px-xs py-3xs pill bg-line-subtle text-xs fg-text-muted">12 new</span>  </span></a>
생성된 CSS
@layer theme {  :root,  :host {    --radius-lg: .5rem  }  :root,  :host {    --shadow-sm: 0 0 0 1px light-dark(oklch(0% 0 none / .04), oklch(100% 0 none / .05)), 0 1px 1px -.5px light-dark(oklch(0% 0 none / .045), oklch(0% 0 none / .3)), 0 2px 2px -1px light-dark(oklch(0% 0 none / .04), oklch(0% 0 none / .26)), 0 4px 4px -2px light-dark(oklch(0% 0 none / .04), oklch(0% 0 none / .24));    --shadow-md: 0 0 0 1px light-dark(oklch(0% 0 none / .05), oklch(100% 0 none / .06)), 0 1px 1px -.5px light-dark(oklch(0% 0 none / .045), oklch(0% 0 none / .3)), 0 2px 2px -1px light-dark(oklch(0% 0 none / .04), oklch(0% 0 none / .26)), 0 4px 4px -2px light-dark(oklch(0% 0 none / .04), oklch(0% 0 none / .24)), 0 8px 8px -4px light-dark(oklch(0% 0 none / .045), oklch(0% 0 none / .26))  }}@layer utilities {  .r-lg {    border-radius: var(--radius-lg)  }  .pill {    border-radius: calc(infinity * 1px)  }  .shadow-sm {    box-shadow: var(--shadow-sm)  }  .shadow-md\:hover:hover {    box-shadow: var(--shadow-md)  }}

프리셋 그림자는 색상에 light-dark()를 사용하므로 shadow-sm은 형태를 유지하며 요소의 색 구성표를 따릅니다. pill은 반지름 토큰이 아닌 고정 유틸리티이며, 너비와 높이가 같으면 원을 만듭니다. rtl-lg, rtr-lg, rbl-lg, rbr-lg로 모서리를 개별 지정할 수 있습니다. 크기 체계와 사용자 지정은 반지름과 그림자를 참고하세요.

같은 pill 반지름도 상자 모양을 따름

높이는 64px를 유지합니다. pill은 반지름을 제공하며 예시 크기는 별도 크기 선언에서 옵니다.

텍스트 설명
  1. pill은 완전히 둥근 모서리 반지름을 설정하며 너비, 높이, 안쪽 여백은 설정하지 않습니다.
  2. 너비와 높이가 같으면 원이 됩니다. 같은 높이의 넓은 상자는 캡슐이 되므로 클래스만으로 원을 보장하지 않습니다.

#밝은 모드와 어두운 모드

프리셋은 루트에 color-scheme: light dark를 설정하고 역할과 그림자에 light-dark()를 사용하므로 추가 클래스 없이 시스템 설정을 따릅니다. 모드를 선택하려면 루트, 중첩 영역 또는 섀도 호스트에 data-theme="light"나 data-theme="dark"를 설정하세요.

하나의 토큰, 두 로컬 색 구성표

@theme {  --color-surface-notes: light-dark(#f4f4f5, #27272a);}@layer base {  [data-theme="light"] {    color-scheme: light;  }  [data-theme="dark"] {    color-scheme: dark;  }}
생성된 CSS
@layer base {  [data-theme="light"] {    color-scheme: light;  }  [data-theme="dark"] {    color-scheme: dark;  }}@layer theme {  :root,  :host {    --color-zinc-30: oklch(78% .0025 265);    --color-zinc-70: oklch(39.8% .0028 265)  }  :root,  :host {    --color-text-body: light-dark(var(--color-zinc-70), var(--color-zinc-30))  }  :root,  :host {    --font-weight-regular: 400;    --font-size-sm: .875rem;    --font-size-md: 1rem;    --font-size-lg: 1.125rem;    --text-sm: var(--font-size-sm);    --text-sm--line-height: 1.5;    --text-sm--letter-spacing: 0;    --text-md: var(--font-size-md);    --text-md--line-height: 1.6;    --text-md--letter-spacing: 0;    --text-lg: var(--font-size-lg);    --text-lg--line-height: 1.5;    --text-lg--letter-spacing: 0;    --radius-md: .375rem;    --radius-lg: .5rem;    --spacing-xs: .5rem;    --spacing-md: 1rem  }  :root,  :host {    --color-surface-notes: light-dark(#f4f4f5, #27272a)  }}@layer utilities {  .text-lg {    font-size: var(--text-lg);    line-height: var(--text-lg--line-height, normal);    letter-spacing: var(--text-lg--letter-spacing, normal)  }  .text-md {    font-size: var(--text-md);    line-height: var(--text-md--line-height, normal);    letter-spacing: var(--text-md--letter-spacing, normal)  }  .text-sm {    font-size: var(--text-sm);    line-height: var(--text-sm--line-height, normal);    letter-spacing: var(--text-sm--letter-spacing, normal)  }  .bg-surface-notes {    background-color: var(--color-surface-notes)  }  .r-lg {    border-radius: var(--radius-lg)  }  .r-md {    border-radius: var(--radius-md)  }  .fg-text-body {    color: var(--color-text-body)  }  .font-regular {    font-weight: var(--font-weight-regular)  }  .m\(0\) {    margin: calc(0 * 1px)  }  .mb-md {    margin-bottom: var(--spacing-md)  }  .mt-xs {    margin-top: var(--spacing-xs)  }  .mb\(0\) {    margin-bottom: calc(0 * 1px)  }  .wrap-anywhere {    overflow-wrap: anywhere  }  .p-md {    padding: var(--spacing-md)  }}
두 영역은 모두 bg-surface-notes를 사용합니다. data-theme 속성이 서로 다른 색 구성표를 선택하므로 light-dark()는 바깥에 어두운 표면, 안쪽에 밝은 표면을 제공합니다.

프리셋에는 이 두 data-theme 선택자가 이미 포함되어 있습니다. 예제 CSS에서는 활성화 방식을 보여 주려고 다시 작성했습니다. light-dark()로 정의한 사용자 지정 색상도 같은 방식으로 적응하며, 모드를 바꿔도 다시 컴파일할 필요가 없습니다. 중첩 영역은 다시 선언하지 않는 한 부모 토큰을 상속합니다. 브랜드 테마에는 [data-brand="ocean"] 같은 자체 선택자를 지정하고 그 안에서 역할 토큰을 다시 선언하세요.

@light와 @dark 접미사는 prefers-color-scheme 미디어 쿼리입니다. 수동 data-theme 설정은 토큰을 바꾸지만 해당 쿼리를 활성화하지는 않습니다. 클래스가 수동 선택에 반응해야 한다면 선택자 mixin을 선언하고 적용하세요.

index.css
@mixin --manual-dark {  &:where([data-theme="dark"], [data-theme="dark"] *) { @contents; }}
<img class="(opacity:.85)@apply(--manual-dark)" src="/chart.png" alt="Weekly usage" />

이 mixin은 실제 색 구성표가 아닌 속성을 검사하므로 중첩된 밝은 영역 안에서도 일치합니다. 적응형 토큰을 우선 사용하세요. 범위와 해석 방식은 변수와 모드를 참고하세요.

조상 선택자는 밝은 영역 안에도 도달

점선 경로는 명시적인 dark 속성을 따릅니다. 중첩된 밝은 경계는 color-scheme을 바꾸며 선택자가 검사하는 조상 관계는 바꾸지 않습니다.

텍스트 설명
  1. 이 글의 --manual-dark 믹스인은 [data-theme="dark"] 자체나 그 하위 요소에 일치합니다.
  2. 중첩된 data-theme="light" 영역 안의 차트도 바깥 dark 영역의 하위 요소입니다. 따라서 light-dark() 값이 로컬 밝은 구성표를 사용해도 불투명도 래퍼는 일치합니다.
  3. 사용하는 색 구성표를 따라야 하는 값에는 적응형 토큰을 사용하세요. 속성 선택자는 자신이 지정한 관계를 그대로 검사합니다.

가장 가까운 색 구성표가 색을 선택

중첩 경계는 color-scheme을 나타냅니다. 토큰 이름과 마크업은 그대로 유지됩니다.

텍스트 설명

밝은 문서가 어두운 영역을 감쌉니다. 같은 의미적 토큰이 바깥에서는 밝은 표면을, 영역 안에서는 어두운 표면을 그립니다.

#실제 구성에서 역할 확인

두 색 구성표에서 같은 내용을 확인하세요. 강조 및 보조 텍스트와 실제 표면의 대비, 링크의 기본 및 포커스 상태, 컨트롤을 구별하는 경계를 점검하세요. 색상 토큰은 값을 제공할 뿐, 대비나 포커스 가시성 또는 상태의 의미를 자체적으로 보장하지 않습니다.

선택 항목이나 상태에는 색상과 함께 라벨, 아이콘 또는 다른 시각적 단서를 제공하세요. 중첩 영역은 별도로 검증하세요. 어두운 페이지 안에서 수동으로 지정한 밝은 영역도 읽기 쉬운 표면과 전경의 조합이 필요합니다.