样式系统实战:白物集如何用 daisyUI 搭 UI
项目做到一定规模,手写 CSS 就撑不住了。白物集建站初期面临的问题很具体:几十个页面和组件,每个都要重复写按钮、导航、卡片、徽章——同事在不同页面里写了两套风格接近但不完全一样的按钮,后来花了半天统一。
解决方案是 Tailwind CSS v4 + daisyUI v5。不是选最潮的框架,是选最快出活的组合。下面直接拆白物集项目里的真实代码。
为什么不是纯 Tailwind
Tailwind CSS 的原子类写法,写过的人都知道:一个按钮能写满一行 class。daisyUI 把常见 UI 模式封装成语义类,一个 btn 就解决按钮的 padding、font-size、border-radius、hover 状态。
白物集用了 daisyUI 的这几个核心组件:
| 组件 | daisyUI 类 | 项目中用的地方 |
|---|---|---|
| 导航栏 | navbar + navbar-start/center/end |
NavBar.astro |
| 按钮 | btn、btn-ghost、btn-sm |
所有页面 |
| 菜单 | menu、menu-horizontal |
导航项 |
| 页脚 | footer、footer-center |
BaseLayout.astro |
| 卡片 | card |
ArticleCard、文章详情 |
| 链接 | link、link-hover |
页脚链接 |
| 面包屑 | breadcrumbs |
文章详情页 |
好处是组件迁移时,HTML 结构改了,但 daisyUI 的 class 不变——NavBar 从横向布局改成纵向,只需要在 <nav> 上换一个类。
项目里的 Tailwind + daisyUI 配置
白物集用 Tailwind v4 的 CSS-first 配置方式(不是传统的 tailwind.config.js),入口文件 global.css 只有三行:
@import "tailwindcss";
@plugin "daisyui";
两个 @ 指令就完成了整站 UI 框架加载。daisyUI v5 兼容 Tailwind v4,可以直接用 @plugin 语法引入,不需要手动 import daisyUI 的 CSS 文件。
Astro 的配置也很简洁——astro.config.mjs 里用 Vite 插件接入 Tailwind:
import { defineConfig } from 'astro/config';
import tailwindcss from '@tailwindcss/vite';
export default defineConfig({
vite: {
plugins: [tailwindcss()],
},
});
package.json 里 daisyui: "^5.6.2" 和 tailwindcss: "^4.3.1" 是 devDependencies,构建时会被 tree-shaken,不会污染生产包。
导航栏——daisyUI 组件实战
白物集的顶部导航栏(NavBar.astro)是 daisyUI 组件用得最多的地方。代码总共 70 行,去掉 SVG logo 和数据逻辑,核心结构就这些:
<nav class="navbar bg-base-100/80 backdrop-blur-md border-b border-base-200 sticky top-0 z-50">
<div class="navbar-start">
<a href="/" class="btn btn-ghost text-sm gap-1 md:gap-2 px-2 md:px-3">
<span class="font-semibold tracking-wide text-sm">白物集</span>
</a>
</div>
<div class="navbar-center hidden sm:flex">
<ul class="menu menu-horizontal px-1 gap-1 text-sm">
{items.map((item) => (
<li><a href={item.href} class="btn btn-ghost btn-sm">{item.label}</a></li>
))}
</ul>
</div>
<div class="navbar-end gap-1">
<button id="theme-toggle" class="btn btn-ghost btn-sm text-lg">🌙</button>
</div>
</nav>
几个关键点:
navbar类自动实现 Flexbox 三栏布局,navbar-start/center/end分别控制左中右对齐——不需要自己写justify-content: space-between。bg-base-100/80+backdrop-blur-md是毛玻璃导航栏效果,/80是 Tailwind v4 的透明度后缀。btn btn-ghost组合实现透明底按钮,hover 时有背景色变化,比手写cursor: pointer+transition少 5 行 CSS。menu menu-horizontal是横向导航菜单,自动处理li间距和a标签的点击区域。
这是项目里最常踩的坑:直接用 HTML 原生 <ul><li> 写导航栏,间距、对齐、hover 效果全部手写,换一个页面又得重新调一遍。用 daisyUI 的 menu 类后,导航栏样式在所有页面保持一致。
卡片组件——设计系统一致性
文章卡片是白物集首页最频繁出现的 UI 模式。ArticleCard.astro 里的卡片用了 daisyUI 的 card 类:
<a href={`/articles/${id}`}
class="card group no-underline block bg-base-100 border border-base-300 rounded-xl p-5
transition-all duration-200 hover:-translate-y-0.5
hover:border-[color-mix(in_srgb,{accentColor}_50%,transparent)]">
<div class="tag text-xs font-semibold uppercase tracking-wider mb-2"
style="background:color-mix(in srgb, {accentColor} 15%, transparent);color:{accentColor};">
{category}
</div>
<h3 class="text-base font-semibold mt-0 mb-2 leading-snug text-base-content">{title}</h3>
<p class="text-sm leading-relaxed mb-0 text-base-content/70">{abstract}</p>
<div class="flex gap-3 mt-3 text-xs text-base-content/50">
<span>📅 {date}</span>
</div>
</a>
text-base-content 是 daisyUI 的语义色变量,对应当前主题的文字色。切换暗色模式时,body 的 data-theme 属性变 "dark",daisyUI 自动重写所有 base-content 的颜色值——整个换肤过程不需要碰一行 CSS。
首页 index.astro 的卡片网格用了一个技巧:按文章频道分别赋予不同的标签色,通过 color-mix 动态生成透明背景:
const tagStyle = isBreakfast
? 'background:rgba(10,132,255,0.12);color:var(--accent-cyan);'
: isApple
? 'background:rgba(255,159,10,0.12);color:var(--accent-amber);'
: isAi
? 'background:rgba(204,102,0,0.12);color:var(--accent-orange);'
: 'background:rgba(90,184,255,0.12);color:var(--brand-secondary);';
暗色主题切换——只用两行 daisyUI
白物集支持浅色/暗色双主题,切换逻辑在 NavBar.astro 的 <script> 里,只有 6 行有效代码:
const html = document.documentElement;
function setTheme(t) {
html.setAttribute('data-theme', t);
localStorage.setItem('theme', t);
}
daisyUI v5 自带两套内置主题(light 和 dark),只需在 <html> 上设置 data-theme="dark",所有 daisyUI 组件的背景色、文字色、边框色自动切换。BaseLayout.astro 里还加了一段首屏 inline script,从 localStorage 恢复用户选择:
(function(){
var s = localStorage.getItem('theme');
if (s) { document.documentElement.setAttribute('data-theme', s); return; }
var p = window.matchMedia('(prefers-color-scheme: dark)').matches;
document.documentElement.setAttribute('data-theme', p ? 'dark' : 'light');
})();
这段代码放在 <head> 里同步执行,避免暗色模式下的首屏白屏闪烁。
白物集的设计 Token 体系
daisyUI 的 base-100、base-200、base-content 是基于 CSS 变量的抽象层,白物集在此基础上扩展了自己的 Token:
:root {
--brand-primary: #0A84FF;
--bg-primary: #0a0a0c;
--bg-card: #16161a;
--border: #2a2a33;
--text-primary: #e8e8ed;
--accent-cyan: var(--brand-primary);
}
[data-theme="light"] {
--bg-primary: #f5f5f7;
--bg-card: #ffffff;
--border: #d2d2d7;
--text-primary: #1d1d1f;
}
不要把所有样式都写在 daisyUI 类里。白物集的经验是:daisyUI 管组件结构(navbar、menu、card、btn),自定义 Token 管品牌色和间距。两者各自负责,不混在一起。页面的主色调用 --brand-primary,组件的行为结构用 daisyUI 的语义类——换主题时改 CSS 变量,不动 HTML。
避坑:daisyUI v5 和 Tailwind v4 的兼容
daisyUI v5 正式支持 Tailwind v4,但有一个注意事项:daisyUI v4 不兼容 Tailwind v4。白物集之前升级 Tailwind 到 v4 后,daisyUI 组件样式全乱了(按钮边框消失、卡片间距错位),排查后发现是 daisyUI 版本没跟上。最终方案是同时升级:tailwindcss@^4.3.1 + daisyui@^5.6.2,配合 @tailwindcss/vite 插件。
另一个坑:daisyUI 的内置主题色(primary、secondary)和白物集的品牌色冲突。daisyUI 默认 primary 是蓝色,白物集的品牌色也是蓝色,但饱和度不同。解决方案是不用 daisyUI 的 btn-primary,而是自己定义 --brand-primary CSS 变量,配合 <style> 标签里的自定义类。品牌一致性比框架便利性优先。
总结
白物集的样式系统结构很简单:Tailwind v4 提供原子类 → daisyUI v5 封装组件 → CSS 变量承载品牌色。三层各司其职,没有一种框架包办一切。对于内容型网站,这是出活最快、维护成本最低的组合。
下一篇拆后端——Express 在白物集里是怎么搭的,19 个 API 路由如何分层管理。