Card

Framed container for grouping related content and actions

Live Code

 

Storybook

 

Usage

Card

Card is a compact container for organizing and displaying content about a single topic.

Card is a versatile composition of a few subcomponents:

Additionally, Card can be used with CardLink to create a clickable card.

Card

Card is the main container of the composition.


                                                
                                                <Card>
                                                  {/* CardArtwork or CardMedia */}
                                                  {/* CardBody */}
                                                  {/* CardFooter */}
                                                </Card>

Regardless of the layout, the Card subcomponents must be arranged in the following order:

  1. CardArtwork (optional) or CardMedia (optional) — first
  2. CardLogo (optional)
  3. CardBody
  4. CardFooter (optional) – last

ℹ️ Every <div> counts, especially on large pages. During the development of the Card component, we did our best to balance between flexibility and simplicity. To provide the best performance, we decided to use the CSS grid layout with predefined grid areas. This way, we can avoid unnecessary elements and keep the Card structure as flat as possible.

ℹ️ Vertical spacing between subcomponents is implemented using the margin-bottom property and the Card relies on the specified order of its subcomponents. Since the Card uses the CSS grid layout with predefined grid areas, using the gap property would lead to redundant spacing when dropping in just some of the subcomponents.

⚠️ Arranging the subcomponents in a different order will break the spacing of the subcomponents and may also have negative impact on accessibility of the Card.

Card Layout

Card can be displayed in a vertical, horizontal, or reversed horizontal layout.


                                                
                                                // Vertical card
                                                <Card direction="vertical">
                                                  {/* … */}
                                                </Card>
                                                
                                                // Horizontal card -->
                                                <Card direction="horizontal">
                                                  {/* … */}
                                                </Card>
                                                
                                                // Reversed horizontal card -->
                                                <Card direction="horizontal-reversed">
                                                  {/* … */}
                                                </Card>

👉 Keep in mind that, no matter the layout, the Card subcomponents must be arranged in the order specified above.

Responsive Card Layout

Pass an object to props to set different values for different breakpoints. The values will be applied from mobile to desktop and if not set for a breakpoint, the value from the previous breakpoint will be used.


                                                
                                                <Card direction={{ mobile: 'vertical', tablet: 'horizontal', desktop: 'horizontal-reversed' }}>{/* … */}</Card>

Boxed Cards

Card can be displayed with a border and a box shadow on hover.


                                                
                                                <Card isBoxed>{/* … */}</Card>

Vertical Alignment of the Content

Card content can be vertically aligned. This is particularly useful in horizontal Card layouts where content height may vary.

To align Card content vertically, use the alignmentY prop:

  • top — aligns content to the top
  • center — centers content vertically
  • bottom — aligns content to the bottom

                                                
                                                <Card direction="horizontal" alignmentY="center">
                                                  {/* … */}
                                                </Card>

Responsive Alignment

Pass an object to alignmentY to set different values for different breakpoints.


                                                
                                                <Card
                                                  direction={{ mobile: 'vertical', tablet: 'horizontal' }}
                                                  alignmentY={{ mobile: 'top', tablet: 'center', desktop: 'bottom' }}
                                                >
                                                  {/* … */}
                                                </Card>

API

Name Type Default Required Description
alignmentY [AlignmentY dictionary | Responsive<AlignmentYDictionaryType>] Vertical alignment of the content, use object to set responsive values, e.g. { mobile: 'top', tablet: 'center' }
direction [DirectionExtended dictionary | object] vertical Direction of the content inside Card component, use object to set responsive values, e.g. { mobile: 'horizontal', tablet: 'vertical', desktop: 'horizontal-reversed' }
elementType ElementType article Type of element
isBoxed bool false Whether the Card have border

On top of the API options, the components accept additional attributes. If you need more control over the styling of a component, you can use style props and escape hatches.

CardArtwork

CardArtwork is an optional subcomponent that displays a small image or icon.


                                                
                                                <CardArtwork>
                                                  <svg width="24" height="24" aria-hidden="true">
                                                    <use xlink:href="/assets/icons/svg/sprite.svg#file" />
                                                  </svg>
                                                </CardArtwork>

Artwork Alignment (Horizontal Layouts Only)

In the vertical Card layout, the artwork can be horizontally aligned to the start, center, or end of the Card. Available alignment options are derived from the AlignmentX dictionary. To align the artwork, use alignmentX prop:

  • left (default)
  • center
  • right

                                                
                                                <CardArtwork alignmentX="center">
                                                  <svg width="24" height="24" aria-hidden="true">
                                                    <use xlink:href="/assets/icons/svg/sprite.svg#file" />
                                                  </svg>
                                                </CardArtwork>

API

Name Type Default Required Description
alignmentX AlignmentX dictionary left Alignment of artwork content

On top of the API options, the components accept additional attributes. If you need more control over the styling of a component, you can use style props and escape hatches.

CardMedia

