tailwindlabs / headlessui

Completely unstyled, fully accessible UI components, designed to integrate beautifully with Tailwind CSS.
https://headlessui.com
MIT License
25.81k stars 1.07k forks source link

transition attribute name collision when using framer motion #3424

Closed spacecat closed 1 month ago

spacecat commented 1 month ago

From the docs under disclosure there's this code example using framer-motion:

https://headlessui.com/react/disclosure#animating-with-framer-motion


<DisclosurePanel
                          static
                          as={motion.div}
                          initial={{ opacity: 0, y: -24 }}
                          animate={{ opacity: 1, y: 0 }}
                          exit={{ opacity: 0, y: -24 }}
                          transition={{ duration: 0.2, ease: easeOut }}
                          className="origin-top"
                        >
                          Yes! You can purchase a license that you can share
                          with your entire team.
                        </DisclosurePanel>

Notice the transition property.

In VSCode the TypeScript compiler will complain about the transition attribute with the following message:

Type '{ duration: number; ease: (t: number) => number; }' is not assignable to type 'boolean | undefined'.ts(2322)
disclosure.d.ts(39, 5): The expected type comes from property 'transition' which is declared here on type 'IntrinsicAttributes & CleanProps<ForwardRefComponent<HTMLDivElement, HTMLMotionProps<"div">>, "static" | ... 1 more ... | "transition"> & ... 4 more ... & { ...; }'
(property) transition?: boolean | undefined

I think this is because the DisclosurePanel has a transition API and so does the motion.div element - so there's a conflict here.

I don't know if there is a work-around, or if I'm missing something, or if this is indeed a miss in headless UI?

adamwathan commented 1 month ago

Hey good catch! The way to work around this is to use as={Fragment} so you can put the transition prop directly on motion.div:

 <DisclosurePanel static as={Fragment}>
  <motion.div
    initial={{ opacity: 0, y: -24 }}
    animate={{ opacity: 1, y: 0 }}
    exit={{ opacity: 0, y: -24 }}
    transition={{ duration: 0.2, ease: easeOut }}
    className="origin-top"
  >
    Yes! You can purchase a license that you can share with your entire team.
  </motion.div>
</DisclosurePanel>

Just pushed a change to that example in the docs, deploying now πŸ‘

spacecat commented 1 month ago

Argh, why didn't I think of that?! πŸ˜΅β€πŸ’«πŸ˜† Thanks, @adamwathan!