Card

ui-card is a bordered, rounded panel. You build it from parts: ui-card-header (with ui-card-title, ui-card-description and an optional ui-card-action), ui-card-content and ui-card-footer. Every part goes in the default slot, in source order, and takes any content.

Previewindex.html
Create project Deploy your new project in one click. Name Cancel Deploy
<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Card demo</title>
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@realmorg/realm@alpha/dist/realm.css">
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/dist/theme.css">
  <script type="module" src="https://cdn.jsdelivr.net/npm/@realmorg/realm@alpha/dist/realm.js"></script>
  <style>
    .demo-narrow {
      width: min(100%, calc(var(--spacing) * 88));
    }
  </style>
</head>
<body>
  <import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/button.html"></import-element>
  <import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/card.html"></import-element>
  <import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/field.html"></import-element>
  <import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/input.html"></import-element>
  <import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/label.html"></import-element>

  <div class="demo-narrow">
    <ui-card>
      <ui-card-header>
        <ui-card-title>Create project</ui-card-title>
        <ui-card-description>Deploy your new project in one click.</ui-card-description>
      </ui-card-header>
      <ui-card-content>
        <ui-field>
          <ui-label for="card-project-name">Name</ui-label>
          <ui-input id="card-project-name" placeholder="Name of your project"></ui-input>
        </ui-field>
      </ui-card-content>
      <ui-card-footer justify="between">
        <ui-button variant="outline">Cancel</ui-button>
        <ui-button>Deploy</ui-button>
      </ui-card-footer>
    </ui-card>
  </div>
</body>
</html>

Examples

Header action

ui-card-action holds a button, badge or menu. It sits to the right of the title and description and spans both rows.

Previewindex.html
Notifications You have 3 unread messages. Mark all read

Your call has been confirmed for Friday at 10:00.

<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Card demo</title>
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@realmorg/realm@alpha/dist/realm.css">
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/dist/theme.css">
  <script type="module" src="https://cdn.jsdelivr.net/npm/@realmorg/realm@alpha/dist/realm.js"></script>
  <style>
    .demo-narrow {
      width: min(100%, calc(var(--spacing) * 88));
    }
    .demo-text {
      margin: 0;
      font-size: var(--text-sm);
    }
  </style>
</head>
<body>
  <import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/button.html"></import-element>
  <import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/card.html"></import-element>

  <div class="demo-narrow">
    <ui-card>
      <ui-card-header>
        <ui-card-title>Notifications</ui-card-title>
        <ui-card-description>You have 3 unread messages.</ui-card-description>
        <ui-card-action><ui-button variant="ghost" size="sm">Mark all read</ui-button></ui-card-action>
      </ui-card-header>
      <ui-card-content>
        <p class="demo-text">Your call has been confirmed for Friday at 10:00.</p>
      </ui-card-content>
    </ui-card>
  </div>
</body>
</html>

Stat card

A card does not need a title. Use only a description and the content for a compact metric.

Previewindex.html
Total revenue +20.1%

$45,231.89

<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Card demo</title>
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@realmorg/realm@alpha/dist/realm.css">
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/dist/theme.css">
  <script type="module" src="https://cdn.jsdelivr.net/npm/@realmorg/realm@alpha/dist/realm.js"></script>
  <style>
    .demo-narrow {
      width: min(100%, calc(var(--spacing) * 88));
    }
    .demo-value {
      margin: 0;
      font-size: var(--text-2xl);
      font-weight: var(--font-weight-bold);
    }
  </style>
</head>
<body>
  <import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/badge.html"></import-element>
  <import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/card.html"></import-element>

  <div class="demo-narrow">
    <ui-card>
      <ui-card-header>
        <ui-card-description>Total revenue</ui-card-description>
        <ui-card-action><ui-badge variant="outline">+20.1%</ui-badge></ui-card-action>
      </ui-card-header>
      <ui-card-content>
        <p class="demo-value">$45,231.89</p>
      </ui-card-content>
    </ui-card>
  </div>
</body>
</html>

Login form

The content can hold a whole form. orientation="vertical" on the footer stacks its children and stretches them to the full width, which suits a single submit button.

Previewindex.html
Log in to your account Enter your email below to log in. Sign up
Email Password
Login Login with Google
<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Card demo</title>
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@realmorg/realm@alpha/dist/realm.css">
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/dist/theme.css">
  <script type="module" src="https://cdn.jsdelivr.net/npm/@realmorg/realm@alpha/dist/realm.js"></script>
  <style>
    .demo-narrow {
      width: min(100%, calc(var(--spacing) * 88));
    }
  </style>
