Loading...
Loading...
Compare original and translation side by side
undefinedundefined
**vite.config.ts**:
```typescript
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import tailwindcss from '@tailwindcss/vite'
import path from 'path'
export default defineConfig({
plugins: [react(), tailwindcss()],
resolve: { alias: { '@': path.resolve(__dirname, './src') } }
}){
"tailwind": {
"config": "", // ← Empty for v4
"css": "src/index.css",
"baseColor": "slate",
"cssVariables": true
}
}
**vite.config.ts**:
```typescript
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import tailwindcss from '@tailwindcss/vite'
import path from 'path'
export default defineConfig({
plugins: [react(), tailwindcss()],
resolve: { alias: { '@': path.resolve(__dirname, './src') } }
}){
"tailwind": {
"config": "", // ← v4 此处留空
"css": "src/index.css",
"baseColor": "slate",
"cssVariables": true
}
}/* src/index.css */
@import "tailwindcss";
@import "tw-animate-css"; /* Required for shadcn/ui animations */
:root {
--background: hsl(0 0% 100%); /* ← hsl() wrapper required */
--foreground: hsl(222.2 84% 4.9%);
--primary: hsl(221.2 83.2% 53.3%);
/* ... all light mode colors */
}
.dark {
--background: hsl(222.2 84% 4.9%);
--foreground: hsl(210 40% 98%);
--primary: hsl(217.2 91.2% 59.8%);
/* ... all dark mode colors */
}@layer basehsl()/* src/index.css */
@import "tailwindcss";
@import "tw-animate-css"; /* shadcn/ui 动画必需 */
:root {
--background: hsl(0 0% 100%); /* ← 必须使用hsl()包裹 */
--foreground: hsl(222.2 84% 4.9%);
--primary: hsl(221.2 83.2% 53.3%);
/* ... 所有浅色模式颜色 */
}
.dark {
--background: hsl(222.2 84% 4.9%);
--foreground: hsl(210 40% 98%);
--primary: hsl(217.2 91.2% 59.8%);
/* ... 所有深色模式颜色 */
}@layer basehsl()@theme inline {
--color-background: var(--background);
--color-foreground: var(--foreground);
--color-primary: var(--primary);
/* ... map ALL CSS variables */
}bg-backgroundtext-primary@theme inline {
--color-background: var(--background);
--color-foreground: var(--foreground);
--color-primary: var(--primary);
/* ... 映射所有CSS变量 */
}bg-backgroundtext-primary@layer base {
body {
background-color: var(--background); /* NO hsl() wrapper here */
color: var(--foreground);
}
}hsl(var(--background))@layer base {
body {
background-color: var(--background); /* 此处不要使用hsl()包裹 */
color: var(--foreground);
}
}hsl(var(--background))<div className="bg-background text-foreground">
{/* No dark: variants needed - theme switches automatically */}
</div><div className="bg-background text-foreground">
{/* 无需使用dark:前缀 - 主题会自动切换 */}
</div>templates/theme-provider.tsx// src/main.tsx
import { ThemeProvider } from '@/components/theme-provider'
ReactDOM.createRoot(document.getElementById('root')!).render(
<ThemeProvider defaultTheme="dark" storageKey="vite-ui-theme">
<App />
</ThemeProvider>
)pnpm dlx shadcn@latest add dropdown-menureference/dark-mode.mdtemplates/theme-provider.tsx// src/main.tsx
import { ThemeProvider } from '@/components/theme-provider'
ReactDOM.createRoot(document.getElementById('root')!).render(
<ThemeProvider defaultTheme="dark" storageKey="vite-ui-theme">
<App />
</ThemeProvider>
)pnpm dlx shadcn@latest add dropdown-menureference/dark-mode.mdhsl():root.dark--bg: hsl(0 0% 100%);@theme inline"tailwind.config": ""tailwind.config.ts@tailwindcss/vite:root.darkhsl()--bg: hsl(0 0% 100%);@theme inline"tailwind.config": ""tailwind.config.ts@tailwindcss/vite:root.dark@layer base.dark { @theme { } }hsl(var(--background))tailwind.config.ts@applydark:@apply@layer base@layer components@utility@layer base:root.dark@layer base.dark { @theme { } }hsl(var(--background))tailwind.config.ts@applydark:@layer base@layer components@apply@utility@layer basetailwindcss-animateundefinedtailwindcss-animateundefined
---
---bg-primary@theme inline@theme inline {
--color-background: var(--background);
--color-foreground: var(--foreground);
--color-primary: var(--primary);
/* ... map ALL CSS variables */
}bg-primary@theme inline@theme inline {
--color-background: var(--background);
--color-foreground: var(--foreground);
--color-primary: var(--primary);
/* ... 映射所有CSS变量 */
}templates/theme-provider.tsxmain.tsx.dark<html>templates/theme-provider.tsx<html>.dark@layer base/* ✅ Correct - single @layer base */
@import "tailwindcss";
:root { --background: hsl(0 0% 100%); }
@theme inline { --color-background: var(--background); }
@layer base { body { background-color: var(--background); } }@layer base/* ✅ 正确写法 - 单个@layer base */
@import "tailwindcss";
:root { --background: hsl(0 0% 100%); }
@theme inline { --color-background: var(--background); }
@layer base { body { background-color: var(--background); } }tailwind.config.tsrm tailwind.config.tssrc/index.css@themetailwind.config.tsrm tailwind.config.tssrc/index.css@theme@theme inlinedata-mode="dark"@theme inline@theme inlinebg-primarybackground-color: oklch(...)@theme/* ✅ CORRECT - Use @theme without inline */
@custom-variant dark (&:where([data-mode=dark], [data-mode=dark] *));
@theme {
--color-text-primary: var(--color-slate-900);
--color-bg-primary: var(--color-white);
}
@layer theme {
[data-mode="dark"] {
--color-text-primary: var(--color-white);
--color-bg-primary: var(--color-slate-900);
}
}"It's more idiomatic in v4 for the actual generated CSS to reference your theme variables. I would personally only use inline when things don't work without it."
@theme inline@theme inlinebg-primarybackground-color: oklch(...)@theme/* ✅ 正确写法 - 使用不带inline的@theme */
@custom-variant dark (&:where([data-mode=dark], [data-mode=dark] *));
@theme {
--color-text-primary: var(--color-slate-900);
--color-bg-primary: var(--color-white);
}
@layer theme {
[data-mode="dark"] {
--color-text-primary: var(--color-white);
--color-bg-primary: var(--color-slate-900);
}
}"在v4中,更符合规范的做法是让生成的CSS引用主题变量。我个人只在不使用inline就无法工作的情况下才会使用它。"
Cannot apply unknown utility class: custom-button@layer base@layer components@apply@layer@utility@apply/* ❌ v3 pattern (worked) */
@layer components {
.custom-button {
@apply px-4 py-2 bg-blue-500;
}
}
/* ✅ v4 pattern (required) */
@utility custom-button {
@apply px-4 py-2 bg-blue-500;
}
/* OR use native CSS */
@layer base {
.custom-button {
padding: 1rem 0.5rem;
background-color: theme(colors.blue.500);
}
}@applyCannot apply unknown utility class: custom-button@layer base@layer components@apply@layer@utility@apply/* ❌ v3写法(曾生效) */
@layer components {
.custom-button {
@apply px-4 py-2 bg-blue-500;
}
}
/* ✅ v4写法(必需) */
@utility custom-button {
@apply px-4 py-2 bg-blue-500;
}
/* 或使用原生CSS */
@layer base {
.custom-button {
padding: 1rem 0.5rem;
background-color: theme(colors.blue.500);
}
}@apply@layer base@layer base/components/utilities@import "tailwindcss/theme.css" layer(theme);
@import "tailwindcss/base.css" layer(base);
@import "tailwindcss/components.css" layer(components);
@import "tailwindcss/utilities.css" layer(utilities);
@layer base {
body {
background-color: var(--background);
}
}@layer base@import "tailwindcss";
:root {
--background: hsl(0 0% 100%);
}
body {
background-color: var(--background); /* No @layer needed */
}@layer base@layer base/components/utilities@import "tailwindcss/theme.css" layer(theme);
@import "tailwindcss/base.css" layer(base);
@import "tailwindcss/components.css" layer(components);
@import "tailwindcss/utilities.css" layer(utilities);
@layer base {
body {
background-color: var(--background);
}
}@layer base@import "tailwindcss";
:root {
--background: hsl(0 0% 100%);
}
body {
background-color: var(--background); /* 不需要@layer */
}@layer base| Symptom | Cause | Fix |
|---|---|---|
| Missing | Add |
| Colors all black/white | Double | Use |
| Dark mode not switching | Missing ThemeProvider | Wrap app in |
| Build fails | | Delete file |
| Animation errors | Using | Install |
| 症状 | 原因 | 解决方法 |
|---|---|---|
| 缺少 | 添加 |
| 颜色全为黑/白 | 双重 | 使用 |
| 深色模式无法切换 | 缺少ThemeProvider | 使用 |
| 构建失败 | 存在 | 删除该文件 |
| 动画错误 | 使用了 | 安装 |
.bg-blue-500 {
background-color: #3b82f6; /* sRGB fallback */
background-color: oklch(0.6 0.24 264); /* Modern browsers */
}@theme {
/* Modern approach (preferred) */
--color-brand: oklch(0.7 0.15 250);
/* Legacy approach (still works) */
--color-brand: hsl(240 80% 60%);
}.bg-blue-500 {
background-color: #3b82f6; /* sRGB降级方案 */
background-color: oklch(0.6 0.24 264); /* 现代浏览器 */
}@theme {
/* 现代写法(推荐) */
--color-brand: oklch(0.7 0.15 250);
/* 遗留写法(仍可使用) */
--color-brand: hsl(240 80% 60%);
}<div className="@container">
<div className="@md:text-lg @lg:grid-cols-2">
Content responds to container width, not viewport
</div>
</div><p className="line-clamp-3">Truncate to 3 lines with ellipsis...</p>
<p className="line-clamp-[8]">Arbitrary values supported</p>
<p className="line-clamp-(--teaser-lines)">CSS variable support</p>@tailwindcss/container-queries@tailwindcss/line-clamp<div className="@container">
<div className="@md:text-lg @lg:grid-cols-2">
内容响应容器宽度,而非视口宽度
</div>
</div><p className="line-clamp-3">截断为3行并显示省略号...</p>
<p className="line-clamp-[8]">支持任意值</p>
<p className="line-clamp-(--teaser-lines)">支持CSS变量</p>@tailwindcss/container-queries@tailwindcss/line-clamp@pluginrequire()@importpnpm add -D @tailwindcss/typography@import "tailwindcss";
@plugin "@tailwindcss/typography";<article class="prose dark:prose-invert">{{ content }}</article>pnpm add -D @tailwindcss/forms@import "tailwindcss";
@plugin "@tailwindcss/forms";<div className="@container">
<div className="@md:text-lg">Responds to container width</div>
</div>/* ❌ WRONG - v3 syntax */
@import "@tailwindcss/typography";
/* ✅ CORRECT - v4 syntax */
@plugin "@tailwindcss/typography";@pluginrequire()@importpnpm add -D @tailwindcss/typography@import "tailwindcss";
@plugin "@tailwindcss/typography";<article class="prose dark:prose-invert">{{ content }}</article>pnpm add -D @tailwindcss/forms@import "tailwindcss";
@plugin "@tailwindcss/forms";<div className="@container">
<div className="@md:text-lg">响应容器宽度</div>
</div>/* ❌ 错误 - v3语法 */
@import "@tailwindcss/typography";
/* ✅ 正确 - v4语法 */
@plugin "@tailwindcss/typography";@tailwindcss/vitevite.config.tstailwindcss()components.json"config": ""tailwind.config.tssrc/index.css:root.darkhsl()@theme inline@layer base@tailwindcss/vitevite.config.tstailwindcss()components.json"config": ""tailwind.config.tssrc/index.css:root.darkhsl()@theme inline@layer basetemplates/cn()templates/cn()reference/migration-guide.mdtailwind.config.ts@theme inline@tailwindcss/line-clampline-clamp-*tailwindcss-animatetw-animate-cssrequire()@pluginreference/migration-guide.mdtailwind.config.ts@theme inline@tailwindcss/line-clampline-clamp-*tw-animate-csstailwindcss-animaterequire()@plugin@tailwindcss/upgrade<h1><h6>pnpm add -D @tailwindcss/typography@import "tailwindcss";
@plugin "@tailwindcss/typography";<article className="prose dark:prose-invert">
{/* All elements styled automatically */}
</article>@layer base {
h1 { @apply text-4xl font-bold mb-4; }
h2 { @apply text-3xl font-bold mb-3; }
h3 { @apply text-2xl font-bold mb-2; }
ul { @apply list-disc pl-6 mb-4; }
ol { @apply list-decimal pl-6 mb-4; }
}<h1><h6>pnpm add -D @tailwindcss/typography@import "tailwindcss";
@plugin "@tailwindcss/typography";<article className="prose dark:prose-invert">
{/* 所有元素自动应用样式 */}
</article>@layer base {
h1 { @apply text-4xl font-bold mb-4; }
h2 { @apply text-3xl font-bold mb-3; }
h3 { @apply text-2xl font-bold mb-2; }
ul { @apply list-disc pl-6 mb-4; }
ol { @apply list-decimal pl-6 mb-4; }
}@tailwindcss/vite// ✅ Vite Plugin - One line, no PostCSS config
import tailwindcss from '@tailwindcss/vite'
export default defineConfig({
plugins: [react(), tailwindcss()],
})
// ❌ PostCSS - Multiple steps, plugin compatibility issues
// 1. Install @tailwindcss/postcss
// 2. Configure postcss.config.js
// 3. Manage plugin order
// 4. Debug plugin conflictspostcss-importpostcss-advanced-variablestailwindcss/nesting@tailwindcss/postcss// ✅ Vite插件 - 一行代码,无需PostCSS配置
import tailwindcss from '@tailwindcss/vite'
export default defineConfig({
plugins: [react(), tailwindcss()],
})
// ❌ PostCSS - 多步骤,插件兼容性问题
// 1. 安装@tailwindcss/postcss
// 2. 配置postcss.config.js
// 3. 管理插件顺序
// 4. 调试插件冲突postcss-importpostcss-advanced-variablestailwindcss/nesting@tailwindcss/postcssringring-3// v3: 3px ring
<button className="ring">Button</button>
// v4: 1px ring (thinner)
<button className="ring">Button</button>
// Match v3 appearance
<button className="ring-3">Button</button>ringring-3// v3: 3px环
<button className="ring">按钮</button>
// v4: 1px环(更细)
<button className="ring">按钮</button>
// 匹配v3外观
<button className="ring-3">按钮</button>