Skip to content

Tailwind CSS - Utility-First 基本原理

Tailwind CSS:理解 Utility-First(工具优先)基础

Section titled “Tailwind CSS:理解 Utility-First(工具优先)基础”

Tailwind CSS 的核心原则是 Utility-First(工具优先)。这意味着你无需编写自定义 CSS 来为组件设置样式,而是通过直接在 HTML 中应用小型、单一用途的工具类(utility class)来构建设计。这种方法倾向于组合而非继承,从而实现快速且高度可定制的 UI 开发。

传统 CSS vs. Utility-First(工具优先)

Section titled “传统 CSS vs. Utility-First(工具优先)”

传统上,你会通过在单独的样式表(stylesheet)中编写自定义 CSS 选择器和规则来为组件设置样式。让我们看看如何用这种方法创建一个简单的进度跟踪器。

<!DOCTYPE html>
<html lang="en">
<head>
<style>
body {
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
background-color: #f1f5f9; /* slate-100 */
margin: 0;
font-family: sans-serif;
}
.card {
width: 100%;
max-width: 800px;
padding: 24px;
background-color: #ffffff;
border-radius: 12px;
box-shadow: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1);
}
.description {
text-align: center;
margin-bottom: 32px;
color: #334155; /* slate-700 */
}
.tracker {
display: flex;
align-items: center;
justify-content: space-between;
position: relative;
}
/* The progress bar line */
/* 进度条线 */
.tracker::before {
content: '';
position: absolute;
top: 50%;
left: 0;
right: 0;
height: 4px;
background-color: #e2e8f0; /* slate-200 */
transform: translateY(-50%);
z-index: 1;
}
.tracker-progress {
position: absolute;
top: 50%;
left: 0;
height: 4px;
width: 50%; /* (2 of 4 steps is 50%) (4步中的2步是50%) */
background-color: #22c55e; /* green-500 */
transform: translateY(-50%);
z-index: 2;
transition: width 0.3s ease;
}
.step {
position: relative;
z-index: 3;
text-align: center;
}
.step-icon {
width: 48px;
height: 48px;
border-radius: 9999px; /* 50% */
color: #ffffff;
display: flex;
align-items: center;
justify-content: center;
margin: 0 auto 8px;
font-size: 20px;
font-weight: 600;
border: 4px solid #ffffff;
}
.step.completed .step-icon {
background-color: #22c55e; /* green-500 */
}
.step.current .step-icon {
background-color: #3b82f6; /* blue-500 */
}
.step.upcoming .step-icon {
background-color: #94a3b8; /* slate-400 */
}
.step-label {
font-size: 14px;
color: #475569; /* slate-600 */
}
</style>
</head>
<body>
<div class="card">
<div class="description">
<p>Follow your progress through the setup stages.</p>
</div>
<div class="tracker">
<div class="tracker-progress"></div>
<div class="step completed">
<div class="step-icon">✓</div>
<span class="step-label">Profile</span>
</div>
<div class="step current">
<div class="step-icon">2</div>
<span class="step-label">Billing</span>
</div>
<div class="step upcoming">
<div class="step-icon">3</div>
<span class="step-label">Security</span>
</div>
<div class="step upcoming">
<div class="step-icon">4</div>
<span class="step-label">Done</span>
</div>
</div>
</div>
</body>
</html>

这种方法可行,但也有缺点。你需要不断地在 HTML 和 CSS 之间切换上下文,你必须创建类名(例如 .card、.step-icon),并且你的 CSS 文件会随着每个新组件的增加而膨胀。

现在,让我们使用 Tailwind CSS 的 Utility-First(工具优先)方法构建相同的组件。

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<!-- For demonstration purposes only. Use the CLI for production. -->
<!-- 仅用于演示目的。生产环境请使用 CLI。 -->
<script src="https://cdn.tailwindcss.com"></script>
<title>Tailwind Progress Tracker</title>
</head>
<body class="flex min-h-screen items-center justify-center bg-slate-100 font-sans">
<div class="w-full max-w-3xl rounded-xl bg-white p-6 shadow-lg">
<div class="mb-8 text-center">
<p class="text-lg text-slate-700">Follow your progress through the setup stages.</p>
</div>
<!-- Progress Tracker -->
<!-- 进度跟踪器 -->
<div class="relative">
<!-- Gray background line -->
<!-- 灰色背景线 -->
<div class="absolute left-0 top-1/2 h-1 w-full -translate-y-1/2 bg-slate-200"></div>
<!-- Green progress line -->
<!-- 绿色进度线 -->
<div class="absolute left-0 top-1/2 h-1 w-1/2 -translate-y-1/2 bg-green-500 transition-all duration-300"></div>
<!-- Steps -->
<!-- 步骤 -->
<div class="relative z-10 flex justify-between">
<!-- Step 1: Completed -->
<!-- 步骤 1:已完成 -->
<div class="text-center">
<div class="flex h-12 w-12 items-center justify-center rounded-full border-4 border-white bg-green-500 text-xl font-semibold text-white">✓</div>
<span class="mt-2 block text-sm text-slate-600">Profile</span>
</div>
<!-- Step 2: Current -->
<!-- 步骤 2:当前 -->
<div class="text-center">
<div class="flex h-12 w-12 items-center justify-center rounded-full border-4 border-white bg-blue-500 text-xl font-semibold text-white">2</div>
<span class="mt-2 block text-sm text-slate-600">Billing</span>
</div>
<!-- Step 3: Upcoming -->
<!-- 步骤 3:待进行 -->
<div class="text-center">
<div class="flex h-12 w-12 items-center justify-center rounded-full border-4 border-white bg-slate-400 text-xl font-semibold text-white">3</div>
<span class="mt-2 block text-sm text-slate-600">Security</span>
</div>
<!-- Step 4: Upcoming -->
<!-- 步骤 4:待进行 -->
<div class="text-center">
<div class="flex h-12 w-12 items-center justify-center rounded-full border-4 border-white bg-slate-400 text-xl font-semibold text-white">4</div>
<span class="mt-2 block text-sm text-slate-600">Done</span>
</div>
</div>
</div>
</div>
</body>
</html>

