DEV Community

loading...

Dynamic Tailwind UI Code with Svelte

Daniel Imfeld
I'm the cofounder and software/data lead at Carevoyance, creating new ways to explore and visualize complex data.
Originally published at imfeld.dev ・2 min read

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

Discussion (0)