Tailwind CSS vs UnoCSS ๆทฑๅบฆๅฏนๆฏ”

Source: Juejin Frontend Hottest

Tailwind CSS vs UnoCSS ๆทฑๅบฆๅฏนๆฏ”

ๅฎŒๆ•ดๆŠ€ๆœฏๆŒ‡ๅ—๏ผšไปŽๆžถๆž„่ฎพ่ฎกๅˆฐ็”Ÿไบงๅฎž่ทต็š„ๅ…จ้ขๅฏนๆฏ”ๅˆ†ๆž

็›ฎๅฝ•

  1. ๆฆ‚่ฟฐ
  2. ๆ ธๅฟƒๆžถๆž„ๆทฑๅบฆๅฏนๆฏ”
  3. ๆ€ง่ƒฝๅŸบๅ‡†ๆต‹่ฏ•
  4. ็”Ÿๆ€็ณป็ปŸๅ…จๆ™ฏๅˆ†ๆž
  5. ๅผ€ๅ‘ไฝ“้ชŒ่ฏฆ่งฃ
  6. ้…็ฝฎ็ณป็ปŸๅฏนๆฏ”
  7. ๅฎžๆˆ˜ๆกˆไพ‹
  8. ๆœ€ไฝณๅฎž่ทต
  9. ๅธธ่ง้—ฎ้ข˜ไธŽ่งฃๅ†ณๆ–นๆกˆ
  10. ่ฟ็งปๆŒ‡ๅ—
  11. ๆœชๆฅๅ‘ๅฑ•่ถ‹ๅŠฟ
  12. ๆ€ป็ป“ไธŽๅปบ่ฎฎ

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 CSSUnoCSS
ๅฎšไฝ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 ๆžถๆž„็‰น็‚น

ไผ˜ๅŠฟ๏ผš

  1. ็กฎๅฎšๆ€ง่พ“ๅ‡บ๏ผšๆฏๆฌกๆž„ๅปบ็”Ÿๆˆไธ€่‡ด็š„ CSS ๆ–‡ไปถ
  2. ้ข„็ผ–่ฏ‘ไผ˜ๅŒ–๏ผšๅฏไปฅๅœจๆž„ๅปบๆ—ถ่ฟ›่กŒๆทฑๅบฆไผ˜ๅŒ–
  3. ็ผ“ๅญ˜ๅ‹ๅฅฝ๏ผš็”Ÿๆˆ็š„ CSS ๆ–‡ไปถๅฏ่ขซ CDN ็ผ“ๅญ˜
  4. ็”Ÿๆ€ๆˆ็†Ÿ๏ผšๅคง้‡ๅทฅๅ…ท้“พๆ”ฏๆŒ้ข„็ผ–่ฏ‘ๆจกๅผ

ๅŠฃๅŠฟ๏ผš

  1. ๆž„ๅปบๅผ€้”€๏ผš้œ€่ฆๆ‰ซๆๆ–‡ไปถๅนถ็”ŸๆˆๅฎŒๆ•ด CSS
  2. ้…็ฝฎๅฑ€้™๏ผšๅŠจๆ€ๅ€ผ้œ€่ฆ็‰นๆฎŠ่ฏญๆณ•ๆ”ฏๆŒ
  3. ๅŒ…ไฝ“็งฏ๏ผšๅณไฝฟๅชไฝฟ็”จๅฐ‘้‡็ฑป๏ผŒไนŸๅฏ่ƒฝๆœ‰่พƒๅคง้…็ฝฎๆ–‡ไปถ
// ๅฎž้™…ๆž„ๅปบๆ—ถ้—ดๅˆ†ๆž๏ผˆ1000 ็ป„ไปถ้กน็›ฎ๏ผ‰
const buildMetrics = {
  initialBuild: '2.5s',     // ้ฆ–ๆฌกๆž„ๅปบ
  incrementalBuild: '150ms', // ๅขž้‡ๆž„ๅปบ
  cssOutput: '45KB',        // ่พ“ๅ‡บๅคงๅฐ๏ผˆgzip๏ผ‰
  configParsing: '80ms'     // ้…็ฝฎ่งฃๆž
}
UnoCSS ๆžถๆž„็‰น็‚น

