11 分钟阅读
-- 次浏览
快速入门 Tailwind CSS

TIP

本文档基于Tailwind CSS v4编写, 如有疑问请参考官方文档.

什么是 Tailwind CSS

第一次看到 Tailwind 代码, 大多数人的反应应该都跟我一样: “class 里的这一坨是什么鬼?” 一个<div>上挂十几个类名, 怎么看都不像正经 CSS 的写法. 但真正上手之后, 你就会发现回不去了.

Tailwind CSS 是一个实用优先? 的 CSS 框架. 它并不会给你预制的按钮和卡片等组件, 而是提供一堆很细的工具类, 让你直接在 HTML 里拼出想要的样式.

举个具体的例子: 如果写一个系统通知卡片. 在传统 CSS 中 需要先想类名, 然后再去 CSS 文件里定义:

<div class="notification">
  <h2 class="notification-title">部署完成</h2>
  <p class="notification-msg">生产环境已更新至 v2.1.0</p>
</div>
.notification {
  padding: 1.5rem;
  border-radius: 0.5rem;
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
}
.notification-title {
  font-size: 1.25rem;
  font-weight: 700;
}
.notification-msg {
  color: #4b5563;
}

而如果你用的是 Tailwind, 同样的效果一行 HTML 搞定:

<div class="p-6 rounded-lg shadow">
  <h2 class="text-xl font-bold">部署完成</h2>
  <p class="text-gray-600">生产环境已更新至 v2.1.0</p>
</div>

渲染效果:

部署完成

生产环境已更新至 v2.1.0

不需要单独建 CSS 文件, 也不需要纠结类名到底要叫.notification还是.notice-card, 样式和结构直接写在一起, 改的时候也不用两个文件来回跳.

安装

v4 的安装体验比 v3 好了太多 — 不用再写tailwind.config.js, 不用配置content路径, 装上就能用. 以 Vite 项目为例:

npm install -D tailwindcss @tailwindcss/vite

vite.config.ts中添加插件:

import { defineConfig } from "vite";
import tailwindcss from "@tailwindcss/vite";

export default defineConfig({
  plugins: [tailwindcss()],
});

在 CSS 入口文件中引入 Tailwind:

@import "tailwindcss";

最后启动开发服务器, Tailwind 就绑定好了:

npm run dev

TIP

Tailwind CSS v4 起采用了全新的引擎, 安装方式比 v3 更简洁. 上面的步骤基于 v4. 如果你使用的是 v3, 请参考官方文档.

v4 会自动扫描项目中的模板文件来检测用到的工具类. 但如果你的项目有非标准路径 (比如 Astro 项目中 Markdown 文件里写的 class), 可能需要手动声明扫描范围:

@import "tailwindcss";
@source "../content/**/*.md";

核心概念

工具类优先

核心思路: 用预定义的工具类直接描述样式, 而不是先想个类名然后再去定义样式.

<!-- 全屏居中的错误提示 -->
<div class="flex items-center justify-center h-screen">
  <p class="text-2xl font-semibold text-red-500">404 - 页面走丢了</p>
</div>

每个 class 只做一件事: flex 开启弹性布局, items-center 垂直居中, justify-center 水平居中, h-screen 高度撑满视口.

响应式设计

我觉得 Tailwind 最省心的地方之一就是做响应式布局. 你不需要开一个单独的媒体查询块, 取而代之的是直接在 class 里用断点前缀. 它采用移动优先? 策略, 内置了这些响应式断点?:

前缀最小宽度对应场景
(无)0px手机
sm640px大屏手机
md768px平板
lg1024px笔记本
xl1280px桌面显示器
2xl1536px大屏显示器

其用法是断点前缀加冒号:

<!-- 手机单列, 平板双列, 桌面三列 -->
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
  <div class="bg-white p-4 rounded">卡片1</div>
  <div class="bg-white p-4 rounded">卡片2</div>
  <div class="bg-white p-4 rounded">卡片3</div>
</div>

状态变体

在传统 CSS 里, 写个:hover效果就得跑到 CSS 文件里加一条规则. Tailwind 把这些伪类状态也做成了前缀, 这套机制叫状态变体?:

<button class="bg-emerald-500 hover:bg-emerald-600 active:bg-emerald-700 text-white font-medium py-2 px-4 rounded-lg">
  确认提交
</button>

