Buttons

Elevated interactive elements that provide tactile feedback via scale and shadow compression on click.

Primary Hierarchy
Default
Hover
Active (Pressed)
Disabled
Error-Disabled
Loading

Physics Buttons use convex shadows (drop shadows) to create depth. On hover, shadow spreads and lifts. On active/press, shadow compresses and element translates down, simulating physical button press.

Secondary & Ghost
Default
Hover
Active
Disabled
Current State: default
Default
Resting state with base drop shadow simulation
Hover
Shadow spreads and lifts (-2px), background brightens slightly
Active (Pressed)
Shadow compresses, element translates down (+2px) simulating physical press

Button Sizes

Available sizes: xs (24px), sm (30px), md (36px, default), lg (42px), xl (48px)

Buttons with Icons

Small Size
Default Size
Large Size
Pass icons through leftIcon or rightIcon: the slot spaces them from the label by the size's gap (4px at sm, 8px at md and lg).

Error State

Error-Disabled State: Used when a button action is blocked by validation errors or missing required data. Pass error with disabled: the button takes the error colors (a red tint, border and label) in place of its own and fades like any disabled button. Like Disabled, it takes no hover, press or click. The library doesn't render the error text, so name the problem next to the field that has it. On its own, error leaves the button enabled.

// Validation errors block the action.
<Button variant="primary" error disabled>
  Submit Form
</Button>

Best Practices

Do

Use primary for main action, secondary for cancel/dismiss

Use icons alongside text for clarity

Use ghost variant for icon-only utility buttons

Don't

Don't use multiple primary buttons in the same context

Don't use icon-only buttons with low-contrast variants

Don't use overly verbose button text

Examples

<Button variant="primary">Save Changes</Button>
<Button variant="primary" leftIcon={<Icon name="check" size="sm" />}>
  Complete Order
</Button>
<Button variant="ghost" iconOnly leftIcon={<Icon name="menu" size="sm" />} />

Icon-Only Buttons

Use the iconOnly prop for square buttons that contain only an icon. This applies proper padding for a balanced appearance.

// Icon-only buttons with different sizes.
// leftIcon takes a ReactNode: a gloss-icons <Icon>, or an SVG of your own.
<Button variant="primary" size="sm" iconOnly leftIcon={<Icon name="plus" size="sm" />} />
<Button variant="primary" iconOnly leftIcon={<Icon name="plus" size="sm" />} />
<Button variant="primary" size="lg" iconOnly leftIcon={<Icon name="plus" size="md" />} />

// Different variants
<Button variant="secondary" iconOnly leftIcon={<SettingsIcon />} />
<Button variant="ghost" iconOnly leftIcon={<Icon name="more-vertical" size="sm" />} />

ButtonGroup

Group related buttons together with consistent spacing or attached styling. ButtonGroup can propagate size and variant to child buttons via context.

Horizontal Group (Default)
<ButtonGroup>
  <Button variant="secondary">Day</Button>
  <Button variant="secondary">Week</Button>
  <Button variant="secondary">Month</Button>
  <Button variant="secondary">Year</Button>
</ButtonGroup>
Attached Style
<ButtonGroup attached>
  <Button variant="secondary">Left</Button>
  <Button variant="secondary">Center</Button>
  <Button variant="secondary">Right</Button>
</ButtonGroup>
Vertical Orientation
<ButtonGroup orientation="vertical" attached>
  <Button variant="secondary" leftIcon={<ListIcon />}>List View</Button>
  <Button variant="secondary" leftIcon={<GridIcon />}>Grid View</Button>
  <Button variant="secondary" leftIcon={<ColumnsIcon />}>Column View</Button>
</ButtonGroup>
Full Width Distribution
<ButtonGroup fullWidth attached>
  <Button variant="secondary">Cancel</Button>
  <Button variant="primary">Confirm</Button>
</ButtonGroup>

SplitButton

A split button combines a primary action with a dropdown menu for secondary actions. The main button handles the default action while the dropdown provides alternatives.

Basic SplitButton
<SplitButton
  onClick={() => handleSave()}
  menuItems={[
    { label: 'Save as draft', onClick: () => handleSaveDraft() },
    { label: 'Save and publish', onClick: () => handlePublish() },
    { label: 'Save as template', onClick: () => handleTemplate() },
  ]}
>
  Save
</SplitButton>
With Icons and Dividers
JSX
<SplitButton
  leftIcon={<DownloadIcon />}
  onClick={() => handleDownload()}
  menuItems={[
    { label: 'Download PDF', onClick: handlePDF, icon: <FileIcon /> },
    { label: 'Download CSV', onClick: handleCSV, icon: <FileIcon /> },
    { divider: true, label: 'Email report', onClick: handleEmail, icon: <MailIcon /> },
  ]}
>
  Download
</SplitButton>
Variants and Sizes
JSX
// Primary variant, small size
<SplitButton variant="primary" size="sm" menuItems={[...]}>
  Primary Small
</SplitButton>

// Secondary variant, default size
<SplitButton variant="secondary" menuItems={[...]}>
  Secondary Default
</SplitButton>

// Ghost variant, large size
<SplitButton variant="ghost" size="lg" menuItems={[...]}>
  Ghost Large
</SplitButton>

Props Reference

Button
PropTypeDefaultDescription
variant'primary' | 'secondary' | 'ghost' | 'outline' | 'destructive''primary'Visual style variant
size'xs' | 'sm' | 'md' | 'lg' | 'xl''md'Button size (24, 30, 36, 42, 48px)
disabledbooleanfalseDisable the button
isLoadingbooleanfalseShow loading spinner
iconOnlybooleanfalseSquare button for icon-only content
leftIconReactNode—Icon before button text
rightIconReactNode—Icon after button text
errorstring | boolean—With disabled, shows the error-disabled state; on its own, leaves the button enabled
ButtonGroup
PropTypeDefaultDescription
orientation'horizontal' | 'vertical''horizontal'Layout direction
attachedbooleanfalseConnect buttons visually (no gaps, merged borders)
size'sm' | 'md' | 'lg'—Size applied to all child buttons
variantButtonVariant—Variant applied to all child buttons
fullWidthbooleanfalseButtons share available space equally
gap'none' | 'sm' | 'md' | 'lg''md'Gap between buttons (when not attached)
SplitButton
PropTypeDefaultDescription
childrenReactNodeRequiredMain button content
onClick() => void—Main button click handler
menuItemsSplitButtonMenuItem[]RequiredDropdown menu items
variant'primary' | 'secondary' | 'ghost' | 'outline' | 'destructive''primary'Button style variant
size'sm' | 'md' | 'lg''md'Button size
disabledbooleanfalseDisable both buttons
leftIconReactNode—Icon for main button
dropdownIconReactNodeBuilt-in chevronIcon for dropdown trigger
SplitButtonMenuItem
PropTypeDefaultDescription
labelReactNodeRequiredMenu item text
onClick() => voidRequiredClick handler
iconstring | ReactNode—Optional icon
disabledboolean—Disabled state
dividerboolean—Show divider before this item