Skip to Content
@anker-in/headless-ui 2.0 is released 🎉

Card 卡片

灵活的容器组件,用于将相关信息组织在一个独立的内容块中。Card 是一个复合组件,包含多个子组件用于构建不同区域的布局。

何时使用

  • 需要展示一组相关的信息或内容时(如产品卡片、用户信息卡、文章预览)
  • 需要在页面中创建视觉上分离的内容块时
  • 需要组织复杂的信息层次结构时(标题、描述、内容、操作按钮)
  • 需要在网格或列表中展示多个信息项时

快速开始

安装

pnpm add @anker-in/headless-ui

基础用法

import { Card, CardHeader, CardTitle, CardContent } from '@anker-in/headless-ui' export default function App() { return ( <Card> <CardHeader> <CardTitle>卡片标题</CardTitle> </CardHeader> <CardContent> 卡片内容 </CardContent> </Card> ) }

演示

Card 组件演示

卡片组件支持多种布局组合,可灵活构建复杂的内容结构

加载中...
当前视口: 1920px × 600px场景: 默认状态
打开链接

组件结构

Card 是一个复合组件,由以下子组件构成:

<Card> <CardHeader> <CardTitle>标题</CardTitle> <CardDescription>描述</CardDescription> </CardHeader> <CardContent> 主要内容 </CardContent> <CardFooter> 底部操作区 </CardFooter> </Card>

子组件说明

组件用途典型内容
Card卡片容器包裹所有子组件
CardHeader头部区域包含标题和描述
CardTitle标题卡片的主标题文本
CardDescription描述卡片的副标题或简短描述
CardContent内容区域卡片的主要内容
CardFooter底部区域操作按钮或补充信息

布局变体

基础卡片

最简单的卡片布局,仅包含内容和底部操作。

<Card> <CardContent> <p>Charge Easily On the Go</p> <p>Charge Everything Everywhere Faster All at Once</p> </CardContent> <CardFooter> <Button>Learn More</Button> </CardFooter> </Card>

带头部的卡片

包含标题和描述的完整卡片。

<Card> <CardHeader> <CardTitle>快速充电技术</CardTitle> <CardDescription>为您的设备提供极速充电体验</CardDescription> </CardHeader> <CardContent> <p>采用最新的 PowerIQ 3.0 技术,充电速度提升 30%</p> </CardContent> <CardFooter> <Button>了解更多</Button> </CardFooter> </Card>

带图片的卡片

图片卡片通常将图片放在 CardContent 的开头。

<Card className="w-[400px]"> <CardContent className="p-0"> <img src="https://example.com/product.jpg" alt="Product" className="w-full rounded-t-lg" /> <div className="p-4"> <p>产品描述文本</p> </div> </CardContent> <CardFooter> <Button>查看详情</Button> </CardFooter> </Card>

仅内容卡片

有时卡片只需要展示内容,不需要头部和底部。

<Card> <CardContent> <h3 className="mb-2 text-lg font-semibold">特性亮点</h3> <ul className="list-disc space-y-1 pl-5"> <li>高效节能</li> <li>快速充电</li> <li>多设备兼容</li> </ul> </CardContent> </Card>

响应式布局

Card 组件内置响应式 padding,在不同屏幕尺寸下自动调整内外边距。

默认响应式行为

  • < 1920px: 标准 padding

    • Header/Footer: px-4 pt-6 / px-4 pb-6
    • Content: p-4
  • ≥ 1920px (lg-desktop): 增大 padding

    • Header/Footer: px-6 pt-8 / px-6 pb-8
    • Content: p-6

自定义响应式布局

<Card className=" w-full tablet:w-[400px] laptop:w-[450px] desktop:w-[500px] "> <CardContent> 响应式宽度卡片 </CardContent> </Card>

网格布局示例

<div className="grid gap-6 tablet:grid-cols-2 laptop:grid-cols-3"> <Card> <CardHeader> <CardTitle>卡片 1</CardTitle> </CardHeader> <CardContent>内容 1</CardContent> </Card> <Card> <CardHeader> <CardTitle>卡片 2</CardTitle> </CardHeader> <CardContent>内容 2</CardContent> </Card> <Card> <CardHeader> <CardTitle>卡片 3</CardTitle> </CardHeader> <CardContent>内容 3</CardContent> </Card> </div>

Props API

Card Props

属性类型默认值必需说明
childrenReactNode-卡片内容
classNamestring-自定义 CSS 类名

CardHeader Props

属性类型默认值必需说明
childrenReactNode-头部内容(通常包含 CardTitle 和 CardDescription)
classNamestring-自定义 CSS 类名

CardTitle Props

属性类型默认值必需说明
childrenReactNode-标题文本
classNamestring-自定义 CSS 类名

