Components/MotionButton

MotionButton

Native buttons and links with 13 independent interactions and 35 source icon combinations

Since 0.6.3BETA

This component doc is in progress

This page is still being migrated. Demos and API details may change.

Installation

import { TxMotionButton } from '@talex-touch/tuffex/motion-button'
import '@talex-touch/tuffex/base.css'
import '@talex-touch/tuffex/motion-button/style.css'

Usage

All interactions and source combinations

Filter any of the 13 interactions, inspect all 35 icon pairs, and switch between grid, list and icon-matrix layouts. Each specimen supports pointer hover, keyboard focus, native activation and decorative replay. Selected state and activation counts live only in the demo's local state; the copy specimen writes to the real clipboard.

Loading demo...

Caller-owned operations and content

sourceId selects visual parameters, not an operation or a default label. A hover may reveal a check icon without claiming an operation succeeded. Only caller-owned selected enables activeLabel.

<script setup lang="ts">
import { ref } from 'vue'
import { TxMotionButton } from '@talex-touch/tuffex/motion-button'

const copied = ref(false)
const busy = ref(false)
const error = ref('')
const marked = ref(false)
async function copyHash() {
  busy.value = true
  error.value = ''
  try {
    await navigator.clipboard.writeText('43c29ce9cdd16459e3eab4992381b8d35b38776a')
    copied.value = true
  }
  catch {
    error.value = 'Clipboard access failed'
  }
  finally {
    busy.value = false
  }
}
</script>

<template>
  <TxMotionButton source-id="4" label="Copy hash" active-label="Copied"
    :selected="copied" :disabled="busy" @click="copyHash" />
  <p role="status">{{ error }}</p>
  <TxMotionButton variant="sparkle" label="Mark local item" :selected="marked" @click="marked = !marked">
    <template #icon><svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="M4 6h16M4 12h16M4 18h16" /></svg></template>
    <template #active-icon><svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="M20 6 9 17l-5-5" /></svg></template>
    <template #default="{ selected }">{{ selected ? 'Local item marked' : 'Mark local item' }}</template>
  </TxMotionButton>
</template>

The application owns its real operations and glyph content. Content slots are labels and decoration; place independent interactive controls outside the button.

Source 35 renders a group of real anchors or buttons. There is no button wrapping other interactive elements, and the component creates no placeholder destinations.

<script setup lang="ts">
import { ref } from 'vue'
import { TxMotionButton } from '@talex-touch/tuffex/motion-button'
const lastLabel = ref('')
</script>

<template>
  <TxMotionButton source-id="35" label="Project links" :items="[
    { label: 'Repository', href: 'https://github.com/TalexDreamSoul/talex-touch' },
    { label: 'Documentation', href: '/docs' },
    { label: 'Local action' },
  ]" @select="item => lastLabel = item.label" />
  <output>{{ lastLabel }}</output>
</template>

Best Practices

  • Provide a meaningful label, default slot or ariaLabel. Icon-only controls still need an accessible name.
  • Run the real operation in @click or @select. Set selected and activeLabel only from your own state; neither hover nor replay emits success or changes that state.
  • Use href for navigation and type="submit" for form submission. Buttons retain native Enter/Space activation; anchors retain native Enter activation and modifier-click navigation.
  • Set disabled while an operation must not run. A disabled link loses its destination and tab stop, rather than leaving a decorative overlay to intercept clicks.
  • Prefer sourceId for original combinations; override variant, icons and colors when your content needs different parameters. Use slots for your own glyphs.
  • Keep focus-blur #item content non-interactive: the owning anchor or button already handles focus and activation.
  • Turn off animated to keep content and endpoint states without decorative work. Reduced motion, hidden documents, offscreen controls and deactivated KeepAlive trees suspend animation automatically.

API Reference

Props

