Armed Buttons
Armed buttons turn risky actions into deliberate two-step interactions. The first click reveals intent; the second click confirms. With Svelte Motion, the transition between those states can feel crisp without adding a modal or shifting the surrounding layout.
Shared insight
Launch notes
Saved thread
Pricing objections
Team highlight
Retention metrics
Archive confirm
The archive pattern starts as a quiet icon button inside a row. On click, the row stores an armed id and renders a compact confirm button in an AnimatePresence block.
<AnimatePresence>
{#if isArmed}
<motion.div
key="archive-confirm"
initial={{ opacity: 0, scale: 0.78, x: 10 }}
animate={{ opacity: 1, scale: 1, x: 0 }}
exit={{ opacity: 0, scale: 0.78, x: 10 }}
transition={{ type: 'spring', stiffness: 560, damping: 30 }}
>
<button onclick={confirmArchive}>Archive</button>
</motion.div>
{/if}
</AnimatePresence><AnimatePresence>
{#if isArmed}
<motion.div
key="archive-confirm"
initial={{ opacity: 0, scale: 0.78, x: 10 }}
animate={{ opacity: 1, scale: 1, x: 0 }}
exit={{ opacity: 0, scale: 0.78, x: 10 }}
transition={{ type: 'spring', stiffness: 560, damping: 30 }}
>
<button onclick={confirmArchive}>Archive</button>
</motion.div>
{/if}
</AnimatePresence>The important detail is placement: the confirm button is absolutely positioned over reserved trailing space. It can grow from the right without resizing the row.
Delete wait
The delete wait pattern arms the full row, disables confirmation during a countdown, then allows the second click once the timer reaches zero.
{#key locked ? 'locked' : armed ? 'ready' : 'idle'}
<motion.span
initial={{ opacity: 0, scale: 0.6 }}
animate={{ opacity: 1, scale: 1 }}
transition={{ type: 'spring', stiffness: 520, damping: 30 }}
>
{#if locked}
<LoaderCircle />
{:else}
<Trash2 />
{/if}
</motion.span>
{/key}{#key locked ? 'locked' : armed ? 'ready' : 'idle'}
<motion.span
initial={{ opacity: 0, scale: 0.6 }}
animate={{ opacity: 1, scale: 1 }}
transition={{ type: 'spring', stiffness: 520, damping: 30 }}
>
{#if locked}
<LoaderCircle />
{:else}
<Trash2 />
{/if}
</motion.span>
{/key}Keyed spans are useful here because the icon swap is the animation. The button keeps a fixed height and reserves a small trailing slot for the countdown, so 3, 2, 1, and Ready do not push the label around.
Accessibility
Use real <button> elements, preserve keyboard focus, and expose clear labels such as aria-label="Archive insight" when the idle state is icon-only. Disable the delete button only while the countdown is locked or a request is pending; once it is ready, the same control should be keyboard-confirmable.
For destructive actions, auto-disarm abandoned state. A short timeout prevents a row from staying primed after the user moves on.