07 — Borders & Outlines (focus ring)
한 줄 답:
border는 박스의 일부(레이아웃 차지)이고,outline은 박스 밖의 시각 효과(레이아웃 무관)다. 한 줄 차이 때문에 포커스 링은outline의 영역이며, 모던 사실상 표준은:focus-visible+outline-offset+outline-color의 3중 조합이다. 그라데이션 border는border-image또는 gradient padding-box / conic border-box 트릭으로.
Why — 왜 border와 outline을 구분하나
표면적으로 비슷해 보이지만 본질이 다르다:
| 속성 | 레이아웃 | 모서리 | 위치 | 다중 |
|---|---|---|---|---|
border | 차지함 (박스 크기에 포함) | border-radius 따름 | 박스 안쪽 | side별 (top/right/bottom/left) |
outline | 차지 안 함 | (대부분 브라우저) border-radius 따름 (2023~) | 박스 밖 | 단일 |
이 차이가 결정적이다:
- 포커스 링을
border로 만들면 → focus 시 2px border가 추가되어 옆 요소가 밀린다 (layout shift). - 포커스 링을
outline으로 만들면 → 박스 밖에 그려져 레이아웃 변동 없음.
이게 접근성 + 시각적 안정성의 갈림길이다.
How — 두 시스템의 비교
What — 5가지 패턴
1) Border 기본
.box {
border: 2px solid oklch(70% 0 0);
border-radius: 8px;
border-top: 4px solid oklch(60% 0.2 254); /* 한 변만 다르게 */
}
/* 개별 분해 */
.box {
border-width: 2px;
border-style: solid;
border-color: oklch(70% 0 0);
}border-style: none / hidden / dotted / dashed / solid / double / groove / ridge / inset / outset. dashed·dotted의 간격은 브라우저 구현체다 — 픽셀 단위 제어 불가.
2) Outline + Focus Ring (모던 표준)
/* 모든 인터랙티브 요소 */
button, a, input, [tabindex] {
outline: 2px solid transparent; /* 미리 둘러 두기 → 변동 없음 */
outline-offset: 2px;
transition: outline-color 0.15s;
}
button:focus-visible,
a:focus-visible,
input:focus-visible,
[tabindex]:focus-visible {
outline-color: oklch(62% 0.21 254);
}핵심 3가지:
:focus-visible— 키보드 포커스만 표시. 마우스 클릭은 표시 안 함 (WCAG 2.4.7).outline-offset— 박스에서 떨어진 거리.:focus-visible과 항상 짝.outline-color: transparent로 미리 둘러 두기 — focus 시 색만 바뀌어 트랜지션 부드럽고 layout shift 없음.
/* 이중 outline 트릭 — 내부 흰 띠로 구분 */
button:focus-visible {
outline: 2px solid oklch(62% 0.21 254);
outline-offset: 2px;
box-shadow: 0 0 0 4px white, 0 0 0 6px oklch(62% 0.21 254);
}3) Gradient Border — border-image
.gradient-border {
border: 4px solid transparent;
border-image: linear-gradient(in oklch, oklch(60% 0.2 254), oklch(60% 0.2 340)) 1;
}border-image: <source> <slice>. 1은 이미지를 1:1로 매핑. 단점: border-radius가 적용되지 않는다 — 둥근 그라데이션 보더에는 다른 트릭 필요.
4) Gradient Border + Radius — background-clip 트릭
.gradient-rounded {
border-radius: 12px;
border: 4px solid transparent;
background:
linear-gradient(white, white) padding-box,
linear-gradient(in oklch, oklch(60% 0.2 254), oklch(60% 0.2 340)) border-box;
}두 배경 레이어:
- 위:
linear-gradient(white, white) padding-box— padding 영역에 흰색 (내부) - 아래:
linear-gradient(...) border-box— border 영역까지 그라데이션 (테두리)
border는 transparent로 두고, border 영역의 배경이 보이게 만드는 트릭. border-radius 잘 동작.
5) Conic Gradient Border — 회전하는 테두리
.spinning-border {
border-radius: 12px;
border: 3px solid transparent;
background:
linear-gradient(var(--surface), var(--surface)) padding-box,
conic-gradient(from 0deg, oklch(60% 0.2 254), oklch(60% 0.2 25), oklch(60% 0.2 145), oklch(60% 0.2 254)) border-box;
}
/* @property로 각도 transition */
@property --angle {
syntax: '<angle>';
initial-value: 0deg;
inherits: false;
}
.spinning-border {
--angle: 0deg;
background:
linear-gradient(var(--surface), var(--surface)) padding-box,
conic-gradient(from var(--angle), ...) border-box;
animation: spin 4s linear infinite;
}
@keyframes spin {
to { --angle: 360deg; }
}AI 모델 페이지·로딩 상태에 자주. 자세한 @property는 00-foundations/04-custom-properties.
What-if — 잘못 쓰면
1) outline: none 단독 — 접근성 차단
/* 절대 금지 */
button:focus { outline: none; }
*:focus { outline: 0; }WCAG 2.4.7 (Focus Visible) 위반. 키보드 사용자는 지금 어디 포커스 있는지 모름. 대체 시각 단서를 반드시 제공:
/* 좋음 */
button { outline: 2px solid transparent; outline-offset: 2px; }
button:focus-visible { outline-color: oklch(62% 0.21 254); }2) :focus만 쓰고 :focus-visible 무시
/* 옛 방식 — 마우스 클릭에도 outline 표시 */
button:focus { outline: 2px solid blue; }문제: 마우스 클릭에도 outline이 떠서 어색. 모던 답:
button:focus-visible { outline: 2px solid blue; }:focus-visible은 사용자가 키보드로 들어왔을 때만 매칭. UA가 최선의 추정을 한다 (키보드 Tab·shortcut·programmatic focus는 매칭, 마우스 클릭은 보통 매칭 안 함). Chrome 86+ / Safari 15.4+ / Firefox 85+.
3) border-radius가 border-image에 안 먹힘
/* radius 무시됨 */
.bad {
border-radius: 12px;
border: 4px solid;
border-image: linear-gradient(red, blue) 1;
}위 4번 트릭(background-clip: border-box)으로 해결. 또는 바깥 wrapper에 그라데이션 + 안쪽 자식에 흰 배경의 2 div 트릭.
4) outline-offset의 음수 — 안쪽 outline
.inset-focus:focus-visible {
outline: 2px solid oklch(62% 0.21 254);
outline-offset: -4px; /* 박스 안쪽으로 들어옴 */
}음수 offset은 박스 안에 outline을 그린다. 큰 카드의 focus 시 안쪽 테두리 효과. 단, border-radius와 함께 쓰면 모서리가 어색할 수 있다 (브라우저별 차이).
5) box-shadow로 outline 흉내 — 다중
/* 다중 outline은 box-shadow로만 가능 */
.fancy:focus-visible {
box-shadow:
0 0 0 2px white,
0 0 0 4px oklch(62% 0.21 254),
0 0 0 6px white,
0 0 0 8px oklch(62% 0.21 25);
}outline은 단일. 여러 겹 outline이 필요하면 box-shadow chain. 단, box-shadow는 layout 차지는 안 하지만 부모의 overflow: hidden에 잘린다 — outline은 그렇지 않다.
6) border-style: dashed의 불일치
.dashed {
border: 2px dashed oklch(70% 0 0);
}dashed·dotted의 점선 간격은 표준이 명시하지 않는다. Chrome·Safari·Firefox가 모두 다르게 그린다. 픽셀 단위 제어가 필요하면:
/* SVG 또는 background-image로 점선 */
.precise-dashed {
background-image: linear-gradient(90deg, oklch(70% 0 0) 50%, transparent 50%);
background-size: 8px 2px;
background-position: bottom;
background-repeat: repeat-x;
}7) forced-colors 모드 호환
button:focus-visible {
outline: 2px solid oklch(62% 0.21 254);
}
@media (forced-colors: active) {
button:focus-visible {
outline: 2px solid CanvasText; /* 시스템 색 사용 */
}
}Windows 고대비 모드에선 작성자가 정의한 색이 무시된다. 시스템 키워드(CanvasText, Highlight, ButtonText)로 대응.
Insight — Outline의 부활
“outline은 한때 옛 속성이었다가 2020년대에 다시 1급 시민이 됐다”
CSS2(1998)에 outline이 도입됐을 때는 디버깅용에 가까웠다 — border와 비슷하지만 layout shift 없는 보조 도구. 2000년대 대부분의 디자이너가 outline: none으로 없애고 border로 포커스를 그렸다. 그 결과 포커스 시 layout shift가 만연했고, 접근성 평가가 늘 깨졌다.
2018년 Selectors Level 4에서 :focus-visible이 표준화되면서 분위기가 바뀌었다. “키보드일 때만 outline 표시” 가 가능해지자, 디자이너들이 outline을 받아들이기 시작했다. 2020년 Chrome이 :focus-visible을 기본 활성화, 2021년 Safari 15.4가 추가. 2023년부터 모든 브라우저가 outline이 border-radius를 따름 — 마지막 outline을 거부하던 시각적 이유가 사라졌다.
흥미로운 반전: 모던 outline은 macOS 시스템 포커스 링과 거의 동일한 동작이 됐다. macOS는 1990년대부터 button 밖 2~3px의 푸른 후광으로 포커스를 표시했다. 30년이 지나 CSS의 outline + offset + :focus-visible이 그 패턴을 따라잡았다.
또 하나의 반전: Tailwind v4는 기본 reset에서 outline: 2px solid transparent; outline-offset: 2px;를 모든 인터랙티브 요소에 깐다. 즉 layout shift 없는 미리-배치된 outline이 사실상 표준이 됐다. 이게 2024년 디자인 시스템의 기본 가정이다.
또 하나: border-image는 거의 죽은 속성이다. 2014년 표준화됐지만 border-radius와 안 맞아서 디자이너들이 거의 외면. 대신 background-clip: padding-box / border-box 2 layer 트릭이 사실상 표준이 됐다 — 표준이 늦게 따라온, 또 다른 사례.
요약 + Mermaid
border는 layout 차지,outline은 차지 안 함. 그래서 포커스 링은outline.- 모던 표준 =
:focus-visible+outline-offset+transparentoutline 미리 깔기. - Gradient border는
background-clip: padding-box / border-box2-layer 트릭 —border-image는border-radius와 안 맞아 사실상 폐기. - 회전하는 conic border는
@property --angle+ animation. outline: none단독은 WCAG 2.4.7 위반 — 항상 대체 시각 단서.forced-colors에선 시스템 키워드(CanvasText).
챕터를 마치며
05-color-visual 챕터는 여기까지다. 색공간(01)에서 시작해 gradient(02) → shadow(03) → filter(04) → blend(05) → background(06) → border/outline(07) 의 7단계를 거쳤다.
이 챕터의 한 줄 결론: 2024년 이후의 시각 효과는 OKLCH를 기본 단위로, color-mix + :focus-visible + isolation을 기본 도구로 한다. 옛 도구(HSL, outline: none, border-image, attachment: fixed)는 대부분 모던 대안에 자리를 내줬다.
다음 챕터: 06-motion — 색이 공간의 합성이라면, 모션은 시간의 합성이다.