Tailwind UI in Svelte
Written — Updated
The Tailwind UI dynamic code is written using Alpine.js, so most of the integration effort goes into translating the Alpine.js code to Svelte.
Update: Since I wrote this the Tailwind UI site has changed to use informative comments instead of Alpine code samples. The Svelte techniques below still apply.
Conditional Showing of Elements 🔗
Alpine:
<div x-data="{open: true}">
<div x-show="open">...</div>
</div>
Svelte:
<script>
let open = true;
</script>
<div>
{#if open}
<div>...</div>
{/if}
</div>
Conditional Classes 🔗
Alpine:
<div :class="{'block': open, 'hidden': !open}">...</div>
Svelte:
<div class:block={open} class:hidden={!open}>...</div>
Event Handlers 🔗
Button Click 🔗
Alpine:
<button @click="open = !open">...</button>
Svelte:
<button on:click={() => open = !open}>...</button>
Click Outside 🔗
Alpine:
<div @click.away="open = false">
...
<div x-show={open}>...</div>
</div>
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>
Key Press 🔗
Alpine:
<div @keydown.window.escape="open = false">
...
</div>
Svelte:
<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
<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
<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>
Thanks for reading! If you have any questions or comments, please send me a note on Twitter. And if you enjoyed this, I also have a newsletter where I send out interesting things I read and the occasional nature photo.