JGuang

Card

Card · Astryx

Preview
npm install @astryxdesign/core@0.7.1 @astryxdesign/theme-neutral@0.7.1 @stylexjs/stylex@0.19.0

Card

Card is a bordered, elevated container for discrete, self-contained items: things you could reorder, remove, or interact with independently. Cards are NOT the default layout tool. Most content groups don't need a container at all; spacing and alignment create visual grouping naturally. Only reach for a Card when items need clear interaction boundaries or visual comparison in a grid.

Installation

npm install @astryxdesign/core@0.7.1 @astryxdesign/theme-neutral@0.7.1 @stylexjs/stylex@0.19.0

Requires React 19+, base CSS and a Theme provider. See Setup.

Card

Card is a bordered, elevated container for discrete, self-contained items: things you could reorder, remove, or interact with independently. Cards are NOT the default layout tool. Most content groups don't need a container at all; spacing and alignment create visual grouping naturally. Only reach for a Card when items need clear interaction boundaries or visual comparison in a grid.

PropTypeDefaultDescription
widthSizeValueWidth of the card (number = pixels, string = used as-is). In a flex row or grid track this is the preferred width: the card shrinks below it when the row is narrower. To hold the width in a flex row, wrap the card in StackItem (static by default) and let the row wrap or scroll. In Grid, set a consumer minWidth on the Card and size the track accordingly.
heightSizeValueHeight of the card (number = pixels, string = used as-is).
maxWidthSizeValueMaximum width of the card.
minHeightSizeValueMinimum height of the card.
childrenReactNodeContent to render inside the card.
padding0 | 0.5 | 1 | 1.5 | 2 | 3 | 4 | 5 | 6 | 8 | 10the theme's card padding (spacing step 4 with no theme)Internal padding using the spacing scale. Omit it and the card takes the theme's card padding rather than a step, so passing a step is a decision to override the theme, not a way to restate the default.
variant'default' | 'transparent' | 'muted' | 'blue' | 'cyan' | 'gray' | 'green' | 'orange' | 'pink' | 'purple' | 'red' | 'teal' | 'yellow''default'Background color variant. default uses the standard card background. transparent drops the background entirely. muted uses the muted background for de-emphasised cards. The non-semantic variants use the corresponding --color-background-<name> token.
elevation'none' | 'low' | 'med' | 'high''none'Resting shadow depth. none is flat; low/med/high map to the shadow token scale. Raise a card only when it needs to float above surrounding content.

Usage guidance

  • Ask "could I reorder or remove this independently?" If yes, it's a card. If no, it's just a section of the page: use a heading + Stack or Section.
  • Use cards for discrete items: a single user profile, a single notification, a single metric, a product in a grid. Each card represents one "thing" with clear interaction boundaries.
  • Spacing and alignment alone create visual grouping. Not everything needs a container; try removing the card and see if the grouping is still clear from whitespace and typography.
  • Keep padding consistent across sibling cards so they align visually in a grid or list.
  • Pair a card with Layout when you need a structured header, scrollable content, and footer with actions.
  • Let cards share a row or grid on a phone: a card shrinks to fit its track and clips content that cannot wrap at its edge. Truncate long IDs, hashes, and URLs with Text maxLines={1} (the full value shows in a tooltip), and give wide content its own scroll region: Table scrolls by itself; put code or other wide content in a ScrollableArea.
  • Avoid: Default to cards for visual grouping. A heading + Stack with proper spacing creates hierarchy without adding borders everywhere. Cards should be the exception, not the default.
  • Avoid: Wrap page sections in cards. "General Settings", "Notification Preferences", form groups: these are page regions, use Section or heading + stack.
  • Avoid: Create identical card grids (icon + heading + text, repeated). Vary the layout or question whether cards are needed at all.
  • Avoid: Nest cards inside other cards; flatten the hierarchy or use spacing and dividers instead.
  • Avoid: Use color variants for status; use Banner or Badge for that. Color cards are for categorization.

