npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@lostisworld/svelte-fliptext

v0.0.1

Published

A per-letter "flip" text effect for Svelte 5, styled with Tailwind CSS v4.

Downloads

196

Readme

svelte-fliptext

A per-letter "flip" text effect for Svelte 5, styled with Tailwind CSS v4.

Each letter renders twice — once visible, once as a clipped duplicate — and slides into place on hover (or on demand) using CSS transitions. No JS animation loop.

Install

npm install @lostisworld/svelte-fliptext
pnpm add @lostisworld/svelte-fliptext

Peer dependencies: svelte ^5.0.0 and tailwindcss ^4.0.0. You need both already set up in your project.

Tailwind v4 setup

Tailwind v4's automatic source detection skips node_modules by default, so the utility classes used inside this package won't be generated unless you point Tailwind at it explicitly. Add a @source directive to your main CSS file:

@import 'tailwindcss';
@source '../node_modules/@lostisworld/svelte-fliptext';

(Adjust the relative path to wherever your CSS file lives relative to node_modules.) Skipping this step means the component renders as plain, unanimated text.

Usage

<script>
	import { Fliptext } from '@lostisworld/svelte-fliptext';
</script>

<Fliptext text="Hover me" class="text-3xl font-bold" />

Direction

<Fliptext text="Right" direction="right" />

direction accepts 'up' (default), 'down', 'left', or 'right'. Each letter has its own clip window (an outer wrapper with overflow-clip that never moves) and an inner wrapper that slides + carries the duplicate — that per-letter isolation is what keeps left/right from visually colliding with neighboring letters.

Manual trigger

By default the flip fires on :hover. Set trigger="manual" and drive it yourself via the bindable active prop — useful for click, scroll-into-view, or timed triggers:

<script>
	let active = $state(false);
</script>

<button onclick={() => (active = !active)}>
	<Fliptext text="Click me" trigger="manual" {active} />
</button>

trigger="manual" also sets data-state="active"/"inactive" on the root element, so you can style the active state directly without any extra local state:

<Fliptext
	text="Active"
	trigger="manual"
	{active}
	class="rounded-lg px-3 py-1 transition-colors data-[state=active]:bg-indigo-500 data-[state=active]:text-white"
/>

Scroll-triggered (or any custom trigger)

ref (bindable) exposes the root element, so you can wire up anything you like — an IntersectionObserver, a timer, another component's state — and drive it into trigger="manual" + active:

<script>
	let ref = $state(null);
	let active = $state(false);

	$effect(() => {
		if (!ref) return;
		const observer = new IntersectionObserver(([entry]) => (active = entry.isIntersecting), {
			threshold: 0.6
		});
		observer.observe(ref);
		return () => observer.disconnect();
	});
</script>

<Fliptext bind:ref text="Scroll to reveal" trigger="manual" {active} />

Disabling the effect

<Fliptext text="Plain text" animated={false} />

Renders the text as-is with none of the per-letter markup or CSS.

Text or background color on hover

No special prop needed — the component is a normal Tailwind element, so hover:/transition-colors on class just works. Color inherits down to the letters automatically:

<Fliptext text="Colorful" class="transition-colors hover:text-pink-400" />
<Fliptext
	text="Highlight"
	class="rounded-lg px-3 py-1 transition-colors hover:bg-amber-400 hover:text-zinc-900"
/>

Per-letter styling

letterClasses accepts a class value, or a function for per-letter control:

<Fliptext text="Highlight" letterClasses={(letter, i) => (i === 0 ? 'text-red-500' : '')} />

Colored pseudo-element (color that only shows once flipped)

Each letter's duplicate is a real ::after pseudo-element, so it takes Tailwind's after: variant. That lets the letter and its flipped-in duplicate have different colors — the color only appears once the flip has actually happened:

<Fliptext text="Reveal" letterClasses="text-white after:text-pink-500" />

If you build the class per-letter dynamically, keep the full class string literal somewhere Tailwind can scan it — a runtime-assembled string like `after:text-${color}` won't generate any CSS. Use a lookup array of complete class names instead:

<script>
	const rainbowAfter = ['after:text-red-400', 'after:text-orange-400', 'after:text-sky-400'];
</script>

<Fliptext
	text="Reveal"
	letterClasses={(_, i) => `text-white ${rainbowAfter[i % rainbowAfter.length]}`}
/>

Element ref

<script>
	let ref = $state(null);
</script>

<Fliptext text="Hello" bind:ref />

Other elements, extra attributes

as renders the root as any non-void element (FliptextElement: p, span, div, h1h6, a, button, li, td, ...). Anything not listed as a prop — href, onclick, id, ARIA attributes, etc. — is spread onto that root element as-is:

<Fliptext as="a" href="/pricing" text="See pricing" class="text-sky-400 underline" />
<Fliptext as="button" text="Submit" onclick={handleSubmit} class="rounded bg-sky-500 px-4 py-2" />

Props

| Prop | Type | Default | Description | | --- | --- | --- | --- | | text | string | — | Required. The text to render. | | as | FliptextElement | 'span' | The root element tag. Restricted to non-void elements (p, span, div, h1-h6, ...) since letters render as child <span>s. | | class | ClassValue | — | Classes for the root element. | | direction | 'up' \| 'down' \| 'left' \| 'right' | 'up' | Flip axis. | | trigger | 'hover' \| 'manual' | 'hover' | How the flip is triggered. | | active | boolean (bindable) | false | Flip state when trigger="manual". Also reflected as data-state="active"/"inactive" on the root. | | animated | boolean | true | Set false to render plain text with no per-letter markup. | | ref | HTMLElement \| null (bindable) | — | The rendered root element. | | letterClasses | ClassValue \| ((letter: string, index: number) => ClassValue) | — | Classes for each letter, or a per-letter function. Supports the after: variant for styling the flipped-in duplicate. | | flipTransition | { delay?, duration?, easing? } | — | delay (ms, multiplied per letter index), duration, easing. |

Any other prop (href, onclick, id, aria-*, ...) is spread onto the root element.

prefers-reduced-motion: reduce is respected automatically — no prop needed.

The root element ships with self-start, so it shrink-wraps to its text even when dropped directly into a flex/grid container that would otherwise stretch it to fill the cross axis.

Developing

npm install
npm run dev

src/routes is a showcase app for local development covering every pattern above; src/lib/svelte-fliptext is the published package.

npm run build   # builds the package to dist/ and runs publint
npm run check   # type-checks