Tailwind CSS v4.0 개발 진행 상황 오픈소스로 공개

Date
Tailwind CSS v4.0-alpha

지난 여름 Tailwind Connect에서 Oxide 미리보기를 공유했습니다. Oxide는 Tailwind CSS를 위한 새로운 고성능 엔진으로, 개발자 경험을 단순화하고 최근 몇 년 동안 웹 플랫폼이 어떻게 진화했는지를 활용하도록 설계되었습니다.

이 새로운 엔진은 원래 v3.x 릴리스로 출시될 예정이었지만, 하위 호환성을 유지하더라도 이번 업데이트는 프레임워크의 새로운 세대임이 분명하기 때문에 v4.0으로 출시하는 것이 적절하다고 판단했습니다.

아직 초기 단계이며 많은 작업이 남아 있지만, 오늘 개발 진행 상황을 오픈소스로 공개하고 첫 번째 공개 버전인 v4.0.0-alpha를 태그했습니다. 여러분도 실험해 보고 올해 안에 안정적인 릴리스를 달성하는 데 도움을 주실 수 있습니다.

안정적인 릴리스 때 더 많은 흥미로운 내용을 공유하기 위해 간단히 요약하겠습니다. 하지만 매우 초기 단계의 실험적인 것을 다루는 것을 좋아한다면, 여기에서 시작하기에 충분한 정보를 얻을 수 있을 것입니다.


속도를 위해 새로 만든 엔진

새로운 엔진은 처음부터 다시 작성되었습니다. 프레임워크에 대한 현재 지식을 모두 활용해 문제 공간을 더 잘 모델링하고, 훨씬 적은 코드로 더 빠르게 동작하도록 만들었습니다.

  • 최대 10배 빠른 속도 — Tailwind CSS 웹사이트 전체 빌드가 960ms에서 105ms로, Catalyst UI 키트는 341ms에서 55ms로 단축되었습니다.
  • 더 작은 용량 — Rust와 Lightning CSS로 재작성한 네이티브 패키지를 포함해도 새 엔진은 설치 용량이 35% 이상 줄었습니다.
  • 핵심 부분에 Rust 적용 — 프레임워크에서 가장 비용이 많이 들고 병렬화 가능한 부분을 Rust로 이전했으며, 확장성을 위해 프레임워크의 핵심은 TypeScript로 유지했습니다.
  • 단일 의존성 — 새 엔진은 Lightning CSS만 의존합니다.
  • 커스텀 파서 — 우리만의 CSS 파서를 작성하고 필요에 맞춘 데이터 구조를 설계해, PostCSS 대비 2배 이상 빠른 파싱 속도를 달성했습니다.

통합 툴체인

Tailwind CSS v4는 더 이상 단순한 플러그인이 아닙니다. 이제는 CSS를 처리하는 올인원 툴로 자리 잡았습니다. Lightning CSS를 프레임워크에 직접 통합했기 때문에 CSS 파이프라인을 따로 설정할 필요가 없습니다.

  • 내장된 @import 처리postcss-import 같은 도구를 설정하고 구성할 필요가 없습니다.
  • 내장된 벤더 프리픽싱 — 더 이상 프로젝트에 autoprefixer를 추가할 필요가 없습니다.
  • 내장된 중첩 지원 — 중첩된 CSS를 평탄화하기 위한 플러그인이 필요 없이 바로 사용할 수 있습니다.
  • 구문 변환oklch() 색상이나 미디어 쿼리 범위 같은 모던 CSS 기능이 더 나은 브라우저 지원을 위한 구문으로 변환됩니다.

여전히 PostCSS 플러그인을 제공하고 있지만, 공식 번들러 플러그인도 탐색 중입니다. 이번 첫 번째 알파 릴리스와 함께 공식 Vite 플러그인도 제공하니 지금 바로 사용해 볼 수 있습니다.


모던 웹을 위해 설계된 Tailwind CSS v4

우리는 Tailwind CSS v4를 통해 미래를 내다보며, 앞으로 몇 년 동안 최첨단으로 느껴질 프레임워크를 구축하려고 합니다.

  • 네이티브 캐스케이드 레이어 — 이제 실제 @layer 규칙을 사용하여, 과거에 고민했던 많은 명시도 문제를 해결했습니다.
  • 명시적으로 정의된 커스텀 속성@property를 사용하여 내부 커스텀 속성을 적절한 타입과 제약 조건으로 정의함으로써, 배경 그라데이션 트랜지션과 같은 작업을 가능하게 했습니다.
  • 투명도 조정을 위한 color-mix 사용 — CSS 변수를 사용하여 색상을 지정하거나 currentColor의 투명도를 조정할 때, 투명도 조정 구문을 더 쉽게 사용할 수 있게 했습니다.
  • 코어에 통합된 컨테이너 쿼리 — 새로운 @min-*@max-* 변형을 통해 컨테이너 쿼리 범위를 지원하는 컨테이너 쿼리 기능을 코어에 직접 추가했습니다.

