Components · Data Display
Card
A surface that groups related content — cover, title, body and footer actions — in default/small size, bordered or flat, with a hover lift and a loading skeleton.
Examples
Size
Bordered
Hoverable
Card · cover · title + extra action · body · footer actions · hoverable
<!-- Paste-and-run: save as .html and open in a browser. No build step.
<aha-card> is the SAME shared custom element React and Vue consume — here in its
native form. Theming comes only from the --aha-* tokens in tokens.css. -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/ahaslides-product/ahaslides-design@master/lib/tokens.css">
<script type="module">
import 'https://cdn.jsdelivr.net/gh/ahaslides-product/ahaslides-design@master/lib/icons.js'; // registers <aha-icon>
import 'https://cdn.jsdelivr.net/gh/ahaslides-product/ahaslides-design@master/lib/aha-card.js'; // registers <aha-card>
</script>
<div style="display:flex; gap:16px; flex-wrap:wrap; align-items:flex-start">
<!-- Full card: cover · title + extra action · body · footer actions · hoverable -->
<aha-card card-title="Live poll" hoverable style="width:300px">
<img slot="cover" src="cover.jpg" alt="" />
<a slot="extra" href="#">Edit</a>
Which release cadence works best for your team?
<div slot="actions">
<aha-icon name="system-share-network" size="16"></aha-icon>
<aha-icon name="system-bookmark-simple" size="16"></aha-icon>
</div>
</aha-card>
<!-- Small size -->
<aha-card size="small" card-title="Weekly digest" style="width:240px">
A small card tightens its padding.
</aha-card>
<!-- Borderless + loading skeleton -->
<aha-card bordered="false" card-title="Borderless">No outline.</aha-card>
<aha-card card-title="Loading" loading style="width:240px"></aha-card>
</div>
import '@ahaslides-product/design/aha-card'; // registers <aha-card>
import '@ahaslides-product/design/icons'; // registers <aha-icon>
// The custom element renders its own shadow DOM; React just passes attributes + slots.
// Boolean attrs (hoverable/loading) render as presence; string attrs (size/bordered) pass through.
function DeckCard({ title, size, bordered, hoverable, loading, cover, extra, actions, children }) {
return (
<aha-card
card-title={title}
size={size}
bordered={bordered}
{...(hoverable ? { hoverable: '' } : {})}
{...(loading ? { loading: '' } : {})}
>
{cover && <img slot="cover" src={cover} alt="" />}
{extra && <a slot="extra" href="#">{extra}</a>}
{children}
{actions && <div slot="actions">{actions}</div>}
</aha-card>
);
}
// usage
<DeckCard title="Live poll" hoverable cover="cover.jpg" extra="Edit"
actions={<><aha-icon name="system-share-network" size="16" /><aha-icon name="system-bookmark-simple" size="16" /></>}>
Which release cadence works best for your team?
</DeckCard>
<DeckCard title="Weekly digest" size="small">A small card tightens its padding.</DeckCard>
<DeckCard title="Loading" loading />
// main.ts — register the elements + mark aha-* as custom elements
import '@ahaslides-product/design/aha-card';
import '@ahaslides-product/design/icons';
app.config.compilerOptions.isCustomElement = (tag) => tag.startsWith('aha-');
// Component.vue — cover · title + extra action · body · footer actions
<template>
<aha-card card-title="Live poll" hoverable style="width:300px">
<img slot="cover" src="cover.jpg" alt="" />
<a slot="extra" href="#">Edit</a>
Which release cadence works best for your team?
<div slot="actions">
<aha-icon name="system-share-network" size="16" />
<aha-icon name="system-bookmark-simple" size="16" />
</div>
</aha-card>
<aha-card card-title="Weekly digest" size="small">A small card tightens its padding.</aha-card>
<aha-card card-title="Borderless" bordered="false">No outline.</aha-card>
<aha-card card-title="Loading" loading />
</template>
API
| Prop | Type | Default | Notes |
|---|---|---|---|
card-title | string | — | Renders the header row; omit for a bare body |
size | 'default' | 'small' | default | Padding density of header/body/actions |
bordered | boolean-ish | true | Set bordered="false" for a flat, outline-less surface |
hoverable | boolean | false | Lift the card (border + shadow) on hover |
loading | boolean | false | Swap the body for a shimmer skeleton while content loads |
cover | slot | — | Full-bleed image/media above the header |
extra | slot | — | Right-aligned header action (a link or button) |
actions | slot | — | Ruled-off footer row of actions |
Install
# .npmrc — once: point the @ahaslides-product scope at GitHub Packages
@ahaslides-product:registry=https://npm.pkg.github.com
//npm.pkg.github.com/:_authToken=${GITHUB_TOKEN} # a GitHub token with read:packages
npm i @ahaslides-product/design
import '@ahaslides-product/design/tokens.css'; // once, at the app root
import '@ahaslides-product/design/aha-card'; // registers <aha-card>
Agent feed for this component (absolute, fetchable anywhere): card.agent.json · card.md · card.llms.txt
When to use
When to use
- Card — grouping related content into a titled surface on a dashboard or settings page
- List — many uniform rows of the same shape
- Descriptions — read-only label/value pairs about one entity
Titles are sentence case and name the group — 'Live results', not 'LIVE RESULTS'. Keep one idea per card; reach for hoverable only when the whole card is clickable. Use a cover for a media-led card, footer actions for card-level operations, and loading while the body fetches.
Surfaces
editor dashboard settings
Spec
Container border 1px #E3E3E3 · radius 12px · bg #FFFFFF · Cover full-bleed media above the header, no inset; hidden when the slot is empty · Header padding 16/20 · 16/24 SemiBold #1A1A1A · divider #F1F1F1 · extra action right-aligned · Body padding 20 · 14/22 #4A4A4A · Actions footer row, 12/20 padding, 8 gap, top divider #F1F1F1; hidden when empty · Small size header 8/12 · body 12 · actions 8/12 (antd Card size=small) · Bordered=false border-color transparent — a flat surface · Hover (hoverable) border #D3B4FF + shadow, motion-mid ease-out on a persistent node · Loading body → shimmer skeleton bars (aria-busy), motion-slow sweep