animationtailwind-csssvelte

animate tailwind tabs component


I created a simple tabs component using tailwind and svelte, like the following one:

tab component

you can see it working on this repl

I'd like to animate the transition from one option to the other

Similar to what happens in this page: https://www.shadcn-svelte.com/examples/dashboard

tab component animated

How could I achieve such animation, either using tailwind classes or svelte directives?


Solution

  • One approach is to draw an element under the tabs and move that around, the movement then can be animated using a transition.

    E.g.

    let buttons = [];
    $: ({ left, top, width, height } = (() => {
        const button = buttons[options.findIndex(o => o == value)];
        if (button == null)
            return { }; // Not mounted yet
    
        return button.getBoundingClientRect();
    })())
    
    <div class="... isolate"> <!-- Prevent z-index leakage -->
        <div
            class="absolute bg-white rounded-md transition-all"
            style="
                left: {left}px;
                top: {top}px;
                width: {width}px;
                height: {height}px;
            "></div>
        {#each options as option, i}
            <button
                bind:this={buttons[i]}
                on:click={() => (value = option)}
                class="z-10 ..."
                >{option}</button
            >
        {/each}
    </div>
    

    REPL

    The shadcn-svelte site uses a Svelte crossfade transition:

    import { cubicInOut } from "svelte/easing";
    import { crossfade } from "svelte/transition";
    
    const [send, receive] = crossfade({
        duration: 250,
        easing: cubicInOut,
    });
    
    {#if isActive}
        <div
            class="bg-muted absolute inset-0 rounded-full"
            in:send={{ key: "activetab" }}
            out:receive={{ key: "activetab" }}
        />
    {/if}
    

    Crossfades have issues with transparency (overlaying two partially transparent elements does not yield a fully opaque area), but even then it probably would not be very visible for small elements like this.

    REPL