Tailwind CSS
Tailwind CSS 是一个原子化 CSS 框架,通过直接在 HTML/JSX 中组合预设的工具类来构建样式,无需编写自定义 CSS。当前版本为 v4,配置方式与 v3 有较大变化。
安装
Vite 项目(推荐)
npm install tailwindcss @tailwindcss/vite
// vite.config.ts
import { defineConfig } from "vite"
import tailwindcss from "@tailwindcss/vite"
export default defineConfig({
plugins: [tailwindcss()],
})
/* src/index.css */
@import "tailwindcss";
Next.js
npm install tailwindcss @tailwindcss/postcss postcss
// postcss.config.mjs
const config = { plugins: { "@tailwindcss/postcss": {} } }
export default config
/* app/globals.css */
@import "tailwindcss";
核心概念
Tailwind 的每个类名对应一条或几条 CSS 规则:
<!-- 传统 CSS 写法 -->
<div class="card">内容</div>
<!-- Tailwind 原子化写法 -->
<div class="rounded-lg bg-white p-6 shadow-md">内容</div>
命名规律
大多数工具类遵循 属性-值 的命名模式:
| 类名 | 对应 CSS |
|---|---|
flex |
display: flex |
p-4 |
padding: 1rem |
mt-2 |
margin-top: 0.5rem |
text-lg |
font-size: 1.125rem |
bg-blue-500 |
background-color: #3b82f6 |
rounded-md |
border-radius: 0.375rem |
w-full |
width: 100% |
hidden |
display: none |
布局
Display
<div class="block">块级</div>
<div class="inline-block">行内块</div>
<div class="inline">行内</div>
<div class="hidden">隐藏(display: none)</div>
<div class="invisible">不可见(visibility: hidden,仍占位)</div>
Flexbox
<!-- 基础 flex 容器 -->
<div class="flex items-center justify-between gap-4">
<div>左</div>
<div>右</div>
</div>
<!-- 主轴方向 -->
<div class="flex flex-row">水平(默认)</div>
<div class="flex flex-col">垂直</div>
<div class="flex flex-row-reverse">反向水平</div>
<div class="flex flex-col-reverse">反向垂直</div>
<!-- 主轴对齐(justify-content) -->
<div class="flex justify-start"> <!-- flex-start -->
<div class="flex justify-center"> <!-- center -->
<div class="flex justify-end"> <!-- flex-end -->
<div class="flex justify-between"> <!-- space-between -->
<div class="flex justify-around"> <!-- space-around -->
<div class="flex justify-evenly"> <!-- space-evenly -->
<!-- 交叉轴对齐(align-items) -->
<div class="flex items-start">
<div class="flex items-center">
<div class="flex items-end">
<div class="flex items-stretch">
<div class="flex items-baseline">
<!-- 换行 -->
<div class="flex flex-wrap">
<div class="flex flex-nowrap">
<!-- flex 子项 -->
<div class="flex-1"> <!-- flex: 1 1 0%,占据剩余空间 -->
<div class="flex-auto"> <!-- flex: 1 1 auto -->
<div class="flex-none"> <!-- flex: none,不伸缩 -->
<div class="flex-shrink-0"> <!-- 禁止收缩 -->
<div class="grow"> <!-- flex-grow: 1 -->
<div class="shrink-0"> <!-- flex-shrink: 0 -->
<div class="order-first"> <!-- order: -9999 -->
<div class="order-last"> <!-- order: 9999 -->
<div class="order-2"> <!-- order: 2 -->
Grid
<!-- 定义列数 -->
<div class="grid grid-cols-3 gap-4">
<div>1</div>
<div>2</div>
<div>3</div>
</div>
<!-- 自适应列(响应式卡片布局利器) -->
<div class="grid grid-cols-[repeat(auto-fill,minmax(200px,1fr))] gap-4">
<!-- 列跨越 -->
<div class="grid grid-cols-4">
<div class="col-span-2">占 2 列</div>
<div class="col-span-1">占 1 列</div>
<div class="col-start-4">从第 4 列开始</div>
</div>
<!-- 行跨越 -->
<div class="row-span-2">占 2 行</div>
<!-- 定义行高 -->
<div class="grid grid-rows-3 grid-flow-col">
<!-- place-items:同时设置 align-items 和 justify-items -->
<div class="grid place-items-center h-screen">居中</div>
Position
<div class="relative">
<div class="absolute top-0 right-0">右上角</div>
<div class="absolute inset-0">完全覆盖父元素</div>
<div class="absolute inset-x-0 bottom-0">底部横幅</div>
</div>
<div class="fixed bottom-4 right-4">固定在右下角</div>
<div class="sticky top-0 z-10">吸顶导航</div>
<!-- inset 简写 -->
<!-- inset-0 = top:0 right:0 bottom:0 left:0 -->
<!-- inset-x-0 = left:0 right:0 -->
<!-- inset-y-0 = top:0 bottom:0 -->
溢出与可见性
<div class="overflow-hidden">
<div class="overflow-auto">
<div class="overflow-x-auto overflow-y-hidden">
<div class="truncate">文本过长时显示省略号</div>
<div class="line-clamp-3">最多显示 3 行,超出省略</div>
间距
默认尺寸比例(1 单位 = 0.25rem = 4px)
| 类名 | rem | px |
|---|---|---|
p-0 |
0 | 0 |
p-1 |
0.25rem | 4px |
p-2 |
0.5rem | 8px |
p-4 |
1rem | 16px |
p-6 |
1.5rem | 24px |
p-8 |
2rem | 32px |
p-12 |
3rem | 48px |
p-16 |
4rem | 64px |
Padding
<div class="p-4">所有方向 1rem</div>
<div class="px-4">水平方向(padding-left + right)</div>
<div class="py-2">垂直方向(padding-top + bottom)</div>
<div class="pt-4 pb-2 pl-6 pr-8">分别设置四个方向</div>
<div class="ps-4 pe-4">逻辑属性(start/end,支持 RTL)</div>
Margin
<div class="m-4">所有方向</div>
<div class="mx-auto">水平居中</div>
<div class="mt-4 mb-8">上下</div>
<div class="-mt-2">负 margin(-0.5rem)</div>
Gap(flex/grid 子项间距)
<div class="flex gap-4"> <!-- 所有方向 -->
<div class="flex gap-x-4 gap-y-2"> <!-- 分别设置 -->
<div class="grid grid-cols-3 gap-6">
Space Between(自动为子项添加间距)
<div class="flex space-x-4">
<div>A</div> <!-- 除最后一个外,其他右侧加 1rem margin -->
<div>B</div>
<div>C</div>
</div>
尺寸
Width / Height
<!-- 固定尺寸 -->
<div class="w-4 h-4"> <!-- 1rem × 1rem -->
<div class="w-16 h-16"> <!-- 4rem × 4rem -->
<!-- 百分比 -->
<div class="w-1/2"> <!-- 50% -->
<div class="w-1/3"> <!-- 33.333% -->
<div class="w-2/3"> <!-- 66.666% -->
<div class="w-full"> <!-- 100% -->
<div class="h-full"> <!-- 100% -->
<div class="h-screen"> <!-- 100vh -->
<div class="h-dvh"> <!-- 100dvh(动态视口高度,适配移动端) -->
<!-- 自适应 -->
<div class="w-auto">
<div class="w-fit"> <!-- fit-content -->
<div class="w-max"> <!-- max-content -->
<div class="w-min"> <!-- min-content -->
<!-- 最大/最小尺寸 -->
<div class="max-w-sm"> <!-- max-width: 24rem -->
<div class="max-w-md"> <!-- max-width: 28rem -->
<div class="max-w-lg"> <!-- max-width: 32rem -->
<div class="max-w-xl"> <!-- max-width: 36rem -->
<div class="max-w-2xl"> <!-- max-width: 42rem -->
<div class="max-w-screen-lg"> <!-- max-width: 1024px -->
<div class="min-h-screen">
<div class="min-w-0"> <!-- 修复 flex 子项溢出问题 -->
<!-- 正方形/圆形 -->
<div class="size-10"> <!-- width: 2.5rem; height: 2.5rem -->
<div class="size-full">
排版
字体
<!-- 字体族 -->
<p class="font-sans"> <!-- 无衬线(默认) -->
<p class="font-serif"> <!-- 衬线 -->
<p class="font-mono"> <!-- 等宽 -->
<!-- 字体大小 -->
<p class="text-xs"> <!-- 0.75rem / 12px -->
<p class="text-sm"> <!-- 0.875rem / 14px -->
<p class="text-base"> <!-- 1rem / 16px -->
<p class="text-lg"> <!-- 1.125rem / 18px -->
<p class="text-xl"> <!-- 1.25rem / 20px -->
<p class="text-2xl"> <!-- 1.5rem / 24px -->
<p class="text-3xl"> <!-- 1.875rem / 30px -->
<p class="text-4xl"> <!-- 2.25rem / 36px -->
<!-- 字体粗细 -->
<p class="font-thin"> <!-- 100 -->
<p class="font-light"> <!-- 300 -->
<p class="font-normal"> <!-- 400 -->
<p class="font-medium"> <!-- 500 -->
<p class="font-semibold"> <!-- 600 -->
<p class="font-bold"> <!-- 700 -->
<p class="font-extrabold"> <!-- 800 -->
<!-- 行高 -->
<p class="leading-none"> <!-- 1 -->
<p class="leading-tight"> <!-- 1.25 -->
<p class="leading-normal"> <!-- 1.5 -->
<p class="leading-loose"> <!-- 2 -->
<p class="leading-6"> <!-- 1.5rem -->
<!-- 字间距 -->
<p class="tracking-tight"> <!-- -0.025em -->
<p class="tracking-normal"> <!-- 0em -->
<p class="tracking-wide"> <!-- 0.025em -->
<p class="tracking-wider"> <!-- 0.05em -->
<p class="tracking-widest"> <!-- 0.1em -->
文本对齐与装饰
<p class="text-left">
<p class="text-center">
<p class="text-right">
<p class="text-justify">
<p class="underline">下划线</p>
<p class="line-through">删除线</p>
<p class="no-underline">去掉下划线</p>
<p class="italic">斜体</p>
<p class="uppercase">全大写</p>
<p class="lowercase">全小写</p>
<p class="capitalize">首字母大写</p>
<p class="whitespace-nowrap">不换行</p>
<p class="whitespace-pre">保留空白</p>
<p class="break-words">长单词换行</p>
<p class="break-all">任意位置换行</p>
颜色
Tailwind 内置了丰富的调色板,格式为 {属性}-{颜色}-{深浅}:
<!-- 文字颜色 -->
<p class="text-gray-900">深灰文字</p>
<p class="text-blue-600">蓝色文字</p>
<p class="text-red-500">红色文字</p>
<p class="text-white">白色</p>
<p class="text-transparent">透明</p>
<!-- 背景颜色 -->
<div class="bg-white">
<div class="bg-gray-50">
<div class="bg-blue-500">
<div class="bg-gradient-to-r from-blue-500 to-purple-600">渐变背景</div>
<!-- 边框颜色 -->
<div class="border border-gray-200">
<div class="border-2 border-blue-500">
<!-- 颜色透明度(/ 语法)-->
<div class="bg-blue-500/50"> <!-- 50% 透明度 -->
<p class="text-black/80"> <!-- 80% 不透明 -->
<div class="border border-gray-900/10">
内置颜色色阶
每种颜色有 50 ~ 950 共 11 个色阶:
<!-- 以 blue 为例 -->
bg-blue-50 <!-- 极浅 -->
bg-blue-100
bg-blue-200
bg-blue-300
bg-blue-400
bg-blue-500 <!-- 标准色 -->
bg-blue-600
bg-blue-700
bg-blue-800
bg-blue-900
bg-blue-950 <!-- 极深 -->
内置颜色名:slate gray zinc neutral stone red orange amber yellow lime green emerald teal cyan sky blue indigo violet purple fuchsia pink rose
边框与圆角
<!-- 边框宽度 -->
<div class="border"> <!-- 1px -->
<div class="border-2"> <!-- 2px -->
<div class="border-4"> <!-- 4px -->
<div class="border-t"> <!-- 仅上边框 -->
<div class="border-x"> <!-- 左右边框 -->
<!-- 圆角 -->
<div class="rounded"> <!-- 0.25rem -->
<div class="rounded-md"> <!-- 0.375rem -->
<div class="rounded-lg"> <!-- 0.5rem -->
<div class="rounded-xl"> <!-- 0.75rem -->
<div class="rounded-2xl"> <!-- 1rem -->
<div class="rounded-full"> <!-- 9999px,圆形/胶囊 -->
<div class="rounded-none"> <!-- 0 -->
<div class="rounded-t-lg"> <!-- 仅上方圆角 -->
<div class="rounded-tl-xl"><!-- 仅左上角 -->
<!-- 边框样式 -->
<div class="border border-solid">
<div class="border border-dashed">
<div class="border border-dotted">
<!-- outline -->
<button class="outline-none focus:outline-2 focus:outline-blue-500">
阴影
<!-- box-shadow -->
<div class="shadow-sm">
<div class="shadow">
<div class="shadow-md">
<div class="shadow-lg">
<div class="shadow-xl">
<div class="shadow-2xl">
<div class="shadow-none">
<div class="shadow-inner"> <!-- 内阴影 -->
<!-- 彩色阴影 -->
<div class="shadow-lg shadow-blue-500/50">
<!-- 文字阴影 -->
<p class="text-shadow-sm">
<p class="text-shadow-md">
响应式设计
Tailwind 采用移动优先策略,断点前缀表示"此断点及以上生效":
| 前缀 | 最小宽度 | 对应设备 |
|---|---|---|
| 无前缀 | 0px | 所有设备(移动端基础样式) |
sm: |
640px | 大手机 |
md: |
768px | 平板 |
lg: |
1024px | 桌面 |
xl: |
1280px | 大屏桌面 |
2xl: |
1536px | 超宽屏 |
<!-- 移动端 1 列,平板 2 列,桌面 4 列 -->
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6">
<!-- 移动端隐藏,桌面显示 -->
<div class="hidden lg:block">桌面导航</div>
<!-- 移动端显示,桌面隐藏 -->
<div class="block lg:hidden">移动端菜单</div>
<!-- 字体随屏幕变化 -->
<h1 class="text-2xl md:text-4xl lg:text-6xl font-bold">
<!-- 内边距随屏幕变化 -->
<div class="p-4 md:p-8 lg:p-12">
<!-- 最大宽度容器 -->
<div class="max-w-sm md:max-w-2xl lg:max-w-5xl mx-auto px-4">
只在某个范围生效
<!-- 只在 md 断点生效(md 到 lg 之间) -->
<div class="md:max-lg:bg-blue-500">
状态变体
伪类
<!-- hover -->
<button class="bg-blue-500 hover:bg-blue-600 transition-colors">
<!-- focus -->
<input class="border focus:border-blue-500 focus:ring-2 focus:ring-blue-200 outline-none">
<!-- active(点击时) -->
<button class="active:scale-95 active:bg-blue-700">
<!-- disabled -->
<button class="disabled:opacity-50 disabled:cursor-not-allowed" disabled>
<!-- focus-within(子元素获焦时) -->
<div class="focus-within:ring-2 focus-within:ring-blue-500">
<input type="text" />
</div>
<!-- first / last / odd / even -->
<ul>
<li class="first:pt-0 last:pb-0 odd:bg-gray-50 even:bg-white py-2">
</ul>
<!-- 空状态 -->
<p class="hidden empty:block">暂无数据</p>
<!-- required / invalid / checked -->
<input class="border invalid:border-red-500 focus:invalid:ring-red-200" required>
<input type="checkbox" class="checked:bg-blue-500">
伪元素
<!-- before / after -->
<div class="before:content-[''] before:block before:w-4 before:h-4 before:bg-red-500">
<!-- placeholder -->
<input class="placeholder:text-gray-400 placeholder:italic">
<!-- selection(文本选中) -->
<p class="selection:bg-blue-200 selection:text-blue-900">
<!-- first-line / first-letter -->
<p class="first-line:uppercase first-letter:text-4xl first-letter:font-bold">
暗色模式
<!-- 跟随系统(class="dark" 也可以) -->
<div class="bg-white dark:bg-gray-900">
<p class="text-gray-900 dark:text-gray-100">
<img class="opacity-100 dark:opacity-80">
在 CSS 中开启 class 策略(手动切换):
/* src/index.css */
@import "tailwindcss";
@custom-variant dark (&:where(.dark, .dark *));
动画与过渡
Transition
<!-- 过渡(需搭配 hover 等状态变体) -->
<button class="bg-blue-500 hover:bg-blue-600 transition-colors duration-200">
<div class="opacity-0 hover:opacity-100 transition-opacity duration-300 ease-in-out">
<!-- transition-all:所有属性 -->
<div class="transition-all duration-200">
<!-- 常用 duration -->
<div class="duration-75"> <!-- 75ms -->
<div class="duration-150"> <!-- 150ms -->
<div class="duration-200"> <!-- 200ms -->
<div class="duration-300"> <!-- 300ms -->
<div class="duration-500"> <!-- 500ms -->
<!-- easing -->
<div class="ease-linear">
<div class="ease-in">
<div class="ease-out">
<div class="ease-in-out">
Transform
<!-- 缩放 -->
<div class="hover:scale-105 transition-transform">
<div class="hover:scale-x-110">
<div class="active:scale-95">
<!-- 位移 -->
<div class="hover:translate-x-1 hover:-translate-y-1">
<div class="-translate-x-1/2 -translate-y-1/2 absolute top-1/2 left-1/2"> <!-- 绝对居中 -->
<!-- 旋转 -->
<div class="hover:rotate-6">
<div class="-rotate-3">
<div class="rotate-180"> <!-- 旋转 180° -->
<!-- 倾斜 -->
<div class="skew-x-3">
内置动画
<div class="animate-spin"> <!-- 旋转(loading 图标) -->
<div class="animate-ping"> <!-- 扩散(通知徽章) -->
<div class="animate-pulse"> <!-- 脉冲(骨架屏) -->
<div class="animate-bounce"> <!-- 弹跳 -->
<!-- 骨架屏示例 -->
<div class="animate-pulse flex space-x-4">
<div class="rounded-full bg-gray-200 size-10"></div>
<div class="flex-1 space-y-2 py-1">
<div class="h-4 bg-gray-200 rounded w-3/4"></div>
<div class="h-4 bg-gray-200 rounded w-1/2"></div>
</div>
</div>
自定义配置(v4)
Tailwind v4 使用 CSS 文件配置,不再需要 tailwind.config.js:
/* src/index.css */
@import "tailwindcss";
/* 自定义主题变量 */
@theme {
/* 颜色 */
--color-primary-50: #eff6ff;
--color-primary-100: #dbeafe;
--color-primary-500: #3b82f6;
--color-primary-600: #2563eb;
--color-primary-900: #1e3a8a;
--color-brand: #e11d48;
/* 字体 */
--font-sans: "Inter", ui-sans-serif, system-ui, sans-serif;
--font-display: "Cal Sans", "Inter", sans-serif;
/* 断点 */
--breakpoint-xs: 480px;
--breakpoint-3xl: 1920px;
/* 间距 */
--spacing-18: 4.5rem;
--spacing-128: 32rem;
/* 圆角 */
--radius-4xl: 2rem;
/* 动画 */
--animate-fade-in: fade-in 0.3s ease-out;
@keyframes fade-in {
from { opacity: 0; transform: translateY(4px); }
to { opacity: 1; transform: translateY(0); }
}
}
/* 自定义工具类 */
@utility container {
width: 100%;
max-width: 1200px;
margin-inline: auto;
padding-inline: 1rem;
@media (width >= theme(--breakpoint-md)) {
padding-inline: 2rem;
}
}
/* 自定义变体 */
@custom-variant dark (&:where(.dark, .dark *));
@custom-variant hocus (&:hover, &:focus);
使用自定义主题变量:
<div class="bg-primary-500 text-white">
<div class="font-display text-4xl">
<div class="p-18">
<div class="animate-fade-in">
<button class="hocus:bg-primary-600"> <!-- 自定义变体 -->
任意值(Arbitrary Values)
对于无法通过预设类名表达的值,使用 [] 语法直接写入 CSS 值:
<!-- 任意尺寸 -->
<div class="w-[328px] h-[calc(100vh-64px)]">
<div class="max-w-[960px]">
<div class="top-[117px]">
<!-- 任意颜色 -->
<div class="bg-[#1da1f2]">
<div class="text-[rgb(99,102,241)]">
<div class="border-[color:var(--brand-color)]">
<!-- 任意字体大小 -->
<p class="text-[13px] leading-[1.6]">
<!-- 任意网格 -->
<div class="grid-cols-[1fr_2fr_1fr]">
<div class="grid-cols-[repeat(auto-fill,minmax(200px,1fr))]">
<!-- 任意变换 -->
<div class="translate-x-[calc(-50%-4px)]">
<!-- CSS 变量 -->
<div class="bg-[var(--card-bg)]">
<div class="text-[--text-color]"> <!-- v4 简写 -->
<!-- 任意属性(需要 Tailwind 不支持的 CSS 属性) -->
<div class="[mask-image:linear-gradient(to_bottom,white,transparent)]">
<div class="[scrollbar-width:none]">
<div class="[&>svg]:w-5 [&>svg]:fill-current"> <!-- 嵌套选择器 -->
提取组件(@apply)
当某组类名频繁重复时,可以用 @apply 提取到 CSS 类中:
/* src/index.css */
@import "tailwindcss";
@layer components {
.btn {
@apply inline-flex items-center justify-center rounded-md px-4 py-2
text-sm font-medium transition-colors focus-visible:outline-none
focus-visible:ring-2 disabled:pointer-events-none disabled:opacity-50;
}
.btn-primary {
@apply btn bg-primary-500 text-white hover:bg-primary-600
focus-visible:ring-primary-500;
}
.btn-outline {
@apply btn border border-gray-300 bg-transparent hover:bg-gray-100;
}
.card {
@apply rounded-xl border border-gray-200 bg-white p-6 shadow-sm;
}
.input {
@apply w-full rounded-md border border-gray-300 px-3 py-2 text-sm
placeholder:text-gray-400 focus:border-primary-500
focus:outline-none focus:ring-2 focus:ring-primary-200;
}
}
大多数情况下,推荐在 React/Vue 中封装组件而不是
@apply,@apply会增大 CSS 文件体积且降低可读性。
在 React 中的最佳实践
用 clsx / cn 合并条件类名
npm install clsx tailwind-merge
// src/lib/cn.ts
import { clsx, type ClassValue } from "clsx"
import { twMerge } from "tailwind-merge"
// twMerge 解决 Tailwind 类名冲突(如 p-4 和 px-2 同时存在时正确合并)
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs))
}
import { cn } from "@/lib/cn"
interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
variant?: "primary" | "outline" | "ghost"
size?: "sm" | "md" | "lg"
}
function Button({ variant = "primary", size = "md", className, ...props }: ButtonProps) {
return (
<button
className={cn(
// 基础样式
"inline-flex items-center justify-center rounded-md font-medium transition-colors",
"focus-visible:outline-none focus-visible:ring-2 disabled:opacity-50",
// 变体
{
"bg-blue-500 text-white hover:bg-blue-600": variant === "primary",
"border border-gray-300 hover:bg-gray-100": variant === "outline",
"hover:bg-gray-100": variant === "ghost",
},
// 尺寸
{
"h-8 px-3 text-xs": size === "sm",
"h-10 px-4 text-sm": size === "md",
"h-12 px-6 text-base": size === "lg",
},
// 允许外部覆盖
className,
)}
{...props}
/>
)
}
CVA(Class Variance Authority)
更系统的组件变体管理方案:
npm install class-variance-authority
import { cva, type VariantProps } from "class-variance-authority"
import { cn } from "@/lib/cn"
const buttonVariants = cva(
// 基础类名
"inline-flex items-center justify-center rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 disabled:opacity-50",
{
variants: {
variant: {
primary: "bg-blue-500 text-white hover:bg-blue-600 focus-visible:ring-blue-300",
destructive: "bg-red-500 text-white hover:bg-red-600",
outline: "border border-gray-300 bg-transparent hover:bg-gray-100",
ghost: "hover:bg-gray-100 hover:text-gray-900",
link: "text-blue-500 underline-offset-4 hover:underline",
},
size: {
sm: "h-8 px-3 text-xs",
md: "h-10 px-4",
lg: "h-12 px-6 text-base",
icon: "size-10",
},
},
compoundVariants: [
// 组合变体:primary + lg 时额外添加类
{ variant: "primary", size: "lg", class: "font-bold" },
],
defaultVariants: {
variant: "primary",
size: "md",
},
}
)
interface ButtonProps
extends React.ButtonHTMLAttributes<HTMLButtonElement>,
VariantProps<typeof buttonVariants> {}
function Button({ variant, size, className, ...props }: ButtonProps) {
return (
<button className={cn(buttonVariants({ variant, size }), className)} {...props} />
)
}
// 使用
<Button>默认</Button>
<Button variant="outline" size="sm">小号边框按钮</Button>
<Button variant="destructive" size="lg">大号危险按钮</Button>
常用组件片段
导航栏
<nav class="sticky top-0 z-50 border-b border-gray-200 bg-white/80 backdrop-blur-sm">
<div class="mx-auto flex h-16 max-w-7xl items-center justify-between px-4">
<a href="/" class="text-xl font-bold text-gray-900">Logo</a>
<div class="hidden md:flex items-center gap-6">
<a class="text-sm font-medium text-gray-600 hover:text-gray-900 transition-colors">首页</a>
<a class="text-sm font-medium text-gray-600 hover:text-gray-900 transition-colors">关于</a>
</div>
<button class="rounded-md bg-blue-500 px-4 py-2 text-sm font-medium text-white hover:bg-blue-600">
登录
</button>
</div>
</nav>
卡片
<div class="rounded-xl border border-gray-200 bg-white p-6 shadow-sm hover:shadow-md transition-shadow">
<div class="flex items-center gap-3 mb-4">
<img class="size-10 rounded-full object-cover" src="avatar.jpg" alt="">
<div>
<p class="text-sm font-semibold text-gray-900">张三</p>
<p class="text-xs text-gray-500">2 小时前</p>
</div>
</div>
<p class="text-sm text-gray-700 leading-relaxed line-clamp-3">内容文字...</p>
</div>
输入框
<div class="space-y-1.5">
<label class="text-sm font-medium text-gray-700">邮箱</label>
<input
type="email"
placeholder="name@example.com"
class="w-full rounded-md border border-gray-300 px-3 py-2 text-sm
placeholder:text-gray-400 transition-colors
focus:border-blue-500 focus:outline-none focus:ring-2 focus:ring-blue-200
invalid:border-red-500 invalid:focus:ring-red-200"
>
<p class="text-xs text-red-500 hidden peer-invalid:block">请输入有效邮箱</p>
</div>
徽章
<span class="inline-flex items-center rounded-full bg-blue-50 px-2.5 py-0.5 text-xs font-medium text-blue-700 ring-1 ring-inset ring-blue-700/10">
进行中
</span>
<span class="inline-flex items-center rounded-full bg-green-50 px-2.5 py-0.5 text-xs font-medium text-green-700 ring-1 ring-inset ring-green-600/20">
已完成
</span>
绝对居中
<!-- 方式一:flex -->
<div class="flex items-center justify-center min-h-screen">
<div>居中内容</div>
</div>
<!-- 方式二:grid -->
<div class="grid place-items-center min-h-screen">
<div>居中内容</div>
</div>
<!-- 方式三:absolute + translate -->
<div class="relative">
<div class="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2">
居中内容
</div>
</div>
常用速查
最常用的工具类组合
<!-- 水平垂直居中 -->
flex items-center justify-center
<!-- 卡片 -->
rounded-lg border bg-white p-6 shadow-sm
<!-- 按钮基础 -->
inline-flex items-center rounded-md px-4 py-2 text-sm font-medium transition-colors
<!-- 输入框基础 -->
w-full rounded-md border px-3 py-2 text-sm focus:outline-none focus:ring-2
<!-- 截断文本 -->
truncate
<!-- 多行截断 -->
line-clamp-2
<!-- 响应式容器 -->
mx-auto max-w-7xl px-4 sm:px-6 lg:px-8
<!-- 分隔线 -->
border-t border-gray-200
<!-- 遮罩层 -->
fixed inset-0 bg-black/50 backdrop-blur-sm z-50
<!-- 隐藏滚动条但可滚动 -->
overflow-auto [scrollbar-width:none] [-ms-overflow-style:none] [&::-webkit-scrollbar]:hidden