DaisyUI Swap 组件完全指南:用纯 CSS 实现开关切换、图标过渡与主题控制器
DaisyUI 的 Swap 组件是一个极简却强大的"双元素显隐切换器":它利用 HTML <label> 与隐藏的 <input type="checkbox">,在无任何 JavaScript 的情况下,即可在"开/关"两种状态间切换两个子元素(文本、图标、emoji 均可),并提供 swap-rotate(旋转)与 swap-flip(翻转)两种过渡动效。本文将以 Swap 官方文档/components/swap/+page.md) 为主线,结合 swap.css 源码 与配套 Skill 文档,带你从零掌握该组件的全部类名、交互原理与真实落地场景(含与 theme-controller 结合的暗色主题切换方案)。
适用前提:Swap 是 DaisyUI 开箱即用的组件,只需在 Tailwind 配置中引入 DaisyUI 插件,即可直接在任意 HTML 模板中使用;以下所有示例均默认可复制运行。
Swap 是什么
Swap 允许你通过一个 checkbox 或一个类名来切换两个元素的可见性。组件本体是一段语义化、轻量的结构:
<label class="swap">
<input type="checkbox" autocomplete="off" />
<div class="swap-on">ON</div>
<div class="swap-off">OFF</div>
</label>
点击后会看到 swap-on 与 swap-off 两个子元素在"显示/隐藏"之间互换。整个交互零 JavaScript——状态机完全由浏览器原生的 :checked 伪类驱动,这也是它与 Collapse、Dropdown 等组件一脉相承的设计理念。
完整类名速查表
Swap 的类名体系可以分为四类,对应文档 frontmatter 中定义的 classnames:
| 分类 | 类名 | 作用 |
|---|---|---|
| component | swap |
Swap 容器,声明一个 swap 结构 |
| part | swap-on |
checkbox 被勾选 / swap 处于激活态时应显示的子元素 |
| part | swap-off |
checkbox 未被勾选 / swap 处于非激活态时应显示的子元素 |
| part | swap-indeterminate |
checkbox 处于 indeterminate(不确定)状态时应显示的子元素 |
| modifier | swap-active |
直接激活 swap(无需 checkbox),配合 JS 增删该类名使用 |
| style | swap-rotate |
为 swap 增加旋转过渡效果 |
| style | swap-flip |
为 swap 增加翻转(3D 翻转)过渡效果 |
基本约束(见 skills/daisyui/components/swap.md):
- 结构上,一个 modifier(
swap-active)可选,一个 style(swap-rotate/swap-flip)可选; - 要么用一个隐藏 checkbox 控制状态,要么用 JS 增删
swap-active类名控制状态; - 需要展示 checkbox
indeterminate状态的独立内容时,使用swap-indeterminate。
核心原理:为何不需要 JavaScript
打开 swap.css 源码,可以看到组件的关键实现全部是选择器层面的状态表达。容器本身使用 CSS Grid 将子元素重叠定位:
.swap {
@layer daisyui.l1.l2 {
@apply relative inline-grid cursor-pointer place-content-center align-middle select-none;
input {
@apply appearance-none;
border: none;
}
> * {
@apply col-start-1 row-start-1; /* 所有直接子元素堆叠在同一个网格单元 */
}
}
}
而"谁显示、谁隐藏"由兄弟选择器决定:
- 默认隐藏
swap-on、swap-indeterminate:.swap-on, .swap-indeterminate, input:indeterminate ~ .swap-on { opacity: 0 } - checkbox 勾选或处于 indeterminate 时隐藏
swap-off:input:is(:checked, :indeterminate) ~ .swap-off { opacity: 0 } - checkbox 勾选时显示
swap-on:input:checked ~ .swap-on { opacity: 100 }
由于两个子元素都处于同一网格单元(col-start-1 row-start-1),并通过 opacity 完成显隐(配合 pointer-events/过渡时间自然平滑切换),因此点击整块 label 的任意位置即可触发——label 与内部 checkbox 的隐式关联让整个容器都成了可点击区域。子元素自带 transition-property: transform, rotate, opacity; transition-duration: 0.2s(且仅在 prefers-reduced-motion: no-preference 时启用,尊重系统减弱动态效果设置)。
源码说明:该规则同时支持
:indeterminate状态(需要少量 JS 设置 checkbox 的.indeterminate = true),此时显示swap-indeterminate内容而不是swap-off。
在源码中的定位与结构
Swap 的样式定义位于组件目录 packages/daisyui/src/components/swap.css,其产物会进入 DaisyUI 的 daisyui.l1 / daisyui.l1.l2 级联层(.swap 本体落在 daisyui.l1.l2,修饰与动效类落在 daisyui.l1),确保与其他组件共存时优先级可控。需要为项目生成独立主题/产物时,它与其他组件一样遵循统一的打包流程(参见 packages/daisyui/package.json 及 functions 目录 中的 CSS 编译工具链)。
六种实战用法逐一解析
下面按官方文档的顺序,覆盖 Swap 最常见的全部应用场景。示例源码位于 components/swap 文档页/components/swap/+page.md),可直接对照查看。
1. 文本切换(Swap text)
最基础的用法,两个子元素直接放文本:
<label class="swap">
<input type="checkbox" autocomplete="off" />
<div class="swap-on">ON</div>
<div class="swap-off">OFF</div>
</label>
要点:swap-on 的文本在勾选后可见,swap-off 的文本在未勾选时可见。输入框不需要额外样式,因为 .swap input 已被 appearance-none + 无边框隐藏,但它必须保留在结构中作为状态载体。
2. 音量图标切换(Swap volume icons)
在实际 UI 里,swap-on / swap-off 放 SVG 图标是最常见组合。示例使用两个内联 SVG(有声音量喇叭与静音喇叭),外层用 fill-current 让图标跟随文字颜色:
<label class="swap">
<!-- this hidden checkbox controls the state -->
<input type="checkbox" />
<!-- volume on icon -->
<svg
class="swap-on fill-current"
xmlns="http://www.w3.org/2000/svg"
width="48"
height="48"
viewBox="0 0 24 24">
<path d="M14,3.23V5.29C16.89,6.15 19,8.83 19,12C19,15.17 16.89,17.84 14,18.7V20.77C18,19.86 21,16.28 21,12C21,7.72 18,4.14 14,3.23M16.5,12C16.5,10.23 15.5,8.71 14,7.97V16C15.5,15.29 16.5,13.76 16.5,12M3,9V15H7L12,20V4L7,9H3Z" />
</svg>
<!-- volume off icon -->
<svg
class="swap-off fill-current"
xmlns="http://www.w3.org/2000/svg"
width="48"
height="48"
viewBox="0 0 24 24">
<path d="M3,9H7L12,4V20L7,15H3V9M16.59,12L14,9.41L15.41,8L18,10.59L20.59,8L22,9.41L19.41,12L22,14.59L20.59,16L18,13.41L15.41,16L14,14.59L16.59,12Z" />
</svg>
</label>
官方示例中为 .swap-on 加了 fill-current,这样切到不同主题(如 data-theme 变体)时图标颜色能自动适配前景色。
3. 旋转过渡:太阳/月亮主题开关(rotate effect)
给容器追加 swap-rotate,两个状态会以旋转方式过渡。太阳图标位于 swap-on,月亮图标位于 swap-off,这是官网暗色主题切换按钮的原型:
<label class="swap swap-rotate">
<!-- this hidden checkbox controls the state -->
<input type="checkbox" />
<!-- sun icon -->
<svg class="swap-on h-10 w-10 fill-current" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<path d="M5.64,17l-.71.71a1,1,0,0,0,0,1.41,1,1,0,0,0,1.41,0l.71-.71A1,1,0,0,0,5.64,17ZM5,12a1,1,0,0,0-1-1H3a1,1,0,0,0,0,2H4A1,1,0,0,0,5,12Zm7-7a1,1,0,0,0,1-1V3a1,1,0,0,0-2,0V4A1,1,0,0,0,12,5ZM5.64,7.05a1,1,0,0,0,.7.29,1,1,0,0,0,.71-.29,1,1,0,0,0,0-1.41l-.71-.71A1,1,0,0,0,4.93,6.34Zm12,.29a1,1,0,0,0,.7-.29l.71-.71a1,1,0,1,0-1.41-1.41L17,5.64a1,1,0,0,0,0,1.41A1,1,0,0,0,17.66,7.34ZM21,11H20a1,1,0,0,0,0,2h1a1,1,0,0,0,0-2Zm-9,8a1,1,0,0,0-1,1v1a1,1,0,0,0,2,0V20A1,1,0,0,0,12,19ZM18.36,17A1,1,0,0,0,17,18.36l.71.71a1,1,0,0,0,1.41,0,1,1,0,0,0,0-1.41ZM12,6.5A5.5,5.5,0,1,0,17.5,12,5.51,5.51,0,0,0,12,6.5Zm0,9A3.5,3.5,0,1,1,15.5,12,3.5,3.5,0,0,1,12,15.5Z" />
</svg>
<!-- moon icon -->
<svg class="swap-off h-10 w-10 fill-current" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<path d="M21.64,13a1,1,0,0,0-1.05-.14,8.05,8.05,0,0,1-3.37.73A8.15,8.15,0,0,1,9.08,5.49a8.59,8.59,0,0,1,.25-2A1,1,0,0,0,8,2.36,10.14,10.14,0,1,0,22,14.05,1,1,0,0,0,21.64,13Zm-9.5,6.69A8.14,8.14,0,0,1,7.08,5.22v.27A10.15,10.15,0,0,0,17.22,15.63a9.79,9.79,0,0,0,2.1-.22A8.11,8.11,0,0,1,12.14,19.73Z" />
</svg>
</label>
尺寸可通过 Tailwind 的 h-* / w-* 工具类(如 h-10 w-10)控制;若省略会默认使用文档示例中的固定 width/height 或随容器尺寸。
4. 汉堡按钮:菜单开关(Hamburger button)
Swap 常与 Button 组件叠加,做成"汉堡/关闭"二象按钮,非常适合移动端导航栏。容器同时叠加 btn btn-circle swap swap-rotate:
<label class="btn btn-circle swap swap-rotate">
<!-- this hidden checkbox controls the state -->
<input type="checkbox" />
<!-- hamburger icon(未勾选时显示) -->
<svg class="swap-off fill-current" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 512 512">
<path d="M64,384H448V341.33H64Zm0-106.67H448V234.67H64ZM64,128v42.67H448V128Z" />
</svg>
<!-- close icon(勾选后显示) -->
<svg class="swap-on fill-current" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 512 512">
<polygon points="400 145.49 366.51 112 256 222.51 145.49 112 112 145.49 222.51 256 112 366.51 145.49 400 256 289.49 366.51 400 400 366.51 289.49 256 400 145.49" />
</svg>
</label>
btn btn-circle 提供圆形按钮的视觉与点击反馈,Swap 负责图标的旋转替换,两者职责分离、互不干扰。要注意子元素在 btn 中的布局:官方示例确保两个 SVG 处于堆叠网格单元,因此即便图标被旋转也始终居中于按钮。
5. 翻转过渡:表情切换(flip effect)
swap-flip 提供类似卡片翻面的 3D 过渡效果。配合任意内容(这里是大号 emoji),就能做一个趣味开关:
<label class="swap swap-flip text-9xl">
<!-- this hidden checkbox controls the state -->
<input type="checkbox" />
<div class="swap-on">😈</div>
<div class="swap-off">😇</div>
</label>
text-9xl 等字号工具类可任意放大文本/emoji 内容。
6. 不依赖点击:用类名激活(Activate using class name)
若你的触发逻辑并不来自 checkbox(比如由其他组件的状态、滚动监听或 JS 逻辑驱动),可以把容器换成 div,并交给 swap-active 类名控制——看到 swap-active 就显示 swap-on,没有就显示 swap-off:
<!-- 默认状态:显示 swap-off(🥶) -->
<label class="swap text-6xl">
<div class="swap-on">🥵</div>
<div class="swap-off">🥶</div>
</label>
<!-- 激活状态:显示 swap-on(🥳),只要存在 swap-active 类即可 -->
<label class="swap swap-active text-6xl">
<div class="swap-on">🥳</div>
<div class="swap-off">😭</div>
</label>
这一模式的核心是"你可以在任意时刻用 JS 增删 swap-active 类名"来切换状态,适合那些把切换动作绑定到其他控件上的交互(例如点击一个外部按钮去控制某个 swap)。由于无需 checkbox,这里也可以使用 div 作为容器。
对应的 CSS 逻辑(见 swap.css)同样处理了与两个动效类的组合:
.swap-active {
@layer daisyui.l1 {
.swap-off { @apply opacity-0; }
.swap-on { @apply opacity-100; }
&.swap-rotate .swap-on { @apply rotate-0; }
&.swap-rotate .swap-off { @apply -rotate-45; }
&.swap-flip .swap-on { transform: rotateY(0deg); }
&.swap-flip .swap-off { transform: rotateY(-180deg); backface-visibility: hidden; @apply opacity-100; }
}
}
因此无论走 checkbox 还是 swap-active,两种激活路径最终都收敛到同一套视觉结果。
旋转与翻转的底层差异
虽然 swap-rotate 与 swap-flip 表面都是"切换时有动画",但源码实现截然不同,理解后能帮你正确选型:
swap-rotate(swap.css):纯 2D 旋转。默认swap-on先处于rotate-45,勾选/激活后旋转到rotate-0显示;swap-off在激活后旋转到-rotate-45隐藏。实现基于 Tailwind 的rotate工具类 + 外层容器统一的transition-property: transform, rotate, opacity。swap-flip(swap.css):真正的 3D 翻面。容器开启transform-style: preserve-3d; perspective: 20rem;默认swap-on位于rotateY(180deg)(背对读者),勾选后转到rotateY(0deg),而swap-off从 0 转到-180deg,配合backface-visibility: hidden制造"卡片翻面"。
总结:追求 360° 旋转图标(太阳↔月亮)选 swap-rotate;追求"牌面翻转"(正反两面)选 swap-flip。
实战联动:用 Swap 做一个暗色主题切换按钮
Swap 最常见的真实场景是作为 theme-controller 的载体。在 theme-controller 文档页/components/theme-controller/+page.md) 中可以看到完整用法——它本质上是一个使用 theme-controller 类名的 checkbox,配合 data-theme 值就能直接切换整个页面的主题:
<label class="swap swap-rotate">
<!-- 同时充当主题控制器:勾选时切到 dark,取消时切回 light -->
<input type="checkbox" class="theme-controller" value="dark" />
<svg class="swap-on h-10 w-10 fill-current" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<!-- 月亮(dark 主题下的显示) -->
<path d="M21.64,13a1,1,0,0,0-1.05-.14...Z" />
</svg>
<svg class="swap-off h-10 w-10 fill-current" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<!-- 太阳(light 主题下的显示) -->
<path d="M5.64,17l-.71.71..." />
</svg>
</label>
theme-controller 的具体行为与主题机制见 theme-controller 文档/components/theme-controller/+page.md) 与 Themes 文档/docs/themes/+page.md)。这种"一个 checkbox 同时控制显隐与主题"的组合,正是 Swap 零 JS 设计的最佳落地。
进阶使用与细节建议
- 状态持久化需要少量 JS:Swap 本身不记忆状态。刷新页面后 checkbox 回到默认未勾选。若需持久化(如记住用户选择的主题),可在
change事件里用 localStorage/Cookie 记录checkbox.checked,页面加载时回填checked即可。 - 不要放两个 checkbox:一个 swap 只能有一个控制状态的 checkbox,其余子元素都是纯展示内容。
- 与动效类的顺序无关:
swap-rotate、swap-flip、swap-active可以任意叠加(如swap swap-active swap-flip),源码中的嵌套规则已分别处理&.swap-rotate、&.swap-flip组合。 - 配合尺寸工具类:容器和内容尺寸可完全由 Tailwind 工具类接管(
w-10 h-10、text-6xl、btn btn-circle等),无需改动组件默认样式。 - 使用自定义前缀时:如果配置了 DaisyUI 的
prefix选项,所有类名(含swap、swap-on等)都需要加前缀。官方文档示例中以$$swap占位符表示"前缀将在此替换",实际书写时替换为你配置的前缀或直接去掉。 - 无障碍:官方示例在 checkbox 上加
autocomplete="off",避免浏览器误填充干扰状态;在真实表单/导航语境中建议补充适当的aria-label(例如"切换菜单"),让辅助技术用户能理解这个 label 的语义。
小结
Swap 组件把"状态存储"交给浏览器原生的 checkbox、把"状态样式"交给 CSS 选择器,从而在无脚本的前提下覆盖了文本切换、图标切换、旋转/翻转动效、按钮化与主题切换等绝大多数开关型交互需求。掌握本文列出的三个 part 类名(swap-on、swap-off、swap-indeterminate)、一个 modifier(swap-active)和两个 style(swap-rotate、swap-flip),再对照 swap.css 理解其兄弟选择器机制,你便可以在任何 DaisyUI 项目中高效地搭建"轻量无 JS"的状态切换 UI。
相关资源:
- Swap 官方组件文档/components/swap/+page.md)
- Swap 组件源码(swap.css)
- DaisyUI 组件 Skill 速查(含规则说明)
- theme-controller + Swap 主题切换示例/components/theme-controller/+page.md)
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0629
MiniCPM5-2BMiniCPM5-2B 是一款面向端侧、本地部署和资源受限场景的 2B 稠密 Transformer,能够达到同尺寸开源模型 SOTA 水平。Markdown00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
HivisionIDPhotos⚡️HivisionIDPhotos: a lightweight and efficient AI ID photos tools. 一个轻量级的AI证件照制作算法。Python07
DragonOSDragonOS is an operating system developed from scratch using Rust, with Linux compatibility. It is designed for **Serverless** scenarios. 使用Rust从0自研内核,具有Linux兼容性的操作系统,面向云计算Serverless场景而设计。Rust00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00