跳到正文
曾亮懿 ZengLiangYi Blog

Tailwind CSS v4 与 Vite:编译工具类,运行时换主题

核对 ChatCrystal 的双包安装、Vite 插件与 CSS 变量主题,区分构建时类名生成和运行时变量注入。

曾亮懿

曾亮懿

阅读约 3 分钟
目录 展开 收起

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

安装与入口必须同时齐全

固定 client/package.json 同时声明 tailwindcss 与 @tailwindcss/vite,两者版本范围均为 ^4.3.3。不能把安装缩成只有插件包。v4 仍有 PostCSS 集成方式;Vite 插件是当前项目采用的路线,不是 Tailwind 已经移除 PostCSS 支持。官方 Vite 安装指南 也列出了两个包。

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

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

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

// vite.config.mjs
import { defineConfig } from 'vite';
import tailwindcss from '@tailwindcss/vite';

export default defineConfig({ plugins: [tailwindcss()] });
<!-- 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>
/* 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 并列注册 react() 与 tailwindcss(),用 import.meta.dirname 设置 @ 别名,还配置主入口与 Electron onboarding 两个构建入口。开发服务绑定 127.0.0.1:13721,/api 代理到 localhost:3721。这是开发代理,不能当作生产部署或云端认证配置。

index.css 除了导入 Tailwind,还导入 tw-animate-css,通过 @theme inline 桥接颜色与圆角变量,并定义应用工具类。@theme 与 @utility 的用途可以对照 指令文档。不要因为用了 CSS-first 配置,就删除项目仍依赖的动画包或兼容变量。

主题变量如何到达组件

ThemeProvider 注册 dark-workshop、dawn-haze 与 jade-abyss,默认 dawn-haze。它从版本化 localStorage 恢复选择,在 effect 中逐项设置根元素变量,再按所选背景色的亮度近似判断 color-scheme。该路径不是根据系统 prefers-color-scheme 自动挑主题。

themeToCSSVars 将 bgPrimary 转成 --bg-primary,也将 graphProject1 转成 --graph-project-1,然后添加字体与圆角。ThemeDefinition 虽声明 density,这个转换函数并没有输出 density 变量;不能把接口里有字段当作页面已实现效果。

类名必须让编译器看得到

Tailwind 扫描源文本中的完整类名,不执行 JavaScript 来推导拼接结果,见 类名检测规则。用完整的静态备选类名表达状态;真正动态的进度等数值放进 style 或 CSS 变量。调试时分别检查类名是否出现在构建 CSS、变量是否赋值、最终 computed style 与对比度是否符合预期。

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

分享:

相关文章

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

对照五个固定适配器,区分 JSONL 容器、事件语义与 SQLite 键布局,用两个内存数据库观察消息索引、孤立数据和未知版本。

曾亮懿 曾亮懿
阅读约 4 分钟
#ChatCrystal #Node.js #SQLite

AI 对话管理方案:四种存储路径与迁移验收

比较 Claude Code、Cursor、Copilot、Trae 的固定适配器输入,区分原始副本、可读导出与知识库,并用合成清单验证身份和信息保留。

曾亮懿 曾亮懿
阅读约 4 分钟
#ChatCrystal #Node.js #SQLite

五种 AI 对话解析器:字段保留与损失边界

从 Claude Code、Codex、Cursor、Trae 和 Copilot 的实际解析器建立验收矩阵,检查工具标记、思考文本、顺序与快照覆盖范围。

曾亮懿 曾亮懿
阅读约 4 分钟
#ChatCrystal #TypeScript #Node.js

从文章到代码

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