To display larger images or videos, use the CardMedia subcomponent.


                                                
                                                <CardMedia>
                                                  <img src="https://via.placeholder.com/300x200" alt="" />
                                                </CardMedia>

👉 Please note the empty alt attribute which means the image is purely decorative and does not convey any information.

👉 Please note that there is no link around or inside the CardMedia subcomponent. See the Linking the Media section for more.

Or, for a video:


                                                
                                                <CardMedia>
                                                  <video
                                                    src="https://archive.org/download/BigBuckBunny_124/Content/big_buck_bunny_720p_surround.mp4"
                                                    poster="https://peach.blender.org/wp-content/uploads/title_anouncement.jpg"
                                                    controls
                                                    muted
                                                    playsInline
                                                  />
                                                </CardMedia>

Media Sizes

CardMedia can be displayed in different sizes. The available sizes are based on the Size dictionary. By default, the media uses the auto size option which means the media will be displayed in its original aspect ratio. Other options set the media to a specific height (in the vertical Card layout) or width (in the horizontal Card layout).

In the vertical Card layout, the media is always expanded to the full width of the CardBody content. For boxed Cards, the media can be even expanded to the edges of the Card.

  • auto (default)
  • small
  • medium
  • large

For example:


                                                
                                                <CardMedia size="small">{/* … */}</CardMedia>

ℹ️ The Card automatically prevents the media from overflowing the Card container or even pushing the subsequent CardBody content out of the Card. In such cases, the media will be cropped to fit the Card container.

Expanding the Media

To expand the media to the full width or height of a boxed Card, use the isExpanded prop. This option is available for both vertical and horizontal (including reversed horizontal) Card layouts.


                                                
                                                <Card>
                                                  <CardMedia isExpanded>{/* … */}</CardMedia>
                                                  <CardBody>{/* … */}</CardBody>
                                                </Card>

Additionally, there is a filledHeight prop that expands the media to match the height of the CardBody content. This option works with both boxed and non-boxed Card, but is only available in the horizontal Card layout.


                                                
                                                <Card direction="horizontal">
                                                  <CardMedia hasFilledHeight>{/* … */}</CardMedia>
                                                  <CardBody>{/* … */}</CardBody>
                                                </Card>

ℹ️ Both options work with all media sizes.

🎉 Fun fact: The isExpanded and hasFilledHeight props produce the same result for non-boxed horizontal (and reversed horizontal) Cards. But in all other contexts, the two props behave differently.

Object Fit

You can control how the media content is resized to fit its container using the fit prop. Available values are based on the CSS object-fit property:

  • contain — the media is scaled to maintain its aspect ratio while fitting within the container
  • cover (default) — the media is sized to maintain its aspect ratio while filling the container (may be clipped)

                                                
                                                <CardMedia fit="contain">
                                                  <img src="https://via.placeholder.com/300x200" alt="" />
                                                </CardMedia>

For infographics and images with fixed aspect ratios that should not be cropped, use fit="contain".

Background Color

You can set the background color of the CardMedia container using the backgroundColor prop.


                                                
                                                <CardMedia backgroundColor="primary">
                                                  <img src="https://via.placeholder.com/300x200" alt="" />
                                                </CardMedia>

API

Name Type Default Required Description
backgroundColor [Background Color dictionary | AccentColorNamesType | EmotionColorNamesTypeIntensity dictionary] Background color of the CardMedia
fit contain | cover cover How the media content should be resized to fit its container
hasFilledHeightClass bool false Whether the image fill the height of a Card
isExpanded bool false Whether the media has space around
size [Size dictionary, auto] auto Size of the image media

On top of the API options, the components accept additional attributes. If you need more control over the styling of a component, you can use style props and escape hatches.

CardLogo is an optional subcomponent that displays a logo. To achieve the best visual result, use the PartnerLogo subcomponent.


                                                
                                                <CardLogo>
                                                  <PartnerLogo>
                                                    <img src="…" alt="Product Name" />
                                                  </PartnerLogo>
                                                </CardLogo>

CardBody

CardBody is the main content area of the Card.


                                                
                                                <CardBody>{/* … */}</CardBody>

API

Name Type Default Required Description
isSelectable bool false Whether the text is selectable

On top of the API options, the components accept additional attributes. If you need more control over the styling of a component, you can use style props and escape hatches.

CardTitle

CardTitle displays the main title of the Card. It uses the <h4> heading element by default, but you can use any other heading level that fits your document outline.


                                                
                                                <CardTitle>
                                                  <a href="#">Card Title</a>
                                                </CardTitle>

The CardTitle is emphasized by default. To deemphasize it, simply set the isHeading prop to false:


                                                
                                                <CardTitle isHeading={false}>
                                                  <a href="#">Card Title</a>
                                                </CardTitle>

👉 See below how to extend the link in CardTitle to make the whole card clickable.

API

