---
title: Tailwind CSS v4 与 Vite：编译工具类，运行时换主题
description: 核对 ChatCrystal 的双包安装、Vite 插件与 CSS 变量主题，区分构建时类名生成和运行时变量注入。
image: "https://zengliangyi.com/og/blog/tailwind-v4-vite-setup.png"
---

ChatCrystal 使用 Tailwind CSS v4 的 Vite 插件编译样式，再由 React ThemeProvider 注入主题变量。构建阶段生成类名对应的规则，运行阶段改变变量值；这两条路径一起决定页面外观。

## 安装与入口必须同时齐全

固定 [client/package.json](https://github.com/ZengLiangYi/ChatCrystal/blob/764276814e5c5a33ecbfeae63754f74b9261a3a8/client/package.json) 同时声明 tailwindcss 与 @tailwindcss/vite，两者版本范围均为 ^4.3.3。不能把安装缩成只有插件包。v4 仍有 PostCSS 集成方式；Vite 插件是当前项目采用的路线，不是 Tailwind 已经移除 PostCSS 支持。[官方 Vite 安装指南](https://tailwindcss.com/docs/installation/using-vite) 也列出了两个包。

以下是空目录中的独立 CSS 练习，不是替换项目现有配置。使用 Node 24 以上：



```bash
npm   init   -y  
  npm   install   -D   vite@8.3.0   tailwindcss@4.3.3   @tailwindcss/vite@4.3.3  
  npx   vite   build
```



最后一条命令应在保存下面三个文件之后运行。



```javascript
// vite.config.mjs  
  import   { defineConfig }   from   'vite'  ;  
  import   tailwindcss   from   '@tailwindcss/vite'  ;  
  
  export   default   defineConfig  ({ plugins: [  tailwindcss  ()] });
```





```html
<!-- index.html -->  
  <!  doctype   html  >  
  <  html   lang  =  "en"  >  
    <  head  >  
      <  meta   charset  =  "UTF-8"   />  
      <  meta   name  =  "viewport"   content  =  "width=device-width, initial-scale=1"   />  
      <  title  >Token exercise</  title  >  
      <  link   rel  =  "stylesheet"   href  =  "/style.css"   />  
    </  head  >  
    <  body  >  
      <  main   class  =  "grid grid-cols-1 gap-4 p-4 sm:grid-cols-2"  >  
        <  div   class  =  "bg-panel text-ink rounded-lg p-4"  >Build classes</  div  >  
        <  div   class  =  "bg-panel text-ink rounded-lg p-4"  >Change variables</  div  >  
      </  main  >  
    </  body  >  
  </  html  >
```





```css
/* style.css */  
  @import   'tailwindcss'  ;  
  
  :root   {  
    --panel  : Canvas;  
    --ink  : CanvasText;  
  }  
  
  @utility   bg-panel {  
    background-color  : var(--panel);  
  }  
  
  @utility   text-ink {  
    color: var(--ink);  
  }
```



构建完成后用 `npx vite preview --host 127.0.0.1` 打开页面，核对单列与 sm 断点后的双列。再在开发者工具改变 `--panel` 与 `--ink`，观察相同工具类是否跟随变量变化。故意删除插件并重新构建，检查产物与页面中工具类是否还生效；恢复插件后再检查。仅命令退出成功，不足以证明 Tailwind 指令经过编译。

## 真实项目比练习多了什么

[vite.config.ts](https://github.com/ZengLiangYi/ChatCrystal/blob/764276814e5c5a33ecbfeae63754f74b9261a3a8/client/vite.config.ts) 并列注册 react() 与 tailwindcss()，用 import.meta.dirname 设置 @ 别名，还配置主入口与 Electron onboarding 两个构建入口。开发服务绑定 127.0.0.1:13721，/api 代理到 localhost:3721。这是开发代理，不能当作生产部署或云端认证配置。

[index.css](https://github.com/ZengLiangYi/ChatCrystal/blob/764276814e5c5a33ecbfeae63754f74b9261a3a8/client/src/index.css) 除了导入 Tailwind，还导入 tw-animate-css，通过 @theme inline 桥接颜色与圆角变量，并定义应用工具类。@theme 与 @utility 的用途可以对照 [指令文档](https://tailwindcss.com/docs/functions-and-directives)。不要因为用了 CSS-first 配置，就删除项目仍依赖的动画包或兼容变量。

## 主题变量如何到达组件

[ThemeProvider](https://github.com/ZengLiangYi/ChatCrystal/blob/764276814e5c5a33ecbfeae63754f74b9261a3a8/client/src/providers/ThemeProvider.tsx) 注册 dark-workshop、dawn-haze 与 jade-abyss，默认 dawn-haze。它从版本化 localStorage 恢复选择，在 effect 中逐项设置根元素变量，再按所选背景色的亮度近似判断 color-scheme。该路径不是根据系统 prefers-color-scheme 自动挑主题。

[themeToCSSVars](https://github.com/ZengLiangYi/ChatCrystal/blob/764276814e5c5a33ecbfeae63754f74b9261a3a8/client/src/themes/theme.types.ts) 将 bgPrimary 转成 `--bg-primary`，也将 graphProject1 转成 `--graph-project-1`，然后添加字体与圆角。ThemeDefinition 虽声明 density，这个转换函数并没有输出 density 变量；不能把接口里有字段当作页面已实现效果。

## 类名必须让编译器看得到

Tailwind 扫描源文本中的完整类名，不执行 JavaScript 来推导拼接结果，见 [类名检测规则](https://tailwindcss.com/docs/detecting-classes-in-source-files)。用完整的静态备选类名表达状态；真正动态的进度等数值放进 style 或 CSS 变量。调试时分别检查类名是否出现在构建 CSS、变量是否赋值、最终 computed style 与对比度是否符合预期。

布局工具类不会自动修复所有窄屏溢出，主题变量也不会自动保证对比度或减少动画。应实际检查键盘焦点、手机宽度、明暗主题与 prefers-reduced-motion 下的行为。涉及桌面宿主时，还应分清页面与主进程，参见 [Electron 入门](/blog/electron-app-basics)。

分享：

##  相关文章

[##  AI 对话格式差异：事件、快照与跨库消息定位

对照五个固定适配器，区分 JSONL 容器、事件语义与 SQLite 键布局，用两个内存数据库观察消息索引、孤立数据和未知版本。          曾亮懿       2026年10月7日      阅读约 4 分钟

#ChatCrystal

#Node.js

#SQLite](/blog/ai-conversation-format-divergence)    [##  AI 对话管理方案：四种存储路径与迁移验收

比较 Claude Code、Cursor、Copilot、Trae 的固定适配器输入，区分原始副本、可读导出与知识库，并用合成清单验证身份和信息保留。          曾亮懿       2026年10月7日      阅读约 4 分钟

#ChatCrystal

#Node.js

#SQLite](/blog/ai-conversation-management-options)    [##  五种 AI 对话解析器：字段保留与损失边界

从 Claude Code、Codex、Cursor、Trae 和 Copilot 的实际解析器建立验收矩阵，检查工具标记、思考文本、顺序与快照覆盖范围。          曾亮懿       2026年10月7日      阅读约 4 分钟

#ChatCrystal

#TypeScript

#Node.js](/blog/ai-conversation-parser-boundaries)

##  从文章到代码

到我的 GitHub 看看 ChatCrystal、开发者工具和日常学习记录，欢迎交流实现细节。

[访问我的 GitHub](https://github.com/ZengLiangYi)

```json
{
  "@context": "https://schema.org",
  "@type": "WebSite",
  "name": "ZengLiangYi Blog",
  "alternateName": [
    "Zeng Liangyi",
    "ZengLiangYi",
    "zengliangyi.com"
  ],
  "url": "https://zengliangyi.com",
  "description": "曾亮懿的个人网站。记录 AI Native 探索、开发者工具与全栈产品实践。"
}
{
  "@context": "https://schema.org",
  "@type": "BlogPosting",
  "headline": "Tailwind CSS v4 与 Vite：编译工具类，运行时换主题",
  "description": "核对 ChatCrystal 的双包安装、Vite 插件与 CSS 变量主题，区分构建时类名生成和运行时变量注入。",
  "url": "https://zengliangyi.com/blog/tailwind-v4-vite-setup/",
  "image": "https://zengliangyi.com/images/blog/tailwind-v4-vite-setup.svg",
  "datePublished": "2026-10-04T00:00:00.000Z",
  "dateModified": "2026-10-04T00:00:00.000Z",
  "author": {
    "@type": "Person",
    "name": "曾亮懿",
    "url": "https://zengliangyi.com/about"
  },
  "publisher": {
    "@type": "Organization",
    "name": "ZengLiangYi Blog",
    "logo": {
      "@type": "ImageObject",
      "url": "https://zengliangyi.com/favicon-96x96.png"
    }
  },
  "mainEntityOfPage": {
    "@type": "WebPage",
    "@id": "https://zengliangyi.com/blog/tailwind-v4-vite-setup/"
  }
}
{
  "@context": "https://schema.org",
  "@type": "BreadcrumbList",
  "itemListElement": [
    {
      "@type": "ListItem",
      "position": 1,
      "name": "首页",
      "item": "https://zengliangyi.com/"
    },
    {
      "@type": "ListItem",
      "position": 2,
      "name": "文章",
      "item": "https://zengliangyi.com/blog"
    },
    {
      "@type": "ListItem",
      "position": 3,
      "name": "Tailwind CSS v4 与 Vite：编译工具类，运行时换主题",
      "item": "https://zengliangyi.com/blog/tailwind-v4-vite-setup/"
    }
  ]
}
```
