Visit our GitHub
uikits

Accordion

A vertically stacked set of interactive headings that each reveal an associated section of content.

Installation

To install the Accordion follow these steps :

Install the themer package

Run the following command in your terminal:

Terminal
npm install @tailus/themer-accordion @radix-ui/react-accordion

Copy and paste

Copy and paste the following code in a .tsx file

Accordion.tsx
import * as AccordionPrimitive from "@radix-ui/react-accordion";
import { cn } from "lib/utils";
import React from "react";
import { ChevronDownIcon } from "@radix-ui/react-icons";
import {
  accordion as defaultTheme,
  outlinedVariant as outlinedTheme,
  elevatedVariant as elevatedTheme,
  ghostVariant as ghostTheme,
  softVariant as softTheme,
  outlinedElevatedVariant as outlinedElevatedTheme,
} from "@tailus/themer-accordion"
import { cva } from "class-variance-authority";

type Variant = "default" | "outlined" | "elevated" | "ghost" | "soft" | "outlinedElevated";
type ElementType = keyof typeof defaultTheme;

const variantTheme = (element: ElementType) => cva('', {
  variants: {
    variant: {
      default: defaultTheme[element],
      outlined: outlinedTheme[element],
      elevated: elevatedTheme[element],
      ghost: ghostTheme[element],
      soft: softTheme[element],
      outlinedElevated: outlinedElevatedTheme[element],
    }
  }
})

const defaultContextValue: Variant = "default";
const Context = React.createContext<Variant>(defaultContextValue);

interface AccordionRootProps {
  variant?: Variant;
}

const AccordionRoot = React.forwardRef<
  React.ElementRef<typeof AccordionPrimitive.Root>,
  React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Root> & AccordionRootProps
>(({className, variant, ...props}, forwardedRef) => {
  return (
    <Context.Provider value={variant || defaultContextValue}>
      <AccordionPrimitive.Root
        className={cn(variantTheme("root")({variant: variant}), className)}
        {...props}
        ref={forwardedRef}
      />
    </Context.Provider>
  )
});

const AccordionItem = React.forwardRef<
  React.ElementRef<typeof AccordionPrimitive.Item>,
  React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Item>
>(({className, ...props}, forwardedRef) => {
  const variant = React.useContext(Context);
  return (
    <AccordionPrimitive.Item
      className={cn(variantTheme("item")({variant: variant}), className)}
      {...props}
      ref={forwardedRef}
    />
  )
});

const AccordionTrigger = React.forwardRef<
  React.ElementRef<typeof AccordionPrimitive.Trigger>,
  React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Trigger>
>(({className, children, ...props}, forwardedRef) => {
  const generateTriggerClassNames = (theme: Variant) => {
    const themes = {
      default: defaultTheme,
      outlined: outlinedTheme,
      elevated: elevatedTheme,
      ghost: ghostTheme,
      soft: softTheme,
      outlinedElevated: outlinedElevatedTheme,
    }

    return {
      parent: themes[theme].trigger.parent,
      icon: themes[theme].trigger.icon,
      content: themes[theme].trigger.content,
    }
  }

  const variant = React.useContext(Context);
  const classNamesIcon = generateTriggerClassNames(variant).icon;
  const classNamesParent = generateTriggerClassNames(variant).parent;
  const classNamesContent = generateTriggerClassNames(variant).content;
  return (
    <AccordionPrimitive.Header className="flex">
      <AccordionPrimitive.Trigger
        className={cn(classNamesParent, className)}
        {...props}
        ref={forwardedRef}
      >
        <div className={classNamesContent}>
          {children}
        </div>
        <ChevronDownIcon className={cn(classNamesIcon)} aria-hidden={true}/>
      </AccordionPrimitive.Trigger>
    </AccordionPrimitive.Header>
  )
});

const AccordionContent = React.forwardRef<
  React.ElementRef<typeof AccordionPrimitive.Content>,
  React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Content>
>(({className, children, ...props}, forwardedRef) => {
  const variant = React.useContext(Context);
  return (
    <AccordionPrimitive.Content
      className={cn(variantTheme("content")({variant: variant}), className)}
      {...props}
      ref={forwardedRef}
    >
      <div className="pb-4">
        {children}
      </div>
    </AccordionPrimitive.Content>
  )
});

