
3D Extruded Buildings & Urban Footprints
Real-world 3D building extrusions with dynamic sunlight shadows, terrain DEM elevations, and pitch/bearing camera controls.
accordionuiVertically stacked, collapsible panels — one or many open at a time. Use for FAQs, settings groups, and any place where space is tight but content needs to stay browsable. Built on Radix UI with smooth animation and full keyboard support.
Also available for Vue ->$pnpm dlx shadcn@latest add https://uipkge.dev/r/react/accordion.json$npx shadcn@latest add https://uipkge.dev/r/react/accordion.json$yarn dlx shadcn@latest add https://uipkge.dev/r/react/accordion.json$bunx shadcn@latest add https://uipkge.dev/r/react/accordion.jsonnpx shadcn@latest add @uipkge-react/accordionInstalls to:components/ui/accordion/| Name | Type / Values | Default | Required |
|---|---|---|---|
variant | | default | optional |
'use client'
import * as React from 'react'
import * as AccordionPrimitive from '@radix-ui/react-accordion'
import { ChevronDown } from 'lucide-react'
import { cn } from '@/lib/utils'
import { accordionVariants, accordionItemVariants, accordionTriggerVariants } from './accordion.variants'
type AccordionVariant = 'default' | 'separated' | 'ghost'
// The Vue source threads the chosen variant from <Accordion> down to each
// <AccordionItem>/<AccordionTrigger> via provide/inject. React's equivalent is
// context — the Root sets it, Item/Trigger read it (default 'default').
const AccordionVariantContext = React.createContext<AccordionVariant>('default')
const Accordion = React.forwardRef<
React.ElementRef<typeof AccordionPrimitive.Root>,
React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Root> & { variant?: AccordionVariant }
>(({ className, variant = 'default', ...props }, ref) => (
<AccordionVariantContext.Provider value={variant}>
<AccordionPrimitive.Root
ref={ref}
data-uipkge=""
data-slot="accordion"
data-variant={variant}
className={cn(accordionVariants({ variant }), className)}
{...props}
/>
</AccordionVariantContext.Provider>
))
Accordion.displayName = 'Accordion'
const AccordionItem = React.forwardRef<
React.ElementRef<typeof AccordionPrimitive.Item>,
React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Item>
>(({ className, ...props }, ref) => {
const variant = React.useContext(AccordionVariantContext)
return (
<AccordionPrimitive.Item
ref={ref}
data-uipkge=""
data-slot="accordion-item"
className={cn(accordionItemVariants({ variant }), className)}
{...props}
/>
)
})
AccordionItem.displayName = 'AccordionItem'
const AccordionHeader = React.forwardRef<
React.ElementRef<typeof AccordionPrimitive.Header>,
React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Header>
>(({ className, ...props }, ref) => (
<AccordionPrimitive.Header
ref={ref}
data-uipkge=""
data-slot="accordion-header"
className={cn('flex', className)}
{...props}
/>
))
AccordionHeader.displayName = 'AccordionHeader'
const AccordionTrigger = React.forwardRef<
React.ElementRef<typeof AccordionPrimitive.Trigger>,
React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Trigger>
>(({ className, children, ...props }, ref) => {
const variant = React.useContext(AccordionVariantContext)
// Do not wrap Header here — matches Vue/Reka and lets consumers use
// <AccordionHeader> for custom layouts (e.g. price outside the trigger).
return (
<AccordionPrimitive.Trigger
ref={ref}
data-uipkge=""
data-slot="accordion-trigger"
className={cn(accordionTriggerVariants({ variant }), className)}
{...props}
>
{children}
<ChevronDown
className="text-muted-foreground size-4 shrink-0 transition-transform duration-200 ease-[cubic-bezier(0.32,0.72,0,1)] group-data-[state=open]/accordion-trigger:rotate-180 motion-reduce:transition-none"
aria-hidden="true"
/>
</AccordionPrimitive.Trigger>
)
})
AccordionTrigger.displayName = 'AccordionTrigger'
const AccordionContent = React.forwardRef<
React.ElementRef<typeof AccordionPrimitive.Content>,
React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Content>
>(({ className, children, ...props }, ref) => (
<AccordionPrimitive.Content
ref={ref}
data-uipkge=""
data-slot="accordion-content"
className={cn(
'text-muted-foreground overflow-hidden text-sm',
// Height via Radix CSS vars + tw-animate-css (not height:auto).
// duration/ease set --tw-duration/--tw-ease consumed by the utility.
'data-[state=closed]:animate-accordion-up data-[state=open]:animate-accordion-down',
'duration-200 ease-[cubic-bezier(0.32,0.72,0,1)]',
'motion-reduce:animate-none',
className,
)}
{...props}
>
<div className="pt-0 pb-4">{children}</div>
</AccordionPrimitive.Content>
))
AccordionContent.displayName = 'AccordionContent'
export { Accordion, AccordionItem, AccordionHeader, AccordionTrigger, AccordionContent }
import type { VariantProps } from 'class-variance-authority'
import { cva } from 'class-variance-authority'
/**
* Variant definitions live in their own file (rather than the package
* `index.ts`) so consuming Vue SFCs can import without creating a circular
* dependency through the index. See card.variants.ts for the canonical
* example + the SSR symptom that motivated the split.
*/
export const accordionVariants = cva('w-full', {
variants: {
variant: {
// Bottom-border between items (the classic shadcn look).
default: '',
// Each item is its own bordered card with a small gap between them.
separated: 'space-y-2',
// Borderless. Use when the parent container already provides framing.
ghost: '',
},
},
defaultVariants: {
variant: 'default',
},
})
export const accordionItemVariants = cva('', {
variants: {
variant: {
default: 'border-b border-border last:border-b-0',
separated: 'rounded-md border border-border bg-card overflow-hidden',
ghost: '',
},
},
defaultVariants: {
variant: 'default',
},
})
export const accordionTriggerVariants = cva(
'group/accordion-trigger flex w-full items-center justify-between gap-3 text-sm font-medium text-foreground outline-none transition-colors focus-visible:ring-2 focus-visible:ring-ring/40 hover:text-primary disabled:pointer-events-none disabled:opacity-50',
{
variants: {
variant: {
default: 'py-4',
separated: 'px-4 py-3 hover:bg-muted/50',
ghost: 'py-3',
},
},
defaultVariants: {
variant: 'default',
},
},
)
export type AccordionVariants = VariantProps<typeof accordionVariants>
export type AccordionItemVariants = VariantProps<typeof accordionItemVariants>
export type AccordionTriggerVariants = VariantProps<typeof accordionTriggerVariants>
export { Accordion, AccordionItem, AccordionHeader, AccordionTrigger, AccordionContent } from './accordion'
// Re-export variant API from the sibling file (kept separate to avoid an
// import cycle and to give consumers a stable `accordionVariants` path).
export {
accordionVariants,
accordionItemVariants,
accordionTriggerVariants,
type AccordionVariants,
type AccordionItemVariants,
type AccordionTriggerVariants,
} from './accordion.variants'
Raw manifest:https://uipkge.dev/r/react/accordion.json