Name Type Default Required Description
elementType ElementType h4 Type of element
isHeading bool true Whether the title is rendered as a heading

On top of the API options, the components accept additional attributes. If you need more control over the styling of a component, you can use style props and escape hatches.

CardEyebrow

CardEyebrow is an optional subcomponent that accompanies the CardTitle.


                                                
                                                <CardEyebrow>Content options</CardEyebrow>
                                                <CardTitle>Card Title</CardTitle>

CardFooter

Use CardFooter for actions or any other content at the bottom of the Card. When using Cards with CardFooter in a Grid, the CardFooters will automatically line up.


                                                
                                                <CardFooter>{/* … */}</CardFooter>

The footer can be horizontally aligned to the start, center, or end of the Card. To align the footer, use one of the following alignmentX prop values:

  • left (default)
  • center
  • right

API

Name Type Default Required Description
alignmentX AlignmentX dictionary left Alignment of footer content

On top of the API options, the components accept additional attributes. If you need more control over the styling of a component, you can use style props and escape hatches.

Card Grid

In a typical use case, you will display multiple Cards in a Grid.


                                                
                                                <Grid cols={{ mobile: 1, tablet: 2, desktop: 3 }}>
                                                  <Card>{/* … */}</Card>
                                                  <Card>{/* … */}</Card>
                                                  <Card>{/* … */}</Card>
                                                </Grid>

Depending on your situation, you may want to use the list semantics. And it will work!


                                                
                                                <Grid cols={{ mobile: 1, tablet: 2, desktop: 3 }} elementType="ul">
                                                  <Card elementType="li">{/* … */}</Card>
                                                  <Card elementType="li">{/* … */}</Card>
                                                  <Card elementType="li">{/* … */}</Card>
                                                </Grid>

Best Practices

Making the Whole Card Clickable

To make the whole Card clickable, use the provided CardLink subcomponent. For best accessibility, you would typically wrap your CardTitle text in the CardLink component:


                                                
                                                <CardTitle>
                                                  <CardLink href="#">Card title</CardLink>
                                                </CardTitle>

This establishes a clickable overlay over the whole Card, making it easier for users to interact with the Card.

ℹ️ Don't worry, any interactive elements inside the Card (like links or buttons) will still work as expected.

If you need the text content of your CardBody remains selectable and copyable, you can use the isSelectable prop on CardBody component:


                                                
                                                <CardBody isSelectable>
                                                  <CardTitle>
                                                    <CardLink href="#">Card title</CardLink>
                                                  </CardTitle>
                                                <CardBody>

API

Name Type Default Required Description
elementType ElementType a Type of element

On top of the API options, the components accept additional attributes. If you need more control over the styling of a component, you can use style props and escape hatches.

Linking the Media

In most cases, using just a single link in the CardTitle and making the whole card clickable is the best approach in terms of accessibility. The Card will have a single accessible link which will be announced by screen readers.

However, if you cannot use the CardLink subcomponent, and you still need to make the media clickable, you can wrap the CardMedia image in a link:


                                                
                                                <CardMedia>
                                                  <Link href="#" aria-hidden="true">
                                                    <img src="https://via.placeholder.com/300x200" alt="" />
                                                  </Link>
                                                </CardMedia>

👉 Please note that the aria-hidden="true" attribute is used to hide the link from screen readers so the user is not confused by too many links in the Card.

The “Read More” Use Case

For article previews or similar use cases, you may want to display a limited amount of text content with a “Read More” link. For optimum accessibility, you should only provide this in the form of a text node, not a button or a link:


                                                
                                                <CardBody>
                                                  <CardTitle>
                                                    <CardLink href="#">Card title</a>
                                                  </CardTitle>
                                                  <p>{/* … */}</p>
                                                  {/* DON'T DO THIS */}
                                                  <Link href="#" underlined="always">Read more</Link>
                                                  {/* This is correct */}
                                                  <div class="link-primary link-underlined">Read more</div>
                                                </CardBody>

This way, the Card will only have a single accessible link which will be announced by screen readers.

Full Example

When you put it all together:


                                                
                                                <Card isBoxed>
                                                  <CardMedia>
                                                    <img src="…" alt="" />
                                                  </CardMedia>
                                                  <CardLogo>
                                                    <PartnerLogo>
                                                      <img src="…" alt="Logo" />
                                                    </PartnerLogo>
                                                  </CardLogo>
                                                  <CardBody>
                                                    <CardEyebrow>Content options</CardEyebrow>
                                                    <CardTitle>
                                                      <CardLink href="#">Card Title</CardLink>
                                                    </CardTitle>
                                                    <p>Card content</p>
                                                  </CardBody>
                                                  <CardFooter>
                                                    <ButtonLink>Primary</ButtonLink>
                                                    <ButtonLink>Secondary</ButtonLink>
                                                  </CardFooter>
                                                </Card>

ℹ️ A big shout-out to Ondřej Pohl for sharing many of these best practices!