Buttons Fancy Stable
Pressable
A wrapper that gives any interactive child a consistent, cross-browser press animation — scale down on pointerdown/keydown, back to rest on release, with a static opacity fallback under reduced motion
Preview
svelte
<script lang="ts">
import { Pressable } from "fancy-ui-svelte";
const REPLIES = ["Sounds good", "Not today", "Let's talk"];
</script>
<div class="flex w-full items-center justify-center p-6">
<div class="border-border bg-card w-full max-w-[280px] rounded-[1.75rem] border p-4 shadow-sm">
<p class="text-muted-foreground mb-3 text-xs">Quick replies</p>
<div class="flex flex-wrap gap-2">
{#each REPLIES as reply}
<Pressable>
<button
type="button"
class="border-border bg-muted text-foreground rounded-full border px-3.5 py-1.5 text-sm"
>
{reply}
</button>
</Pressable>
{/each}
</div>
<p class="text-muted-foreground mt-4 text-xs">
Tap a chip (or focus one and press Space/Enter) — it gives under the press, then settles back
the instant you let go. With reduced motion it dims instead of shrinking.
</p>
</div>
</div>Installation
pnpm add fancy-ui-svelte
import { Pressable } from 'fancy-ui-svelte'
Usage
svelte
<script lang="ts">
import { Pressable } from 'fancy-ui-svelte';
</script>
<Pressable />Examples
Basic Usage
svelte
<script lang="ts">
import { Pressable } from "$lib/fancy-ui/pressable";
const REPLIES = ["Sounds good", "Not today", "Let's talk"];
</script>
<div class="flex w-full items-center justify-center p-6">
<div class="border-border bg-card w-full max-w-[280px] rounded-[1.75rem] border p-4 shadow-sm">
<p class="text-muted-foreground mb-3 text-xs">Quick replies</p>
<div class="flex flex-wrap gap-2">
{#each REPLIES as reply}
<Pressable>
<button
type="button"
class="border-border bg-muted text-foreground rounded-full border px-3.5 py-1.5 text-sm"
>
{reply}
</button>
</Pressable>
{/each}
</div>
<p class="text-muted-foreground mt-4 text-xs">
Tap a chip (or focus one and press Space/Enter) — it gives under the press, then settles back
the instant you let go. With reduced motion it dims instead of shrinking.
</p>
</div>
</div>With Haptics
Touch-only vibration patterns alongside the press scale.
svelte
<script lang="ts">
import { Pressable } from "$lib/fancy-ui/pressable";
</script>
<div class="flex w-full flex-col items-center justify-center gap-4 p-6">
<div class="flex flex-wrap items-center justify-center gap-3">
<Pressable haptic="light">
<button
type="button"
class="border-border bg-card text-foreground rounded-full border px-4 py-2 text-sm font-medium"
>
Like
</button>
</Pressable>
<Pressable haptic="medium">
<button
type="button"
class="bg-primary text-primary-foreground rounded-full px-4 py-2 text-sm font-medium"
>
Add to cart
</button>
</Pressable>
</div>
<p class="text-muted-foreground max-w-xs text-center text-xs">
On a touch device each button also fires a short vibration on press — a lighter buzz for "Like",
a stronger one for "Add to cart". <code class="text-xs">haptic</code> is touch-only and additive:
on this desktop preview (and for mouse or pen everywhere) you'll only ever see the press-scale, exactly
like BasicUsage.
</p>
</div>Props
| Prop | Type | Default | Description |
|---|---|---|---|
scale | number | 0.97 | Target scale() factor while pressed |
haptic | false | "light" | "medium" | "heavy" | "success" | "error" | false | Touch-only vibration pattern fired on pointerdown; false never vibrates |
disabled | boolean | false | Suppresses every listener; data-pressed never appears |
class | string | - | Additional CSS classes, merged onto the wrapper |
ref | HTMLDivElement | null | null | Bindable reference to the wrapper element |
Slots
| Slot | Description |
|---|---|
children | Exactly one interactive child (documented, not enforced at runtime) |