- Restore the step-0 interview as a mechanical gate the skill explicitly depends on; add harness.md (per-harness question tools, limits, fallbacks) and brief-interview.md (slots, question cards, rounds). - Add 10 reference docs absorbed from external design skills (accessibility, interaction-feel, elevation, color, icons, product-copy, component-systems, critique, change-review, print-email) and extend existing references. - Add a review-only route and two hard-gate clauses (truncated content reachability, three-flashes limit). - design-gate: split tap targets into WCAG 2.5.8 and 44px contract layers, run axe-core when available, and fix false positives found on a real site (decorative alt="", stacked wordmark line count, url-only pages). - lint-skill: fail if the interview gate section or its links disappear. - Ship agents/openai.yaml and THIRD_PARTY_NOTICES.md.
13 KiB
elevation — 그림자·깊이·머티리얼
3단계에서 토큰을 정할 때, 4-2 재질에서 읽는다.
tokens.md §3-b(형태)는 radius·line-width만 정의하고 그림자 토큰 자리가 비어 있다. 이 문서가 그 자리를 채운다. --dur-*·--ease-*는 여기서 새로 만들지 않고 tokens.md §4에 정의된 값을 그대로 재사용한다.
이 문서를 읽는 법
| 상황 | 읽을 곳 |
|---|---|
| 그림자 토큰 3단만 빨리 정하면 된다 | §2 |
| radius와 그림자를 같이 정의해야 한다 | §3 |
| 테두리 대신 그림자를 쓸지 고민 중이다 | §4 |
| 다크 모드에서 그림자가 안 보인다 | §5 |
| 카드·리스트가 슬롭처럼 보인다 | §6 |
| 브리프가 유리·Apple풍 질감을 요구한다 | §7 |
prefers-reduced-transparency·forced-colors 대응이 필요하다 |
§8 |
| 커밋 전 확인 목록이 필요하다 | §9 |
0. 언제, tokens.md와의 관계
그림자·깊이 체계는 3단계에서 토큰과 함께 정하고, 4단계 구현에서 그대로 꺼내 쓴다. tokens.md는 타입·색·간격·형태·모션 토큰의 정본이고, 이 문서는 그중 형태(§3-b) 옆에 있어야 할 그림자·머티리얼 절만 담당한다. 토큰 이름은 tokens.md가 색에서 쓰는 역할 기반 명명 관례(--surface, --surface-raised)를 그대로 따라, "얼마나 떠 있는가"라는 역할로 짓는다. 팔레트식 이름(--shadow-1, --shadow-2)은 쓰지 않는다.
1. 원칙
아래 원칙은 관찰 후보다.
- 광원은 하나로 통일한다. 모든 그림자는 화면 위쪽에서 아래로 비추는 하나의 광원을 가정한다.
offset-y가 음수인 그림자, 여러 방향에서 비추는 그림자를 같은 화면에 섞으면 표면이 물리적으로 말이 안 되게 읽힌다. - 깊이는 위계 정보다. 그림자 강도는 장식이 아니라 "이 표면이 다른 표면보다 얼마나 위에 있는가"를 전달하는 신호다. 강도를 정보 없이 임의로 섞으면 신호가 사라진다.
- 그림자 체계는 하나다. 한 프로젝트 안에서 정의한 다층 그림자 토큰과 프레임워크 기본 그림자(
shadow-lg등)를 섞어 쓰지 않는다. 코너 반경·그림자 단계·아이콘 스트로크 굵기가 화면 전체에서 같은 체계로 파생됐는지는 감사 항목이다 [SKILL-DESIGN-REVIEW][SKILL-BEAUTIFUL-SHADOWS].
2. 다층 그림자 시작값 3단
아래 값은 관찰 후보다. 브리프·기존 토큰이 침묵할 때의 시작값이며, 렌더로 확인하고 조정한다. Tailwind 임의값 문법이 아니라 순수 CSS 커스텀 프로퍼티로 옮긴다 — designpaca는 프레임워크를 전제하지 않는다.
:root {
/* 컴팩트 카드, 폼 컨트롤, 필(pill) — 조용한 표면 */
--shadow-resting:
0px 2px 3px -1px rgba(0, 0, 0, 0.1),
0px 1px 0px 0px rgba(25, 28, 33, 0.02),
0px 0px 0px 1px rgba(25, 28, 33, 0.08);
/* 카드, 패널, 팝오버 — 기본 elevated 표면 */
--shadow-raised:
0px 0px 0px 1px rgba(0, 0, 0, 0.06),
0px 1px 1px -0.5px rgba(0, 0, 0, 0.06),
0px 3px 3px -1.5px rgba(0, 0, 0, 0.06),
0px 6px 6px -3px rgba(0, 0, 0, 0.06),
0px 12px 12px -6px rgba(0, 0, 0, 0.06),
0px 24px 24px -12px rgba(0, 0, 0, 0.06);
/* 히어로 미디어, feature callout, 모달류 — 가장 강한 lift */
--shadow-overlay:
0 2.8px 2.2px rgba(0, 0, 0, 0.034),
0 6.7px 5.3px rgba(0, 0, 0, 0.048),
0 12.5px 10px rgba(0, 0, 0, 0.06),
0 22.3px 17.9px rgba(0, 0, 0, 0.072),
0 41.8px 33.4px rgba(0, 0, 0, 0.086),
0 100px 80px rgba(0, 0, 0, 0.12);
}
값과 레이어 구조의 출처: [SKILL-BEAUTIFUL-SHADOWS]. --shadow-raised는 링 1겹 뒤로 offset·blur·spread가 1→3px 다음부터 2배씩(3·6·12·24px) 커지는 레이어이고, --shadow-overlay는 레이어가 멀어질수록(더 흐려질수록) 알파가 0.034→0.12로 비선형으로 진해진다. 레이어가 늘수록 왜 값이 커지고 진해지는지에 대한 근거는 관찰 후보이며 원 출처가 확인되지 않았다 — 자연광 산란을 흉내 낸 설계로 보이지만 이 문장 자체를 근거로 인용하지 않는다.
밀도 → 단계 매핑
| 표면 | 토큰 | 밀도 |
|---|---|---|
| 폼 컨트롤, 배지, 필, 컴팩트 카드 | --shadow-resting |
조용함 |
| 카드, 패널, 팝오버, 드롭다운 | --shadow-raised |
기본 |
| 히어로 미디어, feature callout, 모달, 시트 | --shadow-overlay |
강함 |
인터랙션이 명확히 elevation을 바꾸는 경우(호버로 카드가 뜨는 등)가 아니면, 같은 컴포넌트의 한 상태에는 그림자 토큰을 하나만 쓴다 [SKILL-BEAUTIFUL-SHADOWS]. elevation이 상태에 따라 바뀔 때는 새 duration 토큰을 만들지 않고 tokens.md의 --dur-instant·--ease-out을 그대로 재사용해 전환한다.
3. radius·표면과 짝짓기
그림자 프리셋은 깨끗한 표면 채움(fill)과 일관된 radius를 항상 함께 정의한다 [SKILL-BEAUTIFUL-SHADOWS]. 같은 컴포넌트에서 그림자만 바꾸고 radius·표면색을 따로 관리하면 값이 어긋난다.
중첩된 표면의 radius는 동심원 관계로 계산하는 것이 시작 규칙이다.
외곽 radius = 내부 radius + 둘 사이 padding
패딩이 24px를 넘으면 동심 계산을 포기하고 두 표면을 독립된 radius로 다룬다 [SKILL-BETTER-UI]. radius 토큰 자체("서로 다른 radius 값은 3종 미만으로")는 tokens.md §3-b가 정본이다.
4. 테두리를 대신하는 그림자
언제: 카드·버튼·컨테이너의 깊이·elevation을 표현하는 테두리를 옅은 box-shadow로 대체할 때 쓴다. 구분선(border-top/border-bottom), 셀 경계, selected·focus 상태를 나타내는 테두리에는 적용하지 않는다 — 그 용도는 레이아웃 분리이지 깊이가 아니다 [SKILL-BETTER-UI].
그림자는 투명도를 쓰므로 색이 고정된 border와 달리 어떤 배경 위에서도 자연스럽게 섞인다. 이미지나 여러 배경색 위에 놓이는 표면일수록 이 대체가 유효하다.
:root {
/* 라이트 모드 — 3겹: 1px 링 + 미세한 lift + ambient 깊이 */
--shadow-border:
0px 0px 0px 1px oklch(0 0 0 / 0.06),
0px 1px 2px -1px oklch(0 0 0 / 0.06),
0px 2px 4px 0px oklch(0 0 0 / 0.04);
--shadow-border-hover:
0px 0px 0px 1px oklch(0 0 0 / 0.08),
0px 1px 2px -1px oklch(0 0 0 / 0.08),
0px 2px 4px 0px oklch(0 0 0 / 0.06);
}
@media (prefers-color-scheme: dark) {
:root {
/* 다크 모드 — 레이어드 그림자는 어두운 배경에서 거의 안 보인다.
흰 링 1겹으로 단순화한다. */
--shadow-border: 0 0 0 1px oklch(1 0 0 / 0.08);
--shadow-border-hover: 0 0 0 1px oklch(1 0 0 / 0.13);
}
}
.card {
box-shadow: var(--shadow-border);
transition-property: box-shadow;
transition-duration: var(--dur-instant);
transition-timing-function: var(--ease-out);
}
.card:hover { box-shadow: var(--shadow-border-hover); }
값과 라이트 3겹·다크 1겹 구조의 출처: [SKILL-BETTER-UI]. transition-duration은 원문의 150ms를 그대로 새 값으로 두지 않고 tokens.md의 폐쇄형 duration 토큰(--dur-instant = 100ms)에 매핑했다 — 근거 없는 새 리터럴을 만들지 않는다는 원칙에 따른 실측 조정이다.
5. 다크모드의 깊이
다크 배경에서는 밝은 배경보다 그림자의 명도 대비가 약해 레이어드 그림자가 잘 읽히지 않는다(§4의 "다크 1겹 단순화"가 같은 이유다). 다크 테마의 elevation 위계는 그림자보다 표면 밝기 차이로 먼저 전달한다 — --surface, --surface-raised 같은 역할 토큰(tokens.md §2)을 elevation 단계가 올라갈수록 조금씩 밝게 다시 정의하고, --shadow-border의 흰 링은 경계를 보조하는 역할로만 둔다. 순수 검정에 가까운 표면 위에 그림자만으로 3단 위계를 만들려 하지 않는다.
6. 오용 휴리스틱
아래는 모두 관찰 후보다. 발견 자체가 실패가 아니라, 브리프·실제 렌더로 유지·수정 여부를 판단하는 점검 대상이다.
| 지문 | 왜 문제 | 대신 |
|---|---|---|
| 정의한 다층 그림자 체계와 프레임워크 기본 그림자를 한 화면에 섞어 씀 | 위계 신호가 두 체계로 나뉘어 무엇이 무엇보다 위인지 읽히지 않는다 | §2의 3단 토큰 하나만 쓴다 [SKILL-BEAUTIFUL-SHADOWS] |
강한 색으로 그림자를 틴팅함(rgba(139,92,246,.5)류) |
깊이 신호가 브랜드 강조처럼 읽혀 의미가 겹친다 | 브랜드 토큰이 틴팅을 의도적으로 요구할 때만 예외로 둔다. 중립이 기본값이다 [SKILL-BEAUTIFUL-SHADOWS] |
밀집 리스트·그리드의 각 행에 --shadow-overlay급 강한 그림자를 반복 |
시각 노이즈가 쌓이고 리스트 스크롤에서 페인트 비용이 누적된다 | 밀집 목록에는 --shadow-resting이나 얇은 경계를 쓴다 [SKILL-BEAUTIFUL-SHADOWS] |
| 한 요소에 그림자 토큰 여러 개를 동시에 쌓음 | 어느 레이어가 진짜 위계 신호인지 불명확해진다 | 상태당 그림자 토큰 하나 원칙(§2)을 지킨다 [SKILL-BEAUTIFUL-SHADOWS] |
| 대비가 낮은 배경에서 명확한 경계선 대신 옅은 그림자만으로 표면을 구분 | 저대비 환경·확대·forced-colors에서 경계 자체가 사라질 수 있다 | 저대비 배경에는 --line 토큰의 실제 테두리를 함께 쓴다 [SKILL-BEAUTIFUL-SHADOWS] |
| 여러 카드가 위계와 무관하게 같은 radius·같은 흐린 회색 그림자를 반복(카드 키트 지문) | 콘텐츠가 다른데 표면 처리가 같으면 임의 템플릿으로 읽힌다 | radius·그림자 단계를 콘텐츠 밀도·중요도와 연결해 다시 정한다 [SKILL-FRONTEND-DESIGN] |
7. 머티리얼 위계
언제: 브리프나 프리셋이 유리·Apple풍 질감을 허용할 때만 연다. swiss-minimal처럼 재질을 배제하는 프리셋에는 적용하지 않는다(svg-filters.md §1의 프리셋별 재질 표를 먼저 확인한다).
이 절은 반투명 표면의 깊이 위계를 다룬다. backdrop-filter 구현 자체(변위맵, Safari 폴백, 성능 최적화)와 위계·가독성(vibrancy)·스티키 헤더 마스크·진입 애니메이션의 상세 규칙·코드는 svg-filters.md "머티리얼 위계와 가독성" 절이 정본이다 — 여기서 중복 정의하지 않는다. 핵심만 요약하면, 무겁고 어두운(강한 블러+짙은 배경) 재질은 사이드바 같은 구조적 영역에, 가볍고 밝은 재질은 버튼 같은 인터랙티브 요소에 쓰고, 표면이 클수록 재질도 두껍게 읽혀야 한다 — 머티리얼 무게가 위계를 인코딩한다는 것이 이 절의 한 줄 요약이다[SKILL-APPLE-DESIGN].
8. 선호 신호
| 미디어 쿼리 | 위계 | 대응 |
|---|---|---|
prefers-reduced-transparency: reduce |
프로젝트 계약(§7 적용 시) | 반투명 표면의 배경 불투명도를 높이고 블러를 낮춰 더 서리 낀 상태로 전환한다. 2026-09-24 기준 Chrome/Edge 119만 구현했고 Firefox·Safari는 미지원이라 점진적 향상으로 다룬다 — 미지원 브라우저에서는 아무 효과가 없을 뿐 오류가 나지 않는다 [WEB-BASELINE] |
prefers-contrast: more |
프로젝트 계약 | 거의 불투명한 배경과 뚜렷한 테두리로 전환한다. 2022년 5월부터 주요 브라우저 전반에서 지원되는 안정 기능이다 [WEB-BASELINE] |
forced-colors: active |
하드 게이트 | 강제 색상 모드에서는 box-shadow가 none으로 강제되어 사라진다(대체되지 않는다). border 등 색은 시스템 팔레트로 바뀐다. 그림자만으로 표시한 경계는 이 모드에서 사라지므로, §4의 --shadow-border로만 경계를 나타낸 표면에는 실제 border나 outline을 함께 정의해 forced-colors에서도 경계가 남게 한다. forced-colors는 2022년 9월부터 안정 지원된다 [WEB-BASELINE][WCAG-NONTEXT] |
@media (prefers-reduced-transparency: reduce) {
.glass { background: var(--surface-raised); backdrop-filter: none; }
}
@media (prefers-contrast: more) {
.card { border: 1px solid var(--line); }
}
@media (forced-colors: active) {
.card { border: 1px solid CanvasText; box-shadow: none; }
}
9. 검증
- 두 테마에서 그림자·표면 밝기가 elevation 단계를 실제로 구분해 보이는지 확인한다(§5).
backdrop-filter를 쓴 표면에는 최악의 배경 콘텐츠(다색 이미지, 밝은 텍스트가 섞인 스크린샷)를 뒤에 두고 vibrancy 처리로 텍스트가 읽히는지 확인한다(§7).- 밀집 목록·그리드에서 그림자를 반복 적용했을 때 시각 노이즈나 스크롤 프레임 저하가 없는지 확인한다(§6).
- 200% 확대와
forced-colors·필터를 전부 끈 상태(svg-filters.md §7의 감사 주입 패턴)에서 표면 경계가 여전히 구분되는지 확인한다(§8).