ไผ˜ๅŠฟ๏ผš

  1. ๅณๆ—ถๅ“ๅบ”๏ผšๅผ€ๅ‘ๆœๅŠกๅ™จๅฏๅŠจๅ‡ ไนŽ็žฌ้—ดๅฎŒๆˆ
  2. ๆŒ‰้œ€็”Ÿๆˆ๏ผšๅช็”Ÿๆˆๅฎž้™…ไฝฟ็”จ็š„ CSS
  3. ๅ†…ๅญ˜ๆ•ˆ็އ๏ผšๆ— ้œ€ๆŒไน…ๅŒ– CSS ๆ–‡ไปถ
  4. ๅŠจๆ€่ง„ๅˆ™๏ผšๆญฃๅˆ™่กจ่พพๅผ่ง„ๅˆ™ๆ”ฏๆŒๆ— ้™ๆ‰ฉๅฑ•

ๅŠฃๅŠฟ๏ผš

  1. ่ฟ่กŒๆ—ถไพ่ต–๏ผš้œ€่ฆๅผ€ๅ‘ๆœๅŠกๅ™จๆ”ฏๆŒ
  2. ๆž„ๅปบๅคๆ‚ๅบฆ๏ผšไธๅŒๆž„ๅปบๅทฅๅ…ท้œ€่ฆไธๅŒ้…็ฝฎ
  3. ่ฐƒ่ฏ•้šพๅบฆ๏ผšๅŠจๆ€็”Ÿๆˆ็š„ 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 CSSUnoCSSๆๅ‡ๅ€ๆ•ฐ
ๅ†ทๅฏๅŠจ1850ms210ms8.8x
็ƒญๅฏๅŠจ450ms50ms9.0x
้…็ฝฎ้‡่ฝฝ320ms30ms10.7x
ๅ†…ๅญ˜ๅ ็”จ156MB23MB6.8x
HMR๏ผˆ็ƒญๆ›ดๆ–ฐ๏ผ‰ๆ€ง่ƒฝ
ๆต‹่ฏ•ๅœบๆ™ฏ๏ผšไฟฎๆ”นๅ•ไธช็ป„ไปถๆ–‡ไปถ

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚                 HMR ๅ“ๅบ”ๆ—ถ้—ด๏ผˆๆฏซ็ง’๏ผ‰                โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚                                                    โ”‚
โ”‚  Tailwind CSS                                      โ”‚
โ”‚  โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆ  145ms  โ”‚
โ”‚                                                    โ”‚
โ”‚  UnoCSS                                            โ”‚
โ”‚  โ–ˆโ–ˆโ–ˆโ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘   12ms  โ”‚
โ”‚                                                    โ”‚
โ”‚  ๆ€ง่ƒฝๆๅ‡๏ผš12.1x                                   โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

ไธๅŒๅœบๆ™ฏ็š„ HMR ๆ€ง่ƒฝ๏ผš

ไฟฎๆ”น็ฑปๅž‹Tailwind CSSUnoCSSๅทฎๅผ‚ๅˆ†ๆž
ไฟฎๆ”น็ฑปๅ145ms12msUnoCSS ๅณๆ—ถๅ“ๅบ”
ๆทปๅŠ ็ฑปๅ160ms8msๆ— ้œ€้‡ๆ–ฐๆ‰ซๆ
ๅˆ ้™ค็ฑปๅ140ms15msๆธ…็†้€Ÿๅบฆๅฟซ
ไฟฎๆ”นๅ†…ๅฎน120ms180ms**ๅŒ…ๅซ้กต้ข้‡ๆธฒๆŸ“
้…็ฝฎๆ–‡ไปถ350ms35msUnoCSS ่ง„ๅˆ™็ƒญ้‡่ฝฝ

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 CSSUnoCSS่Š‚็œ
ๅŽŸๅง‹45.2 KB28.6 KB36.7%
Gzip8.4 KB5.2 KB38.1%
Brotli6.8 KB4.1 KB39.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                                    โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

