除本站自媒体官号以外,本文的内容禁止任一组织、个人、账号或平台,以任何形式转载到 RainyDesign.cn 站外。
任何个人或组织进行非法转载的,本站工作人员可对其追究法律责任。
具体信息详见 条例与条款 。
Tailwind CSS 指南
一、前言
1.1 文档背景
就 Tailwind CSS 官方文档来看,单单一个 核心理念 的知识点就够我们学习十数个小时了,所以为了配合教程,我们决定还是将深度知识点单拎出来讲解。
收藏文章请按下键盘上的Ctrl / Command + D加入书签栏。
二、类名速查
本小节仅收录了 Tailwind v4.1 常用的实用类名作为一个简化列表方便查阅学习。如需了解完整的实用类名,请查阅 Tailwind CSS - 官方文档 - 布局 获取更多信息。
2.1 布局(layout)
| 类名 | 属性 | 说明 |
|---|---|---|
block | display: block | 块级元素 |
inline | display: inline | 内联元素 |
inline-block | display: inline-block | 内联块元素 |
flex | display: flex | 弹性盒容器 |
grid | display: grid | 网格容器 |
hidden | display: none | 隐藏元素 |
static | position: static | 静态定位 |
relative | position: relative | 相对定位 |
absolute | position: absolute | 绝对定位 |
fixed | position: fixed | 固定定位 |
sticky | position: sticky | 粘性定位 |
top-0 | top: 0px | 顶部偏移 |
right-0 | right: 0px | 右侧偏移 |
bottom-0 | bottom: 0px | 底部偏移 |
left-0 | left: 0px | 左侧偏移 |
z-10 | z-index: 10 | 层级索引 |
float-left | float: left | 左浮动 |
float-right | float: right | 右浮动 |
clear-both | clear: both | 清除浮动 |
2.2 弹性盒与网格(Flexbox & Grid)
| 类名 | 属性 | 说明 |
|---|---|---|
flex-row | flex-direction: row | 水平排列 |
flex-col | flex-direction: column | 垂直排列 |
flex-wrap | flex-wrap: wrap | 允许换行 |
flex-nowrap | flex-wrap: nowrap | 不换行 |
justify-start | justify-content: flex-start | 主轴起始对齐 |
justify-center | justify-content: center | 主轴居中对齐 |
justify-between | justify-content: space-between | 主轴两端对齐 |
items-start | align-items: flex-start | 交叉轴起始对齐 |
items-center | align-items: center | 交叉轴居中对齐 |
items-end | align-items: flex-end | 交叉轴末尾对齐 |
flex-1 | flex: 1 1 0% | 弹性增长 |
flex-auto | flex: 1 1 auto | 自动弹性 |
flex-none | flex: none | 不弹性 |
grow | flex-grow: 1 | 允许增长 |
shrink | flex-shrink: 1 | 允许收缩 |
grid-cols-12 | grid-template-columns: repeat(12, minmax(0, 1fr)) | 12列网格 |
col-span-6 | grid-column: span 6 / span 6 | 跨越6列 |
gap-4 | gap: 1rem | 网格间隙 |
2.3 间距(Spacing)
| 类名 | 属性 | 说明 |
|---|---|---|
m-0 | margin: 0px | 无外边距 |
m-1 | margin: 0.25rem | 4px外边距 |
m-4 | margin: 1rem | 16px外边距 |
mx-auto | margin-left: auto; margin-right: auto | 水平居中 |
mt-4 | margin-top: 1rem | 顶部外边距 |
mr-4 | margin-right: 1rem | 右侧外边距 |
mb-4 | margin-bottom: 1rem | 底部外边距 |
ml-4 | margin-left: 1rem | 左侧外边距 |
p-0 | padding: 0px | 无内边距 |
p-1 | padding: 0.25rem | 4px内边距 |
p-4 | padding: 1rem | 16px内边距 |
px-4 | padding-left: 1rem; padding-right: 1rem | 水平内边距 |
py-4 | padding-top: 1rem; padding-bottom: 1rem | 垂直内边距 |
pt-4 | padding-top: 1rem | 顶部内边距 |
pr-4 | padding-right: 1rem | 右侧内边距 |
pb-4 | padding-bottom: 1rem | 底部内边距 |
pl-4 | padding-left: 1rem | 左侧内边距 |
space-x-4 | > * + * { margin-left: 1rem } | 子元素水平间距 |
space-y-4 | > * + * { margin-top: 1rem } | 子元素垂直间距 |
2.4 版式(Typography)
| 类名 | 属性 | 说明 |
|---|---|---|
text-xs | font-size: 0.75rem | 12px字体 |
text-sm | font-size: 0.875rem | 14px字体 |
text-base | font-size: 1rem | 16px字体 |
text-lg | font-size: 1.125rem | 18px字体 |
text-xl | font-size: 1.25rem | 20px字体 |
text-2xl | font-size: 1.5rem | 24px字体 |
font-thin | font-weight: 100 | 极细字重 |
font-normal | font-weight: 400 | 正常字重 |
font-bold | font-weight: 700 | 粗体字重 |
italic | font-style: italic | 斜体 |
text-left | text-align: left | 左对齐 |
text-center | text-align: center | 居中对齐 |
text-right | text-align: right | 右对齐 |
text-black | color: rgb(0 0 0) | 黑色文字 |
text-white | color: rgb(255 255 255) | 白色文字 |
text-red-500 | color: rgb(239 68 68) | 红色文字 |
leading-tight | line-height: 1.25 | 紧密行高 |
leading-normal | line-height: 1.5 | 正常行高 |
tracking-wide | letter-spacing: 0.025em | 字母间距 |
2.5 背景(Background)
| 类名 | 属性 | 说明 |
|---|---|---|
bg-transparent | background-color: transparent | 透明背景 |
bg-black | background-color: rgb(0 0 0) | 黑色背景 |
bg-white | background-color: rgb(255 255 255) | 白色背景 |
bg-red-500 | background-color: rgb(239 68 68) | 红色背景 |
bg-blue-500 | background-color: rgb(59 130 246) | 蓝色背景 |
bg-opacity-50 | background-color: rgb(0 0 0 / 0.5) | 背景透明度 |
bg-gradient-to-r | background-image: linear-gradient(to right, ...) | 右向渐变 |
from-blue-500 | --tw-gradient-from: var(--color-blue-500) | 渐变起始色 |
via-purple-500 | --tw-gradient-via: var(--color-purple-500) | 渐变过渡色 |
to-red-500 | --tw-gradient-to: var(--color-red-500) | 渐变结束色 |
from-10% | --tw-gradient-from-position: 10% | 渐变起始位置 |
via-30% | --tw-gradient-via-position: 30% | 渐变过渡位置 |
to-80% | --tw-gradient-to-position: 90% | 渐变结束位置 |
bg-cover | background-size: cover | 背景覆盖 |
bg-contain | background-size: contain | 背景包含 |
bg-center | background-position: center | 背景居中 |
bg-no-repeat | background-repeat: no-repeat | 背景不重复 |
bg-repeat | background-repeat: repeat | 背景重复 |
bg-fixed | background-attachment: fixed | 背景固定 |
2.6 边框(Border)
| 类名 | 属性 | 说明 |
|---|---|---|
border | border-width: 1px | 1px边框 |
border-0 | border-width: 0px | 无边框 |
border-2 | border-width: 2px | 2px边框 |
border-t | border-top-width: 1px | 顶部边框 |
border-r | border-right-width: 1px | 右侧边框 |
border-b | border-bottom-width: 1px | 底部边框 |
border-l | border-left-width: 1px | 左侧边框 |
border-solid | border-style: solid | 实线边框 |
border-dashed | border-style: dashed | 虚线边框 |
border-dotted | border-style: dotted | 点线边框 |
border-black | border-color: rgb(0 0 0) | 黑色边框 |
border-gray-300 | border-color: rgb(209 213 219) | 灰色边框 |
rounded-none | border-radius: 0 | 无圆角 |
rounded | border-radius: 0.25rem | 圆角 |
rounded-lg | border-radius: 0.5rem | 大圆角 |
rounded-xl | border-radius: 0.75rem | xl圆角 |
rounded-2xl | border-radius: 1rem | 2xl圆角 |
rounded-3xl | border-radius: 1.5rem | 3xl圆角 |
rounded-4xl | border-radius: 2rem | 4xl圆角 |
rounded-full | border-radius: 9999px | 完全圆角 |
rounded-t | border-top-left-radius: 0.25rem; border-top-right-radius: 0.25rem | 顶部圆角 |
divide-y | > * + * { border-top-width: 1px } | 子元素分割线 |
2.7 效果(Effects)
| 类名 | 属性 | 说明 |
|---|---|---|
shadow-2xs | box-shadow: 0 1px rgb(0 0 0 / 0.05) | 2xs阴影 |
shadow-xs | box-shadow: 0 1px 2px 0 rgb(0 0 0 / 0.05) | xs阴影 |
shadow | box-shadow: 0 1px 3px 0 rgb(0 0 0 / 0.1) | 阴影 |
shadow-sm | box-shadow: 0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1) | 小阴影 |
shadow-md | box-shadow: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1) | 中阴影 |
shadow-lg | box-shadow: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1) | 大阴影 |
shadow-xl | box-shadow: 0 20px 25px -5px rgb(0 0 0 / 0.1), 0 8px 10px -6px rgb(0 0 0 / 0.1) | xl阴影 |
shadow-2xl | box-shadow: 0 25px 50px -12px rgb(0 0 0 / 0.25) | 2xl阴影 |
shadow-(color:<custom-property>) | box-shadow: --tw-shadow-color: var(<custom-property>) | 阴影颜色 |
shadow-none | box-shadow: 0 0 #0000 | 无阴影 |
shadow-inner | box-shadow: inset 0 2px 4px 0 rgb(0 0 0 / 0.05) | 内阴影 |
drop-shadow | filter: drop-shadow(0 1px 2px rgb(0 0 0 / 0.1)) | 投影滤镜 |
opacity-0 | opacity: 0 | 完全透明 |
opacity-50 | opacity: 0.5 | 半透明 |
opacity-100 | opacity: 1 | 完全不透明 |
mix-blend-multiply | mix-blend-mode: multiply | 混合模式 |
bg-blend-overlay | background-blend-mode: overlay | 背景混合 |
outline-none | outline: 2px solid transparent | 无轮廓 |
outline | outline: 2px solid #3b82f6 | 蓝色轮廓 |
ring | box-shadow: 0 0 0 3px rgb(59 130 246 / 0.5) | 环形阴影 |
ring-2 | box-shadow: 0 0 0 2px rgb(59 130 246 / 0.5) | 2px环形阴影 |
2.8 过滤器(Filters)
| 类名 | 属性 | 说明 |
|---|---|---|
blur | filter: blur(8px) | 模糊滤镜 |
blur-sm | filter: blur(4px) | 小模糊 |
blur-none | filter: blur(0) | 无模糊 |
brightness-50 | filter: brightness(0.5) | 亮度50% |
brightness-100 | filter: brightness(1) | 正常亮度 |
contrast-50 | filter: contrast(0.5) | 对比度50% |
contrast-100 | filter: contrast(1) | 正常对比度 |
grayscale | filter: grayscale(100%) | 灰度滤镜 |
grayscale-0 | filter: grayscale(0) | 无灰度 |
hue-rotate-90 | filter: hue-rotate(90deg) | 色相旋转 |
invert | filter: invert(100%) | 反色滤镜 |
saturate-50 | filter: saturate(0.5) | 饱和度50% |
sepia | filter: sepia(100%) | 棕褐色滤镜 |
backdrop-blur-xs | backdrop-filter: blur(4px) | 背景模糊 |
backdrop-blur-sm | backdrop-filter: blur(8px) | 背景模糊 |
backdrop-blur-md | backdrop-filter: blur(12px) | 背景模糊 |
backdrop-blur-lg | backdrop-filter: blur(16px) | 背景模糊 |
backdrop-blur-xl | backdrop-filter: blur(24px) | 背景模糊 |
backdrop-blur-2xl | backdrop-filter: blur(40px) | 背景模糊 |
backdrop-blur-3xl | backdrop-filter: blur(64px) | 背景模糊 |
backdrop-brightness-50 | backdrop-filter: brightness(0.5) | 背景亮度 |
2.9 表格(Tables)
| 类名 | 属性 | 说明 |
|---|---|---|
table | display: table | 表格显示 |
table-row | display: table-row | 表格行 |
table-cell | display: table-cell | 表格单元格 |
table-auto | table-layout: auto | 自动表格布局 |
table-fixed | table-layout: fixed | 固定表格布局 |
border-collapse | border-collapse: collapse | 边框合并 |
border-separate | border-collapse: separate | 边框分离 |
caption-top | caption-side: top | 标题在顶部 |
caption-bottom | caption-side: bottom | 标题在底部 |
empty-cells-show | empty-cells: show | 显示空单元格 |
empty-cells-hide | empty-cells: hide | 隐藏空单元格 |
2.10 过渡和动画(Transitions & Animation)
| 类名 | 属性 | 说明 |
|---|---|---|
transition | transition-property: all; transition-duration: 150ms | 过渡效果 |
transition-none | transition-property: none | 无过渡 |
transition-colors | transition-property: color, background-color, border-color | 颜色过渡 |
transition-opacity | transition-property: opacity | 透明度过渡 |
transition-transform | transition-property: transform | 变形过渡 |
duration-75 | transition-duration: 75ms | 75ms持续时间 |
duration-300 | transition-duration: 300ms | 300ms持续时间 |
ease-linear | transition-timing-function: linear | 线性缓动 |
ease-in | transition-timing-function: cubic-bezier(0.4, 0, 1, 1) | 缓入 |
ease-out | transition-timing-function: cubic-bezier(0, 0, 0.2, 1) | 缓出 |
delay-75 | transition-delay: 75ms | 75ms延迟 |
animate-spin | animation: spin 1s linear infinite | 旋转动画 |
animate-ping | animation: ping 1s cubic-bezier(0, 0, 0.2, 1) infinite | 脉冲动画 |
animate-pulse | animation: pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite | 呼吸动画 |
animate-bounce | animation: bounce 1s infinite | 弹跳动画 |
2.11 变形(Transforms)
| 类名 | 属性 | 说明 |
|---|---|---|
transform | transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)) | 启用变形 |
transform-none | transform: none | 无变形 |
translate-x-0 | --tw-translate-x: 0px | X轴位移0 |
translate-x-1 | --tw-translate-x: 0.25rem | X轴位移4px |
translate-y-0 | --tw-translate-y: 0px | Y轴位移0 |
translate-y-1 | --tw-translate-y: 0.25rem | Y轴位移4px |
-translate-x-1 | --tw-translate-x: -0.25rem | X轴负位移 |
rotate-0 | --tw-rotate: 0deg | 旋转0度 |
rotate-45 | --tw-rotate: 45deg | 旋转45度 |
rotate-90 | --tw-rotate: 90deg | 旋转90度 |
-rotate-45 | --tw-rotate: -45deg | 逆时针旋转45度 |
skew-x-0 | --tw-skew-x: 0deg | X轴倾斜0度 |
skew-x-12 | --tw-skew-x: 12deg | X轴倾斜12度 |
skew-y-12 | --tw-skew-y: 12deg | Y轴倾斜12度 |
scale-0 | --tw-scale-x: 0; --tw-scale-y: 0 | 缩放为0 |
scale-50 | --tw-scale-x: 0.5; --tw-scale-y: 0.5 | 缩放50% |
scale-100 | --tw-scale-x: 1; --tw-scale-y: 1 | 正常缩放 |
scale-x-50 | --tw-scale-x: 0.5 | X轴缩放50% |
origin-center | transform-origin: center | 变形中心点 |
origin-top | transform-origin: top | 顶部为中心 |
2.12 互动性(Interactivity)
| 类名 | 属性 | 说明 |
|---|---|---|
cursor-auto | cursor: auto | 自动光标 |
cursor-pointer | cursor: pointer | 手型光标 |
cursor-not-allowed | cursor: not-allowed | 禁用光标 |
cursor-wait | cursor: wait | 等待光标 |
cursor-text | cursor: text | 文本光标 |
cursor-move | cursor: move | 移动光标 |
pointer-events-none | pointer-events: none | 禁用指针事件 |
pointer-events-auto | pointer-events: auto | 启用指针事件 |
resize-none | resize: none | 不可调整大小 |
resize | resize: both | 可调整大小 |
resize-x | resize: horizontal | 水平调整大小 |
resize-y | resize: vertical | 垂直调整大小 |
select-none | user-select: none | 不可选择文本 |
select-text | user-select: text | 可选择文本 |
select-all | user-select: all | 全选文本 |
select-auto | user-select: auto | 自动选择 |
appearance-none | appearance: none | 移除默认样式 |
outline-none | outline: 2px solid transparent | 移除焦点轮廓 |
focus:outline-none | outline: 2px solid transparent | 焦点时移除轮廓 |
focus:ring | box-shadow: 0 0 0 3px rgb(59 130 246 / 0.5) | 焦点环形阴影 |
2.13 SVG
| 类名 | 属性 | 说明 |
|---|---|---|
fill-current | fill: currentColor | 填充当前颜色 |
fill-transparent | fill: transparent | 透明填充 |
fill-black | fill: #000 | 黑色填充 |
fill-white | fill: #fff | 白色填充 |
fill-red-500 | fill: #ef4444 | 红色填充 |
fill-blue-500 | fill: #3b82f6 | 蓝色填充 |
stroke-current | stroke: currentColor | 描边当前颜色 |
stroke-transparent | stroke: transparent | 透明描边 |
stroke-black | stroke: #000 | 黑色描边 |
stroke-white | stroke: #fff | 白色描边 |
stroke-red-500 | stroke: #ef4444 | 红色描边 |
stroke-0 | stroke-width: 0 | 无描边宽度 |
stroke-1 | stroke-width: 1 | 1px描边宽度 |
stroke-2 | stroke-width: 2 | 2px描边宽度 |
2.14 状态,伪类与伪元素(States, pseudo-classes and pseudo-elements)
- 伪类:状态选择器
| 前缀 | 说明 | CSS 对应 |
|---|---|---|
hover: | 指针悬停 | :hover |
focus: | 元素获得焦点 | :focus |
focus-within: | 元素或其后代获得焦点 | :focus-within |
focus-visible: | 键盘导航获得焦点 | :focus-visible |
active: | 元素被按下 | :active |
visited: | 链接已访问 | :visited |
target: | 元素ID匹配URL片段 | :target |
disabled: | 表单元素禁用状态 | :disabled |
enabled: | 表单元素启用状态 | :enabled |
checked: | 复选框/单选按钮选中 | :checked |
indeterminate: | 复选框不确定状态 | :indeterminate |
default: | 默认选中的表单元素 | :default |
required: | 必填表单元素 | :required |
optional: | 可选表单元素 | :optional |
valid: | 表单验证通过 | :valid |
invalid: | 表单验证失败 | :invalid |
user-valid: | 用户交互后验证通过 | :user-valid |
user-invalid: | 用户交互后验证失败 | :user-invalid |
in-range: | 输入值在范围内 | :in-range |
out-of-range: | 输入值超出范围 | :out-of-range |
placeholder-shown: | 显示占位符文本 | :placeholder-shown |
autofill: | 浏览器自动填充 | :autofill |
read-only: | 只读状态 | :read-only |
- 伪类:次序选择器
| 前缀 | 说明 | CSS 对应 |
|---|---|---|
first: | 选择第一个子元素 | :first-child |
last: | 选择最后一个子元素 | :last-child |
only: | 选择唯一子元素 | :only-child |
odd: | 选择奇数位置子元素 | :nth-child(odd) |
even: | 选择偶数位置子元素 | :nth-child(even) |
first-of-type: | 选择同类型第一个元素 | :first-of-type |
last-of-type: | 选择同类型最后一个元素 | :last-of-type |
only-of-type: | 选择同类型唯一元素 | :only-of-type |
nth-{n}: | 选择第n个子元素 | :nth-child(n) |
nth-last-{n}: | 从末尾选择第n个子元素 | :nth-last-child(n) |
nth-of-type-{n}: | 选择同类型第n个元素 | :nth-of-type(n) |
nth-last-of-type-{n}: | 从末尾选择同类型第n个元素 | :nth-last-of-type(n) |
empty: | 选择无内容的元素 | :empty |
- 伪类:
has()与not()
| 前缀 | 说明 | CSS 对应 |
|---|---|---|
has-{selector}: | 包含指定后代元素 | :has(selector) |
has-checked: | 包含选中的表单元素 | :has(:checked) |
has-[:focus]: | 包含获得焦点的元素 | :has(:focus) |
has-[img]: | 包含图片元素 | :has(img) |
has-[a]: | 包含链接元素 | :has(a) |
group-has-{selector}: | 基于父级组的后代状态 | .group:has(selector) & |
peer-has-{selector}: | 基于兄弟元素的后代状态 | .peer:has(selector) ~ & |
not-{variant}: | 否定指定条件 | :not(condition) |
not-focus: | 非焦点状态 | :not(:focus) |
not-supports-{feature}: | 不支持指定特性 | @supports not (feature) |
- Aria 与 Data 元素前缀类
| 前缀 | 说明 | CSS 对应 |
|---|---|---|
aria-busy: | ARIA 忙碌状态 | &[aria-busy="true"] |
aria-checked: | ARIA 选中状态 | &[aria-checked="true"] |
aria-disabled: | ARIA 禁用状态 | &[aria-disabled="true"] |
aria-expanded: | ARIA 展开状态 | &[aria-expanded="true"] |
aria-hidden: | ARIA 隐藏状态 | &[aria-hidden="true"] |
aria-pressed: | ARIA 按下状态 | &[aria-pressed="true"] |
aria-readonly: | ARIA 只读状态 | &[aria-readonly="true"] |
aria-required: | ARIA 必填状态 | &[aria-required="true"] |
aria-selected: | ARIA 选择状态 | &[aria-selected="true"] |
aria-[...] | 任意 ARIA 属性 | &[aria-...] |
data-[...] | 任意 Data 属性 | &[data-...] |
- 媒体查询
| 前缀 | 说明 | CSS 对应 |
|---|---|---|
sm: | 屏幕宽度大于或等于断点 sm 值时 | @media (width >= 40rem) |
md: | 屏幕宽度大于或等于断点 md 值时 | @media (width >= 49rem) |
lg: | 屏幕宽度大于或等于断点 lg 值时 | @media (width >= 64rem) |
xl: | 屏幕宽度大于或等于断点 lg 值时 | @media (width >= 80rem) |
2xl: | 屏幕宽度大于或等于断点 lg 值时 | @media (width >= 96rem) |
max-lg | 屏幕宽度小于断点 lg 值时 | @media (width < 64rem) |
@lg | 容器宽度大于或等于断点 lg 值时 | @container (width => 64rem) |
@max-lg | 容器宽度小于断点 lg 值时 | @container (width < 64rem) |
@md:@max-lg: | 容器宽度于断点 md 值与 lg 之间时 | @container (width >= 48rem) and (width < 64rem) |
dark: | 深色模式 | @media (prefers-color-scheme: dark) |
motion-safe: | 允许动画(用户未要求减少动画) | @media (prefers-reduced-motion: no-preference) |
motion-reduce: | 减少动画(用户要求减少动画) | @media (prefers-reduced-motion: reduce) |
contrast-more: | 高对比度模式 | @media (prefers-contrast: more) |
contrast-less: | 低对比度模式 | @media (prefers-contrast: less) |
forced-colors: | 强制颜色模式 | @media (forced-colors: active) |
inverted-colors: | 反色模式 | @media (inverted-colors: inverted) |
print: | 打印模式 | @media print |
noscript: | 无脚本环境 | @media (scripting: none) |
- 指针设备
| 前缀 | 说明 | CSS 对应 |
|---|---|---|
pointer-fine: | 精确指针设备(如鼠标) | @media (pointer: fine) |
pointer-coarse: | 粗糙指针设备(如触屏) | @media (pointer: coarse) |
pointer-none: | 无指针设备 | @media (pointer: none) |
any-pointer-fine: | 任一精确指针设备 | @media (any-pointer: fine) |
any-pointer-coarse: | 任一粗糙指针设备 | @media (any-pointer: coarse) |
any-pointer-none: | 无任何指针设备 | @media (any-pointer: none) |
- 屏幕方向
| 前缀 | 说明 | CSS 对应 |
|---|---|---|
portrait: | 竖屏模式 | @media (orientation: portrait) |
landscape: | 横屏模式 | @media (orientation: landscape) |
- 功能支持
| 前缀 | 说明 | CSS 对应 |
|---|---|---|
supports-[...] | 支持指定 CSS 特性 | @supports (...) |
not-supports-[...] | 不支持指定 CSS 特性 | @supports not (...) |
- 其他状态选择器
| 前缀 | 说明 | CSS 对应 |
|---|---|---|
rtl: | 右至左布局 | &:where(:dir(rtl), [dir="rtl"], [dir="rtl"] *) |
ltr: | 左至右布局 | &:where(:dir(ltr), [dir="ltr"], [dir="ltr"] *) |
starting: | 元素在 DOM 中首次渲染时 | @starting-style |
open: | 打开状态(details/dialog/popover) | &:is([open], :popover-open, :open) |
inert: | 惰性元素(不可交互) | &[inert] |
2.15 断点,媒体与容器查询(Breakpoints, media queries and contain queries)
- 最小宽度断点
| 断点前缀 | 最小宽度 | CSS |
|---|---|---|
sm | 40rem (640px) | @media (width >= 40rem) { ... } |
md | 48rem (768px) | @media (width >= 48rem) { ... } |
lg | 64rem (1024px) | @media (width >= 64rem) { ... } |
xl | 80rem (1280px) | @media (width >= 80rem) { ... } |
2xl | 96rem (1536px) | @media (width >= 96rem) { ... } |
- 最大宽度断点
| 参数 | 媒体查询 |
|---|---|
max-sm | @media (width < 40rem) { ... } |
max-md | @media (width < 48rem) { ... } |
max-lg | @media (width < 64rem) { ... } |
max-xl | @media (width < 80rem) { ... } |
max-2xl | @media (width < 96rem) { ... } |
- 容器查询断点
| 参数 | 最小宽度 | CSS |
|---|---|---|
@3xs | 16rem (256px) | @container (width >= 16rem) { ... } |
@2xs | 18rem (288px) | @container (width >= 18rem) { ... } |
@xs | 20rem (320px) | @container (width >= 20rem) { ... } |
@sm | 24rem (384px) | @container (width >= 24rem) { ... } |
@md | 28rem (448px) | @container (width >= 28rem) { ... } |
@lg | 32rem (512px) | @container (width >= 32rem) { ... } |
@xl | 36rem (576px) | @container (width >= 36rem) { ... } |
@2xl | 42rem (672px) | @container (width >= 42rem) { ... } |
@3xl | 48rem (768px) | @container (width >= 48rem) { ... } |
@4xl | 56rem (896px) | @container (width >= 56rem) { ... } |
@5xl | 64rem (1024px) | @container (width >= 64rem) { ... } |
@6xl | 72rem (1152px) | @container (width >= 72rem) { ... } |
@7xl | 80rem (1280px) | @container (width >= 80rem) { ... } |
三、知识点
3.1 函数与指令(Functions and directives)
与官方文档顺序不同,这里先介绍一下 Tailwind CSS 文件中的各项函数与指令:
3.1.1 指令(Directives)
@import:引入一个文件,可以是 Tailwind CSS 本身:
代码已复制!
@import "tailwindcss";
@theme:规定一个主题层,包含了项目的自定义设计令牌,比如字体、颜色、断点等内容:
代码已复制!
@theme { --font-display: "Satoshi", "sans-serif"; --breakpoint-3xl: 120rem; --color-avocado-100: oklch(0.99 0 0); --color-avocado-200: oklch(0.98 0.04 113.22); --ease-fluid: cubic-bezier(0.3, 0, 0, 1); --ease-snappy: cubic-bezier(0.2, 0, 0, 1); /* ... */ }
@source:规定 Tailwind CSS 的输入源与排除路径:
代码已复制!
@source "../**/*.html"; @source not "../node_modules";
@utility:注册一个实用工具类:
代码已复制!
@utility tab-4 { tab-size: 4; }
@variant:应用一项预定义变体:
代码已复制!
.my-element { background: white; @variant dark { background: black; } }
@custom-variant:注册一个自定义变体:
代码已复制!
@custom-variant theme-midnight (&:where([data-theme="midnight"] *));
@apply:将 Tailwind CSS 定义的实用类名作为参数应用:
代码已复制!
.select2-dropdown { @apply rounded-b-lg shadow-md; }
具体使用方法将于下述小节中逐条讲解。
3.1.2 函数(Functions)
--alpha():调整颜色不透明度:
输入的 CSS
代码已复制!
.my-element { color: --alpha(var(--color-lime-300) / 50%); }
编译的 CSS
代码已复制!
.my-element { color: color-mix(in oklab, var(--color-lime-300) 50%, transparent); }
--spacing():调整间距:
输入的 CSS
代码已复制!
.my-element { margin: --spacing(4); }
编译的 CSS
代码已复制!
.my-element { margin: calc(var(--spacing) * 4); }
与 calc() 结合使用时,对于任意值也非常好用:
代码已复制!
<div class="py-[calc(--spacing(4)-1px)]"> <!-- ... --> </div>
其默认值为 0.25rem(4px) ,如 .mt-4 {margin-top: 1rem}。
3.2 检测源文件中的类(Detecting classes in source files)
我们来一起理解 Tailwind CSS 是如何工作的,以及怎样设置能使 Tailwind CSS 的工作效率达到一个理想状态。
默认情况下 Tailwind 将扫描项目中的每个文件以查找类名,但默认排除下列文件:
.gitignore中声明的文件node_modules目录下的文件- 二进制文件如图像、视频或 zip 等
- CSS 文件
- 常见的包管理文件如
yarn.lock、package.json
由此我们可以进行显示注册源的设置,以我们刚才搭建好的 my-project/static 项目为例:
静态项目结构
代码已复制!
static ├── fonts │ └── ... ├── img │ └── ... ├── styles │ ├── globals.css # 输入文件 │ └── theme.css # 输出文件 ├── index.html └── tailwindcss
代码已复制!
@import "tailwindcss" source("../**/*.html");
上述代码意味着 Tailwind CSS 在引入时仅检测项目中所有的 HTML,如需忽略一些目录或文件也可直接在 .gitignore 中设置,Tailwind 会同步进行忽略。当然你也可以手动设置这些参数:
代码已复制!
@import "tailwindcss"; @source "../**/*.html"; @source not "../node_modules";
此外我们还可添加 source(none) 以禁用自动检测,这在具有多个 Tailwind 样式表的项目中非常有用,举例如下:
代码已复制!
@import "tailwindcss" source(none); @source "../admin"; @source "../shared";
设置 Tailwind CSS 引入源对于大型项目的开发与构建效率而言尤为重要,对于本教程的 Next.JS 课程项目而言,我们仅需设置以下目录作为输入源:
代码已复制!
@import "tailwindcss"; @source "@/pages"; @source "@/components";
至此,Tailwind CSS 会将声明的源文件作为输入源,视为纯文本且不做代码解析,仅是基于 Tailwind 内建或自设的类名进行标记,然后对这些标记生成一个完整的 CSS 进行输出。这句话听起来比较绕,我们可以解释得接地气一点:
- Tailwind CSS 会依据你设定好的源文件目录与类型作为输入源;
- 输入的总是文字格式,Tailwind CSS 并不会做解析;
- 仅当输入的文字包含某个实用类名时,这个类名才会被标记;
- 比如你的输入里只有
relative和w-full这两个类名,它就只标记这两个
- 比如你的输入里只有
- Tailwind CSS 会将所有标记好的类名逐个生成样式,放在一个文件里输出。
由此可见调整输入源的范围与内容就变得极其重要:
输入的类名
代码已复制!
<span class="relative block w-full h-4"></span>
输出文件中包含的 CSS
代码已复制!
... @layer utilities { .relative { position: relative } .block { display: block } .h-4 { height: calc(var(--spacing)*4) } .w-full { width: 100% } }
以官方的例子来说,动态构造类名或用 props 拼接类名将不能被 Tailwind CSS 检测到:
代码已复制!
function Button({ color, children }) { return ( <div class="text-{{ error ? 'red' : 'green' }}-600"> <button className={`bg-${color}-600 hover:bg-${color}-500 ...`}>{children}</button> </div> ); }
代码已复制!
function Button({ color, children }) { const colorVariants = { blue: "bg-blue-600 hover:bg-blue-500", red: "bg-red-600 hover:bg-red-500", }; return ( <div class="{{ error ? 'text-red-600' : 'text-green-600' }}"> <button className={`${colorVariants[color]} ...`}>{children}</button> </div> ); }
前者由于本身不包含某个实用类名,输出文件中将不再包含这些 Tailwind CSS 实用类名,而后者则能正确生成。看到这里相信部分宝子会发现一个大问题,如果我们的输入源中不包含相关类名,但我们希望 Tailwind CSS 无论如何都要生成特定类名,又该如何操作呢?
3.2.1 保存特定类名(Safelisting specific utilities)
Tailwind CSS 使用 SafeList 参数来满足这一需求。有别于 Tailwind CSS v3 的一点是,v4 的 SafeList 不仅不存在于已弃用的 tailwind.config.mjs 文件当中,甚至连名称、使用方法对比前者都大相径庭。
Tailwind CSS v4 使用 @source inline("utilitie-name") 语法保存特定的类名,示例如下:
输入文件
代码已复制!
@import "tailwindcss"; @source inline("underline");
输出文件
代码已复制!
.underline { text-decoration-line: underline; }
我们也可以为其添加参数,比如选择 :hover 与 :focus 两个状态:
输入文件
代码已复制!
@import "tailwindcss"; @source inline("{hover:,focus:,}underline");
输出文件
代码已复制!
.underline { text-decoration-line: underline; } @media (hover: hover) { .hover\:underline:hover { text-decoration-line: underline; } } @media (focus: focus) { .focus\:underline:focus { text-decoration-line: underline; } }
此外,它也可以取值为一个区间:
输入文件
代码已复制!
@import "tailwindcss"; @source inline("{hover:,}bg-red-{50,{100..900..100},950}");
输出文件
代码已复制!
.bg-red-50 { background-color: var(--color-red-50); } .bg-red-100 { background-color: var(--color-red-100); } .bg-red-200 { background-color: var(--color-red-200); } /* ... */ .bg-red-800 { background-color: var(--color-red-800); } .bg-red-900 { background-color: var(--color-red-900); } .bg-red-950 { background-color: var(--color-red-950); } @media (hover: hover) { .hover\:bg-red-50:hover { background-color: var(--color-red-50); } /* ... */ .hover\:bg-red-950:hover { background-color: var(--color-red-950); } }
上述内容中的 bg-red-{50,{100..900..100},950} ,其 50 与 950 不言而喻,而中间的 100、900、100 则为从 100 到 900 递进 100。
3.2.2 排除特定类名(Explicitly excluding classes)
如果你不希望 Tailwind CSS 应用某个实用工具类,则可以使用 @source not inline() 进行排除:
代码已复制!
@import "tailwindcss"; @source not inline("{hover:,focus:,}bg-red-{50,{100..900..100},950}");
3.2.3 引用检测逻辑
这里我们额外补充一点官方没有声明的情况: 引入文件引用了未引入文件,导致相关类名被检测并注入。 听起来有点绕口是吧,那假设我们有 ComponentA.js 和 ComponentB.js 这两个组件,直接上代码:
@/styles/globals.css
代码已复制!
@import "tailwindcss" source("../components/ComponentA.js");
@/components/ComponentA.js
代码已复制!
import { ModuleA, ModuleB } from "@/components/ComponentB" return ( <> <ModuleA data={data.moduleA} /> <ModuleB data={data.moduleB} /> </> )
@/components/ComponentB.js
代码已复制!
const ModuleA = (data) => { return <span className="text-red-500"></span> // ✅ 会被识别,因为被父组件引用,且是完整声明 } const ModuleB = (data) => { return <span className={`text-${data.textColor}-500`}></span> // ❌ 不会被识别,因为非完整显式声明 } const ModuleC = (data) => { return <span className="bg-white"></span> // ❌ 不会被识别,因为没有被父组件引用 } export { ModuleA, ModuleB, ModuleC }
这种情况下,由于 ModuleA 与 ModuleB 被 ComponentA 所引用,所以会被 Tailwind CSS 视作 ComponentA 源码需做编译的一部分。不过由于 ModuleB 中的类名并非完整的显式声明(使用了动态字符串拼接),所以不会被 Tailwind CSS 识别并输出;而 ModuleC 虽然有完整的类名 bg-white,但因为没有被 ComponentA 引用,所以也不会被扫描到。最终结果是只有 ModuleA 中的 text-red-500 被注入到输出的 CSS 文件中。
3.3 状态,伪类与伪元素(States, pseudo-classes and pseudo-elements)
以下是常见的状态、伪类与伪元素选择参考:
代码已复制!
<div> <!-- 标准 --> <span class="block"></span> <!-- 伪类 --> <!-- 伪类:状态 --> <span class="hover:block"></span> <span class="active:block"></span> <span class="checked:block"></span> <!-- 伪类:次序 --> <span class="first:block"></span> <span class="odd:block"></span> <span class="nth-3:block"></span> <span class="nth-last-of-type-6:block"></span> <!-- 伪类:has() 与 not() --> <span class="has-[a]:block"></span> <span class="has-checked:block"></span> <span class="not-invalid:block"></span> <!-- 伪元素 --> <span class="before:block"></span> <span class="after:content-['lorem ipsum']"></span> <span class="placeholder:italic"></span> <ul class="marker:text-lg"></ul> <!-- 媒体查询 --> <span class="md:block"></span> <span class="max-lg:block"></span> <span class="dark:block"></span> ... </div>
请查阅 状态,伪类与伪元素(States, pseudo-classes and pseudo-elements) 了解所有前缀。 如欲查阅官方指南,详询 Tailwind CSS 官方文档 - 悬停,聚焦与其他状态 - 速查表 。
nth-child 的类名逻辑变更,因此请务必对应正确版本,如上述后缀名仅适用于 Tailwind CSS v4 。3.4 响应式设计(Responsive Design)
Tailwind CSS 正是在移动互联网时代下讲究小屏优先的主要推动力,因此与其相得益彰的设计稿也应从移动端扩展端点至平板端,我们的设计稿也遵循这一原则,且作为一个网站项目,我们得考虑到笔记本与台式机浏览的应用情景,所以需要将宽度进一步扩展到桌面端。
我们可以为先前示例里的卡片添加一些断点(md:...):
代码片段
代码已复制!
<div class="relative w-36 h-48 rounded-3xl border border-neutral-400 bg-[url('https://shorturl.at/ErgWw')] bg-center font-serif text-slate-700 transition duration-1000 ease starting:-translate-x-12 starting:opacity-0 md:w-48 md:h-64"> <span class="absolute block h-full w-full rounded-3xl bg-linear-to-b from-neutral-200/0 from-33% via-neutral-200/75 via-67% to-neutral-200"></span> <div class="relative flex h-full flex-col justify-end p-4 md:p-6"> <h4 class="font-bold md:text-lg">Title</h4> <p class="text-sm md:text-base">Lorem ipsum</p> <a href="javascript:void(0)" class="w-full px-2 py-1 mt-2 rounded-full font-sans font-medium text-xs text-center uppercase text-white bg-linear-to-r from-green-500/50 to-emerald-500 transition duration-300 ease hover:from-green-600/50 hover:to-emerald-600 md:px-3 md:py-1.5 md:mt-3 md:text-sm">View more</a> </div> </div>
渲染结果
Title
Lorem ipsum
好的,现在我们会发现当屏幕宽度 ≥ 48rem(768px)之时,卡片的尺寸、内部盒模型与按钮的内边距、字体的大小以及按钮的上边距都明显变大了一些。
3.4.1 响应式设计:断点与媒体查询(Breakpoints and media queries)
让我们先了解一下预设的几个断点:
| 断点前缀 | 最小宽度 | CSS |
|---|---|---|
sm | 40rem (640px) | @media (width >= 40rem) { ... } |
md | 48rem (768px) | @media (width >= 48rem) { ... } |
lg | 64rem (1024px) | @media (width >= 64rem) { ... } |
xl | 80rem (1280px) | @media (width >= 80rem) { ... } |
2xl | 96rem (1536px) | @media (width >= 96rem) { ... } |
这些断点前缀作用于 Tailwind CSS 所有的实用类名,这意味着你可以为任何 HTML 元素按需配置合适的 CSS 样式,且无需撰写任何媒体查询代码块。
接下来,我们还可以定义一个 @media (max-width) 值,就像这样:
代码已复制!
<div class="w-60 max-sm:w-48"></div>
也就是说,上述 div 在屏幕宽度小于 40rem (640px) 时,宽度将设置为 192px。以下是官方对应的预设参数:
| 参数 | 媒体查询 |
|---|---|
max-sm | @media (width < 40rem) { ... } |
max-md | @media (width < 48rem) { ... } |
max-lg | @media (width < 64rem) { ... } |
max-xl | @media (width < 80rem) { ... } |
max-2xl | @media (width < 96rem) { ... } |
: 使用,如 max-sm:,这与 max-w-sm 是完全不同的两个概念,后者不是媒体查询,而是 max-width 样式,需要区别理解。综合以上,如果你只想控制在一个范围区间,比如 max-width 从 768px ~ 1280px,那我们还可以这么写:
代码已复制!
<div class="w-60 md:max-xl:w-96 xl:w-full"></div>
这意味着上述盒模型会在 0 ~ 767px 视窗宽度下设定宽度为 15rem,并在 768 ~ 1279px 视窗宽度下设定宽度为 24rem,再在视窗宽度大于 1280px 时根据父级元素尺寸撑满宽度。当然你也可以采用一个自设值来临时限定宽度:
代码已复制!
<div class="w-60 md:max-[1080px]:w-96 xl:w-full"></div>
那么问题来了,我们该如何调整上述默认值呢?比如说我就想要修改默认的断点值,比如将 xs 调整为 30rem (480px) 而非 40rem (640px) ,或者增添新的断点呢?
请查阅 自定义主题 小节了解如何自定义。
3.4.2 响应式设计:容器查询(Container Queries)
作为一项现代 CSS 特性,我们可以根据父元素的大小而不是整个视图的大小来设置某些子元素的样式,而 Tailwind CSS 为我们提供简便的类名,就像这样:
代码已复制!
<div class="@container"> <div class="flex flex-col @md:flex-row"> <!-- ... --> </div> </div>
与断点前缀一样,Tailwind CSS 在容器查询上总是移动端优先。所以上述内容等价于 .@container 在最小尺寸 >= 48rem 时,其 flex-direction 属性变更为 row。
此外,我们也可以为其添加 max-width 容器查询:
代码已复制!
<div class="@container"> <div class="flex flex-col @max-md:flex-row"> <!-- ... --> </div> </div>
我们同样可以组合上述两者进行范围查询:
代码已复制!
<div class="@container"> <div class="flex flex-col @sm:@max-md:flex-row"> <!-- ... --> </div> </div>
此外,如果我们有多个嵌套容器,我们还可以为容器命名以便于子元素选择。此外我们当然也可以使用任意值以及 cqw(容器查询宽度(Container query width),1cqw 即为查询容器宽度的 1%):
代码已复制!
<div class="@container/main"> <!-- ... --> <div class="@container/sub flex flex-row @sm/main:flex-col"> <!-- ... --> <span class="w-full @min-[400px]/sub:w-[50cqw]"> </div> </div>
具体断点列表请查阅 断点,媒体与容器查询 。
3.5 颜色(Tailwind Colors)
Tailwind CSS 内建了一套颜色系统来实现色彩使用的统一,使开发者们得以简单、快速地将其应用到背景、边框、阴影等常见的参数中去,以下是一些例子:
示例代码
代码已复制!
<div class="py-4 space-x-2"> <span class="relative inline-block px-3.5 py-2 rounded-xl text-rose-100 border border-rose-300 bg-rose-900/75 shadow-xl shadow-rose-500/25 duration-300 ease hover:-translate-y-2 hover:shadow-rose-500/50">Example</span> <span class="relative inline-block px-3.5 py-2 rounded-xl text-sky-100 bg-sky-500/75 shadow-xl shadow-sky-500/25 inset-shadow-sm inset-shadow-sky-200/50 duration-300 ease hover:-translate-y-2 hover:shadow-sky-500/50 hover:inset-shadow-sky-200/75">Example 2</span> </div>
渲染结果
Tailwind CSS v4 目前支持的色彩如下:
| 工具类 | 描述 |
|---|---|
bg-* | 设置元素的背景颜色 |
text-* | 设置元素的文本颜色 |
decoration-* | 设置文本装饰的颜色 |
border-* | 设置元素的边框颜色 |
outline-* | 设置元素的轮廓颜色 |
shadow-* | 设置盒阴影的颜色 |
inset-shadow-* | 设置内阴影的颜色 |
ring-* | 设置环形阴影的颜色 |
inset-ring-* | 设置内环形阴影的颜色 |
accent-* | 设置表单控件的强调色 |
caret-* | 设置表单控件中光标的颜色 |
fill-* | 设置 SVG 元素的填充颜色 |
stroke-* | 设置 SVG 元素的描边颜色 |
具体的写法如 decoration-violet-400/75 来设置一个名为 violet-400 的内置主题颜色,并将其透明度设置为 75%:这里是渲染结果。
此外我们可以访问 https://tailwindcolor.com/ 来实时获取 Tailwind Color,也可以访问 Tailwind CSS 官方文档 - 颜色 页面了解更多信息。
此外,我们优先实现项目,再在有限的条件下做深入教学,因此 Darkmode 用例将暂定为进阶课程中讲解。
3.6 主题参数(Theme variables)
与需要在 v3 中设置 tailwind.config.mjs 不同,我们仅需在 v4 调用的 CSS 文件中引入 tailwindcss ,然后直接定义 @theme 中的内容即可。
3.6.1 默认主题参数
与 v3 不同的是,现在我们仅需在全局 CSS 文件 import Tailwind CSS 即可。这其中发生了什么?
Tailwind CSS v3
代码已复制!
@tailwind base; @tailwind components; @tailwind utilities; ...
Tailwind CSS v4
代码已复制!
@import "tailwindcss";
代码已复制!
@layer theme, base, components, utilities; @import "./theme.css" layer(theme); @import "./preflight.css" layer(base); @import "./utilities.css" layer(utilities);
这里很好的解释 v3 升级到 v4 之后导入语句究竟做了些什么,显然作为主题层(layer(theme))导入的 theme.css 的优先级较低,是可在外部根据 @theme{...} 代码块直接替换样式的,但在框架层面硬编码写死的类名如 flex-col 和 pointer-events-none 则是无法改动的。
3.6.2 自定义主题
我们可以直接在 @theme 层中增删改内置参数:
代码已复制!
@import "tailwindcss"; @theme { /* 覆盖断点 */ --breakpoint-xs: 30rem; --breakpoint-2xl: 100rem; --breakpoint-3xl: 120rem; /* 删除断点 */ --breakpoint-md: initial; /* 覆盖容器查询宽度 */ --container-8xl: 96rem; }
或是添加一个颜色与字体:
代码已复制!
@import "tailwindcss"; @theme { --color-mint-500: oklch(0.72 0.11 178); --font-poppins: Poppins, sans-serif; }
现在跟颜色有关的属性如 text-mint-500、bg-mint-500、border-mint-500 均已生效,而使用 font-poppins 则可以让元素使用 Poppins 字体。以下是官方支持自定义的全局变量:
| 命名空间 | 工具类名 |
|---|---|
--color-* | 颜色工具类,如 bg-red-500、text-sky-300 等 |
--font-* | 字体族工具类,如 font-sans 等 |
--text-* | 字体大小工具类,如 text-xl 等 |
--font-weight-* | 字体粗细工具类,如 font-bold 等 |
--tracking-* | 字母间距工具类,如 tracking-wide 等 |
--leading-* | 行高工具类,如 leading-tight 等 |
--breakpoint-* | 响应式断点变体,如 sm:* 等 |
--container-* | 容器查询变体,如 @sm:* 和尺寸工具类,如 max-w-md 等 |
--spacing-* | 间距和尺寸工具类,如 px-4、max-h-16 等 |
--radius-* | 边框圆角工具类,如 rounded-sm 等 |
--shadow-* | 盒阴影工具类,如 shadow-md 等 |
--inset-shadow-* | 内阴影工具类,如 inset-shadow-xs 等 |
--drop-shadow-* | 投影滤镜工具类,如 drop-shadow-md 等 |
--blur-* | 模糊滤镜工具类,如 blur-md 等 |
--perspective-* | 透视工具类,如 perspective-near 等 |
--aspect-* | 宽高比工具类,如 aspect-video 等 |
--ease-* | 过渡缓动函数工具类,如 ease-out 等 |
--animate-* | 动画工具类,如 animate-spin 等 |
如需了解整个默认主题变量,请参阅 Tailwind CSS 官方文档 - 默认主题变量参考 。
此外,你也可以把所有的断点与颜色都清空掉,再重新自定义:
代码已复制!
@import "tailwindcss"; @theme { --breakpoint-*: initial; --breakpoint-tablet: 40rem; --breakpoint-laptop: 64rem; --breakpoint-desktop: 80rem; --color-*: initial; --color-white: #fff; --color-purple: #3f3cbb; --color-midnight: #121063; --color-tahiti: #3ab7bf; --color-bermuda: #78dcca; }
甚至你可以完全自设属于自己的主题,而不调用任何 Tailwind CSS 默认主题样式:
代码已复制!
@import "tailwindcss"; @theme { --*: initial; --spacing: 4px; --font-body: Inter, sans-serif; --color-lagoon: oklch(0.72 0.11 221.19); --color-coral: oklch(0.74 0.17 40.24); --color-driftwood: oklch(0.79 0.06 74.59); --color-tide: oklch(0.49 0.08 205.88); --color-dusk: oklch(0.82 0.15 73.09); }
此外,我们还可以设置一些动画关键帧:
代码已复制!
@import "tailwindcss"; @theme { --animate-fade-in-scale: fade-in-scale 0.3s ease-out; @keyframes fade-in-scale { 0% { opacity: 0; transform: scale(0.95); } 100% { opacity: 1; transform: scale(1); } } }
这样我们便可以在所需的元素上使用 animate-fade-in-scale 类名来实现动画。
补充一点:对应章节 3.2 检测源文件中的类 所讲解的内容,此处 @theme 中定义的类同样不会自动填充到输出文件,如果你总是需要 @theme 中的所有内容被输出,请使用 @theme static 。下例:
代码已复制!
@import "tailwindcss"; @theme static { --color-primary: var(--color-red-500); --color-secondary: var(--color-blue-500); }
3.7 级联层(@layer)
此处与官方 Document 略有不同,我们将官方的
@layer独立拆分出来做讲解。
时间来到 2022 年,为适配 W3C 官方推出的新标准,Chrome / Edge 99+、Firefox 97+ 及 Safari 15.4+ 均已支持 Cascade Layers 特性。而 Tailwind CSS 作为 CSS Framework 领域的领头羊及技术革新的推动者,自然也在第一时间适配了这一特性。
总的来说,@layer 规定了各层级的优先级,并有效地为混乱的选择器或分布式文件规范地做好分类。参照官方层命名约定,Tailwind CSS 使用下列层命名分别管理对应的内容并设定好了优先级:
| 级联层 | 用途 | 说明 |
|---|---|---|
@layer theme | 主题层 | 管理颜色、字体、间距等设计令牌和主题变量 |
@layer base | 基础层 | 包含重置样式和基础 HTML 元素的默认样式 |
@layer components | 组件层 | 存放可复用的 UI 组件样式,如按钮、卡片等 |
@layer utilities | 实用层 | 包含原子化的工具类,具有最高的样式优先级 |
@layer layout | 布局层(已移除) | 定义页面整体布局结构和容器样式 (v4已移除) |
!important 来造成混乱的样式优先级争夺。以下是一个包含了各层应用标准的 CSS 示例:
代码已复制!
/* 引入 Tailwind CSS */ @import "tailwindcss"; /* 在 theme 层中定义 */ @layer theme{ /* 移除所有内置行高,并设置主要与次要颜色 */ --leading-*: initial; --color-primary: var(--color-sky-500); --color-secondary: var(--color-sky-800); } /* 在 base 层中定义 */ @layer base { /* 为所有 a 标签设置字体颜色为次要色 */ a {color: var(--color-secondary);} /* 选择 section 标签 */ section { /* 选择 section 标签下的指定标签,将部分标签的字体颜色设置为主要色,并为 h1 h2 设置字体大小 */ h1, h2, h3, h4, p, span, li {color: var(--color-primary);} h1 {font-size: var(--text-2xl);} h2 {font-size: var(--text-xl);} } } /* 在 components 层中定义 */ @layer components { /* 选择 .card 类 */ .card { /* 设置各个 css 样式 */ content-visibility: hidden; background-color: var(--color-white); border-radius: var(--radius-lg); padding: --spacing(6); box-shadow: var(--shadow-xl); transition: .3s all ease; /* 应用 Tailwind CSS 命名规则中的变量值,如 :hover、:dark 等 */ @variant hover { transform: translate-y(-.25rem); } } } /* 设置实用类 */ /* 注册名为 “content-auto“ 的类 */ @utility content-auto { content-visibility: auto; }
上述提到过,级链层的优先级为 theme < base < component < utilities,所以在设置 .card.content-auto 类的元素时(即 class="card content-auto"),.content-auto 便会覆盖 .card 中的 content-visiblity 属性优先级,使其变为 auto 。
接下来会讲到 utilities 的具体管理,而 component 层的组件样式将于开发项目的过程中给出例子。
3.8 增设自定义实用工具类(Adding Custom Utitlites)
3.8.1 简单实用类名(Simple utilities)
我们继续来看这一例子:
代码已复制!
@utility content-auto { content-visibility: auto; }
新增的实用类名和预定义实用类在使用方法上没有区别:
代码已复制!
<div> <!-- 常规 --> <span class="content-auto"></span> <!-- 状态伪类 --> <span class="hover:content-auto"></span> <span class="active:content-auto"></span> <!-- 媒体查询伪类 --> <span class="md:content-auto"></span> <span class="dark:content-auto"></span> ... </div>
3.8.2 复杂实用类名(Complex utilities)
如果没有预定义的常用变量值(如 @variant hover),则仍需通过标准的嵌套语法添加至引入文件:
代码已复制!
@utility scrollbar-hidden { &::-webkit-scrollbar { display: none; } }
3.8.3 功能性实用类名(Functional utilities)
我们还可以注册接收传参的功能性实用类名,它们能接受以下几种参数输入:
| 参数名称 | 参考 CSS 代码 |
|---|---|
| 匹配主题值(Matching theme values) | tab-size: --value(--tab-size-*); |
| 裸值(Bare values) | tab-size: --value(integer); |
| 文字值(Literal values) | tab-size: --value("inherit", "initial", "unset"); |
| 任意值(Arbitrary values) | tab-size: --value([integer]); |
| 负值(Negative values) | tab-size: calc(--value(integer) * -1); |
| 分数(Fractions) | tab-size: --value(ratio) |
关于各项功能实用类的注册方法,由于内容较长做成了折叠列表,请按需查阅理解。
匹配主题值(Matching theme values)
代码已复制!
@theme { --tab-size-2: 2; --tab-size-4: 4; --tab-size-github: 8; } @utility tab-* { tab-size: --value(--tab-size-*); }
这样一来,以 tab- 作为前缀的实用类就匹配上了三个设定好的变量。
如果我们想让参数变得更灵活,比如随意设置 tab-size 时,就需要用到下述的裸值、文字值与任意值了。
裸值(Bare values)
代码已复制!
@utility tab-* { tab-size: --value(integer); }
这样一来,我们就可以输入整数作为参数传入以设置 tab-size 的值了,如 tab-size-108 ;如果需要添加更多裸值类型,我们可以直接设置一个数组:
代码已复制!
@utility tab-* { tab-size: --value(integer, ratio); }
这样我们便可以使用 tab-size-108 与 tab-size-1/2 两种类型的值了。下表为支持的裸值数据类型:
| 裸值类型 | 说明 | 示例 |
|---|---|---|
number | 数值 | 10, 0.5 |
integer | 整数 | 5, -15 |
ratio | 比例值 | 1/2, 3/4 |
percentage | 百分比 | 50%, 25% |
裸值的类名格式为 class="tab-size-*" 。
文字值(Literal values)
代码已复制!
@utility tab-* { tab-size: --value("inherit", "initial", "unset"); }
我们同样可以传入一个数组来同时设定一系列合法的值,这样我们便可以使用 tab-size-inherit 、 tab-size-initial 与 tab-size-unset 了。
任意值(Arbitrary values)
代码已复制!
@utility tab-* { tab-size: --value([integer], [percentage]); }
看上去与裸值很像,但请注意参数需要被 [] 包裹。
这样一来我们便可以使用 tab-size-[10] 与 tab-size[50%] 了。下表为支持的任意值数据类型:
| 任意值类型 | 说明 | 示例 |
|---|---|---|
absolute-size | 绝对尺寸关键词 | xx-small, x-small, small, medium, large, x-large, xx-large |
angle | 角度值 | 45deg, 0.25turn, 1.57rad, 50grad |
bg-size | 背景尺寸 | cover, contain, auto, 100px 50px |
color | 颜色值 | red, #ff0000, rgb(255,0,0), hsl(0,100%,50%) |
family-name | 字体家族名称 | "Times New Roman", Arial, serif |
generic-name | 通用字体族 | serif, sans-serif, monospace, cursive, fantasy |
image | 图像值 | url(), linear-gradient(), radial-gradient() |
integer | 整数 | 1, 42, -5 |
length | 长度值 | 10px, 2rem, 50%, 3em, 1vh |
line-width | 线条宽度 | thin, medium, thick, 2px |
number | 数值 | 1.5, 0, 3.14159 |
percentage | 百分比 | 50%, 100%, 25% |
position | 位置值 | top, center, bottom, left, right, 50% 25% |
ratio | 比例值 | 16/9, 4/3, 1/1 |
relative-size | 相对尺寸关键词 | larger, smaller |
url | URL 值 | url("image.jpg"), url(https://example.com/bg.png) |
vector | 矢量值 | SVG 路径数据等 |
* | 通配符 | 接受任何值类型 |
任意值的类名格式为 class="tab-size-[*]" ;可以看到对比裸值,任意值可以设置相当多的参数类型,所以请根据需求进行设置。
同时支持多种类型的值
代码已复制!
@theme { --tab-size-github: 8; } @utility tab-* { /* 请注意,以下三项是允许被同时添加到一个实用类名的 */ tab-size: --value(integer); /* 使裸值支持整数类型,如 tab-size-6 */ tab-size: --value([integer]); /* 使任意值支持整数类型,如 tab-size-[6] */ tab-size: --value(--tab-size-*); /* 使值能勾匹配 @theme 中设置的以 --tab-size- 开头的所有自定义变量,如 --tab-size-github */ }
这样一来 tab-size 将同时支持如 tab-size-2、tab-size-[2] 与 tab-size-github 这类参考值。
此外,与上述文字值支持传入一个数组相同的,我们也可把它们合并到一起;但请务必注意,值的解析顺序永远是从左到右:
代码已复制!
@utility tab-* { /* 同时支持整数裸值、整数任意值与匹配的自定义变量,且解析顺序为 裸值 > 任意值 > 自定义变量 */ tab-size: --value(integer, [integer], --tab-size-*); }
除此以外我们可以加入计算,以匹配角度与百分比等非整数或浮点值,如原生的 transform: rotate() 等,此处以 opacity 举例:
代码已复制!
@utility opacity-* { opacity: calc(--value(integer) * 1%); /* 有需要计算的场景,calc(--value()) 必须单拎出来写 */ opacity: --value([percentage], --opacity-*); }
负值(Negative values)
代码已复制!
@utility inset-* { inset: --spacing(--value(integer)); inset: --value([percentage], [length]); } @utility -inset-* { inset: --spacing(--value(integer) * -1); inset: calc(--value([percentage], [length]) * -1); }
其本质就是使用 calc() 直接乘以一个 -1 来实现负值结果。
修饰符(Modifier)
代码已复制!
@utility text-* { font-size: --value(--text-*, [length]); line-height: --modifier(--leading-*, [length], [*]); }
语法为 --modifier(),工作方式与 --value() 没什么不同,只是传入的值是 Tailwind CSS 内置的预定义值;这也就合理解释了预定义的文字大小类(如 text-lg) 是如何包含了预定义的 line-height 值的。
3.8.4 添加自定义变体(Adding custom variants)
先前我们已经提到过一个运用于 .card 组件类名中的悬停变体(@variant hover {...})。
对于常见的预定义变体如 :before、:hover、:active、:dark、:md 等,我们建议使用 @variant 属性代替 &: 属性,如使用 @variant before 而非 &:before。
但这里也有例外,比如考虑到兼容性问题的 -webkit- 前缀,Tailwind CSS 并没有自带这些变体,那我们可以为其手动设置:
代码已复制!
@custom-variant webkit-scrollbar { &:where(::-webkit-scrollbar) { @slot; } } /* 不需要嵌套时可以简写为 */ @custom-variant webkit-scrollbar (&:where(::-webkit-scrollbar)); /* 将该变体应用至对应的类名 */ @utility scrollbar-hidden { @variant webkit-scrollbar { display: none; } }
@webkit-scrollbar 作为一个复用变体,意味着所有的 Tailwind CSS 自设类名都可以对其进行调用;如果它不像是 hover 这类常见的变体,且没有复用需求的话,使用原生嵌套语法 &::-webkit-scrollbar 会是更好的选择。如果需要选择包含某项参数的 HTML 元素,则参考下列内容撰写:
代码已复制!
@custom-variant theme-midnight { &:where([data-theme="midnight"] *) { @slot; } } /* 不需要嵌套时可以简写为 */ @custom-variant theme-midnight (&:where([data-theme="midnight"] *));
当自定义变体有多个规则时,它们可以相互嵌套:
代码已复制!
@custom-variant any-hover { @media (any-hover: hover) { &:hover { @slot; } } }
3.9 样式重置(Preflight)
由于浏览器自带一些默认样式,所以开始定义样式前,有经验的前端都会引入一张预检样式表,比如 normalize.css 、 reset.css 之类。
理所当然的,Tailwind CSS 也会对页面进行预检。以下是 Tailwind CSS v4.1 的预检样式表:
Tailwind CSS v4.1 Preflight stylesheets
代码已复制!
/* 1. Prevent padding and border from affecting element width. (https://github.com/mozdevs/cssremedy/issues/4) 2. Remove default margins and padding 3. Reset all borders. */ *, ::after, ::before, ::backdrop, ::file-selector-button { box-sizing: border-box; /* 1 */ margin: 0; /* 2 */ padding: 0; /* 2 */ border: 0 solid; /* 3 */ } /* 1. Use a consistent sensible line-height in all browsers. 2. Prevent adjustments of font size after orientation changes in iOS. 3. Use a more readable tab size. 4. Use the user's configured `sans` font-family by default. 5. Use the user's configured `sans` font-feature-settings by default. 6. Use the user's configured `sans` font-variation-settings by default. 7. Disable tap highlights on iOS. */ html, :host { line-height: 1.5; /* 1 */ -webkit-text-size-adjust: 100%; /* 2 */ tab-size: 4; /* 3 */ font-family: --theme( --default-font-family, ui-sans-serif, system-ui, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji' ); /* 4 */ font-feature-settings: --theme(--default-font-feature-settings, normal); /* 5 */ font-variation-settings: --theme(--default-font-variation-settings, normal); /* 6 */ -webkit-tap-highlight-color: transparent; /* 7 */ } /* 1. Add the correct height in Firefox. 2. Correct the inheritance of border color in Firefox. (https://bugzilla.mozilla.org/show_bug.cgi?id=190655) 3. Reset the default border style to a 1px solid border. */ hr { height: 0; /* 1 */ color: inherit; /* 2 */ border-top-width: 1px; /* 3 */ } /* Add the correct text decoration in Chrome, Edge, and Safari. */ abbr:where([title]) { -webkit-text-decoration: underline dotted; text-decoration: underline dotted; } /* Remove the default font size and weight for headings. */ h1, h2, h3, h4, h5, h6 { font-size: inherit; font-weight: inherit; } /* Reset links to optimize for opt-in styling instead of opt-out. */ a { color: inherit; -webkit-text-decoration: inherit; text-decoration: inherit; } /* Add the correct font weight in Edge and Safari. */ b, strong { font-weight: bolder; } /* 1. Use the user's configured `mono` font-family by default. 2. Use the user's configured `mono` font-feature-settings by default. 3. Use the user's configured `mono` font-variation-settings by default. 4. Correct the odd `em` font sizing in all browsers. */ code, kbd, samp, pre { font-family: --theme( --default-mono-font-family, ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono', 'Courier New', monospace ); /* 1 */ font-feature-settings: --theme(--default-mono-font-feature-settings, normal); /* 2 */ font-variation-settings: --theme(--default-mono-font-variation-settings, normal); /* 3 */ font-size: 1em; /* 4 */ } /* Add the correct font size in all browsers. */ small { font-size: 80%; } /* Prevent `sub` and `sup` elements from affecting the line height in all browsers. */ sub, sup { font-size: 75%; line-height: 0; position: relative; vertical-align: baseline; } sub { bottom: -0.25em; } sup { top: -0.5em; } /* 1. Remove text indentation from table contents in Chrome and Safari. (https://bugs.chromium.org/p/chromium/issues/detail?id=999088, https://bugs.webkit.org/show_bug.cgi?id=201297) 2. Correct table border color inheritance in all Chrome and Safari. (https://bugs.chromium.org/p/chromium/issues/detail?id=935729, https://bugs.webkit.org/show_bug.cgi?id=195016) 3. Remove gaps between table borders by default. */ table { text-indent: 0; /* 1 */ border-color: inherit; /* 2 */ border-collapse: collapse; /* 3 */ } /* Use the modern Firefox focus style for all focusable elements. */ :-moz-focusring { outline: auto; } /* Add the correct vertical alignment in Chrome and Firefox. */ progress { vertical-align: baseline; } /* Add the correct display in Chrome and Safari. */ summary { display: list-item; } /* Make lists unstyled by default. */ ol, ul, menu { list-style: none; } /* 1. Make replaced elements `display: block` by default. (https://github.com/mozdevs/cssremedy/issues/14) 2. Add `vertical-align: middle` to align replaced elements more sensibly by default. (https://github.com/jensimmons/cssremedy/issues/14#issuecomment-634934210) This can trigger a poorly considered lint error in some tools but is included by design. */ img, svg, video, canvas, audio, iframe, embed, object { display: block; /* 1 */ vertical-align: middle; /* 2 */ } /* Constrain images and videos to the parent width and preserve their intrinsic aspect ratio. (https://github.com/mozdevs/cssremedy/issues/14) */ img, video { max-width: 100%; height: auto; } /* 1. Inherit font styles in all browsers. 2. Remove border radius in all browsers. 3. Remove background color in all browsers. 4. Ensure consistent opacity for disabled states in all browsers. */ button, input, select, optgroup, textarea, ::file-selector-button { font: inherit; /* 1 */ font-feature-settings: inherit; /* 1 */ font-variation-settings: inherit; /* 1 */ letter-spacing: inherit; /* 1 */ color: inherit; /* 1 */ border-radius: 0; /* 2 */ background-color: transparent; /* 3 */ opacity: 1; /* 4 */ } /* Restore default font weight. */ :where(select:is([multiple], [size])) optgroup { font-weight: bolder; } /* Restore indentation. */ :where(select:is([multiple], [size])) optgroup option { padding-inline-start: 20px; } /* Restore space after button. */ ::file-selector-button { margin-inline-end: 4px; } /* Reset the default placeholder opacity in Firefox. (https://github.com/tailwindlabs/tailwindcss/issues/3300) */ ::placeholder { opacity: 1; } /* Set the default placeholder color to a semi-transparent version of the current text color in browsers that do not crash when using `color-mix(…)` with `currentcolor`. (https://github.com/tailwindlabs/tailwindcss/issues/17194) */ @supports (not (-webkit-appearance: -apple-pay-button)) /* Not Safari */ or (contain-intrinsic-size: 1px) /* Safari 17+ */ { ::placeholder { color: color-mix(in oklab, currentcolor 50%, transparent); } } /* Prevent resizing textareas horizontally by default. */ textarea { resize: vertical; } /* Remove the inner padding in Chrome and Safari on macOS. */ ::-webkit-search-decoration { -webkit-appearance: none; } /* 1. Ensure date/time inputs have the same height when empty in iOS Safari. 2. Ensure text alignment can be changed on date/time inputs in iOS Safari. */ ::-webkit-date-and-time-value { min-height: 1lh; /* 1 */ text-align: inherit; /* 2 */ } /* Prevent height from changing on date/time inputs in macOS Safari when the input is set to `display: block`. */ ::-webkit-datetime-edit { display: inline-flex; } /* Remove excess padding from pseudo-elements in date/time inputs to ensure consistent height across browsers. */ ::-webkit-datetime-edit-fields-wrapper { padding: 0; } ::-webkit-datetime-edit, ::-webkit-datetime-edit-year-field, ::-webkit-datetime-edit-month-field, ::-webkit-datetime-edit-day-field, ::-webkit-datetime-edit-hour-field, ::-webkit-datetime-edit-minute-field, ::-webkit-datetime-edit-second-field, ::-webkit-datetime-edit-millisecond-field, ::-webkit-datetime-edit-meridiem-field { padding-block: 0; } /* Center dropdown marker shown on inputs with paired `<datalist>`s in Chrome. (https://github.com/tailwindlabs/tailwindcss/issues/18499) */ ::-webkit-calendar-picker-indicator { line-height: 1; } /* Remove the additional `:invalid` styles in Firefox. (https://github.com/mozilla/gecko-dev/blob/2f9eacd9d3d995c937b4251a5557d95d494c9be1/layout/style/res/forms.css#L728-L737) */ :-moz-ui-invalid { box-shadow: none; } /* Correct the inability to style the border radius in iOS Safari. */ button, input:where([type='button'], [type='reset'], [type='submit']), ::file-selector-button { appearance: button; } /* Correct the cursor style of increment and decrement buttons in Safari. */ ::-webkit-inner-spin-button, ::-webkit-outer-spin-button { height: auto; } /* Make elements with the HTML hidden attribute stay hidden by default. */ [hidden]:where(:not([hidden='until-found'])) { display: none !important; }
3.10 前缀(Prefix)
我们可以通过在导入 Tailwind CSS 时添加 prefix() 参数以应用自定义前缀:
代码已复制!
@import "tailwindcss" prefix(tw); @theme { --font-display: "Satoshi", "sans-serif"; --breakpoint-3xl: 120rem; --color-avocado-100: oklch(0.99 0 0); --color-avocado-200: oklch(0.98 0.04 113.22); --color-avocado-300: oklch(0.94 0.11 115.03); /* ... */ }
代码已复制!
:root { --tw-font-display: "Satoshi", "sans-serif"; --tw-breakpoint-3xl: 120rem; --tw-color-avocado-100: oklch(0.99 0 0); --tw-color-avocado-200: oklch(0.98 0.04 113.22); --tw-color-avocado-300: oklch(0.94 0.11 115.03); /* ... */ }
输出的所有参数都会应用自定义前缀,以避免代码冲突。
四、代码优化
优化 Tailwind CSS 实用类名顺序,使其兼具可读性、易维护性的同时尽可能降低性能开销。
4.1 优化实用类次序
首先让我们为实用类分类排序以易于阅读及维护,我个人倾向于在撰写期间就按照自己喜欢的顺序排序:
- 命名、分组与去格式:自定义类名如
group,peer,appearance-* - 自身布局:
flex-1,grow,shrink-*,self-*,justify-self-*,place-self,basis-*,col-span-*,row-start-* - 定位:
static,absolute,relative,fixed,sticky - 容器、层级与混合:
container,z-*,isolate - 布局与显示:
flex,grid,block,inline,inline-block,hidden.float,clear,invisible - 容器配置:
flex-row,flex-col,items-*,justify-*,content-*,gap-*,grid-cols-*,grid-rows-* - 尺寸与比例:
w-*,h-*,size-*,aspect-*及所有尺寸的 min / max 变体 - 定位偏移:
left-*,right-*,top-*,bottom-*,inset-* - 间距:
p-*,m-*及方向变体如px-*,mr-*等 - 边框与圆角:
border-*,rounded-*,ring-*,outline-* - 子元素间距:
space-x-*,space-y-*,divide-x-*,divide-y-* - 排版:
text-*,font-*,leading-*,tracking-*,line-clamp-*,whitespace-*,break-* - 背景与图片:
bg-*,via-*,to-*,object-* - 效果与变换:
shadow-*,opacity-*,origin-*,transform,scale-*,rotate-*,mix-blend-mode - 遮罩效果:
blur-*,brightness-*,contrast-*等所有的 filter 效果,以及对应的backdrop-* - 交互:
overflow-*,scroll-*,snap-*,touch-*,user-select-*,resize-*,cursor-* - 过渡与动画:
transition-*,duration-*,animate-* - 状态修饰符:
hover:*,focus:*,active:*,group:*,peer:*,nth-*:* - 响应式修饰符:
sm:*,md:*,lg:*,xl:*,2xl:*
梳理顺序有助于提升易读性,同时筛选出冲突类名以做删减(示例中 block 会被 flex 覆盖:
❌ Don't:
代码已复制!
mt-2 md:mt-4 w-full block opacity-50 flex rotate-45 absolute
✅ Do it:
代码已复制!
absolute flex w-full mt-2 opacity-50 rotate-45 md:mt-4
此外,如果你想像本章节 4.1.3 在线演练 小节中那样实现一键排序,我们推荐以下三种优化建议:
- 使用 VSCode 插件 Headwind 进行自定义实用类排序
- 使用 VSCode 插件 Prettier 进行代码自动格式化
- 使用 Prettier 依赖进行排序
特性对比如下:
| 特性 | Headwind 插件 | Prettier 插件 | Prettier 依赖 |
|---|---|---|---|
| 安装方式 | VSCode 扩展 | VSCode 扩展 | npm/pnpm |
| 格式范围 | 仅注册的 | VSCode 扩展 | npm/pnpm |
| 配置难度 | 中等 | 简单 | 中等 |
| 自定义排序 | ✅ | ❌ | ❌ |
| 任意值支持 | ❌ | ✅ | ✅ |
| 团队协作 | 需同步配置 | ✅ | ✅ |
| 代码格式化 | 仅类名排序 | ✅ | ✅ |
| CI/CD 集成 | ❌ | ✅ | ✅ |
| 适用场景 | 自定义排序需求 | 快速上手 | 团队/生产环境 |
我们按照这个顺序讲解,不过无论你选用哪一种,格式化之前别忘了备份项目:
代码已复制!
# 进入目录 cd ~/Documents/my-project # 将项目文件备份到 backup/nextjs rsync -av --progress --exclude='node_modules' --exclude='.next' --exclude='build' nextjs/ backup/nextjs
4.1.1 VSCode 插件 Headwind
一个面向 Tailwind CSS 简单实用的格式化工具,唯一的缺点是任意值不支持,所以不推荐保存时自动格式化,而是按需使用。
直接在插件商店搜索 Headwind,在安装完毕后打开 VSCode 的 settings.json(按下⌘ / CTRL + ⇧ / SHIFT + P 然后输入 user settings json 并回车),按需新建对象并填充内容即可:
代码已复制!
... // 可选,添加前缀判断,如果你有使用 @import "tailwindcss" prefix() 参数的话,这里需要对应填写 "headwind.customTailwindPrefix": "", // 在保存时格式化,默认为 true "headwind.runOnSave": false, // 格式属性 Regex,此处对应 JSX TSX 中的 className 属性,默认格式 HTML、CSS、JavaScript、TypeScript、JSX、TSX ,识别 class、className 与 CSS 中的 @apply 属性 "headwind.classRegex": { "javascriptreact": "\\bclassName\\s*=\\s*[{\"'`]([^{\"'`]*)[\"'`}]", "typescriptreact": "\\bclassName\\s*=\\s*[{\"'`]([^{\"'`]*)[\"'`}]" }, // 按照下列顺序排序,默认内容比较长,请访问 https://rainydesign.cn/blog/headwind-default-sort-order.json 查看 "headwind.defaultSortOrder": [ "container", // ... ] // 将自定义类名排序到 Tailwind Utilities 之前,默认为 false "headwind.prependCustomClasses": true, // 删除重复类名,默认为 true "headwind.removeDuplicates": true ...
该插件的快捷键是:
- MacOS:⌘ + ⇧ + T
- Windows / Linux:CTRL + SHIFT + T
4.1.2 VSCode 插件 Prettier
最方便快捷的一个,直接搜索插件 Prettier 安装,格式化时选择 Prettier 即可。VSCode 格式化快捷键如下:
- MacOS:⇧ + ⌥ + F
- Windows / Linux:CTRL + SHIFT + F
很遗憾的是 Prettier 不支持自定义顺序,并且会顺带格式化其他内容,所以建议团队协作环境下使用。以下是 Prettier 的默认实用类名排序:
- 布局:
container,flex,grid,block,inline,hidden - 定位:
absolute,relative,fixed,sticky,top-*,left-* - 尺寸:
w-*,h-*,min-w-*,max-w-* - 间距:
m-*,p-*,space-* - 排版:
text-*,font-*,leading-* - 背景:
bg-* - 边框:
border-*,rounded-* - 效果:
shadow-*,opacity-* - 过渡:
transition-*,duration-* - 变换:
transform,scale-*,rotate-* - 交互:
cursor-*,select-* - 状态修饰符:
hover:*,focus:*,active:* - 响应式修饰符:
sm:*,md:*,lg:*,xl:*,2xl:*
如果要清除默认格式化插件,请按下⌘ / CTRL + ⇧ / SHIFT + P 然后输入 user settings json,打开用户设置并清除相关内容:
代码已复制!
... "[javascript]": { // 删除这个对象 "editor.defaultFormatter": "esbenp.prettier-vscode" } ...
4.1.3 使用 Prettier 依赖进行排序
使用 Prettier 依赖的好处是不进一步扩充 VSCode 插件库,并能在部署后于服务器端直接使用命令行格式化。让我们回到项目安装依赖:
# 使用 yarn 安装依赖
yarn add -D prettier prettier-plugin-tailwindcss # 或使用 npm 安装依赖:npm install -D prettier prettier-plugin-tailwindcss
# 然后在项目根目录下创建 .prettierrc 与 .prettierignore
touch .prettierrc .prettierignore
然后把下列内容粘贴进 .prettierrc:
代码已复制!
{ "semi": false, "singleQuote": true, "tabWidth": 2, "trailingComma": "es5", "printWidth": 100, "plugins": ["prettier-plugin-tailwindcss"] }
再编辑项目根目录下的 .prettierignore :
代码已复制!
# 依赖 node_modules .pnp .pnp.js # 构建产物 .next out build dist # 缓存 .cache .parcel-cache # 日志 *.log npm-debug.log* yarn-debug.log* yarn-error.log* # 环境变量 .env .env.local .env.development.local .env.test.local .env.production.local # 其他 .DS_Store *.min.js *.min.css public/ coverage/
最后添加格式化到 package.json 之中:
my-project/nextjs/package.json
代码已复制!
... "scripts": { "dev": "next dev", "build": "next build", "start": "next start", "lint": "eslint", "lint:fix": "eslint --fix", "format": "prettier --write \"**/*.{js,jsx,ts,tsx,json,css,md}\"", "format:check": "prettier --check \"**/*.{js,jsx,ts,tsx,json,css,md}\"" }, ...
保存以上文件,再在终端中执行 yarn format:check 检查语法:
Terminal
代码已复制!
yarn run v1.22.22 $ prettier --check "**/*.{js,jsx,ts,tsx,json,css,md}" Checking formatting... [warn] jsconfig.json [warn] next.config.js [warn] src/components/article/ArticleDynamicZone.js [warn] src/components/blog/FeaturedArticles.js [warn] src/components/course/Courses.js [warn] src/components/course/SpecializedCourses.js [warn] src/components/global/Background.js [warn] src/components/global/CustomizedHead.js [warn] src/components/global/Footer.js [warn] src/components/global/Nav.js [warn] src/components/global/Nav2.js [warn] src/components/global/Subscription.js [warn] src/components/home/Comparisons.js [warn] src/components/home/GuideChapters.js [warn] src/components/home/Playground.js [warn] src/components/home/Frameworks.js [warn] src/components/Layout.js [warn] src/components/NestedLayout.js [warn] src/components/shared/About.js [warn] src/components/shared/ArticleCard.js [warn] src/components/shared/ArticleCardContainer.js [warn] src/components/shared/Information.js [warn] src/lib/api.js [warn] src/lib/formatDate.js [warn] src/lib/markdown.js [warn] src/pages/_app.js [warn] src/pages/_document.js [warn] src/pages/_error.js [warn] src/pages/404.js [warn] src/pages/500.js [warn] src/pages/about.js [warn] src/pages/blog.js [warn] src/pages/blog/[...slug].js [warn] src/pages/category/[slug].js [warn] src/pages/course.js [warn] src/pages/index.js [warn] src/styles/globals.css [warn] src/utils/handleFormSubmit.js [warn] Code style issues found in 38 files. Run Prettier with --write to fix. error Command failed with exit code 1. info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command.
Prettier 会自动检查除 .prettierignore 声明以外的文件,上述报告显示它认为我们写的所有文件都有格式问题,不过我们仅需要格式特定的文件就行了,命令如下:
| 操作 | 命令行示例 | 受影响的文件 | 说明 |
|---|---|---|---|
| 格式化文件 | yarn format src/components/global/Subscription.js | @/components/global/Subscription.js | 格式化指定目录下的特定文件 |
| 格式化目录 | yarn format src/components/global | @/components/global/ 目录下的 | 格式化指定目录 |
| 格式化指定文件名 | yarn format Nav.js | @/components/global/Nav.js, @/components/global/Nav2.js | 格式化指定文件名,如果存在多个同名文件则会一并格式化 |
按照上述命令行提示按需格式化文件即可。
4.2 限定范围
通过减小范围避免撰写更多 class,缩短类名长度:
❌ Don't:
代码已复制!
class=" group-has-peer-checked:bg-slate-50 group-has-peer-checked:shadow-xl group-has-peer-checked:shadow-primary/12 lg:group-has-peer-checked:bg-none lg:shadow-none lg:group-has-peer-checked:shadow-none "
✅ Do it:
代码已复制!
class=" max-lg:group-has-peer-checked:bg-slate-50 max-lg:group-has-peer-checked:shadow-xl max-lg:group-has-peer-checked:shadow-primary/12 "
4.2 提取重复样式组合
便于统一修改全局复用样式,增加可读性,但需注意避免过度抽象,仅对真正复用的样式使用 @apply:
❌ Don't:
代码已复制!
<div class="max-w-8xl mx-auto px-6 md:px-9 lg:px-18"></div> <div class="max-w-8xl mx-auto px-6 md:px-9 lg:px-18"></div>
✅ Do it:
代码已复制!
@layer components { .customized-container { @apply max-w-8xl mx-auto px-6 md:px-9 lg:px-18; } }
代码已复制!
<div class="customized-container"></div> <div class="customized-container"></div>
4.3 向下控制优先
在父元素上汇总通用样式,既能减少子元素的重复定义,也便于统一修改继承或相关样式。因此,撰写 Tailwind 实用类时,请尽可能地采用 “自外而内、由上而下” 的组织顺序。
❌ Don't:
参考代码
代码已复制!
<ul class="relative flex bg-neutral-200 rounded-lg"> <li class="grow my-1 py-2 px-4 border-r border-neutral-600 text-neutral-900 text-center">Lorem ipsum</a> <li class="grow my-1 py-2 px-4 border-r border-neutral-600 text-neutral-900 text-center">Lorem ipsum</a> <li class="grow my-1 py-2 px-4 text-neutral-900 text-center">Lorem ipsum</a> </ul>
渲染结果
- Lorem ipsum
- Lorem ipsum
- Lorem ipsum
✅ Do it:
参考代码
代码已复制!
<ul class="relative flex justify-items-stretch py-1 bg-neutral-200 rounded-lg divide-x divide-neutral-600 text-center text-neutral-900"> <li class="grow py-2 px-4">Lorem ipsum</a> <li class="grow py-2 px-4">Lorem ipsum</a> <li class="grow py-2 px-4">Lorem ipsum</a> </ul>
渲染结果
- Lorem ipsum
- Lorem ipsum
- Lorem ipsum
可见实际渲染出来的视觉效果并无区别。
4.4 变形代替位移
position 属性会触发重排与重绘,而 transform 属性(translate)不影响文本流的情况下还会触发 GPU 加速,相比之下后者性能更好:
❌ Don't:
代码已复制!
left-2 top-6 duration-500 checked:top-0
✅ Do it:
代码已复制!
translate-x-2 translate-y-6 duration-500 checked:translate-y-0
4.5 限定动画类型
使用 transition-transform 限定过渡属性。duration-* 默认等同于 transition-all,会对所有可动画属性(如 padding、opacity)计算过渡,显式指定 transition-transform 可减少不必要的计算开销:
❌ Don't:
代码已复制!
translate-x-4 px-2 duration-300 hover:translate-x-0 md:px-4
✅ Do it:
代码已复制!
translate-x-4 px-2 transition-transform duration-300 hover:translate-x-0 md:px-4
五、原生 CSS 知识点
5.1 小技巧
内容仍在编辑,敬请期待
5.1.1 利用 border 画三角形
待撰写。

