mcpField
W
web-styling-tailwind

web-styling-tailwind

github.com/agents-inc设计与 UI26119 次安装2 天前更新
代码仓库

关于 web-styling-tailwind

面向使用 Tailwind CSS v4+ 的前端开发者,介绍以 CSS 配置主题、编写工具类和迁移旧版项目的模式。重点涵盖 v4 的指令、响应式样式及自定义变体。

主要功能

  1. 01用 CSS 的 @theme 配置主题
  2. 02通过 @utility 注册自定义工具类
  3. 03定义自定义变体
  4. 04编写响应式与容器查询样式
  5. 05提供 v3 到 v4 迁移指引

适用场景

  1. 01为新项目配置 Tailwind CSS v4
  2. 02将 v3 项目升级到 v4
  3. 03按视口或容器调整布局
  4. 04实现暗色模式和组件状态样式

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/agents-inc/skills --skill web-styling-tailwind

安装后在 Claude Code 中输入/web-styling-tailwind确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

metadata.yaml
reference.md
examples/
advanced.md
core.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 本身就是预处理器。

评论

支持 Markdown