Skip to content

ButtonFx

Experimental Alpha

This library is a work-in-progress. We are releasing it early to gather feedback, but it is not ready for production.

An enhanced button component with customizable animation effects that trigger on hover, click, or mount. ButtonFx extends the base Button component with a rich set of visual effects to create engaging, interactive UI elements.

Opt-in Component

ButtonFx adds a few hundred lines of CSS for animation effects. It's ideal for marketing sites, landing pages, or when visual polish is a priority.

Vue
Lit
React
Live Preview

Hover Effects

These effects trigger on hover

Bounce Pulse Jelly Grow Shrink

Click Effects

These effects trigger on click/active state

Press Pop Send the email Press Shadow Press Shadow Proceed to your cart.Do the dangerous thing

Background Effects

Effects that animate the button background

BG Slide Side Slide

Motion Effects

Dynamic movement effects

Wobble Shake Push

Bordered Buttons

Effects work with bordered button styles

Primary Capsule Toss it in the trashWrite the SQL to the DatabaseWarning - you better be careful.

Composite Effect

Special multi-stage animation

Pulse → Wobble Pulse → Wobble Pulse → Wobble Pulse → Wobble

Speed Variations

Control animation speed with the fxSpeed prop

XSSMMDLGXL

Easing Functions

Different easing functions create different animation feels

Examples use fx-speed="xl" to make easing differences more visible. For production, prefer "sm" or "md" speeds.
EaseEase-InEase-OutBounceSpring SMSpring MDSpring LG

Disabling Effects

These buttons have fx="bounce" but :fx-disabled="true" prevents the animation from playing while keeping the buttons clickable

