--- id: wiki-2026-0508-vanilla-extract title: vanilla-extract category: 10_Wiki/Topics status: verified canonical_id: self aliases: [vanilla-extract, vanilla extract, ve, css.ts] duplicate_of: none source_trust_level: A confidence_score: 0.9 verification_status: applied tags: [css-in-js, zero-runtime, vanilla-extract, typescript, build-time] raw_sources: [] last_reinforced: 2026-05-10 github_commit: pending tech_stack: language: typescript framework: vanilla-extract --- # vanilla-extract ## 매 한 줄 > **"매 TypeScript 로 type-safe 한 zero-runtime CSS-in-JS"**. 매 Mark Dalgleish (Seek) 가 2021 년 출시. 매 `.css.ts` 파일을 build time 에 static CSS 로 추출, 매 token / variant / theme 매 모두 typed. 매 Next.js App Router (RSC) 의 사실상 표준, 매 component library 배포 의 zero-runtime guarantee. ## 매 핵심 ### 매 핵심 API - `style({...})`: 매 single CSS class 생성 → unique class name 반환. - `globalStyle('selector', {...})`: 매 global selector style. - `recipe({base, variants})`: 매 variant-based component (CVA-like, type-safe). - `createTheme(contract, values)`: 매 token 의 ThemeProvider 대안. - `createThemeContract({...})`: 매 token shape 정의 (multi-theme). - `createVar()`: 매 CSS custom property (runtime dynamic). - `style([base, override])`: 매 style composition. ### 매 build setup - **Webpack**: `@vanilla-extract/webpack-plugin`. - **Vite**: `@vanilla-extract/vite-plugin`. - **Next.js**: `@vanilla-extract/next-plugin` (App Router 호환). - **esbuild / Rollup / Parcel**: 매 dedicated plugins. - 매 결과: `.css.ts` → `.css` 추출 + `.js` 의 class name string export. ### 매 응용 1. Design system 의 token-based theming. 2. Next.js App Router 의 RSC-compatible styling. 3. Component library 의 zero-runtime publish. 4. CVA-style variants via recipes. 5. CSS variable scoped to component. ## 💻 패턴 ### Basic style ```ts // button.css.ts import { style } from '@vanilla-extract/css'; export const button = style({ padding: '8px 16px', borderRadius: 4, background: 'blue', color: 'white', cursor: 'pointer', selectors: { '&:hover': { background: 'darkblue' }, '&:disabled': { opacity: 0.5, cursor: 'not-allowed' }, }, }); // button.tsx import { button } from './button.css'; export const Button = (props) => ``` ### Sprinkles — atomic CSS (Tailwind-like) ```ts // sprinkles.css.ts import { defineProperties, createSprinkles } from '@vanilla-extract/sprinkles'; const space = defineProperties({ properties: { padding: { sm: 8, md: 16, lg: 24 }, margin: { sm: 8, md: 16, lg: 24 }, }, shorthands: { p: ['padding'], m: ['margin'] }, }); export const sprinkles = createSprinkles(space); // usage
...
``` ### Global style + reset ```ts // reset.css.ts import { globalStyle } from '@vanilla-extract/css'; globalStyle('html, body', { margin: 0, padding: 0, fontFamily: 'system-ui, sans-serif', }); globalStyle('*', { boxSizing: 'border-box', }); // 매 import 'one' 으로 reset 적용 // app.tsx: import './reset.css'; ``` ### Dynamic value via createVar ```ts // progress.css.ts import { createVar, style } from '@vanilla-extract/css'; export const progressVar = createVar(); export const bar = style({ width: progressVar, background: 'blue', height: 4, transition: 'width 0.3s', }); // progress.tsx import { assignInlineVars } from '@vanilla-extract/dynamic';
// 매 runtime dynamic — CSS variable 로 변환. ``` ### Style composition ```ts import { style } from '@vanilla-extract/css'; const base = style({ padding: 8, fontSize: 14 }); const primary = style([base, { background: 'blue', color: 'white' }]); // 매 primary class = base class + extra rules. ``` ### Next.js App Router setup ```ts // next.config.js const { createVanillaExtractPlugin } = require('@vanilla-extract/next-plugin'); const withVanillaExtract = createVanillaExtractPlugin(); module.exports = withVanillaExtract({ // ... existing config }); // app/layout.tsx import { lightTheme } from './theme.css'; import './reset.css'; export default function RootLayout({ children }) { return {children}; } // 매 RSC 호환 — server component 에서 매 className 사용 가능. ``` ## 매 결정 기준 | 상황 | Approach | |---|---| | Next.js App Router (RSC) | vanilla-extract (built-in plugin). | | Type-safe theme tokens | `createThemeContract` + `createTheme`. | | CVA-style variants | `recipe`. | | Atomic / Tailwind-like | `sprinkles`. | | Runtime dynamic value | `createVar` + `assignInlineVars`. | | Component library 배포 | vanilla-extract (zero runtime). | **기본값**: `style` (single class), `recipe` (variants), `createThemeContract` (multi-theme), `createVar` (runtime dynamic). ## 🔗 Graph - 부모: [[CSS_Architecture_and_Styling|CSS-in-JS]] · [[Zero-Runtime CSS-in-JS]] - 변형: [[Panda CSS]] · [[CSS Modules]] - 응용: [[Design System]] · [[React Server Components]] - Adjacent: [[CVA]] · [[CSS_Architecture_and_Styling|Tailwind CSS]] ## 🤖 LLM 활용 **언제**: Next.js App Router (RSC), type-safe design system, component library 배포, multi-theme (light/dark/brand) 의. **언제 X**: 매 simple prototype (Tailwind 매 빠름), 매 highly-dynamic per-pixel value (CSS-in-JS runtime 가 더 적합), 매 already emotion / styled-components codebase 의 incremental migration 어려움. ## ❌ 안티패턴 - **Runtime value 직접 사용**: 매 `style({ width: dynamicWidth })` — `dynamicWidth` 매 build time 에 evaluable 해야. 매 runtime 매 `createVar`. - **`.css.ts` 의 conditional logic**: 매 `if (env.X)` 매 build-time evaluated — 매 runtime 분기 X. - **Theme class 의 dynamic apply 미숙**: 매 theme 변경 시 `` — 매 root 단위. - **plugin setup 누락**: 매 webpack/vite plugin 없으면 .css.ts 매 그냥 TS file 로 처리 — error. - **`recipe` 의 base 의 selector**: 매 `recipe` base 안의 `selectors` 매 매 supported 하나 매 syntax 주의. ## 🧪 검증 / 중복 - Verified (vanilla-extract.style 공식 문서, GitHub seek-oss/vanilla-extract). - 신뢰도 A. ## 🕓 Changelog | 날짜 | 변경 | |---|---| | 2026-05-08 | Phase 1 | | 2026-05-10 | Manual cleanup — vanilla-extract API 전체 (style, recipe, theme contract, sprinkles, createVar) 추가 |