Cards sharing a row on a phone, with long IDs

import {Card} from '@astryxdesign/core/Card';
import {Grid} from '@astryxdesign/core/Grid';
import {VStack} from '@astryxdesign/core/Layout';
import {Heading} from '@astryxdesign/core/Heading';
import {Text} from '@astryxdesign/core/Text';

const payouts = [
  {label: 'Payout', amount: '$1,240.00', id: 'po_1Q8ZK4X7M2N5P3R6T9V1W4YB'},
  {label: 'Refund', amount: '-$86.50', id: 're_3NfK9Lm2Qx7Vb4Tz8Rw1Hy6C'},
  {label: 'Adjustment', amount: '$12.00', id: 'adj_7Hq2Wm9Kx4Lz8Vn3Rb6Tf1PD'},
];

// Each card takes one grid track and shrinks with it, so three cards fit a
// 390px phone without widening the page. The ID cannot wrap, so it truncates
// with maxLines={1} (the full value shows in a tooltip) instead of running
// past the card edge.
function PayoutCards() {
  return (
    <Grid columns={3} gap={3}>
      {payouts.map(payout => (
        <Card key={payout.id}>
          <VStack gap={1}>
            <Heading level={3}>{payout.label}</Heading>
            <Text hasTabularNumbers>{payout.amount}</Text>
            <Text type="supporting" maxLines={1}>
              {payout.id}
            </Text>
          </VStack>
        </Card>
      ))}
    </Grid>
  );
}

Theming API

{
  "container": true,
  "targets": [
    {
      "className": "astryx-card",
      "visualProps": [
        "variant",
        "elevation"
      ]
    }
  ],
  "vars": [
    {
      "name": "--_card-radius",
      "description": "Border radius of the card",
      "default": "var(--radius-container)",
      "private": true
    },
    {
      "name": "--_card-elevation",
      "description": "Resting shadow of the card, set from the elevation prop. Composed into the card box-shadow list alongside --_card-ring rather than written as boxShadow directly, so a ring and an elevation can coexist.",
      "default": "0 0 transparent",
      "private": true
    },
    {
      "name": "--_card-ring",
      "description": "Inset ring drawn in the card box-shadow list. SelectableCard sets it to show selection without taking over the shadow.",
      "default": "0 0 transparent",
      "private": true
    }
  ],
  "derived": [
    {
      "property": "borderRadius",
      "vars": [
        "--_card-radius"
      ]
    },
    {
      "property": "padding",
      "expand": "container"
    }
  ]
}

Anatomy

  • Container: The outer box with border, background, border-radius, and padding.
  • Content: Any children rendered inside the card. Often a stack of heading, text, and actions.

Examples

  • Card: A card with a heading and body text showing the default container style.
  • Card — Callout: Muted-variant cards for tips, notes, or supplementary information. Use when content should be visually distinct but not prominent. The muted variant uses a wash background instead of the elevated default, making it feel recessed rather than raised. Works well in sidebars, help panels, or inline callouts.
  • Card — Elevations: The four elevation levels side by side. Cards are flat by default; raise a card with elevation only when it needs to float above surrounding content.
  • Card — Variants: Default, muted, and color variants side by side. Use color variants to categorize cards visually, like team colors, project tags, or content types. Each color uses the corresponding background token from the theme, so they adapt to light and dark mode automatically.
  • Card — Layout: A card with a structured header, content area, and footer with action buttons. Use for forms, dialogs, or settings panels that need clear sections. Pair Card with Layout to get automatic dividers between header, content, and footer. The footer aligns actions to the right by default.
  • Card — Simple: A card with a heading and body text. Use for summaries, descriptions, or any grouped content that needs visual separation from the page. The card handles its own border, background, and padding; just pass your content as children. Set a width to constrain it, or leave it to fill the parent.