vael-ui

Actions

Button

A clickable action with variants, sizes, loading states, and icon slots built in.

Install

import { Button } from 'vael-ui'

Playground

import { Button } from 'vael-ui'

<Button :disabled="false" variant="primary" size="md" loader="overlay" :icon="false" :pill="false" :block="false" type="button" as="button" badgePlacement="top-end">Button</Button>

Examples

Button

Variants, sizes, and states

Icon buttons: leading/trailing slots, and icon-only via the icon prop

An icon-only button always needs an explicit aria-label, since there's no text content for assistive tech to read.

Loading state

@click forwards straight to the button's own promise handling, so returning a promise is all the wiring a loading state needs. loader="inline" keeps the label visible and puts the spinner next to it instead of covering the button.

Custom loading feedback (GSAP)

Button owns none of this animation, it only exposes the underlying element through defineExpose. This example runs a GSAP tween on that ref after the click promise resolves, then hands the transform back to the stylesheet with clearProps so the button's normal CSS press feedback keeps working afterward.

Notification badge: the #badge slot

Button owns the positioned wrapper (badgePlacement, default top-end); Badge stays the same context-free component it is everywhere else. The wrapper only exists when a badge is actually rendered, so it costs nothing on every other Button in this file.

2
Code

Props

NameTypeDefaultDescription
loadingboolean | "auto" | undefined"auto"`'auto'` (default): promise-returning `@click` handlers trigger loading automatically. `true`/`false`: fully controlled.
disabledboolean | undefinedfalse
variant"primary" | "secondary" | "outline" | "ghost" | "link" | "text" | "danger""primary"
size"md" | "sm" | "lg""md"
loader"overlay" | "inline""overlay"`overlay` (default): loader centered over fading content. `inline`: spinner slides in at start.
iconboolean | undefinedfalseSquare icon-only button; pair with an aria-label.
pillboolean | undefinedfalseFully rounded capsule/pill shape.
blockboolean | undefinedfalseStretch to container's full inline size.
type"button" | "submit" | "reset""button"
asstring | undefined"button"Root tag (e.g. `as="a"` for a link styled as button).
badgePlacement"top-end" | "top-start" | "bottom-start" | "bottom-end""top-end"Where the `#badge` slot wrapper sits relative to the button.
uiPartial<{ root: UiPartValue; badge: UiPartValue; }> | undefined

Slots

NameTypeDescription
default{ loading: boolean; el: HTMLElement | null; run: <T>(fn: () => T | Promise<T>) => Promise<T>; }
loader{ loading: boolean; el: HTMLElement | null; }Custom loader visuals (placement and crossfade are library-owned).
leadinganyIcon before the label (1em box, optically aligned).
trailinganyIcon after the label (1em box, optically aligned).
badgeanyBadge in library-positioned wrapper.

Events

No events.

Exposed

NameTypeDescription
elHTMLElement | null
run<T>(fn: () => T | Promise<T>) => Promise<T>