03 — Shadows (box·text·inset·multi)
한 줄 답:
box-shadow와text-shadow는 모두<offset-x> <offset-y> <blur> [spread] <color>의 5축 함수이고, 콤마로 여러 개를 겹쳐 깊이·층·발광·신경모피즘을 만든다.inset키워드로 안쪽 그림자가 되며, 모던 패턴은 drop-shadow filter·gradient border와 함께 stacking된다.
Why — 왜 shadow가 시각 위계의 핵심인가
화면은 2D지만 사람의 인지는 z축의 깊이를 본다. UI에서 깊이를 만드는 도구는 셋이다:
- Shadow — 광원이 있다는 가정, 가장 보편적.
- Filter blur — 거리에 따라 흐려진다는 가정, 모달·툴팁 배경.
- Color contrast / lightness — 가까운 건 밝다는 가정.
이 중 shadow가 비용이 가장 싸다 (paint 단계, blur는 합성). 그래서 디자인 시스템의 elevation token은 거의 모두 box-shadow로 구현된다 (Material, iOS, Tailwind, Open Props).
How — 다섯 인자와 스택 순서
다섯 인자
box-shadow: <offset-x> <offset-y> <blur-radius> <spread-radius> <color> [inset];
text-shadow: <offset-x> <offset-y> <blur-radius> <color>; /* spread, inset 없음 */| 인자 | 단위 | 효과 |
|---|---|---|
| offset-x | px/rem | 양수=오른쪽, 음수=왼쪽 |
| offset-y | px/rem | 양수=아래, 음수=위 |
| blur-radius | px | 0=선명, 클수록 흐림. 음수 안 됨. |
| spread-radius | px | 양수=더 크게, 음수=작게. box-shadow에만. |
| color | any | 안 적으면 currentColor |
| inset | keyword | 안쪽 그림자 |
스택 순서 (콤마로 여러 개)
box-shadow:
0 1px 2px rgb(0 0 0 / 0.1), /* 가까운, 선명한 그림자 */
0 4px 8px rgb(0 0 0 / 0.06), /* 중간 거리 */
0 16px 32px rgb(0 0 0 / 0.04); /* 멀고 부드러운 */먼저 적은 게 위에 그려진다. (z-index 순서와 반대 아님 — 적힌 순서대로 layer가 쌓임.)
What — 자주 쓰는 6가지 패턴
1) Elevation — Material/Tailwind 스타일
:root {
--shadow-sm: 0 1px 2px rgb(0 0 0 / 0.05);
--shadow: 0 1px 3px rgb(0 0 0 / 0.1),
0 1px 2px rgb(0 0 0 / 0.06);
--shadow-md: 0 4px 6px rgb(0 0 0 / 0.07),
0 2px 4px rgb(0 0 0 / 0.06);
--shadow-lg: 0 10px 15px rgb(0 0 0 / 0.1),
0 4px 6px rgb(0 0 0 / 0.05);
--shadow-xl: 0 20px 25px rgb(0 0 0 / 0.1),
0 10px 10px rgb(0 0 0 / 0.04);
}
.card { box-shadow: var(--shadow-md); }두 개 그림자가 표준: 가까운 작은 그림자가 윤곽을 살리고, 먼 큰 그림자가 분위기를 만든다. 하나만 쓰면 항상 어색하다.
2) Colored Shadow — 색 있는 그림자
.btn-primary {
background: oklch(62% 0.21 254);
box-shadow: 0 8px 24px oklch(62% 0.21 254 / 0.4);
}
/* relative color로 자동 추적 */
.btn {
--c: oklch(62% 0.21 254);
background: var(--c);
box-shadow: 0 8px 24px oklch(from var(--c) l c h / 0.35);
}회색 그림자만 쓰면 진흙 같다. 요소의 색과 같은 hue·낮은 알파가 사실상 표준 — 발광하는 느낌.
3) Inset Shadow — 안쪽
/* 눌린 버튼 */
.pressed {
background: oklch(95% 0 0);
box-shadow:
inset 0 1px 2px rgb(0 0 0 / 0.1),
inset 0 4px 8px rgb(0 0 0 / 0.05);
}
/* input focus 안쪽 글로우 */
.input:focus {
box-shadow: inset 0 0 0 2px oklch(62% 0.21 254);
}inset은 border 안쪽에 그려진다. focus ring을 안쪽으로 넣어 레이아웃 변동 없이 강조하는 트릭에 자주 쓰인다.
4) Neumorphism — 두 방향 그림자
.neumorph {
background: oklch(94% 0.005 250);
box-shadow:
8px 8px 16px oklch(85% 0.005 250), /* 우하단 어두운 그림자 */
-8px -8px 16px oklch(99% 0.005 250); /* 좌상단 밝은 하이라이트 */
border-radius: 16px;
}좌상에서 빛이 오는 가정 → 우하에 어둠, 좌상에 밝음. 2020년 한때 유행, 지금은 접근성(콘트라스트 부족) 때문에 제한적으로 쓰임.
5) Multi-layer Glow — 발광
.glow {
--c: oklch(72% 0.2 280);
box-shadow:
0 0 5px var(--c),
0 0 10px var(--c),
0 0 20px var(--c),
0 0 40px oklch(from var(--c) l c h / 0.5);
}같은 색을 blur 반경만 다르게 4단계로 겹치면 네온 발광 효과. 다크 테마의 강조 요소·로딩 인디케이터에 자주.
6) Text Shadow — 가독성과 임팩트
/* 텍스트 가독성 — 흐린 배경 위에 */
.title {
text-shadow: 0 1px 2px rgb(0 0 0 / 0.5);
}
/* 다중 stroke (윤곽선 흉내) */
.outlined {
text-shadow:
-1px -1px 0 black,
1px -1px 0 black,
-1px 1px 0 black,
1px 1px 0 black;
}
/* 모던 대안: -webkit-text-stroke: 1px black */text-shadow는 spread, inset이 없다. 그라데이션 텍스트도 못 만든다 — 그건 background-clip: text의 영역 (06-background).
What-if — 잘못 쓰면
1) box-shadow를 transition하면 페인트 폭발
/* 나쁨 — hover마다 paint */
.card {
box-shadow: var(--shadow);
transition: box-shadow 0.2s;
}
.card:hover {
box-shadow: var(--shadow-lg);
}
/* 좋음 — pseudo 요소 + opacity transition */
.card {
position: relative;
box-shadow: var(--shadow);
}
.card::after {
content: '';
position: absolute;
inset: 0;
border-radius: inherit;
box-shadow: var(--shadow-lg);
opacity: 0;
transition: opacity 0.2s;
pointer-events: none;
}
.card:hover::after { opacity: 1; }box-shadow transition은 매 프레임 다시 페인트한다. 큰 카드 리스트의 hover에서 jank가 잘 생긴다. ::after로 미리 그려두고 opacity만 트랜지션하면 합성 단계로 옮겨져 60fps 유지.
2) text-shadow로 그라데이션 텍스트?
/* 안 됨 */
.gradient-text {
text-shadow: 0 0 0 linear-gradient(red, blue); /* 문법 자체 안 됨 */
}
/* 해답: background-clip: text */
.gradient-text {
background: linear-gradient(in oklch, red, blue);
-webkit-background-clip: text;
background-clip: text;
color: transparent;
}text-shadow는 단색 + offset만 가능. 그라데이션 텍스트는 06-background의 background-clip: text로.
3) filter: drop-shadow vs box-shadow
/* box-shadow — *상자* 모양 따라 그림자 */
.png {
background: url(transparent.png);
box-shadow: 0 8px 16px rgb(0 0 0 / 0.2);
/* 사각형 상자 그림자 — PNG 투명 영역도 포함 */
}
/* drop-shadow — *알파 채널* 모양 따라 그림자 */
.png {
filter: drop-shadow(0 8px 16px rgb(0 0 0 / 0.2));
/* PNG 실제 모양대로 그림자 */
}투명 PNG·SVG·clip-path로 모양 자른 요소는 filter: drop-shadow 가 맞다. 단, filter는 stacking context를 만들기 때문에 z-index 동작이 달라진다 (04-filters).
4) Spread 음수의 바깥 contour 트릭
/* 상자 위쪽만 그림자 */
.top-shadow-only {
box-shadow: 0 -10px 20px -5px rgb(0 0 0 / 0.3);
/* spread -5px → 그림자 자체가 5px 작아짐 → 좌우/하단은 안 보임 */
}음수 spread는 그림자를 작게 만들어 특정 방향만 보이게 하는 트릭. 상단만, 하단만, 한 쪽만 그림자를 그릴 때 사실상 표준.
5) currentColor로 자동 추적
.alert {
color: oklch(60% 0.2 25);
box-shadow: 0 4px 12px currentColor; /* color가 바뀌면 그림자도 함께 */
}color 한 줄만 바꿔도 border, shadow, gradient(background-clip: text로 만든) 가 함께 따라온다. 테마 시스템의 비밀 무기.
Insight — Shadow의 진화
“2010년대 초 skeuomorphism → flat → material → neumorphism → glassmorphism의 10년”
2007년 iPhone OS의 skeuomorphic(가죽·금속 텍스처)은 다중 그림자의 시대를 열었다. 2013년 iOS 7의 flat design은 그림자를 거의 제거했다. 2014년 Material Design은 elevation이라는 z축 메타포로 그림자를 수학적으로 복귀시켰다 — “높이가 곧 그림자 크기”.
2018년 카드 디자인이 보편화되면서 Tailwind shadow-md / lg / xl이 사실상 표준이 됐다. 흥미로운 점은 Tailwind v1~v3의 그림자가 모두 경험적으로 튜닝된 값이라는 것 — 디자이너가 눈으로 결정한 두 개 그림자의 조합이 사실상 표준이 됐다.
2020년 한때 Neumorphism(soft UI)이 유행했지만, 어두운 그림자 + 밝은 하이라이트의 콘트라스트가 WCAG AA를 못 맞춰서 메인스트림에서 빠르게 사라졌다. 같은 시기 Glassmorphism(backdrop-filter + 반투명 + 약한 그림자) 이 등장해 macOS Big Sur·iOS 7 부활이라 불렸다.
2024년 OKLCH 도입 이후 colored shadow가 다시 주목받는다 — oklch(from var(--c) l c h / 0.4)로 요소 색을 자동 추적하는 그림자가 가능해졌기 때문.
또 하나의 반전: box-shadow는 GPU 가속을 거의 안 받는다. blur가 들어가면 paint 비용이 blur 반경의 제곱에 비례한다. 큰 카드의 큰 그림자는 생각보다 비싸다 — 100개 그릴 거면 SVG·이미지로 대체하는 게 빠르다.
요약 + Mermaid
box-shadow= offset-x · offset-y · blur · spread · color · [inset].text-shadow= spread / inset 없음. 그라데이션 텍스트는background-clip: text로.- 두 개 그림자가 사실상 표준 — 가까운 + 먼.
- Colored shadow는 요소 색 hue + 낮은 알파가 모던 패턴.
transition: box-shadow는 paint 폭발 — pseudo 요소 + opacity transition으로.- 투명 영역 있는 요소(SVG·PNG)는
filter: drop-shadow사용.
다음: 04-filters — drop-shadow도 filter의 일종이다. blur·brightness·backdrop의 GPU 비용도 함께.