尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

Readest 前端样式栈升级实战:daisyUI 5 + Tailwind CSS 4 迁移的层叠语义、兼容钉与回归防线

Readest 前端样式栈升级实战:daisyUI 5 + Tailwind CSS 4 迁移的层叠语义、兼容钉与回归防线 桌面应用跨平台前端【免费下载链接】readestReadest is a modern, feature-rich ebook reader designed for avid readers offering seamless cross-platform access, powerful tools, and an intuitive interface to elevate your reading experience.项目地址https://gitcode.com/gh_mirrors/re/readest点击查看免费下载本文是 Readest 项目将 UI 组件库从 daisyUI 4 升级到 daisyUI 5、并同步将样式引擎从 Tailwind CSS 3 迁移到 Tailwind CSS 4 的完整技术实录。文章以仓库中的迁移笔记 daisyui-v5-tailwind-v4-migration.md 为主线结合 globals.css、themes.ts、daisyui-themes.ts 及多个浏览器级回归测试系统讲解这次升级中真正的难点CSS 级联层cascade layer语义变化、Tailwind 4 工具类排序与组合方式的差异、daisyUI 5 原生 select 弹出层::picker(select)的适配以及 6 个合并后回归问题的根因与修复。读完你不仅能在自己的项目中完成同样的升级还能掌握一套用真实编译结果与计算样式验证 UI的排查方法论。迁移背景为什么必须整体升级daisyUI 5 是一个强依赖 Tailwind 4的大版本旧的tailwind.config.tsJS 配置体系被废弃全部配置必须迁移到 CSS-first 的theme/plugin/custom-variant语法中。因此这次升级不是一个库的替换而是组件库 构建引擎 配置范式三者的整体搬迁。在 Readest 仓库中这次迁移在 2026-08-26 以 PR #5884 合并squash 提交8ef527af7其直接动因是 issue #5587Windows 暗色模式下原生 select 弹出层渲染为白色。daisyUI 5 的.select默认启用appearance: base-select并为主题提供::picker(select)伪元素Chromium 135使弹出层可以由页面绘制从而解决原生弹窗颜色不跟随主题的问题。围绕同一 PR 还发生了两处平台底线抬升可以从 tauri.conf.json 中直接看到WebViewminSupportedMajor92 → 111第 254 行base-select与::picker()需要 Chromium 135 的能力。iOSbundle.iOS.minimumSystemVersion15.0 → 16.4第 101 行macOS 仍保持 12.0第 65 行。原因是 iOS 15.8 的 Safari对应 WebKit缺少color-mix()需 Safari 16.2与property需 16.4会导致 daisyUI 5 输入框边框退化为currentColor、开关toggle几何塌陷macOS 12 Safari 15 存在同样的能力缺口但项目选择暂不处理。也就是说升级组件库之前先要确认目标平台对底层 CSS 特性的支持矩阵否则新版本组件会在旧内核上渲染出错。Tailwind 4 的 CSS-first 配置globals.css 成为唯一配置入口Tailwind 4 将配置从tailwind.config.ts整体搬进 CSS。Readest 的 globals.css 开头几行就是完整范式第 1-20 行import tailwindcss source(../); plugin tailwindcss/typography; plugin daisyui { themes: light --default, dark --prefersdark; logs: false; } /* The built-in Readest themes, compiled from themes.ts (see daisyui-themes.ts). */ plugin ./daisyui-themes.ts; custom-variant eink (html[data-einktrue] ); custom-variant not-eink (html:not([data-einktrue]) ); custom-variant theme-dark (html[data-theme$-dark] ); custom-variant hover (:hover);要点拆解import tailwindcss source(../)指定源码扫描范围相对该 CSS 文件的../Tailwind 4 自动从源码中收集工具类。plugin daisyui { themes: light --default, dark --prefersdark; }只启用 daisyUI 自带的 light/dark 两个基础主题这也与13 个内置主题的计数吻合2 个 daisyUI 主题 11 个 Readest 主题。plugin ./daisyui-themes.ts直接以 TypeScript 文件作为插件入口注册 Readest 自己的 11 个内置主题default、gray、sepia、grass、cherry、sky、solarized、gruvbox、nord、contrast、sunset定义在 themes.ts。四个custom-variantReadest 的特色。其中theme-dark用html[data-theme$-dark]属性选择器跟踪应用内主题Tailwind 自带的dark:跟随系统prefers-color-scheme无法跟踪应用内主题切换eink/not-eink服务于墨水屏设备hover让触屏设备点击时仍能触发 hover 样式Tailwind 4 默认把hover:收进media (hover: hover)。迁移后tailwind.config.ts已从仓库中删除工作目录中不存在该文件原配置中庞大的 v3 safelist 也被移除——因为src/中没有任何运行时动态拼接的类名Tailwind 4 的内容扫描足以覆盖全部工具类。层叠层级daisyUI 5 最大的坑daisyUI 5 的嵌套子层语义daisyUI 5 的组件样式位于layer utilities { layer daisyui.* }这种嵌套子层中。理解这一点是本次迁移的钥匙非分层unlayered的普通 CSS 优先级最高会打败一切工具类——包括w-44这样的 utilities。这是 CSS 级联层的规则分层内容按声明顺序比较而非分层内容永远优先于所有分层内容。daisyUI 的组件默认样式被压缩进daisyui.l1.l2.l3深层子层任何普通规则都能压过它即使特异性为 0。所以 Readest 的做法是把所有自定义规则用layer utilities { ... }包裹见 globals.css 第 254 行 起的整块注释与代码恢复 v3 的优先级语义——utilities 层内按特异性/顺序竞争daisyUI 子层自然落败。如果放任自定义规则处于非分层状态一个:where(.dropdown-content)都可能让w-44失效。另一个硬约束utility块必须保持在顶层cannot be nested不能放进layer。:where()零特异性 shim替代 daisyUI 组件默认值的唯一正确姿势凡是用来顶替 daisyUI 组件默认值的兼容规则必须用:where()包裹保持零特异性。仓库里有个教科书级事故.menu { width: auto }没加:where()直接落在 utilities 层里结果打败了sm:w-44把 FontDropDown 的菜单塌缩到 0px菜单行是w-full overflow-hidden父级宽度归零后全部溢出隐藏。这正是 globals.css 第 919-922 行 使用:where(.menu) { width: auto; line-height: 1.25rem; }的原因v5 的菜单默认fit-content收缩、行高继承 bodyv4 的菜单则是width: autotext-sm行高。v4 外观钉把 v3/v4 的量感逐项钉回来daisyUI 5 对一批组件默认尺寸做了收缩按v4 外观度量全部对照 main 分支计算样式验证组件v4 值v5 值钉法globals.cssmd 尺寸按钮/输入框/下拉高 3rem48px2.5rem40px--size: calc(var(--size-field, .25rem) * 12)、--in-size-mul: 12、--sl-size-mul: 12第 793-812 行小号字号0.875rem0.75rem--fontsize/--font-size-mincheckbox 圆角--radius-field0.5rem药丸徽章圆角:where(.checkbox) { border-radius: var(--radius-field) }第 845-847 行菜单宽度/行高width: auto; line-height: 1.25remfit-content body 行高:where(.menu)第 919-922 行dropdown 展开静态缩放渐隐动画:where(.dropdown .dropdown-content) { animation: none; transition: none }第 404-407 行否则 Dropdown 视口裁剪会量到 95% 的缩放中框体非按钮上的.btn-disabled仅移除 pointer-events涂上 base-content/10:where(.btn-disabled:not(.btn))透明化第 911-914 行toggle 轨道宽3rem2.5rem:where(.toggle) { width: 3rem }第 892-894 行两个容易好心办坏事的点toggle 的颜色不要动daisyUI 5 的 toggle 颜色本来就与 v4 一致base-100 轨道 base-content 旋钮早期一个强行background-color: currentColor的 shim 反而把它反转成了浅色轨道 深色旋钮后来被移除。toggle 尺寸按档位分别钉.toggle-xs/sm/lg各自有独立的 v4 宽度1.5rem/2rem/4rem只钉基础宽度会把所有档位压平所以 第 896-906 行 每个档位单独一条且顺序敏感同为零特异性时后写者胜。浏览器测试 daisyui-v5-tokens.browser.test.tsx 对这批钉做了端到端守护断言 md 按钮/输入框/下拉高 48px、btn-sm高 32px 且字号 14px、checkbox 圆角 8px、toggle 宽 48px/高 24px、loading-lg宽 40px2.5rem。主题令牌--color-*hex 发射器与运行时/编译时一致性daisyUI 5 的主题令牌从 v4 的--b1oklch 三元组 --fallback-*改为CSS 变量直接承载--color-*命名空间。Readest 的核心实现是 themes.ts 的themeVariables()它把调色板发射为一组--color-base-100、--color-primary等 hex 值并钉住 v4 的几何参数--radius-selector: 1.9rem, --radius-field: 0.5rem, --radius-box: 1rem, --size-selector: 0.25rem, --size-field: 0.25rem, --border: 1px, --depth: 0, --noise: 0,--depth: 0关掉 daisyUI 5 的伪 3D 阴影--radius-selector: 1.9rem保留药丸徽章/开关形状。一个值得单独强调的细节是contentColorFor()第 59-63 行daisyUI 4 对每个*-content颜色执行向对比度更高的白/黑方向混合 80%的推导v5 换了自己的公式。Readest 显式复刻 v4 公式用 tinycolor 的 readability 判断方向保证 primary 按钮、徽章、tooltip 的文字可读性与升级前完全一致。双路径一致性是这个设计的关键编译期 daisyui-themes.ts 用同一个themeVariables()通过addBase为每个[data-themename-light|dark]生成规则块运行期applyCustomTheme()themes.ts 第 241-257 行为自定义主题注入同样的令牌。两条路径输出完全相同的 CSS自定义主题与内置主题渲染无差异。对应的浏览器测试在 daisyui-v5-tokens.browser.test.tsx 第 28-63 行逐个主题断言getComputedStyle的 backgroundColor 等于预期 hex。类名冲突与替换daisyUI 5 中部分类名语义变化Readest 做了显式处理progress-info在 v4 中仅是::before伪元素专用v5 升格为 color modifier 后与应用的类名冲突应用类重命名为progress-readout。dropdown-center语义兼容保留使用。label/form-control/label-text/*-bordered这些组件级类在 v5 中不再是推荐用法统一替换为 Tailwind 工具类组合。dropdown 语义两个 v4 行为的恢复daisyUI 5 改变了 dropdown 的两个隐藏机制Readest 在 globals.css 第 409-416 行 逐一恢复关闭态隐藏方式v4 用visibility: hiddenv5 用display: none。后者会被内容上的任何 display 工具类inline、flex覆盖留下一个不可见但可点击的悬浮层——实际事故是它悄悄改变了用户的 CJK 字体选择。恢复为 v4 语义非details、未打开、未 hover、未 focus-within 的 dropdown 内容设为visibility: hidden。FontDropDown 嵌套子列表v4 的.dropdown:focus-within .dropdown-content会把嵌套内容也当作后代匹配子列表常显用户明确选择了仅当所在行被激活时展开的新行为——行本身是rolebutton tabIndex0加onClick focus()于是嵌套.dropdown通过:focus-within激活。迁移中还顺手删除了嵌套列表上一个失效的relative类v4 时代.dropdown .dropdown-content特异性更高压过了它v5 工具类赢了就变成死代码。该行为有浏览器测试 font-dropdown-submenu.browser.test.tsx。select 专项appearance: base-select的适配全集#5587 的完整解法是一整套 select 适配全部集中在 globals.css 第 849-886 行不要设置appearance-none!它会取消base-select让弹出层退回原生渲染修复直接失效。SettingsSelect 和 audiobook 的 select 都移除了该样式。给 picker 上色.select::picker(select)默认继承 select 背景而 chromeless无边框设置项 select 是bg-transparent!picker 会继承透明背景。Readest 用background-color: var(--color-base-100) 阴影把它画成应用内其他菜单的样子并用open:outline-hidden!隐藏:open时的聚焦环。option 换行daisyUI 5 自带option { white-space: normal }且因为弹出层在页面内绘制option上的 CSS 是生效的——Português (Brasil) 这类长条目会在窄 select 里换行。修复:where(.select) option { white-space: nowrap }。2026-08-28 在小米设备Chrome 153上又验证出三个 select 回归进一步充实了这套适配值不再右对齐Select.tsx的text-align-last: end在 v5 中双重失效——.select的width: clamp(3rem,20rem,100%)让框体撑到最大宽度200px 而值只需 96-154px且值被绘制进 UA 生成的selectedcontent影子根里作者 CSS 无法触达实测注入select.selected selectedcontent{text-align:end}同样无效。修复是给 Select 加w-auto让框体贴合值宽再配合行上的justify-between把值贴到 chevron 旁——这条修复就在 Select.tsx 第 33-46 行 的注释里。picker 不与 select 对齐daisyUI 用position-area: self-start span-self-start端对齐后又推回 16pxmargin-inline: 8pxtranslate: -8pxRTL 镜像为.5rem。修复::picker(select)上margin-inline-end: 0; translate: none保留 start 侧 margin 防止宽 picker 顶到对边。调试 picker 的方法::picker(select)没有 DOM 节点要用getComputedStyle(select, ::picker(select))读取真实的translate、margin-inline-*、position-area、width要证明选择器命中它设background-color: red截图。注意 Chrome 153 Android WebView 不支持justify-self: anchor-end和anchor()但支持position-area。Tailwind 4 的排序与组合陷阱Tailwind 4 内部对工具类重新排序、并改变了部分属性的组合方式有两类静默陷阱在这次迁移中被抓出同属性工具类的排序翻转基础类 条件覆盖的写法在 v3 能工作v4 会静默翻转hidden与inline-flex、absolute与fixed、items-center与items-end都出现过顺序反转。修复原则是让条件分支互斥cond ? hidden : inline-flex而不是同时挂两个类。任意值类也改为排在命名类之后text-[0.6em] text-xs在 v3 输出 12px在 v4 输出 9.6px——这个问题只有在真实 Chrome 检查书架标题时才发现。rotate-*/scale-*/translate-*改为与transform组合v3 把这些类折叠进transform经--tw-rotate所以元素上的内联style{{transform:rotate(180deg)}}能覆盖rotate-180类。v4 发射独立的 CSSrotate属性浏览器按translate→rotate→scale→transform顺序依次应用角度相加。事故现场在 AnnotationRangeEditor.tsx 第 107-114 行选区拖拽 Handle 同时挂着className{clsx(typestart rotate-180)}和内联 transform开始端手柄水平方向 1801800、垂直方向 18027090渲染得和结束端一模一样小球落在文本侧而非列外侧。修复是删掉类、只保留内联 transform 作为唯一来源。诊断手法getComputedStyle(svg)会分开报告rotate: 180deg和transform: matrix(...)组合后的结果永远不会出现在transform里svg.style.rotatenone可即时打补丁做前后对比截图。任意值里的逗号不是轨道分隔符TranslatorPopup曾用grid-rows-[1fr,auto,1fr,auto]Tailwind 原样发射grid-template-rows:1fr,auto,1fr,auto——逗号不是轨道分隔符浏览器直接丢弃该声明所有行退化为隐式 auto 轨道翻译面板和 provider 底部被推出屏幕且不可滚动。正确写法是_分隔 minmax(0,1fr)裸1fr以 min-content 为下限会再次溢出。教训在浏览器测试中断言解析后的布局而不是断言类名字符串且getComputedStyle().gridTemplateRows在声明被丢弃时仍会报告实际使用的轨道尺寸所以要用同类的空 grid来探测被丢弃时报告none。合并后的六次回归根因与修复迁移合并PR #5884后一周内连续发现六个回归全部有明确的 CSS 根因。这些案例构成了升级后必查清单。1. Toast 折叠成图标PR #5894v4 的.toast有min-width: fit-contentv5 换成width: max-content。Toast 组件自带的w-autov4 下是 no-op在 v5 下反而胜出配合toast-center的inset-inline: 50%固定定位盒子的宽度解析为0px实测width: 0px, min-width: 0px, left/right 640px。info toast 在所有宽度下都用toast-center因此全屏段损坏success/warning/error 只在 640px 时损坏sm:toast-end在宽屏下救了它们。修复 从 Toast.tsx 移除w-auto。同一 PR 还处理了 v5 把.toast的padding: 1rem挪进 insets 导致顶栏间距丢失的问题——组件用TOP_BAR_HEIGHT (44) TOAST_GAP (16)显式补回Toast.tsx 第 11-12、135-136 行且不要在 globals.css 里恢复 padding否则 v5 的bottom: 1rem会同向叠加成 2rem。2.loading-lg缩小#5892已由 chrox 修复v5 把尺寸放到calc(var(--size-selector,.25rem) * N)lg 72.5rem 缩到 1.75remxs/sm/md 不变。修复是:where(.loading-lg){width:2.5rem}globals.css 第 822-824 行——已在 #5892 修过勿重复修复。3. Dialog 关闭时只剩标题条PR #5894这不是 v5 独有的回归v4 生产也有但 v5 放大了它v4 整个.modal淡出基础规则 200ms 无延迟的opacity: 0v5 的.modal基础规则完全没有 opacity只有visibility .3s allow-discrete而.modal-box自身淡出opacity .2s ease-out 50ms。Readest 的 8 个对话框都用Dialog isOpen{o}{o Body/}/Dialog门控 body关闭帧 body 卸载、box 塌缩到只剩头部。修复在 Dialog.tsx用lastBodyRefisBodyHoldOver状态把最后一个 body在渲染期间保持住CLOSE_TRANSITION_MS 300box 的 opacity 在 250ms 时已为 0卸载永不可见。4. Loading 动画对所有人减速PR #5906daisyUI 5 为每个.loading-*遮罩发射两份一份减速的基础规则替代 reduced-motion 语义dotsdur3s、infinity6s一份media (prefers-reduced-motion: no-preference)的全速覆盖dots1.05s、infinity2s。Tailwind 4 在layer utilities内重排它们且不均匀.loading和.loading-spinner顺序正确而.loading-dots、.loading-infinity以及not-eink:loading-dots变体副本都是媒体块在前、基础规则在后慢速遮罩胜出——表现为圆点上下漂移而非弹跳。上游 daisyui.com 自己都算错。修复在 globals.css 第 834-843 行一个media (prefers-reduced-motion: no-preference)块重声明全速遮罩选择器必须同时覆盖:where(.loading-dots)和:where(html:not([data-einktrue])) :where(.not-eink\:loading-dots)Spinner.tsx 与 EpisodesView 通过变体类触达 dots裸:where(.loading-dots)匹配不到且必须保持 e-ink 门控否则墨水屏会失去loading-spinner替换。测试在 daisyui-v5-tokens.browser.test.tsx 第 108-139 行通过解析计算样式maskImage里的dur值断言。5..modal-box独立底盘永不绘制PR #5916这是最隐蔽的一个。daisyUI 4 把进入动画放在.modal上daisyUI 5 移到子元素.modal-box{opacity:0;scale:.95}只在.modal:is(.modal-open,[open],:popover-open,:target).modal-box时解除隐藏。而 Readest 把.modal-box当独立盒子底盘用bg-base-100、圆角、阴影、eink 边框挂在自绘的fixed inset-0覆盖层下——没有.modal祖先于是布局正常、吞噬点击、永不绘制只有背景变暗。三个出事点TransferQueuePanel.tsx:256、GroupingModal.tsx:257、Annotator.tsx:2286。一条规则全部修复globals.css 第 290-293 行:where(.modal-box):not(:where(.modal *)) { opacity: 1; scale: 1; }not(:where(.modal *))守卫是承重的关闭中的.modal内的 box 必须保持opacity: 0否则 Dialog 的关闭过渡由 dialog-close-frames.browser.test.tsx 守护会破。规则放layer utilities内但本身不分层daisyui.l1.l2.l3子层必然落败:where()保证零特异性Tailwind 工具类仍可覆盖。回归测试 modal-box-standalone.browser.test.ts 需要真实样式表因此是浏览器测试jsdom 看不见。同类的清扫方法解析node_modules/daisyui/components/*.css找基础层里声明opacity:0/display:none/visibility:hidden的组件类。v5 完整名单modal-box、collapse-content、drawer-side、tab-content、validator-hint、menu-dropdown、megamenu-vertical、floating-labelspan、swap-on/off。项目中只有modal-box和collapse-content被用后者Notebook.tsx:490处在真实.collapse中不受影响。注意dropdown-content不是这个 bug它只通过.dropdown ... .dropdown-content隐藏裸用反而过度可见。6. 括号剥离任意值只对命名值有效PR #5985规范化p-[Npx]→p-Npx删括号产出的类匹配不到任何东西、不发任何 CSS——静默失败因为 Tailwind 没有未知类诊断pnpm lintbiome tsc也看不进字符串字面量。事故发生在BookItem.tsx的pt-[2px] sm:pt-[1px]被改成pt-2px sm:pt-1px两个图书馆图标下的光学对齐微调全部失效。间距刻度表里唯一的非数字命名键是px 1px 发丝线class产物pt-px/sm:pt-pxpadding-top: 1pxpt-0.5/-mt-0.5calc(var(--spacing) * ±0.5) 默认 0.25rem--spacing下 ±2pxpt-[1px]/pt-[2px]正确只是非规范pt-1px/pt-2px什么都不发因此 VS Code 的tailwindcss(suggestCanonicalClasses)提示在sm:pt-[1px]上读作sm:pt-px刻度键而非裸长度pt-0.5是基于 rem 的近似等价但会跟随根字号变化属于行为变更而非纯重命名。仓库提供了一份约 1 秒的验证脚本无 dev server、无 Next build——apps/readest-app/tw-check.mjs用tailwindcss包的compileAPI 直接驱动项目自己的编译器候选类不在输出里就说明不发射任何规则aspect-[28/41]→aspect-28/41是真实的 v4 简写——裸分数即比值——所以保留。诊断方法论让哪条规则赢了可证伪整个迁移最可复用的部分是一套不依赖 dev server 的 CSS 级联取证工具链真实级联渲染~600msnpx tailwindcss/cli -i src/styles/globals.css -o out.css直接渲染应用的完整级联然后逐个选择器扫描出现位置、记录最后出现的规则。直接读node_modules/daisyui/components/*.css是误导的——作者源文件里顺序正确是 Tailwind 重排了它。单类存在性验证~1s用apps/readest-app/tw-check.mjs的compile().build([...])读回产物。对生产环境取证迁移期间生产站仍是 v4curl其/_next/static/css/*.css并 grep.toast{、.loading-lg{、.modal{拿精确的 v4 规则v4 tarball 也可用解包daisyui-4.12.24.tgz的package/dist/full.css未压缩版匹配^\.sel\s*\{。控制台直开对话框document.getElementById(about_window).dispatchEvent(new CustomEvent(setDialogVisibility,{detail:{visible:true}}))点击版本字符串会触发真实 info toast。慢放动画注入transition-duration: 4s !important; transition-delay: 0s !important再截图。后台化 Chrome 标签页里requestAnimationFrame和亚秒setTimeout会被节流rAF 采样循环会挂起 CDP 45 秒setTimeout跳到 1000ms。测 toast 前先结束动画v5 给.toast *加了 0.25s 的 scale 动画叠加组件自带的 300ms 过渡getBoundingClientRect会同时读到两者。for (const a of el.getAnimations({subtree:true})) a.finish()后再跑一帧 rAF精确且比等时钟快 10 倍。Side-by-side 双端口对比main 跑pnpm dev-web:3000迁移分支跑pnpm dev-web -p 3001:3001用 Playwright 脚本从应用目录createRequire解析 playwright在两端渲染同一场景、拼左右 PNG、用page.evaluate对比计算样式。演示书在 web 端自动加载用文本打开同一本 Hamlet 保证两端内容一致。渲染时序探针rAF 采样器单独用抓不到延迟交接React 的 passive flush 通常抢在帧前effect 版本能通过。需要两个仪器配合一个渲染在组件之后的CommitProbe每次 commit 用useEffect采样 DOM确定性失败于 effect 版本 rAF 采样器验证用户可见属性且关闭动作必须在act外globalThis.IS_REACT_ACT_ENVIRONMENT false从requestAnimationFrame回调里驱动否则 act 会冲刷 effects 掩盖顺序。浏览器测试里不要直接用react-dom/client挂载——它会解析出第二份 React 副本并以Cannot read properties of null (reading useState)崩溃要用 RTL 的render并切换 act 标志。附带的环境约束与注意事项codemod 陷阱基于字符串字面量正则的自动改写会误伤!important出现在 CSS 文本串/测试里、事件名blur、测试标题与注释里的散文、${}嵌套字符串。防护规则跳过_(/it(/test(/describe(/toContain(之后的字面量跳过以:结尾的 token多 token 字符串要求含短横线 token单 token 字面量要求 className/clsx 上下文。截图 fixture 必须用应用主题用default-dark绝不用 daisyUI 原厂dark——其 base-content 在 v5 中变了annotation-popup 基线为此专门重新生成。E-ink 门控的变体类not-eink:loading-dots这类变体副本由 Tailwind 单独排序、单独匹配修复基础类选择器时必须显式覆盖变体副本参考回归 #4 的双选择器写法。真实级联测试优先modal-box与loading-dots两例都证明jsdom 看不到这类问题必须走浏览器测试vitest browser 模式加载真实编译后的样式表。迁移要点速查先查平台支持矩阵base-select/::picker()需要 Chromium 135iOS 上color-mix()16.2与property16.4决定底线见 tauri.conf.json 的minSupportedMajor: 111与 iOS 16.4。全部配置进 CSSimport tailwindcssplugincustom-varianttheme删掉tailwind.config.ts与 safelist。自定义规则一律layer utilities包裹顶替组件默认值的规则一律:where()零特异性。逐项钉回 v4 量感并用计算样式测试锁死按钮/输入框 48px、小号字号、checkbox 圆角、menu 宽度、toggle 轨道宽、loading-lg 40px。主题令牌统一发射器编译期与运行期共用themeVariables()--depth: 0关伪 3D--radius-*钉 v4 几何。select 的base-select适配不设appearance-none!、给::picker(select)上底色、optionwhite-space: nowrap、w-auto修右对齐。把哪条规则赢了变成可验证问题tailwindcss/cli渲染真实级联 计算样式断言 浏览器测试三件套。升级后专项回归清单toast 折叠、loading 尺寸/速度、dialog 关闭残留、modal-box独立底盘、rotate-*与内联 transform 组合、任意值括号剥离。仓库中完整的迁移记录、每处修复的源码注释与测试用例均可继续深入迁移笔记见 daisyui-v5-tailwind-v4-migration.md样式实现在 globals.css 与 themes.ts端到端守护见 daisyui-v5-tokens.browser.test.tsx 与 modal-box-standalone.browser.test.ts。赞分享桌面应用跨平台前端【免费下载链接】readestReadest is a modern, feature-rich ebook reader designed for avid readers offering seamless cross-platform access, powerful tools, and an intuitive interface to elevate your reading experience.项目地址https://gitcode.com/gh_mirrors/re/readest点击查看免费下载相关推荐daisyUI 如何从 4 升级到 5 并迁移到 Tailwind CSS 4 的 CSS 配置daisyUI 如何从 4 升级到 5 并迁移到 Tailwind CSS 4 的 CSS 配置 如果你的项目还在用 daisyUI 4通过 tailwind前端UI组件daisyUI 5 升级完全指南从 Tailwind CSS 3 迁移到 Tailwind CSS 4 的类名变更与实操手册daisyUI 5 升级完全指南从 Tailwind CSS 3 迁移到 Tailwind CSS 4 的类名变更与实操手册 daisyUI 5 是 dais前端UI组件VoiceStudio 前端视觉回归基线CSS → Tailwind v4 迁移的无后端快照保障体系VoiceStudio 前端视觉回归基线CSS → Tailwind v4 迁移的无后端快照保障体系 导读本文以 VoiceStudio 前端仓库中的 fr人工智能语音音频本地部署MCP 服务桌面应用上一篇Handsontable UndoRedo 插件完全指南撤销与重做机制、栈生命周期与自定义可撤销操作下一篇如何用tchMaterial-parser实现电子课本批量下载高效教育资源管理指南创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表