Skip to content

Card

The card is extremely versetile and can be used on its own, or as a building block for accordions, dialogs and more.

Variants

Text
Outlined
Tonal
Elevated
html
<!-- .text class optional -->
<div class="card text">
  <div class="content">Text</div>
</div>

<div class="card outlined">
  <div class="content">Outlined</div>
</div>

<div class="card tonal">
  <div class="content">Tonal</div>
</div>

<div class="card elevated">
  <div class="content">Elevated</div>
</div>

Why does a text variant exist?

It really doesn't make sense to use the text variant unless you really need to.

The accordion group is a great example where Open Props UI leverages the text variant of the .card component.

Anatomy

Open Props UI include these complementary utility components to handle various use cases:

  1. Container
  2. <hgroup> (optional): a wrapper for the card header
  3. .content (optional): a wrapper for the card content
  4. .actions (optional): a wrapper that groups a set of buttons

Overline

Headline

Subhead

Explain more about the topic shown in the headline and subhead through supporting text.
html
<div class="card outlined">
  <hgroup>
    <p>Overline</p>
    <h2 class="h3">Headline</h2>
    <p>Subhead</p>
  </hgroup>
  <div class="content">
    Explain more about the topic shown in the headline and subhead through
    supporting text.
  </div>
  <div class="actions">
    <button class="button">Share</button>
    <button class="button">Learn more</button>
  </div>
</div>

API

These are the classes and attributes a card can be styled with. As usual, feel free to add your own!

TypeModifiersDefaultDescription
Children& > <hgroup>, & > .content, & > .actions-Optional wrappers for child content.
Variants.text, .outlined, .tonal, .elevated.textThe variant to use.

See also

Browser compatibility

Installation

WARNING

Other components might depend on the card component. Be mindful when making changes.

Accordion, Dialog

css
:where(.card) {
  --_bg-color: transparent;
  --_border-color: transparent;
  --_border-width: 0;
  --_shadow: none;

  background-color: var(--_bg-color);
  border-color: var(--_border-color);
  border-radius: var(--surface-border-radius, 0.25rem);
  border-style: solid;
  border-width: var(--_border-width);
  box-shadow: var(--_shadow);
  overflow: hidden;
  padding-inline: 0;

  /* Variants */
  &.text {
    --_bg-color: transparent;
    --_border-color: transparent;
    --_border-width: 0;
    --_shadow: none;
    padding-inline: 0;
  }

  &.tonal {
    --_bg-color: var(--surface-tonal);
    --_border-width: 1px;
  }

  &.elevated {
    --_bg-color: var(--surface-elevated);
    --_border-color: transparent;
    --_border-width: 0;
    --_shadow: var(--shadow-3);

    /* Adjust shadow in dark mode */
    @container style(--color-scheme: dark) {
      --_shadow: var(--shadow-4);
    }
  }

  &.outlined {
    --_bg-color: var(--surface-default);
    --_border-color: var(--border-color);
    --_border-width: 1px;
  }

  :where(hgroup, .content) {
    padding-inline: var(--size-3);
  }

  hgroup {
    margin-block: var(--size-3);
    & > * {
      margin: 0;
    }

    /* Top paragraph */
    & > p:first-of-type:first-child {
      line-height: 1.3;
    }

    /* Bottom paragraph */
    & > p:last-of-type:last-child:not(:first-child) {
      font-size: var(--font-size-1, 1rem);
    }
  }

  .content {
    margin-block: var(--size-3) var(--size-4, 1.25rem);
  }

  .actions {
    display: flex;
    gap: var(--size-1);
    margin-block: var(--size-4, 1.25rem) 0;
    padding-block-end: var(--size-1);
    padding-inline: var(--size-1) var(--size-3);
  }
}