Skip to content
LogoLogo

Card

Cards collect and divide content into designated areas. Cards can be actionable themselves or have actionable elements within.

Loading...

Overview

Resources

Loading...

Loading...

Loading...

Loading...

Install

yarn add @camp/card

Props

PropTypeDefaultDescription
styleCSSProperties
keyKey
actionablebooleanSetting actionable to true enables on hover effect to indicate clickability to user.
aibooleanSet to true for AI appearance
onClickMouseEventHandler<HTMLDivElement>
onKeyDownKeyboardEventHandler<HTMLDivElement>
roleAriaRole
tabIndexnumber
disabledbooleanRender card in `disabled` state when `actionable` is true. Recommend using `CardContent` for content to automatically apply disabled styles in content.
dismissLabelstringTranslated aria label for the dismiss "X" button. Required if `onDismiss` is provided.
dismissTestIdstringTest ID for the dismiss button.
loadingbooleanWhen `ai` is true, set to true to show the card in a loading state
loadingBackgroundColor"bg-default" | "bg-educational" | "bg-disabled"bg-defaultWhen `loading` and `ai` are true, this prop sets the background color of the loading card.
loadingContentReactNodeWhen `loading` and `ai` is true, this custom content will be rendered within the card while it is in a loading state.
onDismiss(() => void) & (() => void) & (() => void) & (() => void) & (() => void)When actionable is false, onDismiss can be used to provide a dismiss button for the card. If provided, a close "X" button will be rendered on the top right and clicking close will fire onDismiss callback. (Actionable cards cannot be dismissed - `onDismiss` will be ignored if `actionable` is true.)
renderMenu() => ReactNode
selectedbooleanWhen actionable is true, shows a selected state for the card.

Generated from @camp/card@7.16.0 source, ordered by production usage. * required. † standard HTML attribute — all native attributes are supported; only those with production usage are listed.

Upgrading to Next Gen

🎨 Updated Color Palette & Style Refresh: Aligned styling with our refreshed brand standards, delivering a modernized and cohesive look and feel.

Migration steps

  1. Update import path: Replace the old Card imports with Card from @camp/card.
  2. Update prop values:
  • disabled: used to disable an actionable card and prevent any interactions with it. Note: you will need to manually set any buttons to be disabled within it.
  • dismissLabel: used to pass a translated string to the dismiss icon
  • onDismiss: when actionable is false, this can be used to pass a function when the dismiss button is clicked.
  • selected: actionable cards can be shown with a selected state
  1. New <CardContent> component: The <CardContent> is a preformatted component that handles the layout of the card content. It accepts props for title, paragraph, and render functions for various types of content. Card may still be used as a container for other custom styled children, but the <CardContent> component is recommended for most use cases. For full prop details, see the CardContent tab in the Overview section above.

Variations

Default card

Use the default card in most cases when looking to group related content together. Card is the outer container that defines outer styling including padding/border, and CardContent is the inner container which handles the layout of the inner content.

Loading...

import { Card, CardContent } from '@camp/card';
import { Chip } from '@camp/chip';
import { Button } from '@camp/button';
 
<Card>
  <CardContent
    title="Title"
    paragraph="Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labe et dolore magna aliqua. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua."
    renderChips={() => (
      <div style={{ display: 'flex', gap: Space[2] }}>
        <Chip type="default" color="ac-blue">
          Chip
        </Chip>
        <Chip type="default" color="moss">
          Chip
        </Chip>
      </div>
    )}
    renderActions={() => (
      <div style={{ display: 'flex', gap: Space[3] }}>
        <Button size="small" appearance="secondary">
          Card action
        </Button>
        <Button size="small" appearance="secondary">
          Card action
        </Button>
      </div>
    )}
  />
</Card>;

Card with Image

<CardContent> also accepts a render function for an image. This is useful for cards that have a visual element, such as a product image or a photo.

Loading...

import { Card, CardContent } from '@camp/card';
import { Chip } from '@camp/chip';
import { Button } from '@camp/button';
 
