---
title: 'Card'
description: 'Use Card to group related content in a clear, separated area.'
version: 0.0.0-development
generatedAt: 2026-10-22T16:06:36.964Z
checksum: 73b5cf2a4a48d05c2a6aa6657e18e0dcdee50de818989494131d5c38023f38e0
---

# Card

## Import

```tsx
import { Card } from '@dnb/eufemia'
```

The controls and media overlay are available as `Card.Info.CloseButton`, `Card.Info.MediaButton`, and `Card.Info.Overlay`. No additional imports are needed.

## Description

`Card` is a block section element showing the white box with rounded gray borders, adding spacing automatically.

It uses [Flex.Item](/uilib/layout/flex/item) under the hood. When one of these properties were given, `stack`, `direction` or `spacing` – the [Flex.Container](/uilib/layout/flex/container) will be used.

## Relevant links

- [Figma](https://www.figma.com/design/cdtwQD8IJ7pTeE45U148r1/?node-id=70914-1831)
- [Source code](https://github.com/dnbexperience/eufemia/tree/main/packages/dnb-eufemia/src/components/card)
- [Docs code](https://github.com/dnbexperience/eufemia/tree/main/packages/dnb-design-system-portal/src/docs/uilib/components/card)

```jsx
import { Card } from '@dnb/eufemia'
import { Form, Field } from '@dnb/eufemia/extensions/forms'

render(
  <Form.Handler data={existingData} onSubmit={submitHandler}>
    <Card>
      <Field.Email path="/dataPath" />
      <Form.ButtonRow>
        <Form.SubmitButton />
      </Form.ButtonRow>
    </Card>
  </Form.Handler>
)
```

## Accessibility

It uses a `section` element. Which allows you to add an `aria-label` or `aria-labelledby` to provide screen readers with landmarks.


```tsx
render(<Card aria-labelledby="unique-id">
        <Form.SubHeading id="unique-id" space={0}>
          Heading
        </Form.SubHeading>
        <P>Content inside a landmark ...</P>
      </Card>)
```


## Card.List and Card.ListItem

Use `Card.List` and `Card.ListItem` to render a semantic `<ul>` / `<li>` list of cards. `Card.List` provides a responsive flex layout with wrapping. `Card.ListItem` supports a `center` prop to center content vertically — set it to `true` to always center, or `"when-small"` to center only on small screens.

## Security

Like the [Anchor](/uilib/components/anchor), `Card.Action` removes `href` and `to` values that use a script-executing protocol (`javascript:` or `vbscript:`), including obfuscated variants such as `java\tscript:`. This prevents cross-site scripting (XSS) when untrusted input is passed to these properties; the card then renders without a link target. Other protocols, such as `data:` and `blob:`, are left untouched.

## Card.Info

Use `Card.Info` for information and tips. It is a complete Card with helpful defaults, so use it directly without an enclosing Card. Compose its children with headings, paragraphs, images, icons, Flex, and Buttons as needed.

```tsx
<Card.Info>
  <Flex.Stack gap="x-small">
    <H2 size="small">Build your savings</H2>
    <P size="small">A little every month goes a long way.</P>
  </Flex.Stack>
  <Button variant="secondary" onClick={startSaving}>
    Start saving
  </Button>
</Card.Info>
```

### Layout and surface

Card.Info defaults to `dropShadow={true}`, `responsive={false}`, `stack={true}`, and `gap="small"`. Its padding is `small` on small screens and `medium` on larger screens. It keeps rounded corners on small screens. Override any default using the regular [Card properties](/uilib/components/card/properties).

Group a title and its text in `Flex.Stack gap="x-small"` for an 8px gap. Card.Info keeps its default 16px gap between that group and separate actions.

Width follows the containing layout. Constrain that layout when the card should be narrower. Use `innerSpace` to change padding, `dropShadow={false}` to remove the shadow, or `responsive` to use Card's edge-to-edge layout on small screens.

Content is supplied through children. Use `Flex.Horizontal` for a graphic beside text, or `Flex.Stack align="center"` with centered text for a centered layout. Use `Provider skeleton` for loading content. Card.Info has no separate graphic, text, icon, or button props. The optional `title` prop retains Card's title behavior; use a heading child when the page structure needs a semantic heading.

### Images and video

Compose media layouts with Card.Info and ordinary image or video elements. Its defaults provide shadow, rounded corners, and a stretched inner layout. Set `innerSpace={0}` for media that reaches the edges. Add padding around the content and clip media in an inner container so Card.Action's focus outline is preserved. Use Flex wrapping to stack media and content when the card itself becomes narrow.

Set `href` on Card.Info for a whole-card link with no nested controls. Omit whole-card actions when it contains independent links, a close button, or video controls.

Give meaningful images descriptive alternative text. Use `alt=""` for decorative artwork that adds no information beyond the text. Set image dimensions or an aspect ratio to reserve space before loading, and choose cropping that preserves important content.

For decorative video, keep all information in the text, mute the video, and hide it from assistive technology. Provide a keyboard-accessible play/pause control and a poster matching the first video frame. Respect `prefers-reduced-motion`: disable autoplay and pause playback when the preference is enabled, while allowing playback on request. Keep text readable against every frame with a sufficiently dark overlay. For video that conveys information, use native `controls`, captions, and an accessible transcript instead of hiding it or using it as a background.

See the [image](/uilib/components/card/demos/#image-beside-content), [centered image](/uilib/components/card/demos/#centered-image-card), and [video compositions](/uilib/components/card/demos/#full-media-with-video).

### Interactive information cards

Set `href` or `to` for a whole-card link, or `onClick` for a whole-card action. Card.Info uses [Card.Action](/uilib/components/card/demos/#interactive-cards) for hover, focus, and keyboard behavior. Pass a router Link through `element` when using `to`.

```tsx
<Card.Info href="/savings">
  <Flex.Stack gap="x-small">
    <H2 size="small">Start saving</H2>
    <P size="small">Read about our savings accounts.</P>
  </Flex.Stack>
</Card.Info>
```

Do not place buttons, links, inputs, or a close button inside a whole-card action. When the content needs independent controls, omit `href`, `to`, and `onClick` on Card.Info and render those controls as children. Provide `aria-label` or `aria-labelledby` when a noninteractive card needs a named landmark.

### Close button

Compose Card.Info.CloseButton as a direct child of a noninteractive Card or Card.Info. The control provides a translated label, a circular background, and top-right positioning. Padded cards reserve space beside the content automatically. Cards with `innerSpace={0}` or `innerSpace={false}` keep media at full width and place the close control over it; add padding to text inside these cards to avoid overlap. Use its `onClick` to dismiss the card, and move focus to a suitable remaining control when removing focused content. Use HeightAnimation around Card.Info when dismissal should animate.

```tsx
import { Card, P } from '@dnb/eufemia'

function SavingsTip({ onDismiss }) {
  return (
    <Card.Info>
      <Card.Info.CloseButton onClick={onDismiss} />
      <P>Save a little every month.</P>
    </Card.Info>
  )
}
```

Do not nest this control inside a whole-card link or action. See [Card.Info.CloseButton properties](/uilib/components/card/properties/#cardinfoclosebutton-properties).

The close control uses a 24px circular button, and the media control uses a 32px circle. Both have a 16px icon and an extended click/tap area of at least 44px. Use the Button `size` and `bounding` properties to customize these defaults.

See the [information card demos](/uilib/components/card/demos/#info-card-layout).

## Optional media control

Use Card.Info.MediaButton and pass a stable ref attached to your video. Mount the video and control together. The control tracks native playback events, provides translated play/pause labels, and animates the standard icons with reduced-motion support. It resumes playback from the current position. If a render replaces the video element, the control removes the old listeners, tracks the new element, and reapplies playback preferences after the commit. Use `playTitle` and `pauseTitle` to customize the labels.

```tsx
import { useRef } from 'react'
import { Card, Flex } from '@dnb/eufemia'

function DecorativeVideo() {
  const videoRef = useRef<HTMLVideoElement>(null)

  return (
    <Flex.Stack style={{ position: 'relative' }}>
      <video
        ref={videoRef}
        src="/video.mp4"
        poster="/poster.webp"
        muted
        loop
        playsInline
        aria-hidden="true"
        preload="none"
      />
      <Card.Info.MediaButton videoRef={videoRef} />
    </Flex.Stack>
  )
}
```

The control defaults to autoplay for muted video when the user does not prefer reduced motion. Enabling reduced motion pauses playback; manual playback is still available. Set `autoPlay={false}` to require a request before playback starts. If the browser blocks autoplay, the play button remains available. Keep the video's native `autoPlay` attribute off so it does not bypass this preference handling.

You own the video sources, poster, loading strategy, and error fallback. Use native controls and captions for informative video. See the [video example](/uilib/components/card/demos/#full-media-with-video) and [Card.Info.MediaButton properties](/uilib/components/card/properties/#cardinfomediabutton-properties).

## Media content overlay

Use `Card.Info.Overlay` for content over an image or decorative video. It provides responsive padding, a transparent-to-dark gradient, progressive backdrop blur, and dark-surface context for controls. Headings and paragraphs receive text shadows automatically, including inside nested layout components. Headings use a broader shadow than body text. Override `textShadow` on individual text elements when needed. The effects are decorative and do not hide the content from assistive technology or intercept pointer events.

```tsx
<Card.Info.Overlay align="center" style={{ textAlign: 'center' }}>
  <H2>Spar til noe uventet</H2>
  <P>Du vet aldri når du trenger litt ekstra penger.</P>
  <Button href="/sparing">Start sparing</Button>
</Card.Info.Overlay>
```

Place the overlay inside the media container after the image or video. Control its vertical placement through spacing or inline styles, as in the [video demo](/uilib/components/card/demos/#full-media-with-video). Set `blur={false}` to keep only the gradient, or `gradient={false}` to keep only the blur. Check text contrast against every media frame, especially when disabling either effect. See [Overlay properties](/uilib/components/card/properties/#cardinfooverlay-properties).

### Bundles

Card.Info includes CloseButton, MediaButton, and Overlay as compound components. Importing Card retains these helpers and their playback and icon dependencies in a typical production bundle. Applications that do not import Card can still omit the entire component through ESM tree shaking.


## Related components

Card is part of the [Content](/uilib/components/overview/#content) category. Other components for similar needs:

- [Accordion](/uilib/components/accordion/) – to let people open and close sections of related content.
- [Avatar](/uilib/components/avatar/) – to make a person, company, or profile easier to recognize.
- [CountryFlag](/uilib/components/country-flag/) – to show a country by its flag from an ISO country code.
- [DateFormat](/uilib/components/date-format/) – to show dates in the correct DNB format.
- [Heading](/uilib/components/heading/) – to create accessible page headings with the correct level.
- [Icon](/uilib/components/icon/) – to render and size an icon in line with Eufemia styling.

[See all in Content](/uilib/components/overview/#content)


## Demos

### Default border


```tsx
render(<Card>
        <P>
          Lorem ipsum dolor sit amet, consectetur adipiscing elit. Morbi
          cursus pharetra elit in bibendum.
        </P>
      </Card>)
```


### Stack

When `stack` is set to `true`, the Card will add a gap between its children and stretch them to the full.

For [form components](/uilib/extensions/forms/), you should use [Form.Card](/uilib/extensions/forms/Form/Card/) instead of the original Card component.

When `stack` is set to `true`, the Card will add a gap between its children and stretch them to the full.


```tsx
render(<Card stack>
        <P>Stacked content</P>
        <P>Stacked content</P>
      </Card>)
```


### With form fields

When using Eufemia Forms, you may want to use [Form.Card](/uilib/extensions/forms/Form/Card/) instead of the original Card component.


```tsx
render(<Form.Card>
        <Field.String label="Label" value="Value" />
        <Field.String label="Label" value="Value" />
      </Form.Card>)
```


### Nested Cards

Nested cards have `responsive={false}` by default and will not behave responsive.


```tsx
render(<Card stack>
        <P>First Card</P>
        <Card stack>
          <P>Second Card</P>
          <Card stack>
            <P>Third Card (edge case)</P>
          </Card>
        </Card>
      </Card>)
```


### With drop shadow


```tsx
render(<Card stack dropShadow>
        <Lead>Card 1</Lead>
        <Card stack top="2.5rem">
          <Lead size="basis">Card 2</Lead>
          <Card stack top="1.5rem" innerSpace={{
      top: true,
      block: '3rem',
      inline: true
    }}>
            <Lead size="small">Card 3 (edge case)</Lead>
          </Card>
        </Card>
      </Card>)
```


## With `outset`

When using `outset`, the Card will break out of the layout container.
On small screens (mobile) the outset is removed.


```tsx
render(<Flex.Vertical>
        <Form.MainHeading>I'm left aligned</Form.MainHeading>
        <Card stack outset>
          <P>Card content</P>
          <Card>
            <P>Nested card</P>
          </Card>
        </Card>
        <Form.SubmitButton text="I'm also left aligned" />
      </Flex.Vertical>)
```


### Without padding


```tsx
render(<Card innerSpace={false} align="stretch">
        <P>no inner space</P>
      </Card>)
```


### With nested Section

Card components need to have `stack={true}` or `align="stretch"` in order to stretch its children components.


```tsx
render(<Flex.Stack>
        <Card gap="x-small" align="stretch">
          <Form.SubHeading>Card with a nested Section</Form.SubHeading>
          <Section variant="information" innerSpace={{
      top: 'small',
      bottom: 'medium'
    }}>
            <Field.String width="medium" label="In nested Section" />
          </Section>
        </Card>

        <Card innerSpace="x-large" stack>
          <Section variant="information" innerSpace={{
      top: 'small',
      bottom: 'medium'
    }}>
            <Field.String width="medium" label="Card with a 'x-large' inner space" />
          </Section>
        </Card>

        <Card innerSpace={false} align="stretch">
          <P>no inner space</P>
          <Section innerSpace backgroundColor="var(--token-color-background-neutral-alternative)">
            <Field.String width="medium" label="Card with no inner space" />
          </Section>
          <P>no inner space</P>
        </Card>
      </Flex.Stack>)
```


### With Table


```tsx
const MyTable = () => <Table.ScrollView>
            <Table border outline size="medium">
              <thead>
                <Tr noWrap>
                  <Th>Column 1</Th>
                  <Th>Column 2</Th>
                  <Th>Column 3</Th>
                </Tr>
              </thead>
              <tbody>
                <Tr>
                  <Td>Row 1</Td>
                  <Td>Row 1</Td>
                  <Td>Row 1</Td>
                </Tr>
                <Tr>
                  <Td colSpan={3} align="right">
                    <Button>Button</Button>
                  </Td>
                </Tr>
                <Tr>
                  <Td>Row 3</Td>
                  <Td>Row 3</Td>
                  <Td>Row 3</Td>
                </Tr>
              </tbody>
            </Table>
          </Table.ScrollView>;
render(<Card title="Card title" responsive={false} innerSpace={0} filled>
            <MyTable />
          </Card>);
```


### With Grid

Grid wraps the Cards nicely on smaller screens.


```tsx
render(<Grid.Container columns={{
  small: 1,
  medium: 3,
  large: 3
}} columnGap="small">
        <Card stack>
          <H2>Heading</H2>
          <P>Text</P>
        </Card>
        <Card stack>
          <H2>Heading</H2>
          <P>
            Pariatur officia sit adipisicing pariatur commodo enim do quis
          </P>
        </Card>
        <Card stack>
          <H2>Heading</H2>
          <P>Text</P>
        </Card>
      </Grid.Container>)
```


### With Flex

While Flex has the horizontal direction, it uses rowGap when wrapping. So it's the container spacing the Cards then. This is not ideal, because the Cards should ideally have no gap, like in the Grid example above.


```tsx
render(<Flex.Container>
        <Card span={{
    small: 'auto',
    medium: 4,
    large: 4
  }} stack>
          <H2>Heading</H2>
          <P>Text</P>
        </Card>
        <Card span={{
    small: 'auto',
    medium: 4,
    large: 4
  }} stack>
          <H2>Heading</H2>
          <P>
            Pariatur officia sit adipisicing pariatur commodo enim do quis
          </P>
        </Card>
        <Card span={{
    small: 'auto',
    medium: 4,
    large: 4
  }} stack>
          <H2>Heading</H2>
          <P>Text</P>
        </Card>
      </Flex.Container>)
```



  
```tsx
const MyTable = () => <Table.ScrollView>
            <Table border outline size="medium">
              <thead>
                <Tr noWrap>
                  <Th>Column 1</Th>
                  <Th>Column 2</Th>
                  <Th>Column 3</Th>
                </Tr>
              </thead>
              <tbody>
                <Tr>
                  <Td>Row 1</Td>
                  <Td>Row 1</Td>
                  <Td>Row 1</Td>
                </Tr>
                <Tr>
                  <Td colSpan={3} align="right">
                    <Button>Button</Button>
                  </Td>
                </Tr>
                <Tr>
                  <Td>Row 3</Td>
                  <Td>Row 3</Td>
                  <Td>Row 3</Td>
                </Tr>
              </tbody>
            </Table>
          </Table.ScrollView>;
render(<Card title="Card title" responsive={false} filled>
            <MyTable />
          </Card>);
```

  
```tsx
render(<Section backgroundColor="var(--token-color-background-marketing-subtle)" innerSpace="large">
        <Card>Card in colored Section</Card>
      </Section>)
```



## With `backgroundColor` and `outline`


```tsx
render(<Flex.Vertical>
        <Form.MainHeading>I'm left aligned</Form.MainHeading>
        <Card stack backgroundColor="var(--token-color-background-neutral-subtle)" outline="var(--token-color-stroke-neutral)">
          <P>Card content</P>
          <Card outline="transparent">
            <P>Nested card</P>
          </Card>
        </Card>
      </Flex.Vertical>)
```


### Interactive Cards

Use `Card.Action` to make a Card clickable or a link. Pass `onClick` for a button-like card, or `href`/`to` for a link card. Both variants get hover, focus, and keyboard styles automatically.

For custom routing (e.g. react-router), pass a router `Link` via the `element` prop on `Card.Action`.


```tsx
render(<Card.List style={{
  maxWidth: '640px',
  margin: '0 auto'
}}>
        <Card.ListItem center="when-small">
          <Card.Action onClick={() => console.log('Card clicked')} stack gap="x-small">
            <Lead>Clickable card</Lead>
            <P>
              Click this card. It has hover, focus, and keyboard support.
            </P>
            <Button variant="tertiary" icon="chevron_right" text="Read more" element="span" />
          </Card.Action>
        </Card.ListItem>

        <Card.ListItem center="when-small">
          <Card.Action href="/" stack gap="x-small">
            <Lead>Link card</Lead>
            <P>This card navigates with an anchor element.</P>
            <Button variant="tertiary" icon="chevron_right" text="Read more" element="span" />
          </Card.Action>
        </Card.ListItem>
      </Card.List>)
```


### Info card layout

Use `Card.Info` directly for information and tips. Compose its content using existing components.


```tsx
render(<Card.Info>
      <Flex.Horizontal wrap={false} gap="small">
        <IconPrimary icon={lightbulb_medium} size="medium" style={{
      color: 'var(--token-color-icon-action)'
    }} />
        <Flex.Stack gap="small" style={{
      maxWidth: 'var(--prose-max-width)'
    }}>
          <Flex.Stack gap="x-small">
            <P size="small" weight="medium">
              Title of your info/tip
            </P>
            <P size="small">
              This is a description of some information or a tip that will
              inform the user of something that will help them.
            </P>
          </Flex.Stack>
          <Flex.Horizontal gap="small">
            <Button variant="secondary" icon={fish} href="#info-card-actions">
              Some CTA here
            </Button>
            <Button variant="tertiary" icon={fish} href="#info-card-actions">
              Another CTA
            </Button>
          </Flex.Horizontal>
        </Flex.Stack>
      </Flex.Horizontal>
    </Card.Info>)
```


### Info card actions

Set `onClick`, `href`, or `to` on Card.Info when the whole card performs one action or navigates to a URL. Keep the content free of nested links, buttons, and other interactive elements.


```tsx
render(<Flex.Stack>
      <Card.Info onClick={() => console.log('Start saving')}>
        <Flex.Stack gap="x-small">
          <P size="small" weight="medium">
            Start saving
          </P>
          <P size="small">
            Set up a monthly transfer to build your savings.
          </P>
        </Flex.Stack>
      </Card.Info>
      <Card.Info href="#info-card-layout">
        <Flex.Stack gap="x-small">
          <P size="small" weight="medium">
            Read more about saving
          </P>
          <P size="small">The whole card is a link.</P>
        </Flex.Stack>
      </Card.Info>
    </Flex.Stack>)
```


### Closable info card

Compose Card.Info.CloseButton inside Card.Info and wrap the card in [HeightAnimation](/uilib/components/height-animation/) to animate dismissal. HeightAnimation removes the card after the closing animation completes. In your application, move focus to a suitable remaining control when dismissing focused content.


```tsx
const Example = () => {
  const [visible, setVisible] = useState(true);
  const [dismissed, setDismissed] = useState(false);
  const animationRef = useRef<HTMLElement>(null);
  const shouldMoveFocus = useRef(false);
  return !dismissed ? <HeightAnimation ref={animationRef} open={visible} onAnimationStart={state => {
    if (state === 'closing') {
      shouldMoveFocus.current = animationRef.current?.contains(document.activeElement) ?? false;
    }
  }} onAnimationEnd={state => {
    if (state === 'closed') {
      setDismissed(true);
      if (shouldMoveFocus.current) {
        document.getElementById('graphic-and-centered-layouts')?.focus();
      }
    }
  }}>
            <Card.Info>
              <Card.Info.CloseButton onClick={() => setVisible(false)} title="Dismiss savings tip" />
              <Flex.Stack gap="x-small">
                <P size="small" weight="medium">
                  A little every month goes a long way
                </P>
                <P size="small">
                  Set up a monthly transfer to build your savings.
                </P>
              </Flex.Stack>
              <Button variant="secondary" onClick={() => setVisible(false)}>
                Got it
              </Button>
            </Card.Info>
          </HeightAnimation> : null;
};
render(<Example />);
```


### Graphic and centered layouts


```tsx
render(<Flex.Stack>
      <Card.Info>
        <Flex.Horizontal wrap={false}>
          <Avatar size="large" />
          <Flex.Stack gap="x-small">
            <P size="small" weight="medium">
              Your savings adviser
            </P>
            <P size="small">
              Compose an image, illustration, or any other content.
            </P>
          </Flex.Stack>
        </Flex.Horizontal>
      </Card.Info>
      <Card.Info>
        <Flex.Stack align="center" gap="small" style={{
      textAlign: 'center'
    }}>
          <Flex.Stack gap="x-small" align="center">
            <H2 size="small">Build your savings</H2>
            <P size="small">Center the content using Flex.</P>
          </Flex.Stack>
          <Flex.Item alignSelf="center">
            <Button variant="secondary" href="#info-card-layout">
              Learn more
            </Button>
          </Flex.Item>
        </Flex.Stack>
      </Card.Info>
      <Card.Info dropShadow={false} innerSpace="small">
        <Flex.Stack gap="x-small">
          <P size="small" weight="medium">
            Text only, without shadow
          </P>
          <P size="small">Card defaults can be overridden.</P>
        </Flex.Stack>
      </Card.Info>
      <Provider skeleton>
        <Card.Info>
          <Flex.Stack gap="x-small">
            <P size="small" weight="medium">
              Loading title
            </P>
            <P size="small">Loading information</P>
          </Flex.Stack>
        </Card.Info>
      </Provider>
    </Flex.Stack>)
```


### Image beside content

Set `innerSpace={0}` on Card.Info for edge-to-edge media and add padding around the content. Flex wraps the image above the text when the card becomes narrow, including inside a grid. Use descriptive `alt` text for meaningful images and `alt=""` for decorative images.


```tsx
render(<Card.Info innerSpace={0}>
      <Flex.Horizontal layoutEngine="css" gap={false} align="stretch" style={{
    flexDirection: 'row-reverse',
    overflow: 'hidden',
    borderRadius: 'var(--rounded-corner)'
  }}>
        <Flex.Item grow shrink style={{
      flexBasis: '18rem',
      minWidth: 0
    }}>
          <img src="/media/card/savings.webp" alt="" width="688" height="408" loading="lazy" style={{
        display: 'block',
        width: '100%',
        height: '100%',
        minHeight: '12.75rem',
        objectFit: 'cover'
      }} />
        </Flex.Item>
        <Flex.Item grow shrink style={{
      flexBasis: '18rem',
      minWidth: 0
    }}>
          <Flex.Stack layoutEngine="css" gap="small" justify="center" innerSpace={{
        small: 'small',
        medium: 'large',
        large: 'large'
      }} style={{
        height: '100%'
      }}>
            <Flex.Stack gap="xx-small">
              <H2 size="large">Spar til noe uventet</H2>
              <P>Du vet aldri når du trenger litt ekstra penger.</P>
            </Flex.Stack>
            <Flex.Item alignSelf="flex-start">
              <Button href="#info-card-layout">Start sparing</Button>
            </Flex.Item>
          </Flex.Stack>
        </Flex.Item>
      </Flex.Horizontal>
    </Card.Info>)
```


### Centered image card

Set `maxWidth` directly on Card.Info and use Flex to center the content. Round the image's top corners without clipping the card, so keyboard focus outlines remain visible.


```tsx
render(<Card.Info innerSpace={0} gap={false} style={{
  maxWidth: '21.5rem'
}}>
      <img src="/media/card/savings.webp" alt="" width="688" height="408" loading="lazy" style={{
    display: 'block',
    width: '100%',
    height: '6.5rem',
    objectFit: 'cover',
    borderRadius: 'var(--rounded-corner) var(--rounded-corner) 0 0'
  }} />
      <Flex.Stack align="center" gap="small" innerSpace="small" style={{
    textAlign: 'center'
  }}>
        <Flex.Stack gap="xx-small" align="center">
          <H2 size="large">Spar til noe uventet</H2>
          <P>Du vet aldri når du trenger litt ekstra penger.</P>
        </Flex.Stack>
        <Flex.Item alignSelf="center">
          <Button href="#info-card-layout">Start sparing</Button>
        </Flex.Item>
      </Flex.Stack>
    </Card.Info>)
```


### Full media with video

Use a muted background video for decoration and keep all information in the card's text. The video plays automatically unless the user prefers reduced motion. Enabling reduced motion pauses playback; the user can still choose to play it. The keyboard-accessible control toggles play and pause without resetting playback. Use the first video frame as the poster to avoid a visual jump when playback starts. A poster image and the call to action remain available if playback fails.

Replace the video and poster URLs with your own media. `Card.Info.Overlay` supplies the gradient, progressive blur, text shadows for headings and paragraphs, padding, and dark-surface context for its controls. Check text contrast across the full video. For video that conveys information, provide native controls, captions, and a transcript; see [images and video](/uilib/components/card/#images-and-video).

Use `Card.Info.MediaButton` and pass the video ref. It handles playback state, reduced motion, translated labels, and the play/pause icon transition. Keep source, poster, and error fallback handling with the video.

The close control pauses playback, dismisses the card, and moves focus to this section's heading.


```tsx
const Example = () => {
  const videoRef = useRef<HTMLVideoElement>(null);
  const [failed, setFailed] = useState(false);
  const [dismissed, setDismissed] = useState(false);
  return !dismissed ? <Card.Info innerSpace={0}>
            <Card.Info.CloseButton title="Dismiss video card" onClick={() => {
      videoRef.current?.pause();
      document.getElementById('full-media-with-video')?.focus();
      setDismissed(true);
    }} />
            <Flex.Stack layoutEngine="css" gap={false} justify="flex-end" style={{
      position: 'relative',
      minHeight: '21.5rem',
      overflow: 'hidden',
      color: 'var(--token-color-text-neutral-ondark)',
      backgroundColor: 'black',
      borderRadius: 'var(--rounded-corner)'
    }}>
              <img style={{
        position: 'absolute',
        inset: 0,
        width: '100%',
        height: '100%',
        objectFit: 'cover'
      }} src="/media/card/savings-poster.webp" width="752" height="416" alt="" loading="lazy" />
              <video ref={videoRef} style={{
        position: 'absolute',
        inset: 0,
        width: '100%',
        height: '100%',
        objectFit: 'cover',
        visibility: failed ? 'hidden' : undefined
      }} src="/media/card/savings-loop.mp4" poster="/media/card/savings-poster.webp" preload="none" muted loop playsInline aria-hidden="true" onPlaying={() => setFailed(false)} onError={() => {
        setFailed(true);
      }} />
              <Card.Info.MediaButton videoRef={videoRef} playTitle="Play background video" pauseTitle="Pause background video" />
              <Card.Info.Overlay align="center" style={{
        marginTop: '9.75rem',
        textAlign: 'center'
      }}>
                <Flex.Stack gap="xx-small" align="center">
                  <H2 size="x-large">Spar til noe uventet</H2>
                  <P>Du vet aldri når du trenger litt ekstra penger.</P>
                </Flex.Stack>
                <Flex.Item alignSelf="center">
                  <Button href="#info-card-layout">Start sparing</Button>
                </Flex.Item>
                {failed && <P role="status" size="small">
                    Videoen kunne ikke spilles av. Du kan fortsatt lese om
                    sparing.
                  </P>}
              </Card.Info.Overlay>
            </Flex.Stack>
          </Card.Info> : null;
};
render(<Example />);
```

## Properties


```json
{
  "props": {
    "layoutEngine": {
      "doc": "Select the internal Flex layout engine. Defaults to `legacy` for backwards compatibility. Use `css` to opt in to native CSS gap.",
      "type": [
        "\"css\"",
        "\"legacy\""
      ],
      "defaultValue": "'legacy'",
      "status": "optional"
    },
    "outset": {
      "doc": "Whether or not to break out (using negative margins) on larger screens. Defaults to `false`.",
      "type": "boolean",
      "status": "optional"
    },
    "stack": {
      "doc": "True to stack the sub components with space between. The `spacing` will default to `medium`. Defaults to `false`.",
      "type": "boolean",
      "status": "optional"
    },
    "direction": {
      "doc": "Defaults to `vertical`.",
      "type": [
        "\"horizontal\"",
        "\"vertical\""
      ],
      "status": "optional"
    },
    "alignSelf": {
      "doc": "Defaults to `stretch`.",
      "type": [
        "\"flex-start\"",
        "\"flex-end\"",
        "\"center\"",
        "\"baseline\"",
        "\"stretch\""
      ],
      "status": "optional"
    },
    "title": {
      "doc": "Define a title that appears on top of the Card.",
      "type": "React.ReactNode",
      "status": "optional"
    },
    "responsive": {
      "doc": "Define if the card should behave responsive. Defaults to `true`.",
      "type": "boolean",
      "status": "optional"
    },
    "filled": {
      "doc": "Define if the Card should get the same background color as the outline border.",
      "type": "boolean",
      "status": "optional"
    },
    "outline": {
      "doc": "Define the outline color. Defaults to `var(--card-outline-color)`.",
      "type": "string",
      "status": "optional"
    },
    "outlineWidth": {
      "doc": "Define the outline width. Defaults to `var(--card-outline-width)` (`0.0625rem`).",
      "type": [
        "string",
        "number"
      ],
      "status": "optional"
    },
    "dropShadow": {
      "doc": "Define if the Card should have drop shadow. Uses Section `dropShadow`.",
      "type": "boolean",
      "status": "optional"
    },
    "backgroundColor": {
      "doc": "Define the background color. Defaults to `var(--card-background-color)`.",
      "type": "string",
      "status": "optional"
    },
    "element": {
      "doc": "Define the type of element. Defaults to `section`.",
      "type": "React.Element",
      "status": "optional"
    },
    "children": {
      "doc": "Contents.",
      "type": "React.ReactNode",
      "status": "required"
    },
    "[Flex.Container](/uilib/layout/flex/container/properties)": {
      "doc": "Flex.Container properties.",
      "type": "Various",
      "status": "optional"
    },
    "[Flex.Item](/uilib/layout/flex/item/properties)": {
      "doc": "Flex.Item properties.",
      "type": "Various",
      "status": "optional"
    },
    "[Space](/uilib/layout/space/properties)": {
      "doc": "Spacing properties like `top` or `bottom` are supported.",
      "type": [
        "string",
        "object"
      ],
      "status": "optional"
    }
  }
}
```


### Card.Info Properties


```json
{
  "props": {
    "children": {
      "doc": "Freely composed content such as headings, text, graphics, or actions. Do not include interactive controls when the whole card is interactive.",
      "type": "React.ReactNode",
      "status": "required"
    },
    "Card properties": {
      "doc": "All [Card](/uilib/components/card/properties) properties are supported. Defaults to responsive=false, dropShadow=true, stack=true, gap=\"small\", and innerSpace=\"small\" on small screens or \"medium\" on larger screens. All defaults can be overridden.",
      "type": "Various",
      "status": "optional"
    },
    "Card.Action properties": {
      "doc": "[Card.Action](/uilib/components/card/properties/#cardaction-properties) properties are supported for a whole-card link or action. Set href, to, or onClick to make Card.Info interactive. Do not nest Card.Info.CloseButton, Card.Info.MediaButton, or other interactive controls in an interactive card.",
      "type": "Various",
      "status": "optional"
    }
  }
}
```


### Card.Action Properties


```json
{
  "props": {
    "href": {
      "doc": "The URL to navigate to. When set, renders an anchor element. Values using a script-executing protocol (`javascript:` or `vbscript:`) are removed.",
      "type": "string",
      "status": "optional"
    },
    "to": {
      "doc": "Route path for use with a router Link component (e.g. react-router). Pass a custom `element` that accepts `to`. Values using a script-executing protocol (`javascript:` or `vbscript:`) are removed.",
      "type": "string",
      "status": "optional"
    },
    "target": {
      "doc": "The anchor target attribute.",
      "type": "string",
      "status": "optional"
    },
    "rel": {
      "doc": "The anchor rel attribute.",
      "type": "string",
      "status": "optional"
    },
    "element": {
      "doc": "Custom element to render as the wrapper. Defaults to `a` when `href`/`to` is set. Use this for router Link components.",
      "type": [
        "string",
        "React.Element"
      ],
      "status": "optional"
    },
    "children": {
      "doc": "Contents of the Card inside the action wrapper.",
      "type": "React.ReactNode",
      "status": "required"
    },
    "Card properties": {
      "doc": "[Card](/uilib/components/card/properties) properties are supported. A whole-card action must not contain nested interactive controls.",
      "type": "Various",
      "status": "optional"
    }
  }
}
```


### Card.List Properties


```json
{
  "props": {
    "children": {
      "doc": "Card.ListItem elements.",
      "type": "React.ReactNode",
      "status": "required"
    },
    "[Space](/uilib/layout/space/properties)": {
      "doc": "Spacing properties like `top` or `bottom` are supported.",
      "type": [
        "string",
        "object"
      ],
      "status": "optional"
    }
  }
}
```


### Card.ListItem Properties


```json
{
  "props": {
    "center": {
      "doc": "Center the content of the list item. Use `true` to always center, or `\"when-small\"` to center only on small screens.",
      "type": [
        "boolean",
        "\"when-small\""
      ],
      "status": "optional"
    },
    "children": {
      "doc": "A Card or Card.Action element.",
      "type": "React.ReactNode",
      "status": "required"
    },
    "[Space](/uilib/layout/space/properties)": {
      "doc": "Spacing properties like `top` or `bottom` are supported.",
      "type": [
        "string",
        "object"
      ],
      "status": "optional"
    }
  }
}
```


### Card.Info.CloseButton Properties


```json
{
  "props": {
    "title": {
      "doc": "Accessible close label. Defaults to the translated Card.closeTitle.",
      "type": "React.ReactNode",
      "status": "optional"
    },
    "Button properties": {
      "doc": "Button properties are supported, except href and type. Always renders a type=\"button\" control. Compose directly inside a noninteractive Card or Card.Info.",
      "type": "Various",
      "status": "optional"
    }
  }
}
```


### Card.Info.MediaButton Properties


```json
{
  "props": {
    "videoRef": {
      "doc": "Stable ref attached to the video element. Mount the video and control together. After a render commits, the control detects replacement video elements, removes old listeners, and applies playback preferences to the new element. Native play, pause, ended, emptied, and error events update the control.",
      "type": "React.RefObject<HTMLVideoElement | null>",
      "status": "required"
    },
    "autoPlay": {
      "doc": "Automatically plays muted video when reduced motion is not requested. Enabling reduced motion pauses playback. Manual playback remains available. A browser may block autoplay.",
      "type": "boolean",
      "defaultValue": "true",
      "status": "optional"
    },
    "playTitle": {
      "doc": "Accessible play label. Defaults to the translated Card.playTitle.",
      "type": "string",
      "status": "optional"
    },
    "pauseTitle": {
      "doc": "Accessible pause label. Defaults to the translated Card.pauseTitle.",
      "type": "string",
      "status": "optional"
    },
    "title": {
      "doc": "Override both playback labels with a fixed accessible label.",
      "type": "React.ReactNode",
      "status": "optional"
    },
    "Button properties": {
      "doc": "Button properties are supported, except href, type, icon, and transitionState. Position defaults to the top-left corner of the nearest positioned container. The application owns the video element, sources, poster, and error fallback.",
      "type": "Various",
      "status": "optional"
    }
  }
}
```


### Card.Info.Overlay Properties


```json
{
  "props": {
    "gradient": {
      "doc": "Add a transparent-to-dark gradient behind the content.",
      "type": "boolean",
      "defaultValue": "true",
      "status": "optional"
    },
    "blur": {
      "doc": "Add progressive backdrop blur behind the content. The gradient remains available in browsers without backdrop-filter support.",
      "type": "boolean",
      "defaultValue": "true",
      "status": "optional"
    },
    "children": {
      "doc": "Accessible content and controls above the decorative effects. Headings and paragraphs automatically receive text shadows, including when nested in layout components. Provides dark-surface context for Buttons and other supporting components.",
      "type": "React.ReactNode",
      "status": "required"
    },
    "Flex.Stack properties": {
      "doc": "All Flex.Stack properties are supported. Defaults to native CSS gaps, gap=\"small\", and responsive padding: small on small screens and large on larger screens. Use spacing or style to position the overlay within the media container.",
      "type": "Various",
      "status": "optional"
    }
  }
}
```

## Card.Info.CloseButton events


```json
{
  "props": {
    "onClick": {
      "doc": "Called when the close control is activated. The application controls dismissal and focus after removal.",
      "type": "function",
      "status": "optional"
    }
  }
}
```


## Card.Info.MediaButton events


```json
{
  "props": {
    "onClick": {
      "doc": "Runs before toggling playback. Return false or preventDefault() to cancel the toggle. Playback resumes from the current position.",
      "type": "(event: React.MouseEvent<HTMLButtonElement>) => void | boolean",
      "status": "optional"
    }
  }
}
```


## `Card.Action` events


```json
{
  "props": {
    "onClick": {
      "doc": "Click handler. When used without `href`/`to`, renders a button-like wrapper with keyboard support (Enter/Space).",
      "type": "function",
      "status": "optional"
    }
  }
}
```
