The Basics

What it is

Card displays content and actions related to a single concept or item. It can contain images, video, text, buttons, and links. Cards are great for displaying collections: their consistent shape allows them to tile easily and scale responsively.

By default, Card isn’t interactive, but it can contain interactive elements. It also has a clickable variation, where the entire Card acts an oversized button or link.

How it works

  • Card displays a range of content, including static images and text, embedded videos, and forms.
  • The user engages with any interactive elements right in the Card, while on the page.

In the clickable variation:

  • Hovering displays Card’s hover state.
  • Clicking displays Card’s pressed state and:
    • Triggers the action (if Card is acting as a button)
    • Opens the relevant page or section (if Card is acting as a link)
  • If Card contains other buttons or links in the button row, the user can activate them independently, without triggering the clickable Card.

When to use

  • To summarize a single concept or item (like key information about a provider’s office) in a concise, self-contained element
  • To display a collection or group of items at the same level of hierarchy (in a set of multiple Cards)
  • To display links to other pages on a dashboard or home page

When not to use

  • For information that should be condensed and easy to scan, because Card takes up a lot of space
  • As an easy way to separate content areas on a web page, when a simple <div> would work
  • When a List with dividers would suffice, because Card’s additional UI elements add unnecessary visual complexity

What to use instead

List

Use List to show items in a condensed view that’s easy to scan.

DataTable

Use DataTable to show items in a view that’s easy to scan and sort.

Accordion

Use Accordion to display related content in a vertical list.

How to use

A single Card represents a single concept or item and can be used by itself. To represent multiple concepts or items, use multiple Cards. Card tiles easily, making it a great choice for representing a collection or group, like:

  • Upcoming appointments in a schedule
  • Links to patient education resources
  • Representations of inpatient rooms
Do:

Use a single Card to represent a single item or concept.

<p>Use a single Card to represent a single item or concept.</p>
Don't:

Group multiple items or concepts in a single Card.

<p>Group multiple items or concepts in a single Card.</p>

Use a different pattern to represent nested items or concepts. Don’t put a Card in a Card.

Card has several content areas, making it a very flexible component:

  • Media slot: Area at the top for a visual element like an image, video, or other graphic element. This content is full width, even with Card’s default padding.
  • Heading: Text that summarizes what the card represents. You can also include a heading description, which can serve as a subheading or elaborate on the heading.
  • Body: The main content area. In a clickable Card, this should not contain interactive elements (because they conflict with the clickable behavior).
  • Button row: Area at the bottom for buttons and links.

Clickable Cards

Card can be set to make the entire element clickable, so that Card functions as an oversized button or link. Use cases for this could include:

  • Card shows the title and a short description of a web page. Users click the Card to open the page.
  • A dashboard includes Cards that show summaries of different data. Users click each Card to open a separate page with more detailed data.
  • A set of Cards represents a library of patient education handouts. Users click a Card to select it and add it to their saved items.

Buttons and links are the only interactive elements that should be used in a clickable Card. They should be placed in the button row so they don’t interfere with the clickable behavior.

Do:

Put buttons or links in the button row of a clickable Card.

<p>Put buttons or links in the button row of a clickable Card.</p>
Don't:

Use other interactive elements in a clickable Card.

<p>Use other interactive elements in a clickable Card.</p>

Style

Design details

No additional information for this component.

Placement and hierarchy

Cards are good for fluid layouts, because they tile horizontally, as well as vertically on small screens. Multiple Cards should tile from left to right in their container. When they reach the right edge, they should continue on the next row, from left to right.

The number of Cards per row varies, depending on the size of the Card and the width of the container. One Card per row is a good layout on smaller screens or in narrow containers (<760px), making it easy to scroll on mobile devices.

Placement and hierarchy

Content

Media options

Card has a media slot that can contain images, video, or audio:

  • Images default to full width, even when Card’s default padding is applied.
  • Videos shouldn’t autoplay. Include controls for basic playback features like play and pause.
  • Audio (like podcasts or other recordings) shouldn’t autoplay. As with video, add playback controls.