<Card>
  <CardContent
    title="Title"
    paragraph="Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labe et dolore magna aliqua. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua."
    renderImage={() => <img src="https://picsum.photos/id/76/180/250" alt="Bicycle" />}
    renderChips={() => (
      <div style={{ display: 'flex', gap: Space[2] }}>
        <Chip type="default" color="ac-blue">
          Chip
        </Chip>
        <Chip type="default" color="moss">
          Chip
        </Chip>
      </div>
    )}
    renderActions={() => (
      <div style={{ display: 'flex', gap: Space[3] }}>
        <Button size="small" appearance="secondary">
          Card action
        </Button>
        <Button size="small" appearance="secondary">
          Card action
        </Button>
      </div>
    )}
  />
</Card>;

Actionable card

An actionable card can be interacted with. Hover/click on the card below to see. <Card> also accepts all HTML div attributes, so you can pass event handlers such as onClick to the card itself. Dismiss props are not supported on actionable cards.

Loading...

import { Card, CardContent } from '@camp/card';
 
<Card actionable>
  <CardContent
    title="Title"
    paragraph="Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labe et dolore magna aliqua. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua."
  />
</Card>;

Disabled Card

You can make an actionable card disabled, preventing interaction.
Note: you will need to manually set any buttons to be disabled within it.

Loading...

import { Card, CardContent } from '@camp/card';
 
<Card actionable disabled>
  <CardContent
    title="Title"
    paragraph="Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labe et dolore magna aliqua. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua."
  />
</Card>;

Selected Card

You can add a selected state to an actionable card.

Loading...

import { Card, CardContent } from '@camp/card';
 
<Card actionable selected>
  <CardContent
    title="Title"
    paragraph="Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labe et dolore magna aliqua. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua."
  />
</Card>;

With Dismiss

You can also add a dismiss button to the card with the onDismiss prop. This is useful for cards that are not actionable, but you want to give the user the option to dismiss them. The dismissLabel prop is required when using onDismiss to add a label to the button for accessibility.

Loading...

import { Card, CardContent } from '@camp/card';
 
<Card onDismiss={() => alert('Card dismissed')} dismissLabel="Dismiss">
  <CardContent
    title="Title"
    paragraph="Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labe et dolore magna aliqua. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua."
  />
</Card>;

Production Examples

More states from @camp/card's visual test stories, ranked by production usage. Variations already documented above are not repeated here.

Ai Actionable

import { Card } from '@camp/card';
 
const defaultArgs = {
  children: CustomContent(),
};
 
const CardContentWithActionableContent = (
  <CardContent
    title="Title"
    paragraph={paragraph}
    renderChips={renderChips}
    renderImage={renderImage}
  />
);
 
<Card {...defaultArgs} actionable ai>{CardContentWithActionableContent}</Card>

Ai Actionable Selected

import { Card } from '@camp/card';
 
const defaultArgs = {
  children: CustomContent(),
};
 
const CardContentWithActionableContent = (
  <CardContent
    title="Title"
    paragraph={paragraph}
    renderChips={renderChips}
    renderImage={renderImage}
  />
);
 
<Card
  {...defaultArgs}
  actionable
  ai
  selected
>
  {CardContentWithActionableContent}
</Card>

Ai Actionable Disabled

import { Card } from '@camp/card';
 
const defaultArgs = {
  children: CustomContent(),
};
 
const CardContentWithActionableContent = (
  <CardContent
    title="Title"
    paragraph={paragraph}
    renderChips={renderChips}
    renderImage={renderImage}
  />
);
 
<Card
  {...defaultArgs}
  actionable
  ai
  disabled
>
  {CardContentWithActionableContent}
</Card>

Ai Actionable Disabled Custom Content Not Configured

import { Card } from '@camp/card';
 
const defaultArgs = {
  children: CustomContent(),
};
 
<Card {...defaultArgs} actionable ai disabled />

Ai Actionable Disabled Custom Content Configured

import { Card } from '@camp/card';
 
