CSS Protips
CSS 실력을 한 단계 업그레이드해 줄 다양한 팁 모음입니다. 각 항목은 독립적인 패턴이므로 현재 문제에 가장 적합한 것을 적용하세요.
CSS 리셋 사용하기
CSS 리셋은 브라우저 간 스타일 일관성을 유지하고 요소 스타일링을 위한 깨끗한 출발점을 제공합니다. 다양한 리셋 패턴이 있지만, 더 간단한 접근 방식을 사용할 수도 있습니다:
*,
*::before,
*::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
이제 요소의 마진과 패딩이 제거되고, box-sizing을 사용하여 CSS 박스 모델로 레이아웃을 쉽게 관리할 수 있습니다.
데모
[!TIP] 아래의
box-sizing상속 팁을 따른다면 CSS 리셋에box-sizing속성을 포함하지 않아도 됩니다.
box-sizing 상속
box-sizing을 html에서 상속되도록 설정하세요:
html {
box-sizing: border-box;
}
*,
*::before,
*::after {
box-sizing: inherit;
}
이렇게 하면 다른 동작을 사용하는 플러그인이나 컴포넌트에서 box-sizing을 더 쉽게 변경할 수 있습니다.
데모
컴포넌트 리셋에 all: unset 신중하게 사용하기
요소의 속성을 리셋할 때 각 속성을 개별적으로 리셋할 수 있습니다:
button {
background: none;
border: none;
color: inherit;
font: inherit;
outline: none;
padding: 0;
}
또는 all 단축 속성과 unset을 사용할 수 있습니다. all: unset은 거의 모든 속성을 리셋합니다 (unicode-bidi, direction, 사용자 정의 속성 제외) — 상속된 속성은 inherit으로, 나머지는 initial로 되돌아갑니다. <button>은 display: inline이 되고 기본 박스를 잃으므로, 리셋 후에는 항상 레이아웃과 포커스를 복원해야 합니다.
button.reset {
all: unset;
display: inline-flex;
align-items: center;
justify-content: center;
cursor: pointer;
}
button.reset:focus-visible {
outline: 2px solid currentColor;
outline-offset: 0.15em;
}
[!TIP] 빈 상태 대신 사용자 에이전트 스타일시트로 되돌리려면
all: revert를 사용하세요 — 최신 CSS 섹션의 리셋 의도:unsetvsrevert를 참조하세요.
:not()을 사용하여 내비게이션에 테두리 적용/제거하기
테두리를 먼저 적용하고...
/* 테두리 추가 */
.nav li {
border-right: 1px solid #666;
}
...마지막 요소에서 제거하는 대신...
/* 테두리 제거 */
.nav li:last-child {
border-right: none;
}
...:not() 의사 클래스를 사용하여 원하는 요소에만 적용하세요:
.nav li:not(:last-child) {
border-right: 1px solid #666;
}
CSS 선택자가 사람이 설명하는 방식 그대로 읽힙니다.
데모
폰트가 로컬에 설치되어 있는지 확인하기
폰트를 원격에서 가져오기 전에 로컬에 설치되어 있는지 확인할 수 있으며, 이는 좋은 성능 팁이기도 합니다.
@font-face {
font-family: "Dank Mono";
src:
/* 전체 이름 */ local("Dank Mono"), /* 포스트스크립트 이름 */ local("Dank Mono"),
/* 그렇지 않으면 다운로드! */ url("//...a.server/fonts/DankMono.woff");
}
code {
font-family: "Dank Mono", system-ui-monospace;
}
이 팁과 데모를 공유해 준 Adam Argyle에게 감사드립니다.
body에 line-height 추가하기
각 <p>, <h*> 등에 개별적으로 line-height를 추가할 필요가 없습니다. 대신 body에 추가하세요:
body {
line-height: 1.5;
}
이렇게 하면 텍스트 요소가 body에서 쉽게 상속받을 수 있습니다.
데모
폼 요소에 :focus 설정하기
시력 기반 키보드 사용자는 포커스를 사용하여 키보드 이벤트가 페이지에서 어디로 이동하는지 확인합니다. 브라우저 기본 구현보다 더 두드러지고 일관된 포커스 스타일을 폼 요소에 만드세요:
a:focus,
button:focus,
input:focus,
select:focus,
textarea:focus {
box-shadow: none;
outline: #000 dotted 2px;
outline-offset: 0.05em;
}
데모
[!TIP] 최신 코드에서는
:focus-visible을 선호하여 키보드 사용자가 명확한 포커스 링을 유지하면서 모든 마우스 클릭에 포커스 스타일이 표시되지 않도록 하세요.
모든 요소 수직 중앙 정렬하기
모바일에서는 Grid와 동적 뷰포트 단위를 선호하세요 — 100vh는 큰 뷰포트에 매핑되며 브라우저 툴바가 표시/숨겨질 때 잘못 동작할 수 있습니다. 논리 속성을 사용하면 100dvb (동적 뷰포트 블록)를 사용하세요.
.center-page {
min-block-size: 100dvb;
display: grid;
place-items: center;
}
부모가 정의된 블록 크기를 가지면 Flexbox도 같은 방식으로 작동합니다:
html,
body {
min-block-size: 100%;
}
body {
align-items: center;
display: flex;
justify-content: center;
}
[!NOTE] 레거시 패턴:
height: 100vh+place-items: center는 여전히 오래된 튜토리얼에서 나타납니다. 모바일에서 전체 뷰포트 레이아웃에는dvh/dvb/svh/lvh를 선호하세요 — MDN의 뷰포트 상대 길이를 참조하세요.
[!TIP] 다른 것을 중앙에 정렬하고 싶으신가요? CSS-Tricks에 좋은 글이 있습니다.
데모
높이/너비 대신 aspect-ratio 사용하기
aspect-ratio 속성을 사용하면 요소의 크기를 쉽게 지정하고 일관된 너비-높이 비율을 유지할 수 있습니다. 반응형 웹 디자인에서 레이아웃 시프트를 방지하는 데 매우 유용합니다. 이미지의 높이/너비 값이 변경되어도 레이아웃이 깨지지 않도록 object-fit과 함께 사용하세요.
img {
aspect-ratio: 16 / 9; /* 너비 / 높이 */
object-fit: cover;
}
aspect-ratio 속성에 대한 자세한 내용은 web.dev 게시물을 참조하세요.
데모
쉼표로 구분된 목록
목록 항목을 실제 쉼표로 구분된 목록처럼 만드세요:
ul > li:not(:last-child)::after {
content: ",";
}
:not() 의사 클래스를 사용하면 마지막 항목에 쉼표가 추가되지 않습니다.
[!NOTE] 이 팁은 접근성, 특히 스크린 리더에 적합하지 않을 수 있습니다. 또한 브라우저에서 복사/붙여넣기가 CSS 생성 콘텐츠와 함께 작동하지 않습니다. 주의해서 사용하세요.
음수 nth-child를 사용하여 항목 선택하기
CSS에서 음수 nth-child를 사용하여 1부터 n까지 항목을 선택하세요.
li {
display: none;
}
/* 1부터 3까지 항목을 선택하여 표시 */
li:nth-child(-n + 3) {
display: block;
}
또는 :not() 사용에 대해 이미 배웠다면 다음을 시도해 보세요:
/* 처음 3개를 제외한 모든 항목을 선택하여 표시 */
li:not(:nth-child(-n + 3)) {
display: block;
}
데모
아이콘에 SVG 사용하기
아이콘에 SVG를 사용하지 않을 이유가 없습니다:
.logo {
background: url("logo.svg");
}
SVG는 모든 해상도 유형에서 잘 확장되며 IE9까지 모든 브라우저에서 지원됩니다. .png, .jpg 또는 .gif 파일을 버리세요.
[!NOTE] 시력 사용자를 위한 SVG 아이콘 전용 버튼이 있고 SVG를 로드하지 못하는 경우, 접근성을 유지하는 데 도움이 됩니다:
.no-svg .icon-only::after {
content: attr(aria-label);
}
"Lobotomized Owl" 선택자 사용하기
이상한 이름을 가지고 있지만, 보편 선택자(*)와 인접 형제 선택자(+)를 함께 사용하면 강력한 CSS 기능을 제공할 수 있습니다:
* + * {
margin-top: 1.5em;
}
이 예제에서, 문서 흐름에서 다른 요소 뒤에 오는 모든 요소는 margin-top: 1.5em을 받습니다.
[!TIP] "lobotomized owl" 선택자에 대한 자세한 내용은 _A List Apart_의 Heydon Pickering의 글을 읽어보세요.
데모
순수 CSS 슬라이더를 위해 max-height 사용하기
overflow hidden과 함께 max-height를 사용하여 CSS 전용 슬라이더를 구현하세요:
.slider {
max-height: 200px;
overflow-y: hidden;
width: 300px;
}
.slider:hover {
max-height: 600px;
overflow-y: scroll;
}
호버 시 요소가 max-height 값으로 확장되고 overflow로 인해 슬라이더가 표시됩니다.
동일한 너비의 테이블 셀
테이블 작업은 어려울 수 있습니다. table-layout: fixed를 사용하여 셀의 너비를 동일하게 유지하세요:
.calendar {
table-layout: fixed;
}
고통 없는 테이블 레이아웃.
데모
Flexbox로 마진 해킹 제거하기
열 간격을 작업할 때 flexbox의 space-between 속성을 사용하여 nth-, first-, last-child 해킹을 제거할 수 있습니다:
.list {
display: flex;
justify-content: space-between;
}
.list .person {
flex-basis: 23%;
}
이제 열 간격이 항상 균일하게 나타납니다.
빈 링크에 속성 선택자 사용하기
<a> 요소에 텍스트 값은 없지만 href 속성에 링크가 있는 경우 링크를 표시하세요:
a[href^="http"]:empty::before {
content: attr(href);
}
정말 편리합니다.
데모
[!NOTE] 이 팁은 접근성, 특히 스크린 리더에 적합하지 않을 수 있습니다. 또한 브라우저에서 복사/붙여넣기가 CSS 생성 콘텐츠와 함께 작동하지 않습니다. 주의해서 사용하세요.
:is()로 특이성 더 잘 제어하기
:is() 의사 클래스는 여러 선택자를 한 번에 타겟팅하여 중복을 줄이고 코드 가독성을 향상시키는 데 사용됩니다. 큰 선택자를 더 간결한 형태로 작성하는 데 매우 유용합니다.
:is(section, article, aside, nav) :is(h1, h2, h3, h4, h5, h6) {
color: green;
}
위의 규칙 집합은 다음 선택자 규칙과 동일합니다...
section h1,
section h2,
section h3,
section h4,
section h5,
section h6,
article h1,
article h2,
article h3,
article h4,
article h5,
article h6,
aside h1,
aside h2,
aside h3,
aside h4,
aside h5,
aside h6,
nav h1,
nav h2,
nav h3,
nav h4,
nav h5,
nav h6 {
color: green;
}
데모
"기본" 링크 스타일링
"기본" 링크에 스타일을 추가하세요:
a[href]:not([class]) {
color: #008000;
text-decoration: underline;
}
이제 CMS를 통해 삽입되어 일반적으로 class 속성이 없는 링크가 캐스케이드에 일반적인 영향을 주지 않고 구별됩니다.
고유 비율 박스 (레거시 폴백)
[!NOTE] 레거시 전용입니다. 새 코드에는
aspect-ratio(Baseline Widely available)를 선호하세요. 매우 오래된 브라우저를 지원해야 하거나aspect-ratio이전의 패턴이 필요한 경우에만 이 패딩 핵을 유지하세요.
고유 비율을 가진 박스를 만들려면 div에 위쪽 또는 아래쪽 패딩을 적용하세요:
.container {
height: 0;
padding-bottom: 20%;
position: relative;
}
.container div {
border: 2px dashed #ddd;
height: 100%;
left: 0;
position: absolute;
top: 0;
width: 100%;
}
패딩에 20%를 사용하면 박스의 높이가 너비의 20%가 됩니다. 뷰포트 너비에 관계없이 하위 div는 종횡비(100% / 20% = 5:1)를 유지합니다.
데모
깨진 이미지 스타일링
약간의 CSS로 깨진 이미지를 미학적으로 더 보기 좋게 만드세요:
img {
display: block;
font-family: sans-serif;
font-weight: 300;
height: auto;
line-height: 2;
position: relative;
text-align: center;
width: 100%;
}
이제 의사 요소 규칙을 추가하여 사용자 메시지와 깨진 이미지의 URL 참조를 표시합니다:
img::before {
content: "죄송합니다. 아래 이미지가 깨졌습니다 :(";
display: block;
margin-bottom: 10px;
}
img::after {
content: "(url: " attr(src) ")";
display: block;
font-size: 12px;
}
[!TIP] 이 패턴 스타일링에 대한 자세한 내용은 Ire Aderinokun의 글을 참조하세요.
전역 크기 조정에는 rem, 지역 크기 조정에는 em 사용하기
루트에서 기본 글꼴 크기를 설정한 후 (html { font-size: 100%; }), 텍스트 요소의 글꼴 크기를 em으로 설정하세요:
h2 {
font-size: 2em;
}
p {
font-size: 1em;
}
그런 다음 모듈의 글꼴 크기를 rem으로 설정하세요:
article {
font-size: 1.25rem;
}
aside .module {
font-size: 0.9rem;
}
이제 각 모듈이 구획화되어 스타일 지정, 유지 관리 및 유연성이 더 쉬워집니다.
음소거되지 않은 자동 재생 동영상 숨기기
이것은 사용자 정의 사용자 스타일시트를 위한 훌륭한 트릭입니다. 페이지가 로드될 때 자동 재생되는 동영상의 사운드로 사용자에게 과부하가 걸리지 않도록 하세요. 사운드가 음소거되지 않은 경우 동영상을 표시하지 마세요:
video[autoplay]:not([muted]) {
display: none;
}
다시 한 번, :not() 의사 클래스를 활용하고 있습니다.
유연한 타이포그래피를 위해 :root 사용하기
반응형 레이아웃의 글꼴 크기는 각 뷰포트에 맞게 조정될 수 있어야 합니다. :root를 사용하여 뷰포트 높이와 너비를 기반으로 글꼴 크기를 계산할 수 있습니다:
:root {
font-size: calc(1vw + 1vh + 0.5vmin);
}
이제 :root에서 계산된 값을 기반으로 root em 단위를 사용할 수 있습니다:
body {
font: 1rem/1.6 sans-serif;
}
데모
더 나은 모바일 경험을 위해 폼 요소에 font-size 설정하기
모바일 브라우저가 <select> 드롭다운이 포커스될 때 확대/축소하는 것을 방지하려면 폼 요소에 font-size를 추가하세요:
input[type="text"],
input[type="number"],
select,
textarea {
font-size: 16px;
}
:root에서 글꼴 크기를 font-size: 16px;로 설정하고 폼 요소가 상속하도록 할 수도 있습니다. 이 트릭은 자동 확대/축소 동작을 비활성화합니다.
[!TIP] 16px는 모바일 사파리와 크롬에서 확대/축소를 비활성화하는 마법의 숫자입니다. 더 작은 값은 iOS에서 확대/축소를 다시 활성화합니다.
CSS 사용자 정의 속성 (변수) 활용하기
CSS 사용자 정의 속성을 사용하면 브라우저에서 즉시 업데이트할 수 있는 동적 스타일을 만들 수 있습니다. 이는 테마 지정 및 디자인 토큰 관리에 특히 유용합니다.
:root {
--text-color: #333;
--bg-color: #fff;
}
body {
color: var(--text-color);
background-color: var(--bg-color);
}
.dark-theme {
--text-color: #eee;
--bg-color: #111;
}
[!NOTE] 사용자 정의 속성은 캐스케이드에 참여하므로 테마 변경이 의도한 대로 전파됩니다. JavaScript에서도 동적으로 업데이트하여 테마 전환과 같은 고급 패턴을 사용할 수 있습니다.
prefers-reduced-motion 존중하기
시스템 수준의 모션 선호도를 감지하고 과도한 애니메이션을 줄여 접근성을 높이세요:
@media (prefers-reduced-motion: reduce) {
*,
*::before,
*::after {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
scroll-behavior: auto !important;
}
}
이렇게 하면 사용자의 시스템 설정을 존중하여 모션 취약 사용자의 불편을 방지할 수 있습니다.
prefers-color-scheme로 다크 모드 지원하기
prefers-color-scheme 미디어 쿼리를 사용하여 사용자의 시스템 테마에 따라 스타일을 자동으로 조정하세요:
:root {
--bg: white;
--text: black;
}
@media (prefers-color-scheme: dark) {
:root {
--bg: black;
--text: white;
}
}
body {
background: var(--bg);
color: var(--text);
}
사용자 정의 속성과 결합하면 테마 관리가 깔끔하고 유지보수 가능해집니다.
focus보다 :focus-visible 선호하기
키보드 탐색 중에만 포커스 링을 표시하고 마우스 클릭 시에는 숨깁니다. 이는 시각적 혼란을 줄이면서 키보드 접근성을 유지합니다.
/* 모든 포커스에 포커스 스타일을 적용하지 마세요 */
:focus:not(:focus-visible) {
outline: none;
}
/* 키보드 포커스에만 적용 */
:focus-visible {
outline: 3px solid #1565c0;
outline-offset: 2px;
}
[!NOTE]
:focus-visible은 이제 Baseline Widely available입니다. 이전 브라우저를 지원해야 하는 경우 위의:focus:not(:focus-visible)패턴을 폴백으로 사용할 수 있습니다.
논리 속성 사용하기
물리적 방향 (left, right, top, bottom) 대신 논리 속성 (inline-start, inline-end, block-start, block-end)을 사용하여 쓰기 모드 및 방향에 독립적인 레이아웃을 만드세요:
.card {
border-inline-start: 4px solid hotpink;
margin-block-end: 1rem;
padding-inline: 1rem;
}
이렇게 하면 영어, 아랍어(오른쪽에서 왼쪽), 일본어(세로 쓰기)에서 스타일이 올바르게 적용됩니다.
@supports로 점진적 향상
@supports를 사용하여 브라우저가 특정 CSS 기능을 지원하는지 확인하고, 지원하지 않는 경우 우아한 폴백을 제공하세요:
.container {
display: block; /* 폴백 */
}
@supports (display: grid) {
.container {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 1rem;
}
}
이 패턴을 사용하면 모든 사용자에게 기본 기능을 제공하면서 최신 브라우저에서 향상된 경험을 제공할 수 있습니다.
리셋 의도: unset vs revert
| 값 | 설명 | 사용 시기 |
|---|---|---|
unset | 상속 속성은 inherit, 나머지는 initial로 리셋 | 완전히 제어해야 하는 컴포넌트 리셋 |
revert | 사용자 에이전트 스타일시트 값으로 리셋 | 기본 브라우저 스타일을 유지하면서 리셋 |
/* unset: 완전 초기화 */
button.clean {
all: unset;
}
/* revert: 사용자 에이전트 스타일로 복원 */
button.revert {
all: revert;
}
[!TIP] 대부분의 경우
all: unset을 사용한 다음 필요한 레이아웃 속성을 명시적으로 설정하는 것이 더 예측 가능합니다.
최신 CSS Baseline 참조
다음 표는 이 가이드의 기술을 MDN Baseline 상태별로 분류합니다.
| 기술 | Baseline 상태 | 지원 범위 |
|---|---|---|
:is(), :not(), :where() | Widely available | 모든 최신 브라우저 |
aspect-ratio | Widely available | 모든 최신 브라우저 |
display: grid, gap | Widely available | 모든 최신 브라우저 |
| CSS 사용자 정의 속성 | Widely available | 모든 최신 브라우저 |
:focus-visible | Widely available | 모든 최신 브라우저 (Chrome 86+, Firefox 85+, Safari 15.4+) |
| 논리 속성 | Newly available | 대부분의 최신 브라우저 (일부 오래된 브라우저 누락) |
prefers-reduced-motion | Widely available | 모든 최신 브라우저 |
prefers-color-scheme | Widely available | 모든 최신 브라우저 |
@supports | Widely available | 모든 최신 브라우저 (IE 지원 안함) |
dvh/dvb/svh/lvh | Newly available | 최신 Chromium, Firefox, Safari |
[!NOTE] Baseline은 브라우저 호환성 신호일 뿐입니다. 접근성, 키보드 동작, 모션 선호도 및 대비는 별도로 테스트하세요.
출처: AllThingsSmitty/css-protips + Baseline 추가 사항 (web.dev/MDN)