Skip to content
buttery

v1.0.0

Source on GitHub

Version 1.0.0

ExpandableCard

A bento card that opens into a dialog by turning into it, its title travelling along, and closes back into the card.

import { ExpandableCard } from "@butteryui/react";

Demo

Tilt it in 3D

Product shot

Any size

16:9

When to use it

Use it for

  • A feature card with more to say than fits: a short video, steps, a longer note

Not for

  • A card that links to its own page: put a link in a Bento.Card
  • A decision or a form: use Dialog

Examples

Card variants

A tinted card beside a plain one, each opening into the same dialog surface.

Gift a subscription

3 months

Brew guides inside

Pour-over · 1:16

Controlled card

A card opened from elsewhere on the page too, with open and onOpenChange kept in useState.

Any size

16:9

Every change kept

Undo · Redo

Feature detail

A feature card with a longer note behind it, beside a card that doesn't open.

Every change kept

Undo · Redo

Any size

16:9

Features section

A features section whose large card opens into a dialog with a setting to try, beside a plain card.

Made to be presented

Tilt it in 3D

Product shot

Any size

16:9

Variants and states

Cards that open

The card grows into the dialog on spring.morph with its title travelling along; the others step back. Closing turns it back into the card on spring.smooth.

Tilt it in 3D

Product shot

Any size

1:1

16:9

Variants

Each card draws itself as a Bento.Card does: outline, soft, tinted or plain, beside elevated above. Open, every one is a dialog on the dialog surface.

Every change kept

outline

Notes for the room

soft

A background for every talk

tinted

Present from any screen

plain

Props

ExpandableCard

PropTypeDefaultDescription
title

required

stringThe card's title. It travels into the dialog and names it.
detail

required

ReactNodeWhat the open card shows under its title.
span4 | 6 | 8 | 126Columns it spans in a Bento once the grid is 1024px wide. Defaults to 6.
rows1 | 21Rows it spans in a Bento. Defaults to 1.
variant"elevated" | "outline" | "soft" | "plain" | "tinted""elevated"How the card draws its surface in the grid, as a Bento.Card does: "elevated" (the default), "outline", "soft", "plain" or "tinted". Open, it's a dialog on the dialog surface whatever its variant, since a dialog over the scrim needs one.
openbooleanWhether it's open. Pair with onOpenChange to control it.
defaultOpenbooleanfalseWhether it starts open when uncontrolled. Defaults to false.
onOpenChange((open: boolean) => void)Called when it opens or closes.
openLabelstringNames the button that opens it. Defaults to "Open" and the title.
closeLabelstring"Close"Names the button that closes it. Defaults to "Close".

It also takes Omit<MotionElementProps<"article">, "title">.

Accessibility

The card is an article named by its title; its expand button ("Open" and the title) opens a modal dialog named by the same title. Escape, the close button or a press outside closes it, and focus goes back to the expand button. With reduced motion the dialog fades in and out instead of growing from the card.

KeysWhat they do
Enter or SpaceOpens the card into its dialog from the expand button
Tab or Shift+TabMoves through what's in the open dialog and its close button, staying inside
EscapeCloses the dialog back into the card, with focus on the expand button

Motion

It changes shape as one surface, on `spring.smooth`, or `spring.morph` for a big change, rather than closing and reopening.

With reduced motion: The dialog fades in and out in place in 100ms instead of growing out of the card, the title doesn't travel, and the other cards fade back in 100ms.

Layout

In a Bento it spans 4, 6, 8 or 12 of 12 columns and one or two 320px rows (--bt-bento-row) like any card, with a 28px expand button at its top end; the whole card is the press target. In the grid it draws itself as a Bento.Card does, elevated (the default), outline, soft, tinted or plain. Open, it's a dialog on the dialog surface whatever its variant, at most 880px wide (--bt-w-dialog-lg) with the card's radius, 16px inside the window, scrolling inside when its detail is taller.