CardDescription Props

属性类型默认值必需说明
childrenReactNode-描述文本
classNamestring-自定义 CSS 类名

CardContent Props

属性类型默认值必需说明
childrenReactNode-主要内容
classNamestring-自定义 CSS 类名

CardFooter Props

属性类型默认值必需说明
childrenReactNode-底部内容(通常包含按钮)
classNamestring-自定义 CSS 类名

继承的 Props

所有 Card 子组件继承 React.HTMLAttributes<HTMLDivElement> 的所有属性。


类型定义

import * as React from 'react' // Card 组件 const Card = React.forwardRef< HTMLDivElement, React.HTMLAttributes<HTMLDivElement> >(({ className, ...props }, ref) => { // 组件实现 }) Card.displayName = 'Card' // CardHeader 组件 const CardHeader = React.forwardRef< HTMLDivElement, React.HTMLAttributes<HTMLDivElement> >(({ className, ...props }, ref) => { // 组件实现 }) CardHeader.displayName = 'CardHeader' // CardTitle 组件 const CardTitle = React.forwardRef< HTMLDivElement, React.HTMLAttributes<HTMLDivElement> >(({ className, ...props }, ref) => { // 组件实现 }) CardTitle.displayName = 'CardTitle' // CardDescription 组件 const CardDescription = React.forwardRef< HTMLDivElement, React.HTMLAttributes<HTMLDivElement> >(({ className, ...props }, ref) => { // 组件实现 }) CardDescription.displayName = 'CardDescription' // CardContent 组件 const CardContent = React.forwardRef< HTMLDivElement, React.HTMLAttributes<HTMLDivElement> >(({ className, ...props }, ref) => { // 组件实现 }) CardContent.displayName = 'CardContent' // CardFooter 组件 const CardFooter = React.forwardRef< HTMLDivElement, React.HTMLAttributes<HTMLDivElement> >(({ className, ...props }, ref) => { // 组件实现 }) CardFooter.displayName = 'CardFooter' export { Card, CardHeader, CardTitle, CardDescription, CardContent, CardFooter }

使用示例

基础产品卡片

展示产品信息的标准卡片。

import { Card, CardHeader, CardTitle, CardDescription, CardContent, CardFooter, Button } from '@anker-in/headless-ui' export default function ProductCard() { return ( <Card className="w-[350px]"> <CardHeader> <CardTitle>PowerCore 20K</CardTitle> <CardDescription>20,000mAh 超大容量移动电源</CardDescription> </CardHeader> <CardContent> <ul className="space-y-2 text-sm"> <li>✓ 支持 USB-C 双向快充</li> <li>✓ 可为笔记本电脑充电</li> <li>✓ 20,000mAh 超大容量</li> </ul> </CardContent> <CardFooter className="justify-between"> <span className="text-2xl font-bold">$49.99</span> <Button>立即购买</Button> </CardFooter> </Card> ) }

文章预览卡片

用于博客或新闻列表的文章卡片。

import { Card, CardHeader, CardTitle, CardDescription, CardContent, CardFooter, Button } from '@anker-in/headless-ui' export default function ArticleCard() { return ( <Card> <CardHeader> <CardTitle>如何选择合适的充电器</CardTitle> <CardDescription>发布于 2025-01-15 · 阅读时间 5 分钟</CardDescription> </CardHeader> <CardContent> <p className="text-sm text-gray-600"> 选择合适的充电器对于保护设备电池寿命至关重要。 本文将介绍如何根据设备类型、功率需求和使用场景选择最适合的充电方案... </p> </CardContent> <CardFooter> <Button variant="link">阅读全文 →</Button> </CardFooter> </Card> ) }

带图片的产品展示卡片

包含产品图片的展示卡片。

import { Card, CardContent, CardFooter, Button, Picture } from '@anker-in/headless-ui' export default function ImageProductCard() { return ( <Card className="w-[400px] overflow-hidden"> <CardContent className="p-0"> <Picture source="https://images.unsplash.com/photo-1609591328607-e0c4e2f1c4b3" alt="Wireless Charger" className="h-[250px] w-full object-cover" /> <div className="p-4"> <h3 className="mb-2 text-xl font-semibold">无线充电板</h3> <p className="text-sm text-gray-600"> 支持 iPhone 和 AirPods 同时充电,15W 快充,安全高效 </p> </div> </CardContent> <CardFooter className="justify-between pt-0"> <span className="text-xl font-bold">$39.99</span> <Button variant="secondary">加入购物车</Button> </CardFooter> </Card> ) }

用户信息卡片

展示用户资料的卡片。

