Tailwind CSS v3.0을 출시한 지 약 6개월이 지났습니다. 그동안 코드베이스에 많은 작은 개선 사항을 모아왔지만, 아직까지는 "좋아, 이제 출시할 때가 됐다" 고 말할 만한 그-하나의-기능 이 없었습니다.
그러다 몇 주 전 어느 토요일 밤, 디스코드에서 Robin과 :has
를 사용해 문서 깊숙이 있는 클래스를 타겟팅하는 방법에 대해 이야기하던 중, 임의의 변형(arbitrary variants)을 지원하면 어떻게 될지 설명했습니다. 이 아이디어는 1년 넘게 고민해온 것이었습니다.
![Adam Wathan: 임의의 변형을 구현한다면, 문법은 정확히 '[html:has(&)]:bg-blue-500'처럼 되어야 한다고 생각합니다. 이렇게 하면 꽤 유연해질 거예요. 실제 변형으로 할 수 있는 모든 것을 임의의 변형으로도 할 수 있으니까요. '[&>*:not(:first-child)]:pl-4'처럼요.
Robin: 이건 제 머리를 깨뜨릴 것 같아요. '[html:has(&)]:bg-blue-500'가 '&' 안에서 리터럴로 사용될 테니까요. 다른 변형과 조합하면... 🤯.
Adam Wathan: 😅 확실히 머리를 혼란스럽게 만들 거예요. CSS는 이렇게 될 거예요. 'html:has([html:has(&)]:bg-blue-500 {"{"} background: blue 500 }'.
Robin: 정확히 그렇죠. 이제 시도해보고 싶어요. 잠시만요.](/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fdiscord-message.225e322a.png&w=3840&q=75)
20분 후 Robin은 (6줄의 코드로!) 동작하는 프로토타입을 만들었고, Jordan이 클래스 탐지 엔진에서 정규식 기적을 일으킨 지 약 1시간 후, 임의의 변형이 탄생했고, 출시할 만한 기능을 갖추게 되었습니다.
이제 여러분에게 소개합니다 — Tailwind CSS v3.1! 모든 수정 사항과 개선 사항의 전체 목록은 릴리스 노트를 확인하세요. 주요 기능은 다음과 같습니다:
- 퍼스트파티 TypeScript 타입
- CLI에서 CSS 임포트 지원
- 테마 함수 사용 시 색상 불투명도 변경
- 더 쉬운 CSS 변수 색상 설정
- 테두리 간격 유틸리티
- 활성화 및 선택적 변형
- prefers-contrast 변형
- 네이티브 다이얼로그 배경 스타일링
- 변형을 위한 임의의 값
tailwindcss
의 최신 버전을 설치해 프로젝트를 업그레이드하세요:
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를 사용하면서 노드 의존성을 전혀 설치하고 싶지 않은 경우에 매우 유용합니다.
테마 함수 사용 시 색상 불투명도 조정하기
많은 사람들이 잘 모르고 있지만, Tailwind는 CSS 파일에서 theme()
함수를 제공합니다. 이 함수를 사용하면 설정 파일에서 값을 가져와 재사용 가능한 변수처럼 활용할 수 있습니다.
.select2-dropdown { border-radius: theme(borderRadius.lg); background-color: theme(colors.gray.100); color: theme(colors.gray.900);}/* ... */
하지만 이 방법으로 가져온 색상의 알파 채널을 조정할 수 없다는 한계가 있었습니다. 그래서 v3.1에서는 모던 CSS의 rgb
및 hsl
색상 함수처럼 슬래시(/
) 구문을 사용해 불투명도를 조정할 수 있는 기능을 추가했습니다.
.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>
여러분은 아마 이런 생각을 할지도 모릅니다. "나는 이런 식으로 테이블을 만들 일이 없을 것 같은데..." 하지만 잠시만 들어보세요!
이 기능이 정말 유용한 경우는 스틱키 헤더 행을 가진 테이블을 만들 때, 헤더 아래에 고정된 하단 테두리를 원할 때입니다.
이 테이블을 스크롤하여 스틱키 헤더 행을 확인하세요
Name | Role | |
---|---|---|
Courtney Henry | courtney.henry@example.com | Admin |
Tom Cook | tom.cook@example.com | Member |
Whitney Francis | whitney.francis@example.com | Admin |
Leonard Krasner | leonard.krasner@example.com | Owner |
Floyd Miles | floyd.miles@example.com | Member |
Emily Selman | emily.selman@example.com | Member |
Kristin Watson | kristin.watson@example.com | Admin |
Emma Dorsey | emma.dorsey@example.com | Member |
Alicia Bell | alicia.bell@example.com | Admin |
Jenny Wilson | jenny.wilson@example.com | Owner |
Anna Roberts | anna.roberts@example.com | Member |
Benjamin Russel | benjamin.russel@example.com | Member |
Jeffrey Webb | jeffrey.webb@example.com | Admin |
Kathryn Murphy | kathryn.murphy@example.com | 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="bg-indigo-500 hover:bg-indigo-400 disabled:opacity-75 disabled:hover:bg-indigo-500 ..." 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">Social Security Number</span> <input class="border-slate-200 placeholder-slate-400 contrast-more:border-slate-400 contrast-more:placeholder-slate-500" /> <p class="mt-2 text-sm text-slate-600 opacity-10 contrast-more:opacity-100">We need this to steal your identity.</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용)
이번 기능은 정말 주목할 만합니다. 여러분은 커스텀 variants를 만들기 위해 addVariant
API를 제공하는 것을 알고 있을 겁니다.
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="space-y-4 [&>*]:rounded-lg [&>*]:bg-white [&>*]:p-4 [&>*]:shadow"> <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="truncate text-sm text-slate-500">kristen.ramos@example.com</p> </div> </li> <!-- ... --></ul>
심지어 두 번째 li
안의 div
내부의 첫 번째 p
에 hover
시에만 스타일을 적용할 수도 있습니다.
“Floyd Miles” 텍스트에 마우스를 올려보세요
Kristen Ramos
kristen.ramos@example.com
Floyd Miles
floyd.miles@example.com
Courtney Henry
courtney.henry@example.com
<ul role="list" class="space-y-4 [&>*]:rounded-lg [&>*]:bg-white [&>*]:p-4 [&>*]:shadow hover:[&>li:nth-child(2)>div>p:first-child]:text-indigo-500"> <!-- ... --> <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="truncate text-sm text-slate-500">floyd.miles@example.com</p> </div> </li> <!-- ... --></ul>
이런 기능을 자주 사용해야 할까요? 아마 그렇지는 않겠지만, 직접 변경할 수 없는 HTML을 스타일링해야 할 때 꽤 유용한 탈출구가 될 수 있습니다. 날카로운 칼과 같지만, 최고의 요리사는 안전 가위로 음식을 준비하지 않습니다.
이 기능을 조금 사용해 보면 상황에 따라 매우 유용한 도구라는 것을 알게 될 겁니다. 우리는 현재 작업 중인 새로운 웹사이트 템플릿의 까다로운 부분에서 이 기능을 사용하고 있으며, 커스텀 클래스를 만드는 것보다 훨씬 편리합니다.
이것이 Tailwind CSS v3.1입니다! 마이너 버전 변경이므로 기존 기능이 깨지는 변경 사항은 없으며, 최신 버전을 설치하기만 하면 프로젝트를 업데이트할 수 있습니다.
npm install tailwindcss@latest
버그 수정 및 여기서 언급하지 않은 몇 가지 사소한 개선 사항을 포함한 전체 변경 사항은 GitHub의 릴리스 노트에서 확인할 수 있습니다.
저는 이미 Tailwind CSS v3.2를 위한 여러 아이디어를 가지고 있습니다 (드디어 텍스트 그림자가 추가될지도?!), 하지만 지금은 새로운 웹사이트 템플릿을 완성하기 위해 열심히 작업 중입니다. 다음 주나 그 다음 주쯤에 이 주제에 대한 업데이트를 기대해 주세요!