关于 web-styling-tailwind
面向使用 Tailwind CSS v4+ 的前端开发者,介绍以 CSS 配置主题、编写工具类和迁移旧版项目的模式。重点涵盖 v4 的指令、响应式样式及自定义变体。
主要功能
- 01用 CSS 的 @theme 配置主题
- 02通过 @utility 注册自定义工具类
- 03定义自定义变体
- 04编写响应式与容器查询样式
- 05提供 v3 到 v4 迁移指引
适用场景
- 01为新项目配置 Tailwind CSS v4
- 02将 v3 项目升级到 v4
- 03按视口或容器调整布局
- 04实现暗色模式和组件状态样式
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ agents-inc/ skills --skill web-styling-tailwind安装后在 Claude Code 中输入/web-styling-tailwind确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
metadata.yamlreference.mdexamples/advanced.mdcore.md详细介绍
Tailwind CSS v4+ 模式
快速指南: v4 在 CSS 中配置,而不是在 JavaScript 中:
@import "tailwindcss"取代三个@tailwind指令,@theme取代tailwind.config.js——其中的每个变量都会生成实用类 和 一个真正的 CSS 自定义属性。自定义实用类使用@utility创建,自定义变体使用@custom-variant创建;v3 插件 API 和@layer utilities不再接入变体系统。源文件会自动检测,因此不需要content数组。有几项默认值发生了变化:边框颜色为currentColor,ring为 1px,阴影和圆角的刻度各向前移动一级,并且重要性修饰符放在后面(flex!)。
详细资源:
- examples/core.md — 设置和源文件检测、响应式设计、三种暗色模式策略、状态变体、
@theme自定义 - examples/advanced.md —
@utility、@custom-variant、容器查询、3D 变换、@starting-style、遮罩、类组合 - reference.md —
@theme命名空间表、v3 到 v4 的差异、重命名的实用类、浏览器支持
适用路径
- 新项目 — 为你的打包工具安装软件包,添加
@import "tailwindcss",并将所有令牌放入@theme。从 examples/core.md 开始。 - 正在升级的 v3 项目 — 先运行
npx @tailwindcss/upgrade;它会转换配置、指令和大多数已重命名的实用类。reference.md 中的表格介绍了它未处理的内容;自定义插件必须手动改为@utility和@custom-variant——参见 examples/advanced.md。
<critical_requirements>
编写 Tailwind 代码前
在 CSS 中配置:@import "tailwindcss" 和 @theme。 v4 不会查找 tailwind.config.js,除非有 @config 指令指向它;三个 @tailwind 指令也已不存在。
使用 @utility 声明自定义工具类。 这样会将它们注册到变体系统中,因此 hover: 和 lg: 可以作用于这些工具类——@layer utilities 块生成的是普通类,无法添加任何变体前缀。
安装与打包工具匹配的包——@tailwindcss/vite、@tailwindcss/postcss、@tailwindcss/webpack 或 @tailwindcss/cli。tailwindcss 本身不再是 PostCSS 插件,而且供应商前缀和导入内联功能已经内置,因此可以移除 autoprefixer 和 postcss-import。
为 @theme 颜色指定宽色域值,例如 oklch()。 默认调色板使用 oklch,因此十六进制品牌色会被限制在 sRGB 色域内,与默认调色板中的颜色相比显得平淡。
明确指定边框和环的值。 v4 中边框默认使用 currentColor,环的默认宽度为 1px,因此从 v3 沿用的标记会渲染为文本颜色,环也会比预期更细。
</critical_requirements>
自动检测: @import "tailwindcss"、@theme、@theme inline、@utility、@custom-variant、@variant、@source、@reference、@slot、--value()、--modifier()、--spacing()、@tailwindcss/vite、@tailwindcss/postcss、@tailwindcss/cli、tailwind-merge、@container、dark:、group-hover:、peer-checked:、data-[state=…]:
适用范围:
- 直接在标记中使用工具类进行样式设置
- 在 CSS 中声明主题令牌,使其同时生成类和自定义属性
- 使用视口断点和容器查询实现响应式布局
- 通过媒体查询、类或数据属性实现深色模式
- 添加框架未内置的工具类和变体
- 将 v3 项目迁移到 v4
其他部分另行处理:
- 主题条目背后的令牌架构——值如何分层、命名,以及如何为其他平台生成。
@theme是令牌变成一个_类_的地方,而不是设计令牌集合的地方 - 如何选择主题、持久化主题,以及如何在首次绘制前应用主题——
dark变体决定主题的外观,而不是当前激活的是哪个主题 - 如何将组件的属性转换为类组合表——
cn()用于组合列表;列表的定义位置则是另一个问题 - 作用域内的手写样式表——
@reference让样式表内部可以访问主题,而作用域机制本身已在其他部分解决
实用优先,在 v4 中也采用原生 CSS。样式由标记中的单一用途类组合而成,这些类所表达的设计决策则保存在 CSS 中,而非 JavaScript 配置中。这意味着浏览器和构建流程都能读取主题,因为每个 @theme 条目除了生成类,还会生成一个自定义属性。
要牢记的就是这种双重作用。@theme 中的 --color-brand-500 既是任何样式表都能通过 var() 读取的令牌,也是 bg-brand-500、text-brand-500 及该命名空间中其他类的来源。v4 中许多更棘手的细节——为什么需要 inline、为什么 @theme 不能嵌套、为什么裸通道三元组会导致透明度修饰符失效——都源于同一个变量同时承担这两种用途。
<decision_framework>
决策框架
深色模式策略
Should the theme follow the operating system and nothing else?
|-- YES --> Nothing to configure. `dark:` already means prefers-color-scheme: dark.
|-- NO --> A user-facing toggle is needed. What carries the state?
|-- A class on <html> --> @custom-variant dark (&:where(.dark, .dark *))
|-- A data attribute on <html> --> @custom-variant dark (&:where([data-theme=dark], [data-theme=dark] *))
设计决策应该放在哪里
Should it produce a utility class?
|-- YES --> @theme. Reference another variable? Then @theme inline.
|-- NO --> Is it a whole declaration block reused across components?
|-- YES --> @utility, so variants can still prefix it
|-- NO --> :root, read directly with var() — no class is generated
响应什么?
The viewport --> sm: md: lg: breakpoint variants
The element's own container --> @container on the parent, then @sm: @md: on the child
Neither --> unprefixed utilities
</decision_framework>
核心模式
### 模式 1:CSS 优先的设置一次导入、一个主题块,再加上打包工具自带的插件。这里没有 content 数组——系统会自动检测源文件,并遵循 .gitignore。
@import "tailwindcss";
@theme {
--color-brand-500: oklch(0.7 0.15 250);
--font-display: "Satoshi", "sans-serif";
}
@source 可添加自动检测遗漏的路径(默认跳过 node_modules),@source not 可排除路径,而 @source inline("…") 可将运行时拼装、无法通过扫描找到的类加入白名单。@reference "../app.css" 可在作用域样式表中使用主题,同时不会重复输出主题。
完整代码:examples/core.md
模式 2:响应式设计
移动端优先:无前缀的工具类在所有尺寸下生效,而每个断点变体都会在超过对应宽度后添加覆盖样式。自定义断点是 @theme 条目。
<div
class="grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4"
></div>
@theme {
--breakpoint-3xl: 120rem;
}
可叠加变体来指定范围(md:max-lg:hidden);如果规则确实适用于某个宽度以下而非以上的区域,则使用 max-* 变体。处处使用 max-* 就是桌面端优先,这种做法需要在每个断点添加覆盖样式,而不是只添加一个。
完整代码:examples/core.md
模式 3:深色模式
dark: 默认遵循 prefers-color-scheme,无需配置。若要提供面向用户的切换开关,就需要覆盖该变体,让它改为读取类或属性。
@import "tailwindcss";
/* Class-based toggle */
@custom-variant dark (&:where(.dark, .dark *));
/* Or attribute-based */
@custom-variant dark (&:where([data-theme=dark], [data-theme=dark] *));
对于大面积样式,使用语义主题条目比给每个工具类都配一对 dark: 更好:组件只需标明作用,模式切换在样式表中处理一次即可。
@theme inline {
--color-surface: var(--surface);
}
:root {
--surface: oklch(0.99 0 0);
}
.dark {
--surface: oklch(0.15 0 0);
}
完整代码:examples/core.md
模式 4:状态变体
伪类变体用于设置元素自身的样式;group-* 根据祖先元素的状态设置子元素样式,peer-* 则根据兄弟元素的状态设置样式;data-[…] 根据组件设置的属性设置样式。
<div class="group rounded-lg p-4 hover:shadow-lg">
<h3 class="text-gray-900 group-hover:text-blue-600">Title</h3>
</div>
<input class="peer" />
<p class="hidden peer-invalid:block">Please enter a valid email</p>
<button data-state="active" class="data-[state=active]:border-b-2">Tab</button>
在存在嵌套的地方为组或同级元素命名(group/card、group-hover/card:),否则内层会悄悄响应外层的状态。not-* 用于反转条件——not-hover:opacity-75 可替代基础值加悬停覆盖样式的写法。
完整代码:examples/core.md
模式 5:使用 @theme 自定义主题
每个条目都会在其命名空间中生成工具类,同时生成一个自定义属性。--color-*: initial 会先清空一个命名空间,再应用你定义的内容;--*: initial 会清空整个默认主题。
@theme {
--color-brand-500: oklch(0.55 0.2 250);
--font-display: "Satoshi", sans-serif;
--radius-card: 1rem;
--shadow-card: 0 2px 8px oklch(0 0 0 / 0.08);
--ease-fluid: cubic-bezier(0.3, 0, 0, 1);
}
@theme inline 会将引用的变量解析到每个生成的工具类中,因此在 .dark 或 [data-theme] 上重新赋值的值也能应用到这些类。即使没有任何工具类使用对应的自定义属性,@theme static 也会输出这些属性。命名空间与工具类的对应表见 reference.md。
完整代码:examples/core.md
模式 6:使用 @utility 自定义工具类
@utility 会向变体系统注册一个工具类。末尾加上 -* 可使其成为函数式工具类,而 --value() 决定它接受哪些值——主题条目、裸值或方括号中的任意值,按级联顺序匹配。
@utility content-auto {
content-visibility: auto;
}
@utility tab-* {
tab-size: --value(--tab-size-*); /* a theme entry */
tab-size: --value(integer); /* a bare value: tab-4 */
tab-size: --value([integer]); /* arbitrary: tab-[6] */
}
--modifier() 用于处理可选的 /… 后缀;若省略修饰符,对应声明也会被省略。--spacing() 会将主题的间距单位相乘。
完整代码:examples/advanced.md
模式 7:使用 @custom-variant 自定义变体
简写形式接收一个选择器;块形式接收一组规则,并用 @slot 标记工具类自身声明插入的位置。
@custom-variant theme-ocean (&:where([data-theme="ocean"] *));
@custom-variant any-hover {
@media (any-hover: hover) {
&:hover {
@slot;
}
}
}
&:where() 会将变体的优先级保持为零,因此变体不会仅凭优先级胜出。在手写 CSS 中,@variant dark { … } 可应用已有变体,无需使用 @apply。
完整代码:examples/advanced.md
模式 8:容器查询
内置功能,无需插件。@container 会将元素设为查询容器,其后代上的 @sm:、@md: 会根据_该容器_的宽度而非视口宽度作出响应。
<div class="@container">
<div class="flex flex-col gap-4 @sm:flex-row @lg:gap-6">…</div>
</div>
在容器嵌套的地方为容器命名(@container/sidebar,然后使用 @sm/sidebar:)。@max-* 用于查询最大宽度,@min-md:@max-xl: 则用于查询范围。
完整代码:examples/advanced.md
模式 9:框架提供的现代 CSS
v4 将一组平台功能封装为普通工具类,因此它们可以与所有变体组合使用。
<div class="perspective-distant">
<div
class="transform-3d transition-transform hover:rotate-x-2 hover:rotate-y-3"
>
…
</div>
</div>
<div
popover="auto"
class="transition-all open:opacity-100 starting:open:opacity-0"
>
…
</div>
<textarea class="field-sizing-content"></textarea>
bg-linear-45 和 /oklch 支持倾斜渐变和感知插值;mask-* 支持渐变遮罩;text-shadow-* 支持文本阴影;pointer-coarse: 和 pointer-fine: 可根据输入设备调整控件大小。
完整代码:examples/advanced.md
模式 10:组合类名列表
cn() — 条件拼接并解决冲突 — 让组件可以公开一个 className,真正覆盖自身的工具类。
import { type ClassValue, clsx } from "clsx";
import { twMerge } from "tailwind-merge";
function cn(...inputs: ClassValue[]): string {
return twMerge(clsx(inputs));
}
// The caller's className goes last, so it wins the merge
className={cn("rounded-lg px-4 py-2", VARIANT_CLASSES[variant], className)}
与 @apply 相比,更推荐这种做法。@apply 会将工具类从标记中移到 CSS 层,因此比被替换的类更难覆盖。
完整代码:examples/advanced.md
<red_flags>
危险信号
不会报错、但不会产生任何结果的 v3 语法:
@tailwind base; @tailwind components; @tailwind utilities;— 这些指令已被移除。@import "tailwindcss"是唯一替代方案。- 没有
@config的tailwind.config.js— v4 不会查找它,因此文件看起来像已配置,但其中的设置都不会生效。 @layer utilities { .foo { … } }— 虽然会生成类,但变体系统看不到它,因此hover:foo和lg:foo不存在。- 将
tailwindcss用作 PostCSS 插件 — 已移至@tailwindcss/postcss。此外,autoprefixer和postcss-import现在也不再需要。 - 通过插件 API 使用
addVariant()— 已由@custom-variant取代。 !flex和bg-[--var]— 重要修饰符现在放在末尾(flex!),CSS 变量则使用圆括号(bg-(--var))。
会破坏主题的情况:
- 将
@theme嵌套在选择器或媒体查询中 — 它只能位于顶层,因为它生成的类会在构建时输出,不能根据运行时作用域进行条件化。随模式变化的值应放在:root和[data-theme]中,只有别名放在@theme中。 - 别名主题条目未加
inline— 生成的实用类携带链式引用,该引用会在主题条目声明处解析,而非类的使用处解析,因此[data-theme="dark"]中的重新赋值永远不会作用于它。没有构建警告,没有控制台消息,页面仍会保持浅色模式。 - 将裸通道三元组用作主题颜色(
0 0% 100%)— 不透明度修饰符会编译为color-mix(in oklab, var(--color-x) 50%, transparent),它需要真实颜色,因此该声明会被丢弃,只有带修饰符的实用类会失效。 - 在
:root和@theme中重复填写相同的值 — 同一个决策被复制两份,之后可能逐渐不一致,差异会体现为实用类样式与直接样式元素之间细微的颜色差别。 - 在
@theme中使用十六进制颜色 — 与默认的 oklch 调色板并列时,这些颜色会被限制在 sRGB 色域内。
看似回归的默认值变更:
- 单独使用
border时,颜色是currentColor,而非灰色。请指定颜色。 ring为 1px;v3 的默认值是ring-3。- 阴影、圆角和模糊级别都向后移动了一档:v3 的
shadow对应 v4 的shadow-sm,而 v3 的shadow-sm对应shadow-xs。 outline-none已变为outline-hidden。- 变体按从左到右的顺序叠加,遵循级联规则——与 v3 相反。
hover:仅在设备确实支持悬停时生效,因此触屏设备在点按后不再保持悬停状态。space-y-*和divide-*更改了选择器:使用 flex 或 grid 时,gap-*是更可预测的选择。
令人意外的行为:
- 自动源检测会跳过
node_modules,因此已安装的组件库中的类需要显式添加@source。 - 运行时拼接的类无法被检测到。
@source inline("…")可用作 safelist。 - Grid 模板值之间用下划线分隔,而非逗号:
grid-cols-[max-content_auto]。 - 在作用域样式表中使用
@apply前需要先添加@reference,否则主题不在作用域内。 - v4 需要 Safari 16.4+、Chrome 111+ 和 Firefox 128+;没有能够兼容更早版本浏览器的构建目标。
- 不支持 Sass、Less 和 Stylus——v4 本身就是预处理器。