import { Card, CardHeader, CardTitle, CardDescription, CardContent, CardFooter, Button } from '@anker-in/headless-ui' export default function UserProfileCard() { return ( <Card className="w-[350px]"> <CardHeader> <div className="mb-4 flex items-center gap-4"> <img src="https://i.pravatar.cc/100" alt="User avatar" className="size-16 rounded-full" /> <div> <CardTitle>张明</CardTitle> <CardDescription>产品设计师</CardDescription> </div> </div> </CardHeader> <CardContent> <p className="text-sm"> 热爱用户体验设计,专注于创造简洁优雅的产品界面。 5 年设计经验,服务过 20+ 客户。 </p> </CardContent> <CardFooter className="gap-2"> <Button variant="secondary" size="sm">发送消息</Button> <Button variant="link" size="sm">查看主页</Button> </CardFooter> </Card> ) }

统计数据卡片

展示关键指标的卡片。

import { Card, CardHeader, CardTitle, CardContent } from '@anker-in/headless-ui' export default function StatsCard() { return ( <Card> <CardHeader> <CardDescription>总销售额</CardDescription> <CardTitle className="text-3xl">$45,231.89</CardTitle> </CardHeader> <CardContent> <p className="text-sm text-green-600"> ↑ 20.1% 较上月 </p> </CardContent> </Card> ) }

表单卡片

包含表单输入的卡片。

import { Card, CardHeader, CardTitle, CardDescription, CardContent, CardFooter, Button, Input } from '@anker-in/headless-ui' export default function FormCard() { return ( <Card className="w-[450px]"> <CardHeader> <CardTitle>创建账户</CardTitle> <CardDescription>填写信息以创建新账户</CardDescription> </CardHeader> <CardContent className="space-y-4"> <div> <label className="mb-1 block text-sm font-medium">姓名</label> <Input placeholder="请输入姓名" /> </div> <div> <label className="mb-1 block text-sm font-medium">邮箱</label> <Input type="email" placeholder="your@email.com" /> </div> <div> <label className="mb-1 block text-sm font-medium">密码</label> <Input type="password" placeholder="••••••••" /> </div> </CardContent> <CardFooter className="justify-between"> <Button variant="secondary">取消</Button> <Button>创建账户</Button> </CardFooter> </Card> ) }

可访问性

语义化 HTML

Card 组件使用 <div> 元素,可根据内容语义添加适当的 ARIA 属性。

{/* 用作文章卡片 */} <Card as="article"> <CardHeader> <CardTitle>文章标题</CardTitle> </CardHeader> <CardContent>内容</CardContent> </Card> {/* 用作导航卡片 */} <Card as="nav" aria-label="快速导航"> <CardContent>导航链接</CardContent> </Card>

ARIA 标签

为卡片提供描述性的 ARIA 标签以增强可访问性。

{/* 可点击的卡片 */} <Card role="button" tabIndex={0} aria-label="查看产品详情" onClick={handleClick} onKeyDown={(e) => e.key === 'Enter' && handleClick()} > <CardContent>产品内容</CardContent> </Card> {/* 带状态的卡片 */} <Card aria-live="polite" aria-busy={isLoading}> <CardContent> {isLoading ? '加载中...' : '内容'} </CardContent> </Card>

键盘导航

确保卡片中的交互元素可通过键盘访问。

<Card> <CardContent> <button className="w-full text-left" onFocus={() => console.log('focused')} > 可聚焦内容 </button> </CardContent> <CardFooter> <Button>操作按钮</Button> </CardFooter> </Card>

最佳实践

为交互式卡片添加焦点样式

如果卡片本身可点击,确保有清晰的焦点指示器。

<Card tabIndex={0} className="cursor-pointer transition-all hover:shadow-lg focus:ring-2 focus:ring-primary" onClick={handleClick} > <CardContent>可点击的卡片</CardContent> </Card>

使用正确的标题层级

根据页面结构使用合适的标题标签。

<Card> <CardHeader> {/* 如果这是页面的主标题,使用 h1 */} <CardTitle as="h2">卡片标题</CardTitle> </CardHeader> </Card>

为图片提供替代文本

确保所有图片都有描述性的 alt 文本。

<Card> <CardContent className="p-0"> <img src="/product.jpg" alt="Anker PowerCore 20K 移动电源,黑色外观,侧面有 USB-C 接口" /> </CardContent> </Card>

确保足够的颜色对比度

卡片内的文本和背景应有足够的对比度(至少 4.5:1)。

测试屏幕阅读器

使用 VoiceOver、NVDA 或 JAWS 测试卡片内容的可读性。


最佳实践

✅ 推荐做法

  • 保持卡片内容简洁: 避免在单个卡片中放入过多信息
{/* ✅ 推荐:简洁的信息层次 */} <Card> <CardHeader> <CardTitle>快速充电</CardTitle> <CardDescription>30 分钟充满 50%</CardDescription> </CardHeader> <CardContent> <p>采用最新 PowerIQ 3.0 技术</p> </CardContent> <CardFooter> <Button>了解更多</Button> </CardFooter> </Card>
  • 统一卡片尺寸: 在网格布局中使用一致的卡片尺寸
