Card
Group related content and actions in a container.
Basic usage
Keep project details together so your team can work from the same plan.
import { Card } from '@ifhover/leaf-ui';
export function CardBasic() {
return (
<Card title="Project overview">
Keep project details together so your team can work from the same plan.
</Card>
);
}
Sizes
size offers small, default and large cards, adjusting padding, heading typography and heading height together. Embedded Tabs follow the card size unless overridden with tabs.size. Buttons, inputs and other controls inside the card use their own size.
These cards share the same content to compare headings, tab bars and content spacing. Each card's tabs switch independently.
Keep project details, design and development working from the same plan.
Keep project details, design and development working from the same plan.
Keep project details, design and development working from the same plan.
import { Card, Space } from '@ifhover/leaf-ui';
export function CardSizes() {
const items = [
{
key: 'overview',
label: 'Overview',
children: 'Keep project details, design and development working from the same plan.',
},
{
key: 'activity',
label: 'Activity',
children: 'Updated the component guidelines and invited two teammates today.',
},
];
return (
<Space direction="vertical" align="stretch" size="lg">
<Card title="Small" size="sm" tabs={{ items }} />
<Card title="Default" tabs={{ items }} />
<Card title="Large" size="lg" tabs={{ items }} />
</Space>
);
}
Content sections
Use spacing to group content. Set divided to separate the heading, body and footer actions.
The heading, description and body form natural sections.
Enable divided to separate the heading, body and footer actions.
import { Button, Card, Flex, Space, Tag } from '@ifhover/leaf-ui';
export function CardSections() {
return (
<Space direction="vertical" align="stretch" size="lg">
<Card
title="Project overview"
description="Group content with spacing"
extra={<Tag>In progress</Tag>}
>
The heading, description and body form natural sections.
</Card>
<Card
divided
title="Content sections"
footer={
<Flex justify="end" gap="sm" wrap>
<Button variant="outline">Cancel</Button>
<Button>Confirm</Button>
</Flex>
}
>
Enable divided to separate the heading, body and footer actions.
</Card>
</Space>
);
}
Tabbed card
Use tabs.items to define labels and their content. Without a title, tabs fill the heading height with vertically centered labels, and Card's extra appears at their trailing edge.
Keep design and development working from the same plan.- Owner
- Rowan Lin
- Team members
- 8 people
import { Button, Card, Descriptions, Space } from '@ifhover/leaf-ui';
export function CardTabs() {
return (
<Card
extra={<Button variant="link">View project</Button>}
tabs={{
'aria-label': 'Project overview',
items: [
{
key: 'overview',
label: 'Overview',
children: (
<Space direction="vertical" align="stretch" size="md">
<span>Keep design and development working from the same plan.</span>
<Descriptions
columns={2}
items={[
{ key: 'owner', label: 'Owner', children: 'Rowan Lin' },
{ key: 'members', label: 'Team members', children: '8 people' },
]}
/>
</Space>
),
},
{
key: 'activity',
label: 'Activity',
children: 'Updated the component guidelines and invited two teammates today.',
},
],
}}
/>
);
}
Heading with tabs
With a title or description, tabs sit beneath the heading. There is no divider between the heading and tabs; the divider runs below the tab bar. Control selection with tabs.activeKey / tabs.onChange. Visited pages retain input state by default, and divided continues to control the footer divider and surface.
import { Button, Card, Descriptions, Flex, Form, FormField, Input, Tag } from '@ifhover/leaf-ui';
import { useId, useState } from 'react';
export function CardTabsWithTitle() {
const [activeKey, setActiveKey] = useState('settings');
const [saved, setSaved] = useState(false);
const formId = useId();
return (
<Card
divided
title="Project settings"
description="Manage project details and team members."
extra={<Tag>{saved ? 'Saved' : 'Draft'}</Tag>}
tabs={{
activeKey,
onChange: setActiveKey,
items: [
{
key: 'settings',
label: 'Details',
children: (
<Form
id={formId}
layout="vertical"
onChange={() => setSaved(false)}
onSubmit={(event) => {
event.preventDefault();
setSaved(true);
}}
>
<FormField label="Project name">
<Input name="name" defaultValue="Leaf Design System" required />
</FormField>
<FormField label="Contact email">
<Input name="email" type="email" defaultValue="team@example.com" required />
</FormField>
</Form>
),
},
{
key: 'members',
label: 'Members',
children: (
<Descriptions
columns={1}
items={[
{ key: 'owner', label: 'Project lead', children: 'Rowan Lin' },
{ key: 'designer', label: 'Designer', children: 'Alex Chen' },
]}
/>
),
},
{ key: 'billing', label: 'Billing', disabled: true, children: null },
],
}}
footer={
<Flex align="center" gap="sm" justify="end">
<Button type="submit" form={formId} disabled={activeKey !== 'settings'}>
Save changes
</Button>
</Flex>
}
/>
);
}
Card API
Types
Import the exported types when defining props or building item lists.
import type { CardProps, CardTabsProps, TabItem } from '@ifhover/leaf-ui';
Usage notes
Place buttons or links in extra and footer. Use an explicit button or link for actions.
tabs reuses Tabs configuration and interaction with top-aligned line tabs. tabs.items[].children provides each page; Card's children may hold shared content below all pages. loading replaces the content with skeletons while headings, tabs and actions stay available. Tabs follow the card's size; set tabs.size to override. An empty item list restores the ordinary card layout.
Use tabs to configure disabled tabs, icons, manual keyboard activation, retained or destroyed pages, overflow menus and add, close or reorder callbacks. tabs.extra always belongs to the tab bar; with a title, Card's extra stays alongside the heading.
Surfaces and rich content
description adds supporting text beneath the title. Ordinary cards have no internal dividers by default; tabbed cards always have a divider below navigation. divided adds a footer divider and muted surface with the same vertical action spacing as Modal and Drawer. Customize surfaces and borders through ConfigProvider components.Card.
CardTabs API