chore: Tailwind CSS 3.4 迁移到 4.x(CSS-first 配置 + @tailwindcss/vite)

- 升级 tailwindcss@4.3 + @tailwindcss/vite,移除 postcss/autoprefixer/tailwindcss-animate
- tailwindcss-animate 替换为 tw-animate-css(v4 兼容的 animate-in/out 工具类)
- global.css 改用 @import "tailwindcss" + @theme inline 映射 shadcn 设计令牌
- @custom-variant dark 替代 darkMode:'class';@source 显式纳入 pages/ 目录
- 删除 tailwind.config.ts / postcss.config.js,components.json 同步 v4 约定
- vite.config.ts 接入 tailwindcss 插件,__dirname 改 import.meta.dirname
This commit is contained in:
2026-09-09 02:54:56 +08:00
parent 230006a905
commit 3702e3d1f2
7 changed files with 632 additions and 786 deletions

View File

@@ -4,7 +4,7 @@
"rsc": false, "rsc": false,
"tsx": true, "tsx": true,
"tailwind": { "tailwind": {
"config": "tailwind.config.ts", "config": "",
"css": "src/styles/global.css", "css": "src/styles/global.css",
"baseColor": "neutral", "baseColor": "neutral",
"cssVariables": true, "cssVariables": true,

File diff suppressed because it is too large Load Diff

View File

@@ -22,6 +22,7 @@
"@radix-ui/react-switch": "^1.3.0", "@radix-ui/react-switch": "^1.3.0",
"@svg-maps/china": "^2.0.0", "@svg-maps/china": "^2.0.0",
"@svg-maps/world": "^2.0.0", "@svg-maps/world": "^2.0.0",
"@tailwindcss/vite": "^4.3.3",
"@tanstack/react-virtual": "^3.11.2", "@tanstack/react-virtual": "^3.11.2",
"@tiptap/core": "^3.26.1", "@tiptap/core": "^3.26.1",
"@tiptap/extension-code-block": "^3.26.1", "@tiptap/extension-code-block": "^3.26.1",
@@ -33,7 +34,6 @@
"@tiptap/pm": "^3.26.1", "@tiptap/pm": "^3.26.1",
"@tiptap/react": "^3.26.1", "@tiptap/react": "^3.26.1",
"@tiptap/starter-kit": "^3.26.1", "@tiptap/starter-kit": "^3.26.1",
"autoprefixer": "^10.5.0",
"class-variance-authority": "^0.7.1", "class-variance-authority": "^0.7.1",
"clsx": "^2.1.1", "clsx": "^2.1.1",
"compression": "^1.8.1", "compression": "^1.8.1",
@@ -46,7 +46,6 @@
"linkedom": "^0.18.13", "linkedom": "^0.18.13",
"lucide-react": "^1.18.0", "lucide-react": "^1.18.0",
"marked": "^18.0.5", "marked": "^18.0.5",
"postcss": "^8.5.15",
"react": "^19.2.8", "react": "^19.2.8",
"react-dom": "^19.2.8", "react-dom": "^19.2.8",
"react-easy-crop": "^6.0.2", "react-easy-crop": "^6.0.2",
@@ -55,9 +54,9 @@
"react-streaming": "^0.4.20", "react-streaming": "^0.4.20",
"sonner": "^2.0.7", "sonner": "^2.0.7",
"tailwind-merge": "^3.6.0", "tailwind-merge": "^3.6.0",
"tailwindcss": "^3.4.19", "tailwindcss": "^4.3.3",
"tailwindcss-animate": "^1.0.7",
"turndown": "^7.2.4", "turndown": "^7.2.4",
"tw-animate-css": "^1.4.0",
"vike": "^0.4.266", "vike": "^0.4.266",
"zod": "^4.4.3", "zod": "^4.4.3",
"zustand": "^5.0.15" "zustand": "^5.0.15"

View File

@@ -1,6 +0,0 @@
export default {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
};

View File

@@ -1,6 +1,40 @@
@tailwind base; @import "tailwindcss";
@tailwind components; @import "tw-animate-css";
@tailwind utilities;
/* Tailwind 4:class 暗色模式(.dark 挂在 <html> 上) */
@custom-variant dark (&:is(.dark *));
/* Vike 页面位于 frontend/pages,显式纳入候选类扫描 */
@source "../../pages/**/*.{ts,tsx}";
/* shadcn 设计令牌:CSS 变量在 :root/.dark 间切换,@theme inline 让工具类运行时引用变量 */
@theme inline {
--color-border: hsl(var(--border));
--color-input: hsl(var(--input));
--color-ring: hsl(var(--ring));
--color-background: hsl(var(--background));
--color-foreground: hsl(var(--foreground));
--color-primary: hsl(var(--primary));
--color-primary-foreground: hsl(var(--primary-foreground));
--color-secondary: hsl(var(--secondary));
--color-secondary-foreground: hsl(var(--secondary-foreground));
--color-destructive: hsl(var(--destructive));
--color-destructive-foreground: hsl(var(--destructive-foreground));
--color-muted: hsl(var(--muted));
--color-muted-foreground: hsl(var(--muted-foreground));
--color-accent: hsl(var(--accent));
--color-accent-foreground: hsl(var(--accent-foreground));
--color-popover: hsl(var(--popover));
--color-popover-foreground: hsl(var(--popover-foreground));
--color-card: hsl(var(--card));
--color-card-foreground: hsl(var(--card-foreground));
--radius-lg: var(--radius);
--radius-md: calc(var(--radius) - 2px);
--radius-sm: calc(var(--radius) - 4px);
--font-sans: var(--j13-font-family);
}
/* 文档入口壳层(原 index.html 内联 style,改为外链 CSS,对齐 Gitea 式 link stylesheet) */ /* 文档入口壳层(原 index.html 内联 style,改为外链 CSS,对齐 Gitea 式 link stylesheet) */
html { scrollbar-gutter: stable; } html { scrollbar-gutter: stable; }

View File

@@ -1,55 +0,0 @@
import type { Config } from 'tailwindcss';
import animate from 'tailwindcss-animate';
export default {
darkMode: ['class'],
content: ['./index.html', './src/**/*.{ts,tsx}'],
theme: {
extend: {
colors: {
border: 'hsl(var(--border))',
input: 'hsl(var(--input))',
ring: 'hsl(var(--ring))',
background: 'hsl(var(--background))',
foreground: 'hsl(var(--foreground))',
primary: {
DEFAULT: 'hsl(var(--primary))',
foreground: 'hsl(var(--primary-foreground))',
},
secondary: {
DEFAULT: 'hsl(var(--secondary))',
foreground: 'hsl(var(--secondary-foreground))',
},
destructive: {
DEFAULT: 'hsl(var(--destructive))',
foreground: 'hsl(var(--destructive-foreground))',
},
muted: {
DEFAULT: 'hsl(var(--muted))',
foreground: 'hsl(var(--muted-foreground))',
},
accent: {
DEFAULT: 'hsl(var(--accent))',
foreground: 'hsl(var(--accent-foreground))',
},
popover: {
DEFAULT: 'hsl(var(--popover))',
foreground: 'hsl(var(--popover-foreground))',
},
card: {
DEFAULT: 'hsl(var(--card))',
foreground: 'hsl(var(--card-foreground))',
},
},
borderRadius: {
lg: 'var(--radius)',
md: 'calc(var(--radius) - 2px)',
sm: 'calc(var(--radius) - 4px)',
},
fontFamily: {
sans: ['var(--j13-font-family)'],
},
},
},
plugins: [animate],
} satisfies Config;

View File

@@ -1,17 +1,18 @@
import path from 'path'; import path from 'node:path';
import { defineConfig } from 'vite'; import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react'; import react from '@vitejs/plugin-react';
import vike from 'vike/plugin'; import vike from 'vike/plugin';
import tailwindcss from '@tailwindcss/vite';
// 与 Go 后端默认端口一致,可通过 VITE_API_PORT 覆盖 // 与 Go 后端默认端口一致,可通过 VITE_API_PORT 覆盖
const apiPort = process.env.VITE_API_PORT || '3000'; const apiPort = process.env.VITE_API_PORT || '3000';
const apiTarget = `http://localhost:${apiPort}`; const apiTarget = `http://localhost:${apiPort}`;
export default defineConfig({ export default defineConfig({
plugins: [react(), vike()], plugins: [react(), vike(), tailwindcss()],
resolve: { resolve: {
alias: { alias: {
'@': path.resolve(__dirname, './src'), '@': path.resolve(import.meta.dirname, './src'),
}, },
}, },
base: '/', base: '/',