const Accordion = {
  Root: AccordionRoot,
  Item: AccordionItem,
  Trigger: AccordionTrigger,
  Content: AccordionContent
}

export default Accordion;


Update the configuration

Copy and paste the following code in your tailwind.config.js file

tailwind.config.js
/** @type {import('tailwindcss').Config} **/
import themer from "@tailus/themer";

module.exports = {
  content : [
      // ... your other paths here
      "./node_modules/@tailus/themer-**/dist/**/*.{js,ts}"
  ],
  theme: {
    extend: {
      colors: {
        // Add your custom colors here
      },
      keyframes: {
        slideDown: {
          from: { height: 0 },
          to: { height: 'var(--radix-accordion-content-height)' },
        },
        slideUp: {
          from: { height: 'var(--radix-accordion-content-height)' },
          to: { height: 0 },
        },
      },
      animation: {
        slideDown: 'slideDown 300ms cubic-bezier(0.87, 0, 0.13, 1)',
        slideUp: 'slideUp 300ms cubic-bezier(0.87, 0, 0.13, 1)',
      },
    },
  },
  plugins: [
    themer({
      // ... themer options
    })
  ],
};


Usage


Import the component

Import the Accordion component in a .tsx file

MyComponent.tsx
import Accordion from "tailus-ui/Accordion"

Start using

Copy and paste the following code in a .tsx file

MyComponent.tsx
import Accordion from "tailus-ui/Accordion";

const items = [
    // ... your questions and answers here
]

export const Default = () => {
    return (
        <Accordion.Root
            type="single"
            collapsible
            className="max-w-md w-full"
        >
            {
                items.map((item) => (
                    <Accordion.Item value={item.question} key={items.indexOf(item)}>
                        <Accordion.Trigger>
                            {item.question}
                        </Accordion.Trigger>
                        <Accordion.Content>
                            {item.answer}
                        </Accordion.Content>
                    </Accordion.Item>
                ))
            }
            
        </Accordion.Root>
    )
}


Reference

Root

The parent container of the Accordion component

Prop
Type
Default
variant
enum
default

Customization

To customize the Accordion, you can add new tailwindcss utilities to the className attribute, or do it using the themer plugin for more consistency.

Properties

The following properties can be customized using the themer plugin

  • rounded : The border radius of the Accordion.

Using the themer plugin

You can use the themer plugin to customize the Accordion component, to do so, add the following code to your tailwind.config.js file:

tailwind.config.js
import themer from "@tailus/themer";

module.exports = {
  // ... other tailwindcss config
    plugins: [
        themer({
            // ... preconfigs
            components:{
                accordion: {
                  rounded: "none",
                  softBg: "200",
                  ghostBg:"100",
                  shadow: {
                    size: "sm",
                    opacity: 5,
                  },
                  dark: {
                    softBg: "900",
                    ghostBg: "900",
                    elevatedBg : "900",
                  }
                }, 
            }
        })
    ]
}


Using the online component customizer

You can use the online component customizer to customize the Accordion component, go to the Overview section and click on the Customize button placed in the top right corner, Or press E

After customizing, copy and paste the generated code in your tailwind.config.js file.

Examples

Default


DefaultAccordion.tsx
import Accordion from "tailus-ui/Accordion";

const items = [
    // ... your questions and answers here
]

export const Default = () => {
    return (
        <Accordion.Root
            type="single"
            collapsible
            className="max-w-md w-full"
        >
            {
                items.map((item) => (
                    <Accordion.Item value={item.question} key={items.indexOf(item)}>
                        <Accordion.Trigger>
                            {item.question}
                        </Accordion.Trigger>
                        <Accordion.Content>
                            {item.answer}
                        </Accordion.Content>
                    </Accordion.Item>
                ))
            }
            
        </Accordion.Root>
    )
}

Outlined


OutlinedAccordion.tsx
import Accordion from "tailus-ui/Accordion";

const items = [
    ... your questions and answers here
]

