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