FX Disabled FX Disabled
View Vue Code
<template>
  <section>
    <!-- Basic Effects -->
    <div class="mbe4">
      <h2>Hover Effects</h2>
      <p class="mbe2">These effects trigger on hover</p>
    </div>
    <div class="stacked-mobile mbe6">
      <VueButtonFx
        fx="bounce"
        fx-ease="spring-md"
        title="Bounce"
        variant="primary"
        shape="rounded"
      >
        Bounce
      </VueButtonFx>
      <VueButtonFx
        fx="pulse"
        fx-ease="spring-md"
        title="Pulse"
        variant="success"
        shape="rounded"
      >
        Pulse
      </VueButtonFx>
      <VueButtonFx
        fx="jelly"
        fx-ease="spring-lg"
        fx-speed="lg"
        title="Jelly button"
        variant="warning"
        shape="rounded"
      >
        Jelly
      </VueButtonFx>
      <VueButtonFx
        fx="grow"
        fx-ease="spring-md"
        title="Grow button"
        variant="primary"
        shape="rounded"
      >
        Grow
      </VueButtonFx>
      <VueButtonFx
        fx="shrink"
        fx-ease="spring-md"
        title="Shrink button"
        variant="secondary"
        shape="rounded"
      >
        Shrink
      </VueButtonFx>
    </div>

    <!-- Click Effects -->
    <div class="mbe4">
      <h2>Click Effects</h2>
      <p class="mbe2">These effects trigger on click/active state</p>
    </div>
    <div class="stacked-mobile mbe6">
      <VueButtonFx
        fx="press-pop"
        fx-ease="spring-sm"
        title="Press Pop"
        variant="primary"
        shape="rounded"
      >
        Press Pop
      </VueButtonFx>
      <VueButtonFx
        fx="press-pop"
        variant="monochrome"
        title="Send the email"
        shape="rounded-square"
        size="lg"
      >
        <VueIcon no-fill>
          <Mail />
        </VueIcon>
        <VueVisuallyHidden>Send the email</VueVisuallyHidden>
      </VueButtonFx>
      <VueButtonFx
        fx="press-shadow"
        variant="warning"
        title="Press Shadow"
        shape="rounded"
      >
        Press Shadow
      </VueButtonFx>
      <VueButtonFx
        fx="press-shadow"
        variant="success"
        title="Press Shadow"
        shape="rounded"
      >
        Press Shadow
      </VueButtonFx>

      <VueButtonFx
        fx="press-shadow"
        variant="primary"
        title="Shopping cart button"
        shape="rounded-square"
        size="lg"
      >
        <VueIcon no-fill>
          <ShoppingCart />
        </VueIcon>
        <VueVisuallyHidden>Proceed to your cart.</VueVisuallyHidden>
      </VueButtonFx>

      <VueButtonFx
        fx="press-shadow"
        variant="danger"
        title="Dangerous action button"
        shape="rounded-square"
        size="lg"
      >
        <VueIcon no-fill>
          <Bomb />
        </VueIcon>
        <VueVisuallyHidden>Do the dangerous thing</VueVisuallyHidden>
      </VueButtonFx>
    </div>

    <!-- Background Effects -->
    <div class="mbe4">
      <h2>Background Effects</h2>
      <p class="mbe2">Effects that animate the button background</p>
    </div>
    <div class="stacked-mobile mbe6">
      <VueButtonFx
        fx="bg-slide"
        fx-speed="md"
        fx-ease="ease-out"
        title="BG Slide"
        variant="primary"
        shape="rounded"
      >
        BG Slide
      </VueButtonFx>
      <VueButtonFx
        fx="side-slide"
        fx-speed="md"
        fx-ease="ease-out"
        variant="success"
        shape="rounded"
      >
        Side Slide
      </VueButtonFx>
    </div>

    <!-- Motion Effects -->
    <div class="mbe4">
      <h2>Motion Effects</h2>
      <p class="mbe2">Dynamic movement effects</p>
    </div>
    <div class="stacked-mobile mbe6">
      <VueButtonFx
        fx="wobble"
        fx-ease="spring-md"
        title="Wobble"
        variant="warning"
        shape="rounded"
      >
        Wobble
      </VueButtonFx>
      <VueButtonFx
        fx="shake"
        fx-speed="sm"
        title="Shake"
        variant="danger"
        shape="rounded"
      >
        Shake
      </VueButtonFx>
      <VueButtonFx
        fx="push"
        fx-ease="spring-sm"
        title="Push"
        variant="primary"
        shape="rounded"
      >
        Push
      </VueButtonFx>
    </div>

    <!-- Bordered Buttons -->
    <div class="mbe4">
      <h2>Bordered Buttons</h2>
      <p class="mbe2">Effects work with bordered button styles</p>
    </div>
    <div class="stacked-mobile mbe6">
      <VueButtonFx
        fx="jelly"
        fx-ease="spring-lg"
        fx-speed="lg"
        title="Primary Bordered"
        variant="primary"
        :bordered="true"
        shape="rounded"
      >
        Primary
      </VueButtonFx>
      <VueButtonFx
        fx="jelly"
        fx-speed="lg"
        fx-ease="spring-lg"
        title="Success Bordered"
        variant="success"
        :bordered="true"
        shape="capsule"
      >
        Capsule
      </VueButtonFx>
      <VueButtonFx
        fx="jelly"
        fx-ease="spring-lg"
        fx-speed="lg"
        title="Monochrome Bordered"
        variant="monochrome"
        :bordered="true"
        shape="rounded-square"
      >
        <VueIcon no-fill>
          <Trash2 />
        </VueIcon>
        <VueVisuallyHidden>Toss it in the trash</VueVisuallyHidden>
      </VueButtonFx>
      <VueButtonFx
        fx="jelly"
        fx-speed="lg"
        fx-ease="spring-lg"
        title="Danger Bordered"
        variant="danger"
        :bordered="true"
        shape="rounded-square"
      >
        <VueIcon no-fill>
          <Database />
        </VueIcon>
        <VueVisuallyHidden>Write the SQL to the Database</VueVisuallyHidden>
      </VueButtonFx>

      <VueButtonFx
        fx="jelly"
        fx-ease="spring-lg"
        fx-speed="lg"
        title="Warning Bordered"
        variant="warning"
        :bordered="true"
        shape="rounded-square"
      >
        <VueIcon no-fill>
          <Bomb />
        </VueIcon>
        <VueVisuallyHidden>Warning - you better be careful.</VueVisuallyHidden>
      </VueButtonFx>
    </div>

    <!-- Composite Effect -->
    <div class="mbe4">
      <h2>Composite Effect</h2>
      <p class="mbe2">Special multi-stage animation</p>
    </div>
    <div class="stacked-mobile mbe6">
      <VueButtonFx
        fx="pulse-wobble"
        fx-speed="xl"
        title="Pulse → Wobble"
        variant="primary"
        shape="rounded"
      >
        Pulse → Wobble
      </VueButtonFx>

      <VueButtonFx
        fx="pulse-wobble"
        fx-speed="xl"
        title="Pulse → Wobble"
        variant="success"
        shape="rounded"
      >
        Pulse → Wobble
      </VueButtonFx>
      <VueButtonFx
        fx="pulse-wobble"
        fx-speed="xl"
        title="Pulse → Wobble"
        variant="monochrome"
        shape="rounded"
      >
        Pulse → Wobble
      </VueButtonFx>
      <VueButtonFx
        fx="pulse-wobble"
        fx-speed="xl"
        title="Pulse → Wobble"
        variant="danger"
        shape="rounded"
      >
        Pulse → Wobble
      </VueButtonFx>
    </div>

    <!-- Speed Variations -->
    <div class="mbe4">
      <h2>Speed Variations</h2>
      <p class="mbe2">Control animation speed with the fxSpeed prop</p>
    </div>
    <div class="stacked-mobile mbe6">
      <VueButtonFx
        fx="pulse"
        fx-speed="xs"
        title="XS"
        variant="primary"
      >XS</VueButtonFx>
      <VueButtonFx
        fx="pulse"
        fx-speed="sm"
        title="SM"
        variant="primary"
      >SM</VueButtonFx>
      <VueButtonFx
        fx="pulse"
        fx-speed="md"
        title="MD"
        variant="primary"
      >MD</VueButtonFx>
      <VueButtonFx
        fx="pulse"
        fx-speed="lg"
        title="LG"
        variant="primary"
      >LG</VueButtonFx>
      <VueButtonFx
        fx="pulse"
        fx-speed="xl"
        title="XL"
        variant="primary"
      >XL</VueButtonFx>
    </div>

    <!-- Easing Functions -->
    <div class="mbe4">
      <h2>Easing Functions</h2>
      <p class="mbe2">Different easing functions create different animation feels</p>
      <VueAlert :bordered-left="true">
        Examples use <code>fx-speed="xl"</code> to make easing differences more visible.
        For production, prefer <code>"sm"</code> or <code>"md"</code> speeds.
      </VueAlert>
    </div>
    <div class="stacked-mobile mbe6">
      <VueButtonFx
        fx="bounce"
        fx-ease="ease"
        fx-speed="xl"
        title="Ease"
        size="sm"
        variant="primary"
      >Ease</VueButtonFx>
      <VueButtonFx
        fx="bounce"
        fx-ease="ease-in"
        fx-speed="xl"
        title="Ease-In"
        size="sm"
        variant="primary"
      >Ease-In</VueButtonFx>
      <VueButtonFx
        fx="bounce"
        fx-ease="ease-out"
        fx-speed="xl"
        title="Ease-Out"
        size="sm"
        variant="primary"
      >Ease-Out</VueButtonFx>
      <VueButtonFx
        fx="bounce"
        fx-ease="bounce"
        fx-speed="xl"
        title="Bounce"
        size="sm"
        variant="primary"
      >Bounce</VueButtonFx>
      <VueButtonFx
        fx="bounce"
        fx-ease="spring-sm"
        fx-speed="xl"
        title="Spring SM"
        size="sm"
        variant="primary"
      >Spring SM</VueButtonFx>
      <VueButtonFx
        fx="bounce"
        fx-ease="spring-md"
        fx-speed="xl"
        title="Spring MD"
        size="sm"
        variant="primary"
      >Spring MD</VueButtonFx>
      <VueButtonFx
        fx="bounce"
        fx-ease="spring-lg"
        fx-speed="xl"
        title="Spring SM"
        size="sm"
        variant="primary"
      >Spring LG</VueButtonFx>
    </div>

    <!-- Disabled State -->
    <div class="mbe4">
      <h2>Disabling Effects</h2>
      <p class="example-description">
        These buttons have <code>fx="bounce"</code> but <code>:fx-disabled="true"</code>
        prevents the animation from playing while keeping the buttons clickable
      </p>
    </div>
    <div class="stacked-mobile mbe6">
      <VueButtonFx
        fx="bounce"
        :fx-disabled="true"
        title="FX Disabled"
        variant="primary"
      >
        FX Disabled
      </VueButtonFx>
      <VueButtonFx
        fx="bounce"
        :fx-disabled="true"
        title="FX Disabled"
        variant="secondary"
      >
        FX Disabled
      </VueButtonFx>
    </div>
  </section>