PropTypeDefaultDescription
sourceIdMotionButtonSourceId—Original string ID "1"–"35"; chooses interaction, glyph pair, active tint/fill and retention. Never supplies a rendered label or operation.
variantMotionButtonVariantPreset, otherwise morphExplicit interaction override; all 13 values are listed below.
labelstring''Caller-owned visible label; also names an icon-only control unless ariaLabel is supplied.
activeLabelstring—Replaces label only while selected === true.
ariaLabelstring—Accessible name override; names the group for focus-blur.
iconMorphIconSourcePresetFirst glyph: built-in icon name, SVG path d, Lucide-style IconNode, or SVG markup supported by IconMorph.
activeIconMorphIconSourcePreset, otherwise iconTarget glyph for pair interactions.
iconColorstringPreset, otherwise currentColorFirst-icon active tint for pulse/shake and fallback target tint. Resting glyphs inherit the control's ink.
activeIconColorstringPreset, otherwise iconColorImmediate target-glyph tint; CSS color/token value.
activeFillbooleanPreset, otherwise falseFills an active glyph in pulse/color-morph/morph.
size'xs' | 'sm' | 'md' | 'lg'mdHeights 24/30/36/42 px; size vocabulary is shared with other TuffEx controls.
disabledbooleanfalseNative disabled button, or destination-less, non-tabbable disabled link. Also disables every focus-blur item.
animatedbooleantrueEnables decoration when the shared lifecycle gate is active.
selectedboolean—Caller-owned target-icon state; sets aria-pressed on a button. Does not execute or acknowledge an operation.
hrefstring—Renders a native anchor instead of a button.
targetstring—Native anchor target.
relstringnoopener noreferrer for _blankNative anchor relationship; explicit value wins.
type'button' | 'submit' | 'reset'buttonNative button type; not applied to anchors.
iconOnlybooleanfalseSquare control without visible label; preserves accessible naming.
hoverBackgroundstringvar(--tx-fill-color)Immediate interaction fill. No hover color tween.
holdDurationnumberPreset, otherwise 0Decorative target-icon retention after exit, in ms. IDs 4/21/22/24/25 retain for 500 ms; never retains a success label.
springTransitionSource-specificExplicit override for geometry springs. Defaults: outer layout 500/25; ordinary icons 600/25; rotate/text-reveal 400/25; expand-ring 400/20; focus brackets 350/20; notification dot 600/15. Also accepts duration/ease; physical coefficients are forwarded to IconMorph.
magneticStrengthnumber0.35Pointer offset multiplier for magnetic pull.
magneticRangenumber—Optional distance limit in px. Undefined preserves the catalog's unrestricted pull inside the button.
magneticSpringSpringConfig{ stiffness: 500, damping: 25 }Shared per-frame spring for magnetic travel and return.
itemsreadonly MotionButtonItem[][]Caller-owned links/buttons for focus-blur. Each item has label, optional href, target, rel, disabled.
blurAmountnumber4Focus-blur sibling blur in px.
opacityAmountnumber0.4Focus-blur sibling opacity, clamped to 0–1.
showBracketsbooleantrueFocus-blur dashed outline around the hovered/focused item.

Native attributes such as id, name, value, form, download, aria-expanded and aria-controls pass through to the owning element. Focus-blur attributes apply to its group.

Events

EventPayloadDescription
clickMouseEventNative activation of a non-disabled normal button/link. No implicit state change or delayed business action.
select(item: MotionButtonItem, index: number, event: MouseEvent)Native activation of a non-disabled focus-blur item. Navigation proceeds unless the caller prevents it.

Slots

SlotScopeDescription
default{ active, selected, disabled }Visible label/content; fallback is label through TextMorph. Empty focus-blur groups also expose this fallback.
icon{ active, selected }Custom first glyph. In morph/color-morph, a single scoped icon slot can render from current active state.
active-icon{ active, selected }Custom second glyph for slide-arrow, sparkle, ring, morph and color-morph. Paired slots preserve the original movement/scale switch.
reveal{ active }Second, visually revealed text row for text-reveal; decorative and excluded from the accessible name. Defaults to the same label.
item{ item, index, active }Focus-blur item label; place no nested interactive controls in this slot.

Exposed Methods

MethodSignatureDescription
replay() => voidRe-enters the visual trajectory without emitting click/select or changing selected. Replays magnetic pull/return and focus-blur emphasis too; inactive/disabled controls do not start work.
focus() => voidFocuses the native control, or the first enabled focus-blur item.

CSS Variables

