Tailwind CSS 핵심 패턴

# 개요
Tailwind CSS는 별도의 CSS 파일 작성을 최소화하고 HTML 마크업 내에서 유틸리티 클래스(Utility Classes) 등으로 바로 스타일을 적용할 수 있어 빠른 스타일링이 가능한 CSS 프레임워크입니다.
또한 반응형 디자인, 다크 모드, 호버 상태 등의 기타 스타일링도 HTML 안에서 처리할 수 있습니다.
이는 React처럼 CSS 파일로 따로 모듈화하거나 Emotion, styled-components 등의 CSS-in-JS 라이브러리를 활용하는 프레임워크에서 특히 유용합니다.
물론 Vue, Svelte에서도 Tailwind CSS를 사용할 수 있지만, 별도 라이브러리 없이도 *.vue
, *.svelte
등의 단일 파일 컴포넌트(SFC)에 CSS 코드를 포함하고 모듈화할 수 있기 때문에 활용도는 비교적 떨어질 수 있습니다.
그리고 Tailwind CSS는 Bootstrap 등의 일반적인 CSS 프레임워크와는 다르게, 기본적으로 미리 정의된 스타일 컴포넌트를 제공하지 않습니다.
대신 작은 단위의 유틸리티 클래스를 HTML에서 직접 조합해 원하는 디자인을 구현하는 방식을 가집니다.
이를 통해 사용하는 스타일만 최종 빌드에 포함할 수 있고 번들 크기를 더욱 줄일 수 있습니다.
다음 예제에서 두 요소는 인라인 스타일과 유틸리티 클래스를 사용하고 같은 결과를 출력합니다.
12<div style={{ height: '100px', width: '100px', backgroundColor: 'red' }}></div> <div className="h-[100px] w-[100px] bg-[red]"></div>
그리고 최적화된 스타일을 구현할 수 있도록 사전에 정의된 클래스 값도 제공합니다.
다만, 이러한 값들은 덜 직관적이며 효과적인 사용을 위해선 패턴을 익히는 과정이 필요합니다.
그래서 이 글은 자주 사용되는 주요 패턴을 분석하고 소개합니다.
12<div style={{ height: '100px', width: '100px', backgroundColor: 'red' }}></div> <div className="h-25 w-25 bg-red-500"></div>
Tailwind CSS는 변형(Variants)이라는 것을 통해 상태에 따른 스타일을 쉽게 처리할 수 있습니다.
먼저 기존의 인라인 스타일을 사용하는 경우 호버 효과를 처리할 수 없기 때문에, 다음의 .box
클래스 정의처럼 별도의 CSS 코드를 작성해야 합니다.
12345678.box { width: 100px; height: 100px; background-color: red; } .box:hover { background-color: blue; }
그런데 Tailwind CSS를 사용하면 hover:
변형을 사용해 호버 효과를 쉽게 처리할 수 있습니다.
다음 예제에서 bg-blue-500
유틸리티 클래스는 호버 상태에서만 적용됩니다.
12<div className="box"></div> <div className="h-25 w-25 bg-red-500 hover:bg-blue-500"></div>
만약 스타일 재사용이 필요한 경우 @utility
디렉티브(Directive)를 통해 다음과 같이 추상화할 수 있습니다.
다음 예제와 같이 속성과 값으로 작성하거나 @apply
디렉티브를 사용해 유틸리티 클래스를 사용할 수도 있습니다.
1234567891011@utility box1 { width: 100px; height: 100px; background-color: red; &:hover { background-color: blue; } } @utility box2 { @apply w-25 h-25 bg-red-500 hover:bg-blue-500; }
12<div className="box1"></div> <div className="box2"></div>
# 설치 및 구성
# VS Code 확장 프로그램
VS Code에서 사용할 수 있는 Tailwind CSS의 여러 확장 프로그램이 있습니다.
# Tailwind CSS IntelliSense
Tailwind CSS IntelliSense는 자동 완성, 구문 강조 표시, 린팅 등의 여러 기능을 제공하는 공식 확장 프로그램입니다.
zoom_out_map
Tailwind CSS IntelliSense
일부 클래스를 작성하면 적용 가능한 유틸리티 클래스 목록을 자동 완성합니다.
원하는 클래스 항목을 선택해 쉽게 적용할 수 있습니다.
zoom_out_map
클래스 자동 완성
적용된 유틸리티 클래스에 호버하면 실제 적용되는 CSS 스타일이 표시됩니다.
기존에 작성된 스타일을 쉽게 파악할 수 있습니다.
zoom_out_map
클래스 프리뷰
# Tailwind Docs
Tailwind Docs를 통해 Tailwind CSS 문서를 쉽게 검색할 수 있습니다.
VS Code에서 명령 팔레트(Cmd or Ctrl
+ Shift
+ P
)를 열어 tailwind 검색어
를 입력합니다.
zoom_out_map
Tailwind Docs
검색된 결과에서 내가 적용하려는 스타일 항목을 선택해 유틸리티 클래스 사용법을 바로 확인할 수 있습니다.
문서 페이지는 별도 브라우저에 열리지 않고 VS Code 내에서 표시됩니다.
zoom_out_map
명령 팔레트에서 문서 검색
# Vite
Vite 프로젝트에서는 다음과 같이 Tailwind CSS를 설치하고 구성합니다.
만약 Prettier를 사용하지 않으면, prettier-plugin-tailwindcss
는 설치하지 않습니다.
1npm i -D tailwindcss @tailwindcss/vite prettier-plugin-tailwindcss
12345678import { defineConfig } from 'vite' import react from '@vitejs/plugin-react' import tailwindcss from '@tailwindcss/vite' // https://vite.dev/config/ export default defineConfig({ plugins: [react(), tailwindcss()] })
/src/main.tsx
와 연결된 전역 스타일에서 다음과 같이 Tailwind CSS를 구성합니다.
이전에는 @tailwind
디렉티브를 사용했지만, v4부터 CSS @import
규칙을 사용합니다.
1234/* @tailwind base; @tailwind components; @tailwind utilities; */ @import "tailwindcss";
Prettier를 사용할 때 다음과 같이 플러그인을 등록해야 합니다.
12345{ "semi": false, "singleQuote": true, "plugins": ["prettier-plugin-tailwindcss"] }
# 비표준 규칙 경고 제거
@theme
, @apply
같은 Tailwind CSS의 디렉티브를 사용하면, CSS 혹은 SCSS 파일에서 경고가 표시될 수 있습니다.
zoom_out_map
비표준 규칙에 대한 경고 표시
VS Code에서 Tailwind CSS IntelliSense 확장 프로그램을 사용하는 경우, settings.json
파일에 다음과 같이 구성해 경고를 제거할 수 있습니다.
123456{ "files.associations": { "*.css": "tailwindcss", "*.scss": "tailwindcss" } }
# Next
최신 Next 프로젝트를 새롭게 설치하는 경우, 사용 옵션을 선택하면 Tailwind CSS를 따로 구성할 필요가 없습니다.
기존 Next 프로젝트는 Next.js 프로젝트에서 새로운 Tailwind CSS v4 시작하기를 참고하세요.
12345678npx create-next-app@latest <프로젝트이름> ✔ Would you like to use TypeScript? … Yes # 타입스크립트 사용 여부 ✔ Would you like to use ESLint? … Yes # ESLint 사용 여부 ✔ Would you like to use Tailwind CSS? … Yes # Tailwind CSS 사용 여부 ✔ Would you like your code inside a `src/` directory? … No # src/ 디렉토리 사용 여부 ✔ Would you like to use App Router? (recommended) … Yes # App Router 사용 여부 ✔ Would you like to use Turbopack for next dev? … No # Turbopack 사용 여부 ✔ Would you like to customize the import alias (@/* by default)? … No # `@/*` 외 경로 별칭 사용 여부
Prettier를 사용하는 경우, Vite 구성과 마찬가지로 다음과 같이 플러그인을 설치하고 등록합니다.
비표준 규칙 경고 제거도 같으니, 앞서 설명한 Vite 구성을 참고하세요.
1npm i -D prettier-plugin-tailwindcss
12345{ "semi": false, "singleQuote": true, "plugins": ["prettier-plugin-tailwindcss"] }
# 유틸리티 클래스
Tailwind CSS의 유틸리티 클래스(Utility Class)는 하나 이상의 CSS 속성과 값을 가지는 작은 CSS 클래스로, 이를 통해 CSS 속성을 쉽게 적용할 수 있습니다.
# 공통 패턴 이해
width
는 w-*
, margin
은 m-*
과 같이 대부분의 유틸리티 클래스는 개별 CSS 속성과 직접 일치하지만, width
와 height
를 합친 size-*
처럼 일부 클래스는 여러 속성을 조합한 스타일을 제공하기도 합니다.
그리고 flex-basis
속성은 basis-*
클래스로 사용하는데 flex-wrap
속성은 flex-*
클래스로 사용하는 등 유틸리티 클래스의 이름 패턴이 일정하지 않거나, px-2
클래스에서의 숫자는 기본 여백(--spacing
)의 곱 연산으로 적용되는데, border-1
클래서에서의 숫자는 px
단위로 적용되는 등 사용 패턴 또한 다양하기에 어느 정도 러닝 커브가 발생합니다.
그래서 더 쉽게 이해하기 위해 유틸리티 클래스의 공통 패턴을 정리했습니다.
공통 패턴을 이해한 후에 속성과 유틸리티 클래스 표를 확인해 보세요.
# 일반 사용
CSS 속성 접두사(Property Prefix)와 해당 속성의 CSS 값(Value)을 대시(-
) 기호와 조합하는 가장 일반적인 패턴을 사용할 수 있습니다.
예를 들어, CSS float: left;
속성과 값을 그대로 연결해 float-left
클래스로 작성합니다.box-sizing: border-box;
속성과 값의 경우 box-border
클래스와 같이 속성 이름을 box-*
로 축약해서 작성해야 합니다.
그리고 width
는 w-*
로, text-indent
는 indent-*
로, border-radius
는 rounded-*
로 작성하는 등 축약 패턴에 일관성이 떨어지기 때문에 사용에 주의해야 합니다.indent-1
을 text-indent-1
과 같이 잘못된 클래스를 사용할 수 있으니 주의하세요.
1<div className="float-left box-border w-10 rounded-sm indent-1"></div>
# 특수한 값
속성 접두사와 미리 정의된 키워드, 숫자, 비율 같은 특수한 값을 조합하는 패턴도 있습니다.
예를 들어, aspect-ratio: 16/9;
속성과 값을 aspect-16/9
의 비율이나 aspect-video
의 키워드를 사용하는 클래스로 작성하거나 width: 100%;
를 w-full
클래스로 작성합니다.
columns-*
같은 일부 클래스는 columns-md
같이 값으로 크기 이름을 지정할 수 있습니다.
이렇게 크기를 지정하는 패턴에는 기본 5가지(xs
, sm
, md
, lg
, xl
)가 있고, 가장 작거나(xs
) 큰(xl
) 크기 앞에 2
이상의 숫자를 붙여서 3xs
, 2xs
, 6xl
, 7xl
등 더 작거나 더 큰 값을 표시할 수 있습니다.
그리고 음수 값을 사용할 수 있는 일부 CSS 속성은 클래스 앞에 마이너스(-
)를 추가할 수 있습니다.
예를 들어, order: -1;
은 -order-1
클래스로 작성합니다.
1<div className="-order-1 -m-4 aspect-square columns-md"></div>
# 단일 값 클래스
속성 접두사 없이 값만 클래스로 사용하는 패턴이 있습니다.
예를 들어, display: block
속성과 값을 display-block
클래스가 아닌 block
클래스로 작성합니다.
1<span className="visible absolute block"></span>
# 변수 사용
속성 접두사에 소괄호(()
)를 사용해 var()
함수 없이 변수를 지정할 수 있습니다.
예를 들어, width: var(--size);
속성과 값을 w-(--size)
클래스로 작성할 수 있습니다.
지역 변수는 [--size:200px]
와 같이 다음 주제의 임의 값 지정 방식을 사용해 정의할 수 있습니다.
1<div className="h-[calc(var(--size)+50px)] w-(--size) [--size:200px]"></div>
# 임의 값 지정
속성 접두사에 대괄호([]
)를 사용해 미리 정의되지 않은 임의의 값을 지정할 수 있습니다.
예를 들어, width: 123px;
속성과 값을 w-[123px]
클래스로 작성할 수 있습니다.
w-[calc(100%-40px)]
과 같이 CSS 함수를 사용하는 것도 가능합니다.100% - 40px
같이 중간에 공백 문자(띄어쓰기 등)를 사용하는 것은 허용되지 않으니 주의하세요.
추가로 []
에는 속성과 값을 있는 그대로 작성할 수 있습니다.
지역 변수를 선언하거나 Tailwind CSS에서 지원되지 않는 속성을 적용하는 등 자유롭게 사용할 수 있습니다.
1<div className="h-[calc(var(--size)+50px)] w-[var(--size)] [--size:200px]"></div>
1<input className="[width:200px] text-[#ff0000] [-webkit-text-security:circle]" />
# 색상 지정
Tailwind CSS에는 색상 팔레트가 기본적으로 포함되어 있습니다.bg-red-500
, text-blue-300
과 같이 속성접두사-색상이름-색상단계
패턴으로 클래스를 작성합니다.
zoom_out_map
Tailwind CSS 색상 팔레트
색상 이름은 Red
부터 Stone
까지 22가지를 지정할 수 있습니다.
색상 단계는 100
단위로 100
부터 900
까지 기본 9단계와, 가장 밝은 50
과 가장 어두운 950
을 더해 총 11단계로 입력할 수 있습니다.
중간의 500
을 기본 색상으로 하고 숫자를 줄여 더 밝거나 숫자를 늘려 더 어두운 색을 지정하는 개념입니다.
흰색과 검정색은 색상 단계 없이 bg-white
, border-black
로 작성합니다.
만약 색상에 투명도를 적용하고 싶다면, bg-red-500/70
, bg-black/15
와 같이 클래스 끝에 0
부터 100
까지의 불투명도를 추가합니다.
참고로 100
은 불투명하고 0
은 투명합니다.
123<div className="size-[100px] border-2 border-blue-700/90 bg-red-500/50 text-pink-50"> Colors </div>
그리고 색상을 적용할 수 있는 유틸리티 클래스는 모두 *-inherit
, *-current
, *-transparent
클래스를 추가로 사용할 수 있습니다.
부모의 색상을 상속(inherit
) 받거나 현재 색상(currentColor
)을 명시적으로 사용하거나 투명하게(transparent
) 할 수 있습니다.
123<div className="bg-inherit"> background-color: inherit; </div> <div className="accent-current"> accent-color: currentColor; </div> <div className="border-transparent"> border-color: transparent; </div>
# 속성과 유틸리티 클래스 표
앞서 살펴본 공통 패턴을 기반으로 각 속성과 유틸리티 클래스 목록을 정리했습니다.
브라우저에서 Cmd or Ctrl
+ F
키를 눌러 검색하면, 속성이나 클래스 이름을 좀 더 쉽게 찾을 수 있습니다.
그리고 구체적인 정보 확인을 위해, CSS 속성 이름을 선택해 Tailwind CSS의 해당 문서로 이동할 수 있습니다.
각 표에서 (변수)
는 w-(--size)
처럼, CSS 변수 사용이 가능한지 여부입니다.[임의]
는 w-[100px]
처럼, 임의 값 지정이 가능한지 여부입니다.
# 레이아웃
CSS 속성 | 유틸리티 클래스 | 스타일 or 값 | (변수) | [임의] |
---|---|---|---|---|
aspect-ratio | aspect-비율 aspect-square aspect-video aspect-auto |
1 / 1 16 / 9 auto |
O | O |
columns | columns-숫자 columns-3xs ~ 7xl columns-auto |
auto |
O | O |
break-after | break-after-auto break-after-avoid break-after-all break-after-avoid-page break-after-page break-after-left break-after-right break-after-column |
auto avoid all avoid-page page left right column |
||
break-before | break-before-auto break-before-avoid break-before-all break-before-avoid-page break-before-page break-before-left break-before-right break-before-column |
auto avoid all avoid-page page left right column |
||
break-inside | break-inside-auto break-inside-avoid break-inside-avoid-page break-inside-avoid-column |
auto avoid avoid-page avoid-column |
||
box-decoration-break | box-decoration-clone box-decoration-slice |
clone slice |
||
box-sizing | box-border box-content |
border-box content-box |
||
display | inline block inline-block ... hidden sr-only not-sr-only |
inline block inline-block ... none |
||
float | float-right float-left float-start float-end float-none |
right left inline-start inline-end none |
||
clear | clear-left clear-right clear-both clear-start clear-end clear-none |
left right both inline-start inline-end none |
||
isolation | isolate isolation-auto |
isolate auto |
||
object-fit | object-contain object-cover object-fill object-none object-scale-down |
contain cover fill none scale-down |
||
object-position | object-bottom object-center object-left object-left-bottom object-left-top object-right object-right-bottom object-right-top object-top |
bottom center left left bottom left top right right bottom right top top |
O | O |
overflow | overflow-auto overflow-hidden overflow-clip overflow-visible overflow-scroll overflow-x-auto overflow-y-auto overflow-x-hidden overflow-y-hidden overflow-x-clip overflow-y-clip overflow-x-visible overflow-y-visible overflow-x-scroll overflow-y-scroll |
auto hidden clip visible scroll auto auto hidden hidden clip clip visible visible scroll scroll |
||
overscroll-behavior | overscroll-auto overscroll-contain overscroll-none overscroll-x-auto overscroll-x-contain overscroll-x-none overscroll-y-auto overscroll-y-contain overscroll-y-none |
auto contain none auto contain none auto contain none |
||
position | static fixed absolute relative sticky |
static fixed absolute relative sticky |
||
top / right / bottom / left | inset-* inset-x-* inset-y-* start-* end-* top-* right-* bottom-* left-* *-숫자 *-비율 *-px *-full *-auto |
0.25rem * 숫자 100% * 비율 1px 100% auto |
O | O |
visibility | visible invisible collapse |
visible hidden collapse |
||
z-index | z-숫자 z-auto |
auto |
O | O |
# 플렉스 & 그리드
CSS 속성 | 유틸리티 클래스 | 스타일 or 값 | (변수) | [임의] |
---|---|---|---|---|
flex-basis | basis-숫자 basis-비율 basis-full basis-auto basis-3xs ~ 7xl |
0.25rem * 숫자 100% * 비율 100% auto |
O | O |
flex-direction | flex-row flex-row-reverse flex-col flex-col-reverse |
row row-reverse column column-reverse |
||
flex-wrap | flex-nowrap flex-wrap flex-wrap-reverse |
nowrap wrap wrap-reverse |
||
flex | flex-숫자 flex-비율 flex-auto flex-initial flex-none |
100% * 비율 1 1 auto 0 1 auto none |
O | O |
flex-grow | grow grow-숫자 |
1 | O | O |
flex-shrink | shrink shrink-숫자 |
1 | O | O |
order | order-숫자 order-first order-last order-none |
-무한 무한 0 |
O | O |
grid-template-columns | grid-cols-숫자 grid-cols-none grid-cols-subgrid |
repeat(숫자, minmax(0, 1fr)) none subgrid |
O | O |
grid-column | col-span-숫자 col-span-full col-start-숫자 col-start-auto col-end-숫자 col-end-auto col-auto col-숫자 |
span 숫자 / span 숫자 1 / -1 auto auto auto |
O | O |
grid-template-rows | grid-rows-숫자 grid-rows-none grid-rows-subgrid |
repeat(숫자, minmax(0, 1fr)) none subgrid |
O | O |
grid-row | row-span-숫자 row-span-full row-start-숫자 row-start-auto row-end-숫자 row-end-auto row-auto row-숫자 |
span 숫자 / span 숫자 1 / -1 auto auto auto |
O | O |
grid-auto-flow | grid-flow-row grid-flow-col grid-flow-dense grid-flow-row-dense grid-flow-col-dense |
row column dense row dense column dense |
||
grid-auto-columns | auto-cols-auto auto-cols-min auto-cols-max auto-cols-fr |
auto min-content max-content minmax(0, 1fr) |
O | O |
grid-auto-rows | auto-rows-auto auto-rows-min auto-rows-max auto-rows-fr |
auto min-content max-content minmax(0, 1fr) |
O | O |
gap | gap-숫자 gap-x-숫자 gap-y-숫자 |
0.25rem * 숫자 | O | O |
justify-content | justify-start justify-end justify-center justify-between justify-around justify-evenly justify-stretch justify-baseline justify-normal |
flex-start flex-end center space-between space-around space-evenly stretch baseline normal |
||
justify-items | justify-items-start justify-items-end justify-items-center justify-items-stretch justify-items-normal |
start end center stretch normal |
||
justify-self | justify-self-auto justify-self-start justify-self-end justify-self-center justify-self-stretch |
auto start end center stretch |
||
align-content | content-normal content-center content-start content-end content-between content-around content-evenly content-baseline content-stretch |
normal center flex-start flex-end space-between space-around space-evenly baseline stretch |
||
align-items | items-start items-end items-center items-baseline items-stretch |
flex-start flex-end center baseline stretch |
||
align-self | self-auto self-start self-end self-center self-stretch self-baseline |
auto flex-start flex-end center stretch baseline |
||
place-content | place-content-center place-content-start place-content-end place-content-between place-content-around place-content-evenly place-content-baseline place-content-stretch |
center start end space-between space-around space-evenly baseline stretch |
||
place-items | place-items-start place-items-end place-items-center place-items-baseline place-items-stretch |
start end center baseline stretch |
||
place-self | place-self-auto place-self-start place-self-end place-self-center place-self-stretch |
auto start end center stretch |
# 여백
space-*
는 요소 사이 여백(Margin)을 의미하며, 대상 요소가 아닌 부모 요소에 지정해야 합니다.
다음 예제는 각 <li>
사이에 8px
(0.5rem
)의 여백을 만듭니다.
처음과 마지막 <li>
위/아래에는 여백이 생기지 않습니다.
12345<ul className="space-y-2"> <li>Apple</li> <li>Banana</li> <li>Cherry</li> </ul>
zoom_out_map
각 요소의 사이 여백
# 크기
CSS 속성 | 유틸리티 클래스 | 스타일 or 값 | (변수) | [임의] |
---|---|---|---|---|
width | w-* w-screen size-* *-3xs ~ 7xl *-숫자 *-비율 *-auto *-px *-full *-dvw *-dvh *-lvw *-lvh *-svw *-svh *-min *-max *-fit |
100vw 0.25rem * 숫자 100% * 비율 auto 1px 100% 100dvw 100dvh 100lvw 100lvh 100svw 100svh min-content max-content fit-content |
O | O |
min-width | min-w-* *-위와같음 |
O | O | |
max-width | max-w-* *-위와같음 |
O | O | |
height | h-* h-screen size-* *-위와같음 |
100vw |
O | O |
min-height | min-h-* *-위와같음 |
O | O | |
max-height | max-h-* *-위와같음 |
O | O |
# 글꼴 & 문자
text-*
클래스로 글자 크기를 지정할 때, /
기호와 함께 사용하면 줄 높이(line-height
)를 지정할 수 있습니다.
12<div className="text-sm">font-size: 14px;</div> <div className="text-sm/6">font-size: 14px; line-height: 1.5;</div>
CSS content
속성은 ::before
, ::after
가상 요소 선택자에서 사용할 수 있으므로, content-*
클래스는 before:
, after:
변형과 함께 사용합니다.
그리고 값으로 문자를 작성할 때 띄어쓰기 대신 _
기호를 사용해야 하며, 반대로 실제 _
기호를 사용하려면 이스케이프 처리를 해야 합니다.
참고로 url()
함수 안에서는 이스케이프 처리가 필요하지 않습니다.
123<div className="before:content-['Have_a_great_day!']">Have a great day!</div> <div className="before:content-['Have\_a\_great\_day!']">Have_a_great_day!</div> <div className="before:content-[url('https://www.heropy.dev/images/profile_m.jpg')]">HEROPY</div>
CSS 속성 | 유틸리티 클래스 | 스타일 or 값 | (변수) | [임의] |
---|---|---|---|---|
font-family | font-sans font-serif font-mono |
var(--font-sans) var(--font-serif) var(--font-mono) |
O | O |
font-size | text-xs ~ 9xl text-base |
1rem |
O | O |
font-smoothing | antialiased subpixel-antialiased |
antialiased auto |
||
font-style | italic not-italic |
italic normal |
||
font-weight | font-thin font-extralight font-light font-normal font-medium font-semibold font-bold font-extrabold font-black |
100 200 300 400 500 600 700 800 900 |
O | O |
font-stretch | font-stretch-ultra-condensed font-stretch-extra-condensed font-stretch-condensed font-stretch-semi-condensed font-stretch-normal font-stretch-semi-expanded font-stretch-expanded font-stretch-extra-expanded font-stretch-ultra-expanded font-stretch-퍼센트 |
ultra-condensed (50%) extra-condensed (62.5%) condensed (75%) semi-condensed (87.5%) normal (100%) semi-expanded (112.5%) expanded (125%) extra-expanded (150%) ultra-expanded (200%) |
O | O |
font-variant-numeric | normal-nums ordinal slashed-zero lining-nums oldstyle-nums proportional-nums tabular-nums diagonal-fractions stacked-fractions |
normal ordinal slashed-zero lining-nums oldstyle-nums proportional-nums tabular-nums diagonal-fractions stacked-fractions |
||
letter-spacing | tracking-tighter tracking-tight tracking-normal tracking-wide tracking-wider tracking-widest |
-0.05em -0.025em 0em 0.025em 0.05em 0.1em |
O | O |
line-clamp | line-clamp-숫자 line-clamp-none |
unset |
O | O |
line-height | leading-none leading-숫자 |
0.25rem * 숫자 |
O | O |
list-style-image | list-image-none | O | O | |
list-style-position | list-inside list-outside |
inside outside |
||
list-style-type | list-disc list-decimal list-none |
disc decimal none |
O | O |
text-align | text-left text-center text-right text-justify text-start text-end |
left center right justify start end |
||
color | text-색상 | O | O | |
text-decoration-line | underline overline line-through no-underline |
underline overline line-through none |
||
text-decoration-color | decoration-색상 | O | O | |
text-decoration-style | decoration-solid decoration-double decoration-dotted decoration-dashed decoration-wavy |
solid double dotted dashed wavy |
||
text-decoration-thickness | decoration-숫자 decoration-from-font decoration-auto |
숫자px from-font auth |
O | O |
text-underline-offset | underline-offset-숫자 underline-offset-auto |
숫자px auto |
O | O |
text-transform | uppercase lowercase capitalize normal-case |
uppercase lowercase capitalize none |
||
text-overflow | truncate text-ellipsis text-clip |
overflow: hidden; text-overflow: ellipsis; white-space: nowrap; ellipsis clip |
||
text-wrap | text-wrap text-nowrap text-balance text-pretty |
wrap nowrap balance pretty |
||
text-indent | indent-숫자 indent-px |
0.25rem * 숫자 1px |
O | O |
vertical-align | align-baseline align-top align-middle align-bottom align-text-top align-text-bottom align-sub align-super |
baseline top middle bottom text-top text-bottom sub super |
O | O |
white-space | whitespace-normal whitespace-nowrap whitespace-pre whitespace-pre-line whitespace-pre-wrap whitespace-break-spaces |
normal nowrap pre pre-line pre-wrap break-spaces |
||
word-break | break-normal break-words break-all break-keep |
overflow-wrap: normal word-break: normal break-word break-all keep-all |
||
hyphens | hyphens-none hyphens-manual hyphens-auto |
none manual auto |
||
content | content-none | none | O | O |
# 배경
요소 배경의 그라데이션(Gradient)을 지정할 때 from-*
, via-*
, to-*
클래스를 사용해 시작, 중간, 끝 지점의 색상을 지정할 수 있습니다.
다음 예제의 그라데이션은 왼쪽 빨간색으로 시작해서 노락색을 거쳐 오른쪽 파란색으로 끝나도록 출력됩니다.
1<div className="h-20 w-full bg-linear-to-r from-red-500 via-yellow-500 to-blue-500"></div>
zoom_out_map
from-red-500 via-yellow-500 to-blue-500
그리고 from-*
, via-*
, to-*
클래스는 각 지점의 색상뿐만 아니라 정지 위치까지 지정할 수 있습니다.
다음 예제에서 빨간색은 10%
, 노란색은 30%
, 파란색은 50%
위치에서 정지하며, 그 사이 색상은 자연스럽게 처리됩니다.
1<div className="h-20 w-full bg-linear-to-r from-red-500 from-10% via-yellow-500 via-30% to-blue-500 to-50%"></div>
zoom_out_map
from-10% via-30% to-50%
참고로 CSS에서 요소 배경의 색상은 background-color
속성을 사용하지만, 배경의 그라데이션 색상은 background-image
속성을 사용해야 합니다.
CSS 속성 | 유틸리티 클래스 | 스타일 or 값 | (변수) | [임의] |
---|---|---|---|---|
background-attachment | bg-fixed bg-local bg-scroll |
fixed local scroll |
||
background-clip | bg-clip-border bg-clip-padding bg-clip-content bg-clip-text |
border-box padding-box content-box text |
||
background-color | bg-색상 | O | O | |
background-image | bg-none bg-linear-to-t bg-linear-to-tr bg-linear-to-r bg-linear-to-br bg-linear-to-b bg-linear-to-bl bg-linear-to-l bg-linear-to-tl bg-linear-각도 bg-radial bg-conic-각도 from-색상 from-퍼센트 via-색상 via-퍼센트 to-색상 to-퍼센트 |
none linear-gradient(to top) linear-gradient(to top right) linear-gradient(to right) linear-gradient(to bottom right) linear-gradient(to bottom) linear-gradient(to bottom left) linear-gradient(to left) linear-gradient(to top left) linear-gradient(각도) radial-gradient(in oklab) conic-gradient(from 각도 in oklab) |
O | O |
background-origin | bg-origin-border bg-origin-padding bg-origin-content |
border-box padding-box content-box |
||
background-position | bg-bottom bg-center bg-left bg-left-bottom bg-left-top bg-right bg-right-bottom bg-right-top bg-top |
bottom center left left bottom left top right right bottom right top top |
O | O |
background-repeat | bg-repeat bg-repeat-x bg-repeat-y bg-repeat-space bg-repeat-round bg-no-repeat |
repeat repeat-x repeat-y space round no-repeat |
||
background-size | bg-auto bg-cover bg-contain |
auto cover contain |
O | O |
# 테두리 선
divide-*
는 요소 사이 선(Border)을 의미하며, 대상 요소가 아닌 부모 요소에 지정해야 합니다.
다음 예제는 각 <li>
사이에 선을 추가합니다.
처음과 마지막 <li>
위/아래에는 선이 표시되지 않습니다.
12345<ul className="divide-y-2"> <li>Apple</li> <li>Banana</li> <li>Cherry</li> </ul>
zoom_out_map
각 요소의 사이 선
CSS 속성 | 유틸리티 클래스 | 스타일 or 값 | (변수) | [임의] |
---|---|---|---|---|
border-radius | rounded-* rounded-s-* rounded-e-* rounded-t-* rounded-r-* rounded-b-* rounded-l-* rounded-ss-* rounded-se-* rounded-ee-* rounded-es-* rounded-tl-* rounded-tr-* rounded-br-* rounded-bl-* *-xs ~ *-3xl *-none *-full |
start-start (좌상단) + end-start (좌하단) start-end (우상단) + end-end (우하단) top-left (좌상단) + top-right (우상단) top-right + bottom-right bottom-right + bottom-left top-left + bottom-left start-start start-end end-end end-start top-left top-right bottom-right bottom-left 0.125rem (2px) 1.5rem (24px) 0 무한px |
O | O |
border-width | border border-숫자 border-x border-x-숫자 border-y border-y-숫자 border-s border-s-숫자 border-e border-e-숫자 border-t border-t-숫자 border-r border-r-숫자 border-b border-b-숫자 border-l border-l-숫자 divide-x divide-x-숫자 divide-y divide-y-숫자 divide-x-reverse divide-y-reverse |
1px 숫자px 1px 숫자px 1px 숫자px 1px 숫자px 1px 숫자px 1px 숫자px 1px 숫자px 1px 숫자px 1px 숫자px 1px 숫자px 1px 숫자px |
O | O |
border-color | border-색상 border-x-색상 border-y-색상 border-s-색상 border-e-색상 border-t-색상 border-r-색상 border-b-색상 border-l-색상 divide-색상 |
O | O | |
border-style | border-solid border-dashed border-dotted border-double border-hidden border-none divide-solid divide-dashed divide-dotted divide-double divide-hidden divide-none |
solid dashed dotted double hidden none |
||
outline-width | outline outline-숫자 |
1px 숫자px |
O | O |
outline-color | outline-색상 | O | O | |
outline-style | outline-solid outline-dashed outline-dotted outline-double outline-none outline-hidden |
solid dashed dotted double none outline: 2px solid transparent; outline-offset: 2px; |
||
outline-offset | outline-offset-숫자 | 숫자px | O | O |
# 효과
CSS 속성 | 유틸리티 클래스 | 스타일 or 값 | (변수) | [임의] |
---|---|---|---|---|
box-shadow | shadow-2xs ~ 2xl shadow-none shadow-색상 inset-shadow-2xs ~ sm inset-shadow-none inset-shadow-색상 ring ring-숫자 ring-색상 inset-ring inset-ring-숫자 inset-ring-색상 |
none none 0 0 0 1px 0 0 0 숫자px inset 0 0 0 1px inset 0 0 0 숫자px |
O | O |
opacity | opacity-숫자 | 숫자% | O | O |
mix-blend-mode | max-blend-normal max-blend-multiply max-blend-screen max-blend-overlay max-blend-darken max-blend-lighten max-blend-color-dodge max-blend-color-burn max-blend-hard-light max-blend-soft-light max-blend-difference max-blend-exclusion max-blend-hue max-blend-saturation max-blend-color max-blend-luminosity max-blend-plus-darker max-blend-plus-lighter |
normal multiply screen overlay darken lighten color-dodge color-burn hard-light soft-light difference exclusion hue saturation color luminosity plus-darker plus-lighter |
||
background-blend-mode | bg-blend-normal bg-blend-multiply bg-blend-screen bg-blend-overlay bg-blend-darken bg-blend-lighten bg-blend-color-dodge bg-blend-color-burn bg-blend-hard-light bg-blend-soft-light bg-blend-difference bg-blend-exclusion bg-blend-hue bg-blend-saturation bg-blend-color bg-blend-luminosity |
normal multiply screen overlay darken lighten color-dodge color-burn hard-light soft-light difference exclusion hue saturation color luminosity |
# 필터
CSS 속성 | 유틸리티 클래스 | 스타일 or 값 | (변수) | [임의] |
---|---|---|---|---|
filter | filter-none blur-xs ~ 3xl blur-none brightness-숫자 contrast-숫자 drop-shadow-xs ~ 2xl drop-shadow-none grayscale grayscale-숫자 hue-rotate-숫자 invert invert-숫자 saturate-숫자 sepia sepia-숫자 |
none none brightness(숫자%) contrast(숫자%) drop-shadow(0 0 #0000) grayscale(100%) grayscale(숫자%) hue-rotate(숫자deg) invert(100%) invert(숫자%) saturate(숫자%) sepia(100%) sepia(숫자%) |
O | O |
backdrop-filter | backdrop-filter-none backdrop-blur-xs ~ 3xl backdrop-blur-none backdrop-brightness-숫자 backdrop-contrast-숫자 backdrop-grayscale backdrop-grayscale-숫자 backdrop-hue-rotate-숫자 backdrop-invert backdrop-invert-숫자 backdrop-opacity-숫자 backdrop-saturate-숫자 backdrop-sepia backdrop-sepia-숫자 |
none none brightness(숫자%) contrast(숫자%) grayscale(100%) grayscale(숫자%) hue-rotate(숫자deg) invert(100%) invert(숫자%) opacity(숫자%) saturate(숫자%) sepia(100%) sepia(숫자%) |
O | O |
# 표
CSS 속성 | 유틸리티 클래스 | 스타일 or 값 | (변수) | [임의] |
---|---|---|---|---|
border-collapse | border-collapse border-separate |
collapse separate |
||
border-spacing | border-spacing-숫자 border-spacing-x-숫자 border-spacing-y-숫자 |
O | O | |
table-layout | table-auto table-fixed |
auto fixed |
||
caption-side | caption-top caption-bottom |
top bottom |
# 전환 & 애니메이션
@theme
디렉티브 안에서 --animate-*
테마 변수를 정의해서 나만의 애니메이션 유틸리티 클래스를 만들 수 있습니다.
여러 번 재사용할 애니메이션을 정의할 때 유용합니다.
1234567891011@theme { --animate-my-animation: my-keyframes 1s infinite alternate; @keyframes my-keyframes { 0% { transform: translateX(0); } 100% { transform: translateX(100px); } } }
1<div className="animate-my-animation"></div>
CSS 속성 | 유틸리티 클래스 | 스타일 or 값 | (변수) | [임의] |
---|---|---|---|---|
transition-property | transition transition-all transition-colors transition-opacity transition-shadow transition-transform transform-none |
colors + opacity + shadow + transform + filters all |
O | O |
transition-behavior | transition-normal transition-discrete |
normal allow-discrete |
||
transition-duration | duration-숫자 duration-initial |
숫자ms initial |
O | O |
transition-timing-function | ease-linear ease-in ease-out ease-in-out ease-initial |
linear cubic-bezier(0.4, 0, 1, 1) cubic-bezier(0, 0, 0.2, 1) cubic-bezier(0.4, 0, 0.2, 1) initial |
O | O |
transition-delay | delay-숫자 | 숫자ms | O | O |
animation | animate-spin animate-ping animate-pulse animate-bounce animate-none |
spin 1s linear infinite ping 1s cubic-bezier(0, 0, 0.2, 1) infinite pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite bounce 1s infinite none |
O | O |
# 변환
CSS 속성 | 유틸리티 클래스 | 스타일 or 값 | (변수) | [임의] |
---|---|---|---|---|
backface-visibility | backface-hidden backface-visible |
hidden visible |
||
perspective | perspective-dramatic perspective-near perspective-normal perspective-midrange perspective-distant perspective-none |
100px 300px 500px 800px 1200px none |
O | O |
perspective-origin | perspective-origin-center perspective-origin-top perspective-origin-top-right perspective-origin-right perspective-origin-bottom-right perspective-origin-bottom perspective-origin-bottom-left perspective-origin-left perspective-origin-top-left |
center top top right right bottom right bottom bottom left left top left |
O | O |
rotate | rotate-none rotate-숫자 rotate-x-숫자 rotate-y-숫자 rotate-z-숫자 |
none 숫자deg |
O | O |
scale | scale-none scale-숫자 scale-x-숫자 scale-y-숫자 scale-z-숫자 scale-3d |
none 숫자% 숫자% 숫자% 1 1 숫자% 1 1 숫자% 1 1 1 |
O | O |
skew | skew-숫자 skew-x-숫자 skew-y-숫자 |
skewX(숫자deg) skewY(숫자deg) skewX(숫자deg)) skewY(숫자deg) |
O | O |
transform | transform-none transform-gpu transform-cpu |
none translateZ(0) |
O | O |
transform-origin | origin-center origin-top origin-top-right origin-right origin-bottom-right origin-bottom origin-bottom-left origin-left origin-top-left |
center top top right right bottom right bottom bottom left left top left |
O | O |
transform-style | transform-3d transform-flat |
preserve-3d flat |
||
translate | translate-* translate-x-* translate-y-* translate-z-* translate-none *-숫자 *-비율 *-full *-px |
0.25rem * 숫자 100% * 비율 100% 100% 1px 1px |
O | O |
# 상호작용
CSS 속성 | 유틸리티 클래스 | 스타일 or 값 | (변수) | [임의] |
---|---|---|---|---|
accent-color | accent-색상 | O | O | |
appearance | appearance-none appearance-auto |
none auto |
||
caret-color | caret-색상 | O | O | |
color-scheme | scheme-normal scheme-dark scheme-light scheme-light-dark scheme-only-dark scheme-only-light |
normal dark light light dark only dark only light |
||
cursor | cursor-auto cursor-default cursor-pointer cursor-wait ... |
auto default pointer wait ... |
O | O |
field-sizing | field-sizing-fixed field-sizing-content |
fixed content |
||
pointer-events | pointer-events-auto pointer-events-none |
auto none |
||
resize | resize-none resize resize-x resize-y |
none both horizontal vertical |
||
scroll-behavior | scroll-auto scroll-smooth |
auto smooth |
||
scroll-margin | scroll-m-* scroll-mx-* scroll-my-* scroll-ms-* scroll-me-* scroll-mt-* scroll-mr-* scroll-mb-* scroll-ml-* *-숫자 |
0.25rem * 숫자 |
O | O |
scroll-padding | scroll-p-* scroll-px-* scroll-py-* scroll-ps-* scroll-pe-* scroll-pt-* scroll-pr-* scroll-pb-* scroll-pl-* *-숫자 |
0.25rem * 숫자 |
O | O |
scroll-snap-align | snap-start snap-end snap-center snap-align-none |
start end center none |
||
scroll-snap-stop | snap-normal snap-always |
normal always |
||
scroll-snap-type | snap-none snap-x snap-y snap-both snap-mandatory snap-proximity |
none x proximity y proximity both proximity mandatory proximity |
||
touch-action | touch-auto touch-none touch-pan-x touch-pan-left touch-pan-right touch-pan-y touch-pan-up touch-pan-down touch-pinch-zoom touch-manipulation |
auto none pan-x pan-left pan-right pan-y pan-up pan-down pinch-zoom manipulation |
||
user-select | select-none select-text select-all select-auto |
none text all auto |
||
will-change | will-change-auto will-change-scroll will-change-contents will-change-transform |
auto scroll-position contents transform |
O | O |
# SVG
CSS 속성 | 유틸리티 클래스 | 스타일 or 값 | (변수) | [임의] |
---|---|---|---|---|
fill | fill-none fill-색상 |
none | O | O |
stroke | stroke-none stroke-색상 |
none | O | O |
stroke-width | stroke-숫자 | O | O |
# 접근성
CSS 속성 | 유틸리티 클래스 | 스타일 or 값 | (변수) | [임의] |
---|---|---|---|---|
forced-color-adjust | forced-color-adjust-auto forced-color-adjust-none |
auto none |
# 변형
Tailwind CSS의 모든 유틸리티 클래스는 변형(Variants)을 통해 조건부로 적용할 수 있습니다.
대표적으로 요소에 호버 시 스타일을 적용하기 위한 hover:
변형이 있습니다.
다음 예제에서 파란색의 배경은 호버 시에만 적용됩니다.
1<div className="size-25 bg-red-500 hover:bg-blue-500"></div>
그리고 변형은 중첩해서 사용할 수 있습니다.
다음 예제에서 첫 번째 <div>
는 <span>
을 포함하므로 호버 시 파란색 배경이 적용되지만, 두 번째 <div>
는 <span>
을 가지고 있지 않으므로 호버해도 파란색 배경이 적용되지 않습니다.
1234<div className="size-25 bg-red-500 hover:has-[span]:bg-blue-500"> <span>1</span> </div> <div className="size-25 bg-red-500 hover:has-[span]:bg-blue-500">2</div>
# 주요 변형 이해
# 다크 모드
Windows, macOS 등의 여러 운영체제에서 라이트 혹은 다크 모드를 선택할 수 있고, 이는 브라우저에도 자동으로 적용됩니다.
CSS @media
규칙의 prefers-color-scheme
특성을 통해 다크 모드를 확인할 수 있습니다.
1234567@media (prefers-color-scheme: dark) { html { color-scheme: dark; background-color: oklch(0.13 0.028 261.692); /* bg-gray-900 */ color: #fff; } }
Tailwind CSS를 사용하면, dark:
변형을 통해 다크 모드일 때 어떤 스타일을 사용할지만 지정하면 됩니다.
1<input className="border-1 border-gray-300 dark:border-gray-600" />
만약 다크 모드를 자동이 아닌 수동으로 제어하려면 다음과 같이 @custom-variant
디렉티브를 사용할 수 있습니다.
요소가 dark
클래스를 직접 가지고 있거나 그의 후손 요소인 경우에 dark:
변형이 동작합니다.
123@import "tailwindcss"; @custom-variant dark (&:where(.dark, .dark *));
이제 수동으로 dark
클래스를 추가합니다.
문서 전체를 제어하기 위해 주로 <html>
에 추가합니다.
1234<!doctype html> <html lang="ko" class="dark"> <!-- ... --> </html>
dark
클래스를 추가하는 것으로 다크 모드의 스타일이 자동으로 적용되는 것은 아닙니다.
원하는 스타일이 있다면 직접 작성해야 합니다.
12345html.dark { color-scheme: dark; background-color: oklch(0.13 0.028 261.692); /* bg-gray-900 */ color: #fff; }
# 미디어 쿼리
Tailwind CSS는 sm
부터 2xl
까지 5개의 중단점(Breakpoints)을 제공하며, 일반적인 모바일 우선 중단점과 반대로 데스크탑 우선 시스템도 같이 제공합니다.
데스크탑 우선 개발에서는max-*
변형을 사용하게 됩니다.
변형 | CSS | 너비(px) |
---|---|---|
sm |
@media (width >= 40rem) |
640px |
md |
@media (width >= 48rem) |
768px |
lg |
@media (width >= 64rem) |
1024px |
xl |
@media (width >= 80rem) |
1280px |
2xl |
@media (width >= 96rem) |
1536px |
max-sm |
@media (width < 40rem) |
640px |
max-md |
@media (width < 48rem) |
768px |
max-lg |
@media (width < 64rem) |
1024px |
max-xl |
@media (width < 80rem) |
1280px |
max-2xl |
@media (width < 96rem) |
1536px |
max-[...] |
@media (width < ...) |
|
min-[...] |
@media (width >= ...) |
이해를 위해 간단한 예제를 살펴봅시다.
다음 예제에서 요소의 배경 색상은 뷰포트 가로 너비가 767px
이하이면 빨간색이, 768px
이상이면 (md:
) 파란색이 적용됩니다.
1<div className="size-25 bg-red-500 md:bg-blue-500"></div>
다음 예제에서 요소의 배경 색상은 뷰포트 가로 너비가 767px
이하(max-md:
)이면 파란색이, 768px
이상이면 빨간색이 적용됩니다.
1<div className="size-25 bg-red-500 max-md:bg-blue-500"></div>
변형을 중첩해 중단점 범위를 지정할 수도 있습니다.
다음 예제에서 요소의 배경 색상은 기본적으로 빨간색이며, 뷰포트 가로 너비가 768px
이상이고 1279px
이하일 때 파란색이 적용됩니다.
1<div className="size-25 bg-red-500 md:max-xl:bg-blue-500"></div>
나만의 중단점을 정의하고 싶다면, Tailwind CSS @theme
디렉티브에서 --breakpoint-*
기본 테마 변수를 수정합니다.
다음 예제는 Bootstrap에서 사용하는 중단점입니다.
1234567@theme { --breakpoint-sm: 576px; --breakpoint-md: 768px; --breakpoint-lg: 992px; --breakpoint-xl: 1200px; --breakpoint-2xl: 1400px; }
# 컨테이너 쿼리
컨테이너 쿼리도 앞서 살펴본 미디어 쿼리와 비슷한 패턴입니다.3xs
부터 7xl
까지 13개의 중단점을 제공하고 max-*
변형을 사용할 수 있으며 범위를 지정하는 것도 가능합니다.
다만 뷰포트를 기준으로 하는 미디어 쿼리와 다르게, 컨테이너 쿼리는 @container
클래스로 기준 상위 요소를 지정하고, 사용하는 변형 앞에 @
기호를 사용해야 합니다.
다음 예제에서 세 번째 <div>
의 배경 색상은 @container
클래스가 지정된 첫 번째 <div>
의 너비가 3xl
(768px
) 이상이면 파란색이, 767px 이하이면 빨간색이 적용됩니다.
12345<div className="@container max-w-[1000px] border-2"> <div> <div className="size-25 bg-red-500 @3xl:bg-blue-500"></div> </div> </div>
3xs
같은 기본 컨테이너 쿼리의 중단점이 특정 상황에 맞지 않는 경우, @min-[768px]
처럼 임의 값을 사용할 수도 있습니다.
다음 예제는 이전 예제와 같은 결과를 출력됩니다.
12345<div className="@container max-w-[1000px] border-2"> <div> <div className="size-25 bg-red-500 @min-[768px]:bg-blue-500"></div> </div> </div>
# 하위(자식) 요소 스타일링
대부분의 경우 유틸리티 클래스는 해당 요소에 직접 작성하지만, 간혹 상위 요소에서의 스타일 작성이 필요할 수도 있습니다.
이때 *:
변형으로 자식 요소를, **:
변형으로 하위 요소의 스타일을 지정할 수 있습니다.
다음 예제에서는 <ul>
에 지정한 클래스를 통해, <li>
에는 내부 여백이, href
속성을 가진 <a>
에는 글자색이 적용됩니다.
따라서 <a>Apple</a>
에는 글자색이 적용되지 않습니다.
1234567891011<ul className="*:p-2 **:[a[href]]:text-red-400"> <li> <a>Apple</a> </li> <li> <a href="#">Banana</a> </li> <li> <a href="#">Cherry</a> </li> </ul>
만약 하위 요소에서 상위 요소의 상태에 따라 스타일을 적용하고 싶다면, group-*:
변형을 사용할 수 있습니다.
먼저 상위 요소에 group
클래스를 추가해 대상을 특정합니다.
그리고 하위 요소에 group-*:
변형과 함께 상위 요소의 상태(선택자, 유틸리티 클래스, 변형 등)에 따라 적용할 유틸리티 클래스를 작성합니다.
다음 예제의 각 <p>
는 <li>
에 호버하거나 active
클래스가 있는 경우에만 표시됩니다.
따라서 답변 B
는 바로 표시되며, 답변 A
는 호버 시 표시됩니다.
12345678910111213<section> <h2>자주 묻는 질문</h2> <ul> <li className="group"> <h4>질문 A</h4> <p className="invisible group-[.active]:visible group-hover:visible">답변 A</p> </li> <li className="group active"> <h4>질문 B</h4> <p className="invisible group-[.active]:visible group-hover:visible">답변 B</p> </li> </ul> </section>
# 형제 요소 스타일링
형제 요소의 상태에 따라 스타일을 적용할 수도 있습니다.
먼저 형제 요소에 peer
클래스를 추가해 대상을 특정합니다.
그리고 peer-*:
변형과 함께 형제 요소의 상태에 따라 적용할 유틸리티 클래스를 작성합니다.
다음 예제에서는 <p>
는 <input>
의 이메일 양식 유효성에 따라 화면에 표시됩니다.
참고로 invalid:
변형은 CSS :invalid
가상 클래스와 같습니다.
123456789<label> <input type="email" className="peer" /> <p className="invisible text-red-500 peer-invalid:visible"> 이메일 양식에 맞게 작성해 주세요. </p> </label>
# 변형과 CSS 표
변형이 무엇인지 이해했다면, 다음 표를 통해 어떤 변형이 있고 어떤 CSS 내용과 일치하는지 가볍게 살펴보세요.
모든 것을 다 기억할 필요는 없습니다!
필요한 경우 각 변형 이름을 선택해 Tailwind CSS의 해당 문서로 이동하면 구체적인 내용을 확인할 수 있습니다.
변형 | CSS |
---|---|
hover | @media (hover: hover) { &:hover } |
focus | &:focus |
focus-within | &:focus-within |
focus-visible | &:focus-visible |
active | &:active |
visited | &:visited |
target | &:target |
* | :is(& > *) |
** | :is(& *) |
has-[...] | &:has(...) |
group-[...] | &:is(:where(.group)... *) |
peer-[...] | &:is(:where(.peer)... ~ *) |
in-[...] | :where(...) & |
not-[...] | &:not(...) |
inert | &:is([inert], [inert] *) |
first | &:first-child |
last | &:last-child |
only | &:only-child |
odd | &:nth-child(odd) |
even | &:nth-child(even) |
first-of-type | &:first-of-type |
last-of-type | &:last-of-type |
only-of-type | &:only-of-type |
nth-[...] | &:nth-child(...) |
nth-last-[...] | &:nth-last-child(...) |
nth-of-type-[...] | &:nth-of-type(...) |
nth-last-of-type-[...] | &:nth-last-of-type(...) |
empty | &:empty |
disabled | &:disabled |
enabled | &:enabled |
checked | &:checked |
indeterminate | &:indeterminate |
default | &:default |
optional | &:optional |
required | &:required |
valid | &:valid |
invalid | &:invalid |
in-range | &:in-range |
out-of-range | &:out-of-range |
placeholder-shown | &:placeholder-shown |
autofill | &:autofill |
read-only | &:read-only |
before | &::before |
after | &::after |
first-letter | &::first-letter |
first-line | &::first-line |
marker | &::marker, & *::marker |
selection | &::selection |
file | &::file-selector-button |
backdrop | &::backdrop |
placeholder | &::placeholder |
sm | @media (width >= 40rem) |
md | @media (width >= 48rem) |
lg | @media (width >= 64rem) |
xl | @media (width >= 80rem) |
2xl | @media (width >= 96rem) |
min-[...] | @media (width >= ...) |
max-sm | @media (width < 40rem) |
max-md | @media (width < 48rem) |
max-lg | @media (width < 64rem) |
max-xl | @media (width < 80rem) |
max-2xl | @media (width < 96rem) |
max-[...] | @media (width < ...) |
@3xs | @container (width >= 16rem) |
@2xs | @container (width >= 18rem) |
@xs | @container (width >= 20rem) |
@sm | @container (width >= 24rem) |
@md | @container (width >= 28rem) |
@lg | @container (width >= 32rem) |
@xl | @container (width >= 36rem) |
@2xl | @container (width >= 42rem) |
@3xl | @container (width >= 48rem) |
@4xl | @container (width >= 56rem) |
@5xl | @container (width >= 64rem) |
@6xl | @container (width >= 72rem) |
@7xl | @container (width >= 80rem) |
@min-[...] | @container (width >= ...) |
@max-3xs | @container (width < 16rem) |
@max-2xs | @container (width < 18rem) |
@max-xs | @container (width < 20rem) |
@max-sm | @container (width < 24rem) |
@max-md | @container (width < 28rem) |
@max-lg | @container (width < 32rem) |
@max-xl | @container (width < 36rem) |
@max-2xl | @container (width < 42rem) |
@max-3xl | @container (width < 48rem) |
@max-4xl | @container (width < 56rem) |
@max-5xl | @container (width < 64rem) |
@max-6xl | @container (width < 72rem) |
@max-7xl | @container (width < 80rem) |
@max-[...] | @container (width < ...) |
dark | @media (prefers-color-scheme: dark) |
portrait | @media (orientation: portrait) |
landscape | @media (orientation: landscape) |
motion-safe | @media (prefers-reduced-motion: no-preference) |
motion-reduce | @media (prefers-reduced-motion: reduce) |
contrast-more | @media (prefers-contrast: more) |
contrast-less | @media (prefers-contrast: less) |
@media print |
|
supports-[…] | @supports (…) |
aria-busy | &[aria-busy="true"] |
aria-checked | &[aria-checked="true"] |
aria-disabled | &[aria-disabled="true"] |
aria-expanded | &[aria-expanded="true"] |
aria-hidden | &[aria-hidden="true"] |
aria-pressed | &[aria-pressed="true"] |
aria-readonly | &[aria-readonly="true"] |
aria-required | &[aria-required="true"] |
aria-selected | &[aria-selected="true"] |
aria-[…] | &[aria-…] |
data-[...] | &[data-...] |
rtl | &:where(:dir(rtl), [dir="rtl"], [dir="rtl"] *) |
ltr | &:where(:dir(ltr), [dir="ltr"], [dir="ltr"] *) |
open | &:is([open], :popover-open, :open) |
forced-colors | @media (forced-colors: active) |
starting | @starting-style |
# 디렉티브
디렉티브(Directive)는 HTML이 아닌 CSS에 사용하는 Tailwind CSS의 특별한 규칙입니다.
자주 사용할 주요 디렉티브를 살펴봅시다.
# @theme
글꼴, 색상, 중단점 등의 Tailwind CSS가 사용하는 기본 테마 변수(Theme Variables)를 다시 정의하거나 새로운 테마 변수를 생성할 수도 있습니다.
1234567891011121314@theme { /* 기본 테마 변수 수정 */ --font-sans: 'Pretendard', sans-serif; /* ui-sans-serif, system-ui, ... */ --color-white: #e9e9e9; /* #fff */ --color-black: #1a1a1a; /* #000 */ --spacing: 10px; /* 0.25rem */ --radius-md: 10px; /* 0.375rem */ --breakpoint-md: 800px; /* 768px */ --breakpoint-lg: 1100px; /* 1024px */ --breakpoint-xl: 1400px; /* 1280px */ /* 사용자 전용 테마 변수 생성 */ --color-my-highlight: #e96900; }
1<div className="bg-(--color-my-highlight)"</div>
재정의할 수 있는 테마 변수의 네임스페이스는 다음과 같습니다.
테마 변수 | 유틸리티 클래스 |
---|---|
--color-* |
bg-red-500 , text-sky-300 등의 색상 관련 |
--font-* |
font-sans 등의 글꼴 관련 |
--text-* |
text-xl 등의 텍스트 크기 |
--font-weight-* |
font-bold 등의 글꼴 두께 |
--tracking-* |
tracking-wide 등의 글자 간격 |
--leading-* |
leading-tight 등의 줄 간격 |
--breakpoint-* |
sm:* 등의 미디어 쿼리 중단점 |
--container-* |
@sm:* , max-w-md 등의 컨테이너 쿼리 중단점 |
--spacing-* |
px-4 , max-h-16 등의 여백 관련 |
--radius-* |
rounded-sm 등의 모서리 둥글기 |
--shadow-* |
shadow-md 등의 그림자 |
--inset-shadow-* |
inset-shadow-xs 등의 내부 그림자 |
--drop-shadow-* |
drop-shadow-md 등의 필터 그림자 |
--blur-* |
blur-md 등의 블러 효과 |
--perspective-* |
perspective-near 등의 원근 효과 |
--aspect-* |
aspect-video 등의 비율 |
--ease-* |
ease-out 등의 타이밍 함수 |
--animate-* |
animate-spin 등의 애니메이션 |
# @utility
@utility
디렉티브로 나만의 유틸리티 클래스를 정의할 수 있습니다.
12345@utility heropy-box-300 { width: 300px; height: 300px; border-radius: 10px; }
1<div className="heropy-box-300"></div>
조금 더 복잡한 유틸리티 클래스를 정의할 수도 있습니다.
단위가 없는 단순 숫자를 전달할 때 --value()
함수에 number
혹은 integer
타입을 사용할 수 있습니다.
이러한 방식으로 사용할 수 있는 percentage
, ratio
등의 몇 가지 타입이 더 있지만, 사용이 직관적이지 않고 속성에 따라 다르며 모든 값에 대응하지 않기 때문에 추천하기 어렵습니다.
12345@utility heropy-box-* { width: --value(number)px; height: --value(number)px; border-radius: 10px; }
123<div className="heropy-box-100"></div> <div className="heropy-box-300"></div> <div className="heropy-box-500"></div>
그래서 여러 값을 사용할 수 있는 유틸리티 클래스를 만든다면, []
를 사용하는 임의 값 방식을 추천합니다.
와일드카드 문자를 사용해 --value()
함수에 모든 값을 할당할 수 있습니다.
12345@utility heropy-box-* { width: --value([*]); height: --value([*]); border-radius: 10px; }
123<div className="heropy-box-[300px]"></div> <div className="heropy-box-[50%]"></div> <div className="heropy-box-[10dvw]"></div>
색상의 경우, --color-*
테마 변수를 지정합니다.
그러면 Tailwind CSS에서 제공하는 여러 색상을 지정할 수 있습니다.
123456@utility heropy-box-color-* { width: 100px; height: 100px; border-radius: 10px; background-color: --value(--color-*); }
12<div className="heropy-box-color-red-500"></div> <div className="heropy-box-color-blue-400"></div>
--value()
함수와 같이 --modifier()
함수를 사용할 수도 있습니다.
그러면 text-sm/6
이나 bg-red-500/70
처럼 /
기호를 사용하는 추가 값을 처리할 수 있습니다.
1234567@utility heropy-box-color-* { width: 100px; height: 100px; border-radius: 10px; background-color: --value(--color-*); opacity: --modifier(number); }
12<div className="heropy-box-color-red-500"></div> <div className="heropy-box-color-blue-400/0.5"></div>
# @apply
@apply
디렉티브를 사용하면 CSS에서 유틸리티 클래스를 사용할 수 있습니다.
다음 예제는 새로운 유틸리티 클래스를 정의할 때, @apply
를 사용하지 않고 CSS 속성과 값을 직접 작성했습니다.
12345@utility heropy-flex-center { display: flex; align-items: center; justify-content: center; }
다음 예제는 이전 예제와 같은 결과를 출력하지만, CSS 속성 대신 유틸리티 클래스를 사용했습니다.
유틸리티 클래스에 익숙하다면 보다 편리한 방법일 수 있습니다.
123@utility heropy-flex-center { @apply flex items-center justify-center; }
1<div className="heropy-flex-center"></div>
# @variant
@variant
디렉티브를 사용해 유틸리티 클래스에 변형을 추가할 수 있습니다.
다음 예제는 heropy-box-*
클래스를 가진 요소의 호버 스타일을 정의합니다.
123456789@utility heropy-box-* { width: --value([*]); height: --value([*]); border-radius: 10px; transition: transform 0.3s; @variant hover { transform: scale(1.1); } }
이전 예제를 @apply
디렉티브를 사용해 다음과 같이 수정할 수도 있습니다.
123456@utility heropy-box-* { @apply size-[--value([*])] rounded-[10px] transition duration-300; @variant hover { @apply scale-[1.1]; } }
1<div className="heropy-box-[300px]"></div>
@variant
디렉티브를 여러 번 중첩할 수도 있습니다.
다음 예제는 포커스된 요소에서 호버 시에만 배경 색상(bg-blue-100
)이 적용됩니다.
123456789@utility heropy-input { @apply rounded-[10px] transition duration-300; @variant focus { @apply border-blue-400; @variant hover { @apply bg-blue-100; } } }
1<input className="heropy-input" />
# @custom-variant
@custom-variant
디렉티브를 사용해 나만의 변형을 만들 수도 있습니다.
상위 요소의 data-state
HTML 속성 값이 open
일 때의 상태를 확인하는 state-open:
변형을 만들었습니다.
1@custom-variant state-open '[data-state="open"] &';
첫 번째 <div>
의 data-state
속성이 open
인 경우, 두 번째 <div>
의 배경 색상이 파란색으로 적용됩니다.
123456789101112131415import { useState } from 'react' export default function () { const [isActive, setIsActive] = useState(false) return ( <> <div data-state={isActive ? 'open' : 'close'} onClick={() => setIsActive(!isActive)}> <div className="state-open:bg-blue-500"></div> </div> </> ) }
# 추가 사용 패턴
# 접두사 사용
이미 프로젝트에 존재하는 클래스와 유틸리티 클래스가 충돌하는 경우나 유틸리티 클래스를 명확하게 표시하고 싶은 경우에 다음과 같이 접두사 옵션을 사용할 수 있습니다.
1@import "tailwindcss" prefix(tw);
이제 유틸리티 클래스에는 항상 tw:
접두사를 추가해야 합니다.
1<div className="tw:h-25 tw:w-25 tw:bg-red-500"></div>
# 우선순위
특수한 상황에 유틸리티 클래스를 강제로 적용해야 한다면, 이름 끝에 !
를 추가해 !important
를 적용할 수 있습니다.
다음 예제에서는 h-50
클래스가 우선합니다.
1<div className="h-50! h-100"></div>
# 동적 스타일 적용
Tailwind CSS는 프로젝트에서 유틸리티 클래스를 스캔해 실제로 사용한 클래스를 기반으로 필요한 CSS를 생성합니다.
따라서 클래스를 동적으로 구성하지 않아야 합니다.
예를 들어, count
상태가 값이 증가할 때마다 가로 너비가 늘어나는 요소를 만들려고 할 때, 다음 예시의 w-[${width}px]
처럼 유틸리티 클래스가 동적으로 구성되도록 작성하면 해당 스타일은 적용되지 않습니다.
12345678910111213import { useState } from 'react' export default function () { const [width, setWidth] = useState(100) return ( <div className={`w-[${width}px] rounded-md bg-blue-500 p-3 font-bold text-white transition duration-300`} onClick={() => setWidth(width + 20)}> {width}px </div> ) }
따라서 다음과 같이 별도의 스타일 바인딩이 필요합니다.
1234567891011121314import { useState } from 'react' export default function () { const [width, setWidth] = useState(100) return ( <div className={`rounded-md bg-blue-500 p-3 font-bold text-white transition duration-300`} style={{ width: `${width}px` }} onClick={() => setWidth(width + 20)}> {width}px </div> ) }
모든 동적 데이터에 대해 개별적으로 인라인 스타일을 바인딩하지 않고, 다음 예시처럼 유틸리티 클래스들을 미리 정의한 후 필요할 때 적용되도록 작성할 수도 있습니다.
12345678910111213141516171819export default function Button({ color, children }: { color: 'primary' | 'secondary' | 'error' children: React.ReactNode }) { const colors = { primary: 'bg-blue-500 hover:bg-blue-600 text-white', secondary: 'bg-gray-400 hover:bg-gray-500 text-black', error: 'bg-red-500 hover:bg-red-600 text-white' } return ( <button className={`${colors[color]} cursor-pointer rounded-md px-3 py-2 transition duration-200`}> {children} </button> ) }
스타일의 재사용성을 높이고 유지보수를 용이하게 하기 위해, CVA(Class Variance Authority) 라이브러리를 사용해 유틸리티 클래스를 구성할 수도 있습니다.
1npm i class-variance-authority
123456789101112131415161718192021222324252627import { cva, type VariantProps } from 'class-variance-authority' const buttonStyles = cva( 'cursor-pointer rounded-md px-3 py-2 transition duration-200', { variants: { color: { primary: 'bg-blue-500 hover:bg-blue-600 text-white', secondary: 'bg-gray-400 hover:bg-gray-500 text-black', error: 'bg-red-500 hover:bg-red-600 text-white' } }, defaultVariants: { color: 'primary' } } ) export default function Button({ color, children }: { color?: VariantProps<typeof buttonStyles>['color'] children: React.ReactNode }) { return <button className={buttonStyles({ color })}>{children}</button> }
cva
함수에 작성하는 유틸리티 클래스는 단순한 JS 문자열이기 때문에 자동 완성이나 프리뷰 기능이 동작하지 않습니다.
만약 cva
혹은 cx
함수에서 Tailwind CSS IntelliSense 확장 프로그램의 기능을 활용하려면 다음과 같이 구성할 수 있습니다.
123456{ "tailwindCSS.experimental.classRegex": [ ["cva\\(((?:[^()]|\\([^()]*\\))*)\\)", "[\"'`]([^\"'`]*).*?[\"'`]"], ["cx\\(((?:[^()]|\\([^()]*\\))*)\\)", "(?:'|\"|`)([^']*)(?:'|\"|`)"] ] }
zoom_out_map
cva 함수에서 유틸리티 클래스의 색상 표시, 자동 완성, 프리뷰 등의 기능을 사용
# 스타일 병합
Tailwind CSS를 사용하면서, 단일 요소에 중복되는 유틸리티 클래스가 적용되면 어떤 클래스가 더 우선할지 쉽게 알 수 없고 원하는 스타일이 적용되지 않을 수도 있습니다.
다음 예시에서 isActive
상태는 true
이지만 w-25
, bg-blue-500
클래스의 스타일은 요소에 적용되지 않습니다.w-75
, bg-red-500
클래스가 더 우선하기 때문입니다.
참고로 이런 상황의 우선순위는 명시도가 아닌 선언 순서로 결정되기에 Tailwind CSS 빌드에 의해 얼마든지 달라 수 있으므로 패턴을 이해하는 것이 무의미합니다.
그렇다면 이러한 상황을 어떻게 해결해야 할까요?
12345678910import { useState } from 'react' export default function App() { const [isActive] = useState(true) return ( <> <div className={`h-25 w-75 bg-red-500 ${isActive && 'w-25 bg-blue-500'}`}></div> </> ) }
이때 비로 tailwind-merge 라이브러리를 사용해 문제를 해결할 수 있습니다.
먼저 라이브러리를 설치합니다.
1npm i tailwind-merge
라이브러리의 twMerge()
함수는 더 마지막에 적용된 클래스가 우선하도록 만들어 줍니다.
첫 번째 <div>
처럼 단일 인수로 나열하는 것도 가능하지만, 코드 포맷팅으로 인해 클래스 순서는 바뀔 수 있습니다.
따라서 두 번째 <div>
처럼 여러 인수로 나눠서 순서대로 작성하면 더 명확하고 안전하게 우선순위를 지정할 수 있습니다.
이제 isActive
상태에 맞게 w-25
, bg-blue-500
클래스의 스타일이 적용됩니다.
12// 숫자가 증가할 수록 더 우선합니다! twMerge('우선순위1', '우선순위2', '우선순위3')
12345678910111213141516171819import { useState } from 'react' import { twMerge } from 'tailwind-merge' export default function App() { const [isActive] = useState(true) return ( <> <div className={twMerge( `h-25 w-75 bg-red-500 ${isActive && 'w-25 bg-blue-500'}` )}></div> <div className={twMerge( `h-25 w-75 bg-red-500`, isActive && 'w-25 bg-blue-500' )}></div> </> ) }
간혹 우선순위와 상관없이 단순히 유틸리티 클래스를 합치는 작업이 필요할 수도 있습니다.
tailwind-merge를 사용하고 있으니, clsx 대신 twJoin()
함수를 사용해 보세요.
1234567891011121314151617181920import { useState } from 'react' import { twJoin } from 'tailwind-merge' export default function App() { const [isActive] = useState(true) const colorRed = 'bg-red-500' return ( <> <div className={twJoin( ['h-25 w-25'], colorRed, isActive && 'bg-blue-500', ['w-75'] )}></div> {/* ⬇️ 다음과 같이 합쳐집니다. */} <div className="h-25 w-25 w-75 bg-blue-500 bg-red-500"></div> </> ) }
끝까지 읽어주셔서 감사합니다.
좋아요와 응원 댓글은 블로그 운영에 큰 힘이 됩니다!