I've been actively involved in the frontend development of the new AirSwap Member Dashboard app. AirSwap currently leverages a platform named Activate for user stake management. However, within a month, Activate will be phased out, prompting the development of a new app for $AST token stakers. A notable feature of this app is the Accordion component, which I'll discuss here, illustrating its creation with React, Radix, and TailwindCSS.
Radix-UI is an open-source library boasting ready-to-use components. These components are functional out-of-the-box but come unstyled, allowing for greater design flexibility. Radix effectively handles the underlying JavaScript, freeing you to focus on styling. If you’re without design guidelines, Radix's documentation provides some style suggestions.
You can install the Radix-UI accordion into your project with:
npm install @radix-ui/react-accordion
# or
yarn add @radix-ui/react-accordion
The documentation has many more components you can use.
Start by importing the Accordion:
import * as Accordion from '@radix-ui/react-accordion';
The Accordion has various segments, including Root, Item, Header, Trigger, and Content. For an in-depth understanding of these, refer to the Radix-UI Radix-UI documentation.
The Accordion supports numerous props for enhanced control. A notable one is the type
prop, which allows selections of either "single"
or "multiple"
, determining if one or multiple accordion items can be open simultaneously. For a comprehensive list of props, the Radix-UI docs have you covered. Below is my implementation of the component, with a brief explanation thereafter::
import * as RadixAccordion from '@radix-ui/react-accordion';
import { ReactNode, useState } from "react";
import { MdChevronRight } from "react-icons/md";
interface AccordionProps {
rootStyles: string;
type?: "single" | "multiple";
itemId: string;
trigger: ReactNode;
content: ReactNode;
}
export const Accordion = ({
rootStyles,
type = "multiple",
itemId,
trigger,
content,
}: AccordionProps) => {
const [isAccordionOpen, setIsAccordionOpen] = useState<boolean>(false);
const toggleAccordion = () => setIsAccordionOpen((isAccordionOpen) => !isAccordionOpen);
return (
<RadixAccordion.Root className={rootStyles} type={type}>
<RadixAccordion.Item value={itemId} className="flex flex-col overflow-hidden">
<RadixAccordion.Header>
<div className="flex border p-2">
{trigger}
<RadixAccordion.Trigger />
<div onClick={toggleAccordion}>
{!isAccordionOpen ? (
<MdChevronRight size={32} className="rotate-90 transition-transform duration-150"
/>
) : (
<MdChevronRight size={32} className="-rotate-90 transition-transform duration-150"
/>
)}
</div>
</RadixAccordion.Trigger>
</div>
</RadixAccordion.Header>
<RadixAccordion.Content>{content}</RadixAccordion.Content>
</RadixAccordion.Item>
</RadixAccordion.Root>
);
};
This interface encapsulates a set of props, catering to scenarios where the Accordion component might be invoked multiple times with varied data.
rootStyles
: Directs styling for the Root component, accepting TailwindCSS classes.
type
: Specifies if the accordion operates in “single” or “multiple” mode.
itemId
: A unique identifier for the Item.
trigger
: It's positioned outside of the <RadixAccordion.Trigger />
to ensure only the MdChevronRight icon
, when clicked, initiates the accordion's action.
content
: A ReactNode
type defining the content within <RadixAccordion.Content>
nodes.
Clicking the arrow icon (MdChevronRight
) toggles the accordion's state. The isAccordionOpen
state starts as false
, with the accordion defaulting to a closed position.
The Accordion is intentionally kept versatile, with trigger
and content
props as ReactNode
, facilitating potential reuse. Avoiding excessive inline styling makes the component more maintainable.
My experience with Radix-UI was enlightening, and I foresee further engagements. While I've worked with component libraries like Chakra-UI that offer pre-styled components, the customization process can be daunting, especially with specific brand guidelines in play.
Feel free to share your thoughts or questions regarding my Accordion component implementation below!
GitHub: https://github.com/airswap/airswap-voter-rewards/blob/main/src/features/common/Accordion.tsx.