卡片组件是网页开发中最常见的UI设计模式之一。你在房产列表应用、SaaS分析面板、电子商务产品页面以及管理界面中都能看到它们的身影。
但是,要构建一个能够正确处理悬停效果、支持深色模式、保证可访问性,并且能在不同屏幕尺寸下正常使用的卡片组件,仅仅使用
在本教程中,你将通过Shadcn Space和Base UI的基本元素,构建四种可用于实际开发的卡片组件。每种卡片组件都是针对开发者们在开发过程中经常会遇到的具体UI场景而设计的。
学习完本教程后,你将能够掌握以下内容:
-
一种预览卡片组件,它具有悬停时显示图片的效果、叠加的箭头图标,以及用于展示详细信息的布局结构
-
一种分析卡片组件,它可以显示具体的指标数据、根据条件改变徽章的颜色,并且配有装饰性的背景图像
-
一种统计卡片组件,它采用了响应式设计,能够以四列的形式展示电子商务相关的统计数据,并配有图标徽章
-
一种电子商务产品变体卡片组件,用户可以通过它选择产品尺寸、切换加入愿望清单的功能、点击购物按钮时还会看到动画效果
目录
先决条件
在开始学习之前,请确保你已经具备以下条件:
-
已安装Node.js 18或更高版本
-
已经搭建好了Next.js或React项目框架
-
已在你的项目中初始化shadcn/ui插件(执行命令:
npx shadcn@latest init) -
已配置好Tailwind CSS样式
-
具备React和TypeScript的基本使用知识
如果您尚未初始化 shadcn/ui,请在项目根目录中运行 npx shadcn@latest init,并按照提示操作后再继续下一步。
为什么选择 shadcn/ui?
shadcn/ui 是一套基于 Radix UI 和 Base UI 构建的开源 React 组件库,这些组件采用了 Tailwind CSS 进行样式设计。
它的使用方式与传统组件库不同:您无需安装任何包,而是通过 CLI 直接将组件源代码复制到项目中。这样一来,您就能完全掌控所有代码。您可以随意阅读或修改这些代码,而且由于不会受到第三方库更新的影响,您的项目也不会出现任何问题。
它的一些主要优点包括:
-
默认具备无障碍访问功能:基于 Radix UI 和 Base UI 的基础组件构建而成
-
完全采用 Tailwind CSS 设计:无需外部 CSS 文件,也不会出现样式冲突问题
-
不存在任何绑定风险:这些组件被保存在您的
components/目录中,而非node_modules中 -
兼容多种开发环境:Next.js、Vite、Astro、Remix 等各种 React 框架均可使用
在本教程中会使用的 Card、Badge、Button 和 Separator 组件,都来自 shadcn/ui 的基础安装包。
什么是 Shadcn Space?
Shadcn Space 是一个开源的组件库,其中包含了许多已经准备好直接用于生产环境的 UI 组件。它基于 shadcn/ui 的基础组件集进行了扩展,提供了更多从常见设计模式衍生出来的变体,从而帮助开发者打造出更加美观的界面。
与 shadcn/ui 中默认提供的 Card 组件相比,Shadcn Space 提供的卡片组件是专门为特定的布局结构设计的,因此使用时能获得更清晰的结构框架。
Shadcn Space 中的每个组件都同时支持 Radix UI 和 Base UI 的基础功能。此外,这些组件还配备了 复制提示功能。本教程中使用的都是 Base UI 版本的组件,安装方法与其他 shadcn/ui 组件相同,只需通过一条 CLI 命令即可将代码文件添加到项目中。
您可以在 Shadcn 组件库 中浏览所有可用的组件。
你将构建什么
以下是您将制作的四款组件的简要介绍及其具体功能:
预览卡片(card-02)
-
当鼠标悬停时,图片会显示亮度变化及尺寸缩放动画
-
只有当鼠标悬停时才会出现的箭头图标
-
属性名称及其所在位置
-
采用蓝绿色调的价格标识
-
包含床、浴室等设施图标的行显示区
分析数据卡片(card-05)
-
输入TypeScript属性值,并可设置内置的默认数据集
-
两个指标列,中间用竖线分隔
-
根据数据的增减趋势,徽章颜色会相应变化
-
在右下角显示装饰性背景图片
统计信息卡片(card-06)
-
移动设备上显示时,采用四列自适应布局
-
每个指标都会使用对应的图标进行展示
-
徽章上会标明数据趋势的方向
-
最后一列会自动去除边框分隔线
电商产品变体卡片(card-17)
-
产品图片支持3D阴影效果,悬停时可放大查看
-
支持在深色模式下切换心愿单功能
-
尺寸选择器会突出显示当前选中的选项
-
点击按钮后,购物袋图标会显示出来
-
“立即购买”按钮带有CSS动画效果
-
配送日期会以序数后缀的形式进行显示
如何设置CLI注册表
在运行任何安装命令之前,你需要将Shadcn Space注册表添加到你的`components.json`文件中。
打开项目根目录下的`components.json`文件,然后添加`registries`字段:
{
"registries": {
"@shadcn-space": {
"url": "https://shadcnspace.com/r/{name}.json"
}
}
}
这样就可以告诉shadcn CLI在哪里查找以`@shadcn-space/`为前缀的组件。如果不执行这一步,本教程中的所有安装命令都会失败。
添加注册表后,你的`components.json`文件应该看起来像这样:
{
"$schema": "https://ui.shadcn.com/schema.json",
"style": "default",
"rsc": true,
"tsx": true,
"tailwind": {
"config": "tailwind.config.ts",
"css": "app/globals.css",
"baseColor": "neutral",
"cssVariables": true
},
"aliases": {
"components": "@/components",
"utils": "@lib/utils"
},
"registries": {
"@shadcn-space": {
"url": "https://shadcnspace.com/r/{name}.json"
}
}
}
如果你想详细了解CLI是如何与第三方注册表协同工作的,可以访问入门指南。如果你更喜欢通过视频来学习,也可以观看相关教程。
如何制作预览卡片(card-02版本)
预览卡片的用途
预览卡片专为房产列表、酒店页面或任何需要使用大图片并搭配下方详细信息的场景设计。
当用户将鼠标悬停在该卡片上时,图片会变暗并放大,同时角落会出现一个箭头图标。图片下方会显示标题、位置、价格信息以及各种设施详情。
如何安装预览卡片
根据您使用的包管理器,运行以下命令之一即可:
npm:
npx shadcn@latest add @shadcn-space/card-02
pnpm:
pnpm dlx shadcn@latest add @shadcn-space/card-02
Yarn:
yarn dlx shadcn@latest add @shadcn-space/card-02
Bun:
bunx --bun shadcn@latest add @shadcn-space/card-02
命令执行后,该组件会被复制到您的项目中,具体路径为:
components/
shadcn-space/
card/
Card-02.tsx
组件代码
import { Badge } from 「@components/ui/badge」;
import { Card } from 「@components/ui/card」;
import { ArrowRight, Bath, BedDouble, Expand } from "lucide-react";
const PreviewCard = () => (
Serenity Residential Home
15 S Aurora Ave, Miami
$570,000
5间卧室
3间浴室
120平方米
);
export default PreviewCard;
现在让我们来了解一下这段代码的具体工作原理。
1. 组合元素在悬停时的行为
外层Card元素上应用的group类是这个组件的核心。任何带有groupHover:类的子元素都会在卡片被悬停时产生相应的反应,而不仅仅是那个单独的元素。
这就是图片会变暗(group-hover:brightness-50)、放大(group-hover:scale-125),以及箭头图标会出现(groupHover:block)的原因。
2. 图片缩放时的溢出裁剪功能
如果图片包装元素上没有设置overflow-hidden,那么当应用scale-125这种缩放效果时,图像的边缘就会超出卡片的边框。而通过使用overflow-hidden,就可以将图像裁剪掉,使其始终保持在卡片范围内。
请注意,无论是包装元素还是图片本身,都应用了rounded-t-2xl这个样式,这样在过渡过程中图形的圆角效果就能保持一致。
3. 便利设施行中的逻辑边框属性
在便利设施行中,使用了border-e而不是border-r。这是一个CSS的逻辑属性,表示“位于内联方向的边界”。在从左到右的布局中,它指的是右侧;而在从右到左的布局中,这个属性会自动调整方向。对于那些可能需要支持多种语言环境的组件来说,使用逻辑属性是一种很好的开发习惯。
实时预览:

如何构建分析卡片(card-05)
分析卡片的用途
分析卡片是一种简洁的仪表盘组件。它能够并排显示两个指标的值以及它们的百分比变化情况,右下角还会展示一张装饰性的图表。
这个组件的类型定义使用了TypeScript接口,因此可以很容易地从API中获取实际数据并将其填充到组件中。
如何安装分析卡片
npx shadcn@latest add @shadcn-space/card-05
命令行工具会将这个组件复制到以下路径:
components/
shadcn-space/
card/
card-05.tsx
组件代码
import { Badge } from "@/components/ui/badge";
import { Card, CardContent } from "@components/ui/card";
import { Separator } from "@components/ui/separator";
import { cn } from "@lib/utils";
type DashboardMetric = {
label: string;
value: string;
percentage: string;
isPositive?: boolean;
};
type MainDashboardData = {
title: string;
description: string;
metrics: DashboardMetric[];
};
type WidgetProps = {
mainDashboard?: MainDashboardData;
};
const mainDashboardData: MainDashboardData = {
title: "分析仪表盘",
description: "查看所有统计数据",
metrics: [
{
label: "收入",
value: "$27,850",
percentage: "+18%",
isPositive: true,
},
{
label: "支出",
value: "$18,453",
percentage: "-5%",
isPositive: false,
},
],
};
const AnalyticsCard = ({ mainDashboard = mainDashboardData }: WidgetProps) => {
return (
{metric.percentage}
>
{index < mainDashboard.metrics.length - 1 && (
>
))}
>
分析卡的工作原理:
1. 带有默认数据集的可选属性
该组件接受一个可选的 `mainDashboard` 属性。如果您不传递任何值,系统会使用 `mainDashboardData`,这个常量在同一文件中定义:
const AnalyticsCard = ({ mainDashboard = mainDashboardData }: WidgetProps) => {
这种设计使得该组件在演示或Storybook环境中可以立即使用,而在生产环境中则完全依赖真实的API数据。要将其与实时数据连接起来,您只需传递一个形状与 `MainDashboardData` 相匹配的属性即可。
2. 使用 `cn()` 实现条件性的徽标颜色设置
cn() 这个工具函数(来自 `@/lib/utils`)能够合并Tailwind样式类名,并优雅地处理条件逻辑。它还能自动消除重复的Tailwind样式类名,而普通的模板字面量是无法做到这一点的:
className={cn(
"font-normal text-muted-foreground",
metric.isPositive ? "bg-teal-400/10" : "bg-red-500/10"
)}
3>仅在指标之间添加分隔符,不在最后一个指标之后添加
Separator 组件仅在指标之间显示分隔符,而不会在最后一个指标之后出现。通过索引检查可以实现这一逻辑:
)}
4>绝对定位的装饰性图像
图表图像通过 `absolute bottom-0 right-0` 的定位方式固定在卡片的右下角。在小型屏幕上,会使用 `hidden sm:block` 代码使该图像隐藏起来,以避免移动设备上的布局问题。父级 `Card` 元素采用 `relative` 定位方式来容纳这个图像。
实时预览:

