06 Tailwind at rule
1. 설명
Tailwind v4는 별도의 JS 설정 파일 없이 CSS 파일 하나로 모든 설정을 관리한다.
핵심 수단이 @ 지시어(at-rule)이다.
2. @import
Tailwind의 모든 기능을 불러오는 진입점이다. 반드시 CSS 파일의 최상단에 위치해야 한다.
1@import "tailwindcss";전체를 불러오지 않고 필요한 레이어만 선택적으로 불러올 수도 있다.
1@import "tailwindcss/preflight"; /* 브라우저 기본 스타일 초기화 */2@import "tailwindcss/utilities"; /* 유틸리티 클래스만 */파일 구조 권장 순서
@import→@plugin→@source→@theme→@utility→@variant→ 컴포넌트
3. @theme
프로젝트 전체에서 사용할 디자인 토큰을 정의한다. CSS 변수 형태로 선언하면 Tailwind가 자동으로 유틸리티 클래스를 생성한다.
3.1. 기본 문법
1@theme {2 --{카테고리}-{이름}: {값};3}카테고리가 클래스 이름을 결정한다.
예를 들어 --color-*는 text-*, bg-*, border-* 등에 모두 적용된다.
3.2. 색상
1@theme {2 --color-primary: #f97316;3 --color-secondary: #6366f1;4 --color-brand-light: #fff7ed;5 --color-brand-dark: #431407;6}1<style type="text/tailwindcss">2 @theme {3 --color-primary: #f97316;4 --color-secondary: #6366f1;5 --color-brand-light: #fff7ed;6 --color-brand-dark: #431407;7 }8</style>9<div class="bg-primary text-secondary border-brand-light"></div>3.3. 폰트
1@theme {2 --font-sans: "Pretendard", sans-serif;3 --font-mono: "JetBrains Mono", monospace;4}1<p class="font-sans">본문 텍스트</p>2<code class="font-mono">코드 블록</code>3.4. 간격 (spacing)
1@theme {2 --spacing-18: 4.5rem; /* 72px */3 --spacing-22: 5.5rem; /* 88px */4 --spacing-128: 32rem; /* 512px */5}1<div class="mt-18 px-22 w-128"></div>3.5. 브레이크포인트
1@theme {2 --breakpoint-xs: 480px;3 --breakpoint-3xl: 1920px;4}1<div class="xs:flex 3xl:grid"></div>3.6. 기타 토큰
1@theme {2 --shadow-card: 0 4px 24px rgba(0,0,0,0.08);3 --radius-card: 1.25rem;4 --animate-spin-slow: spin 3s linear infinite;5}1<div class="shadow-card rounded-card animate-spin-slow"></div>3.7. inline 키워드
일반 @theme은 빌드 시 값을 고정한다.
inline을 붙이면 CSS 변수 참조를 런타임까지 유지한다.
JS로 테마를 동적으로 변경해야 할 때 사용한다.
1/* 빌드 시 #f97316으로 고정 */2@theme {3 --color-primary: #f97316;4}5
6/* 런타임에 --brand 값을 참조 */7@theme inline {8 --color-primary: var(--brand);9}1// 런타임에서 동적 변경 가능2document.documentElement.style.setProperty('--brand', '#6366f1');3.8. 토큰 초기화
기본 제공 토큰을 전부 제거하고 처음부터 정의할 때 *: initial을 사용한다.
1@theme {2 --color-*: initial; /* 기본 색상 전체 제거 */3 --color-primary: #f97316;4 --color-gray: #6b7280;5}4. @source
Tailwind가 클래스를 스캔할 경로를 추가 지정한다.
1@source "./src/**/*.{html,js,ts,jsx,tsx,vue,svelte}";2@source "../node_modules/@mylib/ui/src";4.1. 언제 필요한가
- 외부 패키지(
node_modules)에 Tailwind 클래스가 있을 때 - 모노레포 구조에서 다른 패키지 파일을 참조할 때
- 자동 감지 범위 밖의 경로를 사용할 때
4.2. 동적 클래스 처리
JS에서 동적으로 생성되는 클래스명은 스캔되지 않는다.
inline으로 안전 목록을 직접 명시한다.
1@source inline("bg-red-500 bg-green-500 bg-blue-500");5. @utility
Tailwind에 없는 유틸리티 클래스를 직접 만든다.
만든 클래스는 hover:, md:, dark: 등 모든 variant와 조합 가능하다.
5.1. 기본 예시
1@utility scrollbar-hide {2 scrollbar-width: none;3 &::-webkit-scrollbar {4 display: none;5 }6}7
8@utility center {9 display: flex;10 align-items: center;11 justify-content: center;12}13
14@utility text-balance {15 text-wrap: balance;16}1<ul class="scrollbar-hide overflow-x-auto flex gap-4">...</ul>2<div class="center w-full h-screen">...</div>3<h1 class="text-balance text-4xl font-bold">...</h1>5.2. variant와 조합
1<div class="center md:block"></div>2<div class="dark:text-balance"></div>3<div class="hover:scrollbar-hide"></div>5.3. 동적 값 수신
--value() 함수를 사용하면 클래스에서 값을 받을 수 있다.
1@utility tab-* {2 tab-size: --value(--tab-size, integer);3}1<pre class="tab-2">...</pre>2<pre class="tab-4">...</pre>6. @variant
Tailwind에 없는 커스텀 variant를 만든다.
@slot은 실제 클래스 내용이 삽입되는 자리를 나타낸다.
6.1. 복합 상태
1@variant hocus {2 &:hover,3 &:focus {4 @slot;5 }6}7
8@variant group-hocus {9 .group:hover &,10 .group:focus & {11 @slot;12 }13}1<button class="hocus:bg-primary hocus:text-white transition">2 버튼3</button>4
5<div class="group">6 <span class="group-hocus:underline">텍스트</span>7</div>6.2. 미디어쿼리
1@variant print {2 @media print { @slot; }3}4
5@variant landscape {6 @media (orientation: landscape) { @slot; }7}8
9@variant retina {10 @media (-webkit-min-device-pixel-ratio: 2) { @slot; }11}1<img class="w-full retina:w-1/2" src="...">2<nav class="hidden print:block">목차</nav>6.3. 속성 기반
1@variant selected {2 &[data-selected="true"] { @slot; }3}4
5@variant aria-expanded {6 &[aria-expanded="true"] { @slot; }7}1<li class="selected:bg-primary" data-selected="true">항목</li>2<button class="aria-expanded:rotate-180" aria-expanded="true">▼</button>7. @plugin
외부 Tailwind 플러그인 또는 직접 만든 JS 플러그인을 불러온다.
1@plugin "@tailwindcss/typography";2@plugin "@tailwindcss/forms";3@plugin "./plugins/my-plugin.js";7.1. 공식 플러그인 사용 예시
1<article class="prose prose-lg">2 <h1>제목</h1>3 <p>본문 내용...</p>4</article>7.2. 커스텀 플러그인 작성
1export default function({ addUtilities, addVariant }) {2 addUtilities({3 '.flex-center': {4 display: 'flex',5 alignItems: 'center',6 justifyContent: 'center',7 }8 });9
10 addVariant('not-last', '&:not(:last-child)');11}1@plugin "./plugins/custom.js";1<div class="flex-center not-last:border-b">...</div>8. @config
기존 tailwind.config.js를 CSS에서 참조한다.
v4로 점진적으로 마이그레이션할 때 사용하며, 전환이 완료되면 제거한다.
1@import "tailwindcss";2@config "../../tailwind.config.js";
@config와@theme이 충돌하면@theme이 우선한다.
9. @apply
CSS 규칙 안에서 Tailwind 유틸리티 클래스를 직접 적용한다.
1.btn {2 @apply inline-flex items-center justify-center;3 @apply px-4 py-2 rounded-lg font-semibold transition-colors;4}5
6.btn-primary {7 @apply btn bg-primary text-white hover:bg-orange-600;8}9
10.btn-outline {11 @apply btn border border-primary text-primary;12 @apply hover:bg-primary hover:text-white;13}14
15.card {16 @apply bg-white rounded-card shadow-card p-6 space-y-4;17}18
19.input {20 @apply w-full px-3 py-2 rounded-lg border border-gray-300;21 @apply focus:outline-none focus:ring-2 focus:ring-primary;22 @apply placeholder:text-gray-400;23}1<button class="btn-primary">확인</button>2<button class="btn-outline">취소</button>3
4<div class="card">5 <input class="input" placeholder="이름을 입력하세요">6</div>남용 주의 —
@apply를 과도하게 사용하면 클래스만 보고 스타일을 파악하는 Tailwind의 장점이 사라진다. 반복적으로 조합되는 공통 컴포넌트에만 제한적으로 사용하는 것이 권장된다.
10. 전체 구조 예시
실제 프로젝트에서 CSS 파일을 구성하는 전형적인 형태이다.
1/* 1. 진입점 */2@import "tailwindcss";3
4/* 2. 플러그인 */5@plugin "@tailwindcss/typography";6
7/* 3. 스캔 경로 */8@source "./src/**/*.{astro,html,js,ts,jsx,tsx}";9
10/* 4. 디자인 토큰 */11@theme {12 --color-primary: #f97316;13 --color-secondary: #6366f1;14 --font-sans: "Pretendard", sans-serif;15 --spacing-18: 4.5rem;16 --radius-card: 1.25rem;17 --shadow-card: 0 4px 24px rgba(0,0,0,0.08);18 --breakpoint-xs: 480px;19}20
21/* 5. 커스텀 유틸리티 */22@utility scrollbar-hide {23 scrollbar-width: none;24 &::-webkit-scrollbar { display: none; }25}26
27@utility center {28 display: flex;29 align-items: center;30 justify-content: center;31}32
33/* 6. 커스텀 variant */34@variant hocus {35 &:hover, &:focus { @slot; }36}37
38@variant print {39 @media print { @slot; }40}41
42/* 7. 컴포넌트 */43.btn {44 @apply inline-flex items-center justify-center;45 @apply px-4 py-2 rounded-lg font-semibold transition-colors;46}