[G1-Sync] Manual knowledge update
This commit is contained in:
@@ -0,0 +1,350 @@
|
||||
---
|
||||
id: frontend-css-migration-tailwind
|
||||
title: CSS Migration — CSS-in-JS → Tailwind / native
|
||||
category: Coding
|
||||
status: draft
|
||||
source_trust_level: B
|
||||
verification_status: conceptual
|
||||
created_at: 2026-05-09
|
||||
updated_at: 2026-05-09
|
||||
tags: [frontend, css, vibe-coding]
|
||||
tech_stack: { language: "CSS / TS", applicable_to: ["Frontend"] }
|
||||
applied_in: []
|
||||
aliases: [CSS migration, Tailwind 4, Panda CSS, vanilla extract, CSS-in-JS death, runtime cost]
|
||||
---
|
||||
|
||||
# CSS Migration
|
||||
|
||||
> CSS-in-JS (Emotion, styled-components) 가 cost 큰. **Tailwind 4 / native CSS / Panda CSS 가 modern**.
|
||||
|
||||
## 📖 핵심 개념
|
||||
- CSS-in-JS = runtime cost (parse, hydrate).
|
||||
- Tailwind = build-time, 0 runtime.
|
||||
- Panda CSS / Vanilla Extract = atomic CSS-in-JS.
|
||||
- Native CSS = modern feature 가 충분.
|
||||
|
||||
## 💻 코드 패턴
|
||||
|
||||
### 옛 (Emotion / styled-components)
|
||||
```tsx
|
||||
import styled from '@emotion/styled';
|
||||
|
||||
const Button = styled.button<{ primary?: boolean }>`
|
||||
padding: 8px 16px;
|
||||
background: ${({ primary }) => primary ? 'blue' : 'gray'};
|
||||
color: white;
|
||||
border-radius: 4px;
|
||||
`;
|
||||
|
||||
<Button primary>Click</Button>
|
||||
```
|
||||
|
||||
→ Runtime parsing. Hydration cost. SSR slow.
|
||||
|
||||
### Tailwind
|
||||
```tsx
|
||||
<button className='px-4 py-2 bg-blue-500 text-white rounded'>
|
||||
Click
|
||||
</button>
|
||||
|
||||
// 또는 cva
|
||||
import { cva } from 'class-variance-authority';
|
||||
|
||||
const button = cva('px-4 py-2 rounded', {
|
||||
variants: {
|
||||
color: { blue: 'bg-blue-500 text-white', gray: 'bg-gray-500 text-white' },
|
||||
},
|
||||
});
|
||||
|
||||
<button className={button({ color: 'blue' })}>Click</button>
|
||||
```
|
||||
|
||||
→ 0 runtime. Build-time only.
|
||||
|
||||
### Tailwind 4 (Lightning CSS)
|
||||
```css
|
||||
@import "tailwindcss";
|
||||
|
||||
@theme {
|
||||
--color-primary: oklch(0.7 0.15 270);
|
||||
}
|
||||
```
|
||||
|
||||
→ JS config 없음. CSS-native.
|
||||
|
||||
### Panda CSS (atomic CSS-in-JS)
|
||||
```tsx
|
||||
import { css } from '../styled-system/css';
|
||||
|
||||
<button className={css({ px: 4, py: 2, bg: 'blue.500', color: 'white' })}>
|
||||
Click
|
||||
</button>
|
||||
```
|
||||
|
||||
→ Build-time atomic CSS. Type-safe. Tailwind 식 + JS DX.
|
||||
|
||||
### Vanilla Extract (zero-runtime)
|
||||
```ts
|
||||
// button.css.ts
|
||||
import { style } from '@vanilla-extract/css';
|
||||
|
||||
export const button = style({
|
||||
padding: '8px 16px',
|
||||
background: 'blue',
|
||||
color: 'white',
|
||||
});
|
||||
```
|
||||
|
||||
```tsx
|
||||
import { button } from './button.css';
|
||||
<button className={button}>Click</button>
|
||||
```
|
||||
|
||||
→ TypeScript + zero runtime.
|
||||
|
||||
### CSS Modules (legacy 가 OK)
|
||||
```tsx
|
||||
// Button.module.css
|
||||
.button { padding: 8px 16px; background: blue; }
|
||||
|
||||
// Button.tsx
|
||||
import styles from './Button.module.css';
|
||||
<button className={styles.button}>Click</button>
|
||||
```
|
||||
|
||||
→ Scope 자동. Modern alternative 가 native @scope.
|
||||
|
||||
### Migration: styled-components → Tailwind
|
||||
```
|
||||
Step 1: Add Tailwind.
|
||||
Step 2: 매 component 의 css = Tailwind class.
|
||||
Step 3: Remove styled-components.
|
||||
|
||||
→ 점진. 1 component 씩.
|
||||
```
|
||||
|
||||
### Migration script
|
||||
```bash
|
||||
# Twin macro 등 의 도구
|
||||
npx codemod styled-components-to-tailwind
|
||||
```
|
||||
|
||||
→ Manual review 가 필요.
|
||||
|
||||
### Why migrate?
|
||||
```
|
||||
CSS-in-JS cost:
|
||||
- Bundle size (Emotion: 12 KB).
|
||||
- Runtime parse.
|
||||
- Hydration delay.
|
||||
- SSR overhead.
|
||||
- Devtools 가 cryptic class.
|
||||
|
||||
Tailwind:
|
||||
- 0 runtime.
|
||||
- 1 css file.
|
||||
- Browser cache.
|
||||
- Devtools 친화 (class).
|
||||
|
||||
→ Production performance ↑.
|
||||
```
|
||||
|
||||
### Cons of Tailwind
|
||||
```
|
||||
- Class string 가 길음 (long).
|
||||
- Learn curve.
|
||||
- Custom design system = config.
|
||||
- Dynamic style 어려움 (config 만).
|
||||
|
||||
→ shadcn/ui + cva 가 답.
|
||||
```
|
||||
|
||||
### Dynamic style (Tailwind)
|
||||
```tsx
|
||||
// ❌ 동적 class
|
||||
<div className={`bg-${color}-500`}>...</div>
|
||||
// → JIT 가 build 시 detect 못 함.
|
||||
|
||||
// ✅ 명시
|
||||
const colorClass = {
|
||||
red: 'bg-red-500',
|
||||
blue: 'bg-blue-500',
|
||||
}[color];
|
||||
<div className={colorClass}>...</div>
|
||||
```
|
||||
|
||||
### Tailwind v4 의 변화
|
||||
```
|
||||
- CSS-native (no JS config).
|
||||
- Lightning CSS (Rust).
|
||||
- @theme directive.
|
||||
- 100x 빠른 build.
|
||||
- 작은 css output.
|
||||
```
|
||||
|
||||
### Native CSS의 modern features
|
||||
```css
|
||||
/* Container query */
|
||||
.card {
|
||||
container-type: inline-size;
|
||||
}
|
||||
@container (min-width: 400px) { ... }
|
||||
|
||||
/* :has() */
|
||||
.form:has(:invalid) { ... }
|
||||
|
||||
/* @scope */
|
||||
@scope (.card) {
|
||||
h2 { color: blue; }
|
||||
}
|
||||
|
||||
/* CSS variables */
|
||||
:root { --primary: oklch(0.7 0.15 270); }
|
||||
```
|
||||
|
||||
→ Tailwind 없이도 CSS 가 powerful.
|
||||
|
||||
### Theme (Tailwind)
|
||||
```css
|
||||
@theme {
|
||||
--color-primary: oklch(0.7 0.15 270);
|
||||
--font-sans: 'Inter', sans-serif;
|
||||
--shadow-card: 0 4px 12px rgba(0,0,0,0.1);
|
||||
}
|
||||
```
|
||||
|
||||
```tsx
|
||||
<div className='bg-primary font-sans shadow-card'>...</div>
|
||||
```
|
||||
|
||||
### Dark mode
|
||||
```css
|
||||
@media (prefers-color-scheme: dark) {
|
||||
:root {
|
||||
--color-bg: black;
|
||||
}
|
||||
}
|
||||
|
||||
/* Or class-based */
|
||||
.dark {
|
||||
--color-bg: black;
|
||||
}
|
||||
```
|
||||
|
||||
```tsx
|
||||
<html className='dark'>
|
||||
```
|
||||
|
||||
### CSS-in-JS 가 적절 한 경우
|
||||
```
|
||||
- 매우 동적 (theme switch runtime).
|
||||
- Library 가 CSS-in-JS 사용.
|
||||
- Team 가 익숙.
|
||||
|
||||
→ 하지만 modern = native CSS 가 충분.
|
||||
```
|
||||
|
||||
### Astro CSS
|
||||
```astro
|
||||
---
|
||||
---
|
||||
<div class='card'>...</div>
|
||||
|
||||
<style>
|
||||
.card {
|
||||
/* Scoped automatically */
|
||||
padding: 16px;
|
||||
}
|
||||
</style>
|
||||
```
|
||||
|
||||
→ Component-scoped CSS native.
|
||||
|
||||
### Production
|
||||
- **Vercel docs**: Tailwind.
|
||||
- **GitHub**: 옛 Sass → 새 design system.
|
||||
- **Notion**: 자체 CSS.
|
||||
- **Stripe**: Sass + design system.
|
||||
- **shadcn/ui**: Tailwind.
|
||||
|
||||
### Real-world migration
|
||||
```
|
||||
1. Audit current CSS.
|
||||
2. Pick 1 (Tailwind / Panda / native).
|
||||
3. Component 별 migrate.
|
||||
4. Test (visual regression).
|
||||
5. Remove old.
|
||||
|
||||
→ 매 milestone 의 measurable.
|
||||
```
|
||||
|
||||
### Bundle size
|
||||
```
|
||||
Emotion: 12 KB runtime.
|
||||
Styled-components: 16 KB.
|
||||
Tailwind: 0 (build).
|
||||
Panda: 0 (build).
|
||||
Vanilla Extract: 0.
|
||||
|
||||
→ Modern = 0 runtime.
|
||||
```
|
||||
|
||||
### LLM 도움
|
||||
```
|
||||
- Style migration 의 component 별.
|
||||
- Tailwind class 의 codegen.
|
||||
- Visual regression 검증.
|
||||
|
||||
→ Cursor 의 큰 use case.
|
||||
```
|
||||
|
||||
### Tools
|
||||
```
|
||||
- Tailwind CSS (v4).
|
||||
- Panda CSS.
|
||||
- Vanilla Extract.
|
||||
- StyleX (Meta).
|
||||
- Lightning CSS.
|
||||
- PostCSS (legacy).
|
||||
```
|
||||
|
||||
### StyleX (Meta)
|
||||
```ts
|
||||
import * as stylex from '@stylex/stylex';
|
||||
|
||||
const styles = stylex.create({
|
||||
base: { padding: 16, background: 'blue' },
|
||||
});
|
||||
|
||||
<button {...stylex.props(styles.base)}>Click</button>
|
||||
```
|
||||
|
||||
→ Atomic + zero runtime + type-safe.
|
||||
|
||||
## 🤔 의사결정 기준
|
||||
| 상황 | 추천 |
|
||||
|---|---|
|
||||
| New project | Tailwind 4 |
|
||||
| Type-safe + JS | Panda CSS / Vanilla Extract |
|
||||
| 매우 dynamic | StyleX / Emotion |
|
||||
| 작은 / static | Native CSS |
|
||||
| Component library | shadcn (Tailwind) |
|
||||
| Migration | 점진 + visual test |
|
||||
|
||||
## ❌ 안티패턴
|
||||
- **CSS-in-JS in 새 project**: runtime cost.
|
||||
- **Dynamic Tailwind class**: JIT miss.
|
||||
- **Tailwind + Sass**: 둘 다 사용.
|
||||
- **Migration big bang**: visual regression.
|
||||
- **No design tokens**: drift.
|
||||
|
||||
## 🤖 LLM 활용 힌트
|
||||
- Tailwind 4 = modern default.
|
||||
- Panda / Vanilla Extract = type-safe alternative.
|
||||
- Native CSS 가 충분 (modern feature).
|
||||
- CSS-in-JS = legacy migration.
|
||||
|
||||
## 🔗 관련 문서
|
||||
- [[Frontend_Tailwind_Architecture]]
|
||||
- [[Frontend_Bun_Lightning_Patterns]]
|
||||
- [[Web_Anchor_Positioning_CSS]]
|
||||
Reference in New Issue
Block a user