또한, 와이드 색영역 색상으로 색상 팔레트를 새롭게 구성하고, @starting-style, 앵커 포지셔닝 등과 같은 다른 모던 CSS 기능에 대한 지원도 도입하고 있습니다.


조합 가능한 변형

새로운 아키텍처는 group-*, peer-*, has-*와 같은 선택자에 작용하는 변형들을 조합할 수 있게 해줍니다. 또한 v4에서 새롭게 도입된 not-* 변형도 함께 사용할 수 있습니다.

이전 버전에서는 group-has-*와 같은 변형이 프레임워크 내에서 명시적으로 정의되었지만, 이제는 group-*가 기존의 has-* 변형과 조합될 수 있습니다. 이 변형은 focus와 같은 다른 변형과도 조합이 가능합니다:

index.html
<div class="group">
  <div class="group-has-[&:focus]:opacity-100">
  <div class="group-has-focus:opacity-100">
    <!-- ... -->
  </div>
</div>

이 조합에는 제한이 없으며, 만약 어쩌다가 그런 끔찍한 상황이 필요하다면 group-not-has-peer-not-data-active:underline과 같은 코드도 작성할 수 있습니다.

제로 설정 콘텐츠 탐지

이 초기 알파 릴리스에서는 content 경로를 설정할 수 없다는 것을 눈치챘을 겁니다. 대부분의 프로젝트에서는 이를 다시 설정할 필요가 없습니다. Tailwind가 여러분의 템플릿 파일을 자동으로 찾아주기 때문입니다.

Tailwind를 프로젝트에 통합한 방식에 따라 두 가지 방법 중 하나를 사용해 이 작업을 수행합니다:

  • PostCSS 플러그인이나 CLI를 사용하는 경우, Tailwind는 프로젝트 전체를 탐색하며 템플릿 파일을 찾습니다. 이때 .gitignore 파일에 포함된 디렉토리는 탐색하지 않거나 바이너리 파일 형식을 무리하는 등, 속도를 유지하기 위해 내장된 여러 휴리스틱을 사용합니다.

  • Vite 플러그인을 사용하는 경우, 모듈 그래프에 의존합니다. 이 방식은 실제로 사용 중인 파일을 정확히 알 수 있어 최대한의 성능을 발휘하며, 거짓 양성이나 거짓 음성 없이 동작합니다. 앞으로 다른 번들러 플러그인을 통해 Vite 생태계 외부에서도 이 방식을 확장할 계획입니다.

향후에는 콘텐츠 경로를 명시적으로 설정할 수 있는 방법을 도입할 예정이지만, 이 자동화된 접근 방식이 얼마나 잘 작동하는지 지켜보고 싶습니다. 우리 프로젝트에서는 이미 훌륭하게 동작하고 있습니다.

CSS 우선 설정

Tailwind CSS v4.0의 주요 목표는 이 프레임워크를 CSS 네이티브처럼 느껴지게 하고, 자바스크립트 라이브러리처럼 느껴지지 않도록 하는 것입니다.

설치한 후에는 일반 CSS @import 문을 사용해 프로젝트에 추가할 수 있습니다:

main.css
@import "tailwindcss";

자바스크립트 설정 파일에서 모든 커스텀 설정을 하는 대신, CSS 변수를 사용하면 됩니다:

main.css
@import "tailwindcss";

@theme {
  --font-family-display: "Satoshi", "sans-serif";

  --breakpoint-3xl: 1920px;

  --color-neon-pink: oklch(71.7% 0.25 360);
  --color-neon-lime: oklch(91.5% 0.258 129);
  --color-neon-cyan: oklch(91.3% 0.139 195.8);
}

특별한 @theme 지시어는 Tailwind에게 이 변수들을 기반으로 새로운 유틸리티와 변형을 사용할 수 있게 합니다. 이를 통해 마크업에서 3xl:text-neon-lime 같은 클래스를 사용할 수 있습니다:

index.html
<div class="max-w-lg 3xl:max-w-xl">
  <h1 class="font-display text-4xl">
    Data to <span class="text-neon-cyan">enrich</span> your online business
  </h1>
</div>

