General

Button

Trigger actions and events with accessible, styled buttons. Supports variants, colors, icons, loading states, active state, avatar, and can render as links.

Playground

Experiment with different props in real-time.

Basic Usage

Import and use the Button component with a label prop.

<script lang="ts">
  import { Button } from 'sv5ui';
</script>

<Button label="Button" />

Variants

6 visual styles for different levels of emphasis.

<Button variant="solid" label="Solid" />
<Button variant="outline" label="Outline" />
<Button variant="soft" label="Soft" />
<Button variant="subtle" label="Subtle" />
<Button variant="ghost" label="Ghost" />
<Button variant="link" label="Link" />

Colors

8 semantic color schemes.

<Button color="primary" label="Primary" />
<Button color="secondary" label="Secondary" />
<Button color="tertiary" label="Tertiary" />
<Button color="success" label="Success" />
<Button color="warning" label="Warning" />
<Button color="error" label="Error" />
<Button color="info" label="Info" />
<Button color="surface" label="Surface" />

Sizes

5 sizes from extra small to extra large.

<Button size="xs" label="Extra Small" />
<Button size="sm" label="Small" />
<Button size="md" label="Medium" />
<Button size="lg" label="Large" />
<Button size="xl" label="Extra Large" />

Icon Only

Use the icon prop for icon-only buttons. Add square for equal width and height.

<!-- Icon only -->
<Button icon="lucide:plus" />

<!-- Icon only with square aspect ratio -->
<Button icon="lucide:settings" square />
<Button icon="lucide:heart" variant="outline" square />
<Button icon="lucide:trash-2" variant="soft" color="error" square />

Leading & Trailing Icons

Add icons before or after the label with leadingIcon and trailingIcon. Use trailing to move a single icon to the trailing side.

<!-- Leading icon -->
<Button leadingIcon="lucide:download" label="Download" />

<!-- Trailing icon -->
<Button trailingIcon="lucide:arrow-right" label="Next" />

<!-- Both icons -->
<Button leadingIcon="lucide:mail" trailingIcon="lucide:send" label="Send" />

<!-- Trailing prop: moves single icon to trailing side -->
<Button icon="lucide:arrow-right" label="Continue" trailing />

Loading

Show a spinner and disable interaction with the loading prop. Customize the spinner icon with loadingIcon.

<!-- Default loading -->
<Button label="Loading" loading />

<!-- Custom loading icon -->
<Button label="Refreshing" loading loadingIcon="lucide:refresh-cw" />

<!-- Loading with variant -->
<Button label="Saving" loading variant="outline" />
<Button label="Loading" loading variant="soft" />

Loading Auto

Use loadingAuto to automatically show a loading spinner while the onclick handler's promise is pending.

<script lang="ts">
  import { Button } from 'sv5ui';

  function handleSave() {
    return new Promise((resolve) => setTimeout(resolve, 2000));
  }
</script>

<!-- Automatically shows loading while the promise is pending -->
<Button label="Save Changes" loadingAuto onclick={handleSave} />
<Button label="Submit" loadingAuto onclick={handleSave} variant="outline" />

Disabled

Disable the button to prevent interaction.

<Button label="Disabled" disabled />
<Button label="Disabled" disabled variant="outline" />
<Button label="Disabled" disabled variant="soft" />

Block (Full Width)

Stretch the button to full width with the block prop.

<Button label="Full Width Button" block />
<Button label="Full Width Outline" variant="outline" block />
<Button label="Full Width Soft" variant="soft" block />

Button Type

Set the HTML type attribute. Useful in forms for submit and reset behavior.

<form onsubmit|preventDefault={() => alert('Submitted!')}>
  <Button type="submit" label="Submit" color="primary" />
  <Button type="reset" label="Reset" variant="outline" color="surface" />
  <Button type="button" label="Cancel" variant="ghost" />
</form>

As Link

Pass an href prop to render as an anchor element. Use external to automatically add target="_blank" and rel="noopener noreferrer".

<!-- Internal link -->
<Button href="/docs" label="Go to Docs" />

<!-- External link with external prop -->
<Button href="https://github.com" external label="GitHub" leadingIcon="mdi:github" />

<!-- External adds target="_blank" and rel="noopener noreferrer" automatically -->

Active State

Control active state manually with active, or let it auto-detect from the current URL when using href. Use exact for exact pathname matching. Customize the active appearance with activeColor, activeVariant, activeClass, and inactiveClass.

<!-- Manually set active state -->
<Button label="Active" active variant="ghost" activeVariant="soft" />

<!-- Active with different color -->
<Button label="Active" active activeColor="success" variant="ghost" />