export const Outlined = () => {
    return (
        <Accordion.Root
            type="single"
            collapsible
            variant="outlined"
            className="max-w-md w-full"
        >
            {
                items.map((item) => (
                    <Accordion.Item value={item.question} key={items.indexOf(item)}>
                        <Accordion.Trigger>
                            {item.question}
                        </Accordion.Trigger>
                        <Accordion.Content>
                            {item.answer}
                        </Accordion.Content>
                    </Accordion.Item>
                ))
            }
            
        </Accordion.Root>
    )
}

Elevated


ElevatedAccordion.tsx
import Accordion from "tailus-ui/Accordion";

const items = [
    // ... your questions and answers here
]

export const Elevated = () => {
    return (
        <Accordion.Root
            type="single"
            collapsible
            variant="elevated"
            className="max-w-md w-full"
        >
            {
                items.map((item) => (
                    <Accordion.Item value={item.question} key={items.indexOf(item)}>
                        <Accordion.Trigger>
                            {item.question}
                        </Accordion.Trigger>
                        <Accordion.Content>
                            {item.answer}
                        </Accordion.Content>
                    </Accordion.Item>
                ))
            }
        </Accordion.Root>
    )
}

Outlined Elevated


OutlinedElevatedAccordion.tsx
import Accordion from "tailus-ui/Accordion";

const items = [
    // ... your questions and answers here
]

export const OutlinedElevated = () => {
    return (
        <Accordion.Root
            type="single"
            collapsible
            variant="outlinedElevated"
            className="max-w-md w-full"
        >
            {
                items.map((item) => (
                    <Accordion.Item value={item.question} key={items.indexOf(item)}>
                        <Accordion.Trigger>
                            {item.question}
                        </Accordion.Trigger>
                        <Accordion.Content>
                            {item.answer}
                        </Accordion.Content>
                    </Accordion.Item>
                ))
            }
            
        </Accordion.Root>
    )
}

Ghost


GhostAccordion.tsx
import Accordion from "tailus-ui/Accordion";

const items = [
    // ... your questions and answers here
]

export const Ghost = () => {
    return (
        <Accordion.Root
            type="single"
            collapsible
            variant="ghost"
            className="max-w-md w-full"
        >
            {
                items.map((item) => (
                    <Accordion.Item value={item.question} key={items.indexOf(item)}>
                        <Accordion.Trigger>
                            {item.question}
                        </Accordion.Trigger>
                        <Accordion.Content>
                            {item.answer}
                        </Accordion.Content>
                    </Accordion.Item>
                ))
            }
            
        </Accordion.Root>
    )
}

Soft


SoftAccordion.tsx
import Accordion from "tailus-ui/Accordion";

const items = [
    // ... your questions and answers here
]

export const Soft = () => {
    return (
        <Accordion.Root
            type="single"
            collapsible
            variant="soft"
            className="max-w-md w-full"
        >
            {
                items.map((item) => (
                    <Accordion.Item value={item.question} key={items.indexOf(item)}>
                        <Accordion.Trigger>
                            {item.question}
                        </Accordion.Trigger>
                        <Accordion.Content>
                            {item.answer}
                        </Accordion.Content>
                    </Accordion.Item>
                ))
            }
            
        </Accordion.Root>
    )
}

Custom


Tailus Themer is a versatile theming library designed to help you create modern and custom web User Interfaces with Tailwind CSS.

CustomAccordion.tsx
import Accordion from "tailus-ui/Accordion";

const items = [
    // ... your questions and answers here
]

export const Custom = () => {
    return (
        <Accordion.Root
            type="single"
            collapsible
            className="max-w-md w-full"
        >
            {
                items.map((item) => (
                    <Accordion.Item value={item.question} key={items.indexOf(item)}>
                        <Accordion.Trigger>
                            <img className="size-8" src={item.icon} alt="tailus accordion img" loading="lazy" width={512} height={512} />
                            {item.question}
                        </Accordion.Trigger>
                        <Accordion.Content className="ml-12">
                            {item.answer}
                        </Accordion.Content>
                    </Accordion.Item>
                ))
            }
            
        </Accordion.Root>
    )
}