VariableDefaultDescription
--tx-motion-button-height24/30/36/42 pxSize-tier control height.
--tx-motion-button-pad12/16/24/28 pxSize-tier horizontal padding.
--tx-motion-button-durationResolved icon/bracket springIcon/bracket geometry transition time; zero while inactive.
--tx-motion-button-easeResolved icon/bracket springCompiled spring easing for icon/bracket geometry and opacity.
--tx-motion-button-layout-durationResolved 500/25 springOuter padding/scale transition time.
--tx-motion-button-layout-easeResolved 500/25 springIndependent outer geometry easing.
--tx-motion-button-dot-durationResolved 600/15 springRing notification-dot transition time.
--tx-motion-button-dot-easeResolved 600/15 springIndependently tuned notification-dot easing.
--tx-motion-button-hover-bghoverBackgroundInstant interaction background.
--tx-motion-button-icon-colorResolved target tintActive shake-label tint; glyph tint/fill is also applied immediately.
--tx-motion-button-blurblurAmountFocus-blur sibling filter.
--tx-motion-button-dimopacityAmountFocus-blur sibling opacity.

Types

Exports include MotionButtonProps, MotionButtonEmits, MotionButtonInstance, MotionButtonItem, MotionButtonSize, MotionButtonVariant, MotionButtonSourceId, MotionButtonCatalogEntry and TxMotionButtonInstance. MotionButton is installable; TxMotionButton is the Vue component. MOTION_BUTTON_VARIANTS, MOTION_BUTTON_SOURCE_IDS, MOTION_BUTTON_CATALOG and MOTION_BUTTON_PRESETS expose the actual enumerations and source metadata.

InteractionIndependent trajectory
slide-arrowFirst icon exits 10 px left; label keeps its place as the right icon enters from 10 px right. Width/gap follows the 600/25 spring.
sparkleFirst icon exits upward 15 px at 0.8 scale; target enters from below. Two separate star particles unwind from −45°/+45°, delayed 50/100 ms.
morphEach original stroke pair uses IconMorph's vector geometry; a 0.5-to-1 scale/opacity entrance keeps the source switch rhythm.
color-morphBookmark/thumb/star keeps its own outline and switches immediate tint plus filled state; the source scale/opacity beat remains.
pulseHeart expands 1 → 1.25 → 1 over 400 ms, with immediate active fill/tint.
rotateSettings or reload glyph rotates 180° with a 400/25 spring and returns on exit.
shakeTrash moves 0/−2/0/−2/0 px vertically with 0/−10°/10°/−10°/0° rotation over 400 ms.
ringBell switches to BellRing via −15°/15°, 0.8-scale icon poses; a separate 6 px notification dot uses its own 600/15 spring after 100 ms.
glareA 50 px, −20° shine travels −150% → 150% in 850 ms with a 1 s repeat gap, only while interacting.
text-revealArrow rotates 45°; two 18 px text rows scroll upward one row with a 400/25 spring.
magneticPointer offset × strength pulls the control; one shared spring carries velocity and returns it to the same origin.
expand-ringGlyph scales to 1.1; the separate outline expands 1 → 1.15 and fades out over 600 ms.
focus-blurOther real links/buttons blur and dim; the active item's dashed brackets use a 350/20 spring from 1.3 → 1.1 scale.

Overview

  • Explicit variant, icons, colors and holdDuration override sourceId. Source metadata labels are attribution only and never become implicit application copy.
  • Hover, focus, pointer press and replay drive decoration. selected holds a target icon and enables activeLabel; it remains caller-owned. The original hover-based “Copied” business claim is intentionally not ported.
  • Decoration uses pointer-inert content/layers. Normal controls remain native buttons/anchors, and focus-blur items remain individual native controls with visible keyboard outlines. Disabled anchors have no href, no tab stop and no emitted activation.
  • Native form submission/reset and link navigation are not replaced by JavaScript keyboard emulation. Enter/Space pressed decoration follows the native element's supported keys.
  • One shared activity gate stops CSS loops, pending replay frames, retention/replay timers, magnetic RAF work and icon/text morph work when inactive. SSR touches no browser APIs. Reduced motion keeps readable content and static endpoint state.
  • Replay is a visual operation: it paints a rest pose before re-entering the same path. Magnetic replay samples the control's size once and uses the same pull/return spring; focus-blur replay emphasizes the first enabled item.