</template>

<script lang="ts">
import { defineComponent } from "vue";
import { VueButtonFx } from "agnosticui-core/button-fx/vue";
import { VueIcon } from "agnosticui-core/icon/vue";
import { VueAlert } from "agnosticui-core/alert/vue";
import { VueVisuallyHidden } from "agnosticui-core/visually-hidden/vue";
import { Mail, Trash2, ShoppingCart, Database, Bomb } from "lucide-vue-next";

export default defineComponent({
  name: "ButtonFxExamples",
  components: {
    Mail,
    Trash2,
    ShoppingCart,
    Bomb,
    Database,
    VueButtonFx,
    VueIcon,
    VueVisuallyHidden,
    VueAlert,
  },
});
</script>
Live Preview
View Lit / Web Component Code
import { LitElement, html } from 'lit';
import 'agnosticui-core/button-fx';
import 'agnosticui-core/icon';
import 'agnosticui-core/alert';

export class ButtonFxLitExamples extends LitElement {
  // Render in light DOM to access global utility classes
  createRenderRoot() {
    return this;
  }

  render() {
    return html`
      <section>
        <!-- Hover Effects -->
        <div class="mbe4">
          <h2>Hover Effects</h2>
          <p class="mbe2">These effects trigger on hover</p>
        </div>
        <div class="stacked-mobile mbe6">
          <ag-button-fx fx="bounce" fx-ease="spring-md" title="Bounce" variant="primary" shape="rounded">
            Bounce
          </ag-button-fx>
          <ag-button-fx fx="pulse" fx-ease="spring-md" title="Pulse" variant="success" shape="rounded">
            Pulse
          </ag-button-fx>
          <ag-button-fx fx="jelly" fx-ease="spring-lg" fx-speed="lg" title="Jelly button" variant="warning" shape="rounded">
            Jelly
          </ag-button-fx>
          <ag-button-fx fx="grow" fx-ease="spring-md" title="Grow button" variant="primary" shape="rounded">
            Grow
          </ag-button-fx>
          <ag-button-fx fx="shrink" fx-ease="spring-md" title="Shrink button" variant="secondary" shape="rounded">
            Shrink
          </ag-button-fx>
        </div>

        <!-- Click Effects -->
        <div class="mbe4">
          <h2>Click Effects</h2>
          <p class="mbe2">These effects trigger on click/active state</p>
        </div>
        <div class="stacked-mobile mbe6">
          <ag-button-fx fx="press-pop" fx-ease="spring-sm" title="Press Pop" variant="primary" shape="rounded">
            Press Pop
          </ag-button-fx>
          <ag-button-fx fx="press-pop" variant="monochrome" title="Send the email" shape="rounded-square" size="lg">
            <ag-icon no-fill>
              <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true">
                <path d="M3 8l7.89 5.26a2 2 0 002.22 0L21 8M5 19h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z"/>
              </svg>
            </ag-icon>
            <span aria-label="Send the email" style="position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border-width: 0;">Send the email</span>
          </ag-button-fx>
          <ag-button-fx fx="press-shadow" variant="warning" title="Press Shadow" shape="rounded">
            Press Shadow
          </ag-button-fx>
          <ag-button-fx fx="press-shadow" variant="success" title="Press Shadow" shape="rounded">
            Press Shadow
          </ag-button-fx>
          <ag-button-fx fx="press-shadow" variant="primary" title="Shopping cart button" shape="rounded-square" size="lg">
            <ag-icon no-fill>
              <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true">
                <path d="M9 2L1 8l8 6 8-6-8-6z"/>
                <path d="M9 20V8M23 14l-8 6-8-6"/>
              </svg>
            </ag-icon>
            <span aria-label="Proceed to your cart." style="position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border-width: 0;">Proceed to your cart.</span>
          </ag-button-fx>
          <ag-button-fx fx="press-shadow" variant="danger" title="Dangerous action button" shape="rounded-square" size="lg">
            <ag-icon no-fill>
              <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true">
                <circle cx="12" cy="12" r="1"/>
                <path d="M12 2v4m0 12v4M4.93 4.93l2.83 2.83m8.48 8.48l2.83 2.83M2 12h4m12 0h4M4.93 19.07l2.83-2.83m8.48-8.48l2.83-2.83"/>
              </svg>
            </ag-icon>
            <span aria-label="Do the dangerous thing" style="position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border-width: 0;">Do the dangerous thing</span>
          </ag-button-fx>
        </div>

        <!-- Background Effects -->
        <div class="mbe4">
          <h2>Background Effects</h2>
          <p class="mbe2">Effects that animate the button background</p>
        </div>
        <div class="stacked-mobile mbe6">
          <ag-button-fx fx="bg-slide" fx-speed="md" fx-ease="ease-out" title="BG Slide" variant="primary" shape="rounded">
            BG Slide
          </ag-button-fx>
          <ag-button-fx fx="side-slide" fx-speed="md" fx-ease="ease-out" variant="success" shape="rounded">
            Side Slide
          </ag-button-fx>
        </div>

        <!-- Motion Effects -->
        <div class="mbe4">
          <h2>Motion Effects</h2>
          <p class="mbe2">Dynamic movement effects</p>
        </div>
        <div class="stacked-mobile mbe6">
          <ag-button-fx fx="wobble" fx-ease="spring-md" title="Wobble" variant="warning" shape="rounded">
            Wobble
          </ag-button-fx>
          <ag-button-fx fx="shake" fx-speed="sm" title="Shake" variant="danger" shape="rounded">
            Shake
          </ag-button-fx>
          <ag-button-fx fx="push" fx-ease="spring-sm" title="Push" variant="primary" shape="rounded">
            Push
          </ag-button-fx>
        </div>

        <!-- Bordered Buttons -->
        <div class="mbe4">
          <h2>Bordered Buttons</h2>
          <p class="mbe2">Effects work with bordered button styles</p>
        </div>
        <div class="stacked-mobile mbe6">
          <ag-button-fx fx="jelly" fx-ease="spring-lg" fx-speed="lg" title="Primary Bordered" variant="primary" bordered shape="rounded">
            Primary
          </ag-button-fx>
          <ag-button-fx fx="jelly" fx-speed="lg" fx-ease="spring-lg" title="Success Bordered" variant="success" bordered shape="capsule">
            Capsule
          </ag-button-fx>
          <ag-button-fx fx="jelly" fx-ease="spring-lg" fx-speed="lg" title="Monochrome Bordered" variant="monochrome" bordered shape="rounded-square">
            <ag-icon no-fill>
              <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true">
                <path d="M3 6h18M8 6V4a2 2 0 012-2h4a2 2 0 012 2v2M19 6v14a2 2 0 01-2 2H7a2 2 0 01-2-2V6"/>
              </svg>
            </ag-icon>
            <span aria-label="Toss it in the trash" style="position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border-width: 0;">Toss it in the trash</span>
          </ag-button-fx>
          <ag-button-fx fx="jelly" fx-speed="lg" fx-ease="spring-lg" title="Danger Bordered" variant="danger" bordered shape="rounded-square">
            <ag-icon no-fill>
              <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true">
                <ellipse cx="12" cy="5" rx="9" ry="3"/>
                <path d="M3 5v14a9 3 0 0018 0V5"/>
              </svg>
            </ag-icon>
            <span aria-label="Write the SQL to the Database" style="position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border-width: 0;">Write the SQL to the Database</span>
          </ag-button-fx>
          <ag-button-fx fx="jelly" fx-ease="spring-lg" fx-speed="lg" title="Warning Bordered" variant="warning" bordered shape="rounded-square">
            <ag-icon no-fill>
              <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true">
                <circle cx="12" cy="12" r="1"/>
                <path d="M12 2v4m0 12v4M4.93 4.93l2.83 2.83m8.48 8.48l2.83 2.83M2 12h4m12 0h4M4.93 19.07l2.83-2.83m8.48-8.48l2.83-2.83"/>
              </svg>
            </ag-icon>
            <span aria-label="Warning - you better be careful." style="position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border-width: 0;">Warning - you better be careful.</span>
          </ag-button-fx>
        </div>

        <!-- Composite Effect -->
        <div class="mbe4">
          <h2>Composite Effect</h2>
          <p class="mbe2">Special multi-stage animation</p>
        </div>
        <div class="stacked-mobile mbe6">
          <ag-button-fx fx="pulse-wobble" fx-speed="xl" title="Pulse → Wobble" variant="primary" shape="rounded">
            Pulse → Wobble
          </ag-button-fx>
          <ag-button-fx fx="pulse-wobble" fx-speed="xl" title="Pulse → Wobble" variant="success" shape="rounded">
            Pulse → Wobble
          </ag-button-fx>
          <ag-button-fx fx="pulse-wobble" fx-speed="xl" title="Pulse → Wobble" variant="monochrome" shape="rounded">
            Pulse → Wobble
          </ag-button-fx>
          <ag-button-fx fx="pulse-wobble" fx-speed="xl" title="Pulse → Wobble" variant="danger" shape="rounded">
            Pulse → Wobble
          </ag-button-fx>
        </div>

        <!-- Speed Variations -->
        <div class="mbe4">
          <h2>Speed Variations</h2>
          <p class="mbe2">Control animation speed with the fxSpeed prop</p>
        </div>
        <div class="stacked-mobile mbe6">
          <ag-button-fx fx="pulse" fx-speed="xs" title="XS" variant="primary">XS</ag-button-fx>
          <ag-button-fx fx="pulse" fx-speed="sm" title="SM" variant="primary">SM</ag-button-fx>
          <ag-button-fx fx="pulse" fx-speed="md" title="MD" variant="primary">MD</ag-button-fx>
          <ag-button-fx fx="pulse" fx-speed="lg" title="LG" variant="primary">LG</ag-button-fx>
          <ag-button-fx fx="pulse" fx-speed="xl" title="XL" variant="primary">XL</ag-button-fx>
        </div>

        <!-- Easing Functions -->
        <div class="mbe4">
          <h2>Easing Functions</h2>
          <p class="mbe2">Different easing functions create different animation feels</p>
          <ag-alert bordered-left>
            Examples use <code>fx-speed="xl"</code> to make easing differences more visible.
            For production, prefer <code>"sm"</code> or <code>"md"</code> speeds.
          </ag-alert>
        </div>
        <div class="stacked-mobile mbe6">
          <ag-button-fx fx="bounce" fx-ease="ease" fx-speed="xl" title="Ease" size="sm" variant="primary">Ease</ag-button-fx>
          <ag-button-fx fx="bounce" fx-ease="ease-in" fx-speed="xl" title="Ease-In" size="sm" variant="primary">Ease-In</ag-button-fx>
          <ag-button-fx fx="bounce" fx-ease="ease-out" fx-speed="xl" title="Ease-Out" size="sm" variant="primary">Ease-Out</ag-button-fx>
          <ag-button-fx fx="bounce" fx-ease="bounce" fx-speed="xl" title="Bounce" size="sm" variant="primary">Bounce</ag-button-fx>
          <ag-button-fx fx="bounce" fx-ease="spring-sm" fx-speed="xl" title="Spring SM" size="sm" variant="primary">Spring SM</ag-button-fx>
          <ag-button-fx fx="bounce" fx-ease="spring-md" fx-speed="xl" title="Spring MD" size="sm" variant="primary">Spring MD</ag-button-fx>
          <ag-button-fx fx="bounce" fx-ease="spring-lg" fx-speed="xl" title="Spring SM" size="sm" variant="primary">Spring LG</ag-button-fx>
        </div>

        <!-- Disabled State -->
        <div class="mbe4">
          <h2>Disabling Effects</h2>
          <p class="example-description">
            These buttons have <code>fx="bounce"</code> but <code>fx-disabled="true"</code>
            prevents the animation from playing while keeping the buttons clickable
          </p>
        </div>
        <div class="stacked-mobile mbe6">
          <ag-button-fx fx="bounce" fx-disabled title="FX Disabled" variant="primary">
            FX Disabled
          </ag-button-fx>
          <ag-button-fx fx="bounce" fx-disabled title="FX Disabled" variant="secondary">
            FX Disabled
          </ag-button-fx>
        </div>
      </section>
    `;
  }
}

