// preview in
saved for every page

Button

Triggers an action. Four variants and four sizes, a loading state that keeps focus, and a look that comes entirely from the active skin.

Built on Base UIPattern WAI-ARIA ButtonClient componentSource ↗
minimal · light · #3fa34d
variant
size
loading
disabled

Installation

Set up Camaleo once (Installation), then import what you need. The skin you load styles it.

import { Button, IconButton, ButtonGroup } from "@camaleo/react";

Anatomy

One element with up to three parts. Every part has a data-slot you can target from CSS.

spinnericonPublish
[data-slot="button"][data-slot="spinner"] · [data-slot="icon"]label (children)

Examples

Every example is a real file you can copy. They all wear the skin, mode and brand from the bar above.

minimal · light · #3fa34d

Sizes

sm, md and lg follow the skin's control heights, so a form and its buttons always line up.

With an icon

Put the icon before the label for something new, after it for direction or download. Mark it data-slot="icon" and aria-hidden.

Icon only

IconButton requires aria-label. Pair it with a Tooltip so sighted users get the name too.

As a link

Navigation is a link, not a button. render={<a href="…" />} with nativeButton={false} keeps the look and gives you a real anchor.

Saving, then saved

Click it. loading keeps the width and the focus while the request runs; a status message announces the result.

Destructive, with a confirm

Click it. A danger action opens an AlertDialog. The safe choice comes first and gets focus.

Grouped

ButtonGroup joins related actions under role="group". For arrow-key navigation between them, use Toolbar.

Accept the terms to continue.

Disabled, with a reason

Never leave people guessing. Say what is missing and link it with aria-describedby, or keep the button enabled and validate on submit.

In context

Buttons are never alone. These are whole pieces of UI you can copy, in the skin you picked.

Most popular

Team

$24 per seat / month

  • Unlimited projects
  • Shared skins and tokens
  • Priority support
Pricing cardCard · Badge · Button

No projects yet

Create your first project or import one from a file.

Empty stateEmptyState · Button

Cookies

We use cookies to keep you signed in and, if you agree, to measure what helps.

Consent bannerCard · Button

Guidelines

DoOne solid button per area.

It tells people what the main action is.

Don’tThree solid buttons side by side.

Nothing stands out, so nothing is clear.

DoStart with a verb.

Say what happens: "Create invoice", "Save changes".

Don’tVague labels.

"OK" and "Click here" mean nothing out of context, or to a screen reader listing the buttons.

Props

PropTypeDefaultDescription
variant"danger" | "ghost" | "outline" | "solid""solid"Visual weight: solid for the main action, outline for secondary ones, ghost for quiet ones, danger for destructive ones.
size"icon" | "lg" | "md" | "sm""md"Height, padding and type size from the skin's control scale. icon makes it square.
loadingbooleanfalseShows a spinner and disables the button, keeping it focusable so focus is not lost.
disabledboolean—Disables the button and removes it from the tab order.
renderReactElement | (props, state) => ReactElement—Allows you to replace the component's HTML element with a different tag, or compose it with another component. Accepts a ReactElement or a function that returns the element to render.
nativeButtonbooleantrueWhether the component renders a native <button> element when replacing it via the render prop. Set to false if the rendered element is not a button (for example, <div>).
focusableWhenDisabledbooleanfalseWhether the button should be focusable when disabled.

IconButton takes the same props without size, plus a required aria-label. Native button attributes pass through.

Styling hooks

Restyle it without forking: target the attributes, or set the tokens on a parent, a skin or the button itself.

Attributes

data-slot"button"
data-variantsolid · outline · ghost · danger
data-sizesm · md · lg · icon
data-loadingpresent while loading
data-disabledpresent when disabled

Tokens it reads

Color

--cml-accent--cml-accent-fg--cml-accent-hover--cml-border--cml-danger--cml-danger-fg--cml-fg--cml-fg-secondary--cml-field-bg--cml-focus-offset--cml-focus-ring--cml-focus-width--cml-input-addon-fg--cml-shadow-control-active--cml-surface-raised

Shape & size

--cml-border-width--cml-control-height--cml-control-height-lg--cml-control-height-sm--cml-control-padding--cml-control-padding-lg--cml-control-padding-sm--cml-field-padding--cml-gap--cml-radius-control--cml-radius-control-lg--cml-radius-control-sm

Type & motion

--cml-duration-fast--cml-ease-standard--cml-font-control--cml-font-size-control--cml-font-size-control-lg--cml-font-size-control-sm--cml-font-weight-control--cml-letter-spacing-control--cml-press-translate--cml-text-transform-control

This component only

--cml-button-danger-image--cml-button-disabled-shadow--cml-button-ghost-decoration--cml-button-ghost-fg--cml-button-outline-bg--cml-button-outline-border--cml-button-outline-shadow--cml-button-solid-image--cml-disabled-opacity
/* a pill-shaped primary, only in this section */
.checkout [data-slot="button"][data-variant="solid"] {
  --cml-radius-control: 999px;
}

Accessibility

EnterActivates the button.
SpaceActivates the button on release.
TabMoves focus to it — also while loading.
Was this page helpful? · Edit this page on GitHub