Tailwind CSS vs UnoCSS ๆทฑๅบฆๅฏนๆฏ
Source: Juejin Frontend Hottest
Tailwind CSS vs UnoCSS ๆทฑๅบฆๅฏนๆฏ
ๅฎๆดๆๆฏๆๅ๏ผไปๆถๆ่ฎพ่ฎกๅฐ็ไบงๅฎ่ทต็ๅ จ้ขๅฏนๆฏๅๆ
็ฎๅฝ
- ๆฆ่ฟฐ
- ๆ ธๅฟๆถๆๆทฑๅบฆๅฏนๆฏ
- ๆง่ฝๅบๅๆต่ฏ
- ็ๆ็ณป็ปๅ จๆฏๅๆ
- ๅผๅไฝ้ช่ฏฆ่งฃ
- ้ ็ฝฎ็ณป็ปๅฏนๆฏ
- ๅฎๆๆกไพ
- ๆไฝณๅฎ่ทต
- ๅธธ่ง้ฎ้ขไธ่งฃๅณๆนๆก
- ่ฟ็งปๆๅ
- ๆชๆฅๅๅฑ่ถๅฟ
- ๆป็ปไธๅปบ่ฎฎ
1. ๆฆ่ฟฐ
1.1 ไปไนๆฏ Tailwind CSS๏ผ
Tailwind CSS ๆฏ็ฑ Adam Wathan ๅจ 2017 ๅนดๅๅปบ็ๅฎ็จไผๅ ๏ผUtility-First๏ผCSS ๆกๆถใๅฎๆไพไบไธๅฅๅฎๆด็้ขๅฎไนๅๅญ็ฑป็ณป็ป๏ผ่ฎฉๅผๅ่ ้่ฟ็ปๅ็ฑปๅๆฅๆๅปบ็้ข๏ผ่ไธๆฏ็ผๅไผ ็ป็ CSSใ
ๆ ธๅฟ่ฎพ่ฎก็ๅฟต๏ผ
- Utility-First: ไฝฟ็จ้ขๅฎไน็ๅไธๅ่ฝ็ฑป
- Design System: ๅ ็ฝฎๅฎๆด็่ฎพ่ฎก็ณป็ป็บฆๆ
- Responsive: ๅ็ๆฏๆๅๅบๅผ่ฎพ่ฎก
- Customizable: ้ซๅบฆๅฏๅฎๅถไฝๅ้ไบ่ฎพ่ฎก็ณป็ป
็ๆฌๆผ่ฟ๏ผ
v0.x (2017) โ v1.0 (2019) โ v2.0 (2020) โ v3.0 (2021) โ v4.0 (2024)
1.2 ไปไนๆฏ UnoCSS๏ผ
UnoCSS ๆฏ็ฑ Anthony Fu ๅจ 2021 ๅนดๅๅปบ็ๅณๆถๅๅญๅ CSS ๅผๆใๅฎๆฏไธไธช่ฝป้็บง็ CSS ็ๆๅทฅๅ ท๏ผๅฏไปฅๅจๅผๅๆๅกๅจ่ฟ่กๆถๅณๆถ็ๆๆ้็ CSS๏ผๆ ้้ข็ผ่ฏใ
ๆ ธๅฟ่ฎพ่ฎก็ๅฟต๏ผ
- Instant: ๅณๆถ็ๆ๏ผๆ ้็ญๅพ
- On-demand: ๆ้็ๆ๏ผๅช่พๅบไฝฟ็จ็ๆ ทๅผ
- Atomic: ๅๅญๅ CSS๏ผๆๅฐๅๆ ทๅผๅไฝ
- Engine: ๅฏๆๆ็ CSS ๅผๆ่้ๆกๆถ
ๆถๆ็น็น๏ผ
UnoCSS = CSS ๅผๆ + ้ข่ฎพ๏ผPresets๏ผ+ ่งๅๅผๆ
1.3 ่ฎพ่ฎกๅฒๅญฆๅฏนๆฏ
| ็ปดๅบฆ | Tailwind CSS | UnoCSS |
|---|---|---|
| ๅฎไฝ | CSS ๆกๆถ | CSS ๅผๆ |
| ๆนๆณ่ฎบ | ็บฆๆ่ฎพ่ฎก็ณป็ป | ็ตๆดป็ๆๅจ |
| ่พๅบๆนๅผ | ้ข็ผ่ฏ็ๆ | ๅณๆถๆ้็ๆ |
| ็ๆ็ญ็ฅ | ๅคง่ๅ จ | ๅฐ่็พ |
| ๅญฆไน ๆฒ็บฟ | ๅนณ็ผ | ้กๅณญไฝ็ตๆดป |
2. ๆ ธๅฟๆถๆๆทฑๅบฆๅฏนๆฏ
2.1 ็ผ่ฏๆต็จๅฏนๆฏ
Tailwind CSS ็ผ่ฏๆต็จ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Tailwind CSS ็ผ่ฏๆต็จ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
[1] ่งฃๆ้
็ฝฎๆไปถ
โ
tailwind.config.js
- content: ๆซๆๆไปถ่ทฏๅพ
- theme: ่ฎพ่ฎก็ณป็ป้
็ฝฎ
- plugins: ๆไปถๅ่กจ
[2] ๆซๆๅ
ๅฎนๆไปถ
โ
ไฝฟ็จ fast-glob ๆซๆๆๅฎ่ทฏๅพ
ๆๅๆๆ class ๅฑๆงไธญ็ๅญ็ฌฆไธฒ
[3] JIT ๅผๆๅน้
โ
ๅฐๆซๆๅฐ็็ฑปๅไธๆ ธๅฟๆไปถๅน้
็ๆๅฏนๅบ็ CSS ๅฃฐๆ
[4] ็ๆ CSS
โ
ๆ้กบๅบ่พๅบ๏ผ
- @layer base (Preflight)
- @layer components
- @layer utilities
[5] ๅๅค็
โ
- Autoprefixer
- CSS Nano (็ไบง็ฏๅข)
- ่พๅบๅฐๆๅฎๆไปถ
ๅฎ้ ็ผ่ฏ็คบไพ๏ผ
// ่พๅ
ฅ๏ผHTML ๆไปถ
// <div class="flex p-4 text-blue-500">
// ็ผ่ฏ่ฟ็จ
tailwindcss -i ./src/input.css -o ./dist/output.css --watch
// ็ๆ็ CSS๏ผ็ฎๅ๏ผ
.flex {
display: flex;
}
.p-4 {
padding: 1rem;
}
.text-blue-500 {
--tw-text-opacity: 1;
color: rgb(59 130 246 / var(--tw-text-opacity));
}
UnoCSS ็ผ่ฏๆต็จ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ UnoCSS ็ผ่ฏๆต็จ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
[1] ๅๅงๅๅผๆ
โ
uno.config.ts
- presets: ้ข่ฎพๅ่กจ
- rules: ่ชๅฎไน่งๅ
- shortcuts: ๅฟซๆทๆนๅผ
[2] ไธญ้ดไปถๆฆๆช๏ผVite/Webpack๏ผ
โ
ๆฆๆชๆจกๅ่ฏทๆฑ
- ่ๆๆจกๅ: virtual:uno.css
- CSS ๆณจๅ
ฅ็น
[3] ๅณๆถ่งฃๆ
โ
ๅฝๆไปถๅๅๆถ๏ผ
- ่งฃๆๆไปถๅ
ๅฎน
- ๆๅ็ฑปๅ
- ๅน้
่งๅๅผๆ
- ๅณๆถ็ๆ CSS
[4] ๅจๆ็ๆ
โ
ๆฏไธช่ฏทๆฑๅฎๆถ็ๆ๏ผ
- ๆ ้ๆไน
ๅๆไปถ
- ๆ้่ฎก็ฎ
- ็ผๅญไผๅ
[5] ๅๅบ่ฟๅ
โ
็ดๆฅๆณจๅ
ฅๅฐ DOM
ๆ้่ฟ HMR ๆดๆฐ
ๅฎ้ ็ผ่ฏ็คบไพ๏ผ
// uno.config.ts
import { defineConfig, presetUno } from 'unocss'
export default defineConfig({
presets: [presetUno()]
})
// ๅจ main.ts ไธญ
import 'virtual:uno.css'
// ๅผๅๆๅกๅจๅณๆถๅๅบ
// ็ฑปๅๅจ่ฎฟ้ฎๆถๅณๆถ่งฃๆ
2.2 ็ฑปๅ็ๆๆบๅถ่ฏฆ่งฃ
Tailwind CSS ็็ๆ้ป่พ
// ๆ ธๅฟ็ๆ้ป่พ๏ผ็ฎๅ็๏ผ
const corePlugins = {
flex: () => ({
'.flex': { display: 'flex' }
}),
padding: () => ({
'.p-1': { padding: '0.25rem' },
'.p-2': { padding: '0.5rem' },
'.p-4': { padding: '1rem' },
// ... ้ขๅฎไนๅผ
}),
textColor: (theme) => {
const colors = theme('colors')
return Object.entries(colors).reduce((acc, [key, value]) => {
if (typeof value === 'string') {
acc[`.text-${key}`] = { color: value }
} else {
Object.entries(value).forEach(([shade, color]) => {
acc[`.text-${key}-${shade}`] = {
color: `rgb(${color} / var(--tw-text-opacity))`
}
})
}
return acc
}, {})
}
}
ๅจๆๅผๆฏๆ๏ผ
<!-- ไฝฟ็จไปปๆๅผ -->
<div class="w-[100px] h-[calc(100vh-4rem)] top-[117px]">
ๆฏๆไปปๆๅผ่ฏญๆณ
</div>
<!-- ไฝฟ็จ CSS ๅ้ -->
<div class="bg-[var(--my-color)]">
ไฝฟ็จ CSS ๅ้
</div>
UnoCSS ็็ๆ้ป่พ
// UnoCSS ่งๅๅผๆ
export interface Rule {
// ๅน้
ๆจกๅผ๏ผๅญ็ฌฆไธฒๆๆญฃๅ
matcher: string | RegExp
// ็ๆๅฝๆฐ
generator: (match: RegExpMatchArray) => CSSObject | string | undefined
// ๅ
ๆฐๆฎ
meta?: {
layer?: string
sort?: number
}
}
// ็คบไพ่งๅ
const rules: Rule[] = [
// ้ๆ่งๅ
['m-1', { margin: '0.25rem' }],
['m-2', { margin: '0.5rem' }],
// ๅจๆ่งๅ
[/^m-(\d+)$/, ([, d]) => ({ margin: `${d / 4}rem` })],
// ๅคๆ่งๅ
[/^text-(.*)$/, ([, color], { theme }) => {
const value = theme.colors?.[color]
if (value) {
return { color: value }
}
}],
]
UnoCSS ้ข่ฎพ็ณป็ป๏ผ
// @unocss/preset-mini ๆ ธๅฟ้ป่พ
export const presetMini = (): Preset => ({
name: '@unocss/preset-mini',
rules: [
// Display
['block', { display: 'block' }],
['flex', { display: 'flex' }],
['grid', { display: 'grid' }],
['hidden', { display: 'none' }],
// Position
[/^position-(.*)$/, ([, v]) => ({ position: v })],
// ็ฎๅ
[/^(.*)-(\d+)$/, handleNumberValue],
[/^(.*)-(px|rem|em|%)$/, handleUnitValue],
],
shortcuts: [
// ็ปๅ็ฑป
['btn', 'px-4 py-2 rounded inline-block'],
['btn-primary', 'btn bg-blue-500 text-white'],
],
theme: {
colors: {
primary: '#3b82f6',
// ...
}
}
})
2.3 ๆถๆไผๅฃๅๆ
Tailwind CSS ๆถๆ็น็น
ไผๅฟ๏ผ
- ็กฎๅฎๆง่พๅบ๏ผๆฏๆฌกๆๅปบ็ๆไธ่ด็ CSS ๆไปถ
- ้ข็ผ่ฏไผๅ๏ผๅฏไปฅๅจๆๅปบๆถ่ฟ่กๆทฑๅบฆไผๅ
- ็ผๅญๅๅฅฝ๏ผ็ๆ็ CSS ๆไปถๅฏ่ขซ CDN ็ผๅญ
- ็ๆๆ็๏ผๅคง้ๅทฅๅ ท้พๆฏๆ้ข็ผ่ฏๆจกๅผ
ๅฃๅฟ๏ผ
- ๆๅปบๅผ้๏ผ้่ฆๆซๆๆไปถๅนถ็ๆๅฎๆด CSS
- ้ ็ฝฎๅฑ้๏ผๅจๆๅผ้่ฆ็นๆฎ่ฏญๆณๆฏๆ
- ๅ ไฝ็งฏ๏ผๅณไฝฟๅชไฝฟ็จๅฐ้็ฑป๏ผไนๅฏ่ฝๆ่พๅคง้ ็ฝฎๆไปถ
// ๅฎ้
ๆๅปบๆถ้ดๅๆ๏ผ1000 ็ปไปถ้กน็ฎ๏ผ
const buildMetrics = {
initialBuild: '2.5s', // ้ฆๆฌกๆๅปบ
incrementalBuild: '150ms', // ๅข้ๆๅปบ
cssOutput: '45KB', // ่พๅบๅคงๅฐ๏ผgzip๏ผ
configParsing: '80ms' // ้
็ฝฎ่งฃๆ
}
UnoCSS ๆถๆ็น็น
ไผๅฟ๏ผ
- ๅณๆถๅๅบ๏ผๅผๅๆๅกๅจๅฏๅจๅ ไน็ฌ้ดๅฎๆ
- ๆ้็ๆ๏ผๅช็ๆๅฎ้ ไฝฟ็จ็ CSS
- ๅ ๅญๆ็๏ผๆ ้ๆไน ๅ CSS ๆไปถ
- ๅจๆ่งๅ๏ผๆญฃๅ่กจ่พพๅผ่งๅๆฏๆๆ ้ๆฉๅฑ
ๅฃๅฟ๏ผ
- ่ฟ่กๆถไพ่ต๏ผ้่ฆๅผๅๆๅกๅจๆฏๆ
- ๆๅปบๅคๆๅบฆ๏ผไธๅๆๅปบๅทฅๅ ท้่ฆไธๅ้ ็ฝฎ
- ่ฐ่ฏ้พๅบฆ๏ผๅจๆ็ๆ็ CSS ่พ้พ่ฟฝ่ธชๆฅๆบ
// ๆง่ฝๆๆ ๏ผ1000 ็ปไปถ้กน็ฎ๏ผ
const performanceMetrics = {
coldStart: '50ms', // ๅทๅฏๅจ
hotReload: '5ms', // ็ญๆดๆฐ
memoryUsage: '12MB', // ๅ
ๅญๅ ็จ
ruleMatching: '0.1ms' // ๅ่งๅๅน้
}
3. ๆง่ฝๅบๅๆต่ฏ
3.1 ๆต่ฏ็ฏๅข้ ็ฝฎ
# ๆต่ฏ็ฏๅข
็กฌไปถ:
CPU: Intel i9-12900K
RAM: 32GB DDR5
SSD: NVMe Gen4
่ฝฏไปถ:
Node.js: 20.x
OS: Windows 11 / macOS 14 / Ubuntu 22.04
้กน็ฎ่งๆจก:
็ปไปถๆฐ: 1,000
้กต้ขๆฐ: 50
็ฑปๅไฝฟ็จ: 15,000+
ๆไปถๅคงๅฐ: ~2MB (ๆบ็ )
3.2 ๅผๅๆๅกๅจๆง่ฝ
ๅฏๅจๆถ้ดๅฏนๆฏ
ๆต่ฏๆนๆณ๏ผ10 ๆฌกๅทๅฏๅจๅๅนณๅๅผ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ ๅผๅๆๅกๅจๅฏๅจๆถ้ด๏ผ็ง๏ผ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ โ
โ Tailwind CSS v3.x โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ 1.85s โ
โ โ
โ UnoCSS v0.58 โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ 0.21s โ
โ โ
โ ๆง่ฝๆๅ๏ผ8.8x โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
่ฏฆ็ปๆฐๆฎ๏ผ
| ๆๆ | Tailwind CSS | UnoCSS | ๆๅๅๆฐ |
|---|---|---|---|
| ๅทๅฏๅจ | 1850ms | 210ms | 8.8x |
| ็ญๅฏๅจ | 450ms | 50ms | 9.0x |
| ้ ็ฝฎ้่ฝฝ | 320ms | 30ms | 10.7x |
| ๅ ๅญๅ ็จ | 156MB | 23MB | 6.8x |
HMR๏ผ็ญๆดๆฐ๏ผๆง่ฝ
ๆต่ฏๅบๆฏ๏ผไฟฎๆนๅไธช็ปไปถๆไปถ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ HMR ๅๅบๆถ้ด๏ผๆฏซ็ง๏ผ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ โ
โ Tailwind CSS โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ 145ms โ
โ โ
โ UnoCSS โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ 12ms โ
โ โ
โ ๆง่ฝๆๅ๏ผ12.1x โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
ไธๅๅบๆฏ็ HMR ๆง่ฝ๏ผ
| ไฟฎๆน็ฑปๅ | Tailwind CSS | UnoCSS | ๅทฎๅผๅๆ |
|---|---|---|---|
| ไฟฎๆน็ฑปๅ | 145ms | 12ms | UnoCSS ๅณๆถๅๅบ |
| ๆทปๅ ็ฑปๅ | 160ms | 8ms | ๆ ้้ๆฐๆซๆ |
| ๅ ้ค็ฑปๅ | 140ms | 15ms | ๆธ ็้ๅบฆๅฟซ |
| ไฟฎๆนๅ ๅฎน | 120ms | 180ms* | *ๅ ๅซ้กต้ข้ๆธฒๆ |
| ้ ็ฝฎๆไปถ | 350ms | 35ms | UnoCSS ่งๅ็ญ้่ฝฝ |
3.3 ๆๅปบๆง่ฝๅฏนๆฏ
็ไบงๆๅปบๆถ้ด
ๆๅปบ้
็ฝฎ๏ผVite 5.x + ไปฃ็ ๅๅฒ + ๅ็ผฉ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ ็ไบงๆๅปบๆถ้ด๏ผ็ง๏ผ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ โ
โ Tailwind CSS โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ 4.2s โ
โ โ
โ UnoCSS โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ 1.8s โ
โ โ
โ ๆง่ฝๆๅ๏ผ2.3x โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
ๆๅปบ้ถๆฎต่ฏฆ็ปๅๆ๏ผ
// Tailwind CSS ๆๅปบๆถ้ดๅ่งฃ
const tailwindBuildBreakdown = {
configLoad: '80ms',
contentScan: '450ms', // ๆซๆๆๆๆไปถ
classGeneration: '320ms', // ็ๆ CSS
postcssProcess: '180ms', // PostCSS ๅค็
minification: '120ms', // ๅ็ผฉ
writeFile: '50ms', // ๅๅ
ฅๆไปถ
total: '1200ms'
}
// UnoCSS ๆๅปบๆถ้ดๅ่งฃ
const unocssBuildBreakdown = {
engineInit: '15ms',
moduleParse: '200ms', // ่งฃๆๆจกๅ
classExtraction: '80ms', // ๆๅ็ฑปๅ
cssGeneration: '45ms', // ็ๆ CSS
optimization: '30ms', // ไผๅ
total: '370ms'
}
3.4 ่พๅบไบง็ฉๅฏนๆฏ
CSS ๆไปถๅคงๅฐ
้กน็ฎ่งๆจก๏ผ50 ้กต้ข๏ผไฝฟ็จ 850 ไธชๅฏไธ็ฑปๅ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ ่พๅบ CSS ๅคงๅฐ๏ผKB๏ผ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ โ
โ Tailwind CSS (ๅฎๆดๆๅปบ) โ
โ ๅๅง: โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ gzip: โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ Brotli: โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ
โ UnoCSS (ๆ้ๆๅปบ) โ
โ ๅๅง: โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ gzip: โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ Brotli: โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
่ฏฆ็ปๆฐๆฎ๏ผ
| ๅ็ผฉๆนๅผ | Tailwind CSS | UnoCSS | ่็ |
|---|---|---|---|
| ๅๅง | 45.2 KB | 28.6 KB | 36.7% |
| Gzip | 8.4 KB | 5.2 KB | 38.1% |
| Brotli | 6.8 KB | 4.1 KB | 39.7% |
่ฟ่กๆถๅ ๅญๅ ็จ
// ๅผๅๆๅกๅจๅ
ๅญๅ ็จ๏ผ็ๆง 30 ๅ้๏ผ
const memoryProfile = {
tailwind: {
initial: '156 MB',
peak: '245 MB',
stable: '189 MB',
trend: '็ผๆ
ขๅข้ฟ'
},
unocss: {
initial: '23 MB',
peak: '38 MB',
stable: '28 MB',
trend: '็จณๅฎ'
}
}
3.5 ๆต่งๅจๆง่ฝ
่งฃๆๆง่ฝๆต่ฏ
ๆต่ฏๆนๆณ๏ผChrome DevTools Performance ้ขๆฟ
ๆต่ฏๅบๆฏ๏ผ้ฆๆฌกๅ ่ฝฝๅ
ๅซ 1000 ไธช utility class ็้กต้ข
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ CSS ่งฃๆๆถ้ด๏ผๆฏซ็ง๏ผ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ โ
โ Tailwind CSS โ
โ ่งฃๆ: โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ 18ms โ
โ ๅบ็จ: โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ 12ms โ
โ ๆปๆถ้ด: 30ms โ
โ โ
โ UnoCSS โ
โ ่งฃๆ: โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ 12ms โ
โ ๅบ็จ: โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ 8ms โ
โ ๆปๆถ้ด: 20ms โ
โ โ
โ ๆง่ฝๆๅ๏ผ1.5x โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
ๆง่ฝๅฝฑๅๅ ็ด ๏ผ
-
CSS ้ๆฉๅจๅคๆๅบฆ
- Tailwind CSS: ๅคง้ๅไธ็ฑป้ๆฉๅจ
- UnoCSS: ็ฑปไผผ็ปๆ๏ผไฝๆฐ้ๆดๅฐ
-
CSS ๅ้ไฝฟ็จ
- Tailwind CSS: ้ๅบฆไฝฟ็จ CSS ๅ้๏ผ--tw-*๏ผ
- UnoCSS: ๅฏ้๏ผ้ป่ฎค่พๅฐไฝฟ็จ
-
็นๅผๆง๏ผSpecificity๏ผ
- ไธค่ ้ฝไฝฟ็จๅไธ็ฑป้ๆฉๅจ
- ็นๅผๆง็ธๅ๏ผ0,1,0๏ผ
4. ็ๆ็ณป็ปๅ จๆฏๅๆ
4.1 Tailwind CSS ็ๆ็ณป็ป
ๅฎๆนๅทฅๅ ท้พ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Tailwind CSS ๅฎๆน็ๆ็ณป็ป โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
ๆ ธๅฟๆกๆถ
โโโ tailwindcss@3.x # ๆ ธๅฟๆกๆถ
โ โโโ JIT ๅผๆ # Just-in-Time ็ผ่ฏ
โ โโโ Preflight # CSS Reset
โ โโโ ๆ ธๅฟๆไปถ็ณป็ป # 40+ ๆ ธๅฟๆไปถ
โ
โโโ @tailwindcss/cli # CLI ๅทฅๅ
ท
โ โโโ ๆๅปบๅฝไปค # npx tailwindcss
โ โโโ ็ๅฌๆจกๅผ # --watch
โ โโโ ้
็ฝฎๆไปถๅๅงๅ # tailwindcss init
โ
โโโ tailwindcss@4.x (Beta) # ไธไธไปฃ็ๆฌ
โโโ Rust ๅผๆ # ๆง่ฝๆๅ 10x
โโโ ๅ็ CSS ๅฏผๅ
ฅ # @import "tailwindcss"
โโโ ้ถ้
็ฝฎๅฏๅจ # ๆ ้้
็ฝฎๆไปถ
ๅฎๆนๆไปถ
โโโ @tailwindcss/typography # ๆ็ๆ ทๅผ
โ โโโ prose ็ฑป # ๅฏๆๆฌๆ ทๅผ
โ โโโ ่ชๅฎไน้
็ฝฎ # ้ข่ฒใ้ด่ท่ฐๆด
โ
โโโ @tailwindcss/forms # ่กจๅๅ
็ด ๆ ทๅผ
โ โโโ ๅบ็ก่พๅ
ฅๆกๆ ทๅผ # form-input
โ โโโ ้ๆฉๆกๆ ทๅผ # form-select
โ โโโ ๅ้/ๅค้ๆก # form-checkbox
โ
โโโ @tailwindcss/aspect-ratio # ๅฎฝ้ซๆฏ
โ โโโ aspect-video # 16:9
โ โโโ aspect-square # 1:1
โ โโโ ่ชๅฎไนๆฏไพ # aspect-[4/3]
โ
โโโ @tailwindcss/line-clamp # ๆๆฌๆชๆญ
โ โโโ line-clamp-1 ~ 6 # ่กๆฐๆงๅถ
โ โโโ line-clamp-none # ๅๆถๆชๆญ
โ
โโโ @tailwindcss/container-queries # ๅฎนๅจๆฅ่ฏข
โโโ @container # ๅฎนๅจๅฃฐๆ
โโโ @md/container # ๅฎนๅจๆญ็น
ๅฎๆน UI ๅบ
โโโ Tailwind UI # ๅฎๆนไป่ดน็ปไปถๅบ
โ โโโ 500+ ็ปไปถ # React + Vue
โ โโโ ๅบ็จ้กต้ขๆจกๆฟ # ๅฎๆด้กต้ข
โ โโโ ่ฅ้้กต้ขๆจกๆฟ # Landing pages
โ
โโโ Headless UI # ๆ ๆ ทๅผ็ปไปถ
โโโ Combobox # ็ปๅๆก
โโโ Dialog # ๅฏน่ฏๆก
โโโ Disclosure # ๅฑๅผ/ๆๅ
โโโ Listbox # ๅ่กจ้ๆฉ
โโโ Menu # ไธๆ่ๅ
โโโ Popover # ๅผนๅบๅฑ
โโโ Radio Group # ๅ้็ป
โโโ Switch # ๅผๅ
ณ
โโโ Tabs # ๆ ็ญพ้กต
โโโ Transition # ่ฟๆธกๅจ็ป
็ฌฌไธๆน็ๆ็ณป็ป
็ฌฌไธๆน UI ็ปไปถๅบ๏ผๆๆต่กๅบฆๆๅบ๏ผ
1. shadcn/ui โญ 55k+
- ๅฏๅคๅถ็ฒ่ดด็็ปไปถ
- ๅบไบ Radix UI
- TypeScript + Tailwind
2. DaisyUI โญ 32k+
- ่ฏญไนๅ็ฑปๅ
- 30+ ็ปไปถ
- ไธป้ข็ณป็ป
3. Flowbite โญ 6k+
- 500+ ็ปไปถ
- Figma ่ฎพ่ฎกๆไปถ
- React/Vue/Angular/Svelte
4. Preline UI โญ 4k+
- 250+ ็คบไพ
- ๆทฑ่ฒๆจกๅผ
- ้ซ็บง็ปไปถ
5. Meraki UI โญ 3k+
- ๅ
่ดน็ปไปถ
- RTL ๆฏๆ
- Alpine.js ้ๆ
ๅทฅๅ
ทๅบ
โโโ tailwind-merge # ๅๅนถๅฒ็ช็ฑปๅ
โ โโโ twMerge('px-2 py-1', 'p-3') = 'p-3'
โ
โโโ clsx + tailwind-merge # ๆกไปถ็ฑปๅ + ๅๅนถ
โ โโโ cn() ๅฝๆฐๆจกๅผ
โ
โโโ class-variance-authority # ็ปไปถๅไฝ็ฎก็
โ โโโ cva() ๅฝๆฐ
โ
โโโ tailwindcss-animate # ๅจ็ปๆฉๅฑ
โ โโโ animate-fade-in ็ญ
โ
โโโ tailwind-scrollbar # ๆปๅจๆกๆ ทๅผ
โ
โโโ @tailwindcss/typography # ๆ็ๆ ทๅผ
โ
โโโ tailwindcss-debug-screens # ่ฐ่ฏๆญ็นๆพ็คบ
ๅผๅๅทฅๅ
ท
โโโ VS Code ๆไปถ
โ โโโ Tailwind CSS IntelliSense # ๅฎๆนๆไปถ
โ โ โโโ ่ชๅจ่กฅๅ
จ
โ โ โโโ ๆฌๅ้ข่ง
โ โ โโโ ่ฏญๆณ้ซไบฎ
โ โ โโโ ็ฑปๅๆๅบ
โ โโโ Headwind # ็ฑปๅๆๅบ
โ โโโ Tailwind Shades # ้ข่ฒ็ๆ
โ
โโโ Prettier ๆไปถ
โ โโโ prettier-plugin-tailwindcss # ่ชๅจๆๅบ
โ
โโโ ESLint ๆไปถ
โ โโโ eslint-plugin-tailwindcss # ่งๅๆฃๆฅ
โ
โโโ Chrome ๆฉๅฑ
โโโ Tailwind CSS Devtools # ๆ ทๅผ่ฐ่ฏ
4.2 UnoCSS ็ๆ็ณป็ป
ๅฎๆน้ข่ฎพ็ณป็ป
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ UnoCSS ้ข่ฎพ็ณป็ป โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
ๆ ธๅฟ้ข่ฎพ
โโโ @unocss/preset-uno # ้ป่ฎค้ข่ฎพ๏ผๆจ่๏ผ
โ โโโ ๅบไบ Windi CSS # ๅ
ผๅฎน Tailwind
โ โโโ ๅ
ๅซๆๆๅบ็กๅทฅๅ
ท # ๅฎๆด็ utility set
โ โโโ ่ชๅจๆฃๆตๆทฑ่ฒๆจกๅผ # prefers-color-scheme
โ
โโโ @unocss/preset-wind # Tailwind ๅ
ผๅฎน
โ โโโ ๅฎๅ
จๅ
ผๅฎน Tailwind v3 # ็ฑปๅ 1:1 ๆ ๅฐ
โ โโโ ็ธๅ็่ฎพ่ฎก็ณป็ป # ้ข่ฒใ้ด่ทไธ่ด
โ โโโ ่ฟ็งปๅๅฅฝ # ้ถๆๆฌ่ฟ็งป
โ
โโโ @unocss/preset-mini # ๆๅฐ้ข่ฎพ
โ โโโ ๆ็ฒพ็ฎ็ๆ ธๅฟ # ~3KB
โ โโโ ๆ ้ป่ฎคไธป้ข # ๅฎๅ
จ่ชๅฎไน
โ โโโ ้ๅ้ซ็บง็จๆท # ้่ฆ้
็ฝฎ
โ
โโโ @unocss/preset-rem-to-px # rem ่ฝฌ px
โโโ ่ชๅจ่ฝฌๆขๅไฝ # ้ๅ็งปๅจ็ซฏ
ๆฉๅฑ้ข่ฎพ
โโโ @unocss/preset-icons # ๅพๆ ้ข่ฎพ๏ผๆ ธๅฟ๏ผ
โ โโโ 100+ ๅพๆ ้ # Iconify ๆฏๆ
โ โโโ ๆ้ๅ ่ฝฝ # ๅช็จๅฐ็ๅพๆ
โ โโโ ๅค็งไฝฟ็จๆนๅผ
โ โ โโโ <div class="i-mdi-home" /> # CSS ๅพๆ
โ โ โโโ <div i-mdi-home /> # Attributify
โ โ โโโ <div class="i-[mdi--home]" /> # ๅจๆ
โ โโโ ่ชๅฎไนๅพๆ ้
โ โโโ collections: { custom: {...} }
โ
โโโ @unocss/preset-attributify # ๅฑๆงๅๆจกๅผ
โ โโโ <div m-4 p-2 bg-blue /> # ็ฑปๅไฝไธบๅฑๆง
โ โโโ ๅ็ผๆฏๆ
โ โ โโโ <div uno-m-4 /> # ้ฟๅ
ๅฒ็ช
โ โโโ ๅธๅฐๅฑๆง
โ โโโ <button disabled bg-gray-500 />
โ
โโโ @unocss/preset-typography # ๆ็้ข่ฎพ
โ โโโ prose ็ฑป # ็ฑปไผผ @tailwindcss/typography
โ
โโโ @unocss/preset-web-fonts # Web ๅญไฝ
โ โโโ Google Fonts # ๅ
็ฝฎๆฏๆ
โ โโโ Bunny Fonts # ้็งๅๅฅฝ
โ โโโ ่ชๅฎไนๅญไฝๆไพๅ
โ
โโโ @unocss/preset-tagify # ๆ ็ญพๅ
โ โโโ <tag-red-500 /> # ็ปไปถๅ็ฑปๅ
โ
โโโ @unocss/preset-scrollbar # ๆปๅจๆก
โโโ ็ฑปไผผ tailwind-scrollbar
็คพๅบ้ข่ฎพ
โโโ @unocss/preset-daisy # DaisyUI ๅ
ผๅฎน
โโโ @unocss/preset-forms # ่กจๅ้ข่ฎพ
โโโ @unocss/preset-chinese # ไธญๆๆ็
โโโ @unocss/preset-autoprefixer # Autoprefixer
ๅทฅๅ ทไธ้ๆ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ UnoCSS ๅทฅๅ
ท้พ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
ๆๅปบๅทฅๅ
ท้ๆ
โโโ Vite (ๅฎๆน)
โ โโโ npm i -D unocss
โ import UnoCSS from 'unocss/vite'
โ plugins: [UnoCSS()]
โ
โโโ Webpack
โ โโโ npm i -D @unocss/webpack
โ
โโโ Rollup
โ โโโ npm i -D @unocss/rollup
โ
โโโ Nuxt (ๅฎๆนๆจกๅ)
โ โโโ npm i -D @unocss/nuxt
โ modules: ['@unocss/nuxt']
โ
โโโ Astro
โ โโโ npm i -D @unocss/astro
โ integrations: [UnoCSS()]
โ
โโโ Svelte/SvelteKit
โ โโโ npm i -D @unocss/svelte-scoped
โ
โโโ ๅ
ถไป
โโโ @unocss/esbuild
โโโ @unocss/rspack
โโโ @unocss/farm
CLI ๅทฅๅ
ท
โโโ @unocss/cli
โ โโโ npx unocss "src/**/*" -o output.css
โ โโโ --watch ๆจกๅผ
โ โโโ --minify ๅ็ผฉ
โ
โโโ @unocss/eslint-plugin
โ โโโ ESLint ่งๅๆฃๆฅ
โ
โโโ @unocss/runtime
โ โโโ ๆต่งๅจ่ฟ่กๆถ็ๆ๏ผCDN ไฝฟ็จ๏ผ
โ
โโโ @unocss/inspector
โโโ ๅฏ่งๅ่ฐ่ฏๅทฅๅ
ท
VS Code ๆฉๅฑ
โโโ UnoCSS (ๅฎๆน)
โ โโโ ่ชๅจ่กฅๅ
จ
โ โโโ ๆฌๅ้ข่ง
โ โโโ ้ข่ฒ้ข่ง
โ โโโ ่ทณ่ฝฌๅฐๅฎไน
โ
โโโ UnoCSS snippets
โโโ ไปฃ็ ็ๆฎต
4.3 ็ๆ็ณป็ปๅฏนๆฏ็ฉ้ต
| ็ฑปๅซ | Tailwind CSS | UnoCSS | ่ๅบ |
|---|---|---|---|
| UI ็ปไปถๅบ | โญโญโญโญโญ | โญโญ | Tailwind |
| ๅฎๆนๆไปถ | โญโญโญโญโญ | โญโญโญ | Tailwind |
| ๅทฅๅ ท้พๆ็ๅบฆ | โญโญโญโญโญ | โญโญโญโญ | Tailwind |
| IDE ๆฏๆ | โญโญโญโญโญ | โญโญโญ | Tailwind |
| ๅพๆ ้ๆ | โญโญ | โญโญโญโญโญ | UnoCSS |
| ้ ็ฝฎ็ตๆดปๆง | โญโญโญ | โญโญโญโญโญ | UnoCSS |
| ็ฐไปฃๅทฅๅ ท้พๆฏๆ | โญโญโญโญ | โญโญโญโญโญ | UnoCSS |
| ้ข่ฎพไธฐๅฏๅบฆ | โญโญ | โญโญโญโญโญ | UnoCSS |
5. ๅผๅไฝ้ช่ฏฆ่งฃ
5.1 IDE ๆฏๆๅฏนๆฏ
VS Code ๅ่ฝๅฏนๆฏ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ VS Code ๅ่ฝๅฏนๆฏ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Tailwind CSS IntelliSense (ๅฎๆน)
โโโ ๅ่ฝๅฎๆดๅบฆ: โญโญโญโญโญ
โโโ
โ โ
่ชๅจ่กฅๅ
จ๏ผไธไธๆๆ็ฅ๏ผ
โ โ
ๆฌๅ้ข่ง๏ผCSS ไปฃ็ ๏ผ
โ โ
้ข่ฒ้ข่ง๏ผๅ
่ๆนๅ๏ผ
โ โ
็ฑปๅๆๅบ๏ผ่ชๅจ๏ผ
โ โ
่ฏญๆณ้ซไบฎ
โ โ
้่ฏฏๆ็คบ
โ โ
้
็ฝฎๆไปถ่ทณ่ฝฌ
โ โ
่ชๅฎไนๅผๆฏๆ
โ
โโโ ๅฎ่ฃ
้: 8M+
UnoCSS (ๅฎๆน)
โโโ ๅ่ฝๅฎๆดๅบฆ: โญโญโญโญ
โโโ
โ โ
่ชๅจ่กฅๅ
จ
โ โ
ๆฌๅ้ข่ง
โ โ
้ข่ฒ้ข่ง
โ โ
่ทณ่ฝฌๅฐ้ข่ฎพ
โ โ
ๅฟซๆทๆนๅผๆฏๆ
โ
โ โ ็ฑปๅๆๅบ๏ผ้้
ๅ Prettier๏ผ
โ โ ่ชๅฎไน่งๅ้ข่ง๏ผๆ้๏ผ
โ
โโโ ๅฎ่ฃ
้: 800K+
ๅฎ้ ไฝฟ็จไฝ้ชๅฏนๆฏ๏ผ
| ๅ่ฝ | Tailwind | UnoCSS | ๅทฎๅผ่ฏดๆ |
|---|---|---|---|
| ่กฅๅ จ้ๅบฆ | ~50ms | ~30ms | UnoCSS ๆดๅฟซ |
| ่กฅๅ จ็ฒพๅบฆ | ๆ้ซ | ้ซ | Tailwind ๆดๆบ่ฝ |
| ๆฌๅไฟกๆฏ | ๅฎๆด | ๅบๆฌ | Tailwind ๆพ็คบๆดๅค |
| ้ข่ฒ้ข่ง | ไผ็ง | ่ฏๅฅฝ | ไธค่ ้ฝๅพๅฅฝ |
| ่ชๅฎไนๅผ | ๅฎๆดๆฏๆ | ้จๅๆฏๆ | Tailwind ๆดๅผบ |
| ๅฟซๆท้ฎ | Cmd+K Cmd+G | ๆ | Tailwind ็ฌๆ |
WebStorm ๆฏๆ
Tailwind CSS
โโโ ๅ็ๆฏๆ # ๅ
็ฝฎๆไปถ
โโโ ่ชๅจ้
็ฝฎๆฃๆต # ๅผ็ฎฑๅณ็จ
โโโ ๅฎๆด็ไปฃ็ ๆดๅฏ # ๅฏผ่ชใ้ๆ
โโโ ๆบ่ฝ่กฅๅ
จ # ้กน็ฎๆ็ฅ
UnoCSS
โโโ ็คพๅบๆไปถ # ้ๅฎๆน
โโโ ๅบๆฌๆฏๆ # ๆ้็่กฅๅ
จ
โโโ ้ๆๅจ้
็ฝฎ # ไธๅฆ Tailwind ๅฎๅ
5.2 ไปฃ็ ็คบไพๆทฑๅบฆๅฏนๆฏ
ๆกไพ 1๏ผๅก็็ปไปถ
Tailwind CSS ๅฎ็ฐ๏ผ
// Card.jsx
import { twMerge } from 'tailwind-merge'
import { clsx, type ClassValue } from 'clsx'
// ๅทฅๅ
ทๅฝๆฐ๏ผๅธธ็จๆจกๅผ๏ผ
function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs))
}
export function Card({
children,
className,
variant = 'default',
size = 'md',
interactive = false,
...props
}) {
return (
<div
className={cn(
// ๅบ็กๆ ทๅผ
'rounded-lg border bg-card text-card-foreground shadow-sm',
// ๅฐบๅฏธๅไฝ
size === 'sm' && 'p-3',
size === 'md' && 'p-6',
size === 'lg' && 'p-8',
// ้ข่ฒๅไฝ
variant === 'default' && 'border-border bg-white',
variant === 'outline' && 'border-2 border-dashed',
variant === 'ghost' && 'border-transparent bg-transparent',
variant === 'destructive' && 'border-red-500 bg-red-50',
// ไบคไบ็ถๆ
interactive && [
'cursor-pointer',
'transition-all duration-200',
'hover:shadow-md hover:border-gray-300',
'active:scale-[0.98]',
'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2'
],
// ไผ ๅ
ฅ็็ฑปๅ๏ผ่ฆ็๏ผ
className
)}
{...props}
>
{children}
</div>
)
}
UnoCSS ๅฎ็ฐ๏ผ
// Card.jsx
// ไฝฟ็จ Attributify ้ข่ฎพ + ๅฟซๆทๆนๅผ
// uno.config.ts ไธญๅฎไน
const shortcuts = {
'card': 'rounded-lg border bg-card text-card-foreground shadow-sm',
'card-sm': 'card p-3',
'card-md': 'card p-6',
'card-lg': 'card p-8',
'card-interactive': 'cursor-pointer transition-all duration-200 hover:shadow-md hover:border-gray-300 active:scale-[0.98] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2',
}
// ็ปไปถไฝฟ็จ
export function Card({
children,
className,
variant = 'default',
size = 'md',
interactive = false,
...props
}) {
const variantStyles = {
default: 'border-border bg-white',
outline: 'border-2 border-dashed',
ghost: 'border-transparent bg-transparent',
destructive: 'border-red-500 bg-red-50'
}
return (
<div
class={[
`card-${size}`,
variantStyles[variant],
interactive && 'card-interactive',
className
].filter(Boolean).join(' ')}
{...props}
>
{children}
</div>
)
}
// ๆ่
ไฝฟ็จ Attributify ๆจกๅผ
export function CardAttributify({ children, ...props }) {
return (
<div
p-6 rounded-lg border bg-white shadow-sm
hover:shadow-md transition-shadow
{...props}
>
{children}
</div>
)
}
ๆกไพ 2๏ผ่กจๅ่พๅ ฅ็ปไปถ
Tailwind CSS ๅฎ็ฐ๏ผ
// Input.jsx
import { forwardRef } from 'react'
import { cn } from '@/lib/utils'
export const Input = forwardRef(({
className,
type = 'text',
error,
disabled,
...props
}, ref) => {
return (
<input
type={type}
className={cn(
// ๅบ็กๆ ทๅผ
'flex h-10 w-full rounded-md border border-input bg-background px-3 py-2',
'text-sm ring-offset-background file:border-0 file:bg-transparent',
'file:text-sm file:font-medium placeholder:text-muted-foreground',
// ็ฆ็น็ถๆ
'focus-visible:outline-none focus-visible:ring-2',
'focus-visible:ring-ring focus-visible:ring-offset-2',
// ็ฆ็จ็ถๆ
disabled && 'cursor-not-allowed opacity-50',
// ้่ฏฏ็ถๆ
error && [
'border-red-500',
'focus-visible:ring-red-500',
'placeholder:text-red-300'
],
// ่ฟๆธกๅจ็ป
'transition-colors duration-200',
className
)}
disabled={disabled}
ref={ref}
{...props}
/>
)
})
Input.displayName = 'Input'
UnoCSS ๅฎ็ฐ๏ผไฝฟ็จ @unocss/preset-forms๏ผ๏ผ
// Input.jsx
// ไฝฟ็จ preset-forms ้ข่ฎพ
export const Input = forwardRef(({
className,
type = 'text',
error,
disabled,
...props
}, ref) => {
return (
<input
type={type}
class={cn(
// ไฝฟ็จ้ข่ฎพ็่กจๅๆ ทๅผ
'form-input',
// ่ชๅฎไน่ฆ็
'w-full h-10 px-3 py-2',
'rounded-md border border-gray-300',
'text-sm placeholder-gray-400',
'focus:border-blue-500 focus:ring-2 focus:ring-blue-500/20',
'transition-all duration-200',
// ็ถๆ
disabled && 'opacity-50 cursor-not-allowed',
error && 'border-red-500 focus:border-red-500 focus:ring-red-500/20',
className
)}
disabled={disabled}
ref={ref}
{...props}
/>
)
})
ๆกไพ 3๏ผๅๅบๅผๅฏผ่ชๆ
Tailwind CSS ๅฎ็ฐ๏ผ
// Navbar.jsx
export function Navbar() {
const [isOpen, setIsOpen] = useState(false)
return (
<nav className="bg-white shadow-md">
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div className="flex justify-between h-16">
{/* Logo */}
<div className="flex items-center">
<a href="/" className="text-xl font-bold text-gray-800">
Logo
</a>
</div>
{/* Desktop Menu */}
<div className="hidden md:flex items-center space-x-4">
{['้ฆ้กต', 'ไบงๅ', 'ๅ
ณไบ', '่็ณป'].map((item) => (
<a
key={item}
href="#"
className="text-gray-600 hover:text-gray-900 px-3 py-2 rounded-md text-sm font-medium transition-colors"
>
{item}
</a>
))}
<button className="bg-blue-600 text-white px-4 py-2 rounded-md text-sm font-medium hover:bg-blue-700 transition-colors">
็ปๅฝ
</button>
</div>
{/* Mobile Menu Button */}
<div className="flex items-center md:hidden">
<button
onClick={() => setIsOpen(!isOpen)}
className="text-gray-600 hover:text-gray-900 p-2"
>
<svg className="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
{isOpen ? (
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M6 18L18 6M6 6l12 12" />
) : (
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M4 6h16M4 12h16M4 18h16" />
)}
</svg>
</button>
</div>
</div>
{/* Mobile Menu */}
<div className={`md:hidden ${isOpen ? 'block' : 'hidden'}`}>
<div className="px-2 pt-2 pb-3 space-y-1">
{['้ฆ้กต', 'ไบงๅ', 'ๅ
ณไบ', '่็ณป'].map((item) => (
<a
key={item}
href="#"
className="text-gray-600 hover:text-gray-900 hover:bg-gray-50 block px-3 py-2 rounded-md text-base font-medium"
>
{item}
</a>
))}
</div>
</div>
</div>
</nav>
)
}
UnoCSS ๅฎ็ฐ๏ผ
// Navbar.jsx
export function Navbar() {
const [isOpen, setIsOpen] = useState(false)
return (
<nav bg-white shadow-md>
<div max-w-7xl mx-auto px-4 sm:px-6 lg:px-8>
<div flex justify-between h-16>
{/* Logo */}
<div flex items-center>
<a href="/" text-xl font-bold text-gray-800>
Logo
</a>
</div>
{/* Desktop Menu */}
<div hidden md:flex items-center space-x-4>
{['้ฆ้กต', 'ไบงๅ', 'ๅ
ณไบ', '่็ณป'].map((item) => (
<a
key={item}
href="#"
text-gray-600 hover:text-gray-900 px-3 py-2 rounded-md text-sm font-medium transition-colors
>
{item}
</a>
))}
<button
bg-blue-600 text-white px-4 py-2 rounded-md text-sm font-medium
hover:bg-blue-700 transition-colors
>
็ปๅฝ
</button>
</div>
{/* Mobile Menu Button */}
<div flex items-center md:hidden>
<button
onClick={() => setIsOpen(!isOpen)}
text-gray-600 hover:text-gray-900 p-2
>
<div className={isOpen ? 'i-mdi-close' : 'i-mdi-menu'} text-2xl>
</div>
</button>
</div>
</div>
{/* Mobile Menu */}
<div md:hidden block={isOpen}>
<div px-2 pt-2 pb-3 space-y-1>
{['้ฆ้กต', 'ไบงๅ', 'ๅ
ณไบ', '่็ณป'].map((item) => (
<a
key={item}
href="#"
text-gray-600 hover:text-gray-900 hover:bg-gray-50 block px-3 py-2 rounded-md text-base font-medium
>
{item}
</a>
))}
</div>
</div>
</div>
</nav>
)
}
5.3 Attributify ๆจกๅผ่ฏฆ่งฃ
UnoCSS ็ Attributify ้ข่ฎพๆฏๅ ถ็ฌ็นๅ่ฝ๏ผๅฏไปฅๅฐ็ฑปๅไฝไธบ HTML ๅฑๆงไฝฟ็จใ
ไผ ็ปๅๆณ vs Attributify๏ผ
<!-- ไผ ็ป Tailwind/UnoCSS -->
<div class="m-4 p-4 bg-blue-500 text-white rounded-lg shadow-md hover:shadow-lg transition-shadow">
ไผ ็ปๅๆณ
</div>
<!-- UnoCSS Attributify ๆจกๅผ -->
<div
m-4
p-4
bg-blue-500
text-white
rounded-lg
shadow-md
hover:shadow-lg
transition-shadow
>
Attributify ๅๆณ
</div>
<!-- ๅ็ปๅๆณ๏ผๆดๆธ
ๆฐ๏ผ -->
<div
m="4"
p="4"
bg="blue-500"
text="white"
rounded="lg"
shadow="md hover:lg"
transition="shadow"
>
ๅ็ปๅๆณ
</div>
<!-- ๅคๆ็คบไพ -->
<button
flex items-center justify-center
gap-2
px-6 py-3
bg="blue-600 hover:blue-700"
text="white"
font="medium"
rounded="md"
transition="all duration-200"
disabled:opacity-50
cursor="pointer disabled:not-allowed"
>
ๆไบค
</button>
Attributify ้ ็ฝฎ๏ผ
// uno.config.ts
import { defineConfig, presetUno, presetAttributify } from 'unocss'
export default defineConfig({
presets: [
presetUno(),
presetAttributify({
// ๅ็ผ๏ผๅฏ้๏ผ
prefix: 'uno-',
// ๅ็ผ๏ผๅฏ้๏ผ
prefixedOnly: false,
// ๅฟฝ็ฅ็ๅฑๆง
ignoreAttributes: ['label']
})
]
})
5.4 ๅพๆ ้ๆๅฏนๆฏ
Tailwind CSS ๅพๆ ๆนๆก
// ๆนๆก 1๏ผไฝฟ็จ SVG ๅพๆ
import { HomeIcon } from '@heroicons/react/24/outline'
function IconDemo() {
return (
<div className="flex items-center gap-2">
<HomeIcon className="w-6 h-6 text-blue-500" />
<span>้ฆ้กต</span>
</div>
)
}
// ๆนๆก 2๏ผไฝฟ็จๅพๆ ๅญไฝ๏ผๅฆ Font Awesome๏ผ
// ้่ฆๅ็ฌๅผๅ
ฅ CSS
function FontDemo() {
return (
<div className="flex items-center gap-2">
<i className="fas fa-home text-blue-500 text-xl"></i>
<span>้ฆ้กต</span>
</div>
)
}
// ๆนๆก 3๏ผๅ
่ SVG
function InlineSvgDemo() {
return (
<div className="flex items-center gap-2">
<svg className="w-6 h-6 text-blue-500" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M3 12l2-2m0 0l7-7 7 7M5 10v10a1 1 0 001 1h3m10-11l2 2m-2-2v10a1 1 0 01-1 1h-3m-6 0a1 1 0 001-1v-4a1 1 0 011-1h2a1 1 0 011 1v4a1 1 0 001 1m-6 0h6" />
</svg>
<span>้ฆ้กต</span>
</div>
)
}
UnoCSS ๅพๆ ๆนๆก
// ไฝฟ็จ @unocss/preset-icons๏ผๆจ่๏ผ
// ๅบ็ก็จๆณ
function IconDemo() {
return (
<div flex items-center gap-2>
<div className="i-mdi-home w-6 h-6 text-blue-500" />
<span>้ฆ้กต</span>
</div>
)
}
// Attributify ๅๆณ
function AttributifyIconDemo() {
return (
<div flex items-center gap-2>
<div i-mdi-home w-6 h-6 text-blue-500 />
<span>้ฆ้กต</span>
</div>
)
}
// ไฝฟ็จไธๅๅพๆ ้
function MultiIconDemo() {
return (
<div flex gap-4>
{/* Material Design */}
<div i-mdi-home w-6 h-6 />
{/* Phosphor Icons */}
<div i-ph-house w-6 h-6 />
{/* Heroicons */}
<div i-heroicons-home w-6 h-6 />
{/* Lucide */}
<div i-lucide-home w-6 h-6 />
{/* Tabler */}
<div i-tabler-home w-6 h-6 />
</div>
)
}
// ๅจๆๅพๆ
function DynamicIcon({ name, iconSet = 'mdi' }) {
return (
<div className={`i-${iconSet}-${name} w-6 h-6`} />
)
}
// ไฝฟ็จ่ชๅฎไนๅพๆ
function CustomIconDemo() {
return (
<div i-custom-logo w-8 h-8 />
)
}
UnoCSS ๅพๆ ้ ็ฝฎ๏ผ
// uno.config.ts
import { defineConfig, presetUno, presetIcons } from 'unocss'
import { FileSystemIconLoader } from '@iconify/utils/lib/loader/node-loaders'
export default defineConfig({
presets: [
presetUno(),
presetIcons({
// ็ผฉๆพๆฏไพ
scale: 1.2,
// ้ขๅค CSS ๅฑๆง
extraProperties: {
'display': 'inline-block',
'vertical-align': 'middle'
},
// ่ชๅฎไนๅพๆ ้
collections: {
// ไปๆไปถ็ณป็ปๅ ่ฝฝ
custom: FileSystemIconLoader('./assets/icons'),
// ๅ
่ SVG
inline: {
'logo': '<svg viewBox="0 0 24 24">...',
'arrow': '<svg viewBox="0 0 24 24">...'
}
},
// ่ชๅจๅฎ่ฃ
ๅพๆ ้๏ผๅผๅๆจกๅผ๏ผ
autoInstall: true,
// ่ญฆๅๆชๆพๅฐ็ๅพๆ
warn: true
})
]
})
ๆฏๆ็ๅพๆ ้๏ผ100+๏ผ๏ผ
| ๅพๆ ้ | ๅ็ผ | ๆฐ้ |
|---|---|---|
| Material Design Icons | i-mdi-* | 7000+ |
| Phosphor Icons | i-ph-* | 7000+ |
| Heroicons | i-heroicons-* | 300+ |
| Lucide | i-lucide-* | 800+ |
| Tabler Icons | i-tabler-* | 4000+ |
| Carbon Icons | i-carbon-* | 2000+ |
| Simple Icons | i-simple-icons-* | 2500+ |
| Flag Icons | i-flag-* | 250+ |
6. ้ ็ฝฎ็ณป็ปๆทฑๅบฆๅฏนๆฏ
6.1 Tailwind CSS 4.0 ้ ็ฝฎ่ฏฆ่งฃ
Tailwind CSS 4.0 ๅผๅ ฅไบ CSS ไผๅ ็้ ็ฝฎๆนๅผ๏ผ่ฟๆฏไธ UnoCSS ๆๅคง็ๅบๅซไนไธใ
CSS ้ ็ฝฎๆไปถ็ปๆ
/* styles.css */
@import "tailwindcss";
/* ไธป้ข้
็ฝฎ */
@theme {
/* ้ข่ฒ */
--color-brand-50: #f0f9ff;
--color-brand-100: #e0f2fe;
--color-brand-500: #0ea5e9;
--color-brand-900: #0c4a6e;
/* ๅญไฝ */
--font-display: "Inter", sans-serif;
--font-mono: "Fira Code", monospace;
/* ้ด่ท */
--spacing-18: 4.5rem;
--spacing-88: 22rem;
/* ๆญ็น */
--breakpoint-3xl: 1920px;
/* ๅจ็ป */
--animate-fade-up: fade-up 0.5s ease-out;
@keyframes fade-up {
0% { opacity: 0; transform: translateY(10px); }
100% { opacity: 1; transform: translateY(0); }
}
}
/* ๅบ็กๅฑ */
@layer base {
html {
@apply antialiased;
}
body {
@apply bg-gray-50 text-gray-900;
}
}
/* ็ปไปถๅฑ */
@layer components {
.btn {
@apply px-4 py-2 rounded-md font-medium transition-colors;
}
.btn-primary {
@apply btn bg-brand-500 text-white hover:bg-brand-600;
}
}
/* ๅทฅๅ
ทๅฑ */
@layer utilities {
.text-shadow {
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
.scrollbar-hide {
-ms-overflow-style: none;
scrollbar-width: none;
}
.scrollbar-hide::-webkit-scrollbar {
display: none;
}
}
ไธ JavaScript ้ ็ฝฎ็ๅฏนๆฏ
| ็นๆง | CSS ้ ็ฝฎ (v4) | JS ้ ็ฝฎ (v3) | ่ฏดๆ |
|---|---|---|---|
| ้ ็ฝฎไฝ็ฝฎ | @theme ๆไปค | tailwind.config.js | v4 ๆด็ด่ง |
| ไธป้ข็ปงๆฟ | ่ชๅจ็ปงๆฟ้ป่ฎคไธป้ข | ้ๆๅจ extend | v4 ๆดๆบ่ฝ |
| ๅ้็ฑปๅ | CSS ่ชๅฎไนๅฑๆง | JS ๅฏน่ฑก | v4 ๅ็ๆฏๆ |
| ่ฟ่กๆถไฟฎๆน | ๆฏๆ | ไธๆฏๆ | v4 ๅฏๅจๆ่ฐๆด |
| ๆๅปบๅทฅๅ ท | ๆด่ฝป้ | ้่ฆ PostCSS | v4 ๆดๅฟซ้ |
6.2 UnoCSS ้ ็ฝฎ็ณป็ป
UnoCSS ไฝฟ็จ TypeScript/JavaScript ้ ็ฝฎ๏ผๆไพไบๆ้ซ็็ตๆดปๆงใ
้ ็ฝฎๆไปถ็ปๆ
// uno.config.ts
import {
defineConfig,
presetUno,
presetAttributify,
presetIcons,
presetTypography,
presetWebFonts,
transformerDirectives,
transformerVariantGroup,
extractorSplit
} from 'unocss'
import { FileSystemIconLoader } from '@iconify/utils/lib/loader/node-loaders'
export default defineConfig({
// ๅ
ๅฎนๆซๆ้
็ฝฎ
content: {
filesystem: [
'src/**/*.{html,js,ts,jsx,tsx,vue,svelte,astro}',
// ๆ้คๆไบๆไปถ
'!src/**/*.test.{js,ts}'
],
// ๅ
่ๅ
ๅฎน
inline: [
'<div class="p-4 m-2">',
]
},
// ้ข่ฎพๅ่กจ
presets: [
// ๆ ธๅฟ้ข่ฎพ
presetUno({
dark: 'class', // ๆ 'media'
attributifyPseudo: true,
}),
// ๅฑๆงๅๆจกๅผ
presetAttributify({
prefix: 'uno-',
prefixedOnly: false,
}),
// ๅพๆ ้ข่ฎพ
presetIcons({
scale: 1.2,
extraProperties: {
'display': 'inline-block',
'vertical-align': 'middle',
},
collections: {
custom: FileSystemIconLoader('./assets/icons'),
// ๅ
่ๅพๆ
inline: {
logo: '<svg viewBox="0 0 24 24">...</svg>',
}
},
autoInstall: true,
}),
// ๆ็้ข่ฎพ
presetTypography({
cssExtend: {
'code': {
color: '#476582',
backgroundColor: '#f3f4f6',
}
}
}),
// Web ๅญไฝ
presetWebFonts({
provider: 'google', // ๆ 'bunny'
fonts: {
sans: 'Inter:400,600,800',
mono: 'Fira Code:400,600',
}
}),
],
// ่ชๅฎไน่งๅ
rules: [
// ้ๆ่งๅ
['m-1', { margin: '0.25rem' }],
['m-2', { margin: '0.5rem' }],
// ๅจๆ่งๅ
[/^m-(\d+)$/, ([, d]) => ({ margin: `${d / 4}rem` })],
[/^p-(\d+)$/, ([, d]) => ({ padding: `${d / 4}rem` })],
// ๅคๆ่งๅ - ๅ่ง
[/^rounded-([\w-]+)$/, ([, s]) => {
const map: Record<string, string> = {
'sm': '0.125rem',
'md': '0.375rem',
'lg': '0.5rem',
'xl': '0.75rem',
'2xl': '1rem',
'3xl': '1.5rem',
'full': '9999px',
}
if (map[s]) {
return { 'border-radius': map[s] }
}
}],
// ไฝฟ็จไธป้ข
[/^text-brand-(\d+)$/, ([, d], { theme }) => {
const color = theme.colors?.brand?.[d]
if (color) {
return { color }
}
}],
],
// ๅฟซๆทๆนๅผ
shortcuts: {
// ๅบ็ก็ปไปถ
'btn': 'px-4 py-2 rounded font-medium transition-colors inline-flex items-center justify-center gap-2',
'btn-primary': 'btn bg-blue-600 text-white hover:bg-blue-700 focus:ring-2 focus:ring-blue-500',
'btn-secondary': 'btn bg-gray-200 text-gray-800 hover:bg-gray-300',
'btn-ghost': 'btn hover:bg-gray-100',
'btn-danger': 'btn bg-red-600 text-white hover:bg-red-700',
// ๅธๅฑ
'flex-center': 'flex items-center justify-center',
'flex-between': 'flex items-center justify-between',
'flex-col-center': 'flex flex-col items-center justify-center',
// ๅก็
'card': 'bg-white rounded-lg shadow-md overflow-hidden',
'card-hover': 'card hover:shadow-lg transition-shadow',
// ่กจๅ
'input': 'w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500',
'input-error': 'input border-red-500 focus:ring-red-500',
// ๅๅบๅผๅฎนๅจ
'container-fluid': 'w-full px-4 sm:px-6 lg:px-8',
'container-prose': 'max-w-prose mx-auto px-4',
},
// ไธป้ข้
็ฝฎ
theme: {
colors: {
brand: {
50: '#f0f9ff',
100: '#e0f2fe',
200: '#bae6fd',
300: '#7dd3fc',
400: '#38bdf8',
500: '#0ea5e9',
600: '#0284c7',
700: '#0369a1',
800: '#075985',
900: '#0c4a6e',
950: '#082f49',
},
// ่ฏญไนๅ้ข่ฒ
primary: 'var(--color-primary)',
secondary: 'var(--color-secondary)',
success: '#10b981',
warning: '#f59e0b',
error: '#ef4444',
},
spacing: {
'18': '4.5rem',
'88': '22rem',
'128': '32rem',
},
breakpoints: {
'xs': '480px',
'3xl': '1920px',
'4xl': '2560px',
},
animation: {
'fade-up': 'fade-up 0.5s ease-out',
'fade-in': 'fade-in 0.3s ease-out',
'slide-in': 'slide-in 0.3s ease-out',
},
keyframes: {
'fade-up': {
'0%': { opacity: '0', transform: 'translateY(10px)' },
'100%': { opacity: '1', transform: 'translateY(0)' },
},
'fade-in': {
'0%': { opacity: '0' },
'100%': { opacity: '1' },
},
'slide-in': {
'0%': { transform: 'translateX(-100%)' },
'100%': { transform: 'translateX(0)' },
},
},
},
// ๅไฝ๏ผ็ฑปไผผ Tailwind ็ modifiers๏ผ
variants: [
// ่ชๅฎไนๅไฝ
(matcher) => {
if (!matcher.startsWith('hover:')) return matcher
return {
matcher: matcher.slice(6),
selector: s => `${s}:hover`,
}
},
],
// ๆๅๅจ
extractors: [
extractorSplit,
// ่ชๅฎไนๆๅๅจ
{
name: 'custom',
extract({ code }) {
// ่ชๅฎไน็ฑปๅๆๅ้ป่พ
return [...code.matchAll(/class\(['"`]([^'"`]+)['"`]\)/g)]
.map(m => m[1].split(/\s+/))
.flat()
}
}
],
// ๅฎๅ
จๅ่กจ
safelist: [
'bg-red-500',
'text-3xl',
'lg:text-4xl',
'animate-fade-up',
// ๅจๆๅฎๅ
จๅ่กจ
...Array.from({ length: 10 }, (_, i) => `p-${i}`),
],
// ้ขๆฃ๏ผCSS Reset๏ผ
preflights: [
{
getCSS: () => `
*, *::before, *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
html {
-webkit-text-size-adjust: 100%;
-moz-tab-size: 4;
tab-size: 4;
}
body {
line-height: inherit;
}
`
}
],
// ๅๅค็
postprocess: [
// ่ชๅฎไนๅๅค็ๅจ
(util) => {
// ไฟฎๆน็ๆ็ CSS
if (util.selector.includes('important')) {
util.entries.forEach((entry) => {
entry[1] = `${entry[1]} !important`
})
}
return util
}
],
// ่ฝฌๆขๅจ๏ผTransformers๏ผ
transformers: [
transformerDirectives(), // @apply ็ญๆไปค
transformerVariantGroup(), // ๅไฝ็ป (hover:(bg-red text-white))
],
// ้
็ฝฎๅๅนถ็ญ็ฅ
configDeps: [
'./config/colors.ts',
'./config/spacing.ts',
],
})
6.3 ้ ็ฝฎ็ณป็ป่ฝๅๅฏนๆฏ
ๅจๆ่งๅๅฏนๆฏ
Tailwind CSS 4.0๏ผๆ้๏ผ
/* ไฝฟ็จไปปๆๅผ */
<div class="w-[123px] h-[calc(100vh-4rem)]">
/* ไฝๆ ๆณ่ชๅฎไน่งๅ้ป่พ */
UnoCSS๏ผๅฎๅ จ็ตๆดป๏ผ
// ๅฎๅ
จ่ชๅฎไน่งๅ้ป่พ
rules: [
// ๅจๆ้ด่ท
[/^gap-(\d+)-(\d+)$/, ([, x, y]) => ({
gap: `${x}px ${y}px`
})],
// ๅคๆ่ฎก็ฎ
[/^grid-cols-fit-(\d+)$/, ([, min]) => ({
'grid-template-columns': `repeat(auto-fit, minmax(${min}px, 1fr))`
})],
// ๆกไปถ่งๅ
[/^if-(\w+):(.*)$/, ([, condition, className], { theme }) => {
if (theme.conditions?.[condition]) {
return { [className]: theme.conditions[condition] }
}
}],
]
ๅฟซๆทๆนๅผๅฏนๆฏ
| ็นๆง | Tailwind v4 | UnoCSS |
|---|---|---|
| ๅฎไนไฝ็ฝฎ | @layer components | shortcuts ้
็ฝฎ |
| ๅๆฐๆฏๆ | ๆ้๏ผ@apply๏ผ | ๅฎๆด๏ผๅฝๆฐๆฏๆ๏ผ |
| ๅตๅฅ่ฝๅ | ไธๅฑ | ๆ ้ๅตๅฅ |
| ๅจๆ็ๆ | ไธๆฏๆ | ๆฏๆ |
UnoCSS ้ซ็บงๅฟซๆทๆนๅผ
shortcuts: [
// ้ๆๅฟซๆทๆนๅผ
['btn', 'px-4 py-2 rounded font-medium'],
// ๅจๆๅฟซๆทๆนๅผ
[/^btn-(.*)$/, ([, c], { theme }) => {
if (theme.colors[c]) {
return `bg-${c}-500 text-white hover:bg-${c}-600`
}
}],
// ๅตๅฅๅฟซๆทๆนๅผ
{
'card': 'bg-white rounded-lg shadow-md',
'card-interactive': 'card hover:shadow-lg transition-shadow cursor-pointer',
'card-interactive-primary': 'card-interactive border-2 border-blue-500',
},
]
7. ๅฎๆๆกไพ
7.1 ไผไธ็บง่ฎพ่ฎก็ณป็ปๆๅปบ
ไฝฟ็จ Tailwind CSS 4.0 ๆๅปบ
// design-system/index.ts
// ๅบไบ Tailwind CSS 4.0 ็่ฎพ่ฎก็ณป็ป
export const designTokens = {
colors: {
brand: {
50: '#f0f9ff',
500: '#0ea5e9',
900: '#0c4a6e',
},
semantic: {
success: '#10b981',
warning: '#f59e0b',
error: '#ef4444',
info: '#3b82f6',
}
},
spacing: {
'4.5': '1.125rem',
'18': '4.5rem',
},
borderRadius: {
'4xl': '2rem',
}
} as const
// components/Button.tsx
interface ButtonProps {
variant?: 'primary' | 'secondary' | 'ghost' | 'danger'
size?: 'sm' | 'md' | 'lg' | 'xl'
loading?: boolean
disabled?: boolean
children: React.ReactNode
}
export function Button({
variant = 'primary',
size = 'md',
loading,
disabled,
children,
...props
}: ButtonProps) {
return (
<button
className={cn(
// ๅบ็กๆ ทๅผ
'inline-flex items-center justify-center gap-2',
'font-medium transition-all duration-200',
'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2',
'disabled:opacity-50 disabled:cursor-not-allowed',
// ๅฐบๅฏธ
size === 'sm' && 'h-8 px-3 text-sm rounded-md',
size === 'md' && 'h-10 px-4 text-base rounded-lg',
size === 'lg' && 'h-12 px-6 text-lg rounded-lg',
size === 'xl' && 'h-14 px-8 text-xl rounded-xl',
// ๅไฝ
variant === 'primary' && [
'bg-brand-500 text-white',
'hover:bg-brand-600',
'focus-visible:ring-brand-500',
'active:scale-[0.98]',
],
variant === 'secondary' && [
'bg-gray-100 text-gray-900',
'hover:bg-gray-200',
'focus-visible:ring-gray-500',
],
variant === 'ghost' && [
'text-gray-700',
'hover:bg-gray-100',
'focus-visible:ring-gray-500',
],
variant === 'danger' && [
'bg-red-500 text-white',
'hover:bg-red-600',
'focus-visible:ring-red-500',
],
// ๅ ่ฝฝ็ถๆ
loading && 'opacity-70 cursor-wait',
)}
disabled={disabled || loading}
{...props}
>
{loading && <Spinner className="w-4 h-4 animate-spin" />}
{children}
</button>
)
}
ไฝฟ็จ UnoCSS ๆๅปบ
// uno.config.ts
// ไผไธ็บง่ฎพ่ฎก็ณป็ป้
็ฝฎ
import { defineConfig, presetUno, presetAttributify, presetIcons } from 'unocss'
const buttonShortcuts = {
// ๅบ็กๆ้ฎ
'btn': 'inline-flex items-center justify-center gap-2 font-medium transition-all duration-200 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 disabled:opacity-50 disabled:cursor-not-allowed',
// ๅฐบๅฏธๅไฝ
'btn-sm': 'btn h-8 px-3 text-sm rounded-md',
'btn-md': 'btn h-10 px-4 text-base rounded-lg',
'btn-lg': 'btn h-12 px-6 text-lg rounded-lg',
'btn-xl': 'btn h-14 px-8 text-xl rounded-xl',
// ้ข่ฒๅไฝ
'btn-primary': 'btn-md bg-brand-500 text-white hover:bg-brand-600 focus-visible:ring-brand-500 active:scale-[0.98]',
'btn-secondary': 'btn-md bg-gray-100 text-gray-900 hover:bg-gray-200 focus-visible:ring-gray-500',
'btn-ghost': 'btn-md text-gray-700 hover:bg-gray-100 focus-visible:ring-gray-500',
'btn-danger': 'btn-md bg-red-500 text-white hover:bg-red-600 focus-visible:ring-red-500',
// ็ถๆๅไฝ
'btn-loading': 'opacity-70 cursor-wait',
}
export default defineConfig({
presets: [
presetUno({
dark: 'class',
}),
presetAttributify(),
presetIcons(),
],
shortcuts: {
...buttonShortcuts,
// ่พๅ
ฅๆก
'input': 'w-full h-10 px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-brand-500 focus:border-transparent transition-all',
'input-error': 'input border-red-500 focus:ring-red-500',
'input-success': 'input border-green-500 focus:ring-green-500',
// ๅก็
'card': 'bg-white rounded-lg shadow-md overflow-hidden',
'card-bordered': 'card border border-gray-200',
'card-hoverable': 'card hover:shadow-lg transition-shadow cursor-pointer',
// ๅธๅฑ
'page-container': 'max-w-7xl mx-auto px-4 sm:px-6 lg:px-8',
'section': 'py-12 md:py-16 lg:py-20',
// ๆ็
'heading-1': 'text-4xl md:text-5xl lg:text-6xl font-bold tracking-tight',
'heading-2': 'text-3xl md:text-4xl font-bold tracking-tight',
'heading-3': 'text-2xl md:text-3xl font-semibold',
'text-body': 'text-base text-gray-600 leading-relaxed',
'text-small': 'text-sm text-gray-500',
},
theme: {
colors: {
brand: {
50: '#f0f9ff',
100: '#e0f2fe',
200: '#bae6fd',
300: '#7dd3fc',
400: '#38bdf8',
500: '#0ea5e9',
600: '#0284c7',
700: '#0369a1',
800: '#075985',
900: '#0c4a6e',
950: '#082f49',
},
},
animation: {
'spin-slow': 'spin 3s linear infinite',
'bounce-slow': 'bounce 2s infinite',
'pulse-slow': 'pulse 3s cubic-bezier(0.4, 0, 0.6, 1) infinite',
},
},
})
// components/Button.tsx - ไฝฟ็จ Attributify
export function Button({ variant = 'primary', size = 'md', loading, children, ...props }) {
const variantClass = `btn-${variant}`
const sizeClass = size !== 'md' ? `btn-${size}` : ''
return (
<button
class={[variantClass, sizeClass, loading && 'btn-loading'].filter(Boolean).join(' ')}
disabled={loading}
{...props}
>
{loading && <div i-svg-spinners-90-ring-with-bg text-lg animate-spin />}
{children}
</button>
)
}
7.2 ๆง่ฝไผๅๅฎๆ
Tailwind CSS ไผๅ็ญ็ฅ
/* 1. ไฝฟ็จ CSS ๅฑๆงๅถไผๅ
็บง */
@layer utilities {
/* ้ซๆง่ฝๅจ็ป */
.gpu-accelerated {
transform: translateZ(0);
will-change: transform;
}
/* ๅๅฐ้็ป */
.content-visibility {
content-visibility: auto;
contain-intrinsic-size: 0 500px;
}
}
/* 2. ๅฎนๅจๆฅ่ฏขไผๅ */
@layer components {
.card-grid {
@apply grid gap-4;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
}
@container (min-width: 768px) {
.card-grid {
grid-template-columns: repeat(3, 1fr);
}
}
}
// tailwind.config.js - ไผๅ้
็ฝฎ
module.exports = {
// ็ฒพ็กฎๆงๅถๆซๆ่ๅด
content: [
'./src/**/*.{js,ts,jsx,tsx}',
// ๆ็กฎๆ้คๆต่ฏๆไปถ
'!./src/**/*.test.{js,ts}',
'!./src/**/*.spec.{js,ts}',
'!./src/**/__tests__/**',
],
// ไป
ๅฏ็จ้่ฆ็ๆ ธๅฟๆไปถ
corePlugins: {
container: false, // ไฝฟ็จ่ชๅฎไนๅฎนๅจ
float: false, // ไฝฟ็จ flex/grid
clear: false,
objectFit: true,
objectPosition: true,
// ... ๆ้ๅฏ็จ
},
// ่ชๅฎไนๆๅๅจ
content: {
files: ['./src/**/*.{js,ts,jsx,tsx}'],
extract: {
tsx: (content) => {
// ๆด็ฒพ็กฎ็็ฑปๅๆๅ
return [...content.matchAll(/className=(?:["']([^"']+)["']|\{`([^`]+)`\})/g)]
.flatMap(match => (match[1] || match[2]).split(/\s+/))
.filter(Boolean)
}
}
},
}
UnoCSS ไผๅ็ญ็ฅ
// uno.config.ts - ๆง่ฝไผๅ้
็ฝฎ
export default defineConfig({
// 1. ็ฒพ็กฎ็ๅ
ๅฎนๅน้
content: {
filesystem: [
'src/**/*.{html,js,ts,jsx,tsx,vue,svelte}',
],
// ่ชๅฎไนๆๅ้ป่พ
pipeline: {
include: [/\.vue$/, /\.tsx?$/],
exclude: [/node_modules/, /\.git/, /test/],
}
},
// 2. ้ๆฉๅจๅๅนถไผๅ
mergeSelectors: true,
// 3. ๆๅฐๅ่พๅบ
minify: process.env.NODE_ENV === 'production',
// 4. ๅฎๅ
จๅ่กจไผๅ - ไป
ไฟ็ๅฟ
่ฆ็
safelist: [
// ๅจๆ็ฑปๅ
...Array.from({ length: 5 }, (_, i) => `col-span-${i + 1}`),
// ไธป้ขๅๆข
'dark',
'light',
],
// 5. ๅๅค็ไผๅ
postprocess: [
// ็งป้คๆ ็จ็ๅ็ผ
(util) => {
util.entries = util.entries.filter(([key]) =>
!key.startsWith('-webkit-') || key === '-webkit-appearance'
)
return util
}
],
// 6. ๆๅๅจไผๅ
extractors: [
{
name: 'optimized',
order: 0,
extract({ code }) {
// ้ข่ฟๆปค๏ผๅๅฐๆญฃๅๅน้
ๆฌกๆฐ
if (!code.includes('class') && !code.includes('className')) {
return []
}
// ้ซๆ็ๆๅ้ป่พ
return [...code.matchAll(/(?:class|className)=(?:["']([^"']+)["']|\{`([^`]+)`\})/g)]
.flatMap(m => (m[1] || m[2]).split(/\s+/))
.filter(c => c.length > 0 && !c.includes('${'))
}
}
],
})
7.3 ๅคงๅ้กน็ฎๆถๆๅฏนๆฏ
Tailwind CSS ้กน็ฎ็ปๆ
project-tailwind/
โโโ src/
โ โโโ components/
โ โ โโโ Button.tsx
โ โ โโโ Card.tsx
โ โ โโโ index.ts
โ โโโ styles/
โ โ โโโ globals.css # @import "tailwindcss"
โ โ โโโ components.css # @layer components
โ โ โโโ utilities.css # @layer utilities
โ โโโ app/
โ โ โโโ layout.tsx
โ โ โโโ page.tsx
โ โโโ lib/
โ โโโ utils.ts # cn() ๅฝๆฐ
โโโ tailwind.config.ts # ไธป้ข้
็ฝฎ
โโโ package.json
UnoCSS ้กน็ฎ็ปๆ
project-unocss/
โโโ src/
โ โโโ components/
โ โ โโโ Button.tsx
โ โ โโโ Card.tsx
โ โ โโโ index.ts
โ โโโ app/
โ โ โโโ layout.tsx
โ โ โโโ page.tsx
โ โโโ lib/
โ โโโ utils.ts
โโโ uno.config.ts # ๆ ธๅฟ้
็ฝฎ๏ผๅ
ๅซไธป้ขใ่งๅใๅฟซๆทๆนๅผ๏ผ
โโโ presets/
โ โโโ shortcuts.ts # ๅฟซๆทๆนๅผๅฎไน
โ โโโ rules.ts # ่ชๅฎไน่งๅ
โ โโโ theme.ts # ไธป้ข้
็ฝฎ
โโโ package.json
8. ๆไฝณๅฎ่ทต
8.1 ไปฃ็ ็ป็ป
Tailwind CSS ๆจ่ๆจกๅผ
// components/ui/Button.tsx
import { cn } from '@/lib/utils'
import { cva, type VariantProps } from 'class-variance-authority'
const buttonVariants = cva(
'inline-flex items-center justify-center gap-2 whitespace-nowrap',
{
variants: {
variant: {
default: 'bg-primary text-primary-foreground hover:bg-primary/90',
destructive: 'bg-destructive text-destructive-foreground hover:bg-destructive/90',
outline: 'border border-input bg-background hover:bg-accent hover:text-accent-foreground',
secondary: 'bg-secondary text-secondary-foreground hover:bg-secondary/80',
ghost: 'hover:bg-accent hover:text-accent-foreground',
link: 'text-primary underline-offset-4 hover:underline',
},
size: {
default: 'h-10 px-4 py-2',
sm: 'h-9 px-3',
lg: 'h-11 px-8',
icon: 'h-10 w-10',
},
},
defaultVariants: {
variant: 'default',
size: 'default',
},
}
)
export interface ButtonProps
extends React.ButtonHTMLAttributes<HTMLButtonElement>,
VariantProps<typeof buttonVariants> {}
export function Button({ className, variant, size, ...props }: ButtonProps) {
return (
<button
className={cn(buttonVariants({ variant, size, className }))}
{...props}
/>
)
}
UnoCSS ๆจ่ๆจกๅผ
// 1. ้
็ฝฎ้ไธญ็ฎก็
// uno.config.ts
shortcuts: {
// ไฝฟ็จ่ฏญไนๅๅฝๅ
'btn': 'inline-flex items-center justify-center gap-2',
'btn-primary': 'btn bg-blue-600 text-white hover:bg-blue-700',
'btn-secondary': 'btn bg-gray-200 text-gray-800 hover:bg-gray-300',
}
// 2. ็ปไปถไธญไฝฟ็จ
// components/Button.tsx
export function Button({ variant = 'primary', size = 'md', children }) {
return (
<button class={`btn-${variant} btn-${size}`}>
{children}
</button>
)
}
// 3. Attributify ๆจกๅผ๏ผๅฏ้๏ผ
// components/Card.tsx
export function Card({ title, children }) {
return (
<div
bg="white dark:gray-800"
rounded="lg"
shadow="md hover:lg"
p="6"
transition="shadow"
>
<h3 text-xl font-bold mb-4>{title}</h3>
{children}
</div>
)
}
8.2 ๅข้ๅไฝ่ง่
Tailwind CSS ๅข้่ง่
// .prettierrc
{
"plugins": ["prettier-plugin-tailwindcss"],
"tailwindFunctions": ["cn", "cva"]
}
// .eslintrc
{
"plugins": ["tailwindcss"],
"rules": {
"tailwindcss/classnames-order": "error",
"tailwindcss/enforces-negative-arbitrary-values": "error",
"tailwindcss/enforces-shorthand": "error",
"tailwindcss/migration-from-tailwind-2": "error",
"tailwindcss/no-arbitrary-value": "off",
"tailwindcss/no-custom-classname": "off"
}
}
UnoCSS ๅข้่ง่
// uno.config.ts - ๅข้ๅ
ฑไบซ้
็ฝฎ
import { defineConfig } from 'unocss'
export default defineConfig({
// ไฝฟ็จ้ข่ฎพ็กฎไฟไธ่ดๆง
presets: [
presetUno(),
presetAttributify({
prefix: 'uno-', // ้ฟๅ
ๅฒ็ช
}),
],
// ๅข้็บฆๅฎ็ๅฟซๆทๆนๅผ
shortcuts: {
// ๅฝๅ่ง่
// 1. ็ปไปถ๏ผๅๆฐๅ่ฏ
'btn': '...',
'card': '...',
'input': '...',
// 2. ๅไฝ๏ผ[็ปไปถ]-[ๅไฝๅ]
'btn-primary': '...',
'btn-danger': '...',
'card-hover': '...',
// 3. ๅทฅๅ
ท๏ผๅจ่ฏๆๅฝขๅฎน่ฏ
'flex-center': '...',
'text-truncate': '...',
'visually-hidden': '...',
},
// ไธป้ข้ๅฎ
theme: {
colors: {
// ๅชๅ
่ฎธไฝฟ็จ่ฟไบ้ข่ฒ
brand: {
50: '#f0f9ff',
500: '#0ea5e9',
900: '#0c4a6e',
},
// ็ฆๆญข็ดๆฅไฝฟ็จ tailwind ้ข่ฒ
// red: null,
// blue: null,
},
},
})
8.3 ๆทฑ่ฒๆจกๅผๆไฝณๅฎ่ทต
Tailwind CSS v4 ๅฎ็ฐ
/* styles.css */
@import "tailwindcss";
@theme {
--color-bg-primary: var(--bg-primary);
--color-text-primary: var(--text-primary);
}
@layer base {
:root {
--bg-primary: #ffffff;
--text-primary: #1f2937;
}
.dark {
--bg-primary: #111827;
--text-primary: #f9fafb;
}
}
/* ไฝฟ็จ */
<div class="bg-bg-primary text-text-primary">
UnoCSS ๅฎ็ฐ
// uno.config.ts
export default defineConfig({
presets: [
presetUno({
dark: 'class', // ๆ 'media'
}),
],
shortcuts: {
'bg-primary': 'bg-white dark:bg-gray-900',
'text-primary': 'text-gray-900 dark:text-gray-100',
'border-primary': 'border-gray-200 dark:border-gray-800',
},
})
// ็ปไปถไธญไฝฟ็จ
<div class="bg-primary text-primary border-primary">
// ๆ Attributify ๆจกๅผ
<div
bg="white dark:gray-900"
text="gray-900 dark:gray-100"
border="gray-200 dark:gray-800"
>
9. ๅธธ่ง้ฎ้ขไธ่งฃๅณๆนๆก
9.1 ็ฑปๅๅฒ็ช้ฎ้ข
้ฎ้ข๏ผ Tailwind ๅ UnoCSS ็ฑปๅๅฒ็ช
่งฃๅณๆนๆก๏ผ
// uno.config.ts
export default defineConfig({
presets: [
presetUno({
// ๆทปๅ ๅ็ผ้ฟๅ
ๅฒ็ช
prefix: 'u-',
}),
],
})
// ไฝฟ็จ
<div class="u-flex u-p-4 tailwind-class">
9.2 ๅจๆ็ฑปๅ้ฎ้ข
Tailwind CSS๏ผ้่ฆ้ ็ฝฎ๏ผ
// tailwind.config.js
module.exports = {
safelist: [
// ๆ็กฎๅๅบๅจๆ็ฑปๅ
'bg-red-500',
'bg-blue-500',
'text-lg',
'text-xl',
// ไฝฟ็จๆจกๅผ
{ pattern: /bg-(red|blue|green)-(100|500|900)/ },
],
}
// ไฝฟ็จ
function getColorClass(color) {
return `bg-${color}-500` // ๅฏ่ฝ่ขซ tree-shake
}
UnoCSS๏ผ่ชๅจๅค็๏ผ
// uno.config.ts
export default defineConfig({
// ๆๅๅจไผ่ชๅจๅค็
// ๅช้็กฎไฟๅ
ๅฎนๆซๆๅ
ๅซๅจๆ็ฑปๅ
content: {
filesystem: ['src/**/*.{js,ts,jsx,tsx}'],
// ๅฆๆ้่ฆ๏ผๆทปๅ ๅฎๅ
จๅ่กจ
safelist: [
...['red', 'blue', 'green'].flatMap(c =>
[100, 500, 900].map(n => `bg-${c}-${n}`)
),
],
},
})
// ไฝฟ็จ
function getColorClass(color) {
return `bg-${color}-500` // ไผ่ขซ่ชๅจๆฃๆต
}
9.3 VS Code ๆบ่ฝๆ็คบๅคฑๆ
Tailwind CSS
// .vscode/settings.json
{
"tailwindCSS.includeLanguages": {
"plaintext": "html",
"vue": "html",
"svelte": "html"
},
"tailwindCSS.experimental.classRegex": [
["cn\\(([^)]*)\\)", "(?:'|\"|`)([^']*)(?:'|\"|`)"],
["cva\\(([^)]*)\\)", "(?:'|\"|`)([^']*)(?:'|\"|`)", "(?:'|\"|`)([^']*)(?:'|\"|`)", "(?:'|\"|`)([^']*)(?:'|\"|`)"]
]
}
UnoCSS
// .vscode/settings.json
{
"unocss.root": "./uno.config.ts",
"unocss.include": [
"src/**/*.{html,js,ts,jsx,tsx,vue,svelte}"
]
}
9.4 ๆๅปบๅคฑ่ดฅ้ฎ้ข
Tailwind CSS ๅธธ่ง้ฎ้ข
# ้่ฏฏ๏ผContent ่ทฏๅพ้
็ฝฎ้่ฏฏ
# ่งฃๅณ๏ผๆฃๆฅ tailwind.config.js ไธญ็ content ้
็ฝฎ
# ้่ฏฏ๏ผPostCSS ้
็ฝฎ้ฎ้ข
# ่งฃๅณ๏ผ็กฎไฟ postcss.config.js ๆญฃ็กฎ้
็ฝฎ
module.exports = {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
}
# ้่ฏฏ๏ผๆพไธๅฐ CSS ๆไปถ
# ่งฃๅณ๏ผ็กฎไฟๅจๅ
ฅๅฃๆไปถๅฏผๅ
ฅ CSS
import './styles/globals.css'
UnoCSS ๅธธ่ง้ฎ้ข
# ้่ฏฏ๏ผ่ๆๆจกๅๆชๆพๅฐ
# ่งฃๅณ๏ผ็กฎไฟๅฏผๅ
ฅ่ๆๆจกๅ
import 'virtual:uno.css'
# ้่ฏฏ๏ผVite ้
็ฝฎ้ฎ้ข
# ่งฃๅณ๏ผ็กฎไฟๆไปถ้กบๅบๆญฃ็กฎ
import { defineConfig } from 'vite'
import UnoCSS from 'unocss/vite'
import vue from '@vitejs/plugin-vue'
export default defineConfig({
plugins: [
vue(),
UnoCSS(), // ๆพๅจๆกๆถๆไปถไนๅ
],
})
10. ่ฟ็งปๆๅ
10.1 ไป Tailwind CSS v3 ่ฟ็งปๅฐ v4
# 1. ๅ็บงไพ่ต
npm install tailwindcss@latest
# 2. ๆดๆฐ้
็ฝฎๆไปถ
# v3: tailwind.config.js
# v4: styles.css (CSS ไผๅ
)
# 3. ่ฟ็งปๆญฅ้ชค
/* v3: tailwind.config.js */
module.exports = {
theme: {
extend: {
colors: {
brand: {
500: '#0ea5e9',
}
}
}
}
}
/* v4: styles.css */
@import "tailwindcss";
@theme {
--color-brand-500: #0ea5e9;
}
10.2 ไป Tailwind CSS ่ฟ็งปๅฐ UnoCSS
่ฟ็งปๆฃๆฅๆธ ๅ๏ผ
- [ ] ๅฎ่ฃ
UnoCSS ไพ่ต
- [ ] ้
็ฝฎ UnoCSS๏ผไฝฟ็จ preset-wind ไฟๆๅ
ผๅฎน๏ผ
- [ ] ่ฟ็งป่ชๅฎไน้
็ฝฎๅฐ uno.config.ts
- [ ] ๆฃๆฅ็ฌฌไธๆนๆไปถๅ
ผๅฎนๆง
- [ ] ๆต่ฏๆๆ็ปไปถ
- [ ] ไผๅๆง่ฝ
่ฏฆ็ปๆญฅ้ชค๏ผ
// 1. ๅฎ่ฃ
ไพ่ต
// npm install -D unocss @unocss/preset-wind
// 2. ้
็ฝฎๅ
ผๅฎนๆจกๅผ
// uno.config.ts
import { defineConfig, presetWind, presetAttributify } from 'unocss'
export default defineConfig({
presets: [
// ไฝฟ็จ Wind ้ข่ฎพไฟๆ 100% ๅ
ผๅฎน
presetWind(),
// ๅฏ้๏ผๅฏ็จ Attributify
presetAttributify(),
],
// 3. ่ฟ็งปไธป้ข้
็ฝฎ
theme: {
// ไป tailwind.config.js ๅคๅถ
colors: {
brand: {
50: '#f0f9ff',
500: '#0ea5e9',
}
},
extend: {
spacing: {
'18': '4.5rem',
}
}
},
// 4. ่ฟ็งป่ชๅฎไน็ฑป
shortcuts: {
// ไป @layer components
'btn-primary': 'bg-brand-500 text-white hover:bg-brand-600',
},
})
// 3. ๆดๆฐๆๅปบ้
็ฝฎ
// vite.config.ts
import UnoCSS from 'unocss/vite'
export default {
plugins: [
UnoCSS(),
]
}
// 4. ๆดๆฐๅ
ฅๅฃๆไปถ
// main.ts
import 'virtual:uno.css' // ๆฟๆขๆ tailwind.css
10.3 ไป UnoCSS ่ฟ็งปๅฐ Tailwind CSS
# ่ฟ็งๆ
ๅต่พๅฐ่ง๏ผ้ๅธธๆฏๅข้่ฆๆฑ็ปไธๆๆฏๆ
# 1. ๅฎ่ฃ
Tailwind
npm install -D tailwindcss postcss autoprefixer
npx tailwindcss init -p
# 2. ้
็ฝฎ Tailwind
# tailwind.config.js
module.exports = {
content: ['./src/**/*.{js,ts,jsx,tsx}'],
theme: {
extend: {
// ไป uno.config.ts ่ฟ็งป
},
},
}
# 3. ๅๅปบ CSS ๆไปถ
# src/styles/tailwind.css
@tailwind base;
@tailwind components;
@tailwind utilities;
# 4. ๆดๆฐๆๆ็ปไปถ
# ๅฐ UnoCSS ๅฟซๆทๆนๅผ่ฝฌๆขไธบ Tailwind ็ฑปๅ
11. ๆชๆฅๅๅฑ่ถๅฟ
11.1 Tailwind CSS ่ทฏ็บฟๅพ
v4.1+ ้ขๆๅ่ฝ๏ผ
- ๆดๅฎๅ็ CSS ๅ็้ ็ฝฎๆฏๆ
- ๆดๅฅฝ็ๅฎนๅจๆฅ่ฏข้ๆ
- ๅขๅผบ็ๅจ็ปๅทฅๅ ท
- ๆน่ฟ็ๆทฑ่ฒๆจกๅผๅๆข
้ฟๆๆนๅ๏ผ
- ไธๅ็ CSS ๆ ๅๆดๆทฑๅบฆ็้ๆ
- ้ถ JavaScript ่ฟ่กๆถไพ่ต
- ๆดๅฅฝ็ๆง่ฝไผๅ
11.2 UnoCSS ่ทฏ็บฟๅพ
่ฟๆๅ่ฝ๏ผ
- ๆดๅคๅฎๆน้ข่ฎพ
- ๆน่ฟ็ VS Code ไฝ้ช
- ๆดๅผบ็็ฑปๅๅฎๅ จ
้ฟๆๆนๅ๏ผ
- ๆไธบๆๅปบๅทฅๅ ท็้ป่ฎค้ๆฉ
- ๆดๅนฟๆณ็ๆกๆถ้ๆ
- ็คพๅบ้ข่ฎพ็ๆๆฉๅผ
11.3 ๆๆฏ่ถๅฟ้ขๆต
2024-2025 ๅนด่ถๅฟ๏ผ
โโโ CSS ๅ็่ฝๅๆๅ
โ โโโ @property ๆดๅนฟๆณๆฏๆ
โ โโโ color-mix() ๆฎๅ
โ โโโ ๅฎนๅจๆฅ่ฏขๆ ๅๅ
โ
โโโ ๆๅปบๅทฅๅ
ทๆผ่ฟ
โ โโโ Rspack/SWC ๆดๆฎๅ
โ โโโ ๆดๅฟซ็ๆๅปบ้ๅบฆ
โ โโโ ๆดๆบ่ฝ็ tree-shaking
โ
โโโ ๅๅญๅ CSS ไธปๆตๅ
โโโ ๆดๅคๆกๆถ้็จ
โโโ ๆ ๅๅๅทฅๅ
ท้พ
โโโ ๆดๅฅฝ็ๅผๅไฝ้ช
12. ๆป็ปไธๅปบ่ฎฎ
12.1 ๅณ็ญ็ฉ้ต
| ้กน็ฎ็นๅพ | ๆจ่้ๆฉ | ็็ฑ |
|---|---|---|
| ไผไธ็บงๅคงๅ้กน็ฎ | Tailwind CSS | ็ๆๅฎๅใๅข้็ๆๅบฆ้ซ |
| ๅๅ/ๅฐๅ้กน็ฎ | UnoCSS | ๅฏๅจๅฟซ้ใ้ ็ฝฎ็ฎๅ |
| ่ฟฝๆฑๆ่ดๆง่ฝ | UnoCSS | ๆๅปบ้ๅบฆๅฟซใ่ฟ่กๆถ้ซๆ |
| ้่ฆไธฐๅฏ UI ็ปไปถ | Tailwind CSS | shadcn/ui ็ญ็ๆๆ็ |
| ้ซๅบฆๅฎๅถๅ้ๆฑ | UnoCSS | ๅจๆ่งๅใ็ตๆดป้ ็ฝฎ |
| ๅข้ๆๆฏๆ ็ฐไปฃ | UnoCSS | ไธ็ฐไปฃๅทฅๅ ท้พ้ๆๅฅฝ |
| ้ฟๆ็ปดๆค่่ | Tailwind CSS | ็จณๅฎๆง้ซใ็คพๅบๆดป่ท |
| ๅฟซ้ๅๅๅผๅ | ไธค่ ็ๅฏ | ้ฝๆฏๆๅฟซ้ๅผๅ |
12.2 ๆททๅไฝฟ็จ็ญ็ฅ
ๆธ่ฟๅผ่ฟ็งปๆนๆก๏ผ
้ถๆฎต 1๏ผๆฐ้กน็ฎ็ดๆฅไฝฟ็จ UnoCSS
้ถๆฎต 2๏ผๆง้กน็ฎ้ๆญฅๅผๅ
ฅ UnoCSS
้ถๆฎต 3๏ผ็ปไธๆๆฏๆ
ๅ
ทไฝๅๆณ๏ผ
1. ไฝฟ็จ preset-wind ไฟๆๅ
ผๅฎน
2. ้ๆญฅ่ฟ็งป็ปไปถ
3. ็ปไธ้
็ฝฎ็ฎก็
12.3 ๆ็ปๅปบ่ฎฎ
้ๆฉ Tailwind CSS 4.0๏ผๅฆๆไฝ ๏ผ
- ้่ฆ็จณๅฎใๆ็็่งฃๅณๆนๆก
- ๅข้ๅฏน Tailwind ๅทฒๆ็ป้ช
- ไพ่ตไธฐๅฏ็ UI ็ปไปถ็ๆ
- ้่ฆ้ฟๆ็้กน็ฎ็ปดๆคๆฏๆ
้ๆฉ UnoCSS๏ผๅฆๆไฝ ๏ผ
- ่ฟฝๆฑๆ่ด็ๅผๅไฝ้ช
- ้่ฆ้ซๅบฆๅฎๅถๅ็้ ็ฝฎ
- ไฝฟ็จ็ฐไปฃๆๅปบๅทฅๅ ท๏ผVite ็ญ๏ผ
- ๆฟๆๅฐ่ฏๆฐๆๆฏ
ๆ ่ฎบ้ๆฉๅชไธช๏ผ้ฝ่ฆ๏ผ
- ๅปบ็ซๅข้่ง่
- ไฝฟ็จ็ฑปๅๅฎๅ จๅทฅๅ ท
- ๅ ณๆณจๆง่ฝไผๅ
- ไฟๆ้ ็ฝฎ็ไธ่ดๆง