// Register the custom element
customElements.define('buttonfx-lit-examples', ButtonFxLitExamples);

Interactive Preview: Click the "Open in StackBlitz" button below to see this example running live in an interactive playground.

View React Code
import { ReactButtonFx } from "agnosticui-core/button-fx/react";
import { ReactIcon } from "agnosticui-core/icon/react";
import { ReactAlert } from "agnosticui-core/alert/react";
import { ReactVisuallyHidden } from "agnosticui-core/visually-hidden/react";
import { Mail, Trash2, ShoppingCart, Database, Bomb } from "lucide-react";

export default function ButtonFxReactExamples() {
  return (
    <section>
      {/* Hover Effects */}
      <div className="mbe4">
        <h2>Hover Effects</h2>
        <p className="mbe2">These effects trigger on hover</p>
      </div>
      <div className="stacked-mobile mbe6">
        <ReactButtonFx
          fx="bounce"
          fxEase="spring-md"
          title="Bounce"
          variant="primary"
          shape="rounded"
        >
          Bounce
        </ReactButtonFx>
        <ReactButtonFx
          fx="pulse"
          fxEase="spring-md"
          title="Pulse"
          variant="success"
          shape="rounded"
        >
          Pulse
        </ReactButtonFx>
        <ReactButtonFx
          fx="jelly"
          fxEase="spring-lg"
          fxSpeed="lg"
          title="Jelly button"
          variant="warning"
          shape="rounded"
        >
          Jelly
        </ReactButtonFx>
        <ReactButtonFx
          fx="grow"
          fxEase="spring-md"
          title="Grow button"
          variant="primary"
          shape="rounded"
        >
          Grow
        </ReactButtonFx>
        <ReactButtonFx
          fx="shrink"
          fxEase="spring-md"
          title="Shrink button"
          variant="secondary"
          shape="rounded"
        >
          Shrink
        </ReactButtonFx>
      </div>

      {/* Click Effects */}
      <div className="mbe4">
        <h2>Click Effects</h2>
        <p className="mbe2">These effects trigger on click/active state</p>
      </div>
      <div className="stacked-mobile mbe6">
        <ReactButtonFx
          fx="press-pop"
          fxEase="spring-sm"
          title="Press Pop"
          variant="primary"
          shape="rounded"
        >
          Press Pop
        </ReactButtonFx>
        <ReactButtonFx
          fx="press-pop"
          variant="monochrome"
          title="Send the email"
          shape="rounded-square"
          size="lg"
        >
          <ReactIcon noFill>
            <Mail size={24} />
          </ReactIcon>
          <ReactVisuallyHidden>Send the email</ReactVisuallyHidden>
        </ReactButtonFx>
        <ReactButtonFx
          fx="press-shadow"
          variant="warning"
          title="Press Shadow"
          shape="rounded"
        >
          Press Shadow
        </ReactButtonFx>
        <ReactButtonFx
          fx="press-shadow"
          variant="success"
          title="Press Shadow"
          shape="rounded"
        >
          Press Shadow
        </ReactButtonFx>
        <ReactButtonFx
          fx="press-shadow"
          variant="primary"
          title="Shopping cart button"
          shape="rounded-square"
          size="lg"
        >
          <ReactIcon noFill>
            <ShoppingCart size={24} />
          </ReactIcon>
          <ReactVisuallyHidden>Proceed to your cart.</ReactVisuallyHidden>
        </ReactButtonFx>
        <ReactButtonFx
          fx="press-shadow"
          variant="danger"
          title="Dangerous action button"
          shape="rounded-square"
          size="lg"
        >
          <ReactIcon noFill>
            <Bomb size={24} />
          </ReactIcon>
          <ReactVisuallyHidden>Do the dangerous thing</ReactVisuallyHidden>
        </ReactButtonFx>
      </div>

      {/* Background Effects */}
      <div className="mbe4">
        <h2>Background Effects</h2>
        <p className="mbe2">Effects that animate the button background</p>
      </div>
      <div className="stacked-mobile mbe6">
        <ReactButtonFx
          fx="bg-slide"
          fxSpeed="md"
          fxEase="ease-out"
          title="BG Slide"
          variant="primary"
          shape="rounded"
        >
          BG Slide
        </ReactButtonFx>
        <ReactButtonFx
          fx="side-slide"
          fxSpeed="md"
          fxEase="ease-out"
          variant="success"
          shape="rounded"
        >
          Side Slide
        </ReactButtonFx>
      </div>

      {/* Motion Effects */}
      <div className="mbe4">
        <h2>Motion Effects</h2>
        <p className="mbe2">Dynamic movement effects</p>
      </div>
      <div className="stacked-mobile mbe6">
        <ReactButtonFx
          fx="wobble"
          fxEase="spring-md"
          title="Wobble"
          variant="warning"
          shape="rounded"
        >
          Wobble
        </ReactButtonFx>
        <ReactButtonFx
          fx="shake"
          fxSpeed="sm"
          title="Shake"
          variant="danger"
          shape="rounded"
        >
          Shake
        </ReactButtonFx>
        <ReactButtonFx
          fx="push"
          fxEase="spring-sm"
          title="Push"
          variant="primary"
          shape="rounded"
        >
          Push
        </ReactButtonFx>
      </div>

      {/* Bordered Buttons */}
      <div className="mbe4">
        <h2>Bordered Buttons</h2>
        <p className="mbe2">Effects work with bordered button styles</p>
      </div>
      <div className="stacked-mobile mbe6">
        <ReactButtonFx
          fx="jelly"
          fxEase="spring-lg"
          fxSpeed="lg"
          title="Primary Bordered"
          variant="primary"
          bordered
          shape="rounded"
        >
          Primary
        </ReactButtonFx>
        <ReactButtonFx
          fx="jelly"
          fxSpeed="lg"
          fxEase="spring-lg"
          title="Success Bordered"
          variant="success"
          bordered
          shape="capsule"
        >
          Capsule
        </ReactButtonFx>
        <ReactButtonFx
          fx="jelly"
          fxEase="spring-lg"
          fxSpeed="lg"
          title="Monochrome Bordered"
          variant="monochrome"
          bordered
          shape="rounded-square"
        >
          <ReactIcon noFill>
            <Trash2 size={24} />
          </ReactIcon>
          <ReactVisuallyHidden>Toss it in the trash</ReactVisuallyHidden>
        </ReactButtonFx>
        <ReactButtonFx
          fx="jelly"
          fxSpeed="lg"
          fxEase="spring-lg"
          title="Danger Bordered"
          variant="danger"
          bordered
          shape="rounded-square"
        >
          <ReactIcon noFill>
            <Database size={24} />
          </ReactIcon>
          <ReactVisuallyHidden>Write the SQL to the Database</ReactVisuallyHidden>
        </ReactButtonFx>
        <ReactButtonFx
          fx="jelly"
          fxEase="spring-lg"
          fxSpeed="lg"
          title="Warning Bordered"
          variant="warning"
          bordered
          shape="rounded-square"
        >
          <ReactIcon noFill>
            <Bomb size={24} />
          </ReactIcon>
          <ReactVisuallyHidden>Warning - you better be careful.</ReactVisuallyHidden>
        </ReactButtonFx>
      </div>

      {/* Composite Effect */}
      <div className="mbe4">
        <h2>Composite Effect</h2>
        <p className="mbe2">Special multi-stage animation</p>
      </div>
      <div className="stacked-mobile mbe6">
        <ReactButtonFx
          fx="pulse-wobble"
          fxSpeed="xl"
          title="Pulse → Wobble"
          variant="primary"
          shape="rounded"
        >
          Pulse → Wobble
        </ReactButtonFx>
        <ReactButtonFx
          fx="pulse-wobble"
          fxSpeed="xl"
          title="Pulse → Wobble"
          variant="success"
          shape="rounded"
        >
          Pulse → Wobble
        </ReactButtonFx>
        <ReactButtonFx
          fx="pulse-wobble"
          fxSpeed="xl"
          title="Pulse → Wobble"
          variant="monochrome"
          shape="rounded"
        >
          Pulse → Wobble
        </ReactButtonFx>
        <ReactButtonFx
          fx="pulse-wobble"
          fxSpeed="xl"
          title="Pulse → Wobble"
          variant="danger"
          shape="rounded"
        >
          Pulse → Wobble
        </ReactButtonFx>
      </div>

      {/* Speed Variations */}
      <div className="mbe4">
        <h2>Speed Variations</h2>
        <p className="mbe2">Control animation speed with the fxSpeed prop</p>
      </div>
      <div className="stacked-mobile mbe6">
        <ReactButtonFx fx="pulse" fxSpeed="xs" title="XS" variant="primary">
          XS
        </ReactButtonFx>
        <ReactButtonFx fx="pulse" fxSpeed="sm" title="SM" variant="primary">
          SM
        </ReactButtonFx>
        <ReactButtonFx fx="pulse" fxSpeed="md" title="MD" variant="primary">
          MD
        </ReactButtonFx>
        <ReactButtonFx fx="pulse" fxSpeed="lg" title="LG" variant="primary">
          LG
        </ReactButtonFx>
        <ReactButtonFx fx="pulse" fxSpeed="xl" title="XL" variant="primary">
          XL
        </ReactButtonFx>
      </div>

      {/* Easing Functions */}
      <div className="mbe4">
        <h2>Easing Functions</h2>
        <p className="mbe2">Different easing functions create different animation feels</p>
        <ReactAlert borderedLeft>
          Examples use <code>fxSpeed="xl"</code> to make easing differences more visible.
          For production, prefer <code>"sm"</code> or <code>"md"</code> speeds.
        </ReactAlert>
      </div>
      <div className="stacked-mobile mbe6">
        <ReactButtonFx
          fx="bounce"
          fxEase="ease"
          fxSpeed="xl"
          title="Ease"
          size="sm"
          variant="primary"
        >
          Ease
        </ReactButtonFx>
        <ReactButtonFx
          fx="bounce"
          fxEase="ease-in"
          fxSpeed="xl"
          title="Ease-In"
          size="sm"
          variant="primary"
        >
          Ease-In
        </ReactButtonFx>
        <ReactButtonFx
          fx="bounce"
          fxEase="ease-out"
          fxSpeed="xl"
          title="Ease-Out"
          size="sm"
          variant="primary"
        >
          Ease-Out
        </ReactButtonFx>
        <ReactButtonFx
          fx="bounce"
          fxEase="bounce"
          fxSpeed="xl"
          title="Bounce"
          size="sm"
          variant="primary"
        >
          Bounce
        </ReactButtonFx>
        <ReactButtonFx
          fx="bounce"
          fxEase="spring-sm"
          fxSpeed="xl"
          title="Spring SM"
          size="sm"
          variant="primary"
        >
          Spring SM
        </ReactButtonFx>
        <ReactButtonFx
          fx="bounce"
          fxEase="spring-md"
          fxSpeed="xl"
          title="Spring MD"
          size="sm"
          variant="primary"
        >
          Spring MD
        </ReactButtonFx>
        <ReactButtonFx
          fx="bounce"
          fxEase="spring-lg"
          fxSpeed="xl"
          title="Spring SM"
          size="sm"
          variant="primary"
        >
          Spring LG
        </ReactButtonFx>
      </div>

      {/* Disabled State */}
      <div className="mbe4">
        <h2>Disabling Effects</h2>
        <p className="example-description">
          These buttons have <code>fx="bounce"</code> but <code>fxDisabled={"{true}"}</code>
          prevents the animation from playing while keeping the buttons clickable
        </p>
      </div>
      <div className="stacked-mobile mbe6">
        <ReactButtonFx
          fx="bounce"
          fxDisabled
          title="FX Disabled"
          variant="primary"
        >
          FX Disabled
        </ReactButtonFx>
        <ReactButtonFx
          fx="bounce"
          fxDisabled
          title="FX Disabled"
          variant="secondary"
        >
          FX Disabled
        </ReactButtonFx>
      </div>
    </section>
  );
}
Open in StackBlitz

