--- id: wiki-2026-0508-zero-runtime-css-in-js title: Zero-Runtime CSS-in-JS category: 10_Wiki/Topics status: verified canonical_id: self aliases: [Zero-Runtime CSS-in-JS, Zero Runtime CSS, Static CSS-in-JS] duplicate_of: none source_trust_level: A confidence_score: 0.9 verification_status: applied tags: [css, css-in-js, build-time, zero-runtime, vanilla-extract, linaria] raw_sources: [] last_reinforced: 2026-05-10 github_commit: pending tech_stack: language: typescript framework: vanilla-extract --- # Zero-Runtime CSS-in-JS ## 매 한 줄 > **"매 build time 에 CSS 로 추출 — runtime overhead 0"**. 매 styled-components/emotion 의 runtime injection 의 단점 (FCP 손실, hydration cost, RSC 비호환) 매 해결. 매 vanilla-extract (Mark Dalgleish), Linaria, Panda CSS, StyleX (Meta) 매 대표주자. 매 React Server Component 시대의 사실상 표준. ## 매 핵심 ### 매 runtime CSS-in-JS 의 문제 - **Bundle size**: 매 emotion ~10KB, styled-components ~12KB minified. - **FCP 손실**: 매 JS 가 parse → execute → CSS inject → paint. 매 3-tier waterfall. - **Hydration cost**: 매 server-rendered CSS 와 client style 의 sync overhead. - **RSC 비호환**: 매 React Server Component 매 runtime context 없음 — 매 styled-components/emotion 매 client-only. - **Re-render cost**: 매 dynamic style prop 의 매 render 마다 hash + inject. ### 매 zero-runtime 의 해결 - **Build-time extraction**: 매 .css.ts → .css 로 매 extract. - **Type safety**: 매 TypeScript 로 token, theme, variants 매 type-check. - **No runtime**: 매 0 KB 추가 — 매 static CSS 와 동일. - **RSC 호환**: 매 build-time output 이라 server/client 무관. - **CSS-only features**: 매 :hover, :focus, media query 매 CSS 자체 사용. ### 매 응용 1. Next.js App Router (RSC) 와 매 vanilla-extract pairing. 2. Design system 의 token-based theming (light/dark, brand variant). 3. Component library publishing (no runtime dependency). 4. Performance-critical apps (e-commerce, content sites). ## 💻 패턴 ### vanilla-extract — 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', ':hover': { background: 'darkblue', }, }); // button.tsx import { button } from './button.css'; export const Button = (props) => ``` ### vanilla-extract — theme contract ```ts // theme.css.ts import { createTheme, createThemeContract } from '@vanilla-extract/css'; export const vars = createThemeContract({ color: { brand: null, text: null, bg: null }, space: { sm: null, md: null, lg: null }, }); export const lightTheme = createTheme(vars, { color: { brand: '#0066ff', text: '#000', bg: '#fff' }, space: { sm: '4px', md: '8px', lg: '16px' }, }); export const darkTheme = createTheme(vars, { color: { brand: '#3399ff', text: '#fff', bg: '#000' }, space: { sm: '4px', md: '8px', lg: '16px' }, }); // component uses vars import { vars } from './theme.css'; const card = style({ background: vars.color.bg, color: vars.color.text, padding: vars.space.md, }); ``` ### Linaria — tagged template ```ts import { css } from '@linaria/core'; import { styled } from '@linaria/react'; const button = css` padding: 8px 16px; background: ${({ primary }) => primary ? 'blue' : 'gray'}; `; const Button = styled.button` border-radius: 4px; &:hover { opacity: 0.8; } `; // 매 build-time 에 CSS 추출, dynamic prop 매 CSS variable 로 변환. ``` ### Panda CSS — atomic + tokens ```ts // panda.config.ts export default defineConfig({ theme: { tokens: { colors: { brand: { value: '#0066ff' } }, }, }, }); // component import { css } from 'styled-system/css';
Hello
// 매 build-time 에 atomic class generation. Tailwind-like. ``` ### StyleX (Meta) — atomic ```ts import * as stylex from '@stylexjs/stylex'; const styles = stylex.create({ button: { padding: '8px 16px', backgroundColor: { default: 'blue', ':hover': 'darkblue' }, }, }); // 매 Meta Facebook/Instagram 사용. 매 atomic class deduplication. ``` ### Migration — emotion → vanilla-extract ```ts // Before (emotion) const Button = styled.button` padding: ${props => props.size === 'lg' ? '16px' : '8px'}; `; // After (vanilla-extract recipes) const button = recipe({ base: {}, variants: { size: { sm: { padding: 8 }, lg: { padding: 16 } }, }, }); const Button = ({ size, ...rest }) =>