base-ui icon indicating copy to clipboard operation
base-ui copied to clipboard

[tabs] How does it work with Motion?

Open subproject22 opened this issue 9 months ago • 8 comments

So I tried to use it similar way as Radix, but it's doesn't work

"use client"
import * as React from "react"
import { useState } from "react"
import { Tabs } from "@base-ui-components/react/tabs"
import { AnimatePresence, motion } from "motion/react"

export function NewTabsDemo() {
  const [active, setActive] = useState("one")
  return (
    <Tabs.Root value={active} onValueChange={setActive} defaultValue="one">
      <Tabs.List>
        <Tabs.Tab value="one">One</Tabs.Tab>
        <Tabs.Tab value="two">Two</Tabs.Tab>
        <Tabs.Tab value="three">Three</Tabs.Tab>
      </Tabs.List>

      <AnimatePresence mode="popLayout">
        {active === "one" && (
          <Tabs.Panel
            className="bg-red-500 p-12"
            value="one"
            keepMounted
            render={
              <motion.div
                initial={{
                  opacity: 0,
                  y: -100,
                }}
                animate={{
                  opacity: 1,
                  y: 0,
                }}
                exit={{
                  opacity: 0,
                  y: 100,
                }}
              />
            }
          >
            one
          </Tabs.Panel>
        )}

        {active === "two" && (
          <Tabs.Panel
            keepMounted
            className="bg-green-500 p-12"
            value="two"
            render={
              <motion.div
                initial={{
                  opacity: 0,
                  y: -100,
                }}
                animate={{
                  opacity: 1,
                  y: 0,
                }}
                exit={{
                  opacity: 0,
                  y: 100,
                }}
              />
            }
          >
            two
          </Tabs.Panel>
        )}

        {active === "three" && (
          <Tabs.Panel
            keepMounted
            className="bg-yellow-500 p-12"
            value="three"
            render={
              <motion.div
                initial={{
                  opacity: 0,
                  y: -100,
                }}
                animate={{
                  opacity: 1,
                  y: 0,
                }}
                exit={{
                  opacity: 0,
                  y: 100,
                }}
              />
            }
          >
            three
          </Tabs.Panel>
        )}
      </AnimatePresence>
    </Tabs.Root>
  )
}

I haven't found a single examples of usage with Motion.

subproject22 avatar Mar 01 '25 01:03 subproject22

Hi @subproject22, in our next release the docs will be updated with some Motion examples but this mainly pertains to popup-related components, not ones like Tabs.

You can read the updated docs here - we can investigate Motion animations with Tabs soon though

atomiks avatar Mar 01 '25 01:03 atomiks

hey @atomiks thanks for response. As for my example with Tabs, adding key helps, but still can't make exit animation work.

subproject22 avatar Mar 01 '25 01:03 subproject22

Related to https://github.com/mui/base-ui/issues/1486

mj12albert avatar Mar 03 '25 08:03 mj12albert

@subproject22 Just wondering if you've gotten Radix Tabs working well with motion, and if so are you able to share a sandbox or demo 🙏

mj12albert avatar Mar 03 '25 16:03 mj12albert

adding key helps, but still can't make exit animation work

I found that this is when the active state changes, the exiting panel is gone immediately despite keepMounted because they are rendered like: {value === 'one' && (<Panel />)}

mj12albert avatar Mar 28 '25 14:03 mj12albert

Also realized Panel uses the hidden attribute when not active, so Tailwind 4's reset could interfere with exit animations, since it applies:

[hidden]:where(:not([hidden='until-found'])) {
    display: none !important;
}

mj12albert avatar Mar 28 '25 14:03 mj12albert

@mj12albert – Is Tailwind 4's reset so weird? Seems like a good way to ensure that author CSS cannot accidentally unhide a hidden element with a high specificity selector. IMO the problem is that Motion should also use !important in its inline styles to ensure it beats any non-runtime styles.

benface avatar Mar 28 '25 14:03 benface

Is Tailwind 4's reset so weird?

I think it's a sensible default for this, it's just annoying because it seems the only way to opt-out of one thing is to fork the whole file 🙈

mj12albert avatar Mar 31 '25 05:03 mj12albert