<input class="border border-gray-300 focus:border-indigo-500 focus:ring-2 focus:ring-indigo-200 rounded-lg px-3 py-2 outline-none" placeholder="搜索..." />

渲染效果 (在上面试试悬停和聚焦):

常用的状态前缀有:

  • hover: — 鼠标悬停
  • focus: — 获得焦点
  • active: — 点击中
  • disabled: — 禁用态
  • first: / last: — 首个 / 末个子元素
  • dark: — 暗色模式

暗色模式

对暗色模式的支持也是开箱即用的, 只要加个dark:前缀就完事. 本博客就用了 Tailwind 的暗色模式, 你现在看到的页面的背后就是它在工作:

<div class="bg-white dark:bg-gray-900 text-black dark:text-white p-6">
  冷知识: 这段内容会自动适配亮色和暗色主题.
</div>

渲染效果 (切换页面主题查看):

冷知识: 这段内容会自动适配亮色和暗色主题.

默认跟随系统prefers-color-scheme设置. v4 中如果需要手动切换 (比如通过 JS toggle), 可以在 CSS 中用@custom-variant定义暗色模式的触发方式:

/* 跟随 .dark 类切换, 而非系统偏好 */
@custom-variant dark (.dark &);

常用工具类速查

间距

间距类遵循4px基准制: 数值 x 4px = 实际像素.

<div class="p-4">   <!-- padding: 16px (4x4) -->
<div class="m-2">   <!-- margin: 8px (2x4) -->
<div class="px-6">  <!-- padding-left + right: 24px -->
<div class="mt-8">  <!-- margin-top: 32px -->
<div class="space-y-4"> <!-- 子元素间垂直间距16px -->

排版

<p class="text-sm">小字</p>         <!-- 14px -->
<p class="text-base">正文</p>       <!-- 16px -->
<p class="text-lg">稍大</p>         <!-- 18px -->
<p class="text-xl">标题级</p>       <!-- 20px -->
<p class="text-2xl">大标题</p>      <!-- 24px -->

<p class="font-light">细体</p>      <!-- 300 -->
<p class="font-normal">常规</p>     <!-- 400 -->
<p class="font-semibold">半粗</p>   <!-- 600 -->
<p class="font-bold">粗体</p>       <!-- 700 -->

<p class="leading-tight">紧凑行高</p>  <!-- 1.25 -->
<p class="leading-relaxed">宽松行高</p> <!-- 1.625 -->

颜色

颜色方面, 内置了一整套调色板, 每种颜色有 50 到 950 共 11 个深浅等级:

<p class="text-gray-500">灰色文字</p>
<div class="bg-blue-100">浅蓝背景</div>
<div class="border border-red-300">红色边框</div>

数值越小越浅, 越大越深. 500通常是该颜色的” 标准” 色调.

Flexbox 与 Grid

<!-- Flex水平排列, 间距, 垂直居中 -->
<div class="flex items-center gap-4">
  <img class="w-12 h-12 rounded-full" src="avatar.png" />
  <div>
    <p class="font-bold">用户名</p>
    <p class="text-gray-500 text-sm">描述文字</p>
  </div>
</div>

<!-- Grid三列等宽 -->
<div class="grid grid-cols-3 gap-6">
  <div>列1</div>
  <div>列2</div>
  <div>列3</div>
</div>

尺寸

<div class="w-full">    <!-- width: 100% -->
<div class="w-1/2">     <!-- width: 50% -->
<div class="h-screen">  <!-- height: 100vh -->
<div class="max-w-md">  <!-- max-width: 448px -->
<div class="min-h-0">   <!-- min-height: 0 -->
<img class="size-16" /> <!-- width + height: 64px -->

过渡与动画

<!-- 悬停放大的标签 -->
<span class="inline-block bg-violet-100 text-violet-700 px-3 py-1 rounded-full text-sm transition-transform duration-200 hover:scale-110">
  New
</span>

<!-- 内置动画 -->
<div class="animate-spin w-8 h-8 border-4 border-teal-500 border-t-transparent rounded-full"></div>
<div class="animate-pulse bg-gray-300 h-4 rounded"></div>
<div class="animate-bounce text-2xl">↓</div>

渲染效果:

New

常用的过渡类:

  • transition / transition-colors / transition-opacity / transition-transform — 指定参与过渡的属性
  • duration-150 / duration-300 / duration-500 — 过渡时长 (毫秒)
  • ease-in / ease-out / ease-in-out — 缓动函数

