Files
Ubuntu 18c4ddcd05 drover: task-1767764907653950146
Task: Improve API documentation
2026-01-10 05:38:15 +00:00

8.5 KiB

Button Component API

A versatile button component with multiple variants, sizes, and states for all interactive needs.


Installation

# Cargo.toml
[dependencies]
shadcn-ui-leptos-button = "0.7"
use shadcn_ui_leptos_button::Button;

Import

// Default theme
use shadcn_ui_leptos_button::{
    Button,
    ButtonVariant,
    ButtonSize,
    ButtonChildProps
};

// New York theme
use shadcn_ui_leptos_button::{
    ButtonNewYork,
    ButtonVariantNewYork,
    ButtonSizeNewYork
};

// Signal-managed variant
use shadcn_ui_leptos_button::{
    SignalManagedButton,
    SignalManagedButtonState
};

Component API

Button

Props

Prop Type Default Description
variant MaybeProp<ButtonVariant> Default Visual style variant
size MaybeProp<ButtonSize> Default Button size
on_click Option<Callback<()>> None Click event handler
disabled Signal<bool> false Disable button interaction
loading Signal<bool> false Show loading spinner
class MaybeProp<String> None Additional CSS classes
id MaybeProp<String> None Unique identifier
style Signal<Style> None Inline styles
aria_label MaybeProp<String> None Accessible name
aria_describedby MaybeProp<String> None Description reference
as_child Option<Callback<ButtonChildProps, AnyView>> None Render as custom element
children Option<Children> None Button content

ButtonVariant

pub enum ButtonVariant {
    Default,      // Primary brand color
    Destructive,  // Red, destructive actions
    Outline,      // Bordered outline
    Secondary,    // Secondary color
    Ghost,        // No background, hover effect
    Link,         // Text link style
}

ButtonSize

pub enum ButtonSize {
    Default,  // Standard size (h-10 px-4 py-2)
    Sm,       // Small (h-9 rounded-md px-3)
    Lg,       // Large (h-11 rounded-md px-8)
    Icon,     // Square icon (h-10 w-10)
}

ButtonChildProps

When using as_child, these props are passed to your custom component:

pub struct ButtonChildProps {
    pub class: String,                    // CSS classes
    pub id: String,                       // Element ID
    pub style: String,                    // Inline styles
    pub disabled: bool,                   // Disabled state
    pub r#type: String,                   // HTML type (button)
    pub onclick: Option<Callback<()>>,     // Click handler
}

Usage Examples

Basic Button

use leptos::prelude::*;
use shadcn_ui_leptos_button::Button;

#[component]
pub fn MyApp() -> impl IntoView {
    view! {
        <Button on_click=move || println!("Clicked!")>
            "Click me"
        </Button>
    }
}

Variants

view! {
    <div class="flex gap-2">
        <Button variant=ButtonVariant::Default>"Default"</Button>
        <Button variant=ButtonVariant::Destructive>"Destructive"</Button>
        <Button variant=ButtonVariant::Outline>"Outline"</Button>
        <Button variant=ButtonVariant::Secondary>"Secondary"</Button>
        <Button variant=ButtonVariant::Ghost>"Ghost"</Button>
        <Button variant=ButtonVariant::Link>"Link"</Button>
    </div>
}

Sizes

view! {
    <div class="flex items-center gap-2">
        <Button size=ButtonSize::Sm>"Small"</Button>
        <Button size=ButtonSize::Default>"Default"</Button>
        <Button size=ButtonSize::Lg>"Large"</Button>
        <Button size=ButtonSize::Icon>
            <span>"+"</span>
        </Button>
    </div>
}

With Loading State

#[component]
pub fn SaveButton() -> impl IntoView {
    let (loading, set_loading) = signal(false);

    let handle_save = move |_| {
        set_loading.set(true);
        // Simulate async operation
        setTimeout(move || {
            set_loading.set(false);
        }, 2000);
    };

    view! {
        <Button
            on_click=handle_save
            loading=loading.into()
        >
            "Save"
        </Button>
    }
}