<!-- Auto-detected from URL (with exact matching) -->
<Button href="/docs" label="Docs" variant="ghost" activeVariant="soft" activeColor="primary" exact />

<!-- Custom active/inactive classes -->
<Button
  href="/docs"
  label="Custom Active"
  variant="ghost"
  activeClass="font-bold ring-2 ring-primary"
  inactiveClass="opacity-60"
/>

Avatar

Display an avatar before the label with the avatar prop. It accepts AvatarProps.

<Button
  avatar={{ src: 'https://i.pravatar.cc/32?u=1', alt: 'John' }}
  label="John Doe"
/>

<Button
  avatar={{ src: 'https://i.pravatar.cc/32?u=2', alt: 'Jane' }}
  label="Jane Smith"
  variant="outline"
/>

<Button
  avatar={{ src: 'https://i.pravatar.cc/32?u=3', alt: 'User' }}
  label="Profile"
  trailingIcon="lucide:chevron-down"
  variant="ghost"
/>

Element Reference

Bind to the root DOM element with bind:ref for imperative access.

<script lang="ts">
  import { Button } from 'sv5ui';

  let buttonRef = $state<HTMLElement | null>(null);

  function inspectButton() {
    if (!buttonRef) return;
    console.log('Tag:', buttonRef.tagName);
    console.log('Size:', buttonRef.offsetWidth, 'x', buttonRef.offsetHeight);
    console.log('Text:', buttonRef.textContent?.trim());
    console.log('Classes:', buttonRef.className);
  }
</script>

<Button bind:ref={buttonRef} label="Inspect Me" leadingIcon="lucide:search" onclick={inspectButton} />

UI Slots

Use the ui prop to override classes on specific internal elements of the Button.

SlotDescription
baseRoot button element — controls shape, padding, shadow, etc.
labelText label wrapper — controls font, spacing, text styles
leadingIconIcon before the label — controls icon size, color, animation
trailingIconIcon after the label — controls icon size, color, animation
leadingAvatarAvatar before the label — controls avatar wrapper styles
leadingAvatarSizeAvatar size wrapper — controls avatar dimensions
<Button
  label="Custom Styled"
  leadingIcon="lucide:star"
  ui={{
    base: 'rounded-full shadow-lg',
    label: 'font-bold tracking-wide',
    leadingIcon: 'text-yellow-400'
  }}
/>
<Button
  label="Gradient Button"
  leadingIcon="lucide:sparkles"
  ui={{
    base: 'bg-gradient-to-r from-purple-500 to-pink-500 border-none text-white hover:from-purple-600 hover:to-pink-600',
    leadingIcon: 'animate-pulse'
  }}
/>

Snippets

Use snippets to fully replace the leading or trailing content with custom markup.

SnippetReplacesDescription
childrenlabelCustom label content — use for rich text, inline elements, etc.
leadingSlotleadingIcon / avatarCustom content before the label — replaces icon and avatar
trailingSlottrailingIconCustom content after the label — replaces trailing icon
<Button variant="outline" color="primary">
  {#snippet leadingSlot()}
    <div class="flex h-5 w-5 items-center justify-center rounded-full bg-primary">
      <span class="text-[10px] font-bold text-on-primary">AI</span>
    </div>
  {/snippet}
  Ask Claude
  {#snippet trailingSlot()}
    <Kbd value="⌘K" size="sm" />
  {/snippet}
</Button>
<Button variant="soft" color="success">
  {#snippet leadingSlot()}
    <span class="relative flex h-2.5 w-2.5">
      <span class="absolute inline-flex h-full w-full animate-ping rounded-full bg-success opacity-75"></span>
      <span class="relative inline-flex h-2.5 w-2.5 rounded-full bg-success"></span>
    </span>
  {/snippet}
  Online
</Button>

Props

PropTypeDefault
variant'solid' | 'outline' | 'soft' | 'subtle' | 'ghost' | 'link''solid'
color'primary' | 'secondary' | 'tertiary' | 'success' | 'warning' | 'error' | 'info' | 'surface''primary'
size'xs' | 'sm' | 'md' | 'lg' | 'xl''md'
labelstring-
iconstring-
leadingIconstring-
trailingIconstring-
trailingbooleanfalse
loadingbooleanfalse
loadingAutobooleanfalse
loadingIconstring'lucide:loader-circle'
disabledbooleanfalse
blockbooleanfalse
squarebooleanfalse
hrefstring-
type'button' | 'submit' | 'reset''button'
externalbooleanfalse
activeboolean-
exactbooleanfalse
activeColorColorType-
activeVariantVariantType-
activeClassstring-
inactiveClassstring-
avatarAvatarProps-
refHTMLElement | nullnull
classstring-
uiRecord<Slot, Class>-