Tailwind CSS · 반응형, 상태, 다크모드
지금까지는 "기본 모양"을 만드는 클래스였습니다. 이번엔 접두사를 붙여
"언제 적용할지"를 정합니다. 화면이 넓을 때만(md:), 마우스를 올렸을 때만(hover:),
어두운 테마일 때만(dark:) 같은 조건을 클래스 앞에 붙이는 방식이죠.
bg-blue-500은 항상 켜진 전등이고,
hover:bg-blue-500은 "마우스를 올리면 켜지는" 전등이에요. 같은 색이라도 켜지는 조건이 다릅니다.1. 반응형 — 모바일 우선(mobile-first)
Tailwind? 의 반응형은 "작은 화면이 기본"이라는 원칙으로 동작합니다.
접두사가 없는 클래스는 모든 크기에 적용되는 기본값이고,
sm: md: lg: xl: 2xl: 접두사가 붙은 클래스는
그 폭 이상에서만 적용됩니다. "최소 폭" 기준이라 작은 화면에는 영향을 주지 않아요.
md:)를 덧입는다고
생각하면 됩니다. 작은 화면=얇게, 큰 화면=덧입기.v4의 기본 브레이크포인트는 다음과 같습니다(공식 기준).
| 접두사 | 최소 폭 | px 환산 | 의미 |
|---|---|---|---|
| (없음) | 0 | 0px | 모든 크기 — 기본값 |
sm: | 40rem | 640px | 작은 화면 이상 |
md: | 48rem | 768px | 태블릿 이상 |
lg: | 64rem | 1024px | 노트북 이상 |
xl: | 80rem | 1280px | 데스크톱 이상 |
2xl: | 96rem | 1536px | 큰 데스크톱 이상 |
흔한 패턴 두 가지 — 글자 크기와 그리드 열 수를 화면에 따라 바꿉니다.
<!-- 기본 작게 → md 이상 보통 → lg 이상 크게 --> <p class="text-sm md:text-base lg:text-lg">화면 따라 커지는 글자</p> <!-- 모바일은 1열, md 이상에서 3열 그리드 --> <div class="grid grid-cols-1 md:grid-cols-3 gap-4"> <div>...</div> <div>...</div> <div>...</div> </div>
읽는 법: grid-cols-1(기본 1열)이 모든 화면에 깔리고, 폭이 48rem(md)을 넘으면 md:grid-cols-3가 덮어써서 3열이 됩니다.
정해진 sm/md/lg로 부족하면 대괄호로 직접 폭을 지정할 수 있습니다.
min-[600px]:flex 는 "폭 600px 이상에서 flex". 반대로 max-[600px]:hidden 처럼
max- 로 "그 폭 미만"도 됩니다. md:max-lg:flex 처럼 두 개를 겹치면 "md~lg 사이 구간만"도 가능해요.
아래 라이브 데모의 실행 결과 창(iframe)은 폭이 좁아 sm:/md: 변화가 잘 드러나지 않습니다.
그래서 반응형은 위처럼 코드로 설명하고, 아래 라이브 데모는 직접 만져볼 수 있는 hover·focus·group·peer·dark 같은 상태 위주로 준비했습니다.
2. 상태 variant — hover, focus, ...
variant(배리언트)는 "어떤 상태일 때 적용"을 뜻하는 접두사입니다. 마우스를 올렸을 때, 클릭 중일 때, 입력칸이 비활성일 때 등
평소 CSS? 의 :hover :focus 가상 클래스에 해당하는 것들을 클래스 앞에 붙여 씁니다.
| variant | 적용 시점 |
|---|---|
hover: | 마우스를 올렸을 때 |
focus: / focus-visible: | 포커스됐을 때 / 키보드 포커스일 때만 |
focus-within: | 자식 중 하나라도 포커스됐을 때 |
active: | 누르고 있는 동안 |
visited: | 방문한 링크 |
disabled: | 비활성 상태(disabled) |
checked: / required: | 체크된 입력 / 필수 입력 |
placeholder: | 입력칸의 안내 글자(placeholder) |
first: / last: | 형제 중 첫째 / 막내 |
odd: / even: | 홀수 번째 / 짝수 번째(줄무늬 표) |
empty: | 내용이 비어 있을 때 |
before: / after: | 가상 요소(앞/뒤에 끼우는 장식) |
<!-- 평소엔 파랑, 올리면 진한 파랑, 누르면 더 진하게 --> <button class="bg-blue-500 hover:bg-blue-600 active:bg-blue-700 text-white px-4 py-2 rounded"> 버튼 </button> <!-- 줄무늬 표: 짝수 줄만 회색 배경 --> <tr class="even:bg-gray-100">...</tr> <!-- 가상 요소: 필수 항목 뒤에 빨간 별 붙이기 (content 필요) --> <label class="after:content-['*'] after:text-red-500 after:ml-1">이름</label>
before:/after: 는 거의 항상 content-[''](또는 content-['★'] 같은 내용)와 짝으로 씁니다. 내용이 없으면 가상 요소가 화면에 안 나타나요.
버튼 하나로 hover·focus를 직접 만져보세요. (마우스를 올리거나 Tab으로 포커스)
3. group / peer — 부모·형제 상태로 제어
상태 variant는 보통 "자기 자신"의 상태를 봅니다. 그런데 "부모에 마우스를 올리면 자식 색이 바뀌게" 하고 싶을 때가 있죠. 이때 두 가지 도구를 씁니다.
group— 부모 요소에class="group"을 달면, 자식에서group-hover:group-focus:로 부모의 상태를 보고 반응합니다.peer— 앞선 형제 요소에class="peer"를 달면, 뒤따르는 형제에서peer-checked:peer-focus:로 그 형제의 상태를 보고 반응합니다.
group은 "부모가 웃으면 아이도 따라 웃기", peer는 "옆 친구가 손을 들면 나도 반응하기"입니다.
group은 위→아래(부모→자식), peer는 옆→옆(형제→형제) 방향이에요.<!-- group: 카드(부모)에 올리면 안의 화살표가 움직임 --> <a class="group block p-4 border rounded"> 더 보기 <span class="inline-block group-hover:translate-x-1 transition">→</span> </a> <!-- peer: 체크박스(형제)가 켜지면 뒤의 라벨 색이 바뀜 --> <input type="checkbox" class="peer" id="agree"> <label for="agree" class="peer-checked:text-green-600 peer-checked:line-through"> 동의함 </label>
주의: peer 는 HTML 구조상 먼저 나온 형제의 상태만 볼 수 있습니다(CSS의 형제 선택자 한계). 그래서 입력칸을 라벨보다 앞에 둡니다.
카드에 마우스를 올려보세요(group). 화살표가 움직이고 테두리 색이 바뀝니다.
체크박스를 직접 켜고 꺼보세요(peer). 켜면 라벨 모양과 안내 문구가 바뀝니다.
위 데모에서 두 번째 문단은 라벨 바깥이라 반응하지 않는 게 정상입니다. peer-checked: 는 같은 부모 안에서 체크박스 뒤에 오는 형제에만 닿습니다.
4. data-* / aria-* variant
요즘 컴포넌트는 "열림/닫힘", "선택됨" 같은 상태를 data-state 나 aria-expanded 같은 속성으로 표시합니다.
Tailwind는 이 속성값을 직접 보고 스타일을 바꿀 수 있어요.
<!-- data-state 가 open 일 때만 적용 --> <div data-state="open" class="data-[state=open]:bg-blue-50 data-[state=closed]:hidden"> 패널 </div> <!-- aria-expanded 가 true 일 때 화살표 회전 --> <button aria-expanded="true" class="aria-[expanded=true]:rotate-180">▾</button>
이 패턴은 Shadcn UI(내부적으로 Radix)에서 핵심입니다. Radix가 컴포넌트 상태를
data-[state=open], data-[side=top] 등으로 자동으로 달아 주면, 우리는 그에 맞춰
data-[state=open]:animate-in 같은 클래스로 모양만 입히면 됩니다. 자세한 건 Shadcn 챕터에서 다룹니다.
5. 다크모드 — dark:
어두운 테마용 스타일은 dark: 접두사로 답니다. 평소 색을 기본으로 두고, 어두울 때 덮어쓸 색을 dark: 로 더해요.
<!-- 밝을 땐 흰 배경/검은 글자, 어두울 땐 짙은 배경/밝은 글자 --> <div class="bg-white text-gray-900 dark:bg-gray-900 dark:text-gray-100"> 본문 </div>
Tailwind v4 에서 dark: 의 기본 동작은 운영체제의 다크모드 설정(prefers-color-scheme)을 따릅니다.
즉 따로 설정하지 않으면 OS가 어두우면 dark: 가 켜집니다. 버튼으로 직접 토글하는 방식(.dark 클래스)으로
바꾸려면, CSS 파일에 아래 한 줄을 추가해 dark variant를 재정의해야 합니다.
/* app.css — .dark 클래스 토글 방식으로 변경 (v4) */ @import "tailwindcss"; @custom-variant dark (&:where(.dark, .dark *));
이렇게 두면 <html class="dark"> (또는 그 안 어떤 조상에든 .dark)가 있을 때만 dark: 가 적용됩니다.
그 다음 자바스크립트로 document.documentElement.classList.toggle("dark") 처럼 클래스를 켜고 끄면 됩니다.
이 문서 우상단의 🌙 버튼으로 테마를 바꿀 수 있습니다. 라이브 데모의 실행 창은 현재 사이트 테마를 그대로 따라갑니다
(테마가 어두우면 데모도 .dark 클래스로 실행돼요). 테마를 바꾼 뒤 데모의 실행 버튼을 다시 누르면 새 테마로 다시 그려집니다.
6. variant 조합 순서
여러 조건을 한 번에 걸 수도 있습니다. 접두사를 이어 붙이면 "모두 만족할 때"가 됩니다. 읽는 순서는 왼쪽이 바깥, 오른쪽이 안쪽이라고 보면 편해요.
<!-- md 이상이면서, 마우스를 올렸을 때만 --> <a class="underline md:hover:text-blue-600">링크</a> <!-- 다크모드이면서, md 이상일 때만 배경 변경 --> <div class="dark:md:bg-gray-800">...</div> <!-- 부모에 올렸고(group) + 다크모드일 때 --> <span class="dark:group-hover:text-white">...</span>
순서를 바꿔도(hover:md: ↔ md:hover:) 결과는 대개 같습니다. 둘 다 "두 조건 AND"라서요. 팀 안에서 한 가지 순서로 통일해 두면 읽기 편합니다.
한눈에 — variant 카탈로그
| 분류 | variant | 적용 시점 |
|---|---|---|
| 반응형 | sm: md: lg: xl: 2xl: | 그 폭 이상 |
| 반응형(임의) | min-[600px]: max-[600px]: | 지정 폭 이상 / 미만 |
| 마우스/포커스 | hover: focus: focus-visible: focus-within: active: | 올림 / 포커스 / 누름 |
| 폼 상태 | disabled: checked: required: placeholder: | 입력 요소 상태 |
| 구조 | first: last: odd: even: empty: | 형제 위치 / 비어 있음 |
| 가상 요소 | before: after: | 앞/뒤 장식(content 필요) |
| 부모/형제 | group-hover: group-focus: peer-checked: peer-focus: | 부모/형제 상태 |
| 속성 | data-[state=open]: aria-[expanded=true]: | 속성값 일치 |
| 다크 | dark: | 다크모드(시스템 또는 .dark) |
| 조합 | md:hover: dark:md: | 모든 조건 AND |
이 프로젝트 프론트엔드는 Tailwind CSS 4 로 화면을 짜며, 반응형 레이아웃·버튼 호버·다크모드 토글이 모두 이 variant들로 표현됩니다.
특히 data-[state=...] / aria-[...] variant는 Shadcn UI 컴포넌트의 동작 표현에 그대로 쓰이고,
실제 화면 구성은 웹앱 7장 UI 구성(07-ui) 에서 확인할 수 있습니다.
다음 단계
- 이 variant들을 그대로 활용하는 복사형 컴포넌트 → Shadcn UI
- variant 없이 쓰는 기본 유틸리티 복습 → Tailwind CSS 개요
- 화면 실전 구성 → 07. UI 구성