ๆ€ง่ƒฝๅฝฑๅ“ๅ› ็ด ๏ผš

  1. CSS ้€‰ๆ‹ฉๅ™จๅคๆ‚ๅบฆ

    • Tailwind CSS: ๅคง้‡ๅ•ไธ€็ฑป้€‰ๆ‹ฉๅ™จ
    • UnoCSS: ็ฑปไผผ็ป“ๆž„๏ผŒไฝ†ๆ•ฐ้‡ๆ›ดๅฐ‘
  2. CSS ๅ˜้‡ไฝฟ็”จ

    • Tailwind CSS: ้‡ๅบฆไฝฟ็”จ CSS ๅ˜้‡๏ผˆ--tw-*๏ผ‰
    • UnoCSS: ๅฏ้€‰๏ผŒ้ป˜่ฎค่พƒๅฐ‘ไฝฟ็”จ
  3. ็‰นๅผ‚ๆ€ง๏ผˆ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 CSSUnoCSS่ƒœๅ‡บ
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+

ๅฎž้™…ไฝฟ็”จไฝ“้ชŒๅฏนๆฏ”๏ผš

ๅŠŸ่ƒฝTailwindUnoCSSๅทฎๅผ‚่ฏดๆ˜Ž
่กฅๅ…จ้€Ÿๅบฆ~50ms~30msUnoCSS ๆ›ดๅฟซ
่กฅๅ…จ็ฒพๅบฆๆž้ซ˜้ซ˜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 Iconsi-mdi-*7000+
Phosphor Iconsi-ph-*7000+
Heroiconsi-heroicons-*300+
Lucidei-lucide-*800+
Tabler Iconsi-tabler-*4000+
Carbon Iconsi-carbon-*2000+
Simple Iconsi-simple-icons-*2500+
Flag Iconsi-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.jsv4 ๆ›ด็›ด่ง‚
ไธป้ข˜็ปงๆ‰ฟ่‡ชๅŠจ็ปงๆ‰ฟ้ป˜่ฎคไธป้ข˜้œ€ๆ‰‹ๅŠจ extendv4 ๆ›ดๆ™บ่ƒฝ
ๅ˜้‡็ฑปๅž‹CSS ่‡ชๅฎšไน‰ๅฑžๆ€งJS ๅฏน่ฑกv4 ๅŽŸ็”Ÿๆ”ฏๆŒ
่ฟ่กŒๆ—ถไฟฎๆ”นๆ”ฏๆŒไธๆ”ฏๆŒv4 ๅฏๅŠจๆ€่ฐƒๆ•ด
ๆž„ๅปบๅทฅๅ…ทๆ›ด่ฝป้‡้œ€่ฆ PostCSSv4 ๆ›ดๅฟซ้€Ÿ

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 v4UnoCSS
ๅฎšไน‰ไฝ็ฝฎ@layer componentsshortcuts ้…็ฝฎ
ๅ‚ๆ•ฐๆ”ฏๆŒๆœ‰้™๏ผˆ@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 CSSshadcn/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 ็ญ‰๏ผ‰
  • ๆ„ฟๆ„ๅฐ่ฏ•ๆ–ฐๆŠ€ๆœฏ

ๆ— ่ฎบ้€‰ๆ‹ฉๅ“ชไธช๏ผŒ้ƒฝ่ฆ๏ผš

  • ๅปบ็ซ‹ๅ›ข้˜Ÿ่ง„่Œƒ
  • ไฝฟ็”จ็ฑปๅž‹ๅฎ‰ๅ…จๅทฅๅ…ท
  • ๅ…ณๆณจๆ€ง่ƒฝไผ˜ๅŒ–
  • ไฟๆŒ้…็ฝฎ็š„ไธ€่‡ดๆ€ง