</head>
<body>
  <import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/button.html"></import-element>
  <import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/card.html"></import-element>
  <import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/field.html"></import-element>
  <import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/input.html"></import-element>
  <import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/label.html"></import-element>

  <div class="demo-narrow">
    <ui-card>
      <ui-card-header>
        <ui-card-title>Log in to your account</ui-card-title>
        <ui-card-description>Enter your email below to log in.</ui-card-description>
        <ui-card-action><ui-button variant="link">Sign up</ui-button></ui-card-action>
      </ui-card-header>
      <ui-card-content>
        <form id="card-login" onsubmit="event.preventDefault()">
          <ui-field-group>
            <ui-field>
              <ui-label for="card-email">Email</ui-label>
              <ui-input id="card-email" name="email" type="email" placeholder="m@example.com"></ui-input>
            </ui-field>
            <ui-field>
              <ui-label for="card-password">Password</ui-label>
              <ui-input id="card-password" name="password" type="password"></ui-input>
            </ui-field>
          </ui-field-group>
        </form>
      </ui-card-content>
      <ui-card-footer orientation="vertical">
        <ui-button type="submit" form="card-login">Login</ui-button>
        <ui-button variant="outline">Login with Google</ui-button>
      </ui-card-footer>
    </ui-card>
  </div>
</body>
</html>

Repeated from state

Cards are often rendered from a list with <repeat-list>.

Previewindex.htmlcard-stats-demo.html

<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Card demo</title>
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@realmorg/realm@alpha/dist/realm.css">
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/dist/theme.css">
  <script type="module" src="https://cdn.jsdelivr.net/npm/@realmorg/realm@alpha/dist/realm.js"></script>
</head>
<body>
  <import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/card.html"></import-element>

  <import-element from="./card-stats-demo.html"></import-element>
  <card-stats-demo></card-stats-demo>
</body>
</html>
<custom-element name="card-stats-demo">
  <script>
    const dashboard = useState({
      stats: [
        { id: "revenue", label: "Revenue", value: "$45,231", change: "+20.1% from last month" },
        { id: "subscriptions", label: "Subscriptions", value: "+2,350", change: "+180.1% from last month" },
        { id: "active", label: "Active now", value: "+573", change: "+201 since last hour" },
      ],
    });
  </script>
  <style>
    :host {
      display: grid;
      grid-template-columns: repeat(auto-fit, minmax(calc(var(--spacing) * 48), 1fr));
      gap: calc(var(--spacing) * 4);
      width: 100%;
    }
    .value {
      margin: 0;
      font-size: var(--text-2xl);
      font-weight: var(--font-weight-bold);
    }
    .change {
      margin: calc(var(--spacing) * 1) 0 0;
      font-size: var(--text-xs);
      color: var(--muted-foreground);
    }
  </style>
  <repeat-list of="#dashboard.stats" as="stat" key="id">
    <ui-card>
      <ui-card-header>
        <ui-card-description><slot name="#stat.label"></slot></ui-card-description>
      </ui-card-header>
      <ui-card-content>
        <p class="value"><slot name="#stat.value"></slot></p>
        <p class="change"><slot name="#stat.change"></slot></p>
      </ui-card-content>
    </ui-card>
  </repeat-list>
</custom-element>

API

Parts

Part Slot Renders
ui-card The bordered panel. A flex column with a gap between its parts. Fills its container's width.
ui-card-header (default) A grid for the title, description and action, with horizontal padding.
ui-card-title (default) A heading (level 3 unless level says otherwise) in var(--text-base) semibold. Place it in ui-card-header.
ui-card-description (default) Supporting text in muted var(--text-sm). Place it in ui-card-header.
ui-card-action (default) Content in the header's second column, aligned to the end and spanning the title and description rows.
ui-card-content (default) The body, with horizontal padding.
ui-card-footer (default) A row of actions at the bottom, with a gap and horizontal padding.

Every part has a single default slot that takes any content.

ui-card-title attributes

Attribute Type Default Description
level number (1 to 6) 3 The heading level. Use 1 for a card that is the page's main content and 2 for cards directly under the page heading.
Attribute Type Default Description
justify "between" | "end" Spreads the children apart, or pushes them to the end. Without it they start at the left.
orientation "vertical" Stacks the children and stretches them to the full width.

The other parts have no attributes.

CSS parts

Element Part Description
ui-card-title title The heading element (role="heading" with aria-level) around the title text.

Accessibility

The card has no role. ui-card-title renders a heading, level 3 by default, so the card shows up in the document outline. Set level so the heading follows the page's outline without skipping levels. Wrap the card in <article> or <section> when it represents a standalone item.

Edit this page on GitHub · View as Markdown