mirror of
https://github.com/cloud-shuttle/leptos-shadcn-ui.git
synced 2026-05-14 02:20:40 +00:00
8.5 KiB
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 labelaria-describedby- References description elementaria-busy- Set to "true" when loadingdisabled- 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
- Use clear, descriptive labels - "Submit" is better than "OK"
- Match variant to action - Use
Destructivefor irreversible actions - Provide loading feedback - Use the
loadingprop for async actions - Maintain consistent sizing - Keep buttons within a section the same size
- Consider touch targets - Minimum 44x44 pixels for mobile
See Also
- Input - Form input component
- Dialog - Modal dialog component
- Accessibility Guide