Skip to content

Image Accordion

A row of pictures where one opens wide and the rest wait as slim strips, each with a caption.

import { ImageAccordion, ImageAccordionItem } from '@youj/react'

Installation & usage

npm i @youj/react

For an app already set up with npx @youj/cli init, which installs the peers (@youj/core, @youj/motion, motion and Base UI) and wires the provider. New app? Start at Installation.

Use an image accordion to show a handful of pictures in the room of one: destinations, product shots, case studies. One panel is open wide with its title and a short line; the others stay visible as strips, so the whole set reads at a glance. For a long sequence to step through, use a Carousel.

import { ImageAccordion, type ImageAccordionItem } from '@youj/react'

const places: ImageAccordionItem[] = [
  { id: 'coast', media: { src: '/coast.jpg', alt: 'Coast road at sunrise' }, title: 'Coast road', description: 'Morning light over the headland' },
  { id: 'dunes', media: { src: '/dunes.jpg', alt: 'Sand dunes at dusk' }, title: 'Dunes', description: 'Wind-cut ridges at dusk' },
  { id: 'valley', media: <MyIllustration />, title: 'Valley' },
]

<ImageAccordion items={places} />

media is { src, alt } for an image (it covers the panel) or any node: an illustration, a video, a gradient. The first panel is open at first; value, defaultValue and onValueChange hold the open id.

With activation="hover" (the default) a panel opens when the pointer rests on it or it takes focus, and on touch when it's tapped. activation="click" opens only on a press, Enter or Space, and hover and the arrow keys just point. orientation="vertical" stacks the panels.

The component keeps its size: the panels share its box (h-80 across, h-120 stacked), so opening one only moves the split. Set your own height with className.

Reference

Propsitems, value, activation, orientation… (7)

Props

ImageAccordion props
PropType
itemsRequiredImageAccordionItem[]–
valuestring

The open panel's id (controlled).

–
defaultValuestring

The panel open at first. Default: the first.

–
onValueChange(id: string) => void

Called with the id of the panel that opens.

–
activation'hover' | 'click'

hover opens a panel on hover or focus (a press on touch); click only on a press.

default 'hover'

'hover'
orientation'horizontal' | 'vertical'

Panels side by side (horizontal) or stacked (vertical).

default 'horizontal'

'horizontal'
aria-labelstring

Names the group. Default: the labels' "Gallery".

–
soundboolean

With sound on (YoujProvider’s sound), opening a panel by press or key plays select; hovering stays silent. false keeps this one silent.

default true

true
Other props pass through: extends ComponentProps<'div'>.

Items

ImageAccordionItem props
PropType
idRequiredstring–
mediaRequiredReactNode | { src: string; alt: string }

The picture: { src, alt } for an image, or your own node (an illustration, a video, a gradient). It fills the panel.

–
titleRequiredReactNode

The caption's first line, and the panel's accessible name.

–
descriptionReactNode

A short line under the title.

–
StylingParts and data attributes

Styling

Parts: image-accordion (with data-orientation horizontal or vertical and data-yj-activation hover or click), image-accordion-panel (with data-state active or inactive), image-accordion-media, image-accordion-caption, image-accordion-title and image-accordion-description.

MotionSplit, caption, strips · reduced motion

Motion

Motion tokens
MomentTokens
Splitthe opening panel widens as the others narrow, retargeting mid-glide
spring.smooth

0.36s, bounce 0

0.36s, bounce 0
Captionfades up once its panel has started to open
duration.baseease.enter

220ms · cubic-bezier(0.16, 1, 0.3, 1)

220mscubic-bezier(0.16, 1, 0.3, 1)
Stripssit a little quieter, and brighten under the pointer
duration.baseease.standard

220ms · cubic-bezier(0.2, 0, 0, 1)

220mscubic-bezier(0.2, 0, 0, 1)

Reduced motion

The split and the caption jump.

Sound

With sound on (YoujProvider's sound), opening a panel by a press or a key plays select; hovering stays silent. sound={false} keeps one accordion quiet.

Reduced motion

The split and the caption jump; nothing glides.

AccessibilityGroup, buttons, keys

Accessibility

  • It is a group named "Gallery" (imageAccordion in the labels), or your aria-label.
  • Each panel is a button named by its title and described by its line; the open one carries aria-current. Captions stay in the page for screen readers whichever panel is open.
  • The panels are one tab stop. The arrow keys of the orientation move between them (Left and Right across, mirrored in RTL; Up and Down stacked), and Home and End go to the first and last. In hover mode focus opens the panel; in click mode Enter or Space does.
  • Give each image its alt, or your media its own name if it carries meaning.