Headless UI Transitions

Heya, I'm trying to create a simple little animation where a dropdown menu slides down when revealed (similar to an accordion), but I can't seem to get it to properly work.
<div className='absolute w-full max-h-32 z-50'>
<Transition
enter="transition-all duration-150"
enterFrom="max-h-0"
enterTo="max-h-32"
leave="transition-all duration-150"
leaveFrom="h-80"
leaveTo="h-0"
>

<Listbox.Options className="mt-2 border-2 max-h-64 border-white z-50 w-full overflow-auto rounded-md bg-gray-950 text-white">
{yearChoiceLoad()}
</Listbox.Options>

</Transition>
</div>
<div className='absolute w-full max-h-32 z-50'>
<Transition
enter="transition-all duration-150"
enterFrom="max-h-0"
enterTo="max-h-32"
leave="transition-all duration-150"
leaveFrom="h-80"
leaveTo="h-0"
>

<Listbox.Options className="mt-2 border-2 max-h-64 border-white z-50 w-full overflow-auto rounded-md bg-gray-950 text-white">
{yearChoiceLoad()}
</Listbox.Options>

</Transition>
</div>
At the moment it just pops into/out of view after a short delay. Is there an easy way to solve this? Or a better way to do this (without Headless UI). Thanks!
Solution:
I ended up switching over to Framer Motion; ```tsx <Listbox> {({ open }) => ( ...
Jump to solution
5 Replies
JulieCezar
JulieCezar17mo ago
Here is a simple example
const [open, setOpen] = useState(false);

return (
<div className="w-full bg-gray-50">
<div
onClick={() => setOpen((prev) => !prev)}
className="w-full bg-gray-100"
>
Title
</div>

<div
className={`h-80 w-full bg-teal-200 transition-all duration-200 ${
open ? "" : "h-0"
}`}
></div>
</div>
)
const [open, setOpen] = useState(false);

return (
<div className="w-full bg-gray-50">
<div
onClick={() => setOpen((prev) => !prev)}
className="w-full bg-gray-100"
>
Title
</div>

<div
className={`h-80 w-full bg-teal-200 transition-all duration-200 ${
open ? "" : "h-0"
}`}
></div>
</div>
)
Solution
General Mudkipp
General Mudkipp17mo ago
I ended up switching over to Framer Motion;
<Listbox>
{({ open }) => (

{/* ... */}

<AnimatePresence>
{ open && (
<motion.div
initial={{ height: 0 }}
animate={{ height:"16rem" }}
exit={{ height: 0 }}
transition={{ duration: 0.2 }}
className="absolute w-full z-50"
>
<Listbox.Options static className="max-h-64 mt-2 border-2 h-full border-white overflow-auto z-50 w-full rounded-md bg-gray-950 text-white">
{yearChoiceLoad()}
</Listbox.Options>
</motion.div>
)}
</AnimatePresence>

{/* ... */}

)}
</Listbox>
<Listbox>
{({ open }) => (

{/* ... */}

<AnimatePresence>
{ open && (
<motion.div
initial={{ height: 0 }}
animate={{ height:"16rem" }}
exit={{ height: 0 }}
transition={{ duration: 0.2 }}
className="absolute w-full z-50"
>
<Listbox.Options static className="max-h-64 mt-2 border-2 h-full border-white overflow-auto z-50 w-full rounded-md bg-gray-950 text-white">
{yearChoiceLoad()}
</Listbox.Options>
</motion.div>
)}
</AnimatePresence>

{/* ... */}

)}
</Listbox>
Works pretty much perfectly.
JacobParis
JacobParis17mo ago
one of the biggest gotchas with HeadlessUI's transition component is that classes in the enter, enterFrom, etc aren't detected by tailwind by default
General Mudkipp
General Mudkipp17mo ago
iirc The issue I had was to do with the way that the Transition component was creating a parent div (?) to animate it, which my child element wasn't liking Might have also been to do with the max-height, but then again there were a lot of problems with that initial code lol
JacobParis
JacobParis17mo ago
It does create a parent div, though you do have the option to pass <Transition as={React.Fragment}
Want results from more Discord servers?
Add your server