새로운 CSS 변수를 추가하는 것은 이전 버전의 extend와 비슷하게 동작하지만, --color-*: initial 같은 구문을 사용해 네임스페이스를 초기화한 후 모든 커스텀 값을 정의할 수 있습니다:

main.css
@import "tailwindcss";

@theme {
  --color-*: initial;

  --color-gray-50: #f8fafc;
  --color-gray-100: #f1f5f9;
  --color-gray-200: #e2e8f0;
  /* ... */
  --color-green-800: #3f6212;
  --color-green-900: #365314;
  --color-green-950: #1a2e05;
}

아직 몇 가지 네이밍 규칙을 조정 중이지만, GitHub에서 기본 테마를 탐색하여 커스터마이징할 수 있는 항목을 확인할 수 있습니다.

기본 테마를 명시적으로 초기화하지 않고 처음부터 시작하려면, "tailwindcss/preflight""tailwindcss/utilities"를 직접 임포트하여 기본 테마를 임포트하지 않을 수 있습니다:

main.css
@import "tailwindcss";
@import "tailwindcss/preflight" layer(base);
@import "tailwindcss/utilities" layer(utilities);

@theme {
  --color-*: initial;
  --color-gray-50: #f8fafc;
  --color-gray-100: #f1f5f9;
  --color-gray-200: #e2e8f0;
  /* ... */
  --color-green-800: #3f6212;
  --color-green-900: #365314;
  --color-green-950: #1a2e05;
}

또한, 모든 테마 값을 커스텀 CSS에서 네이티브 CSS 변수로 사용할 수 있습니다:

dist/main.css
:root {
  --color-gray-50: #f8fafc;
  --color-gray-100: #f1f5f9;
  --color-gray-200: #e2e8f0;
  /* ... */
  --color-green-800: #3f6212;
  --color-green-900: #365314;
  --color-green-950: #1a2e05;
}

이를 통해 theme() 함수 없이도 임의의 값에서 테마 값을 쉽게 참조할 수 있습니다:

index.html
<div class="p-[calc(var(--spacing-6)-1px)]">
  <!-- ... -->
</div>

또한, Framer Motion 같은 UI 라이브러리와 함께 작업할 때 resolveConfig() 함수를 사용하지 않고도 테마 값을 사용할 수 있습니다:

JSX
import { motion } from "framer-motion"

export const MyComponent = () => (
  <motion.div
    initial={{ y: 'var(--spacing-8)' }}
    animate={{ y: 0 }}
    exit={{ y: 'var(--spacing-8)' }}
  >
    {children}
  </motion.div>
)

변경 사항