如何构建统计卡片(card-06)
统计卡片的功能
统计卡片以水平网格的形式显示四个电子商务指标:订单量、销售额、利润和支出。每一列都包含一个图标、一个具体的数值、一个时间周期标签,以及一个用于显示百分比变化趋势的徽标。
这种布局具有完全的响应式特性:在中等尺寸的屏幕上,它会从四列变为两列;在移动设备上,则会以堆叠的形式显示这些数据。
该卡片使用了 `@iconify/react` 来生成图标,而不是 `lucide-react`。这样一来,您就可以通过基于字符串的图标名称来使用成千上万的图标集了。
如何安装统计信息卡片组件
首先,如果还没有安装@iconify/react,请先进行安装:
npm install @iconify/react
然后添加该卡片组件:
npx shadcn@latest add @shadcn-space/card-06
命令行工具会将该组件复制到以下路径中:
components/
shadcn-space/
card/
card-06.tsx
组件代码
"use client";
import { Icon } from "@iconify/react";
import { Card, CardContent } from "@/components/ui/card";
import { Badge } from"@components/ui/badge";
const StatisticsCard = () => {
const EcommerceActions = [
{
title: "订单",
subtitle: "5868",
cardIcon: "solar:bag-4-line-duotone",
badgeColor: "bg-teal-400/10",
statusValue: "+18%",
statusIcon: "solar:course-up-line-duotone",
},
{
title: "销售额",
subtitle: "$96,850",
cardIcon: "solar:box-line-duotone",
badgeColor: "bg-orange-400/10",
statusValue: "-5%",
statusIcon: "solar:course-down-line-duotone",
},
{
title: "利润",
subtitle: "$82,906",
cardIcon: "solar:chart-square-line-duotone",
badgeColor: "bg-teal-400/10",
statusValue: "+18%",
statusIcon: "solar:course-up-line-duotone",
},
{
title: "支出",
subtitle: "$14,653",
cardIcon: "solar:star-line-duotone",
badgeColor: "bg-teal-400/10",
statusValue: "+18%",
statusIcon: "solar:course-up-line-duotone",
},
];
return (
>
{item.title}</h5>
>
{item.subtitle}</h5>
过去7天
>
>
>
>
))}
>
>
>
);
};
export default StatisticsCard;
统计卡片的运作原理:
1. 基于数组的数据驱动布局
所有四个指标都存储在`EcommerceActions`数组中。添加或删除某个指标只需更新该数组即可,JSX代码保持不变。对于任何具有重复结构的组件来说,这种做法都是正确的——将数据与标记分离处理。
2. 自适应的列宽设置
每列都会使用三种不同的宽度样式来适应不同的屏幕分辨率:
-
在移动设备上使用`w-full`,此时会显示为单列并垂直堆叠
-
在中等尺寸的屏幕上使用`md:w-6/12`,此时会显示为两列
-
在大型屏幕上使用`lg:w-3/12`,此时会显示为四列且宽度相等
`CardContent`元素上的`flex-wrap`属性使得在小屏幕上列能够自动换行;而`lg:flex-nowrap`属性则确保在大屏幕上所有列仍能显示在同一行中。
3>使用`last:`属性去除最后一列的边框
`last:border-e-0`这个类会移除最后一行列的右侧边框。如果没有这个设置,卡片的右边缘就会出现多余的边框。
`last:`是一个Tailwind伪类,它能够精准地定位到某个组中的最后一个子元素,这样比手动计算索引要更加简洁方便。
4>为什么在这里需要使用`”use client”`
`@iconify/react`这个包需要在浏览器环境中运行。在Next.js中,如果某个组件引用了仅适用于客户端的库,那么就需要在文件的开头添加`”use client”`指令。否则,在渲染过程中服务器会抛出错误。
实时预览:

