Deck
Browse the catalog

Build / Organisms

PersonCard

  • organism
  • play-fn

The professional business card — the artifact you hand over, and the canonical child of CardPile.

A fixed box, the shape of a physical business card: 1.75:1 lying down and 1:1.75 stood up, portrait on a phone and landscape from sm up. The box never grows to fit — the name clamps to two lines and anything past the edge is clipped — because a card is a fixed-size object, and content that cannot fit on one is content that does not belong on one.

import { PersonCard } from '@meetcard/deck'

Example

<PersonCard name="Ben Ackles" title="Builder" company="MeetCard" companyHref="/companies/meetcard" location="Boulder, Colorado" tagline="What a lovable guy" footer={<> <Button iconStart={<CalendarIcon />}>Book with me</Button> <Button variant="secondary" iconStart={<HandshakeIcon />}> Exchange cards </Button> </>} />

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.

The full card, as handed over during an introduction.

Default
Source
const Default = () => <div style={{ maxWidth: 420 }}>
    <PersonCard
        name="Ben Ackles"
        title="Builder"
        company="MeetCard"
        location="Boulder, Colorado"
        tagline="What a lovable guy"
        contactActions={(<>
          <IconButton label="Email Ben" icon={<EmailIcon />} size="sm" round />
          <IconButton
            label="Ben on LinkedIn"
            icon={<LinkedInIcon />}
            size="sm"
            round
          />
          <IconButton label="Share card" icon={<ShareIcon />} size="sm" round />
        </>)}
        footer={(<>
          <Button iconStart={<CalendarIcon />}>Book with me</Button>
          <Button variant="secondary" iconStart={<HandshakeIcon />}>
            Exchange cards
          </Button>
        </>)} />
</div>;

Just the essentials — name and eyebrow are the only required fields.

Minimal
Source
const Minimal = () => <div style={{ maxWidth: 420 }}>
    <PersonCard
        name="Ben Ackles"
        title={undefined}
        company={undefined}
        location={undefined}
        tagline={undefined} />
</div>;

The company links to its profile — the tap-through on a card.

With Company Link
Source
const WithCompanyLink = () => <div style={{ maxWidth: 420 }}>
    <PersonCard
        name="Ben Ackles"
        title="Builder"
        company="MeetCard"
        location="Boulder, Colorado"
        tagline="What a lovable guy"
        contactActions={(<>
          <IconButton label="Email Ben" icon={<EmailIcon />} size="sm" round />
          <IconButton
            label="Ben on LinkedIn"
            icon={<LinkedInIcon />}
            size="sm"
            round
          />
          <IconButton label="Share card" icon={<ShareIcon />} size="sm" round />
        </>)}
        footer={(<>
          <Button iconStart={<CalendarIcon />}>Book with me</Button>
          <Button variant="secondary" iconStart={<HandshakeIcon />}>
            Exchange cards
          </Button>
        </>)}
        companyHref="/companies/meetcard" />
</div>;

With a private note written. The dot on the pill says there is something on the back.

With Private Note
Source
const WithPrivateNote = () => <div style={{ maxWidth: 420 }}>
    <PersonCard
        name="Ben Ackles"
        title="Builder"
        company="MeetCard"
        location="Boulder, Colorado"
        tagline="What a lovable guy"
        contactActions={(<>
          <IconButton label="Email Ben" icon={<EmailIcon />} size="sm" round />
          <IconButton
            label="Ben on LinkedIn"
            icon={<LinkedInIcon />}
            size="sm"
            round
          />
          <IconButton label="Share card" icon={<ShareIcon />} size="sm" round />
        </>)}
        footer={(<>
          <Button iconStart={<CalendarIcon />}>Book with me</Button>
          <Button variant="secondary" iconStart={<HandshakeIcon />}>
            Exchange cards
          </Button>
        </>)}
        privateNote={{ hasContent: true }} />
</div>;

A long name still fits — it truncates rather than breaking the layout.

