Card
Purpose
A Card is a bounded content container that groups related information or actions into a single scannable unit. Use a Card when a set of content belongs together and benefits from visual separation from its surroundings — an entity summary, a form section, a data panel, a list item in a grid. Do NOT use a Card when the content is part of a continuous page flow that does not need visual grouping. Do NOT use a Card to add decorative whitespace around unrelated elements; Cards signal meaningful grouping, not layout padding.
Cards are the fundamental building block of the Force UI overview and detail page layouts. They are designed for data-dense enterprise contexts where many Cards appear simultaneously in grids and lists.
Anatomy
A Card is a rectangular container composed of up to five distinct slots. Not all slots are required in every instance.
- Card root (required): the outer container that establishes border, background, radius, and padding context. All visual variant logic lives here.
- Card header (optional): a horizontal row at the top of the card, typically containing a title, a description, and an optional action slot. The header is visually separated from card body content by a bottom border (
--force-color-border-default) when body content follows it. This separation is not a shadow — it is a 1px structural border. - Card title (optional, lives inside header): the primary label for the card. Uses
--force-font-size-xl(20px) or--force-font-size-sm(14px) depending on context, at--force-font-weight-semibold. - Card description (optional, lives inside header): supplementary text below the title. Uses
--force-font-size-xs(12px) at--force-font-weight-regularin--force-color-text-tertiary. - Card action slot (optional, lives inside header or footer): a push-right control such as a checkbox, icon button, or dropdown trigger. It is mechanically pushed to the far right of the header row via
margin-left: auto. - Card media (optional): a full-bleed image or visual area that renders flush to the card edges above the header. No padding is applied to this slot. Used for entity thumbnails and asset preview cards.
- Card media action (optional, lives inside card media): an absolutely-positioned overlay in one of the four corners of the media area. Used for selection checkboxes or quick-action icon buttons that live on top of a media image.
- Card header media (optional, lives inside header): a constrained-width thumbnail panel flush against the left edge of the header. Distinct from card media — this is an inline thumbnail beside the title/description, not a full-width hero image.
- Card content (optional): the free-form body area. Renders below the header. Carries horizontal padding matching the card size but no fixed height — content determines height.
- Card footer (optional): a horizontal row at the bottom of the card, typically containing action buttons. Carries horizontal and bottom padding. Buttons in the footer follow the standard Button composition rules (primary CTA right, secondary left).
Variants
filled (default)
White background (--force-color-bg-surface), 1px border in --force-color-border-default (neutral-200), --force-radius-card (8px) corner radius, --force-spacing-6 (24px) internal padding. This is the standard card used on page backgrounds (--force-color-bg-surface), sidebar panels, and modal content areas. The border is always present — it is the structural edge signal.
filled-alt
Slightly tinted background using --force-color-bg-muted (neutral-50, the characteristic Perforce cool-grey tint), transparent border. Use when the card must visually recede against an already-white surface, or when nesting a secondary panel inside a primary card. Do NOT use filled-alt as the default card style — filled is the default.
outline
Transparent background, visible 1px border in --force-color-border-default. Use when a card needs to sit within an already-colored or tinted container and a white background would create an unintended contrast jump. Common use: a nested grouping panel inside a semantic-variant section.
subtle
No background, no border. Content only. Use for structural grouping that needs spacing and slot alignment but no visual chrome — typically an inline expandable section or a region of a larger form that should not appear as a separate panel.
Semantic variants (success, warning, error, info)
These are not separate variant values in the card component itself; they are achieved by applying a semantic tinted background and a corresponding border to a filled or outline card. Use the following token pairings on the card root:
- success: background
--force-color-bg-success-subtle, border--force-color-border-success - warning: background
--force-color-bg-warning-subtle, border--force-color-border-warning - error: background
--force-color-bg-error-subtle, border--force-color-border-error - info: background
--force-color-bg-info-subtle, border--force-color-border-info
Semantic-variant cards are used for contextual callouts and status summaries (e.g., a "Build failed" summary card on a repository overview). The text inside a semantic card should use the matching semantic text token. Do NOT apply semantic tinting to cards for decorative purposes — semantic color communicates system state.
Sizes
Cards come in three size presets that scale internal spacing and radius:
- sm:
--force-radius-md(6px), gaps and padding at--force-spacing-2(8px) - md (default):
--force-radius-lg(8px), gaps and padding at--force-spacing-3(12px) - lg:
--force-radius-lg(8px), gaps and padding at--force-spacing-4(16px)
The default card padding of --force-spacing-6 (24px) described in the spacing spec applies to the canonical md-size card in page grid layouts. Use the size variants when the card appears in a condensed context (sm) or a spacious editorial context (lg).
States
Default (non-interactive): The card is a static container. It has no hover, press, or focus behavior. This is the correct state for the overwhelming majority of Cards in the system — Cards containing form sections, data panels, or entity details are not themselves interactive.
Interactive (selectable/clickable card): When an entire card functions as a navigation target or a selectable item (e.g., a project card in a grid that navigates to a project detail page, or a template card in a selection wizard), the card enters the interactive state. Interactive cards receive:
- Hover: background transitions to
--force-color-bg-interactive-hover(neutral-50); border transitions to--force-color-border-strong - Active/pressed: retain the hover background (
--force-color-bg-interactive-hover); applytransform: scale(0.995)for tactile feedback rather than a further color step - Selected: background transitions to
--force-color-bg-interactive-active(indigo-50), border to--force-color-border-primary(indigo-500) - Focus (keyboard): 2px focus ring using
--force-shadow-focus-ring+--force-color-border-focus, applied via:focus-visibleonly - Disabled: background
--force-color-bg-muted, text--force-color-text-disabled,opacity: --force-opacity-disabled(0.5), pointer-events none
State transitions use transition-duration: --force-duration-fast (150ms) with --force-easing-standard.
Dragged: When a card is being dragged in a drag-and-drop context, it receives a subtle elevation shadow (--force-shadow-sm) to signal that it is floating above its original position. This is the only circumstance in which a card may display a shadow — and it is a transient drag state, not a static presentation style.
Behavior
Non-interactive cards have no click target, no keyboard interaction, and no cursor change. Interactive cards use tabIndex="0" and respond to Enter and Space to trigger their primary action.
Cards do not scroll internally by default. When content exceeds the card height, the card either expands to fit (the standard behavior) or the content area receives an explicit overflow-y: auto with a constrained max-height — this must be a deliberate design decision, not a default.
Cards in a grid are sized by the grid, not by their own width. Do not set explicit widths on cards placed inside grid layouts. Use the grid gap token --force-spacing-4 (16px) between cards in a standard card grid.
Accessibility
Non-interactive cards are purely presentational containers. They do not receive a landmark role, a role="region", or a tabIndex. Screen readers will read the card content in DOM order. Ensure the card title is a proper heading element (h2, h3, etc.) with the appropriate hierarchy level for its context — do not use a div with font styling as a visual heading without a corresponding semantic heading.
Interactive cards must have either an aria-label describing the card's target (e.g., "Open project 'prod-clone'") or a visually-hidden link element inside the card that provides the accessible name. The card root should be a <div role="button" tabIndex={0}> only when no navigation occurs; for navigation, prefer an <a> element wrapping the card or an <a> overlay positioned absolutely within the card that covers the entire surface.
When a card contains a checkbox action (in the action slot), the checkbox handles its own accessibility independently. The card itself does not become the checkbox's label — use proper <label> association on the checkbox.
Do not nest multiple independently focusable interactive elements inside a card that is itself interactive. Either the card is the interaction target, or the elements inside it are — not both simultaneously.
Composition
Cards are the primary layout atoms of the Force UI detail and overview page patterns. They appear in three canonical layouts:
- 3-column card grid (overview tabs):
display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--force-spacing-4). Each card is a data section (activity, recent items, linked entities). - Full-width stacked panels (detail pages): a single column of cards stacked with
gap: var(--force-spacing-6)between them. Each card contains a titled section (metadata, settings, associated records). - Selectable card grid (wizard steps, template pickers): cards with the interactive prop enabled, rendered in a 2- or 3-column grid. Selection state uses the indigo-tinted interactive-active tokens.
Cards commonly contain: DataTable, List components, empty-state prompts, form field groups, CalloutCard grids (inside the card content area), and single-field metadata displays. Do not place a Card inside another Card unless the inner card is a visually subordinate panel using the filled-alt or outline variant — never nest two filled cards at the same visual weight.
Cards must never contain floating elements (modals, tooltips, dropdowns) as direct children — those elements use portal rendering and are positioned independently of card stacking context.
Guidance
Cards use borders, never shadows. This is a hard rule in the Force UI system. box-shadow is reserved for floating/detached elements (modals, dropdowns, tooltips, toasts). A card that sits in the page flow signals its boundary with border: 1px solid var(--force-color-border-default). Do NOT add box-shadow to a card to make it "pop" — this misuses the elevation vocabulary. The only exception is a card in its transient dragged state.
The card header separator is a border, not a shadow. The visual line between a card header and card body is produced by border-bottom: 1px solid var(--force-color-border-default) on the header element. Do not replicate this with a box-shadow or a background gradient.
Do not use semantic tinting for visual interest. Success/warning/error/info card variants exist to communicate actual system state. A card must not use --force-color-bg-success-subtle because green looks nice or because the content is "positive" in a general sense. Reserve semantic variants for genuine status communication.
Do not add arbitrary padding to the card root. Padding is controlled by the size variant via spacing tokens. Do not override it with inline styles or ad hoc utility classes unless you have a documented exception. When the standard padding is genuinely wrong for a use case, use the sm or lg size variant before reaching for a custom value.
Cards sit on --force-color-bg-surface or --force-color-bg-muted backgrounds. Do not place a filled (white) card on another white (--force-color-bg-surface) surface without a border — the card boundary will be invisible. If the page background is --force-color-bg-surface, always use the filled variant (which has a border) or a filled-alt variant (which has a tinted background). The outline variant is correct when the card sits on a colored background.
Token Usage
filled (default)
- Background:
--force-color-bg-surface - Border:
1px solid var(--force-color-border-default) - Border radius:
--force-radius-card(8px) - Internal padding:
--force-spacing-6(24px) for default md size - Header separator:
border-bottom: 1px solid var(--force-color-border-default)
filled-alt
- Background:
--force-color-bg-muted - Border: transparent
- Border radius:
--force-radius-card
outline
- Background: transparent
- Border:
1px solid var(--force-color-border-default) - Border radius:
--force-radius-card
subtle
- Background: transparent
- Border: transparent
- Border radius:
--force-radius-card
Semantic overlays (applied on top of filled or outline)
- Success: background
--force-color-bg-success-subtle, border--force-color-border-success, text--force-color-text-success - Warning: background
--force-color-bg-warning-subtle, border--force-color-border-warning, text--force-color-text-warning - Error: background
--force-color-bg-error-subtle, border--force-color-border-error, text--force-color-text-error - Info: background
--force-color-bg-info-subtle, border--force-color-border-info, text--force-color-text-info
Interactive state tokens
- Hover background:
--force-color-bg-interactive-hover - Hover border:
--force-color-border-strong - Selected background:
--force-color-bg-interactive-active - Selected border:
--force-color-border-primary - Focus ring:
--force-shadow-focus-ring+--force-color-border-focus - Disabled text:
--force-color-text-disabled - Disabled opacity:
--force-opacity-disabled - Drag elevation:
--force-shadow-sm(transient only)
Typography
- Card title:
--force-font-size-xl(20px) or--force-font-size-sm(14px),--force-font-weight-semibold - Card description:
--force-font-size-xs(12px),--force-font-weight-regular,--force-color-text-tertiary - Body content:
--force-font-size-sm(14px),--force-font-weight-regular,--force-color-text-secondary
Grid layout
- Gap between cards:
--force-spacing-4(16px) - Card stack gap:
--force-spacing-6(24px)
Motion
- State transition:
--force-duration-fast(150ms),--force-easing-standard