DEV Community

Daniel Imfeld
Daniel Imfeld

Posted on • Originally published at imfeld.dev

Dynamic Tailwind UI Code with Svelte

Some components in Tailwind UI have dynamic functionality that requires Javascript to implement. The original dynamic code for Tailwind UI was written using Alpine.js, and I put together this list of how to implement the techniques in Svelte back in March 2020.

Tailwind UI has since changed to use informative comments instead of Alpine code samples, but the Svelte techniques below still apply.

Conditional Showing of Elements

Alpine:

<div x-data="{open: true}">
    <div x-show="open">...</div>
</div>
Enter fullscreen mode Exit fullscreen mode

Svelte:

<script>
  let open = true;
</script>

<div>
    {#if open}
        <div>...</div>
    {/if}
</div>

Enter fullscreen mode Exit fullscreen mode

Conditional Classes

Alpine:

<div :class="{'block': open, 'hidden': !open}">...</div>
Enter fullscreen mode Exit fullscreen mode

Svelte:

<div class:block={open} class:hidden={!open}>...</div>
Enter fullscreen mode Exit fullscreen mode

Event Handlers

Button Click

Alpine:

<button @click="open = !open">...</button>
Enter fullscreen mode Exit fullscreen mode

Svelte:

<button on:click={() => open = !open}>...</button>
Enter fullscreen mode Exit fullscreen mode

Click Outside

Alpine:

<div @click.away="open = false">
    ...
    <div x-show={open}>...</div>
</div>
Enter fullscreen mode Exit fullscreen mode

Svelte (REPL):


<script>
function clickOutside(node, { enabled: initialEnabled, cb }) {
    const handleOutsideClick = ({ target }) => {
      if (!node.contains(target)) {
        cb();
      }
    };

    function update({enabled}) {
      if (enabled) {
        window.addEventListener('click', handleOutsideClick);
      } else {
        window.removeEventListener('click', handleOutsideClick);
      }
    }

    update(initialEnabled);
    return {
      update,
      destroy() {
        window.removeEventListener( 'click', handleOutsideClick );
      }
    };
  }

  let open = true;
</script>

<div use:clickOutside={{ enabled: open, cb: () => open = false }}>
   <button>...</button>
   {#if open}
    <div>
      ...
    </div>
  {/if}
</div>

Enter fullscreen mode Exit fullscreen mode

Key Press

Alpine:

<div @keydown.window.escape="open = false">
    ...
</div>
Enter fullscreen mode Exit fullscreen mode

Svelte:

<script>
    function handleEscape({key}) {
        if (key === 'Escape') {
            open = false;
        }
    }
</script>

<svelte:window on:keyup={handleEscape} />

{#if open}
<div>...</div>
{/if}
Enter fullscreen mode Exit fullscreen mode

This could also be done with a use: action similar to the Click Outside example.

Transitions

Alpine

<div
    x-transition:enter="transition ease-out duration-100"
    x-transition:enter-start="transform opacity-0 scale-95"
    x-transition:enter-end="transform opacity-100 scale-100"
    x-transition:leave="transition ease-in duration-75"
    x-transition:leave-start="transform opacity-100 scale-100"
    x-transition:leave-end="transform opacity-0 scale-95">
 ...
</div>
Enter fullscreen mode Exit fullscreen mode

Svelte

<script>
  import { scale } from 'svelte/transition';
  import { cubicIn, cubicOut } from 'svelte/easing';
</script>

<div in:scale={{ duration: 100, start: 0.95, easing: cubicOut }}
     out:scale={{ duration: 75, start: 0.95, easing: cubicIn }}>
  Start is the scale value divided by 100.
</div>
Enter fullscreen mode Exit fullscreen mode

Top comments (2)

Collapse
 
shinokada profile image
shin • Edited

Excellent! This is what I was looking for. Thank you for the post.
Do you know how to change this to Svelte?

:class="{'rotate-180': open, 'rotate-0': !open}"
Enter fullscreen mode Exit fullscreen mode
Collapse
 
dimfeld profile image
Daniel Imfeld • Edited

Glad you liked it! You might also want to check out github.com/rgossiaux/svelte-headle... which is a Svelte port of Tailwind's "Headless UI" component library. I haven't used it myself yet but it looks promising.

And for your question, Svelte's equivalent is this:

<div class:rotate-180={open} class:rotate-0={!open}>
Enter fullscreen mode Exit fullscreen mode