우리는 주요 변경 사항을 가볍게 다루지 않지만, v4에서 지금까지 달라진 몇 가지 사항을 공유할 가치가 있습니다:

  • 더 이상 사용되지 않는 유틸리티 제거 — 오래 전부터 문서화를 중단한 text-opacity-*, flex-grow-*, decoration-slice와 같은 유틸리티를 제거했습니다. 대신 text-{color}/*, grow-*, box-decoration-slice와 같은 현대적인 대체품을 사용합니다.
  • PostCSS 플러그인과 CLI가 별도의 패키지로 분리 — 메인 tailwindcss 패키지에는 더 이상 이들이 포함되지 않습니다. 모든 사람이 필요로 하지 않기 때문에, 대신 @tailwindcss/postcss@tailwindcss/cli를 별도로 설치해야 합니다.
  • 기본 테두리 색상 없음border 유틸리티는 이전에 gray-200을 기본값으로 사용했지만, 이제는 브라우저처럼 currentColor를 기본값으로 사용합니다. 이 변경은 zincslate 등 다른 회색을 메인으로 사용할 때 실수로 잘못된 회색을 도입하는 것을 방지하기 위해 이루어졌습니다.
  • 링 두께가 기본 1px로 변경ring 유틸리티는 이전에 기본적으로 3px 파란색 링이었지만, 이제는 currentColor를 사용한 1px 링으로 변경되었습니다. 우리는 프로젝트에서 ring-* 유틸리티를 테두리 대안으로 사용하고, 포커스 링에는 outline-*를 사용하는 경우가 많아, 이 부분을 일관성 있게 만드는 것이 도움이 될 것이라고 판단했습니다.

이 외에도 프로젝트에 어떤 식으로든 영향을 미칠 수 있는 매우 저수준의 구현 세부 사항 변경이 몇 가지 있지만, 이들처럼 의도적인 변경은 아닙니다. 예상치 못한 문제를 발견하면 알려주세요.


v4.0 로드맵

이 새로운 엔진은 처음부터 다시 작성된 것이며, 지금까지는 새로운 설정 방식을 사용한 개발자 경험에 집중해 왔습니다.

우리는 하위 호환성에 큰 가치를 두고 있으며, 이것이 올해 안에 안정적인 v4.0 버전을 출시하기 전에 대부분의 작업이 필요한 부분입니다.

  • JavaScript 설정 파일 지원 — 기존의 tailwind.config.js 파일과의 호환성을 다시 도입하여 v4로의 마이그레이션을 쉽게 만듭니다.
  • 명시적인 콘텐츠 경로 설정 — 자동 콘텐츠 감지가 설정에 충분하지 않을 때, Tailwind에게 템플릿이 어디에 있는지 정확히 알려줄 수 있게 합니다.
  • 다른 다크 모드 지원 — 현재는 미디어 쿼리를 사용한 다크 모드만 지원하며, 선택자와 변형 전략을 다시 구현해야 합니다.
  • 플러그인과 커스텀 유틸리티 — 아직 플러그인 지원이나 변형과 자동으로 작동하는 커스텀 유틸리티 작성 기능이 없습니다. 당연히 안정적인 출시 전에 이를 구현할 예정입니다.
  • 접두사 지원 — 아직 클래스에 접두사를 설정할 방법이 없지만, 반드시 다시 도입할 것입니다.
  • 안전 목록과 차단 목록 — 특정 클래스를 강제로 생성하거나 다른 클래스의 생성을 방지할 수 있는 기능이 아직 없습니다.
  • important 설정 지원 — 현재 유틸리티가 모두 !important로 생성되도록 설정할 방법이 없지만, 이를 구현할 계획입니다.
  • theme() 함수 지원 — 새로운 프로젝트에서는 var()를 사용할 수 있기 때문에 필요하지 않지만, 하위 호환성을 위해 구현할 것입니다.
  • 독립형 CLI — 새로운 엔진을 위한 독립형 CLI는 아직 작업하지 않았지만, v4.0 출시 전에 반드시 제공할 것입니다.

이 외에도, 많은 버그를 수정하고, 몇 가지 흥미로운 새로운 CSS 기능을 추가하며, 출시 전에 더 다듬어야 할 새로운 API를 개선할 것입니다.

특정 출시 일정에 대해 약속하고 싶지는 않지만, 개인적으로는 여름 휴가 시즌이 시작되기 전에 v4.0을 안정적인 버전으로 표시하고 싶습니다.


알파 버전 사용해 보기

이미 몇 가지 알파 버전을 출시했으니, 여러분의 프로젝트에서 지금 바로 사용해 볼 수 있습니다.

VS Code용 Tailwind CSS IntelliSense 확장을 사용 중이라면, 확장 페이지에서 프리릴리스 버전으로 전환하세요. Prettier 플러그인을 사용 중이라면 최신 버전을 설치하세요.

문제를 발견하시면 GitHub에서 알려주세요. 안정 버전을 출시하기 전에 이 도구를 완벽하게 만들고 싶습니다. 발견한 문제를 보고해 주시면 큰 도움이 됩니다.

Vite 사용하기

Tailwind CSS v4 알파 버전과 새로운 Vite 플러그인을 설치하세요:

$ npm install tailwindcss@next @tailwindcss/vite@next

그런 다음 vite.config.ts 파일에 플러그인을 추가하세요:

vite.config.ts
import tailwindcss from '@tailwindcss/vite'
import { defineConfig } from 'vite'

export default defineConfig({
  plugins: [tailwindcss()],
})

마지막으로, 메인 CSS 파일에서 Tailwind를 불러오세요:

app.css
@import "tailwindcss";

PostCSS 사용하기

Tailwind CSS v4 알파 버전과 별도의 PostCSS 플러그인 패키지를 설치합니다:

$ npm install tailwindcss@next @tailwindcss/postcss@next

그런 다음 postcss.config.js 파일에 플러그인을 추가합니다:

postcss.config.js
module.exports = {
  plugins: {
    '@tailwindcss/postcss': {}
  }
}

마지막으로, 메인 CSS 파일에서 Tailwind를 불러옵니다:

app.css
@import "tailwindcss";

CLI 사용하기

Tailwind CSS v4 알파 버전과 별도의 CLI 패키지를 설치합니다:

$ npm install tailwindcss@next @tailwindcss/cli@next

다음으로, 메인 CSS 파일에 Tailwind를 불러옵니다:

app.css
@import "tailwindcss";

마지막으로, CLI 도구를 사용해 CSS를 컴파일합니다:

$ npx @tailwindcss/cli@next -i app.css -o dist/app.css