Usage

TIP

The framework examples below import AgnosticUI as an npm package. Alternatively, you can use the CLI for complete control, AI/LLM visibility, and full code ownership:

bash
npx ag init --framework FRAMEWORK # react, vue, lit, svelte, etc.
npx ag add ButtonFx

The CLI copies source code directly into your project, giving you full visibility and control. After running npx ag add, you'll receive exact import instructions.

Vue
vue
<template>
  <VueButtonFx
    fx="bounce"
    fx-ease="spring-md"
    variant="primary"
    shape="rounded"
  >
    Hover Me
  </VueButtonFx>
</template>
<script lang="ts">
import { defineComponent } from "vue";
import { VueButtonFx } from "agnosticui-core/button-fx/vue";
export default defineComponent({
  components: { VueButtonFx },
});
</script>
React
tsx
import { ReactButtonFx } from "agnosticui-core/button-fx/react";

export default function Example() {
  return (
    <ReactButtonFx
      fx="bounce"
      fxEase="spring-md"
      variant="primary"
      shape="rounded"
    >
      Hover Me
    </ReactButtonFx>
  );
}
Lit (Web Components)
html
<script type="module">
  import "agnosticui-core/button-fx";
</script>
<ag-button-fx fx="bounce" fx-ease="spring-md" variant="primary" shape="rounded">
  Hover Me