在 Tailwind 示例中,样式是通过组合工具类来实现的:

  • 布局与尺寸: flex、items-center、justify-between 创建 Flexbox 布局。w-full、max-w-3xl、h-12、w-12 控制元素尺寸。
  • 间距: p-6 为卡片内部添加内边距(padding),而 mb-8 和 mt-2 控制外边距(margins)。
  • 颜色: bg-slate-100、bg-white、text-slate-700、bg-green-500 应用来自一致预定义调色板的背景色和文本颜色。
  • 排版: font-sans、text-lg、text-center、font-semibold 控制文本外观。
  • 边框与阴影: rounded-xl、rounded-full 创建圆角。shadow-lg 应用预配置的盒阴影(box shadow)。
  • 定位与变换: relative、absolute、-translate-y-1/2 用于精确分层进度条。
  • 状态管理: 此示例是静态的,但添加状态非常简单。例如,按钮可以使用 hover:bg-blue-600 在悬停时改变颜色,并使用 focus-visible:ring-2 focus-visible:ring-blue-500 实现可访问的焦点环。

为何采用 Utility-First(工具优先)?

Section titled “为何采用 Utility-First(工具优先)?”
  • 提高开发速度: 直接在 HTML 中设置样式,无需切换文件或创建类名。这显著加快了开发周期。
  • 内置设计系统: 工具类并非随机值;它们来自一个可配置的设计系统。这确保了整个项目在间距、颜色和尺寸上的一致性。
  • 性能: Tailwind 的即时编译(Just-In-Time, JIT)编译器会扫描你的文件并只生成你实际使用的 CSS,从而产生极小的生产环境包。
  • 默认响应式: 使用屏幕尺寸前缀,如 md:flex 或 lg:text-xl,直观地构建完全响应式的界面。
  • 杜绝全局 CSS 副作用: 改变一个元素的样式不会意外破坏另一个。样式是局部作用于它们所应用的元素。

这是一个常见的初次印象,但工具类在本质上比内联 style="..." 属性更强大。原因如下:

  • 设计约束: 内联样式使用“魔法数字”(style="font-size: 15px")。Tailwind 工具类使用设计系统中预定义的标记(text-base),确保一致性。
  • 响应式设计: 内联样式无法使用媒体查询(media queries)。Tailwind 的响应式前缀(md:、lg:)对于现代 Web 开发至关重要。
  • 状态变体: 内联样式无法处理 :hover、:focus 或 :disabled 等状态。Tailwind 为所有这些以及更多状态提供了变体,例如 hover:bg-blue-600 或 disabled:opacity-50。
<!-- Example of state variants, which are impossible with inline styles -->
<!-- 状态变体示例,内联样式无法实现 -->
<button
class="rounded-lg bg-indigo-600 px-4 py-2 font-bold text-white transition-colors hover:bg-indigo-700 focus:outline-none focus-visible:ring-2 focus-visible:ring-indigo-500 focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:bg-slate-400">
Interactive Button
</button>

另一个担忧是,冗长的类名字符串可能会变得重复且难以管理。现代开发实践优雅地解决了这个问题。

  • 基于组件的框架: 在 React、Vue 或 Svelte 等框架中,你将标记(markup)和样式封装到可重用组件中。你可以在组件文件中定义一次冗长的类名字符串,然后随处重用该组件。这是推荐的方法。
  • 使用 @apply(谨慎使用): 对于常见的模式,你可以在 CSS 文件中使用 Tailwind 的 @apply 指令将工具类组合成一个新的类。这对于在不使用组件框架的项目中,如按钮或表单输入等常见元素非常有用。
/* In your input.css file */
/* 在你的 input.css 文件中 */
@tailwind base;
@tailwind components;
@tailwind utilities;
@layer components {
.btn-primary {
@apply rounded-lg bg-indigo-600 px-4 py-2 font-bold text-white transition-colors hover:bg-indigo-700 disabled:bg-slate-400;
}
}
<!-- In your HTML -->
<!-- 在你的 HTML 中 -->
<button class="btn-primary">Click Me</button>
<button class="btn-primary" disabled>Cannot Click</button>

通过采纳基于组件的架构,并在适当的时候使用 @apply 等功能,Tailwind CSS 项目即使在大规模下也能保持高度可维护性。