Dynamic Tailwind UI Code with Svelte
Tags: svelte, tailwindcss
Published: 2020-07-24 02:06:35

Some components in Tailwind UI have dynamic functionality that requires Javascript to implement. The original dynamic code for [Tailwind UI](https://tailwindui.com) was written using [Alpine.js](https://github.com/alpinejs/alpine/), 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:
```html
<div x-data="{open: true}">
    <div x-show="open">...</div>
</div>
```

Svelte:
```html
<script>
  let open = true;
</script>

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

```

# Conditional Classes

Alpine:
```html
<div :class="{'block': open, 'hidden': !open}">...</div>
```

Svelte:
```html
<div class:block={open} class:hidden={!open}>...</div>
```

# Event Handlers

## Button Click

Alpine:
```html
<button @click="open = !open">...</button>
```

Svelte:
```html
<button on:click={() => open = !open}>...</button>
```

## Click Outside

Alpine:
```html
<div @click.away="open = false">
    ...
    <div x-show={open}>...</div>
</div>
```

Svelte ([REPL](https://svelte.dev/repl/dae848c2157e48ab932106779960f5d5?version=3.19.2)):
```html

<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>

```

## Key Press

Alpine:
```html
<div @keydown.window.escape="open = false">
    ...
</div>
```

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

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

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

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

# Transitions

Alpine
```html
<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>
```

Svelte
```html
<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>
```