Tempa UI

Accordion

Expandable content panels for FAQs and documentation.

Code

components/Accordion.astro
    
      ---interface AccordionItem {  title: string;  content: string;} interface Props {  items: AccordionItem[];  class?: string;} const {  items,  class: className = "",} = Astro.props;--- <div class:list={["accordion divide-y divide-black-onyx/10 border border-black-onyx/10 rounded overflow-hidden", className]}>  {items.map((item, i) => (    <div class="accordion-item">      <button        class="accordion-header flex items-center justify-between w-full px-4 py-3 text-sm font-medium text-black-onyx hover:bg-black-onyx/5 transition-colors cursor-pointer text-left"        id={`accordion-header-${i}`}        aria-expanded={i === 0}        aria-controls={`accordion-panel-${i}`}      >        {item.title}        <svg aria-hidden="true" class="accordion-arrow shrink-0 transition-transform duration-300" xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" viewBox="0 0 256 256">          <path d="M213.66,101.66l-80,80a8,8,0,0,1-11.32,0l-80-80a8,8,0,0,1,11.32-11.32L128,165.05l74.34-74.35a8,8,0,0,1,11.32,11.32Z"></path>        </svg>      </button>      <div        id={`accordion-panel-${i}`}        role="region"        aria-labelledby={`accordion-header-${i}`}        class="accordion-panel overflow-hidden transition-all duration-300 ease-in-out"        style={i === 0 ? `max-height: ${items[i].content.length * 2}px; opacity: 1;` : "max-height: 0; opacity: 0;"}      >        <div class="px-4 py-3 text-sm text-black-onyx/80">          {item.content}        </div>      </div>    </div>  ))}</div> <script>  document.querySelectorAll(".accordion").forEach((accordion) => {    const panels = accordion.querySelectorAll(".accordion-panel");    const headers = accordion.querySelectorAll(".accordion-header");    const arrows = accordion.querySelectorAll(".accordion-arrow");     headers.forEach((header, i) => {      const panel = panels[i];      const inner = panel.querySelector("div");       function togglePanel(open) {        if (open) {          panel.style.maxHeight = inner.scrollHeight + 24 + "px";          panel.style.opacity = "1";          header.setAttribute("aria-expanded", "true");          arrows[i]?.classList.add("rotate-180");        } else {          panel.style.maxHeight = "0";          panel.style.opacity = "0";          header.setAttribute("aria-expanded", "false");          arrows[i]?.classList.remove("rotate-180");        }      }       header.addEventListener("click", () => {        const isOpen = panel.style.opacity === "1";         // Close all panels        panels.forEach((p, j) => {          p.style.maxHeight = "0";          p.style.opacity = "0";          headers[j].setAttribute("aria-expanded", "false");          arrows[j]?.classList.remove("rotate-180");        });         // Open clicked panel if it was closed        if (!isOpen) togglePanel(true);      });       header.addEventListener("keydown", (e) => {        if (e.key === "Enter" || e.key === " ") {          e.preventDefault();          header.click();        }      });    });  });</script> 
    
  

Preview

Default Accordion

Install the package using npm, yarn, or pnpm. Then import the components you need into your project.
Override CSS variables to match your brand. All components use CSS custom properties for colors, spacing, and typography.
Each component exposes a set of props for customization. Check the documentation page for each component to see the full API.

Usage

    
      <Accordion  items={[    { title: "Getting Started", content: "Install the package..." },    { title: "Customization", content: "Override CSS variables..." },    { title: "API Reference", content: "Full component API..." },  ]}/>
    
  

Props

Prop Type Default Description
items AccordionItem[] Array of objects with title and content strings.
class string Additional CSS classes.