Bouncy Accordion
beui/bouncy-accordionFreeComponent
Single-open accordion with weighted spring layout, icon rows and reduced-motion-safe content reveals.
Install it
npx shadcn@latest add https://beui.dev/r/bouncy-accordion.jsonSource
1"use client";2// beui.dev/components/motion/bouncy-accordion34import {5 motion,6 useReducedMotion,7 type Transition,8} from "motion/react";9import { ChevronDown } from "lucide-react";10import {11 useCallback,12 useId,13 useLayoutEffect,14 useRef,15 useState,16 type ReactNode,17} from "react";18import { EASE_OUT } from "@/lib/ease";19import { cn } from "@/lib/utils";2021export type BouncyAccordionItem = {22 id: string;23 title: ReactNode;24 description?: ReactNode;25 icon?: ReactNode;26 disabled?: boolean;27};2829export type BouncyAccordionClassNames = {30 root?: string;31 item?: string;32 trigger?: string;33 icon?: string;34 title?: string;35 chevron?: string;36 content?: string;37 description?: string;38};3940export interface BouncyAccordionProps {41 items: BouncyAccordionItem[];42 value?: string | null;43 defaultValue?: string | null;44 onValueChange?: (value: string | null) => void;45 collapsible?: boolean;46 className?: string;47 classNames?: BouncyAccordionClassNames;48}4950// Local springs keep the accordion's connected groups moving together while51// avoiding scale projection on text-heavy row contents.52// Gap spring: must not overshoot y — positive y overshoot drifts items below53// their mt-3 resting point and briefly overlaps the next item.54const ROW_TRANSITION: Transition = {55 type: "spring",56 duration: 0.55,57 bounce: 0.38,58};5960const CONTENT_OPEN_TRANSITION: Transition = {61 type: "spring",62 duration: 0.58,63 bounce: 0.32,64};6566const CONTENT_CLOSE_TRANSITION: Transition = {67 type: "spring",68 duration: 0.46,69 bounce: 0.26,70};7172const DESCRIPTION_TRANSITION: Transition = {73 duration: 0.18,74 ease: EASE_OUT,75};7677const CHEVRON_TRANSITION: Transition = {78 type: "spring",79 duration: 0.42,80 bounce: 0.28,81};828384function useControllableAccordionValue({85 value,86 defaultValue,87 onValueChange,88}: {89 value?: string | null;90 defaultValue?: string | null;91 onValueChange?: (value: string | null) => void;92}) {93 const [internalValue, setInternalValue] = useState(defaultValue ?? null);94 const isControlled = value !== undefined;95 const currentValue = value ?? internalValue;9697 const setValue = useCallback(98 (next: string | null) => {99 if (!isControlled) {100 setInternalValue(next);101 }102103 onValueChange?.(next);104 },105 [isControlled, onValueChange],106 );107108 return [currentValue, setValue] as const;109}110111function BouncyAccordionRow({112 item,113 open,114 startsGroup,115 endsGroup,116 separatedFromPrevious,117 contentId,118 triggerId,119 reduce,120// … truncated
What it pulls in
npm packages
Files it writes
More from beui
All 105 items| Component | Registry | Kind | Access | Installs | Command |
|---|---|---|---|---|---|
| Action Swap Bluraction-swap-blur | beui | Components | Free | 3 deps · 4 files | |
| Action Swap Cascadeaction-swap-cascade | beui | Components | Free | 3 deps · 4 files | |
| Action Swap Rollaction-swap-roll | beui | Components | Free | 3 deps · 4 files | |
| Agent Activityagent-activity | beui | Components | Free | 4 deps · 9 files | |
| Agent Loading States Agent Progressagent-progress | beui | Components | Free | 3 deps · 3 files | |
| AI Sidebarai-sidebar | beui | Components | Free | 4 deps · 5 files | |
| Animated Badgeanimated-badge | beui | Components | Free | 4 deps · 3 files | |
| Number Animation Animated Numberanimated-number | beui | Components | Free | 3 deps · 3 files |