</ag-button-fx>

Available Effects

Hover Effects

  • bounce - Vertical pop animation
  • pulse - Scale grow effect
  • jelly - Squash and stretch animation
  • grow - Scale up
  • shrink - Scale down
  • push - Press down effect
  • bg-slide - Background slides up
  • side-slide - Background wipes left to right
  • wobble - Rotate wobble
  • shake - Horizontal shake

Click/Active Effects

  • press-pop - Quick press animation
  • press-shadow - Shadow press effect

Mount Effects

  • slide-in - Entrance animation

Composite Effects

  • pulse-wobble - Combines pulse and wobble effects sequentially. For this effect it's recommended to use a slower fx-speed.

Props

FX Props

PropTypeDefaultDescription
fxstring | string[]''Effect name(s) to apply. Can be a single effect or array of effects
fxSpeed'xs' | 'sm' | 'md' | 'lg' | 'xl''md'Animation duration speed
fxEase'ease' | 'ease-in' | 'ease-out' | 'ease-in-out' | 'bounce' | 'spring-sm' | 'spring-md' | 'spring-lg''ease'Animation easing function
fxDisabledbooleanfalseDisable FX effects entirely

Button Props

PropTypeDefaultDescription
variant'primary' | 'secondary' | 'success' | 'warning' | 'danger' | 'monochrome'''Visual style of the button
size'x-sm' | 'sm' | 'md' | 'lg' | 'xl''md'Size of the button
shape'capsule' | 'rounded' | 'circle' | 'square' | 'rounded-square'''Shape of the button
borderedbooleanfalseApplies a bordered style with transparent background
ghostbooleanfalseMinimal button with transparent background
linkbooleanfalseLink-style button with underline on hover
type'button' | 'submit' | 'reset''button'Button type for form behavior
disabledbooleanfalseDisables the button
loadingbooleanfalseShows a loading state

