Tailwind CSS v3.1: 미쳤다고? 좋아, 한번 미쳐보자!
- Date
- Adam Wathan
Tailwind CSS v3.0을 출시한 지 약 6개월이 지났고, 그동안 코드베이스에 많은 작은 개선 사항을 모아왔지만, 아직까지 _“좋아, 이제 출시할 때가 됐다”_고 말할 만한 _그-하나의-기능_이 없었습니다.
그런데 몇 주 전 어느 토요일 밤, Discord에서 Robin과 문서 내부의 클래스를 사용해 html
엘리먼트를 타겟팅하는 방법에 대해 이야기하던 중, 임의의 변형(arbitrary variants)을 지원하면 어떻게 될지 설명했습니다. 이 아이디어는 1년 넘게 고민해온 것이었죠.
20분 후 Robin은 동작하는 개념 증명을 완성했고 (단 6줄의 코드로!), Jordan이 클래스 탐지 엔진에서 정규식 기적을 일으킨 지 약 1시간 후, 임의의 변형이 탄생했고, 출시할 만한 기능을 확보했습니다.
자, 이제 Tailwind CSS v3.1입니다! 모든 수정 사항과 개선 사항의 전체 목록은 릴리스 노트를 확인하세요. 주요 내용은 다음과 같습니다:
- 퍼스트파티 TypeScript 타입
- CLI에서 CSS 임포트 내장 지원
- theme 함수 사용 시 색상 불투명도 변경
- 쉬운 CSS 변수 색상 설정
- 테두리 간격 유틸리티
- 활성화 및 선택적 변형
- prefers-contrast 변형
- 네이티브 dialog 배경 스타일링
- 변형을 위한 임의 값
tailwindcss
의 최신 버전을 npm에서 설치해 프로젝트를 업그레이드하세요:
npm install tailwindcss@latest
또는 Tailwind Play를 열어 브라우저에서 새로운 기능들을 직접 시험해보세요.
First-party TypeScript 타입
이제 Tailwind를 사용할 때 작업하는 모든 JS API에 대한 타입을 제공합니다. 특히 tailwind.config.js
파일에 대한 타입을 포함합니다. 이를 통해 다양한 유용한 IDE 지원을 받을 수 있으며, 문서를 참조하지 않고도 설정을 변경하기가 훨씬 쉬워집니다.
설정을 적용하려면, 설정 정의 위에 타입 주석을 추가하면 됩니다:
/** @type {import('tailwindcss').Config} */
module.exports = {
content: [
// ...
],
theme: {
extend: {},
},
plugins: [],
}
TypeScript를 좋아하는 분이라면 실제 타입 정의를 살펴보는 재미를 느낄 수 있을 겁니다. 이렇게 복잡한 객체를 지원하기 위해 많은 흥미로운 작업이 이루어지고 있습니다.
CLI에 내장된 CSS 임포트 지원
여러분이 CSS를 컴파일하기 위해 CLI 도구를 사용한다면, 이제 postcss-import
가 기본적으로 내장되어 있어 추가 설정 없이도 커스텀 CSS를 여러 파일로 나눠 관리할 수 있습니다.
@import "tailwindcss/base";
@import "./select2-theme.css";
@import "tailwindcss/components";
@import "tailwindcss/utilities";
만약 CLI 도구를 사용하지 않고 Tailwind를 PostCSS 플러그인으로 사용한다면, autoprefixer
와 마찬가지로 postcss-import
를 직접 설치하고 설정해야 합니다. 하지만 CLI 도구를 사용한다면 이제 별도의 설정 없이도 바로 사용할 수 있습니다.
이 기능은 특히 독립형 CLI를 사용하면서 노드 의존성을 전혀 설치하고 싶지 않은 경우에 매우 유용합니다.
theme 함수를 사용할 때 색상 불투명도 조절하기
많은 사람들이 잘 모르고 있지만, Tailwind는 CSS 파일에서 theme()
함수를 제공합니다. 이 함수를 사용하면 설정 파일에서 값을 가져와 변수처럼 재사용할 수 있습니다.
.select2-dropdown {
border-radius: theme(borderRadius.lg);
background-color: theme(colors.gray.100);
color: theme(colors.gray.900);
}
/* ... */
하지만 이 방법으로 가져온 색상의 알파 채널을 조절할 수 없다는 한계가 있었습니다. 그래서 v3.1부터는 슬래시(/
) 구문을 사용해 불투명도를 조절할 수 있도록 지원했습니다. 이는 모던 rgb
및 hsl
CSS 색상 함수와 유사한 방식입니다.
.select2-dropdown {
border-radius: theme(borderRadius.lg);
background-color: theme(colors.gray.100 / 50%);
color: theme(colors.gray.900);
}
/* ... */
이 기능은 tailwind.config.js
파일의 theme
함수에서도 동작합니다.
module.exports = {
content: [
// ...
],
theme: {
extend: {
colors: ({ theme }) => ({
primary: theme('colors.blue.500'),
'primary-fade': theme('colors.blue.500 / 75%'),
})
},
},
plugins: [],
}
심지어 임의의 값에서도 이 기능을 사용할 수 있습니다. 이는 꽤 놀라운 일이며, 특히 특이한 커스텀 그라디언트 등을 만들 때 유용합니다.
<div class="bg-[image:linear-gradient(to_right,theme(colors.red.500)_75%,theme(colors.red.500/25%))]">
<!-- ... -->
</div>
CSS 파일을 수정하지 않아도 되는 방법이라면 뭐든 좋죠, 그렇죠?
더 쉬운 CSS 변수 색상 설정
CSS 변수로 색상을 정의하고 설정하는 것을 좋아한다면, 아마도 현재 tailwind.config.js
파일에 다음과 같은 끔찍한 보일러플레이트 코드가 있을 것입니다:
function withOpacityValue(variable) {
return ({ opacityValue }) => {
if (opacityValue === undefined) {
return `rgb(var(${variable}))`
}
return `rgb(var(${variable}) / ${opacityValue})`
}
}
module.exports = {
theme: {
colors: {
primary: withOpacityValue('--color-primary'),
secondary: withOpacityValue('--color-secondary'),
// ...
}
}
}
v3.1에서는 이 과정을 훨씬 덜 고통스럽게 만들었습니다. 함수를 사용하는 대신 포맷 문자열로 색상을 정의할 수 있도록 지원을 추가했습니다:
module.exports = {
theme: {
colors: {
primary: 'rgb(var(--color-primary) / <alpha-value>)',
secondary: 'rgb(var(--color-secondary) / <alpha-value>)',
// ...
}
}
}
opacityValue
인자를 받는 함수를 작성하는 대신, <alpha-value>
자리 표시자가 포함된 문자열을 작성하면 Tailwind가 유틸리티에 따라 올바른 알파 값으로 해당 자리 표시자를 대체합니다.
이 내용을 처음 보는 경우, 더 자세한 정보를 위해 업데이트된 CSS 변수 사용하기 문서를 확인해 보세요.
Border spacing 유틸리티
separate borders를 사용할 때 테이블 테두리 간의 간격을 조절할 수 있도록 새로운 border-spacing
속성 유틸리티를 추가했습니다.
State | City |
---|---|
Indiana | Indianapolis |
Ohio | Columbus |
Michigan | Detroit |
<table class="border-separate border-spacing-2 ..."> <thead> <tr> <th class="border border-slate-300 ...">State</th> <th class="border border-slate-300 ...">City</th> </tr> </thead> <tbody> <tr> <td class="border border-slate-300 ...">Indiana</td> <td class="border border-slate-300 ...">Indianapolis</td> </tr> <!-- ... --> </tbody> </table>
<table class="border-separate border-spacing-2 ..."> <thead> <tr> <th class="border border-slate-600 ...">State</th> <th class="border border-slate-600 ...">City</th> </tr> </thead> <tbody> <tr> <td class="border border-slate-700 ...">Indiana</td> <td class="border border-slate-700 ...">Indianapolis</td> </tr> <!-- ... --> </tbody> </table>
여러분은 아마 이런 생각을 할지도 모릅니다. “나는 이런 식의 테이블을 만들어 본 적이 없는데…” 하지만 잠시만 들어보세요!
이 기능이 정말 유용한 경우 중 하나는 스틱키 헤더 행이 있는 테이블을 만들 때, 헤더 아래에 고정된 하단 테두리를 유지하고 싶을 때입니다.
이 테이블을 스크롤하여 스틱키 헤더 행이 동작하는 것을 확인하세요
Name | Role |
---|---|
Courtney Henry | Admin |
Tom Cook | Member |
Whitney Francis | Admin |
Leonard Krasner | Owner |
Floyd Miles | Member |
Emily Selman | Member |
Kristin Watson | Admin |
Emma Dorsey | Member |
Alicia Bell | Admin |
Jenny Wilson | Owner |
Anna Roberts | Member |
Benjamin Russel | Member |
Jeffrey Webb | Admin |
Kathryn Murphy | Member |
<table class="border-separate border-spacing-0">
<thead class="bg-gray-50">
<tr>
<th class="sticky top-0 z-10 border-b border-gray-300 ...">Name</th>
<th class="sticky top-0 z-10 border-b border-gray-300 ...">Email</th>
<th class="sticky top-0 z-10 border-b border-gray-300 ...">Role</th>
</tr>
</thead>
<tbody class="bg-white">
<tr>
<td class="border-b border-gray-200 ...">Courtney Henry</td>
<td class="border-b border-gray-200 ...">courtney.henry@example.com</td>
<td class="border-b border-gray-200 ...">Admin</td>
</tr>
<!-- ... -->
</tbody>
</table>
여러분은 아마 border-collapse
를 사용하면 된다고 생각할 수 있습니다. 하지만 이 경우에는 border-separate
와 border-spacing-0
없이 테두리가 헤더 아래에 고정되지 않고 스크롤되어 사라질 것입니다. CSS는 정말 재미있죠?
더 자세한 내용은 border spacing 문서를 확인하세요.
활성화 및 선택적 상태 변형
:enabled
와 :optional
의사 클래스에 대한 새로운 변형을 추가했습니다. 이 변형들은 폼 엘리먼트가 활성화되어 있거나 선택적일 때 적용됩니다.
“하지만 Adam, 왜 이런 게 필요할까요? 활성화와 선택적 상태는 기본값인데, 웹사이트를 만드는 거 맞아요?”
아야, 맞는 말이라서 더 아픕니다. 저는 이제 거의 이메일을 쓰고 GitHub에서 같은 질문에 반복해서 답변만 하고 있으니까요.
하지만 이 비활성화된 버튼 예제를 한번 보세요:
<button type="button" class="bg-indigo-500 hover:bg-indigo-400 disabled:opacity-75 ..." disabled>
처리 중...
</button>
버튼 위에 마우스를 올렸을 때, 비활성화된 상태임에도 배경색이 바뀌는 걸 확인할 수 있습니다. 이전 버전에서는 보통 이렇게 고쳤을 겁니다:
<button type="button" class="disabled:hover:bg-indigo-500 bg-indigo-500 hover:bg-indigo-400 disabled:opacity-75 ..." disabled>
처리 중...
</button>
하지만 새로운 enabled
수식어를 사용하면 이렇게 작성할 수 있습니다:
<button type="button" class="bg-indigo-500 hover:enabled:bg-indigo-400 disabled:opacity-75 ..." disabled>
처리 중...
</button>
버튼이 비활성화되었을 때 호버 색상을 기본 색상으로 되돌리는 대신, hover
와 enabled
변형을 결합하여 버튼이 비활성화된 경우 호버 스타일을 아예 적용하지 않도록 했습니다. 이게 더 낫다고 생각합니다!
새로운 optional
수식어와 형제 상태 기능을 결합하여 필수가 아닌 필드에 대한 “필수” 알림을 숨기는 예제를 살펴보세요:
<form>
<div>
<label for="email" ...>이메일</label>
<div>
<input required class="peer ..." id="email" />
<div class="peer-optional:hidden ...">
필수
</div>
</div>
</div>
<div>
<label for="name" ...>이름</label>
<div>
<input class="peer ..." id="name" />
<div class="peer-optional:hidden ...">
필수
</div>
</div>
</div>
<!-- ... -->
</form>
이렇게 하면 모든 폼 그룹에 동일한 마크업을 사용하고, 조건부 렌더링을 직접 처리하는 대신 CSS가 처리하도록 할 수 있습니다. 꽤 깔끔하죠!
Prefers-contrast variants
prefers-contrast
미디어 쿼리를 알고 계셨나요? 이제 Tailwind에서 기본적으로 지원합니다.
새로운 contrast-more
와 contrast-less
변형을 사용하여 사용자가 더 높거나 낮은 대비를 요청했을 때 디자인을 수정할 수 있습니다. 보통 macOS의 “대비 증가”와 같은 운영 체제 접근성 설정을 통해 요청됩니다.
개발자 도구에서 `prefers-contrast: more`를 에뮬레이트하여 변경 사항을 확인해 보세요
<form>
<label class="block">
<span class="block text-sm font-medium text-slate-700">주민등록번호</span>
<input class="border-slate-200 placeholder-slate-400 contrast-more:border-slate-400 contrast-more:placeholder-slate-500"/>
<p class="mt-2 opacity-10 contrast-more:opacity-100 text-slate-600 text-sm">
여러분의 신원을 도용하기 위해 필요합니다.
</p>
</label>
</form>
이에 대해 문서를 작성했지만, 솔직히 여기서 더 많이 작성했습니다.
네이티브 다이얼로그 배경 스타일링
최근에 등장한 HTML <dialog>
엘리먼트는 꽤 괜찮은 브라우저 지원을 자랑하며, 최신 기술을 시도해보고 싶은 분들에게 추천할 만한 요소입니다.
다이얼로그에는 ::backdrop
이라는 새로운 의사 엘리먼트가 있습니다. 이는 다이얼로그가 열려 있을 때 렌더링되며, Tailwind CSS v3.1에서는 이 배경을 스타일링할 수 있는 새로운 backdrop
수정자를 추가했습니다:
<dialog class="backdrop:bg-slate-900/50 ...">
<form method="dialog">
<!-- ... -->
<button value="cancel">Cancel</button>
<button>Submit</button>
</form>
</dialog>
이 기능에 대해 더 깊이 알고 싶다면 MDN 다이얼로그 문서를 읽어보는 것을 추천합니다. 흥미로운 내용이 많지만, 알아야 할 것도 많습니다.
임의 값(variants용)
이 기능은 정말로 눈에 띄는 부분입니다. 여러분은 이미 addVariant
API를 통해 커스텀 variants를 만들 수 있다는 걸 알고 계실 겁니다.
const plugin = require('tailwindcss/plugin')
module.exports = {
// ...
plugins: [
plugin(function({ addVariant }) {
addVariant('third', '&:nth-child(3)')
})
]
}
그리고 HTML에서 직접 원하는 값을 사용할 수 있는 임의 값(arbitrary values) 기능도 알고 계실 겁니다.
<div class="top-[117px]">
<!-- ... -->
</div>
이제 Tailwind CSS v3.1에서는 임의 variants(arbitrary variants)를 도입하여 HTML에서 직접 임시 variants를 만들 수 있습니다.
<div class="[&:nth-child(3)]:py-0">
<!-- ... -->
</div>
이 기능은 특정 CSS 기능을 브라우저가 지원하는지 확인하는 @supports
쿼리와 같이, 매개변수화가 필요한 variants에 매우 유용합니다.
<div class="bg-white [@supports(backdrop-filter:blur(0))]:bg-white/50 [@supports(backdrop-filter:blur(0))]:backdrop-blur">
<!-- ... -->
</div>
심지어 [&>*]
와 같은 임의 variants를 사용해 자식 엘리먼트를 대상으로 스타일을 적용할 수도 있습니다.
-
Kristen Ramos
kristen.ramos@example.com
-
Floyd Miles
floyd.miles@example.com
-
Courtney Henry
courtney.henry@example.com
<ul role="list" class="[&>*]:p-4 [&>*]:bg-white [&>*]:rounded-lg [&>*]:shadow space-y-4">
<li class="flex">
<img class="h-10 w-10 rounded-full" src="..." alt="" />
<div class="ml-3 overflow-hidden">
<p class="text-sm font-medium text-slate-900">Kristen Ramos</p>
<p class="text-sm text-slate-500 truncate">kristen.ramos@example.com</p>
</div>
</li>
<!-- ... -->
</ul>
심지어 두 번째 li
안에 있는 div
의 첫 번째 p
엘리먼트에 hover
시 스타일을 적용할 수도 있습니다.
Try hovering over the text “Floyd Miles”
-
Kristen Ramos
kristen.ramos@example.com
-
Floyd Miles
floyd.miles@example.com
-
Courtney Henry
courtney.henry@example.com
<ul role="list" class="hover:[&>li:nth-child(2)>div>p:first-child]:text-indigo-500 [&>*]:p-4 [&>*]:bg-white [&>*]:rounded-lg [&>*]:shadow space-y-4">
<!-- ... -->
<li class="flex">
<img class="h-10 w-10 rounded-full" src="..." alt="" />
<div class="ml-3 overflow-hidden">
<p class="text-sm font-medium text-slate-900">Floyd Miles</p>
<p class="text-sm text-slate-500 truncate">floyd.miles@example.com</p>
</div>
</li>
<!-- ... -->
</ul>
이런 기능을 자주 사용해야 할까요? 그렇지는 않겠지만, 직접 변경할 수 없는 HTML을 스타일링할 때 꽤 유용한 탈출구가 될 수 있습니다. 날카로운 칼과 같지만, 최고의 요리사는 안전 가위로 음식을 준비하지 않습니다.
이 기능을 조금 사용해 보면, 상황에 따라 매우 유용한 도구라는 걸 알게 될 겁니다. 우리는 현재 작업 중인 새로운 웹사이트 템플릿에서 몇 가지 까다로운 부분에 이 기능을 사용하고 있으며, 커스텀 클래스를 만드는 것보다 훨씬 편리합니다.
이것이 바로 Tailwind CSS v3.1입니다! 마이너 버전 변경이기 때문에 호환성이 깨지는 변경 사항은 없으며, 최신 버전을 설치하기만 하면 프로젝트를 업데이트할 수 있습니다.
npm install tailwindcss@latest
버그 수정 및 여기서 언급하지 않은 몇 가지 사소한 개선 사항을 포함한 전체 변경 사항은 GitHub의 릴리스 노트에서 확인할 수 있습니다.
저는 이미 Tailwind CSS v3.2를 위한 여러 아이디어를 가지고 있습니다 (드디어 텍스트 그림자가 추가될지도?!), 하지만 현재는 새로운 웹사이트 템플릿을 완성하기 위해 열심히 작업 중입니다. 다음 주나 그 다음 주쯤에 또 다른 업데이트를 기대해 주세요!