Skip to content

Media Accordion

Rows that open by height with a photo beside the title that grows with them.

From the Titen template — see it in context

Two people reviewing notes pinned to a wall

Positioning, audience research and a voice your team can actually keep to.

Designer sketching layouts in a notebook

Layouts, type and motion designed together, tested at every screen size.

Code on a laptop screen in a dim room

Fast, accessible sites your team can edit without a developer.

Installation

$ npx shadcn@latest add https://framebrick.co/r/media-accordion.json

Usage

import { MediaAccordion } from "@/components/ui/media-accordion";

<MediaAccordion
  defaultValue="strategy"
  items={services.map((s) => ({
    value: s.slug,
    title: s.title,
    content: s.summary,
    media: <Image src={s.photo} alt="" fill />,
  }))}
/>

Props

PropTypeDefaultDescription
items{ value, title, content, media }[]—Rows. The media sits beside the title and is always there, small while the row is closed.
valuestring | null—Open row (controlled); null closes them all.
defaultValuestring—Row that starts open (uncontrolled); leave it out for all closed.
onValueChange(value: string | null) => void—Called when a row opens or closes.
closedHeightnumber192Height of a closed row's media from 1024 px up, in px.
openHeightnumber363Height of an open row's media from 1024 px up, in px.
mediaWidthnumber277Width of the media column from 1024 px up, in px.
  • The difference from `accordion-list`: there any media sits inside the panel and appears with it; here the media itself grows, so nothing pops in.
  • One row is open at a time, and clicking the open row closes it.
  • Closed panels stay in the HTML (search engines read them) but are inert, so keyboards and screen readers skip them.

AI prompt

Paste into v0, Lovable, Bolt or Claude to rebuild this component in your own project.

Create a React + Tailwind CSS + Motion (motion/react) component called MediaAccordion.
- Rows separated by 1px borders. Each row: a full-width button with the title (text-base font-medium) and a plus icon
  whose upright stroke scales to 0 as the row opens, so it becomes a minus. Spring: bounce 0.2, duration 0.4s.
- Under the button, a flex row (column below 1024px): the media on the left in a rounded-lg frame, animating its height
  between closedHeight and openHeight with the same spring, always present; the text panel on the right, animating
  height 0 → auto with the content fading in 0.25s after a 0.1s delay.
- One row open at a time; clicking the open row closes it. Controlled (value, which may be null) or uncontrolled.
- Give the media its height inline as well, so the server renders the row at its real size.
- Accessibility: aria-expanded and aria-controls on the button, role region with aria-labelledby on the panel,
  ids from the row's index, and a closed panel is inert.

Be the first to know

Get new templates and components first

One short email when we release a template or a free component. No spam — unsubscribe anytime.

We use your email only for Framebrick updates. Privacy policy