Skip to main content

Button

Function Button 

Source
pub fn Button(props: ButtonProps) -> impl IntoView
Expand description

Runs a single command when activated — form submits, dialog confirmations, toolbar actions, and inline commands.

Pick ButtonAppearance::Primary for the one main action on a surface. Wire async work with loading and on_click. For navigation, use Link instead.

§When to use

  • Submitting forms, confirming dialogs, or firing one-off commands - Toolbar and card actions where a single clear primary action is needed - Icon-only affordances when space is tight (pair with aria-label on a wrapper)

§Usage

  1. Pick an ButtonAppearancePrimary for the main action, Secondary for alternatives. 2. Wire on_click with Callback when the button should run logic (not submit a native form). 3. Set loading while async work runs; disable the control when input is invalid or work is in-flight. 4. For E2E hooks, wrap the button in a native element with data-testid (see project UI rules).

§Best Practices

§Do’s

  • Use appearance=ButtonAppearance::Primary for the main action on a surface * Show loading during async handlers so users see in-progress state * Disable while the form is invalid or a request is outstanding * Use icon for recognizable actions (save, search, add)

§Don’ts

  • Do not stack multiple primary buttons in one row * Do not use data-testid on the component itself — wrap with a native element * Do not use icon-only buttons without an accessible name on the wrapper

§Button family

Orbital ships several command controls. When Button is not the right fit:

  • Single command on clickButton (this component). Use Link for navigation. - Primary command plus related alternatesActionMenuButton (Save + Save as / Export). - Menu of options, no primary segmentMenuButton, or Menu for custom triggers. - Primary label plus supporting lineCompoundButton. - One action pinned to the viewportFloatingButton. - Primary float plus fan-out secondariesFloatingActionsMenu. - Merge adjacent buttons visuallyButtonGroup (layout only; style each child explicitly). - Toolbar on/off pressed stateToggleButton. Prefer Switch for immediate settings.

§Examples

§Primary button

Default call-to-action on a form or dialog footer.

view! {
    <div data-testid="button-preview">
        <Button appearance=ButtonAppearance::Primary>
            "Save"
        </Button>
    </div>
}

§Secondary outline

Secondary actions beside the primary—cancel, back, or low-commit choices. Outline styling keeps emphasis below Primary without blending into the surface.

view! {
    <div data-testid="button-secondary">
        <Button appearance=ButtonAppearance::Secondary>
            "Cancel"
        </Button>
    </div>
}

§Subtle and transparent

Low-emphasis actions that blend into the surface until hovered.

view! {
    <div data-testid="button-subtle">
        <Button appearance=ButtonAppearance::Subtle>"More"</Button>
        <Button appearance=ButtonAppearance::Transparent>"Dismiss"</Button>
    </div>
}

§With icon

Leading icon reinforces the action (save, add, search) while the text label keeps meaning clear for sighted users and screen readers.

view! {
    <div data-testid="button-icon">
        <Button appearance=ButtonAppearance::Primary icon=icondata::AiSaveOutlined>
            "Save"
        </Button>
    </div>
}

§Icon-only

Compact affordance when toolbar space is tight. Wrap with aria-label in app code—the button has no visible text for assistive technologies.

view! {
    <div data-testid="button-icon-only">
        <Button icon=icondata::AiSearchOutlined appearance=ButtonAppearance::Subtle />
    </div>
}

§Sizes

Small fits toolbars and dense rows; medium is the default; large suits prominent mobile CTAs or hero actions. Set each with size=ButtonSize::….

use crate::{Button, ButtonSize};
view! {
    <div data-testid="button-sizes">
        <Button size=ButtonSize::Small>"Small"</Button>
        <Button size=ButtonSize::Medium>"Medium"</Button>
        <Button size=ButtonSize::Large>"Large"</Button>
    </div>
}

§Block (full width)

Stretches to the full container width—common for mobile form footers, stacked dialog actions, and narrow layouts.

view! {
    <div data-testid="button-block">
        <Button block=true appearance=ButtonAppearance::Primary>
            "Continue"
        </Button>
    </div>
}

§Loading state

Spinner replaces the icon slot and blocks clicks while async work runs. Pair with disabled form controls to prevent double submission.

view! {
    <div data-testid="button-loading">
        <Button appearance=ButtonAppearance::Primary loading=true>
            "Saving…"
        </Button>
    </div>
}

§Click handler

use leptos::prelude::*;
view! {
    <Button
        appearance=ButtonAppearance::Primary
        on_click=|_| {}
    >
        "Run action"
    </Button>
}

§Shapes

Rounded is the default for labeled buttons. Circular and square fit icon-only controls in toolbars, floating actions, and compact settings grids.

use crate::{Button, ButtonShape};
view! {
    <div data-testid="button-shapes">
        <Button shape=ButtonShape::Rounded>"Rounded"</Button>
        <Button shape=ButtonShape::Circular icon=icondata::AiPlusOutlined />
        <Button shape=ButtonShape::Square icon=icondata::AiSettingOutlined />
    </div>
}

§Disabled

Unavailable actions stay visibly disabled and ignore clicks. disabled_focusable keeps the button in tab order for tooltips or custom disabled messaging.

view! {
    <div data-testid="button-disabled">
        <Button appearance=ButtonAppearance::Primary disabled=true>
            "Unavailable"
        </Button>
        <Button appearance=ButtonAppearance::Secondary disabled_focusable=true>
            "Focusable when disabled"
        </Button>
    </div>
}

§Theme: primary uses brand token

Wrap in OrbitalThemeProvider with a custom brand palette so primary buttons use the theme’s brand color token.

use leptos::prelude::*;
use orbital_theme::{BrandPalette, OrbitalThemeProvider, Theme, ThemeMode};

view! {
    <div data-testid="button-theme-brand">
        <OrbitalThemeProvider theme=RwSignal::new(Theme::with_brand(
            ThemeMode::Light,
            BrandPalette { primary: "#E3008C".to_string() },
        ))>
            <Button appearance=ButtonAppearance::Primary>"Brand action"</Button>
        </OrbitalThemeProvider>
    </div>
}

§Imperative handle

// Focus or programmatically click via ButtonRef after mount.
use crate::{Button, ButtonRef};
use orbital_base_components::ComponentRef;
let btn_ref = ComponentRef::<ButtonRef>::default();
view! {
    <Button comp_ref=btn_ref appearance=ButtonAppearance::Primary>"Focus me"</Button>
}

§Optional Props