如何构建电商产品变体卡片(card-17)
电商产品变体卡片的功能
这是本教程中最具交互性的卡片。它用于展示鞋子信息,支持悬停放大、添加到心愿单、选择尺码、切换包装方式,以及点击购买按钮时触发波纹动画效果。所有这些交互功能都是通过React的`useState`状态管理机制来实现的,因此不需要使用任何外部状态管理库。
如何安装产品变体卡片组件
npx shadcn@latest add @shadcn-space/card-17
通过命令行工具,该组件会被安装到以下路径:
components/
shadcn-space/
card/
card-17.tsx
组件代码
"use client";
import { useState } from "react";
import { Card, CardContent, CardFooter } from "@/components/ui/card";
import { Button } from "@components/ui/button";
import { Heart, ShoppingBag } from "lucide-react";
import { cn } from "@lib/utils";
const sizes = ["7", "8", "9", "10"];
const getDeliveryDate = () => {
const date = new Date();
date.setDate(date.getDate() + 3);
const day = date.getDate();
const month = [
"Jan", "Feb", "Mar", "Apr", "May", "Jun",
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
][date.getMonth()];
const suffix = ["th", "st", "nd", "rd"][
day % 10 > 3 ? 0 : (day % 100 - day % 10 !== 10 ? day % 10 : 0)
];
return `${day}${suffix} ${month}`;
};
export default function EcommerceProductCard() {
const [activeSize, setActiveSize] = useState(1);
const [isWishlisted, setIsWishlisted] = useState(false);
const [inBag, setInBag] = useState(false);
return (
{/* 图片区域 */}
{/* 折扣标志 */}
-21%
{/* 加入愿望清单按钮 */}
{/* 信息区域 */}
Nike
Air Max Pulse跑鞋
优惠幅度:21%
价格:$150
折扣后价格:$119
配送日期:{" "}
{getDeliveryDate}
{/* 动作区域 */}
);
}
电子商务产品变体卡的运作原理:
1. 带有名称的悬停组范围
这种卡片使用了两个独立的悬停组范围:外部卡片使用group/card,而“立即购买”按钮则使用group/btn。Tailwind框架通过添加/name后缀来区分这两个范围:
// 卡片级别的悬停效果:会放大产品图片
<Card className="... group/card">
<img className="... group-hover/card:scale-105" />
// 按钮级别的悬停效果:会触发波纹动画
<Button className="... group/btn">
如果没有使用带有名称的悬停组,那么当鼠标悬停在按钮上时,也会触发卡片的悬停效果。而/card和/btn这些后缀正好避免了这种情况。
2. “立即购买”按钮上的CSS波纹动画
这种波纹效果是通过纯CSS的缩放动画实现的。当鼠标悬停在按钮上时,一个白色圆圈(w-8 h-8 rounded-full)会从scale-0状态逐渐变为scale-[20]状态。Buttonoverflow-hidden属性使得这个动画效果仅限于按钮范围内显示,而标签上的z-10属性则确保文字能够始终显示在扩大的圆圈上方。
3>交货日期的序数后缀生成逻辑
getDeliveryDate()函数会计算出三天后的日期,并为其添加正确的序数后缀(如“th”、“nd”等):
const suffix = ["th", "st", "nd", "rd"][
day % 10 > 3 ? 0 : (day % 100 - day % 10 !== 10 ? day % 10 : 0)
];
这种逻辑能够正确处理第11天、第12天和第13天的情况——在这些情况下,无论日期的个位数是多少,都会使用“th”作为后缀。这是序数格式化中常见的一个注意事项。
4>交货日期文本框上的suppressHydrationWarning功能
交货日期是在渲染时通过new Date()计算得出的;服务器在接收到请求时会重新计算这个日期,而客户端在数据加载完成时也会再次进行计算。
如果不同区域的时区设置存在差异,React会抛出“数据不一致”的警告。suppressHydrationWarning功能可以针对特定节点屏蔽这种警告,而不会影响到页面的其他部分。
实时预览:

