diff --git a/apps/docs/src/components/document-renderer/components/category-overview/category-overview.tsx b/apps/docs/src/components/document-renderer/components/category-overview/category-overview.tsx
index ffb2b0a58..e6a11be3d 100644
--- a/apps/docs/src/components/document-renderer/components/category-overview/category-overview.tsx
+++ b/apps/docs/src/components/document-renderer/components/category-overview/category-overview.tsx
@@ -93,13 +93,12 @@ const CategoryOverviewContent: FC<{ variant?: string }> = ({ variant }) => {
-
+
@@ -118,7 +117,7 @@ const CategoryOverviewContent: FC<{ variant?: string }> = ({ variant }) => {
-
+
@@ -135,11 +134,10 @@ const CategoryOverviewContent: FC<{ variant?: string }> = ({ variant }) => {
-
+
@@ -157,7 +155,7 @@ const CategoryOverviewContent: FC<{ variant?: string }> = ({ variant }) => {
{doc.description}
-
+
))}
diff --git a/apps/docs/src/components/document-renderer/components/frontpage/frontpage.tsx b/apps/docs/src/components/document-renderer/components/frontpage/frontpage.tsx
index f8d0d31a5..72dd52da3 100644
--- a/apps/docs/src/components/document-renderer/components/frontpage/frontpage.tsx
+++ b/apps/docs/src/components/document-renderer/components/frontpage/frontpage.tsx
@@ -39,7 +39,7 @@ const links = [
export const Frontpage = () => {
return (
-
+
Nimbus
@@ -55,15 +55,15 @@ export const Frontpage = () => {
-
+
{links.map((link) => (
-
-
+
+ {link.icon}
@@ -77,7 +77,7 @@ export const Frontpage = () => {
-
+
))}
diff --git a/openspec/changes/rework-card-component/proposal.md b/openspec/changes/rework-card-component/proposal.md
new file mode 100644
index 000000000..a6c3e60c3
--- /dev/null
+++ b/openspec/changes/rework-card-component/proposal.md
@@ -0,0 +1,140 @@
+# Proposal: Rework Card Component
+
+## Summary
+
+Rework the Card component to fix architectural flaws, align with Nimbus compound
+component conventions, and add missing sub-components. The current
+implementation uses a unique context-registration pattern (useState + useEffect
+to register JSX from children into parent state) that no other Nimbus component
+uses and that introduces re-render risks. This proposal replaces it with direct
+rendering (matching Dialog, DefaultPage, Accordion, etc.), renames Content to
+Body for Nimbus naming consistency, adds a Footer sub-component, and moves
+padding logic to CSS-only slot styling.
+
+## Motivation
+
+1. **Context registration anti-pattern**: CardHeader and CardContent return
+ `null` and use `useEffect` to register JSX into parent state. The effect
+ dependencies include `styleProps` and `functionalProps` which are new objects
+ every render, creating infinite re-render risk. Every other compound
+ component in Nimbus renders children directly.
+
+2. **Missing Footer**: Every major UI library (Chakra v3, MUI, shadcn, React
+ Bootstrap) and Nimbus compound components (Dialog, DefaultPage) provide a
+ footer region. Card lacks one.
+
+3. **Naming inconsistency**: Nimbus convention is "Content" for outer structural
+ containers (Dialog.Content, Drawer.Content) and "Body" for main content areas
+ (Dialog.Body, Drawer.Body). Card.Content is doing the Body job.
+
+4. **Non-standard variant props**: Card uses `cardPadding`, `borderStyle`,
+ `elevation`, and `backgroundStyle` instead of the standard Nimbus `variant`
+ and `size` props. Every other Nimbus component (Button, Badge, Alert, Tabs,
+ etc.) uses `variant` for visual treatment and `size` for dimensional scaling.
+ The four separate visual props create a combinatorial explosion (12
+ permutations) that should be collapsed into curated `variant` presets.
+
+5. **Hardcoded gap**: The internal Stack with `gap="200"` is not configurable.
+ Consumers cannot fine-tune spacing between card sections.
+
+6. **`display: inline-flex`**: Unusual default for a card container. Cards
+ typically fill available width.
+
+7. **Empty slot styles**: Header and Content slots are declared but have zero
+ styles in the recipe, making the slot system pointless for these parts.
+
+## Impact
+
+- **Breaking change**: `Card.Content` renamed to `Card.Body`. Card is not yet in
+ consumer use, so impact is zero.
+- **Breaking change**: `cardPadding`, `borderStyle`, `elevation`,
+ `backgroundStyle` replaced by standard `variant` and `size` props.
+- **Additive**: `Card.Footer` new sub-component.
+- **Internal**: Architecture simplification (context registration removed),
+ recipe restructured for CSS-only padding distribution.
+
+## Scope
+
+- Affected spec: `nimbus-card`
+- Files changed: `card.tsx`, `card.types.ts`, `card.recipe.ts`, `card.slots.tsx`,
+ `components/card.root.tsx`, `components/card.header.tsx`,
+ `components/card.content.tsx` (renamed to card.body.tsx),
+ new `components/card.footer.tsx`, `components/index.ts`,
+ `card.stories.tsx`, `card.docs.spec.tsx`, `card/index.ts`
+- Theme registration: slot-recipes/index.ts (slot name update)
+
+## Design Decisions
+
+### Direct rendering over context registration
+
+Every other Nimbus compound component (Dialog, DefaultPage, Accordion,
+PageContent) renders children directly via slot components. The Card's
+context-registration pattern was meant to enforce Header-before-Content ordering
+regardless of JSX placement, but this guarantee adds complexity without clear
+consumer value. Removing it aligns Card with the rest of the library.
+
+### CSS-only padding distribution
+
+Move padding from Root to individual slots. Each slot receives full padding
+(`p: --card-spacing`). When two card slots are directly adjacent, the later slot
+suppresses its own top padding via adjacent sibling class selectors (e.g.
+`.nimbus-card__header + .nimbus-card__body`). This ensures correct spacing
+regardless of which combination of Header/Body/Footer is present, with no
+runtime logic. When a non-slot element (e.g. Separator) sits between slots, both
+slots retain full padding, providing visually balanced spacing around the
+element.
+
+### Standard `variant` and `size` props
+
+Nimbus convention: `variant` controls visual treatment (border, shadow,
+background), `size` controls dimensions (padding, gap, font). Card's four
+separate props (`cardPadding`, `borderStyle`, `elevation`, `backgroundStyle`)
+are replaced by:
+
+**`size`** (sm | md | lg, default: md) — replaces `cardPadding`:
+- Sets `--card-spacing` CSS variable used for all slot padding
+- sm: spacing.300
+- md: spacing.400
+- lg: spacing.600
+
+**`variant`** (outlined | elevated | filled | plain, default: outlined) —
+replaces `borderStyle` + `elevation` + `backgroundStyle`:
+- `outlined`: border solid-25 + colorPalette.6, default bg, no shadow (current default)
+- `elevated`: no border, shadow level 1, default bg
+- `filled`: no border, no shadow, colorPalette.2 muted bg
+- `plain`: no border, no shadow, default bg (minimal)
+
+This eliminates the 12-permutation combinatorial explosion and gives consumers
+the same vocabulary used by Button, Alert, Badge, Tabs, and every other Nimbus
+component.
+
+### Unified spacing via `--card-spacing`
+
+Instead of a hardcoded `gap="200"`, a single `--card-spacing` CSS variable
+controls both slot padding and inter-slot spacing. Each size variant sets this
+variable, and adjacent sibling selectors collapse redundant padding between
+slots. This keeps proportional spacing without adding a new prop.
+
+### display: flex (not inline-flex)
+
+Cards are block-level containers. `flex` with `flexDirection: column` gives
+natural full-width behavior while maintaining flex layout for internal spacing
+via `gap`.
+
+### Slot-based ARIA wiring via React Aria
+
+Card.Root uses React Aria's `useSlotId` + `Provider` pattern (identical to React
+Aria Components' DropZone) to automatically wire `aria-labelledby` and
+`aria-describedby`. When a consumer places `` or
+`` inside a Card, the Card:
+
+1. Gets `role="article"` (only when slots are present)
+2. Gets `aria-labelledby` pointing to the Heading's auto-generated ID
+3. Gets `aria-describedby` pointing to the Text's auto-generated ID
+
+This follows the Adobe React Spectrum Card spec
+([adobe/react-spectrum#2080](https://github.com/adobe/react-spectrum/issues/2080)).
+The wiring is zero-config for consumers and non-breaking: Cards without
+slot-prop children remain plain divs with no role or ARIA attributes. The
+conditional `role="article"` avoids polluting screen reader landmark/article
+navigation for cards that are purely visual containers.
diff --git a/openspec/changes/rework-card-component/specs/nimbus-card/spec.md b/openspec/changes/rework-card-component/specs/nimbus-card/spec.md
new file mode 100644
index 000000000..fd473d528
--- /dev/null
+++ b/openspec/changes/rework-card-component/specs/nimbus-card/spec.md
@@ -0,0 +1,354 @@
+# Spec Delta: nimbus-card
+
+## MODIFIED Requirements
+
+### Requirement: Namespace Structure
+
+The component SHALL export as compound component namespace.
+
+#### Scenario: Component parts
+
+- **WHEN** Card is imported
+- **THEN** SHALL provide Card.Root as card container
+- **AND** SHALL provide Card.Header for title section
+- **AND** SHALL provide Card.Body for main content area
+- **AND** SHALL provide Card.Footer for actions/metadata section
+- **AND** Root SHALL be first property in namespace
+
+#### Scenario: Flexible composition
+
+- **WHEN** consumer uses Card components
+- **THEN** SHALL allow Card.Root without compound parts (free-form content)
+- **AND** SHALL allow Card.Root with any single part (Header, Body, or Footer)
+- **AND** SHALL allow any combination of Header, Body, and Footer
+- **AND** SHALL render parts in DOM order as placed by consumer
+- **AND** SHALL apply correct edge padding via CSS regardless of which parts are
+ present
+
+### Requirement: Container Component
+
+The component SHALL provide root container with styling configuration.
+
+#### Scenario: Root rendering
+
+- **WHEN** Card.Root renders
+- **THEN** SHALL render as div element
+- **AND** SHALL accept all Chakra style props
+- **AND** SHALL apply recipe variants
+- **AND** SHALL NOT use React context for child layout coordination
+
+#### Scenario: Layout behavior
+
+- **WHEN** Card.Root renders with child parts
+- **THEN** SHALL use CSS flexbox column layout
+- **AND** SHALL use a unified `--card-spacing` CSS variable for all spacing
+- **AND** spacing between adjacent slot children equals `--card-spacing` (via
+ slot padding, not flexbox gap)
+- **AND** SHALL NOT wrap children in an intermediate Stack component
+
+#### Scenario: Style prop forwarding
+
+- **WHEN** style props are provided on Root
+- **THEN** SHALL extract and apply style props to root element
+- **AND** SHALL separate recipe props from style props
+- **AND** SHALL apply recipe variants first, then style overrides
+
+### Requirement: Title Section
+
+The component SHALL provide header section for titles and metadata.
+
+#### Scenario: Header rendering
+
+- **WHEN** Card.Header renders
+- **THEN** SHALL render as div element via header slot component
+- **AND** SHALL accept all Chakra style props
+- **AND** SHALL render content as provided
+- **AND** SHALL forward ref to div element
+
+#### Scenario: Header padding
+
+- **WHEN** Card.Header renders
+- **THEN** SHALL receive full padding (`p: --card-spacing`) on all sides
+- **AND** when followed directly by Body or Footer, the next slot suppresses its
+ own top padding via adjacent sibling selectors
+
+### Requirement: Main Content Area
+
+The component SHALL provide primary content container named Body.
+
+#### Scenario: Body rendering
+
+- **WHEN** Card.Body renders
+- **THEN** SHALL render as div element via body slot component
+- **AND** SHALL accept all Chakra style props
+- **AND** SHALL support any content type
+- **AND** SHALL forward ref to div element
+
+#### Scenario: Body padding
+
+- **WHEN** Card.Body renders
+- **THEN** SHALL receive full padding (`p: --card-spacing`) on all sides
+- **AND** SHALL suppress top padding when directly preceded by Card.Header
+ (adjacent sibling selector)
+
+### Requirement: Footer Section
+
+The component SHALL provide footer section for actions and metadata.
+
+#### Scenario: Footer rendering
+
+- **WHEN** Card.Footer renders
+- **THEN** SHALL render as div element via footer slot component
+- **AND** SHALL accept all Chakra style props
+- **AND** SHALL render content as provided
+- **AND** SHALL forward ref to div element
+
+#### Scenario: Footer padding
+
+- **WHEN** Card.Footer renders
+- **THEN** SHALL receive full padding (`p: --card-spacing`) on all sides
+- **AND** SHALL suppress top padding when directly preceded by Card.Header or
+ Card.Body (adjacent sibling selectors)
+- **AND** when a non-slot element (e.g. Separator) sits between slots, both
+ slots retain full padding for visually balanced spacing around the element
+
+### Requirement: Size Options
+
+The component SHALL support size variants controlling padding and gap per
+nimbus-core standards.
+
+#### Scenario: Small size
+
+- **WHEN** size="sm" is set on Root
+- **THEN** SHALL set `--card-spacing` to `spacing.300` token
+- **AND** all slots SHALL use `--card-spacing` for padding on all sides
+- **AND** adjacent slots SHALL collapse top padding on the later slot
+
+#### Scenario: Medium size
+
+- **WHEN** size="md" is set or no value provided (default)
+- **THEN** SHALL set `--card-spacing` to `spacing.400` token
+- **AND** all slots SHALL use `--card-spacing` for padding on all sides
+- **AND** adjacent slots SHALL collapse top padding on the later slot
+
+#### Scenario: Large size
+
+- **WHEN** size="lg" is set on Root
+- **THEN** SHALL set `--card-spacing` to `spacing.600` token
+- **AND** all slots SHALL use `--card-spacing` for padding on all sides
+- **AND** adjacent slots SHALL collapse top padding on the later slot
+
+### Requirement: Visual Variant Options
+
+The component SHALL support variant prop for visual treatment per nimbus-core
+standards.
+
+#### Scenario: Outlined variant
+
+- **WHEN** variant="outlined" is set or no value provided (default)
+- **THEN** SHALL render with solid-25 border using colorPalette.6
+- **AND** SHALL use default background (bg token)
+- **AND** SHALL have no shadow
+
+#### Scenario: Elevated variant
+
+- **WHEN** variant="elevated" is set
+- **THEN** SHALL render without border
+- **AND** SHALL apply shadow token level 1
+- **AND** SHALL use default background (bg token)
+
+#### Scenario: Filled variant
+
+- **WHEN** variant="filled" is set
+- **THEN** SHALL render without border
+- **AND** SHALL have no shadow
+- **AND** SHALL use colorPalette.2 muted background
+
+#### Scenario: Plain variant
+
+- **WHEN** variant="plain" is set
+- **THEN** SHALL render without border
+- **AND** SHALL have no shadow
+- **AND** SHALL use default background (bg token)
+
+### Requirement: Layout Display
+
+The component SHALL control layout display characteristics.
+
+#### Scenario: Display mode
+
+- **WHEN** Card.Root renders
+- **THEN** SHALL use display: flex
+- **AND** SHALL use flexDirection: column
+- **AND** SHALL allow width override via style props
+- **AND** SHALL align items flex-start for vertical layout
+
+### Requirement: Multi-Slot Recipe
+
+The component SHALL use multi-slot recipe per nimbus-core standards.
+
+#### Scenario: Slot styling
+
+- **WHEN** card renders
+- **THEN** SHALL apply card slot recipe from theme/slot-recipes
+- **AND** SHALL style: root, header, body, footer slots
+- **AND** SHALL support size variants: sm, md, lg
+- **AND** SHALL support variant options: outlined, elevated, filled, plain
+
+#### Scenario: Recipe registration
+
+- **WHEN** Card component is used
+- **THEN** cardRecipe SHALL be registered in theme/slot-recipes/index.ts
+- **AND** registration SHALL use "nimbusCard" key
+- **AND** registration SHALL be manual (no auto-discovery)
+
+### Requirement: Type Definitions
+
+The component SHALL provide comprehensive TypeScript types per nimbus-core
+standards.
+
+#### Scenario: Recipe props type
+
+- **WHEN** CardRecipeProps type is defined
+- **THEN** SHALL include size?: "sm" | "md" | "lg"
+- **AND** SHALL include variant?: "outlined" | "elevated" | "filled" | "plain"
+- **AND** SHALL extend UnstyledProp
+
+#### Scenario: Slot props types
+
+- **WHEN** slot props types are defined
+- **THEN** SHALL export CardRootSlotProps extending HTMLChakraProps<"div",
+ CardRecipeProps>
+- **AND** SHALL export CardHeaderSlotProps extending HTMLChakraProps<"div">
+- **AND** SHALL export CardBodySlotProps extending HTMLChakraProps<"div">
+- **AND** SHALL export CardFooterSlotProps extending HTMLChakraProps<"div">
+
+#### Scenario: Main props types
+
+- **WHEN** main component props types are defined
+- **THEN** SHALL export CardProps with children, ref, data-\* attributes
+- **AND** SHALL export CardHeaderProps with children and ref
+- **AND** SHALL export CardBodyProps with children and ref
+- **AND** SHALL export CardFooterProps with children and ref
+- **AND** SHALL omit internal props using OmitInternalProps utility
+- **AND** all props SHALL have JSDoc documentation
+
+### Requirement: Debug Identification
+
+The component SHALL provide display names for debugging per nimbus-core
+standards.
+
+#### Scenario: Display name setting
+
+- **WHEN** Card components are defined
+- **THEN** Card.Root SHALL set displayName="Card.Root"
+- **AND** Card.Header SHALL set displayName="Card.Header"
+- **AND** Card.Body SHALL set displayName="Card.Body"
+- **AND** Card.Footer SHALL set displayName="Card.Footer"
+- **AND** names SHALL appear in React DevTools
+
+### Requirement: Content Flexibility
+
+The component SHALL support flexible content composition.
+
+#### Scenario: Free-form content
+
+- **WHEN** Card.Root contains non-compound children
+- **THEN** SHALL render children directly
+- **AND** SHALL not require Header, Body, or Footer components
+- **AND** SHALL maintain styling from variants
+- **AND** SHALL allow complete layout freedom
+
+#### Scenario: Mixed content
+
+- **WHEN** Card.Root contains both compound parts and other elements
+- **THEN** SHALL render all children in DOM order
+- **AND** SHALL apply slot styling to compound parts
+- **AND** SHALL maintain proper spacing via gap
+
+### Requirement: Slot-Based Accessibility
+
+The component SHALL automatically apply ARIA attributes when React Aria slot
+children are present.
+
+#### Scenario: Full slot wiring
+
+- **WHEN** Card.Root contains `` and
+ ``
+- **THEN** SHALL apply `role="article"` on root element
+- **AND** SHALL apply `aria-labelledby` pointing to the Heading's generated ID
+- **AND** SHALL apply `aria-describedby` pointing to the Text's generated ID
+
+#### Scenario: Title slot only
+
+- **WHEN** Card.Root contains `` but no
+ ``
+- **THEN** SHALL apply `role="article"` on root element
+- **AND** SHALL apply `aria-labelledby` pointing to the Heading's generated ID
+- **AND** SHALL NOT apply `aria-describedby`
+
+#### Scenario: Description slot only
+
+- **WHEN** Card.Root contains `` but no
+ ``
+- **THEN** SHALL apply `role="article"` on root element
+- **AND** SHALL NOT apply `aria-labelledby`
+- **AND** SHALL apply `aria-describedby` pointing to the Text's generated ID
+
+#### Scenario: No slots
+
+- **WHEN** Card.Root contains no slot-prop children
+- **THEN** SHALL NOT apply `role` attribute
+- **AND** SHALL NOT apply `aria-labelledby`
+- **AND** SHALL NOT apply `aria-describedby`
+
+#### Scenario: Manual aria-label
+
+- **WHEN** consumer passes `aria-label` directly to Card.Root
+- **THEN** SHALL forward `aria-label` to the root element
+- **AND** slot-based wiring SHALL still function if slots are also present
+
+#### Scenario: Slot matching
+
+- **WHEN** `` without `slot="title"` is inside Card.Root
+- **THEN** SHALL NOT affect the Heading (slot matching is strict)
+- **AND** SHALL NOT apply `role="article"`
+
+## REMOVED Requirements
+
+### Requirement: Rendering Optimization
+
+_Removed: The context-based registration pattern (useMemo for context value,
+useEffect for registration) is being removed entirely. There is no context to
+optimize. Standard React rendering applies._
+
+### Requirement: Main Content Area (original Content-based)
+
+_Removed: Replaced by the Body-based Main Content Area requirement above.
+Card.Content is renamed to Card.Body._
+
+### Requirement: Card Padding Options
+
+_Removed: Replaced by the Size Options requirement. `cardPadding` prop is
+renamed to `size` to align with Nimbus conventions._
+
+### Requirement: Border Styling Options
+
+_Removed: Collapsed into the Visual Variant Options requirement. `borderStyle`
+prop is replaced by `variant`._
+
+### Requirement: Shadow Options
+
+_Removed: Collapsed into the Visual Variant Options requirement. `elevation`
+prop is replaced by `variant`._
+
+### Requirement: Background Styling Options
+
+_Removed: Collapsed into the Visual Variant Options requirement.
+`backgroundStyle` prop is replaced by `variant`._
+
+### Requirement: Variant Composition
+
+_Removed: The four independent variant props are collapsed into two standard
+props (`variant` and `size`). The combinatorial explosion is replaced by curated
+presets._
diff --git a/openspec/changes/rework-card-component/tasks.md b/openspec/changes/rework-card-component/tasks.md
new file mode 100644
index 000000000..c54693aa3
--- /dev/null
+++ b/openspec/changes/rework-card-component/tasks.md
@@ -0,0 +1,235 @@
+# Tasks: Rework Card Component
+
+## - [x] Task 1: Update recipe — add footer slot, restructure to variant/size, fix display
+
+**File:** `packages/nimbus/src/components/card/card.recipe.ts`
+
+- Add `"body"` and `"footer"` to slots array, rename `"content"` to `"body"`
+- Change root `display` from `inline-flex` to `flex`
+- Add `flexDirection: "column"` to root base
+- Remove old variants (`cardPadding`, `borderStyle`, `elevation`,
+ `backgroundStyle`)
+- Add `size` variant (sm, md, lg) controlling:
+ - Gap on root: sm=100, md=200, lg=400
+ - Shared slot styles for header, body, footer:
+ - Horizontal padding (`px`) from size variant
+ - `_first`: top padding matching size
+ - `_last`: bottom padding matching size
+- Add `variant` option (outlined, elevated, filled, plain):
+ - `outlined`: border solid-25 + colorPalette.6, bg, no shadow
+ - `elevated`: no border, shadow 1, bg
+ - `filled`: no border, no shadow, colorPalette.2 bg
+ - `plain`: no border, no shadow, bg
+- Set defaults: `size: "md"`, `variant: "outlined"`
+- Update theme registration in `src/theme/slot-recipes/index.ts` if slot names
+ changed
+
+**Verify:** Recipe compiles, theme typings generate without errors
+(`pnpm --filter @commercetools/nimbus build-theme-typings`)
+
+## - [x] Task 2: Update types — rename Content to Body, add Footer, use variant/size
+
+**File:** `packages/nimbus/src/components/card/card.types.ts`
+
+- Replace `CardRecipeProps` internals:
+ - Remove `cardPadding`, `borderStyle`, `elevation`, `backgroundStyle`
+ - Add `size?: "sm" | "md" | "lg"`
+ - Add `variant?: "outlined" | "elevated" | "filled" | "plain"`
+- Rename `CardContentSlotProps` to `CardBodySlotProps`
+- Rename `CardContentProps` to `CardBodyProps`
+- Add `CardFooterSlotProps` extending `HTMLChakraProps<"div">`
+- Add `CardFooterProps` with children, ref, OmitInternalProps
+- Update all JSDoc comments
+
+**Verify:** `pnpm --filter @commercetools/nimbus typecheck`
+
+## - [x] Task 3: Update slots — rename content to body, add footer
+
+**File:** `packages/nimbus/src/components/card/card.slots.tsx`
+
+- Rename `CardContent` slot to `CardBody` (withContext "body")
+- Add `CardFooter` slot (withContext "footer")
+- Update type imports
+
+**Verify:** Types align with recipe slot names
+
+## - [x] Task 4: Rewrite card.root.tsx — remove context, direct rendering
+
+**File:** `packages/nimbus/src/components/card/components/card.root.tsx`
+
+- Remove `createContext`, `useState`, `useMemo` imports
+- Remove `CardContext` and `CardContextValue`
+- Remove `headerNode`, `contentNode` state
+- Remove `contextValue` memo
+- Remove `CardContext.Provider` wrapper
+- Remove intermediate `Stack` component
+- Render `CardRootSlot` with `children` directly (no reordering)
+- Keep recipe splitting and style prop extraction
+
+**Verify:** Component renders children in DOM order
+
+## - [x] Task 5: Rewrite card.header.tsx — simple passthrough
+
+**File:** `packages/nimbus/src/components/card/components/card.header.tsx`
+
+- Remove `useContext`, `useEffect` imports
+- Remove context registration pattern
+- Render `CardHeaderSlot` directly with children, forwarding ref and props
+- Follow Dialog.Body/Dialog.Footer pattern (simple passthrough)
+
+**Verify:** Header renders content directly, no null return
+
+## - [x] Task 6: Rename card.content.tsx to card.body.tsx
+
+**File:** `packages/nimbus/src/components/card/components/card.content.tsx` →
+`card.body.tsx`
+
+- Rename file
+- Rename component from `CardContent` to `CardBody`
+- Remove `useContext`, `useEffect` imports
+- Remove context registration pattern
+- Render `CardBodySlot` directly with children, forwarding ref and props
+- Update displayName to `"Card.Body"`
+
+**Verify:** Body renders content directly
+
+## - [x] Task 7: Create card.footer.tsx
+
+**File:** `packages/nimbus/src/components/card/components/card.footer.tsx` (new)
+
+- Create footer component following same simple pattern as Header/Body
+- Import `CardFooterSlot` from slots
+- Import `CardFooterProps` from types
+- Render slot directly with children, forwarding ref and props
+- Set displayName to `"Card.Footer"`
+
+**Verify:** Footer renders correctly
+
+## - [x] Task 8: Update barrel exports
+
+**Files:**
+
+- `components/index.ts` — export CardBody (not CardContent), add CardFooter
+- `card.tsx` — update namespace: rename Content to Body, add Footer with JSDoc
+- `card/index.ts` — verify re-exports
+
+**Verify:** `import { Card } from "@commercetools/nimbus"` provides Root,
+Header, Body, Footer
+
+## - [x] Task 9: Update stories
+
+**File:** `packages/nimbus/src/components/card/card.stories.tsx`
+
+- Replace all `Card.Content` with `Card.Body`
+- Replace `cardPadding`, `borderStyle`, `elevation`, `backgroundStyle` with
+ `variant` and `size`
+- Add stories demonstrating Card.Footer
+- Add story for Header + Body + Footer combination
+- Add story for Body-only (verify padding)
+- Add story for Header + Body without Footer
+- Showcase all variant values (outlined, elevated, filled, plain)
+- Showcase all size values (sm, md, lg)
+- Add play functions verifying rendering and padding behavior
+
+**Verify:** `pnpm test:dev packages/nimbus/src/components/card/card.stories.tsx`
+
+## - [x] Task 10: Update docs spec
+
+**File:** `packages/nimbus/src/components/card/card.docs.spec.tsx`
+
+- Replace all `Card.Content` with `Card.Body`
+- Replace old variant props with `variant` and `size`
+- Add test case for Card with Footer
+- Add test case for Body-only card
+- Verify all combinations render expected content
+
+**Verify:**
+`pnpm test:dev packages/nimbus/src/components/card/card.docs.spec.tsx`
+
+## - [x] Task 11: Update MDX documentation
+
+**Files:** `card.mdx`, `card.dev.mdx`, `card.guidelines.mdx`, `card.a11y.mdx`
+
+- Replace all `Card.Content` references with `Card.Body`
+- Replace old variant prop references with `variant` and `size`
+- Document Card.Footer in API reference
+- Update examples to show Header + Body + Footer pattern
+- Document padding behavior (CSS-driven, adapts to present parts)
+- Document variant options with visual examples
+
+## - [x] Task 12: Build and full test
+
+- `pnpm --filter @commercetools/nimbus build`
+- `pnpm test:dev` (storybook + unit tests against source)
+- `pnpm --filter @commercetools/nimbus typecheck`
+
+## - [x] Task 13: Add @react-aria/utils dependency
+
+**Files:**
+
+- `pnpm-workspace.yaml` — add `"@react-aria/utils": 3.33.1` to `react:` catalog
+- `packages/nimbus/package.json` — add `"@react-aria/utils": "catalog:react"` to
+ dependencies
+
+Run `pnpm install`. Verify no version conflicts.
+
+**Verify:** `pnpm --filter @commercetools/nimbus typecheck`
+
+## - [x] Task 14: Implement slot-based ARIA wiring in Card.Root
+
+**File:** `packages/nimbus/src/components/card/components/card.root.tsx`
+
+- Import `useSlotId` from `@react-aria/utils`
+- Import `Provider`, `HeadingContext`, `TextContext` from
+ `react-aria-components`
+- Call `useSlotId()` twice: once for titleId, once for descriptionId
+- Derive conditional ARIA props (`role="article"`, `aria-labelledby`,
+ `aria-describedby`) when at least one slot is detected
+- Wrap children in
+ ``
+- Pass ARIA props to CardRootSlot
+
+**Verify:** `pnpm --filter @commercetools/nimbus typecheck`
+
+## - [x] Task 15: Add slot-based accessibility stories
+
+**File:** `packages/nimbus/src/components/card/card.stories.tsx`
+
+- Add `Heading` to imports from `@commercetools/nimbus`
+- Add story: SlotBasedAccessibility (both title + description slots)
+- Add story: WithoutSlots (regression test: no role on plain cards)
+- Add story: TitleSlotOnly (partial slot usage)
+- All stories must have play functions with ARIA attribute assertions
+
+**Verify:**
+`pnpm test:dev packages/nimbus/src/components/card/card.stories.tsx`
+
+## - [x] Task 16: Update docs spec with slot examples
+
+**File:** `packages/nimbus/src/components/card/card.docs.spec.tsx`
+
+- Add `Heading`, `Text` to imports
+- Add "Slot-based accessibility" describe block
+- Test automatic `aria-labelledby` wiring with `Heading slot="title"`
+- Test `aria-describedby` wiring with `Text slot="description"`
+- Test no-role behavior without slots
+
+**Verify:**
+`pnpm test:dev packages/nimbus/src/components/card/card.docs.spec.tsx`
+
+## - [x] Task 17: Update MDX documentation
+
+**Files:**
+
+- `card.a11y.mdx` — rewrite with slot-based ARIA guidance, behavior table, code
+ examples
+- `card.dev.mdx` — add "Accessible cards" section with live example, update
+ accessibility notes
+
+## - [x] Task 18: Build and full test
+
+- `pnpm --filter @commercetools/nimbus build`
+- `pnpm test:dev` (storybook + unit tests against source)
+- `pnpm --filter @commercetools/nimbus typecheck`
+- Verify no regressions in Heading, Text, or Combobox
+- Verify no regressions in other components
diff --git a/packages/nimbus/package.json b/packages/nimbus/package.json
index a68d431fb..5065d7020 100644
--- a/packages/nimbus/package.json
+++ b/packages/nimbus/package.json
@@ -47,6 +47,7 @@
"@github-ui/storybook-addon-performance-panel": "catalog:tooling",
"@internationalized/string": "catalog:react",
"@react-aria/interactions": "catalog:react",
+ "@react-aria/utils": "catalog:react",
"dequal": "catalog:utils",
"escape-html": "^1.0.3",
"is-hotkey": "^0.2.0",
diff --git a/packages/nimbus/src/components/card/card.a11y.mdx b/packages/nimbus/src/components/card/card.a11y.mdx
index fdfaac0c4..58e27ad86 100644
--- a/packages/nimbus/src/components/card/card.a11y.mdx
+++ b/packages/nimbus/src/components/card/card.a11y.mdx
@@ -9,10 +9,54 @@ Accessibility ensures that digital content and functionality are usable by
everyone, including people with disabilities, by addressing visual, auditory,
cognitive, and physical limitations.
+### Automatic ARIA wiring
+
+Card supports automatic ARIA wiring via React Aria's slot system. When you place
+a `` or `` inside a Card, the
+root element automatically gains `role="article"` along with `aria-labelledby`
+and/or `aria-describedby` pointing to those elements.
+
+```jsx live
+const App = () => (
+
+
+ Sprint Summary
+
+
+
+ The team completed 85% of planned story points this sprint.
+
+
+
+
+
+
+)
+```
+
+### Slot behavior
+
+| Slots present | `role` | `aria-labelledby` | `aria-describedby` |
+|---|---|---|---|
+| `Heading slot="title"` + `Text slot="description"` | `article` | points to Heading | points to Text |
+| `Heading slot="title"` only | `article` | points to Heading | — |
+| `Text slot="description"` only | `article` | — | points to Text |
+| No slots | — | — | — |
+
+Cards without slot-prop children remain plain `
` elements with no ARIA role
+or attributes. A `` or `` without a `slot` prop is not affected.
+
+### Manual labeling
+
+If slot-based labeling is not suitable, pass `aria-label` directly to
+`Card.Root`:
+
```jsx live
const App = () => (
-
-
+
+
+ Content with manual aria-label.
+
)
```
@@ -33,8 +77,6 @@ const App = () => (
adequate.
- **Meaningful sequence:** Ensure the content within the card is presented in a
logical order.
-- **Labels or instructions:** Provide clear labels or instructions for form
- elements within cards.
- **Semantic HTML:** Use semantic HTML to ensure compatibility with assistive
technologies.
- **Avoid complex interactive cards:** Avoid making the entire card a single
diff --git a/packages/nimbus/src/components/card/card.dev.mdx b/packages/nimbus/src/components/card/card.dev.mdx
index 1199d76dc..de8ee7691 100644
--- a/packages/nimbus/src/components/card/card.dev.mdx
+++ b/packages/nimbus/src/components/card/card.dev.mdx
@@ -14,15 +14,15 @@ import { Box, Card, Link, type CardProps } from '@commercetools/nimbus';
### Basic usage
-The Card component uses a compound pattern to structure content. You must wrap `Card.Header` and `Card.Content` within `Card.Root`.
+The Card component uses a compound pattern to structure content. Wrap `Card.Header`, `Card.Body`, and `Card.Footer` within `Card.Root`.
```jsx live-dev
const App = () => (
Card Title
-
+ This is the main card content.
-
+
)
```
@@ -31,24 +31,24 @@ const App = () => (
### Size options
-The `cardPadding` prop controls the internal spacing of the card. Available sizes are `sm`, `md`, and `lg`.
+The `size` prop controls the internal padding and gap of the card. Available sizes are `sm`, `md`, and `lg`.
```jsx live-dev
const App = () => (
-
- Small Padding
- Compact content spacing.
+
+ Small
+ Compact content spacing.
-
- Medium Padding
- Standard content spacing (default).
+
+ Medium
+ Standard content spacing (default).
-
- Large Padding
- Spacious content layout.
+
+ Large
+ Spacious content layout.
)
@@ -56,29 +56,74 @@ const App = () => (
### Visual variants
-Customize the card's appearance using `borderStyle`, `elevation`, and `backgroundStyle`.
+The `variant` prop controls the card's visual treatment.
```jsx live-dev
const App = () => (
-
+ Outlined (Default)
- Standard bordered look.
+ Standard bordered look.
-
+ Elevated
- Shadow depth without border.
+ Shadow depth without border.
-
- Muted Background
- Subtle background color for differentiation.
+
+ Filled
+ Muted background for differentiation.
+
+
+
+ Plain
+ Minimal visual treatment.
)
```
+### Card with footer
+
+Use `Card.Footer` for actions and metadata placed below the body content.
+
+```jsx live-dev
+const App = () => (
+
+ Project Summary
+
+ The project is on track for the next milestone.
+
+
+
+
+
+)
+```
+
+### Accessible cards
+
+Use `` and `` to automatically wire `aria-labelledby` and `aria-describedby` on the card. The card gains `role="article"` only when these slots are used.
+
+```jsx live-dev
+const App = () => (
+
+
+ Sprint Summary
+
+
+
+ The team completed 85% of planned story points this sprint.
+
+
+
+
+
+
+)
+```
+
### Interactive cards
When making a card interactive, avoid placing `onClick` directly on the `Card.Root`. Instead, wrap the card in an anchor tag or use proper ARIA roles to ensure accessibility.
@@ -95,11 +140,11 @@ const App = () => (
_hover={{ textDecoration: 'none' }}
aria-label="Navigate to details"
>
-
+ Navigational Card
-
+
This entire card acts as a link using the system Link component.
-
+
@@ -117,11 +162,11 @@ const App = () => (
cursor="pointer"
aria-label="Trigger action"
>
-
+ Action Card
-
+
Behaves like a button with keyboard support (Tab, Enter, Space).
-
+
@@ -132,10 +177,12 @@ const App = () => (
## Accessibility
-The `Card` component handles most accessibility requirements internally, rendering semantic `div` elements by default. It acts as a generic container.
+The `Card` component supports automatic ARIA wiring via React Aria's slot system.
-- **Headings**: Ensure the content within `Card.Header` follows your page's heading hierarchy (e.g., use `Heading` component or `h3`, `h4` tags).
-- **Interactive Cards**: If a card is interactive (clickable), do not attach `onClick` directly to the card div if possible. Instead, wrap the card in a link or button, or ensure proper ARIA roles (`role="button"`) and keyboard handling (`tabIndex={0}`, Enter/Space key listeners) are implemented.
+- **Automatic ARIA wiring**: Place `` inside the card for automatic `aria-labelledby`. Place `` for `aria-describedby`. The card gains `role="article"` only when these slots are used. Without slots, the card remains a plain `
`.
+- **Manual labeling**: Pass `aria-label` directly to `Card.Root` when slot-based labeling is not suitable.
+- **Headings**: Ensure the content within `Card.Header` follows your page's heading hierarchy (e.g., use `Heading` component with appropriate `as` prop).
+- **Interactive Cards**: If a card is interactive (clickable), do not attach `onClick` directly to the card div. Instead, wrap the card in a link or button, or ensure proper ARIA roles (`role="button"`) and keyboard handling (`tabIndex={0}`, Enter/Space key listeners) are implemented.
If your use case requires tracking and analytics for this component, it is good practice to add a **persistent**, **unique** id to the component:
@@ -145,7 +192,7 @@ const PERSISTENT_ID = "project-summary-card";
export const Example = () => (
Summary
- ...
+ ...
);
```
@@ -167,4 +214,3 @@ These examples demonstrate how to test your implementation when using Card withi
## Resources
- [Storybook](https://nimbus-storybook.vercel.app/?path=/docs/components-card--docs)
-
diff --git a/packages/nimbus/src/components/card/card.docs.spec.tsx b/packages/nimbus/src/components/card/card.docs.spec.tsx
index e57bce9ef..793fdc037 100644
--- a/packages/nimbus/src/components/card/card.docs.spec.tsx
+++ b/packages/nimbus/src/components/card/card.docs.spec.tsx
@@ -1,6 +1,6 @@
import { describe, it, expect } from "vitest";
import { render, screen } from "@testing-library/react";
-import { Card, NimbusProvider } from "@commercetools/nimbus";
+import { Card, Heading, Text, NimbusProvider } from "@commercetools/nimbus";
/**
* @docs-section basic-rendering
@@ -14,7 +14,7 @@ describe("Card - Basic rendering", () => {
Project X
- Status: Active
+ Status: Active
);
@@ -35,15 +35,90 @@ describe("Card - Basic rendering", () => {
expect(screen.getByText("Card Title")).toBeInTheDocument();
});
- it("renders with content only", () => {
+ it("renders with body only", () => {
render(
- This is the main content.
+ This is the main content.
);
expect(screen.getByText("This is the main content.")).toBeInTheDocument();
});
+
+ it("renders with footer", () => {
+ render(
+
+
+ Title
+ Content
+ Footer actions
+
+
+ );
+
+ expect(screen.getByText("Title")).toBeInTheDocument();
+ expect(screen.getByText("Content")).toBeInTheDocument();
+ expect(screen.getByText("Footer actions")).toBeInTheDocument();
+ });
+
+ it("renders with variant and size props", () => {
+ render(
+
+
+ Elevated card
+
+
+ );
+
+ expect(screen.getByText("Elevated card")).toBeInTheDocument();
+ });
+});
+
+/**
+ * @docs-section slot-based-accessibility
+ * @docs-title Slot-Based Accessibility
+ * @docs-description Using Heading and Text slots for automatic ARIA wiring
+ * @docs-order 2
+ */
+describe("Card - Slot-based accessibility", () => {
+ it("renders accessible card with title and description slots", () => {
+ render(
+
+
+
+
+ Product Details
+
+
+
+
+ Overview of the product's key features.
+
+
+
+
+ );
+
+ expect(screen.getByText("Product Details")).toBeInTheDocument();
+ expect(screen.getByText(/key features/)).toBeInTheDocument();
+ });
+
+ it("has no role or ARIA attributes without slots", () => {
+ render(
+
+
+ Title
+ Content
+
+
+ );
+
+ const card = screen.getByTestId("card-plain");
+
+ expect(card).not.toHaveAttribute("role");
+ expect(card).not.toHaveAttribute("aria-labelledby");
+ expect(card).not.toHaveAttribute("aria-describedby");
+ });
});
diff --git a/packages/nimbus/src/components/card/card.figma.tsx b/packages/nimbus/src/components/card/card.figma.tsx
index cf5c1971c..6ee479a96 100644
--- a/packages/nimbus/src/components/card/card.figma.tsx
+++ b/packages/nimbus/src/components/card/card.figma.tsx
@@ -1,10 +1,10 @@
import figma from "@figma/code-connect/react";
import { Card } from "./card";
-// --- Card content → Card.Content ---
+// --- Card content → Card.Body ---
// NOTE: Skipped INSTANCE_SWAP "image" → no matching code prop "image"
figma.connect(
- Card.Content,
+ Card.Body,
"https://www.figma.com/design/AvtPX6g7OGGCRvNlatGOIY/NIMBUS-design-system?node-id=266-482",
{
props: {
@@ -20,10 +20,10 @@ figma.connect(
<>
{props.leadingElement}
{props.header}
-
+
{props.instance}
{props.children}
-
+
>
),
}
@@ -36,13 +36,13 @@ figma.connect(
{
props: {
children: figma.children("*"),
- borderStyle: figma.enum("Outlined", { Yes: "outlined", No: "none" }),
- elevation: figma.enum("Elevated", { Yes: "elevated", No: "none" }),
+ variant: figma.enum("Elevated", {
+ Yes: "elevated" as const,
+ No: "outlined" as const,
+ }),
},
example: (props) => (
-
- {props.children}
-
+ {props.children}
),
}
);
diff --git a/packages/nimbus/src/components/card/card.guidelines.mdx b/packages/nimbus/src/components/card/card.guidelines.mdx
index 333046b3c..00ab47b4d 100644
--- a/packages/nimbus/src/components/card/card.guidelines.mdx
+++ b/packages/nimbus/src/components/card/card.guidelines.mdx
@@ -92,8 +92,8 @@ clear headings and short descriptions.
```jsx live
const App = () => (
-
-
+
+
@@ -112,7 +112,7 @@ const App = () => (
-
+
);
```
@@ -127,8 +127,8 @@ const App = () => (
```jsx live
const App = () => (
-
-
+
+
@@ -166,7 +166,7 @@ const App = () => (
-
+
);
```
@@ -189,8 +189,8 @@ interaction.
```jsx live
const App = () => (
-
-
+
+ (
-
+
);
```
@@ -228,8 +228,8 @@ const App = () => (
const App = () => (
{/* Main Card */}
-
-
+
+ (
-
+
{/* Bottom Row Cards */}
{/* Products Card */}
{/* Half width approx */}
-
+ (
Products
-
+
{/* Discounts Card */}
{/* Half width approx */}
-
+ (
Discounts
-
+
diff --git a/packages/nimbus/src/components/card/card.recipe.ts b/packages/nimbus/src/components/card/card.recipe.ts
index 09322df94..12eeb4298 100644
--- a/packages/nimbus/src/components/card/card.recipe.ts
+++ b/packages/nimbus/src/components/card/card.recipe.ts
@@ -5,74 +5,83 @@ import { defineSlotRecipe } from "@chakra-ui/react/styled-system";
* Defines the styling variants and base styles using Chakra UI's recipe system.
*/
export const cardRecipe = defineSlotRecipe({
- slots: ["root", "header", "content"],
+ slots: ["root", "header", "body", "footer"],
className: "nimbus-card",
base: {
root: {
colorPalette: "slate",
- display: "inline-flex",
+ display: "flex",
+ flexDirection: "column",
alignItems: "flex-start",
borderRadius: "300",
focusVisibleRing: "outside",
},
+ header: {
+ p: "var(--card-spacing)",
+ width: "100%",
+ },
+ body: {
+ p: "var(--card-spacing)",
+ width: "100%",
+ // When directly preceded by header, collapse top padding (header's
+ // bottom padding provides the gap). When a non-slot element like
+ // Separator sits between them, both slots keep full padding for
+ // visually balanced spacing around the element.
+ ".nimbus-card__header + &": { pt: 0 },
+ },
+ footer: {
+ p: "var(--card-spacing)",
+ width: "100%",
+ // Same adjacent-sibling collapsing as body — see comment above.
+ ".nimbus-card__header + &": { pt: 0 },
+ ".nimbus-card__body + &": { pt: 0 },
+ },
},
variants: {
- cardPadding: {
+ size: {
sm: {
- root: {
- padding: "200",
- },
+ root: { "--card-spacing": "spacing.300" },
},
md: {
- root: {
- padding: "400",
- },
+ root: { "--card-spacing": "spacing.400" },
},
lg: {
- root: {
- padding: "600",
- },
+ root: { "--card-spacing": "spacing.600" },
},
},
- borderStyle: {
- none: {},
+ variant: {
outlined: {
root: {
border: "solid-25",
- borderColor: "colorPalette.3",
+ borderColor: "colorPalette.6",
+ backgroundColor: "bg",
},
},
- },
- elevation: {
- none: {},
elevated: {
root: {
shadow: "1",
+ backgroundColor: "bg",
},
},
- },
- backgroundStyle: {
- default: {
+ filled: {
root: {
- backgroundColor: "bg",
+ backgroundColor: "colorPalette.2",
},
},
- muted: {
+ plain: {
root: {
- backgroundColor: "colorPalette.2",
+ backgroundColor: "bg",
},
},
},
},
defaultVariants: {
- cardPadding: "md",
- borderStyle: "outlined",
- elevation: "none",
- backgroundStyle: "default",
+ size: "md",
+ variant: "outlined",
},
});
diff --git a/packages/nimbus/src/components/card/card.slots.tsx b/packages/nimbus/src/components/card/card.slots.tsx
index 80667f6a2..72e8cd83b 100644
--- a/packages/nimbus/src/components/card/card.slots.tsx
+++ b/packages/nimbus/src/components/card/card.slots.tsx
@@ -1,7 +1,8 @@
import { createSlotRecipeContext } from "@chakra-ui/react/styled-system";
import type { SlotComponent } from "../../type-utils/slot-types";
import type {
- CardContentSlotProps,
+ CardBodySlotProps,
+ CardFooterSlotProps,
CardHeaderSlotProps,
CardRootSlotProps,
} from "./card.types";
@@ -20,5 +21,8 @@ export const CardRoot: SlotComponent =
export const CardHeader: SlotComponent =
withContext("div", "header");
-export const CardContent: SlotComponent =
- withContext("div", "content");
+export const CardBody: SlotComponent =
+ withContext("div", "body");
+
+export const CardFooter: SlotComponent =
+ withContext("div", "footer");
diff --git a/packages/nimbus/src/components/card/card.stories.tsx b/packages/nimbus/src/components/card/card.stories.tsx
index eea7b2df6..72781506f 100644
--- a/packages/nimbus/src/components/card/card.stories.tsx
+++ b/packages/nimbus/src/components/card/card.stories.tsx
@@ -1,11 +1,22 @@
import type { Meta, StoryObj } from "@storybook/react-vite";
-import { Card, type CardProps, Stack } from "@commercetools/nimbus";
+import {
+ Card,
+ type CardProps,
+ Stack,
+ Text,
+ Heading,
+ Button,
+ Separator,
+} from "@commercetools/nimbus";
import { within, expect } from "storybook/test";
-const cardPaddings: CardProps["cardPadding"][] = ["sm", "md", "lg"];
-const elevations: CardProps["elevation"][] = ["none", "elevated"];
-const borderStyles: CardProps["borderStyle"][] = ["none", "outlined"];
-const backgroundStyles: CardProps["backgroundStyle"][] = ["default", "muted"];
+const sizes: CardProps["size"][] = ["sm", "md", "lg"];
+const variants: CardProps["variant"][] = [
+ "outlined",
+ "elevated",
+ "filled",
+ "plain",
+];
const meta: Meta = {
title: "Components/Card",
@@ -28,10 +39,8 @@ export const Base: Story = {
args: {
children: "Demo Card",
"data-testid": "test-card",
- cardPadding: "md",
- backgroundStyle: "default",
- borderStyle: "none",
- elevation: "none",
+ variant: "outlined",
+ size: "md",
},
play: async ({ canvasElement, args, step }) => {
const canvas = within(canvasElement);
@@ -48,121 +57,361 @@ export const Base: Story = {
};
/**
- * Card padding
+ * Sizes
+ * Demonstrates sm, md, lg padding and gap scaling
*/
-export const CardPaddings: Story = {
- render: (args) => {
+export const Sizes: Story = {
+ render: () => {
return (
-
- {cardPaddings.map((cardPadding) => {
- return (
-
-
-
- Padding size: {cardPadding as string}
-
-
-
- Lorem ipsum dolor sit amet, consectetur adipiscing elit.
-