内置的动画: animate-spin(旋转), animate-ping(脉冲扩散), animate-pulse(呼吸闪烁), animate-bounce(弹跳).

自定义与扩展

任意值

内置工具类覆盖不到的场景, 可以用方括号写任意值:

<div class="w-34.25">精确宽度</div>
<div class="bg-[#1a1a2e]">自定义颜色</div>
<div class="grid-cols-[200px_1fr_200px]">自定义网格</div>
<div class="top-[calc(100vh-80px)]">计算值</div>
<div class="bg-(--my-color)">使用CSS变量</div>

主题配置

在 CSS 文件中使用@theme指令扩展设计系统:

@import "tailwindcss";

@theme {
  --color-brand: #6366f1;
  --font-display: "Inter", sans-serif;
}

之后就可以使用text-brand, bg-brand, font-display等工具类.

使用@apply提取组件

如果某组工具类在项目里出现了五六次以上, 每次都复制粘贴一长串 class 确实烦人. 这时候可以用@apply把它们收编成一个 CSS 类:

.tag {
  @apply inline-block px-3 py-1 rounded-full text-sm font-medium transition-colors;
}
.tag-info {
  @apply tag bg-sky-100 text-sky-700 hover:bg-sky-200;
}
.tag-warn {
  @apply tag bg-amber-100 text-amber-700 hover:bg-amber-200;
}

WARNING

当然, 不要一上来就@apply所有东西. 否则你又会绕回传统 CSS 的老路. 先想类名, 再定义样式. 只有那些真正在多个文件里反复出现的组合才值得提取, 其他情况直接正常写工具类就好.

实战: 构建一个 Profile 卡片

把上面的知识组合起来 — 就拿这个博客的主页 hero section 做原型, 复刻一个简约版:

<div class="max-w-md mx-auto bg-white dark:bg-gray-800 rounded-xl shadow-lg p-6">
  <div class="flex items-center gap-4">
    <img
      class="w-16 h-16 rounded-full border border-gray-200 dark:border-gray-700 shadow-sm object-cover"
      src="/avatar.jpg"
      alt="Catluo"
    />
    <div>
      <h2 class="text-lg font-bold text-gray-900 dark:text-white">Catluo</h2>
      <p class="text-sm text-gray-500 dark:text-gray-400">Talk is cheap. Show me the code.</p>
    </div>
  </div>
  <div class="flex flex-wrap gap-2 mt-4">
    <span class="px-2 py-0.5 text-xs font-medium border border-gray-200 dark:border-gray-700 rounded-full text-gray-600 dark:text-gray-400">Android</span>
    <span class="px-2 py-0.5 text-xs font-medium border border-gray-200 dark:border-gray-700 rounded-full text-gray-600 dark:text-gray-400">C/C++</span>
    <span class="px-2 py-0.5 text-xs font-medium border border-gray-200 dark:border-gray-700 rounded-full text-gray-600 dark:text-gray-400">LLVM</span>
    <span class="px-2 py-0.5 text-xs font-medium border border-gray-200 dark:border-gray-700 rounded-full text-gray-600 dark:text-gray-400">Reversing</span>
  </div>
  <div class="flex gap-3 mt-4">
    <a href="https://github.com/steveday763" class="text-gray-500 hover:text-gray-900 dark:hover:text-white transition-colors">
      GitHub →
    </a>
  </div>
</div>

渲染效果:

Catluo

Catluo

Talk is cheap. Show me the code.

Android C/C++ LLVM Reversing

在这个卡片中, 我们用到了:

  • Flexbox 布局 (flex, items-center, gap-4, flex-wrap)
  • 暗色模式 (dark:bg-gray-800, dark:text-white, dark:border-gray-700)
  • 状态变体 (hover:text-gray-900)
  • 过渡动画 (transition-colors)
  • 响应式宽度 (max-w-md, mx-auto)
  • 间距与排版 (各种p-, mt-, text-, font-类)

编辑器配置

VS Code

需要装两个扩展:

TIP

如果只装了 Tailwind IntelliSense 没装 PostCSS 插件, VS Code 会对@apply这些 Tailwind 指令标黄, 报” Unknown at rule” 警告. 装上 PostCSS Language Support 就好了.

Prettier

安装官方的 Prettier 插件自动排序 class 属性中的工具类:

