Tailwind CSS v4의 임포트 방식, CSS 레이어 시스템, @utility로 커스텀 유틸리티 정의하기, 그리고 스타일 우선순위 관리 전략을 다룹니다.
2장에서 @theme 디렉티브로 디자인 토큰을 정의하는 방법을 배웠습니다. 이 장에서는 v4의 새로운 임포트 구조, CSS 레이어 시스템, 그리고 @utility 디렉티브를 통한 커스텀 유틸리티 정의를 다룹니다. 이 기능들은 스타일의 우선순위와 구조를 세밀하게 제어하는 핵심 도구입니다.
CSS @layer는 CSS Cascade Layers 사양(W3C)의 일부로, 스타일 규칙의 우선순위를 명시적으로 제어합니다. 레이어가 선언된 순서에 따라 우선순위가 결정되며, 나중에 선언된 레이어의 스타일이 우선합니다.
/* 레이어 순서 선언 */
@layer base, components, utilities;
/* base 레이어: 가장 낮은 우선순위 */
@layer base {
h1 { font-size: 2rem; }
}
/* components 레이어: 중간 우선순위 */
@layer components {
.card { padding: 1rem; }
}
/* utilities 레이어: 가장 높은 우선순위 */
@layer utilities {
.p-4 { padding: 1rem; }
}Tailwind v4는 CSS @layer를 기반으로 다음 레이어 구조를 사용합니다.
@layer theme, base, components, utilities;| 레이어 | 우선순위 | 역할 |
|---|---|---|
theme | 가장 낮음 | CSS 커스텀 프로퍼티 (디자인 토큰) |
base | 낮음 | 리셋 스타일, 기본 요소 스타일 |
components | 중간 | 재사용 가능한 컴포넌트 스타일 |
utilities | 가장 높음 | 유틸리티 클래스 |
이 레이어 순서 덕분에, 유틸리티 클래스는 항상 컴포넌트나 기본 스타일보다 우선합니다. 이것이 Tailwind에서 class="card p-8"처럼 유틸리티로 컴포넌트 스타일을 오버라이드할 수 있는 원리입니다.
@import "tailwindcss";이 한 줄은 다음과 동일합니다.
@import "tailwindcss/preflight" layer(base);
@import "tailwindcss/theme" layer(theme);
@import "tailwindcss/utilities" layer(utilities);각 레이어에 자신의 스타일을 추가할 수 있습니다.
@import "tailwindcss";
/* base 레이어에 커스텀 리셋 추가 */
@layer base {
html {
font-family: var(--font-sans);
-webkit-font-smoothing: antialiased;
}
body {
background-color: var(--color-surface);
color: var(--color-foreground);
}
a {
color: var(--color-primary);
text-decoration-line: underline;
text-underline-offset: 2px;
}
}
/* components 레이어에 재사용 컴포넌트 추가 */
@layer components {
.card {
background-color: var(--color-card);
border-radius: var(--radius-lg);
padding: var(--spacing-6);
box-shadow: var(--shadow-card);
}
.btn {
display: inline-flex;
align-items: center;
justify-content: center;
padding: var(--spacing-2) var(--spacing-4);
border-radius: var(--radius-md);
font-weight: 500;
transition: all 0.15s ease;
}
.btn-primary {
background-color: var(--color-primary);
color: var(--color-primary-foreground);
}
.btn-primary:hover {
background-color: var(--color-primary-hover);
}
}@layer components에 정의한 스타일은 @layer utilities보다 우선순위가 낮습니다. 따라서 class="btn btn-primary px-8"에서 px-8 유틸리티가 .btn의 padding을 오버라이드합니다. 이것이 Tailwind의 설계 의도에 부합하는 동작입니다.
v4에서는 @utility 디렉티브로 Tailwind의 유틸리티 레이어에 커스텀 유틸리티를 추가할 수 있습니다.
@utility content-auto {
content-visibility: auto;
}
@utility scrollbar-hidden {
scrollbar-width: none;
&::-webkit-scrollbar {
display: none;
}
}
@utility text-balance {
text-wrap: balance;
}<div class="content-auto scrollbar-hidden">
<h1 class="text-balance">긴 제목이 균형 있게 줄바꿈됩니다</h1>
</div>@utility로 정의한 클래스는 Tailwind의 모든 변형(variants)과 자동으로 결합됩니다.
<!-- hover, responsive, dark mode 등 모든 변형 사용 가능 -->
<div class="scrollbar-hidden hover:scrollbar-hidden md:content-auto dark:text-balance"></div>기능적 유틸리티(값을 받는 유틸리티)를 정의할 수도 있습니다.
@utility tab-* {
tab-size: --value(--tab-size-*);
}<pre class="tab-4">코드 블록</pre>
<pre class="tab-2">코드 블록</pre>@layer 밖에 작성한 스타일은 어떤 레이어보다도 높은 우선순위를 가집니다. 이는 서드파티 라이브러리의 스타일을 오버라이드할 때 유용합니다.
@import "tailwindcss";
/* 모든 레이어보다 높은 우선순위 */
.third-party-override {
margin: 0 !important;
}레이어 밖 스타일은 유틸리티 클래스보다도 우선하므로, Tailwind의 유틸리티 오버라이드 패턴이 깨질 수 있습니다. 가능한 한 모든 스타일을 적절한 레이어 안에 배치하세요.
같은 레이어 내에서는 일반 CSS 명시도(specificity) 규칙이 적용됩니다.
@layer components {
/* 명시도 0-1-0 */
.card { padding: 1rem; }
/* 명시도 0-2-0 — 더 높음 */
.card.card-compact { padding: 0.5rem; }
}다른 레이어 간에는 레이어 순서가 명시도보다 우선합니다.
@layer components {
/* 명시도 0-2-0 */
.card.special { padding: 2rem; }
}
@layer utilities {
/* 명시도 0-1-0이지만, utilities 레이어가 더 높으므로 우선 */
.p-4 { padding: 1rem; }
}대규모 프로젝트에서는 스타일을 여러 파일로 분리하는 것이 일반적입니다.
@import "tailwindcss";
/* 테마 */
@import "./theme/colors.css";
@import "./theme/typography.css";
@import "./theme/spacing.css";
/* 기본 스타일 */
@import "./base/reset.css" layer(base);
@import "./base/typography.css" layer(base);
/* 컴포넌트 */
@import "./components/buttons.css" layer(components);
@import "./components/cards.css" layer(components);
@import "./components/forms.css" layer(components);
/* 커스텀 유틸리티 */
@import "./utilities/custom.css";@theme {
--color-primary: oklch(0.6 0.2 250);
--color-primary-hover: oklch(0.55 0.22 250);
--color-primary-foreground: white;
--color-surface: oklch(0.99 0 0);
--color-foreground: oklch(0.15 0 0);
}/* 이미 layer(components)로 임포트되므로 @layer 불필요 */
.btn {
display: inline-flex;
align-items: center;
gap: var(--spacing-2);
padding: var(--spacing-2) var(--spacing-4);
border-radius: var(--radius-md);
font-weight: 500;
cursor: pointer;
transition: all 0.15s ease;
}
.btn-primary {
background-color: var(--color-primary);
color: var(--color-primary-foreground);
}
.btn-primary:hover {
background-color: var(--color-primary-hover);
}파일 분리 시, 임포트 순서가 같은 레이어 내에서의 우선순위를 결정합니다. 관련 있는 스타일을 논리적 그룹으로 묶고, 의존성이 있는 파일은 의존 대상 뒤에 임포트하세요.
Tailwind의 Preflight(기본 리셋 스타일)를 선택적으로 비활성화하거나 수정할 수 있습니다.
/* Preflight를 제외하고 임포트 */
@import "tailwindcss/theme" layer(theme);
@import "tailwindcss/utilities" layer(utilities);
/* 자신만의 리셋 사용 */
@layer base {
*,
*::before,
*::after {
box-sizing: border-box;
margin: 0;
}
}v3에서 자주 사용된 @apply는 v4에서도 지원되지만, @layer와 @utility를 활용하는 것이 더 권장됩니다.
.btn {
@apply inline-flex items-center gap-2 px-4 py-2 rounded-md font-medium;
}@layer components {
.btn {
display: inline-flex;
align-items: center;
gap: var(--spacing-2);
padding: var(--spacing-2) var(--spacing-4);
border-radius: var(--radius-md);
font-weight: 500;
}
}v4 방식의 장점은 CSS 도구(린팅, 포매팅, IDE 지원)와의 완전한 호환성, 그리고 테마 변수를 직접 참조하여 런타임 테마 전환이 가능하다는 점입니다.
Tailwind CSS v4의 레이어 시스템은 CSS 표준 @layer를 기반으로, 스타일 우선순위를 명확하고 예측 가능하게 관리합니다. @utility로 커스텀 유틸리티를 정의하면 변형과의 자동 결합이 가능하고, 파일 분리를 통해 대규모 프로젝트에서도 스타일을 체계적으로 조직할 수 있습니다.
다음 장에서는 v4의 새로운 기본 색상 시스템인 OKLCH를 심층 분석합니다. 색상 이론, OKLCH의 장점, 색상 팔레트 설계, 그리고 색상 조작 유틸리티를 다룹니다.
이 글이 도움이 되셨나요?
관련 주제 더 보기
Tailwind CSS v4의 기본 색상 시스템인 OKLCH의 원리, 장점, 색상 팔레트 설계 방법, 그리고 투명도 수정자와 색상 조합 패턴을 실전 중심으로 다룹니다.
Tailwind CSS v4의 핵심 설정 방식인 @theme 디렉티브를 심층 분석합니다. 디자인 토큰 정의, 네임스페이스 규칙, 기본 테마 확장과 재정의 패턴을 다룹니다.
Tailwind CSS v4의 네이티브 컨테이너 쿼리 지원, @container 변형, 기존 미디어 쿼리와의 비교, 그리고 컴포넌트 기반 반응형 디자인 패턴을 다룹니다.