OperantAI Workflow Design System
ComfortableCompact

Card (.ods-card) is the base container primitive. It ships unopinionated about internal layout — compose it with CardHeader/CardTitle/CardBody/CardFooter for the common header-body-footer shape, or use the base Card alone for a plain bordered surface.

Installation

HTML — already included if you loaded the 3 stylesheets. See the Installation guide.

React

npm install @operant/ui

@operant/ui is currently a monorepo workspace package, not yet published to the public npm registry.

Usage

HTML

<div class="ods-card">
  <div class="ods-card-header">
    <div class="ods-card-title">Deploy pipeline</div>
    <span class="ods-badge -success">Healthy</span>
  </div>
  <div class="ods-card-body">Last run completed 4 minutes ago across 3 regions.</div>
  <div class="ods-card-footer">
    <button type="button" class="ods-btn -ghost -sm">View trace</button>
  </div>
</div>

React

import { Badge, Button, Card, CardBody, CardFooter, CardHeader, CardTitle } from "@operant/ui";

export default function Example() {
  return (
    <Card style={{ width: 320 }}>
      <CardHeader>
        <CardTitle>Deploy pipeline</CardTitle>
        <Badge variant="success">Healthy</Badge>
      </CardHeader>
      <CardBody>Last run completed 4 minutes ago across 3 regions.</CardBody>
      <CardFooter>
        <Button variant="ghost" size="sm">
          View trace
        </Button>
      </CardFooter>
    </Card>
  );
}

Examples

Basic

The default Card has a flat surface, a hairline border, and no shadow — appropriate for cards sitting in a grid where every card is equally "at rest."

<div class="ods-card" style="width: 320px">
  <div class="ods-card-body">A plain card with body content only.</div>
</div>
<Card style={{ width: 320 }}>
  <CardBody>A plain card with body content only.</CardBody>
</Card>

Elevated

-elevated adds --shadow-sm to lift the card above the page — use for a card that should read as "on top of" its surroundings (e.g. a floating summary panel), not for every card in a dense list.

<div class="ods-card -elevated" style="width: 320px">
  <div class="ods-card-body">
    Elevated cards use <code>--shadow-sm</code> to sit above the page.
  </div>
</div>
<Card elevated style={{ width: 320 }}>
  <CardBody>Elevated cards use `--shadow-sm` to sit above the page.</CardBody>
</Card>

Interactive

-interactive sets cursor: pointer and animates border-color/box-shadow on hover — use when the entire card is a click target (e.g. it opens a run detail page).

<div class="ods-card -interactive" style="width: 320px">
  <div class="ods-card-body">Click anywhere on this card to open the run.</div>
</div>
<Card interactive style={{ width: 320 }}>
  <CardBody>Click anywhere on this card to open the run.</CardBody>
</Card>

API Reference

<Card>

Renders a <div class="ods-card">.

PropTypeDefaultDescription
elevatedbooleanfalseAdds -elevated (--shadow-sm).
interactivebooleanfalseAdds -interactive (pointer cursor, hover border/shadow transition).
classNamestring—Additional classes merged onto the card.
childrenReactNode—Card content — typically CardHeader/CardBody/CardFooter.

All other native <div> attributes are forwarded.

<CardHeader> / <CardTitle> / <CardBody> / <CardFooter>

Each is a thin wrapper with no props beyond native <div> attributes:

ComponentClassNotes
CardHeader.ods-card-headerFlex row, bottom hairline border. Typically holds CardTitle + a Badge.
CardTitle.ods-card-title--fs-lg / --fw-semibold heading text.
CardBody.ods-card-bodyPadded content area.
CardFooter.ods-card-footerFlex row, top hairline border. Typically holds action Buttons.

HTML-side modifiers: .ods-card (base) + optional -elevated and/or -interactive.

Accessibility

  • Card renders a plain <div> with no implicit role or focusability. If a card is -interactive and the entire surface is clickable, make sure the click target is also reachable by keyboard — e.g. wrap the card in a real <a>/<button>, or add tabIndex={0} plus a keydown handler for Enter/Space, and an appropriate role (link or button).
  • Don't rely on -interactive's hover styling alone to signal clickability — pair it with a visible affordance (a chevron, "View trace" link, etc.) inside the card.
  • CardTitle is a styled <div>, not a heading element — use a real <h2>–<h6> inside it (or as a sibling) if the card title should participate in the page's heading outline.