Events

EventPayloadDescription
toggleCustomEventFired when button is clicked/toggled
focusFocusEventFired when button receives focus
blurFocusEventFired when button loses focus

Examples

Effect Combinations

You can combine multiple effects by passing an array:

Vue
vue
<VueButtonFx :fx="['pulse', 'glow']" variant="primary">
  Multiple Effects
</VueButtonFx>
React
tsx
<ReactButtonFx fx={["pulse", "glow"]} variant="primary">
  Multiple Effects
</ReactButtonFx>

Speed Variations

Control animation speed with the fxSpeed prop:

Vue
vue
<VueButtonFx fx="pulse" fx-speed="xs" variant="primary">XS Speed</VueButtonFx>
<VueButtonFx fx="pulse" fx-speed="sm" variant="primary">SM Speed</VueButtonFx>
<VueButtonFx fx="pulse" fx-speed="md" variant="primary">MD Speed</VueButtonFx>
<VueButtonFx fx="pulse" fx-speed="lg" variant="primary">LG Speed</VueButtonFx>
<VueButtonFx fx="pulse" fx-speed="xl" variant="primary">XL Speed</VueButtonFx>

Easing Functions

Customize the animation feel with different easing functions:

Vue
vue
<VueButtonFx
  fx="bounce"
  fx-ease="spring-sm"
  variant="primary"
