Skip to content

Tailwind hover effects and UI blocks for Svelte

Effects convert to a Svelte component with scoped styles. Blocks and pages give you their markup as a .svelte file, without the React state.

  • Svelte 5
  • SvelteKit
  • Vite

An effect, in Svelte

This is the Gradient Shift Button effect, run through the converter when this page was built — not a sample of what the output is meant to look like. All 1,126 effects convert the same way, because every one of them is markup plus a stylesheet.

BtnGradient.svelte
<!--
  Gradient Shift Button — A button whose multi-color gradient slides across on hover, paired with a soft drop shadow that intensifies.
  Generated by Hoverlab from the "btn-gradient" effect.
-->
<button class="fx-btn-gradient">Get Started</button>

<style>
.fx-btn-gradient {
  position: relative;
  padding: 0.75rem 1.75rem;
  border: none;
  border-radius: 0.625rem;
  font-weight: 600;
  font-size: 0.95rem;
  color: #fff;
  cursor: pointer;
  background: linear-gradient(120deg, #f43f5e, #f59e0b, #10b981, #6366f1);
  background-size: 300% 300%;
  background-position: 0% 50%;
  transition: background-position 0.6s ease, transform 0.2s ease, box-shadow 0.3s ease;
  box-shadow: 0 6px 18px -6px rgba(244, 63, 94, 0.5);
}
.fx-btn-gradient:hover {
  background-position: 100% 50%;
  transform: translateY(-2px);
  box-shadow: 0 12px 28px -8px rgba(99, 102, 241, 0.55);
}
.fx-btn-gradient:active { transform: translateY(0); }
</style>

What to know before you paste it

  • Svelte scopes component styles and prunes selectors it cannot statically match. If the compiler reports an unused selector, wrap it in :global(...) — that is expected for rules targeting pseudo-elements on dynamically added markup.

A block, as a Svelte file

This is where the honest answer is different, so here it is with the words that make it different. 433 blocks and pages are React components with state and handlers. What you get in Svelte is the block rendered to markup and wrapped as a file your framework compiles — a real presentational component, carrying the layout, the Tailwind classes and the accessible structure, and not a port of the React logic.

The sample below is Magic Link Sign In, and it is an interactive one, chosen so you can see exactly what is missing rather than a case where nothing is. The caveat is printed at the top of the file itself, because the file is what gets read six months later.

auth-magic-link-form.svelte
<!--
  Magic Link Sign In — markup from the Hoverlab catalog.

  This is the block rendered once to HTML and wrapped as a component
  file. It is not a port of the React source: the Tailwind classes carry
  the design, which is the part that took the work, and they are the same
  in every framework.

  This block is interactive in React and the handlers are NOT here.
  Buttons, toggles and menus render in their initial state and do
  nothing until you wire them up.
-->
<section aria-labelledby="_R_0_-auth-magic-link-form-heading" class="w-full bg-background px-6 py-16 ">
  <div class="mx-auto max-w-md rounded-xl border border-border bg-card p-8 shadow-sm">
    <h2 id="_R_0_-auth-magic-link-form-heading" class="text-xl font-semibold tracking-tight text-card-foreground">Sign in without a password</h2>
    <p class="mt-2 text-sm text-muted-foreground">One address, one link, no password to forget or reuse. The link works once and expires in fifteen minutes, which is stated here rather than discovered when it fails.</p>
    <form class="mt-6 space-y-4">
      <div>
        <label for="_R_0_-auth-magic-link-form-email" class="block text-sm font-medium text-foreground">Email</label>
        <input id="_R_0_-auth-magic-link-form-email" type="email" required="" aria-describedby="_R_0_-auth-magic-link-form-email-hint" class="mt-1.5 w-full rounded-md border border-input bg-background px-3 py-2 text-sm text-foreground placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring" name="email" />
        <p id="_R_0_-auth-magic-link-form-email-hint" class="mt-1 text-xs text-muted-foreground">The link lands here and works once. It expires after 15 minutes.</p>
      </div>
      <button type="submit" class="w-full rounded-md bg-primary px-4 py-2 text-sm font-medium text-primary-foreground transition-opacity hover:opacity-90 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring disabled:opacity-60">Email me a link</button>
      <p role="status" aria-live="polite" class="min-h-5 text-sm text-muted-foreground"></p>
    </form>
  </div>
</section>

Getting it into your project

terminal
npx hoverlab add btn-gradient

No flag. The CLI reads your package.json and emits Svelte because svelte is a dependency. It also picks the folder — an existing components directory rather than a new top-level one.

In a monorepo where detection has nothing to go on, say it outright: npx hoverlab add btn-gradient --framework svelte

For the block markup, the API

The CLI’s framework flag covers effects; it writes blocks as their React source. The markup wrapper comes from the public API instead — no key, no account:

terminal
curl "https://hoverlab5.netlify.app/api/v1/blocks/auth-magic-link-form?format=html&framework=svelte"
  1. Save the file under src/lib/components. A .svelte file that is markup and a <style> block is a complete component.
  2. Svelte scopes component styles and prunes selectors it cannot statically match.
  3. If the compiler reports an unused selector, wrap it in :global(...). That is expected for rules targeting pseudo-elements, and it is not a mistake in the output.

Where the licence comes into it

Svelte output is Pro in the website’s export panel, and it is worth being exact about what that is. The conversion runs in your browser, and the public API and the CLI hand every format to any caller with no key and no account, on purpose. What Pro sells is the licence to ship it, not access to it.

Also targeted