moduix-react
itemaccordionsource: ok · refreshed
Accordion
moduix React Accordion component.
npx shadcn add "https://moduix.dev/r/react/accordion.json"free
Interaction
accordion
Tags auto-generated by rules on 9/24/2026 — empty means no evidence, not lack of support.
Dependencies
Bundle weight · lightui-primitives@ark-ui/react
Registry dependencies
@moduix-react/foundation@moduix-react/icons
Files3 · source cached 10/8/2026, 8:49:53 AM
src/components/accordion/Accordion.tsx
'use client';
import {
Accordion as AccordionPrimitive,
useAccordion,
useAccordionContext,
useAccordionItemContext,
} from '@ark-ui/react/accordion';
import type { HTMLArkProps } from '@ark-ui/react/factory';
import { ark } from '@ark-ui/react/factory';
import { clsx } from 'clsx';
import type { ComponentProps, ComponentRef } from 'react';
import { forwardRef } from 'react';
import { PlusIcon } from '@/lib/moduix/icons/ui';
import styles from './Accordion.module.css';
const Accordion = forwardRef<
ComponentRef<typeof AccordionPrimitive.Root>,
ComponentProps<typeof AccordionPrimitive.Root>
>(function Accordion({ className, ...props }, ref) {
return (
<AccordionPrimitive.Root
ref={ref}
className={clsx(styles.root, className)}
{...props}
data-slot="accordion-root"
/>
);
});
const AccordionRootProvider = forwardRef<
ComponentRef<typeof AccordionPrimitive.RootProvider>,
ComponentProps<typeof AccordionPrimitive.RootProvider>
>(function AccordionRootProvider({ className, ...props }, ref) {
return (
<AccordionPrimitive.RootProvider
ref={ref}
className={clsx(styles.root, className)}
{...props}
data-slot="accordion-root-provider"
/>
);
});
const AccordionItem = forwardRef<
ComponentRef<typeof AccordionPrimitive.Item>,
ComponentProps<typeof AccordionPrimitive.Item>
>(function AccordionItem({ className, ...props }, ref) {
return (
<AccordionPrimitive.Item
ref={ref}
className={clsx(styles.item, className)}
{...props}
data-slot="accordion-item"
/>
);
});
const AccordionItemTrigger = forwardRef<
ComponentRef<typeof AccordionPrimitive.ItemTrigger>,
ComponentProps<typeof AccordionPrimitive.ItemTrigger>
>(function AccordionItemTrigger({ className, ...props }, ref) {
return (
<AccordionPrimitive.ItemTrigger
ref={ref}
className={clsx(styles.itemTrigger, className)}
{...props}
data-slot="accordion-item-trigger"
/>
);
});
const AccordionItemIndicator = forwardRef<
ComponentRef<typeof AccordionPrimitive.ItemIndicator>,
ComponentProps<typeof AccordionPrimitive.ItemIndicator>
>(function AccordionItemIndicator({ className, children, ...props }, ref) {
return (
<AccordionPrimitive.ItemIndicator
ref={ref}
className={clsx(styles.itemIndicator, className)}
{...props}
data-slot="accordion-item-indicator"
>
{children ?? <PlusIcon />}
</AccordionPrimitive.ItemIndicator>
);
});
const AccordionItemContent = forwardRef<
ComponentRef<typeof AccordionPrimitive.ItemContent>,
ComponentProps<typeof AccordionPrimitive.ItemContent>
>(function AccordionItemContent({ className, ...props }, ref) {
return (
<AccordionPrimitive.ItemContent
ref={ref}
className={clsx(styles.itemContent, className)}
{...props}
data-slot="accordion-item-content"
/>
);
});
const AccordionItemBody = forwardRef<ComponentRef<typeof ark.div>, HTMLArkProps<'div'>>(
function AccordionItemBody({ className, ...props }, ref) {
return (
<ark.div
ref={ref}
className={clsx(styles.itemBody, className)}
{...props}
data-scope="accordion"
data-part="item-body"
data-slot="accordion-item-body"
/>
);
},
);
const AccordionContext = AccordionPrimitive.Context;
const AccordionItemContext = AccordionPrimitive.ItemContext;
export {
Accordion,
AccordionContext,
AccordionItem,
AccordionItemBody,
AccordionItemContent,
AccordionItemContext,
AccordionItemIndicator,
AccordionItemTrigger,
AccordionRootProvider,
useAccordion,
useAccordionContext,
useAccordionItemContext,
};src/components/accordion/Accordion.module.css
@layer moduix.components {
@keyframes accordion-item-content-open {
from {
height: 0;
opacity: var(--moduix-accordion-item-content-closed-opacity, 0.01);
}
to {
height: var(--height);
opacity: var(--moduix-accordion-item-content-open-opacity, 1);
}
}
@keyframes accordion-item-content-closed {
from {
height: var(--height);
opacity: var(--moduix-accordion-item-content-open-opacity, 1);
}
to {
height: 0;
opacity: var(--moduix-accordion-item-content-closed-opacity, 0.01);
}
}
@keyframes accordion-item-content-open-horizontal {
from {
width: 0;
opacity: var(--moduix-accordion-item-content-closed-opacity, 0.01);
}
to {
width: var(--width);
opacity: var(--moduix-accordion-item-content-open-opacity, 1);
}
}
@keyframes accordion-item-content-closed-horizontal {
from {
width: var(--width);
opacity: var(--moduix-accordion-item-content-open-opacity, 1);
}
to {
width: 0;
opacity: var(--moduix-accordion-item-content-closed-opacity, 0.01);
}
}
.root {
box-sizing: border-box;
display: flex;
width: var(--moduix-accordion-width, 100%);
max-width: var(--moduix-accordion-max-width, 100%);
min-width: 0;
flex-direction: column;
color: var(--moduix-accordion-color, var(--moduix-color-foreground));
&[data-orientation='horizontal'] {
width: var(--moduix-accordion-horizontal-width, auto);
height: var(--moduix-accordion-horizontal-height, 20rem);
max-height: var(--moduix-accordion-horizontal-max-height, 100%);
flex-direction: row;
}
}
.item {
overflow-anchor: none;
&[data-orientation='vertical']:not(:last-child) {
border-bottom: var(--moduix-accordion-item-border-width, var(--moduix-border-width-sm)) solid
var(--moduix-accordion-item-border-color, var(--moduix-color-border));
}
&[data-orientation='horizontal'] {
display: flex;
&:not(:last-child) {
border-inline-end: var(--moduix-accordion-item-border-width, var(--moduix-border-width-sm))
solid var(--moduix-accordion-item-border-color, var(--moduix-color-border));
}
}
}
.itemTrigger {
box-sizing: border-box;
display: flex;
width: 100%;
align-items: center;
justify-content: space-between;
gap: var(--moduix-accordion-trigger-gap, var(--moduix-spacing-4));
margin: 0;
padding-block: var(--moduix-accordion-trigger-padding-y, var(--moduix-spacing-2));
padding-inline: var(--moduix-accordion-trigger-padding-x, var(--moduix-spacing-3));
border: 0;
background-color: var(--moduix-accordion-trigger-bg, var(--moduix-color-muted));
color: inherit;
font: inherit;
font-size: var(--moduix-accordion-trigger-font-size, var(--moduix-text-md));
line-height: var(--moduix-accordion-trigger-line-height, var(--moduix-line-height-text-md));
text-align: start;
user-select: none;
outline: 0;
cursor: pointer;
transition:
background-color var(--moduix-transition-default),
color var(--moduix-transition-default);
&:not(:is([disabled], [data-disabled])) {
@media (hover: hover) {
&:hover {
background-color: var(--moduix-accordion-trigger-bg-hover, var(--moduix-color-accent));
}
}
&:active {
background-color: var(
--moduix-accordion-trigger-bg-active,
var(--moduix-accordion-trigger-bg-hover, var(--moduix-color-accent))
);
}
}
&:focus-visible {
position: relative;
z-index: 1;
outline: var(
--moduix-accordion-focus-ring-width,
var(--moduix-focus-ring-width, var(--moduix-border-width-md))
)
solid var(--moduix-accordion-focus-ring-color, var(--moduix-color-ring));
outline-offset: calc(
var(--moduix-accordion-focus-ring-offset, var(--moduix-border-width-sm)) * -1
);
}
&:is([disabled], [data-disabled]) {
cursor: default;
opacity: var(--moduix-accordion-disabled-opacity, var(--moduix-opacity-disabled));
}
&[data-orientation='horizontal'] {
width: var(--moduix-accordion-horizontal-trigger-width, 2.5rem);
height: 100%;
justify-content: center;
writing-mode: sideways-lr;
}
}
.itemIndicator {
box-sizing: border-box;
display: inline-flex;
align-items: center;
justify-content: center;
width: var(--moduix-accordion-icon-size, var(--moduix-spacing-3));
height: var(--moduix-accordion-icon-size, var(--moduix-spacing-3));
flex-shrink: 0;
transition: transform var(--moduix-accordion-icon-transition, var(--moduix-transition-default));
& svg {
width: 100%;
height: 100%;
}
&[data-state='open'] {
transform: var(--moduix-accordion-icon-open-transform, rotate(45deg) scale(1.1));
}
}
.itemContent {
box-sizing: border-box;
overflow: hidden;
color: var(--moduix-accordion-item-content-color, var(--moduix-color-muted-foreground));
font-size: var(--moduix-accordion-item-content-font-size, var(--moduix-text-md));
line-height: var(
--moduix-accordion-item-content-line-height,
var(--moduix-line-height-text-md)
);
&[hidden]:not([hidden='until-found']) {
display: none;
}
&[data-state='open'][data-orientation='vertical'] {
animation: accordion-item-content-open
var(--moduix-accordion-item-content-transition, var(--moduix-transition-default));
}
&[data-state='closed'][data-orientation='vertical'] {
animation: accordion-item-content-closed
var(--moduix-accordion-item-content-transition, var(--moduix-transition-default));
}
&[data-state='open'][data-orientation='horizontal'] {
animation: accordion-item-content-open-horizontal
var(--moduix-accordion-item-content-transition, var(--moduix-transition-default));
}
&[data-state='closed'][data-orientation='horizontal'] {
animation: accordion-item-content-closed-horizontal
var(--moduix-accordion-item-content-transition, var(--moduix-transition-default));
}
&[data-orientation='horizontal'] > * {
width: var(--moduix-accordion-horizontal-content-width, 16rem);
}
}
.itemBody {
display: grid;
gap: var(--moduix-accordion-item-body-gap, var(--moduix-spacing-3));
min-width: 0;
padding: var(--moduix-accordion-item-body-padding, var(--moduix-spacing-3));
}
@media (prefers-reduced-motion: reduce) {
.itemTrigger,
.itemIndicator {
transition: none;
}
.itemContent[data-state][data-orientation] {
animation-duration: 1ms;
animation-delay: 0ms;
}
}
}src/components/accordion/index.ts
export * from './Accordion.js';