Disabled State

view! {
    <Button disabled=Signal::derive(|| true)>
        "Disabled"
    </Button>
}

Custom Styling

view! {
    <Button
        variant=ButtonVariant::Primary
        class="w-full shadow-lg"
        id="submit-button"
        style=Style::new("margin-top: 1rem")
    >
        "Submit"
    </Button>
}

With Icon

view! {
    <Button variant=ButtonVariant::Outline size=ButtonSize::Sm>
        <span class="mr-2">"+"</span>
        "Add New"
    </Button>
}

As Child (Custom Element)

view! {
    <Button
        as_child=Callback::new(|props| {
            view! {
                <a
                    class=props.class
                    id=props.id
                    style=props.style
                    href="https://example.com"
                >
                    "Link Button"
                </a>
            }.into_any()
        })
    />
}

Accessibility

Keyboard Navigation

Key Action
Enter Activates button
Space Activates button
Tab Focuses next button

ARIA Attributes

The component automatically manages ARIA attributes:

  • aria-label - Custom accessible label
  • aria-describedby - References description element
  • aria-busy - Set to "true" when loading
  • disabled - HTML disabled attribute

Screen Reader Support

// Good: Provide accessible label
<Button aria_label="Close dialog".to_string()>
    <span>"X"</span>
</Button>

// Good: Use descriptive text
<Button>"Delete Account"</Button>

// Avoid: Non-descriptive icons without labels
<Button>
    <span>X</span>  // Screen reader won't understand
</Button>

Signal-Managed Variant

The SignalManagedButton provides built-in state management:

use shadcn_ui_leptos_button::SignalManagedButton;

#[component]
pub fn MyForm() -> impl IntoView {
    let state = SignalManagedButtonState::new();

    view! {
        <SignalManagedButton
            state=state
            on_click=move || println!("Clicked!")
        >
            "Submit"
        </SignalManagedButton>
    }
}

CSS Classes

Base Classes

.shadcn-button {
    inline-flex items-center justify-center
    whitespace-nowrap rounded-md text-sm font-medium
    ring-offset-background transition-colors
    focus-visible:outline-none focus-visible:ring-2
    focus-visible:ring-ring focus-visible:ring-offset-2
    disabled:pointer-events-none disabled:opacity-50
}

Variant Classes

Variant CSS Classes
Default bg-primary text-primary-foreground hover:bg-primary/90
Destructive bg-destructive text-destructive-foreground hover:bg-destructive/90
Outline border border-input bg-background hover:bg-accent hover:text-accent-foreground
Secondary bg-secondary text-secondary-foreground hover:bg-secondary/80
Ghost hover:bg-accent hover:text-accent-foreground
Link text-primary underline-offset-4 hover:underline

Size Classes

Size CSS Classes
Default h-10 px-4 py-2
Sm h-9 rounded-md px-3
Lg h-11 rounded-md px-8
Icon h-10 w-10

TypeScript API

interface ButtonProps {
  variant?: 'default' | 'destructive' | 'outline' | 'secondary' | 'ghost' | 'link';
  size?: 'default' | 'sm' | 'lg' | 'icon';
  onClick?: () => void;
  disabled?: boolean;
  loading?: boolean;
  className?: string;
  id?: string;
  style?: React.CSSProperties;
  ariaLabel?: string;
  ariaDescribedby?: string;
  asChild?: boolean;
  children?: React.ReactNode;
}

export const Button: React.FC<ButtonProps>;

Best Practices

  1. Use clear, descriptive labels - "Submit" is better than "OK"
  2. Match variant to action - Use Destructive for irreversible actions
  3. Provide loading feedback - Use the loading prop for async actions
  4. Maintain consistent sizing - Keep buttons within a section the same size
  5. Consider touch targets - Minimum 44x44 pixels for mobile

See Also


Source: packages/leptos/button/src/default.rs