npm install -D prettier prettier-plugin-tailwindcss

统一的类名顺序能显著提升团队协作时的可读性.

从传统 CSS 迁移所发生的心智转变

如果你写惯了传统 CSS 或 BEM 命名 ?, 第一次打开 Tailwind 项目多半会被一长串 class 吓到 — “这也太丑了吧”. 我当时也是这么想的. 但用了一阵之后, 你会有几个认知上的转变:

  1. 再也不用想类名了. 以前每次新建一个组件, 光是纠结叫.card-wrapper还是.card-container就能浪费好几分钟. 而现在直接用工具类描述样式, 这个心智负担就彻底消失了.

  2. 样式和结构写在一起, 其实是优势. 改一个组件的时候不用在 HTML 和 CSS 两个文件之间跳来跳去, 所有信息都在眼前.

  3. 删代码终于不用提心吊胆了. 传统 CSS 里删一个类名, 你永远不确定别的地方还有没有在用. Tailwind 的工具类是跟着元素走的, 删掉元素, 样式就跟着消失了, 不会留下死代码.

  4. 打包体积不是问题. Tailwind 构建时只打包实际用到的工具类, 最终 CSS 通常就几十 KB.

附录: v3 到 v4 迁移踩坑记录

如果你正在从 Tailwind CSS v3 迁移到 v4, 以下是我在实际项目中踩过的坑.

配置迁移对照

v4 采用CSS-first 配置, 不再需要tailwind.config.js:

v3 (JS 配置)v4 (CSS 指令)
tailwind.config.js迁移到 CSS (仍可通过@config加载 JS)
@tailwind base/components/utilities@import "tailwindcss"
plugins: [require('@tailwindcss/typography')]@plugin "@tailwindcss/typography"
darkMode: ["class"]@custom-variant dark (.dark &)
content: ["./src/**/*.{astro,md}"]@source "../**/*.{astro,md}"
theme.extend.fontFamily@theme { --font-sans: ...; }

踩坑 1: CSS Cascade Layer 优先级

v4 使用原生 CSS Cascade Layers 管理优先级, 层级从低到高:

@layer theme < base < components < utilities

如果你在@layer components中写了自定义样式, 它会被@layer utilities中的 typography 插件样式覆盖 — 即使你的选择器更具体.

解决方案: 把需要高优先级的自定义样式放在所有@layer之外 (成为 unlayered 样式), 它们的优先级最高.

踩坑 2: @apply prose展开行为变化

v4 中@apply prose展开 typography 插件的所有嵌套规则, 把.prose替换为目标选择器, 而不是简单地添加一个类名.

这意味着如果你这样写:

article {
  @apply prose;
}

v3 生成的 CSS 中, 子元素匹配的是.prose h2, .prose p等. 你的自定义样式.prose .footnotes能正常匹配.

v4 生成的 CSS 中, 子元素匹配的是article h2, article p等. 此时你写的.prose .footnotes不再匹配了, 因为 HTML 中的<article>元素根本没有prose类.

解决方案: 将所有.prose父选择器改为实际的元素选择器 (如article).

踩坑 3: @custom-variant dark与伪元素不兼容

@custom-variant dark (.dark &)在 v4 中会将选择器包裹在:is():where()中. 对于普通元素这没问题, 但对于伪元素 (::marker, ::after, ::before) 会生成无效 CSS:

/* 期望生成 */
.dark article li::marker { color: white; }

/* 实际生成的选择器会将 :where() 拼接到伪元素上, 产生无效CSS */
article li::marker:where(.dark, .dark *) { color: white; } /* 无效! */

浏览器会静默丢弃这些无效规则, 导致暗色模式下伪元素样式丢失, 且没有任何报错.

解决方案: 对包含伪元素的选择器, 不使用dark:变体, 而是手动写html.dark祖先选择器:

article li::marker {
  @apply text-black/50;
}
/* 手动处理暗色模式 */
html.dark article li::marker {
  @apply text-white/75;
}

CAUTION

这个问题在构建时和浏览器控制台都不会报错, 只会表现为暗色模式下某些样式” 消失”. 如果你迁移后发现暗色模式下列表标记, checkbox 勾选标记等样式丢失, 大概率是这个原因.

进一步学习

NOTE

文章作者: Catluo
文章链接: 快速入门 Tailwind CSS
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 授权协议。
转载请注明来源!