If using different media types in the same set of Cards, use descriptive headings or some other indicator to convey the type of media each Card contains.

Text and formatting

  • Use title case for Card heading text.
  • Use sentence case for Card heading description text.
  • Use sentence case for Card body text. Keep it short so the card doesn’t get too tall.
  • Use title case for any action buttons associated with the content. Put these buttons in the button row at the bottom of the Card.
  • For clickable cards, include descriptive text for screen readers to announce what clicking the Card will do (“Go to medication list”, “Book an appointment”, “Read information about type 2 diabetes”).

Demos

Card Basic Share

Card With Buttons Share

Cards With Images Share

Coding

Developer tips

Clickable Cards

To make the entire Card clickable, supply a function to the onClick prop. This also gives the border of the Card a visual style on hover (indicating that it can be clicked) and on click (indicating that it was clicked).

Interactive elements in clickable Cards

Avoid putting interactive elements (like buttons or links) in the body of a clickable Card, because it creates a confusing user experience.

It also causes technical problems. Card works as a container for whatever content is passed as children, which renders in the Card body. Technically, you can add any valid JSX, but if you add interactive elements as children to a clickable Card, event bubbling will cause problems. When users click an element in the Card body, the event will bubble up to the parent Card component, which usually isn’t the desired behavior.

If you need to add buttons or links to your clickable Card, put them in the button row at the bottom of the Card using the buttons prop. Elements passed to the buttons prop are cloned and have event handlers for mouseenter and mouseleave events added to them. This prevents showing the Card’s hover state when hovering over these elements and triggering the onClick function when they’re activated.

If your use case absolutely requires putting interactive elements in the body of a clickable Card, you can use event.stopPropagation() on the elements’ click handlers as a workaround. To prevent click events triggered by any element passed as a child from bubbling up to the Card component, use the stopPropagation() method before the end of each interactive child's click handler:

<Card>
<a
href="<https://athenahealth.com>"
onClick={(e) => {
// All click handler code should go before the next line
e.stopPropagation();
}}
>
Go to athenahealth!
</a>
</Card>;

Padding

By default, the padded prop is set to {true} and adds padding around all content inside Card except for mediaSlot content, which is full width. Set padded={false} to remove it.

Repository

Implementation links

Card directory in Bitbucket

Implementation details

It is strongly recommended to familiarize yourself with the Forge source code. While this documentation is a best effort to document the intent and usage of a component, sometimes some features only become clear when looking at the source code. Also, looking at Forge's source code may help identify and fix bugs in either your application or Forge itself.

Storybook files

Forge maintains at least one storybook file per component. While the primary audience for these files is typically the Forge team, these storybook files may cover usages of the component not covered by a demo. The storybook for the latest version of forge can be found at go/forge-storybook.

Testing library

Forge strongly encourages using testing-library to write tests for your application.

"The more your tests resemble the way your software is used, the more confidence they can give you."

If you're having trouble testing a Forge component using testing-library, it would be a good idea to see how Forge tests its own components. For the most part, Forge tries to use screen.getByRole as much as it can, as that API provides the best feedback on a11y compliance. Forge discourages the use of document.querySelector and screen.getByTestId as both APIs encourage using implementation details to test your component, and discourage adding roles to your component.

With that being said, many of Forge's components were not built with accessability in mind. These components do break the recommendations listed above.

Import statements

In Nimbus applications

athenaOne serves the Forge bundle independently from your application's bundle. Importing Forge components directly from '@athena/forge' takes advantage of this feature.

import { Card } from '@athena/forge'

In standalone applications

Importing components using the exact path to the module takes advantage of webpack's tree shaking feature. Webpack will include only that module and its dependencies.

import Card from '@athena/forge/Card';

To use this import guidance, Typescript applications must use typescript >= 4.7.3, and should add this setting to their tsconfig.json file:

{
"compilerOptions": {
"moduleResolution": "Node16",
}
}

If this setting doesn't work for your application, use this import statement instead:

import Card from '@athena/forge/dist/Card';

Props