快速参考表
卡片类型
标识符
使用场景
预览卡片
card-02
用于房产列表、酒店信息展示以及产品预览页面
分析卡片
card-05
包含各种指标数据的仪表盘组件
统计卡片
card-06
用于展示电子商务相关统计数据的信息框
产品变体卡片
card-17
用于产品页面,可让用户选择商品尺寸并加入购物车
要安装其中任何一项,只需在 CLI 命令中替换相应的标识符即可:
npx shadcn@latest add @shadcn-space/
关键概念总结
以下是本教程中四个示例所使用的 Tailwind、React 以及 TypeScript 的核心设计模式的汇总。
Tailwind 组合样式与悬停效果
当父元素上应用了 group 类时,其子元素可以通过 groupHover: 类来响应父元素的悬停状态。
对于嵌套的悬停效果范围,可以使用诸如 group/card 或 group/btn 这样的命名组合,并配合 group-hover/card: 或 groupHover/btn: 来实现特定的样式效果。这样就能避免悬停样式影响到其他组件。
`cn()` 实用工具
来自 `<@/lib/utils>` 的 `cn()` 函数可以合并 Tailwind 中的类名字符串,处理条件性的类逻辑,并消除重复的辅助函数。每当需要使用条件性类样式时,都可以优先使用这个函数而非模板字面量。
`last:` 伪类
`last:` 伪类可用于选中某个组中的最后一个子元素。在统计信息卡片中,`last(border-e-0)` 可以去除最后一列的边框,而无需在 JavaScript 中进行索引判断。
CSS 逻辑属性
border-e 表示“内联方向的边界”,在左-to-right 的布局中指的是右侧,在右-to-left 的布局中则指左侧。使用 border-e、ps-、pe- 这类逻辑属性,而非传统的 border-r、pl-、pr-,可以让组件默认具备适应不同语言环境的布局能力。
TypeScript 中带默认值的可选属性
在函数签名中直接为属性指定默认值(例如 `{ mainDashboard = mainDashboardData }: WidgetProps `),是一种简洁的实现方式。这种机制既能为组件提供合理的默认值,同时又保留了配置的灵活性。无论是用于演示、Storybook 还是实际的生产环境,并且配合真实的 API 数据使用,这种写法都是可行的。
在 Next.js 应用程序路由器中使用 `use client`
任何使用了 `useState`、浏览器 API 或仅适用于客户端的库(如 `@iconify/react`)的组件,都必须在文件顶部添加 `"use client"` 指令。如果不加这个指令,Next.js 应用程序路由器会尝试在服务器端渲染这些组件,从而导致错误。
抑制 hydration 警告
当服务器端渲染的值(例如当前日期或时间)由于时区差异与客户端渲染的值不同时,React 会发出 hydration 不匹配的警告。通过在特定的元素上添加 `suppressHydrationWarning`,就可以消除这个警告,而不会影响组件树的其余部分。
CSS涟漪动画效果
无需使用JavaScript,只需在位于overflow-hidden容器内的rounded-full元素上应用从scale-0到scale-[N]的过渡效果,就能实现CSS涟漪动画效果。当鼠标悬停在该元素上时,该圆圈会扩大,但其边缘仍会被容器的边界所限制;标签文本则通过relative z-10样式位于其上方。
总结
在本教程中,你使用了shadcn/ui和Base UI的基本组件,构建了四个可供实际使用的卡片组件:
-
预览卡片:包含悬停时的图像动画效果、溢出内容的裁剪功能,以及用于排列标签的逻辑结构。
-
分析卡片:支持输入属性并设置默认值,通过cn()函数实现条件性的徽标颜色变化,同时还包含一个绝对定位的装饰性图像。
-
统计卡片:采用数据驱动的布局方式,弹性柱会根据屏幕尺寸自动调整大小,同时会使用last:规则自动去除最后一个元素的边框。
-
电商产品变体卡片:支持命名悬停组,拥有CSS涟漪效果按钮,能够对日期进行顺序格式化处理,同时还具备抑制显示警告信息的功能。
每个卡片只需通过一条CLI命令即可安装到项目中,这些代码完全归你所有,你可以根据自己的设计需求对其进行修改。
这里介绍的各种设计模式,无论是命名悬停组、支持默认值的输入属性,还是逻辑性的CSS样式设置,都不仅适用于卡片组件,你在使用shadcn/ui和Tailwind CSS构建其他UI组件时,也会发现它们非常有用。
资源链接
-
Shadcn UI:组件库及文档主页。
-
-
-
-
-
-
-
-
-
-
Comments are closed.




