Button
Actions & selectionBrick 0.1.2

Button

Button presents a finished action or emphasized native link. Brick owns its visual recipes and styling; the public Atom Button owns semantics, interaction, form behavior, unavailable/loading behavior, and composition.

Live example

Built from the published package

Interactive
Choose with confidence

Know when Button is the right part

Use it when

Use Button for immediate actions such as saving, submitting, confirming, cancelling, continuing, opening another surface, or retrying work. Supply href when an emphasized call to action navigates instead of performing an action.

Choose another path when

  • Use an ordinary link when navigation should read as navigation rather than a prominent action.
  • Use IconButton for an icon-only action.
  • Use Toggle for persistent pressed/unpressed state.
  • Use a component with more specific semantics for menu items, tabs, or arbitrary interactive surfaces.
  • Use variants for normal hierarchy. Reserve info, success, warning, and danger tones for actions with that actual meaning.

Button intentionally has no label, iconOnly, loadingText, loadingIndicator, icon registry, arbitrary radius/color, gradient, or translucent prop.

Installation and imports

Import Button from the package root or its stable subpath, then load Brick's compiled stylesheet once in the application:

tsx
import { Button } from "@flowstack-ui/brick";
// or
import { Button } from "@flowstack-ui/brick/button";

import "@flowstack-ui/brick/styles.css";

The complete stylesheet above is the recommended default. For a measured route-aware build, replace it with the shared foundation and this component's stylesheet:

tsx
import "@flowstack-ui/brick/styles/core.css"; // once at the application root
import "@flowstack-ui/brick/styles/button.css";

Add the modular stylesheet for every other Brick component the route renders. Do not combine modular styles with styles.css or tokens.css.

Brick currently requires React 18 or newer and the package's exact released Atom dependency.

Quick start

tsx
<Button onPress={() => saveProject()}>Save changes</Button>

The defaults are variant="solid", tone="accent", size="md", shape="rounded", and fullWidth={false}.

Visual recipes and states

Variants

VariantIntended hierarchy
solidHighest local emphasis
softQuieter filled treatment
outlineSecondary transparent treatment with a visible border
ghostLowest standalone emphasis with a transparent rest border

Tones

neutral supports secondary and cancel actions. accent is the normal product action. info, success, warning, and danger communicate genuine semantic meaning; they are not decorative palette choices.

Sizes and shapes

SizeMinimum block size
xs28px
sm36px
md44px
lg52px
xl60px

sharp, rounded, and pill change radius only. fullWidth changes inline width only and is independent of size or shape.

Every recipe supports rest, capability-gated hover, active/pressed, focus-visible, disabled, loading, light/dark appearance, forced colors, and reduced motion. Disabled and loading block activation through Atom. Loading remains focusable and overlays a centered visual spinner without resizing the Button.

Examples

tsx
<Button endIcon={<ArrowIcon />}>Continue</Button>

<Button tone="danger" variant="outline">
  Delete project
</Button>

<Button loading aria-describedby="save-status">
  Save changes
</Button>
<span id="save-status" role="status">
  Saving changes
</span>
tsx
<form id="profile-form">
  <Button name="intent" type="submit" value="save">
    Save profile
  </Button>
  <Button tone="neutral" type="reset">
    Reset
  </Button>
</form>
Public contract

API

Start with the public parts and root options below. Components with multiple parts separate each area into its own named subsection.

Exports

ts
Button
ButtonProps
ButtonVariant
ButtonTone
ButtonSize
ButtonShape

Brick visual props

PropAllowed valuesDefault
variant"solid", "soft", "outline", "ghost""solid"
tone"neutral", "accent", "info", "success", "warning", "danger""accent"
size"xs", "sm", "md", "lg", "xl""md"
shape"sharp", "rounded", "pill""rounded"
fullWidthbooleanfalse
startIconReactNodenone
endIconReactNodenone

Inherited Atom and native props

Button preserves the applicable public Atom Button contract:

PropContract
childrenVisible content and accessible name on the normal path
hrefRenders a native anchor when present
target, relNative anchor behavior; Atom preserves safe new-tab relationships
disabledBlocks activation and exposes the unavailable state
loadingBlocks activation, remains focusable, and exposes aria-busy
onPressMouseEventHandler<HTMLElement> normalized by Atom
onClick, onKeyDownNative-compatible handlers composed by Atom
type"button", "submit", or "reset"; defaults to "button"
asChildUses one child element as the host; defaults to false
renderUses an Atom render element or callback as the host
data-slotDefaults to "button" and may be overridden
native/global propsApplicable form, ARIA, data-*, event, className, and style props

The native color attribute is intentionally omitted because Brick uses semantic tone.

asChild and render are mutually exclusive. When asChild is true, children must be one ReactElement, and startIcon/endIcon are rejected by the public type. Put any icon inside that child instead.

Shared responsibility

Accessibility

Provide meaningful visible children for the accessible name. Use the default button path for actions and href for navigation.

Atom owns the native role, keyboard activation, form behavior, unavailable and loading guards, safe anchor behavior, event composition, and ref forwarding. Brick supplies visible focus, recipe contrast, minimum target geometry, forced-color presentation, and reduced-motion styling.

startIcon and endIcon are decorative. Do not rely on them as the only source of meaning. Loading retains the original accessible name; put detailed asynchronous status in adjacent live feedback and connect it with aria-describedby when useful.

The md default supplies a 44 CSS pixel minimum block size. If using xs or sm on touch interfaces, the application must provide adequate surrounding target spacing.

Responsive behavior

Button is intrinsic and mobile-first. Labels may wrap, block size grows with zoom or localization, padding and icon order use logical directions, and the root remains constrained by its container. It never becomes full width automatically.

Use fullWidth for unconditional width. Breakpoint-dependent width, placement, visibility, or grouping remains application layout:

css
.checkout-submit {
  inline-size: 100%;
}

@media (min-width: 48rem) {
  .checkout-submit {
    inline-size: auto;
  }
}

Brick does not mirror icon artwork. Supply the correct directional arrow or chevron for the current writing direction.

Stable visual contract

Styling and tokens

Customization

Prefer customization in this order:

  1. Button props for supported recipes;
  2. Brick semantic tokens on an application scope;
  3. the public Button tokens above;
  4. className and style for a local escape hatch.
tsx
<Button className="checkout-submit">Place order</Button>
css
.checkout-submit {
  --brick-button-radius: var(--brick-radius-full);
  --brick-button-background: var(--brick-color-success-solid);
  --brick-button-background-hover: var(--brick-color-success-solid-hover);
  --brick-button-background-pressed: var(--brick-color-success-solid-pressed);
  --brick-button-foreground: var(--brick-color-success-on-solid);
  --brick-button-border-color: var(--brick-button-background);
}

When overriding colors, verify rest, hover, pressed, focus, disabled, loading, light/dark appearance, and forced colors.

Tokens and CSS hooks

Stable styling surface

  • root class: .brick-button
  • default overridable slot: data-slot="button"
  • visual attributes: data-variant, data-tone, data-size, data-shape
  • conditional layout attribute: data-full-width
  • Atom state attributes: data-disabled, data-loading
  • native className and style

Brick targets .brick-button; changing data-slot does not remove its styles. The private content/icon classes and loading pseudo-element are not public customization contracts.

Public Button tokens

text
--brick-button-background
--brick-button-background-hover
--brick-button-background-pressed
--brick-button-foreground
--brick-button-border-color
--brick-button-focus-ring
--brick-button-min-block-size
--brick-button-padding-block
--brick-button-padding-inline
--brick-button-gap
--brick-button-radius
--brick-button-font-family
--brick-button-font-size
--brick-button-font-weight
--brick-button-line-height
--brick-button-letter-spacing
--brick-button-icon-size

Internal tone-mapping and spinner variables used by the stylesheet are not public tokens. The typography variables default to the matching control-xscontrol-xl semantic recipes.

Advanced reference

Open these details only when you need to inspect DOM ownership, native forwarding, or lower-level composition.

Maintainer resources

Tests, playground evidence, source notes, and release history remain available without crowding the plug-and-play guide.