Skip to content

Button

A control that runs an action. Base UI's Button primitive with Neba's acrylic surface on top of it.

tsx
import { Button } from 'neba';

<Button onClick={save}>Save</Button>;

Props

PropTypeDefaultDescription
variantshared'solid' | 'outline' | 'text''solid'Weight of the surface: filled, hairline, or none
sizeshared'xs' | 'sm' | 'md' | 'lg' | 'xl''md'Height and type scale. xs 22px · sm 26px · md 32px · lg 40px · xl 48px
colorshared'primary' | 'secondary' | 'success' | 'warning' | 'danger' | 'info''primary'Semantic colour role. Arbitrary colour values are not accepted
densityshared'default' | 'compact''default'Padding only — never the height, never the type scale
elevationshared0 | 1 | 2 | 30Drop shadow depth. 0 is flat; hover adds a level and pressing removes one
startIconReactNodeContent before the label. Sized in em, so it tracks the label
endIconReactNodeContent after the label
loadingbooleanfalseSpinner in place of startIcon; stops activation but keeps focus
readOnlybooleanfalseInert but not dimmed — the action exists, it just is not available here
disabledbooleanfalseUnavailable. Drops the colour family for neutral grey and leaves the tab order
fullWidthbooleanfalseStretches to the width of the container
childrenReactNodeThe label. Omit it and the button goes square for an icon

Every native <button> attribute passes straight through. The one exception is color, which is omitted because it collides with the color in the table above.

What the shared axes (variant size color density elevation) mean across the library is in Prop conventions.

Examples

Variants

Keep one solid per screen. If there are two primary actions, neither of them is the primary action.

Colours

Six roles, and that is all. Arbitrary colour values are not accepted — a colour is a role, not a value.

Sizes

md (32px) is the desktop default. xs and sm are for toolbars and table rows; lg and xl are for the one action a screen is actually about.

Density

density changes horizontal padding and nothing else. Two buttons of the same size are the same height whatever their density, so a mixed row keeps its baseline.

Icons

Icons are drawn at 1.2em, so they track the label and never need a size of their own. Pass no label and the button goes square — which is when it needs an aria-label.

States

StateAppearanceFocusNative disabled
loadingUnchanged; a spinner takes the startIcon slotKeptNo
readOnlyKeeps its colour, goes flat, drains saturationKeptNo
disabledDrops the colour family for neutral greyLostYes

None of the three let a click reach the parent.

Elevation

The default 0 means no shadow at all — what separates the surface from the page is the acrylic edge. Hovering adds a level and pressing removes one, so a flat button answers a press without moving.

Full width

Accessibility

  • Always renders a native <button>. type passes through, so type="submit" works inside a form.
  • Give icon-only buttons an aria-label.
  • The focus ring only appears on :focus-visible, so a mouse click never draws one.
  • loading and readOnly keep focus: dropping out of the focus order costs keyboard users their sense of the page.
  • Every colour combination meets 4.5:1 for text on the fill.

Released under the MIT License