Deck
Browse the catalog

Build / Organisms

ContactCard

  • organism
  • play-fn

A saved contact, with their reachable details and follow-up state.

This is the "after the handshake" view: PersonCard helps you find someone, ContactCard helps you act on the relationship. Details render as a real <dl> so the label/value pairing survives in a screen reader.

import { ContactCard } from '@meetcard/deck'

Example

<ContactCard name="Ada Lovelace" company="MeetCard" metAt="Met at SaaSConf · 12 June" details={[{ label: 'Email', value: 'ada@meetcard.com', href: 'mailto:ada@meetcard.com' }]} status={{ label: 'Follow up', tone: 'warning' }} />

Stories

Every preview below is the real component, rendered in this page from the same story Storybook runs. The width control resizes each preview's container; CSS media queries still follow the browser window, so breakpoint behaviour is worth checking in Storybook.

Default
Source
const Default = () => <div style={{ maxWidth: 440 }}>
    <ContactCard
        name="Ada Lovelace"
        title="Head of Partnerships"
        company="MeetCard"
        details={[
          {
            label: 'Email',
            value: 'ada@meetcard.com',
            href: 'mailto:ada@meetcard.com',
          },
          { label: 'Phone', value: '+44 20 7946 0958', href: 'tel:+442079460958' },
          { label: 'Location', value: 'London, UK' },
        ]} />
</div>;

Where and when you met — the context that makes a contact useful later.

With Met At
Source
const WithMetAt = () => <div style={{ maxWidth: 440 }}>
    <ContactCard
        name="Ada Lovelace"
        title="Head of Partnerships"
        company="MeetCard"
        details={[
          {
            label: 'Email',
            value: 'ada@meetcard.com',
            href: 'mailto:ada@meetcard.com',
          },
          { label: 'Phone', value: '+44 20 7946 0958', href: 'tel:+442079460958' },
          { label: 'Location', value: 'London, UK' },
        ]}
        metAt="Met at SaaSConf · 12 June" />
</div>;

A contact you owe something — the status is the reason to open the card.

Needs Follow Up
Source
const NeedsFollowUp = () => <div style={{ maxWidth: 440 }}>
    <ContactCard
        name="Ada Lovelace"
        title="Head of Partnerships"
        company="MeetCard"
        details={[
          {
            label: 'Email',
            value: 'ada@meetcard.com',
            href: 'mailto:ada@meetcard.com',
          },
          { label: 'Phone', value: '+44 20 7946 0958', href: 'tel:+442079460958' },
          { label: 'Location', value: 'London, UK' },
        ]}
        metAt="Met at SaaSConf · 12 June"
        status={{ label: 'Follow up', tone: 'warning' }} />
</div>;

Email and phone are actionable; plain values stay as text.

Actionable Details
Source
const ActionableDetails = () => <div style={{ maxWidth: 440 }}>
    <ContactCard
        name="Ada Lovelace"
        title="Head of Partnerships"
        company="MeetCard"
        details={[
          {
            label: 'Email',
            value: 'ada@meetcard.com',
            href: 'mailto:ada@meetcard.com',
          },
          { label: 'Phone', value: '+44 20 7946 0958', href: 'tel:+442079460958' },
          { label: 'Location', value: 'London, UK' },
        ]} />
</div>;

With actions, for acting on the follow-up without leaving the card.

With Actions
Source
const WithActions = () => <div style={{ maxWidth: 440 }}>
    <ContactCard
        name="Ada Lovelace"
        title="Head of Partnerships"
        company="MeetCard"
        details={[
          {
            label: 'Email',
            value: 'ada@meetcard.com',
            href: 'mailto:ada@meetcard.com',
          },
          { label: 'Phone', value: '+44 20 7946 0958', href: 'tel:+442079460958' },
          { label: 'Location', value: 'London, UK' },
        ]}
        status={{ label: 'Follow up', tone: 'warning' }}
        actions={(<>
          <Button size="sm">Send message</Button>
          <Button size="sm" variant="ghost">
            Snooze
          </Button>
        </>)} />
</div>;

Details are optional — the identity block stands on its own.

Without Details
Source
const WithoutDetails = () => <div style={{ maxWidth: 440 }}>
    <ContactCard
        name="Ada Lovelace"
        title="Head of Partnerships"
        company="MeetCard"
        details={undefined} />
</div>;

Props

PropTypeDefaultDescription
namerequiredstring——
actionsReactNode—Footer controls, typically Buttons.
avatarSrcstring——
companystring——
detailsContactDetail[]—Reachable details, rendered as a description list.
metAtstring—Where and when you met — the context that makes a contact useful later.
status{ label: string; tone?: BadgeTone }—Follow-up state, e.g. { label: 'Follow up', tone: 'warning' }.
titlestring——

Also accepts everything from Omit, so id, data-* and event handlers pass straight through.

Open in Storybookfor controls, the a11y panel and the interaction runner.