GitHub

card

content and footer are raw HTML slots โ€” they pass through as-is. Wrap user-supplied values in escHtml() before composing them into the string.

Recent activity

No activity in the last 7 days.

card({
  title:   'Recent activity',
  content: `<p class="u-text-muted u-text-sm">No activity in the last 7 days.</p>`,
  footer:  button({ label: 'View all', href: '/activity', variant: 'ghost', size: 'sm' }),
})

With image

Use flush to remove body padding, then add an image at the top and restore padding on the text content below it.

๐ŸŒ„

Mountain retreat

A peaceful escape in the highlands. Three nights, all-inclusive.

card({
  flush: true,
  content: `
    <div class="u-rounded-md u-overflow-hidden" style="height:180px;background:...">
      <img src="/img/retreat.jpg" alt="Mountain retreat" style="width:100%;height:100%;object-fit:cover">
    </div>
    <div class="u-p-5">
      <p class="u-font-semibold u-mb-2">Mountain retreat</p>
      <p class="u-text-muted u-text-sm">A peaceful escape in the highlands.</p>
    </div>
  `,
  footer: button({ label: 'Book now', href: '/book', variant: 'primary', size: 'sm' }),
})

With badges

Compose badge() inside content to show status labels or category tags.

API rate limits

Production v2.1

Requests are capped at 1,000/min per API key. Contact support to increase your limit.

card({
  title:   'API rate limits',
  content: `
    <div class="u-flex u-gap-2 u-mb-4">
      ${badge({ label: 'Production', variant: 'success' })}
      ${badge({ label: 'v2.1',       variant: 'info' })}
    </div>
    <p class="u-text-muted u-text-sm">Requests are capped at 1,000/min per API key.</p>
  `,
  footer: `
    ${button({ label: 'View docs',        href: '/docs',    variant: 'ghost',     size: 'sm' })}
    ${button({ label: 'Request increase', href: '/contact', variant: 'secondary', size: 'sm' })}
  `,
})

With metadata row

A flex row inside content works well for author, date, read-time and similar metadata.

Building with Pulse

Learn how to scaffold a new project and ship your first spec-driven page in under ten minutes.

Jane Smith ยท Mar 15, 2024 5 min read
card({
  title:   'Building with Pulse',
  content: `
    <p class="u-text-muted u-text-sm u-mb-4">Learn how to scaffold a new project...</p>
    <div class="u-flex u-gap-3" style="align-items:center;flex-wrap:wrap">
      <img src="/img/avatar.jpg" class="u-rounded-full" style="width:28px;height:28px" alt="">
      <span class="u-text-sm u-text-muted">Jane Smith</span>
      <span class="u-text-sm u-text-muted">ยท</span>
      <span class="u-text-sm u-text-muted">Mar 15, 2024</span>
      <span class="u-text-sm u-text-muted u-ml-auto">5 min read</span>
    </div>
  `,
  footer: button({ label: 'Read article โ†’', href: '/blog/pulse', variant: 'ghost', size: 'sm' }),
})

Stat / metric card

Cards without a title work well as simple metric tiles. Use grid() to lay multiple cards out side by side.

Total revenue

ยฃ48,295

โ†‘ 12% this month

Active users

3,842

Across all plans

Churn rate

1.4%

โ†‘ 0.2% vs last month

grid({
  cols: 3,
  gap: 'sm',
  content: [
    card({
      content: `
        <p class="u-text-muted u-text-sm u-mb-1">Total revenue</p>
        <p class="u-text-3xl u-font-bold">ยฃ48,295</p>
        <p class="u-text-sm u-text-green">โ†‘ 12% this month</p>
      `,
    }),
    card({ ... }),
    card({ ... }),
  ].join(''),
})

Grid inside a card

Components compose freely โ€” pass a grid() into a card's content slot for structured layouts inside a surface.

Plan comparison

Starter

ยฃ0

Up to 3 projects

Pro

ยฃ12

Unlimited projects

Enterprise

Custom

SLA + support

card({
  title:   'Plan comparison',
  content: grid({
    cols: 3,
    gap: 'sm',
    content: [
      `<div class="u-text-center u-p-2">...</div>`,
      `<div class="u-text-center u-p-2">...</div>`,
      `<div class="u-text-center u-p-2">...</div>`,
    ].join(''),
  }),
  footer: button({ label: 'View full pricing', href: '/pricing', variant: 'ghost', size: 'sm' }),
})
PropTypeDefault
titlestringโ€”Escaped automatically
levelnumber3Heading tag for the title (1โ€“6). Visual style is unchanged โ€” use this to keep the document outline correct when the surrounding context already has an h2 or h3.
contentstringโ€”HTML string โ€” not escaped
footerstringโ€”HTML string โ€” not escaped
flushbooleanfalseRemoves body padding โ€” useful for full-bleed images or tables
classstringโ€”