const CustomContent = (disabled?: boolean) => (
  <div>
    <div
      style={{ display: 'flex', flexDirection: 'column', gap: Space[2], paddingBottom: Space[3] }}
    >
      <Text color={disabled ? 'disabled' : undefined} appearance="body-small" as="p">
        Here is some custom card content. It is sized to fit its content, or it will accept inline
        styles for setting size. This content is {!disabled && 'not'} configured for a disabled
        state.
      </Text>
      <Text color={disabled ? 'disabled' : undefined} appearance="body-small" as="p">
        DISMISS BUTTON: It is the consumer's responsibility to format it such that it doesn't
        overlap with a dismiss button, if one exists.
      </Text>
      <Text color={disabled ? 'disabled' : undefined} appearance="body-small" as="p">
        DISABLED STATE: It is also the consumer's responsibility to set the opacity or colors of
        their custom inner elements to account for a disabled state, if one is needed for their
        implementation.
      </Text>
    </div>
    <div style={{ display: 'flex', gap: Space[3] }}>
      <Button disabled={disabled}>Button</Button>
      <IconButton disabled={disabled}>
        <AddMedium title="title" />
      </IconButton>
    </div>
  </div>
);
 
const defaultArgs = {
  children: CustomContent(),
};
 
<Card {...defaultArgs} actionable ai disabled>{CustomContent(true)}</Card>

Ai

import { Card } from '@camp/card';
 
const defaultArgs = {
  children: CustomContent(),
};
 
const renderActions = () => (
  <div style={{ display: 'flex', gap: Space[3] }}>
    <Button size="small" appearance="secondary">
      Card action
    </Button>
    <Button size="small" appearance="secondary">
      Card action
    </Button>
  </div>
);
 
const renderMenu = () => (
  <IconButton appearance="floating">
    <MoreMenuSmall title="more menu" />
  </IconButton>
);
 
const paragraph =
  'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labe et dolore magna aliqua. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.';
 
<Card
  {...defaultArgs}
  ai
>
  {(
      <CardContent
        title="Title"
        paragraph={paragraph}
        renderActions={renderActions}
        renderMenu={renderMenu}
      />
    )}
</Card>

19 more variations in Storybook

AI Card


Loading...

The ai prop is used to create an AI card. When using the ai prop, the dismiss props are not allowed, since AI cards are not dismissable.

Static

<CardContent> can also be used within an ai card to render the content. The renderMenu prop is available for use on <CardContent> to render a menu, which is a common pattern for AI cards.

Loading...

import { Card, CardContent } from '@camp/card';
 
<Card ai>
  <CardContent
    title="Title"
    paragraph="Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labe et dolore magna aliqua. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua."
    renderActions={renderActions}
    renderMenu={renderMenu}
  />
</Card>;

Actionable

Actionable AI cards have hover, focus, and active states. They also have a selected state. Dismiss props are not supported on actionable AI cards.

Loading...

Loading...

Loading...

import { Card, CardContent } from '@camp/card';
 
<Card ai actionable>
  <CardContent title="Card 1" paragraph="Some descriptive text." />
</Card>
<Card ai actionable>
  <CardContent title="Card 2" paragraph="Some descriptive text." />
</Card>
<Card ai actionable selected>
  <CardContent title="Card 3" paragraph="This card is selected." />
</Card>

Loading

The Loading AI card can be used by itself in place of content that is loading, or the loading state can be toggled on and off for the card itself. The size of the loading card will expand to fill the parent container (this differs from the static card, which is sized based on the inner content; size your parent container accordingly).

Title and medium loading indicator

Loading...

No title and small loading indicator

Loading...

Custom loading background color and custom content

Loading...

Any content passed as children to the Card will not be visible when the card is loading. It is replaced by any content passed as loadingContent.

You can also update the loading background color by passing a value for loadingBackgroundColor. The available colors are: bg-default, bg-educational, bg-disabled.

The helper component <CardLoadingContent> may be used to optionally render a text and/or loading indicator. Full prop descriptions for <CardLoadingContent> can be found in the Overview section above.

import { Card, CardContent } from '@camp/card';
 
<Card
  ai
  loading
  loadingContent={<CardLoadingContent title="Loading..." loadingIndicatorSize="medium" />}
>
  <CardContent title="Card 1" paragraph="Some descriptive text." />
</Card>;

Usage

Best practices

✅ DO

  • Do use to visually separate content that is meant to draw focus. Use cards with dropshadows for the most important information on the page.

🚫 DON’T

  • Don't place cards inside of another card. Review our rules on elevation and for separation within cards, use dividiers or a small banner to call content out separately.

Accessibility

Keyboard support

  • You can tab into an actionable card and then into its content.
  • You can tab into the dismiss in the card (last).

Labels

  • The developer should set dismissLabel to provide a translated aria-label for the dismiss "X" button.
Copyright © 2026 ActiveCampaign