feat(skill): route fixes, delivery report, composition diversity, platform refresh

- route table: extension route for products with an existing visual
  language, feedback row, derived state tokens stay local
- completion: blocking 0 plus reported unverified; hard-gate surface is
  the touched scope; delivery report starts with what and how to open
- design.md created at step 0 with a skeleton, fingerprint line and
  change log; product-surface rubric reading; direction delegation
  states the chosen card and reason
- layout: hero families, nav forms, footer forms; art-direction page
  convergence 6 axes; genre sections; image treatments; placeholder
  conventions; multi-screen order; Tailwind theme mapping
- platform: scroll-driven examples, anchor positioning, canvas OT,
  three/R3F pins, shadcn style prefixes, Safari 26 notes, subgrid
- design-gate: four result states, exit codes, cwd module resolution,
  --url/--out-dir/--strict, oklch contrast, dead selector word match
- Korean font budget split from the Latin baseline
This commit is contained in:
Yun Chan 2026-10-06 16:55:38 +09:00
parent 9bcb45cbd3
commit 88c11682b1
51 changed files with 5834 additions and 2259 deletions

View file

@ -14,7 +14,7 @@
| 상태색·강조색의 의미와 로케일을 정한다 | §7 |
| 표준 7개 역할 밖의 색(비활성·scrim 등)이 필요하다 | §8 |
| 고채도 브랜드 색이 화면에서 탁하게 보인다 | §9 |
| 리디자인에서 기존 팔레트를 정리한다 | §10 |
| 리디자인이나 정리·일관화 작업에서 기존 팔레트를 정리한다 | §10 |
| 값을 어떤 표기법(hex·oklch 등)으로 쓸지 모르겠다 | §11 |
| 마무리 전 확인 | §12 |
@ -68,20 +68,27 @@
**절대 손이나 눈으로 계산하지 않는다.** 색 라이브러리(`culori`, `colorjs.io`, `chroma.js` 등)로 지각 공간에서 보간하고 프로젝트 표기법으로 출력한다 [SKILL-BETTER-COLORS].
```js
import { formatHex, interpolate, samples } from 'culori'
import { clampChroma, formatHex } from 'culori'
// 지각적 보간. hex로 받아 hex로 낸다
const ramp = interpolate(['#eff6ff', '#3b82f6', '#172554'], 'lab')
const steps = samples(11).map((t) => formatHex(ramp(t)))
// OKLCH에서 hue를 고정하고, 밝은 끝을 촘촘하게 둔 명도 배열을 직접 준다(원칙 4)
const L = [0.98, 0.955, 0.92, 0.86, 0.78, 0.68, 0.58, 0.49, 0.41, 0.33, 0.25]
const hue = 259, peakChroma = 0.19
const steps = L.map((l, i) => {
const t = i / (L.length - 1)
const c = peakChroma * Math.sin(Math.PI * t) + 0.01 // 채도는 중간에서 정점(원칙 3)
return formatHex(clampChroma({ mode: 'oklch', l, c, h: hue }, 'oklch')) // sRGB 밖이면 채도만 깎는다
})
```
명도 배열과 채도 곡선은 시작값이다. 브랜드 색을 핀으로 고정하면 그 스텝의 `l`·`c`를 브랜드 값으로 바꾸고 이웃을 다시 고른다.
**Tailwind `50`-`950` 11스텝, Radix `1`-`12` 12스텝 매핑은 그 체계를 이미 쓰는 프로젝트에서만 참조한다.** 이 매핑을 새 프로젝트의 기본값으로 강제하지 않는다. 두 컨벤션은 번호가 아니라 종류가 다르다 — Radix는 스텝을 역할로 정의해 다크 스케일이 같은 번호를 재사용하는 별도 램프라 `--accent-9`가 라이트·다크 양쪽에서 "솔리드 필"을 뜻하지만, Tailwind는 스텝을 명도로 정의해(`50`=밝음, `950`=어두움) 다크모드에서 매핑이 반전된다(페이지 배경이 `950`이 됨) [SKILL-BETTER-COLORS]. 양끝 모두 순수 검정·흰색에는 못 미치게 둔다 — 거기 닿는 순간 색이 페이지 배경이 사는 지점에서 정체성을 잃는다.
---
## §3. OKLCH 파생
`oklch(L C H)` — 명도(L) 0~1, 채도(C) 0~약 0.4, hue(H) 0~360, 알파는 `/`로 분리한다. 지각적으로 균일한 명도·안정적 hue·예측 가능한 램프가 장점이며, 2023년 5월부터 주요 브라우저에서 널리 지원된다(Baseline Widely) [WEB-BASELINE].
`oklch(L C H)` — 명도(L) 0~1, 채도(C) 0~약 0.4, hue(H) 0~360, 알파는 `/`로 분리한다. 지각적으로 균일한 명도·안정적 hue·예측 가능한 램프가 장점이다. Baseline 2023-05 Newly(모든 주요 엔진 지원), 2025-11경 Widely다 [WEB-BASELINE].
**hue와 chroma를 고정하고 lightness만 움직이면 라이트·다크 쌍을 체계적으로 파생할 수 있다.**
@ -95,7 +102,9 @@ const steps = samples(11).map((t) => formatHex(ramp(t)))
**진짜 새 색 시스템이면 `oklch()`가 최선의 기본값이다.** 그 외에는 색 라이브러리로 프로젝트 고유 표기법에 맞춰 같은 연산을 한다 — 표기법 선택 자체는 §11을 따른다.
`color-mix()`는 상태용 색 파생(호버·비활성 등)에 유용하며 Baseline Widely다(2023년 5월부터) [WEB-BASELINE]. 다만 생성값이 토큰 계층 밖에 놓여 디자인 툴에서 검사할 수 없다는 한계가 있으므로, 반복해서 쓰이는 값이면 토큰으로 승격할지 판단한다. 상대 색 문법(`oklch(from var(--x) calc(l - 0.1) c h)`)도 같은 종류로 강력하지만, 3중 이상 연쇄 파생은 읽을 수 없어진다 — **관찰 후보**로 두고 체인이 길어지면 고정 값으로 되돌린다 [SKILL-BETTER-COLORS].
`color-mix()`는 상태용 색 파생(호버·비활성 등)에 유용하다. Baseline 2023-05 Newly, 2025-11경 Widely다 [WEB-BASELINE]. 다만 생성값이 토큰 계층 밖에 놓여 디자인 툴에서 검사할 수 없다는 한계가 있으므로, 반복해서 쓰이는 값이면 토큰으로 승격할지 판단한다. 상대 색 문법(`oklch(from var(--x) calc(l - 0.1) c h)`)도 같은 용도로 강력하다. 다만 Baseline Newly(2024-09-16)로 `color-mix()`보다 16개월 늦어 아직 Widely가 아니다 — 지원 하한이 Safari 17 이하까지 내려가는 프로젝트는 고정 값 폴백을 먼저 선언한다[WEB-BASELINE]. 3중 이상 연쇄 파생은 읽을 수 없어진다 — **관찰 후보**로 두고 체인이 길어지면 고정 값으로 되돌린다 [SKILL-BETTER-COLORS].
`contrast-color(<배경>)`는 배경에 대해 검정·흰색 중 대비가 큰 쪽을 고른다(Baseline Newly 2026-04-10). 사용자 지정 색 위의 글자색처럼 배경을 미리 알 수 없는 자리의 폴백으로만 쓴다 — 결과가 둘뿐이라 브랜드 잉크 토큰을 대체하지 못하고, 중간 명도 배경에서는 고른 쪽도 4.5:1에 못 미칠 수 있으므로 §6의 대비 측정을 건너뛰는 근거가 되지 않는다. [WEB-BASELINE]
---
@ -116,7 +125,7 @@ const steps = samples(11).map((t) => formatHex(ramp(t)))
.c { background: linear-gradient(in oklch longer hue, #eff6ff, #3b82f6); }
```
그라디언트 보간 공간 지정 문법은 Baseline Newly available이다(2024년 6월 기준, Firefox가 마지막으로 지원을 완료) — 아직 Widely에는 이르지 않았으므로 지정하지 않았을 때의 기본 sRGB 결과가 허용 가능한지 확인하거나 `@supports` 폴백을 둔다 [WEB-BASELINE].
그라디언트 보간 공간 지정 문법은 Baseline Newly available이다(2024-06-11, Firefox가 마지막으로 지원을 완료). Widely는 그 날짜에서 30개월 뒤다 — 읽는 시점이 그 전이면 지정하지 않았을 때의 기본 sRGB 결과가 허용 가능한지 확인하거나 `@supports` 폴백을 둔다 [WEB-BASELINE].
**밴딩**은 스톱 간 대비가 낮은 넓은 영역에서 8비트 디스플레이의 계단이 드러나는 현상이다. 대비를 넓히거나 영역을 줄이거나 미세한 노이즈 텍스처를 얹는다. `shorter hue`/`longer hue` 키워드로 `oklch` 보간이 도는 방향을 고를 수 있다.
@ -138,23 +147,44 @@ const steps = samples(11).map((t) => formatHex(ramp(t)))
|---|---|---|
| `prefers-color-scheme` 단독 | 테마 토글 UI가 없을 때 | persist·hydrate할 상태가 없어야 성립한다 |
| `.dark` 클래스 | 사용자가 시스템 설정을 오버라이드할 수 있을 때 | 미디어쿼리는 초깃값만 정하고, 클래스가 최종 결정이다 |
| `light-dark()` | `color-scheme`도 함께 선언하는 프로젝트 | 클래스 기반 토글이라도 `color-scheme` 속성을 함께 갱신해야 값이 반영된다. Baseline Newly available(2024년 5월 기준, 아직 Widely 아님)이므로 지원 범위를 확인한다 [WEB-BASELINE] |
| `light-dark()` | `color-scheme`도 함께 선언하는 프로젝트 | 클래스 기반 토글이라도 `color-scheme` 속성을 함께 갱신해야 값이 반영된다. Baseline Newly available(2024-05-13. Widely는 30개월 뒤)이므로 읽는 시점이 그 전이면 지원 범위를 확인한다 [WEB-BASELINE] |
어느 행인지는 **기본 테마(시스템 따름·라이트·다크)와 사용자 토글 유무**가 정한다. 둘 다 브리프의 값이다 — 브리프에 없으면 프로젝트의 기존 테마 파일이 쓰는 방식을 따르고, 임의로 토글을 더하거나 빼지 않는다.
```css
:root {
color-scheme: light dark;
--surface: light-dark(#fbfaf8, #14130f);
--ink: light-dark(#1a1917, #e8e4dc);
--surface: light-dark(<밝은 테마 표면>, <어두운 테마 표면>);
--ink: light-dark(<밝은 테마 본문>, <어두운 테마 본문>);
}
```
값은 3단계에서 정한 것을 넣는다. 성격이 다른 표면 세 쌍의 예다 — 형식을 보이려는 것이니 그대로 쓰지 않고, 대비는 각자 다시 잰다.
- 쿨 회색: `light-dark(oklch(0.97 0.005 250), oklch(0.2 0.02 250))`
- 중간 명도 유채: `light-dark(oklch(0.86 0.06 120), oklch(0.3 0.05 120))`
- 깊은 유채 다크: `light-dark(oklch(0.95 0.02 30), oklch(0.18 0.04 300))`
**다크모드 완결성 — 값 밖의 다섯 가지.** 다크모드를 지원하기로 했다면 지킨다(프로젝트 계약). 마지막 두 줄은 관찰 후보다.
1. **`color-scheme`을 선언한다.** CSS `color-scheme`(위 예시)이나 `<meta name="color-scheme" content="light dark">`로 알려야 스크롤바·폼 컨트롤·기본 캔버스색이 테마를 따른다. 한 테마만 제공하면 그 값만 적는다.
2. **첫 페인트 전에 테마를 정한다(FOUC 방지).** 사용자 선택을 저장하는 토글이 있으면 `<head>` 안 인라인 스크립트로 저장값을 읽어 `<html>`에 클래스·`color-scheme`을 붙인 뒤 CSS가 그려지게 한다. 외부 JS나 하이드레이션 뒤에 정하면 흰 화면이 한 번 번쩍인다. 저장소 접근이 막힌 환경(try/catch)에서는 시스템 선호로 떨어진다.
```html
<script>
try { const t = localStorage.getItem("theme"); if (t) { document.documentElement.dataset.theme = t; document.documentElement.style.colorScheme = t; } } catch {}
</script>
```
3. **브라우저 UI 색을 테마마다 맞춘다.** `<meta name="theme-color" content="<밝은 테마 표면색>" media="(prefers-color-scheme: light)">`와 다크용 한 줄을 둔다. 이 값을 브라우저 탭에서 쓰는 것은 Android Chrome이고, Safari는 26부터 **설치된 웹앱에서만** 쓴다(MDN 호환성 데이터, 확인 2026-10-06)[WEB-BASELINE]. Safari 탭의 툴바 색을 정하는 규칙은 문서화되지 않았으므로 특정 색을 약속하지 않는다 — `html`·`body`에 테마별 `background-color`를 실제 표면색으로 선언하고(투명·그라디언트만 두지 않는다), 상단 고정 헤더가 있으면 iPhone 실기기의 라이트·다크에서 툴바와 어울리는지 확인한다. 사용자 토글이 시스템 선호와 다를 수 있으면 토글 때 스크립트로 `content`와 루트 배경을 함께 갱신한다.
4. **이미지·일러스트를 테마별로 본다.** 흰 바탕을 품은 PNG·스크린샷은 다크 위에서 흰 상자가 된다. `<picture><source media="(prefers-color-scheme: dark)">`로 전용 자산을 주거나, 사진은 밝기를 살짝 낮추는 필터를 후보로 둔다. 어느 쪽이든 실제 렌더로 확인한다.
5. **(관찰 후보) 순수 `#000` 표면과 다크 본문 굵기.** 순수 검정 위의 밝은 글자는 번져 보일 수 있어(할레이션) 거의 검정 표면이 흔한 시작값이다. 같은 이유로 다크에서 본문 웨이트를 한 단계 낮추는 선택이 있다. 실제 기기에서 비교해 정한다.
**테마 전환 순간 색·배경·테두리·그림자가 동시에 바뀌어 뭉개지는(smear) 것을 막는 전환 억제 레시피**는 구현 기법이므로 [motion.md](motion.md)를 따른다 — 이 문서에서는 값만 다룬다.
---
## §6. 대비
**하드 게이트: WCAG 2 대비.** 일반 텍스트(24px 미만, bold 18.5px 미만) AA 4.5:1, 큰 텍스트(24px 이상, bold 18.5px 이상) AA 3:1 [WCAG-READ]. UI 컴포넌트와 그래픽 객체는 인접 색과 3:1 — 비활성 컴포넌트, 필수 로고·브랜드 마크, 장식용 그래픽, 텍스트 대체물이 있는 그래픽은 예외다 [WCAG-NONTEXT]. 이 수치는 협상 대상이 아니다.
**하드 게이트: WCAG 2 대비.** 일반 텍스트 AA 4.5:1, 큰 텍스트(18pt≈24px 이상, 또는 bold 14pt≈18.7px 이상) AA 3:1 [WCAG-READ]. 로고·로고타입의 예외는 이 텍스트 대비(1.4.3)의 것이다. UI 컴포넌트와 이해에 필요한 그래픽 객체는 인접 색과 3:1(1.4.11) — 예외는 비활성 컴포넌트, 사용자 에이전트가 정한 외형을 작성자가 바꾸지 않은 컴포넌트, 특정 표현 자체가 정보 전달에 필수인 그래픽이다. 이해에 필요하지 않은 장식 그래픽은 처음부터 범위 밖이다 [WCAG-NONTEXT]. 이 수치는 협상 대상이 아니다.
**수정 절차: 명도를 먼저 바꾼다.** 대비가 실제로 반응하는 채널은 명도다 — hue와 채도는 측정값을 훨씬 덜 움직이므로, hue를 바꿔 대비를 고치려는 시도는 대체로 헛수고다. hue·채도를 고정한 채 전경을 배경에서 지각적으로 더 멀리 옮기고 재측정한다. hue를 고정하는 이유는 "대비 수정"이 조용히 "팔레트 변경"이 되는 것을 막기 위해서다 [SKILL-BETTER-COLORS]. APCA 기준으로는 지각 명도 약 75% 안팎 배경에서 순수 검정도 Lc 60 안팎에 그친다[SKILL-BETTER-COLORS] — WCAG 2 비율로는 이 배경에서 검정 텍스트가 여유 있게 통과하므로 두 척도를 섞지 않는다. WCAG 2 기준으로 흑·백 어느 전경도 여유가 적은 구간은 중간 회색(CIELAB L* 약 50 부근, 최대 약 4.6:1)이며, 그런 배경에서는 전경보다 배경을 바꾸는 편이 낫다. 명도를 밀면 갬멀을 벗어날 수 있으니 렌더 가능하도록 채도를 함께 낮춘다. **값을 바꾼 뒤에는 항상 재측정한다 — 고쳐졌다고 가정하지 않는다.**
@ -166,9 +196,9 @@ const steps = samples(11).map((t) => formatHex(ramp(t)))
## §7. 의미와 상태색
**시맨틱 색을 비파괴 액션에 쓰지 않는다.** 상태색(`danger`, `success` 등)은 그 상태를 실제로 표시하는 곳에만 쓴다. 삭제 같은 파괴적 행동이 아닌 일반 강조에 `danger` 색을 빌려 쓰면 사용자가 실제로 위험한 것으로 오독한다. **프로젝트 계약**: 하나의 색은 하나의 의미만 진다 — hue 15도 이내는 같은 색으로 취급하므로, 강조색이 "인터랙티브"를 뜻하면 그 hue가 정적 텍스트에 쓰이는 순간 클릭 불가능한 것을 클릭하라고 말하는 셈이다 [SKILL-BETTER-COLORS].
**시맨틱 색을 비파괴 액션에 쓰지 않는다.** 상태색(`danger`, `success` 등)은 그 상태를 실제로 표시하는 곳에만 쓴다. 삭제 같은 파괴적 행동이 아닌 일반 강조에 `danger` 색을 빌려 쓰면 사용자가 실제로 위험한 것으로 오독한다. **프로젝트 계약**: 하나의 색은 하나의 의미만 진다 — 역할이 같으면 같은 토큰을 쓴다. 강조색이 "인터랙티브"를 뜻하면 그 색이 정적 텍스트에 쓰이는 순간 클릭 불가능한 것을 클릭하라고 말하는 셈이다. **관찰 후보**: hue가 15도 안팎으로 가깝고 명도·채도도 비슷한 두 색은 사용자에게 같은 색으로 읽힐 수 있으니 의미가 다르면 명도·채도까지 벌린다(외부 스킬 관찰값, 근거 미검증) [SKILL-BETTER-COLORS].
**뷰당 채색된 주요 액션은 하나다.** 채운 색이 주요 강조를 인코딩할 때는 뷰당 주 행동 하나만 색을 채우고 나머지는 중성으로 둔다. 색은 배경에 올리지 라벨에 올리지 않는다 — 채운 버튼은 방 건너편에서도 주요 행동으로 읽히지만, 중성 버튼 위 강조색 텍스트는 링크로 읽힌다. 서로 다른 상태·카테고리를 인코딩하는 경우라면 여러 색 배경도 괜찮다 [SKILL-BETTER-COLORS]. **프로젝트 계약**.
**결정 단위당 채운 주요 액션은 하나다.** 결정 단위는 사용자가 한 번에 하나를 고르는 범위다 — 히어로 하나, 폼 하나, 다이얼로그 하나, 반복 모듈 하나. 채운 색이 주요 강조를 인코딩할 때는 결정 단위마다 주 행동 하나만 색을 채우고 나머지는 중성으로 둔다. 상품 카드별 "담기"·요금제 열별 가입처럼 **반복 모듈은 모듈마다 하나**가 정상 관례이며, 요금제는 추천 열만 채우고 나머지는 외곽선으로 두는 것을 시작값으로 한다. 구매/정기구독처럼 결과가 다른 두 경로는 세그먼트 선택 + 단일 CTA와 병렬 두 CTA 중 브리프의 목표 행동 우선순위로 고른다. 색은 배경에 올리지 라벨에 올리지 않는다 — 채운 버튼은 방 건너편에서도 주요 행동으로 읽히지만, 중성 버튼 위 강조색 텍스트는 링크로 읽힌다. 서로 다른 상태·카테고리를 인코딩하는 경우라면 여러 색 배경도 괜찮다 [SKILL-BETTER-COLORS]. **프로젝트 계약**.
**색만으로 의미를 전달하지 않는다.** 이 규칙 자체(비색각 사용자·회색조 인쇄를 위한 아이콘·라벨 병기)는 하드 게이트이며 정본은 [accessibility.md](accessibility.md)다.
@ -198,7 +228,7 @@ const steps = samples(11).map((t) => formatHex(ramp(t)))
> 언제: 고채도 브랜드 색이 화면에서 탁하거나 밋밋해 보일 때만 연다(점진적 향상).
모든 sRGB 색은 Display P3 색역 안에 있지만 역은 성립하지 않는다. P3는 sRGB보다 약 50% 더 많은 색을 커버하며, 그 차이는 가장 채도 높은 값에서만 의미가 있다 — 최대 생생함의 60% 이하인 색은 양쪽 색역에서 똑같아 보인다 [SKILL-BETTER-COLORS].
모든 sRGB 색은 Display P3 색역 안에 있지만 역은 성립하지 않는다. 차이는 채도가 높은 색에서만 의미가 있다 — 낮은 채도의 색은 두 색역에서 같게 보이므로 P3 값을 따로 둘 이유가 없다 [SKILL-BETTER-COLORS].
**순서가 중요하다.** sRGB 값을 먼저 선언하고, `@media (color-gamut: p3)` 안에서 더 채도 높은 P3 값으로 오버라이드한다.
@ -219,7 +249,7 @@ const steps = samples(11).map((t) => formatHex(ramp(t)))
## §10. 기존 팔레트 감사 5단계
> 언제: 리디자인에서 팔레트를 재구조화하기 전에 연다.
> 언제: 리디자인이나 정리·일관화 작업에서 팔레트를 재구조화하기 전에 연다.
1. **모든 리터럴을 수집한다.** `hex`, `rgb(`, `hsl(`, `oklch(`, 프로젝트 유틸리티 클래스 접두어를 grep한다. 스타일시트뿐 아니라 SVG `fill`/`stroke`, 차트 설정, 이메일 템플릿까지 포함한다 — 색은 스타일시트 밖에도 숨는다.
2. **각 hue 계열 안에서 지각된 명도로 정렬한다.** 근접 중복이 이웃으로 즉시 드러난다.
@ -243,17 +273,18 @@ const steps = samples(11).map((t) => formatHex(ramp(t)))
---
## §12. 체크리스트
## §12. 구현 중 자기 점검 (납품 판정은 preflight.md X6·A3·A24)
- [ ] `tokens.md` §2의 역할 토큰이 먼저 채워져 있다. 원시값 계층은 다중 hue·정밀 다크모드가 실제로 필요할 때만 추가했다.
- [ ] 라이트·다크 두 외형 모두 실제 렌더 배경에 대해 대비를 측정했다(WCAG 2, 하드 게이트).
- [ ] 대비를 고쳤다면 명도를 먼저 바꾸고 hue를 고정했으며, 값을 바꾼 뒤 재측정했다.
- [ ] 그라디언트를 썼다면 보간 공간을 의도적으로 골랐고, Newly available 문법에는 결과가 허용 가능한 기본 sRGB 폴백이 있다.
- [ ] 다크모드를 반전이 아니라 재조정했다(생생함 낮추기·어두운 끝 분리·대비 비대칭 재검사 3항목).
- [ ] 전환 메커니즘을 하나만 골라 전체에 썼다(미디어쿼리·클래스·`light-dark()` 혼용 없음).
- [ ] 상태색·강조색이 하나의 색=하나의 의미를 지킨다. 뷰당 채색된 주요 액션이 하나다.
- [ ] 색만으로 의미를 전달하는 곳이 없다([accessibility.md](accessibility.md)).
- [ ] 손익·상태처럼 문화적으로 뒤집힐 수 있는 색은 데이터 제공자·브리프의 실제 관례를 확인했다(지어내지 않았다).
- [ ] P3를 썼다면 sRGB 폴백이 먼저 선언돼 있다.
- [ ] 리디자인이면 기존 팔레트를 5단계로 감사해 인벤토리를 먼저 보고했다.
- [ ] 표기법은 프로젝트 것을 따랐고, 이 문서를 열었다는 이유만으로 값을 변환하지 않았다.
납품 판정은 [preflight.md](preflight.md) X6이 하고, 라이트·다크 대비 측정은 A3, 색만으로 의미 전달 금지는 A24가 판정한다.
- `tokens.md` §2의 역할 토큰이 먼저 채워져 있다. 원시값 계층은 다중 hue·정밀 다크모드가 실제로 필요할 때만 추가했다.
- 대비를 고쳤다면 명도를 먼저 바꾸고 hue를 고정했으며, 값을 바꾼 뒤 재측정했다.
- 그라디언트를 썼다면 보간 공간을 의도적으로 골랐고, Newly available 문법에는 결과가 허용 가능한 기본 sRGB 폴백이 있다.
- 다크모드를 반전이 아니라 재조정했다(생생함 낮추기·어두운 끝 분리·대비 비대칭 재검사 3항목).
- 전환 메커니즘을 하나만 골라 전체에 썼다(미디어쿼리·클래스·`light-dark()` 혼용 없음).
- 다크모드를 지원하면 `color-scheme` 선언, 첫 페인트 전 테마 결정(번쩍임 없음), 테마별 `theme-color`, 테마별 이미지 확인을 마쳤다(§5).
- 상태색·강조색이 하나의 색=하나의 의미를 지킨다. 결정 단위당 채운 주요 액션이 하나다(반복 모듈은 모듈마다 하나).
- 손익·상태처럼 문화적으로 뒤집힐 수 있는 색은 데이터 제공자·브리프의 실제 관례를 확인했다(지어내지 않았다).
- P3를 썼다면 sRGB 폴백이 먼저 선언돼 있다.
- 리디자인이면 기존 팔레트를 5단계로 감사해 인벤토리를 먼저 보고했다.
- 표기법은 프로젝트 것을 따랐고, 이 문서를 열었다는 이유만으로 값을 변환하지 않았다.