<div className="grid gap-4 tablet:grid-cols-3"> <Card className="h-[300px]">内容 1</Card> <Card className="h-[300px]">内容 2</Card> <Card className="h-[300px]">内容 3</Card> </div>
  • 合理使用 CardFooter: 将操作按钮放在底部,保持对齐
<CardFooter className="justify-between"> <span>价格: $49.99</span> <Button>购买</Button> </CardFooter>
  • 响应式图片: 使用响应式图片避免布局撕裂
<Card> <CardContent className="p-0"> <img src="/image.jpg" alt="Product" className="aspect-video w-full object-cover" /> </CardContent> </Card>

❌ 避免做法

  • 不要嵌套卡片: 避免在 Card 内部嵌套另一个 Card
{/* ❌ 错误:嵌套卡片 */} <Card> <CardContent> <Card> <CardContent>嵌套内容</CardContent> </Card> </CardContent> </Card> {/* ✅ 正确:使用其他容器 */} <Card> <CardContent> <div className="rounded-lg border p-4"> 分组内容 </div> </CardContent> </Card>
  • 不要覆盖默认 padding: 除非必要,保留子组件的默认 padding
{/* ❌ 错误:破坏默认间距 */} <Card> <CardHeader className="p-0"> <CardTitle>标题</CardTitle> </CardHeader> </Card> {/* ✅ 正确:使用默认间距 */} <Card> <CardHeader> <CardTitle>标题</CardTitle> </CardHeader> </Card>
  • 不要在 CardHeader 中放置过多内容: CardHeader 应简洁明了
{/* ❌ 错误:Header 过于复杂 */} <CardHeader> <CardTitle>标题</CardTitle> <CardDescription>描述</CardDescription> <div>大量其他内容...</div> <Button>按钮</Button> </CardHeader> {/* ✅ 正确:内容放在 CardContent */} <CardHeader> <CardTitle>标题</CardTitle> <CardDescription>描述</CardDescription> </CardHeader> <CardContent> 详细内容和其他元素 </CardContent>

常见问题

如何自定义卡片的圆角?

使用 className 覆盖默认的 rounded-lg

<Card className="rounded-2xl"> <CardContent>更大的圆角</CardContent> </Card>

如何创建可点击的卡片?

添加点击事件和相应的样式:

<Card className="cursor-pointer transition-all hover:shadow-lg" onClick={() => console.log('clicked')} tabIndex={0} role="button" > <CardContent>点击我</CardContent> </Card>

如何移除卡片的阴影和边框?

覆盖默认样式:

<Card className="border-0 shadow-none"> <CardContent>无边框无阴影</CardContent> </Card>

如何在卡片中使用自定义 padding?

通过 className 自定义各个子组件的 padding:

<Card> <CardHeader className="px-8 pt-8"> <CardTitle>自定义 padding</CardTitle> </CardHeader> <CardContent className="px-8"> 内容 </CardContent> <CardFooter className="px-8 pb-8"> 底部 </CardFooter> </Card>

如何实现卡片的悬停效果?

使用 Tailwind CSS 的 hover: 前缀:

<Card className="transition-all duration-300 hover:scale-105 hover:shadow-xl"> <CardContent>悬停时放大</CardContent> </Card>

如何创建水平布局的卡片?

调整布局方向和子组件排列:

<Card className="flex flex-row"> <CardContent className="flex-shrink-0 p-0"> <img src="/image.jpg" alt="Product" className="h-full w-[200px] object-cover" /> </CardContent> <div className="flex flex-1 flex-col"> <CardHeader> <CardTitle>水平卡片</CardTitle> <CardDescription>左图右文布局</CardDescription> </CardHeader> <CardContent className="flex-1"> 详细描述内容 </CardContent> <CardFooter> <Button>操作</Button> </CardFooter> </div> </Card>

如何在移动端优化卡片布局?

使用响应式类名:

<Card className=" w-full tablet:w-[350px] laptop:w-[400px] "> <CardContent className=" p-3 tablet:p-4 laptop:p-6 "> 响应式内容 </CardContent> </Card>

相关资源


更新日志

v2.0.0 (2025-01-17)

  • 🎉 完善组件文档,添加详细的使用指南和最佳实践
  • ✨ 响应式 padding 支持(lg-desktop 断点)
  • 🔧 改进子组件的间距和排版
  • 📝 添加丰富的使用示例(产品卡片、文章卡片、表单卡片等)

本文档有帮助吗?

在 GitHub 上反馈

Last updated on