Technologies

  • Upstream: Amicro, MIT, Copyright (c) 2026 SYED SUBHAN UDDIN.
  • Behavioral sources: src/components/AnimatedButton.tsx:39–415, src/data/buttons.tsx:43–77, src/components/cards/FocusBlur.tsx:17–73. Registry hover/magnetic-button.tsx and hover/glow-button.tsx were reviewed; their standalone registry variants belong to the Motion interaction family.
  • Original glyphs come from the upstream lockfile's pinned lucide-react 0.546.0. All 46 SVG icons retain their source nodes and geometry attributes; only React keys are omitted. The ISC and Feather-derived MIT notices are preserved in full in icons.ts's @license header.
  • TuffEx implementation: motion-button/src/TxMotionButton.vue, catalog.ts, icons.ts and MotionButtonGlyph.vue. Stroke changes reuse IconMorph, value changes reuse TextMorph, and physics/lifecycle reuse the existing shared spring and useMotionActivity. An activity-boundary key destroys the old morph controller rather than merely changing its reduced-motion flag.
  • The table below preserves all 35 actual combinations; these IDs change glyphs, interaction parameters, fills and retention, not just labels. The fixed labels are source metadata; the caller supplies rendered copy.
Source IDSource combinationInteractionGlyphsSource
1Download for Macslide-arrowApple → ArrowRightbuttons.tsx:43
2Star on GitHubsparkleGitHub → Starbuttons.tsx:44
3Deploy AppmorphCloud → CloudUploadbuttons.tsx:45
4Copy HashmorphCopy → Check; 500 ms icon retentionbuttons.tsx:46
5SponsorpulseHeart; active fillbuttons.tsx:47
6SharemorphLink → Sendbuttons.tsx:48
7PreviewmorphPlay → Pausebuttons.tsx:49
8SettingsrotateSettingsbuttons.tsx:50
9DeleteshakeTrash2buttons.tsx:51
10SubscriberingBell → BellRingbuttons.tsx:52
11SearchmorphSearch → Xbuttons.tsx:53
12ThememorphMoon → Sunbuttons.tsx:54
13MicrophonemorphMic → MicOffbuttons.tsx:55
14CameramorphVideo → VideoOffbuttons.tsx:56
15VolumemorphVolume2 → VolumeXbuttons.tsx:57
16LockmorphLock → Unlockbuttons.tsx:58
17DirectorymorphFolder → FolderOpenbuttons.tsx:59
18VisibilitymorphEye → EyeOffbuttons.tsx:60
19Save Latercolor-morphBookmark outline → filledbuttons.tsx:61
20Likecolor-morphThumbsUp outline → filledbuttons.tsx:62
21DownloadmorphDownload → Check; 500 ms icon retentionbuttons.tsx:63
22UploadmorphUpload → Check; 500 ms icon retentionbuttons.tsx:64
23AccountmorphUser → UserCheckbuttons.tsx:65
24SubmitmorphSend → Check; 500 ms icon retentionbuttons.tsx:66
25EditmorphPen → Check; 500 ms icon retentionbuttons.tsx:67
26NetworkmorphWifi → WifiOffbuttons.tsx:68
27PowermorphBattery → BatteryChargingbuttons.tsx:69
28ExpandmorphMaximize → Minimizebuttons.tsx:70
29ReloadrotateRefreshCwbuttons.tsx:71
30Favoritecolor-morphStar outline → filledbuttons.tsx:72
31Glare ShineglareStar with sweeping shinebuttons.tsx:73
32Text Revealtext-revealArrowRight and two text rowsbuttons.tsx:74
33Magnetic FieldmagneticGitHub and pointer pullbuttons.tsx:75
34Expand Ringexpand-ringLink and separate expanding outlinebuttons.tsx:76
35Focus Blur Linksfocus-blurCaller-owned link/button groupbuttons.tsx:77
查看源码
packages/tuffex/packages/components/src/motion-button/index.ts