# Smooth accordion

> A question that opens smoothly to reveal its answer, with an arrow that turns as it expands.

- Category: [Microinteractions](https://library.aimran.es/components/microinteractions)
- Built with: React + Motion
- Live editor: https://library.aimran.es/blocks/smooth-accordion
- Library: AIMRAN Library (https://library.aimran.es), free, no sign-up, by Aimran Oukham

## Where to use it

Accordions keep FAQ pages and product details scannable: people read the questions and open only what they care about. Use it for “What does the booking include?” on a hotel site, shipping and returns in an online shop or the program of a course. The height animates instead of jumping, which makes this React accordion feel calm.

Write each question the way a customer would ask it, not as a heading like “Policies”. Keep answers to a short paragraph. If people usually need every answer, just show them on the page; hiding them only adds clicks.

## Code

### React (Motion)

```tsx
import { useState } from "react";
import { AnimatePresence, motion } from "framer-motion";
import { ChevronDown } from "lucide-react";

const spring = { type: "spring" as const, stiffness: 300, damping: 30 };

interface AccordionProps {
  title?: string;
}

export default function Accordion({ title = "What’s included in the booking?" }: AccordionProps) {
  const [open, setOpen] = useState(false);

  return (
    <div className="w-72 overflow-hidden rounded-[22px] bg-white dark:bg-[#1c1c1e] text-[#1d1d1f] dark:text-[#f5f5f7] shadow-[0_1px_2px_rgba(0,0,0,0.03),0_8px_28px_rgba(0,0,0,0.05)]">
      <button
        type="button"
        aria-expanded={open}
        onClick={() => setOpen((o) => !o)}
        className="flex w-full items-center justify-between p-5 text-left text-sm font-medium"
      >
        {title}
        <motion.span animate={{ rotate: open ? 180 : 0 }} transition={spring}>
          <ChevronDown className="h-4 w-4" />
        </motion.span>
      </button>
      <AnimatePresence initial={false}>
        {open && (
          <motion.div
            initial={{ height: 0, opacity: 0 }}
            animate={{ height: "auto", opacity: 1 }}
            exit={{ height: 0, opacity: 0 }}
            transition={spring}
            className="overflow-hidden"
          >
            <p className="border-t border-black/10 px-5 py-4 text-sm text-[#86868b] dark:border-white/10">
              Everything you need is included. You can change this answer from the controls.
            </p>
          </motion.div>
        )}
      </AnimatePresence>
    </div>
  );
}
```

## Prompt for AI assistants

Paste this into ChatGPT, Claude, Cursor, v0 or Lovable to adapt the block to your stack (Tailwind, Vue, Svelte…):

````text
I want to add the “Smooth accordion” block (Microinteractions) from AIMRAN Library to my project.

## What it is
A question that opens smoothly to reveal its answer, with an arrow that turns as it expands.

## Chosen settings
- Question: "What’s included in the booking?"

## What I need
1. Add it to my project with the same look and animation as the reference code: same sizes, colours, radii, shadows, timings and easing.
2. It is built with React + TypeScript, with framer-motion for the animation and lucide-react for the icons. If my project uses something else (Vue, Svelte, Astro, plain HTML…), translate it without losing any behaviour. If I use Tailwind, turn the styles into classes; if not, keep the CSS in its own file.
3. Expose the settings above as props (or CSS variables) with those values as defaults.
4. Make it work well on phones: nothing that depends only on hover, touch targets of at least 44 px and no overflow on 320 px screens.
5. Make it look right in light and dark mode.
6. Keep it accessible: semantic HTML, roles and aria-* where needed, visible focus and keyboard support.
7. Respect prefers-reduced-motion: with reduced motion, no big animations but the same end result.

## Reference code
```tsx
import { useState } from "react";
import { AnimatePresence, motion } from "framer-motion";
import { ChevronDown } from "lucide-react";

const spring = { type: "spring" as const, stiffness: 300, damping: 30 };

interface AccordionProps {
  title?: string;
}

export default function Accordion({ title = "What’s included in the booking?" }: AccordionProps) {
  const [open, setOpen] = useState(false);

  return (
    <div className="w-72 overflow-hidden rounded-[22px] bg-white dark:bg-[#1c1c1e] text-[#1d1d1f] dark:text-[#f5f5f7] shadow-[0_1px_2px_rgba(0,0,0,0.03),0_8px_28px_rgba(0,0,0,0.05)]">
      <button
        type="button"
        aria-expanded={open}
        onClick={() => setOpen((o) => !o)}
        className="flex w-full items-center justify-between p-5 text-left text-sm font-medium"
      >
        {title}
        <motion.span animate={{ rotate: open ? 180 : 0 }} transition={spring}>
          <ChevronDown className="h-4 w-4" />
        </motion.span>
      </button>
      <AnimatePresence initial={false}>
        {open && (
          <motion.div
            initial={{ height: 0, opacity: 0 }}
            animate={{ height: "auto", opacity: 1 }}
            exit={{ height: 0, opacity: 0 }}
            transition={spring}
            className="overflow-hidden"
          >
            <p className="border-t border-black/10 px-5 py-4 text-sm text-[#86868b] dark:border-white/10">
              Everything you need is included. You can change this answer from the controls.
            </p>
          </motion.div>
        )}
      </AnimatePresence>
    </div>
  );
}
```

Give me the complete final code, ready to paste, and tell me in one line where to put it in my project.
````