Long Name
Source
const LongName = () => <div style={{ maxWidth: 420 }}>
    <PersonCard
        name="Augusta Ada King-Noel, Countess of Lovelace"
        title="Head of Strategic Partnerships and Developer Relations"
        company="MeetCard"
        location="Boulder, Colorado"
        tagline="What a lovable guy" />
</div>;

A card has two sides. Opening the private note turns this one over, because what you write about someone belongs on the back of their card rather than in a panel somewhere else — and the dot on the front tells you there is something written there without showing it.

Note On The Back
Source
const NoteOnTheBack = () => <FlipHarness />;

On a phone, on its own. A card stands up on a phone whether or not a CardPile is around it — before, only a pile could turn one, and a lone card stayed lying down on a screen it was too wide to read on.

On Its Own On A Phone
Source
const OnItsOwnOnAPhone = () => <div style={{ maxWidth: 420 }}>
    <PersonCard
        name="Ben Ackles"
        title="Builder"
        company="MeetCard"
        location="Boulder, Colorado"
        tagline="What a lovable guy"
        contactActions={(<>
          <IconButton label="Email Ben" icon={<EmailIcon />} size="sm" round />
          <IconButton
            label="Ben on LinkedIn"
            icon={<LinkedInIcon />}
            size="sm"
            round
          />
          <IconButton label="Share card" icon={<ShareIcon />} size="sm" round />
        </>)}
        footer={(<>
          <Button iconStart={<CalendarIcon />}>Book with me</Button>
          <Button variant="secondary" iconStart={<HandshakeIcon />}>
            Exchange cards
          </Button>
        </>)} />
</div>;

Pinned. orientation is for a space that has already committed to a shape — a landscape slot on a phone, say — and it wins over the viewport.

Pinned Landscape
Source
const PinnedLandscape = () => <div style={{ maxWidth: 420 }}>
    <PersonCard
        name="Ben Ackles"
        title="Builder"
        company="MeetCard"
        location="Boulder, Colorado"
        tagline="What a lovable guy"
        contactActions={(<>
          <IconButton label="Email Ben" icon={<EmailIcon />} size="sm" round />
          <IconButton
            label="Ben on LinkedIn"
            icon={<LinkedInIcon />}
            size="sm"
            round
          />
          <IconButton label="Share card" icon={<ShareIcon />} size="sm" round />
        </>)}
        footer={(<>
          <Button iconStart={<CalendarIcon />}>Book with me</Button>
          <Button variant="secondary" iconStart={<HandshakeIcon />}>
            Exchange cards
          </Button>
        </>)}
        orientation="landscape" />
</div>;

Props

PropTypeDefaultDescription
namerequiredstring——
avatarSrcstring——
backReactNode—The card's other face — normally a PrivateNote. Supplying it turns the private-note control into a flip: a business card has two sides, and the things you write about someone belong on the back of theirs rather than in a panel somewhere else. Without this the card renders exactly as before, unwrapped, and privateNote.onClick is left to the caller.
companystring——
companyHrefstring—Makes company a link, styled as a dotted-underline reference.
contactActionsReactNode—Circular icon buttons for contact methods — email, LinkedIn, share.
eyebrowstring'Meet'Small caps label above the name. Deck renders it upper-cased via CSS, so pass natural case — defaults to "Meet".
flippedboolean—Controlled flip state. Omit to let the card manage its own.
footerReactNode—Primary/secondary actions along the bottom edge.
locationstring——
onFlippedChange(flipped: boolean) => void——
orientationResolvedCardOrientation | 'responsive'—Which way up the card sits. Default responsive: the surrounding CardPile's orientation if there is one, otherwise portrait on a phone and landscape from sm up. Pin it only when the space around the card has already committed to a shape.
privateNotePersonCardPrivateNote—A dismissible, private annotation only the card's owner can see.
taglinestring—Short status line under the identity row, e.g. "What a lovable guy".
titlestring—Role or function, e.g. "Builder".

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.