>Spring Small</VueButtonFx>
<VueButtonFx
  fx="bounce"
  fx-ease="spring-md"
  variant="primary"
>Spring Medium</VueButtonFx>
<VueButtonFx
  fx="bounce"
  fx-ease="spring-lg"
  variant="primary"
>Spring Large</VueButtonFx>
<VueButtonFx fx="bounce" fx-ease="bounce" variant="primary">Bounce</VueButtonFx>

Composite Effect - Pulse Wobble

The special pulse-wobble effect creates a two-stage animation:

Vue
vue
<VueButtonFx fx="pulse-wobble" variant="primary" shape="rounded">
  Pulse → Wobble
</VueButtonFx>
React
tsx
<ReactButtonFx fx="pulse-wobble" variant="primary" shape="rounded">
  Pulse → Wobble
</ReactButtonFx>
Lit (Web Components)
html
<ag-button-fx fx="pulse-wobble" variant="primary" shape="rounded">
  Pulse → Wobble
</ag-button-fx>

Disabling Effects

You can disable effects while keeping the button functional applying :fx-disabled="true" which prevents the animation from playing while keeping the button clickable

Vue
vue
<VueButtonFx fx="bounce" :fx-disabled="true" variant="primary">
  No Animation
</VueButtonFx>

Accessibility

  • Reduced Motion: All effects automatically respect the prefers-reduced-motion media query. When users have reduced motion enabled in their OS settings, animations are disabled.
  • Keyboard Navigation: ButtonFx maintains full keyboard accessibility with proper focus states.
  • Screen Readers: The component uses semantic HTML button elements that work correctly with assistive technologies.
  • Focus Indicators: Clear focus states are maintained even with effects applied.
  • Color Contrast: All button variants meet WCAG AA contrast requirements.

Best Practices

  1. Choose Appropriate Effects: Match effects to button purpose (e.g., shake for errors, pulse for primary actions)
  2. Performance: Limit the number of animated buttons on a single page for optimal performance
  3. Consistency: Use consistent effects across similar actions in your application
  4. Subtlety: Start with medium speeds and springs; adjust based on user testing
  5. Testing: Always test with prefers-reduced-motion enabled to ensure graceful degradation

Browser Support

ButtonFx uses modern CSS features and is supported in all evergreen browsers:

  • Chrome/Edge 90+
  • Firefox 88+
  • Safari 14+

Animations gracefully degrade in older browsers while maintaining functionality.