第十四章:CSS 样式方案与 Tailwind 集成
博客v1.0系列教程(Dioxus)博客 v1.0 系列教程 (Dioxus)
第十四章:CSS 样式方案与 Tailwind 集成
1. Dioxus 中的样式方式
Dioxus 为组件提供了多种样式方案:
| 方式 | 适用场景 | 特点 |
|------|---------|------|
| 内联 style | 动态样式、少量覆盖 | 直接绑定 Signal,响应式变化 |
| 类名 class | 静态样式、Tailwind | 配合 CSS 框架,批量管理 |
| document::Stylesheet | 全局样式文件 | 引入外部 CSS |
| CSS-in-RSX | 组件级样式 | 类似 styled-components |
1.1 内联样式
Dioxus 的内联样式支持两种写法:
// 方式一:字符串(推荐,支持动态值)
div {
style: "color: var(--text); background: {bg_color}; padding: {padding}px;",
"内容"
}
// 方式二:键值对(仅静态)
div {
color: "red",
background_color: "blue",
padding: "10px",
}
注意:键值对方式不支持动态值(不能插入
{signal}),如果需要条件样式,用字符串方式并拼接:
div {
style: "background: {if dark_mode() { \"#1a1a2e\" } else { \"#fff\" }}; color: {if dark_mode() { \"#fff\" } else { \"#000\" }};",
}
1.2 类名管理
// 静态类名
div { class: "flex items-center gap-4 p-4 rounded-lg" }
// 条件类名(字符串拼接)
div { class: "p-4 rounded-lg {if selected() { \"bg-blue-500 text-white\" } else { \"bg-gray-100\" }}" }
// 多条件类名(预计算)
let card_class = use_memo(move || {
let base = "rounded-lg border p-4 transition-all";
match (theme(), variant()) {
("dark", "primary") => format!("{base} bg-blue-900 border-blue-700"),
("light", "primary") => format!("{base} bg-blue-50 border-blue-200"),
("dark", _) => format!("{base} bg-gray-800 border-gray-700"),
_ => format!("{base} bg-white border-gray-200"),
}
});
div { class: "{card_class}" }
1.3 条件样式的三种模式
// 模式一:类名切换(推荐)
let btn = "px-4 py-2 rounded {if disabled() { \"opacity-50 cursor-not-allowed\" } else { \"hover:bg-blue-600 cursor-pointer\" }}";
// 模式二:内联 style 切换
button {
style: "background: {if danger() { \"#ef4444\" } else { \"#3b82f6\" }};",
}
// 模式三:use_memo 预计算完整样式字符串
let style = use_memo(move || {
format!(
"background:{}; color:{}; border:1px solid {};",
if dark() { "#1e1e2e" } else { "#fff" },
if dark() { "#cdd6f4" } else { "#4c4f69" },
if dark() { "#313244" } else { "#ccd0da" },
)
});
div { style: "{style}" }
2. Tailwind CSS 集成
2.1 CDN 方式(开发用)
在 index.html 中添加:
<script src="https://cdn.tailwindcss.com"></script>
这是项目当前使用的方式,适合开发和原型阶段。
2.2 编译方式(生产推荐)
# 安装 Tailwind CLI
npm install -D tailwindcss
# 初始化配置
npx tailwindcss init
// tailwind.config.js
module.exports = {
content: ["./src/**/*.rs", "./index.html"],
theme: { extend: {} },
plugins: [],
}
# 构建 CSS
npx tailwindcss -i ./input.css -o ./public/tailwind.css --watch
2.3 Tailwind 与 CSS 变量配合
/* main.css */
:root {
--bg: #eff1f5;
--card: #e6e9ef;
--border: #ccd0da;
--text: #4c4f69;
--primary: #1e66f5;
}
[data-theme="dark"] {
--bg: #1e1e2e;
--card: #181825;
--border: #313244;
--text: #cdd6f4;
--primary: #89b4fa;
}
在组件中使用:
div {
class: "rounded-lg border p-5",
style: "background: var(--card); border-color: var(--border);",
h3 { class: "font-semibold", style: "color: var(--secondary);", "标题" }
}
这样做的好处:Tailwind 处理布局和间距,CSS 变量处理颜色主题,分工清晰。
3. 响应式布局
3.1 Tailwind 断点
// 响应式网格
div { class: "grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6",
// 手机: 1列
// 平板: 2列
// 桌面: 3列
}
// 响应式三栏布局
div { class: "lg:grid lg:grid-cols-[220px_1fr_240px] lg:gap-8",
aside { class: "hidden lg:block", /* 侧边栏 */ }
main { /* 主内容 */ }
aside { class: "hidden lg:block", /* 右侧边栏 */ }
}
| 断点 | 前缀 | 最小宽度 |
|------|------|---------|
| 手机 | 默认 | 0 |
| 平板 | md: | 768px |
| 桌面 | lg: | 1024px |
| 宽屏 | xl: | 1280px |
3.2 响应式侧边栏模式
// 移动端:侧边栏作为抽屉
#[component]
fn ResponsiveLayout() -> Element {
let sidebar_open = use_signal(|| false);
rsx! {
div { class: "flex",
// 移动端遮罩
if sidebar_open() {
div {
class: "fixed inset-0 bg-black/50 z-40 lg:hidden",
onclick: move |_| sidebar_open.set(false),
}
}
// 侧边栏:移动端浮层 | 桌面端固定
aside {
class: "fixed lg:sticky top-0 left-0 h-full z-50
transform {if sidebar_open() { \"translate-x-0\" } else { \"-translate-x-full\" }}
lg:transform-none lg:w-60 w-72
transition-transform duration-300",
style: "background: var(--card);",
// 侧边栏内容
}
// 主内容
main { class: "flex-1 min-w-0",
// 移动端菜单按钮
button {
class: "lg:hidden p-2",
onclick: move |_| sidebar_open.set(true),
"☰"
}
}
}
}
}
4. 动画与过渡
4.1 CSS Transition
// Tailwind 过渡类
a {
class: "transition-colors duration-200 hover:text-blue-500",
}
button {
class: "transition-all duration-300 hover:scale-105 hover:shadow-lg",
}
div {
class: "transition-opacity duration-500 {if visible() { \"opacity-100\" } else { \"opacity-0\" }}",
}
4.2 动画进入/离开
#[component]
fn FadeIn(visible: bool, children: Element) -> Element {
rsx! {
div {
class: "transition-all duration-500 overflow-hidden",
style: "max-height: {if visible { \"500px\" } else { \"0\" }}; opacity: {if visible { \"1\" } else { \"0\" }};",
{children}
}
}
}
// 使用
FadeIn {
visible: expanded(),
div { class: "p-4", "折叠面板内容..." }
}
4.3 列表动画
// 列表项出现动画
for (i, article) in articles.iter().enumerate() {
div {
key: "{article.slug}",
class: "transition-all duration-300",
style: "animation: fadeInUp 0.3s ease-out {i * 50}ms both;",
ArticleCard { summary: article.clone() }
}
}
CSS 中定义关键帧:
@keyframes fadeInUp {
from { opacity: 0; transform: translateY(20px); }
to { opacity: 1; transform: translateY(0); }
}
5. CSS 变量主题系统深入
5.1 变量定义
:root {
/* 颜色 */
--bg: #eff1f5;
--card: #e6e9ef;
--border: #ccd0da;
--text: #4c4f69;
--secondary: #5c5f77;
--tertiary: #9ca0b0;
--primary: #1e66f5;
--primary-light: #d6e4fb;
--hover: #ccd0da;
/* 间距 */
--sidebar-width: 220px;
--header-height: 64px;
/* 字体 */
--font-mono: "SF Mono", "Fira Code", monospace;
}
[data-theme="dark"] {
--bg: #1e1e2e;
--card: #181825;
--border: #313244;
--text: #cdd6f4;
--secondary: #bac2de;
--tertiary: #6c7086;
--primary: #89b4fa;
--primary-light: #2a4a7a;
--hover: #313244;
}
5.2 组件级变量覆盖
div {
style: "
--card-bg: var(--card);
--card-border: var(--border);
background: var(--card-bg);
border-color: var(--card-border);
",
}
5.3 JavaScript 主题切换
function toggleTheme() {
var html = document.documentElement;
var next = html.getAttribute('data-theme') === 'dark' ? 'light' : 'dark';
html.setAttribute('data-theme', next);
localStorage.setItem('blog-theme', next);
}
6. 实战:三栏布局完整实现
use dioxus::prelude::*;
#[component]
fn ThreeColumnLayout(
left: Element,
center: Element,
right: Element,
) -> Element {
rsx! {
div { class: "min-h-screen flex flex-col",
style: "background: var(--bg); color: var(--text);",
// 顶部导航栏
Header {}
// 三栏主体
main { class: "flex-1",
div { class: "max-w-[1400px] mx-auto px-4 md:px-6 py-7
lg:grid lg:grid-cols-[220px_1fr_240px] lg:gap-8",
// 左侧栏
aside { class: "hidden lg:block",
div { class: "sticky top-24 max-h-[calc(100vh-8rem)] overflow-y-auto
space-y-5",
style: "scrollbar-width: thin; scrollbar-color: var(--border) transparent;",
{left}
}
}
// 主内容区
div { class: "min-w-0", {center} }
// 右侧栏
aside { class: "hidden lg:block",
div { class: "sticky top-24 max-h-[calc(100vh-8rem)] overflow-y-auto
space-y-5",
style: "scrollbar-width: thin; scrollbar-color: var(--border) transparent;",
{right}
}
}
}
}
Footer {}
}
}
}
7. 暗夜模式图片适配
/* 图片自动适配暗夜模式 */
.prose-content img {
filter: none;
transition: filter 0.3s;
}
[data-theme="dark"] .prose-content img {
filter: brightness(0.8) contrast(1.2);
}
/* 或者使用 picture 元素提供两套图 */
picture {
source {
srcset: "{dark_src}",
media: "(prefers-color-scheme: dark)",
}
img { src: "{light_src}" }
}
8. 小结
- Dioxus 支持内联
style和class两种样式方式 - 动态样式用字符串内联 + Signal,静态用 Tailwind 类名
- CSS 变量 +
data-theme是实现暗夜模式的最佳方案 - Tailwind 断点实现响应式布局,
hidden lg:block控制侧边栏显隐 - 用
transition-*类实现简单动画 - CSS 变量集中管理颜色主题,组件只引用
var(--xxx),解耦和维护性都好
dioxuscsstailwindstylingresponsive