Button
An action control. The Button JSX wrapper renders an <a-button> web
component (or <a role="button"> when href is set). Tone, priority,
size, and state are all plain attributes, so the styling is identical
whether you use the wrapper or author the element by hand.
Play with it in playground.
Priorities
A button’s priority helps control how much attention it draws:
<Button tone="brand" priority="primary" label="Publish" /><Button tone="brand" priority="secondary" label="Move" /><Button tone="brand" priority="tertiary" label="Cancel" /><Button tone="brand" priority="quaternary" label="Remind" />primary— Saturated fill. Main call to action.secondary— Lighter background. Default priority.tertiary— Background-less at rest, fills on hover.quaternary— Text only, no background.
Tones
Six named tones — neutral (default), brand, critical, info,
success, warning. Omitting tone — or passing an empty string —
resolves to neutral. In addition, a custom tone is possible: pass any
literal CSS color and the button will adapt to it.
<Button label="Save" /><Button tone="brand" label="Publish" /><Button tone="critical" label="Delete" /><Button tone="info" label="Preview" /><Button tone="success" label="Approve" /><Button tone="warning" label="Archive" /><Button priority="primary" tone="#ff1493" label="Pinkify" /><Button priority="secondary" tone="oklch(0.655 0.261 356.9)" label="Pinkify" /><Button priority="tertiary" tone="hsl(328 100% 54%)" label="Pinkify" /><Button priority="primary" tone="mediumaquamarine" label="Mintify" /><Button priority="secondary" tone="rgb(102 205 170)" label="Mintify" /><Button priority="tertiary" tone="lch(75.7% 39.2 167.8)" label="Mintify" />oklch() relative color), so
pale or low-chroma inputs (tone="#cccccc", tone="white") still produce a
legible button. The full priority × state matrix — rest, hover, active,
secondary alpha overlay, tertiary fill — is derived automatically, and
re-tunes between light and dark. --button-fg or --button-bg
yourself — inline via style, or from your own class or selector. The
resolver lives in @layer anta, so any un-layered rule of yours wins
regardless of specificity (and inline beats everything). The same override
also pins hover and active to that color — it overrides the per-state rules
too, so there’s no state change; to keep distinct states, set the per-state
variables instead — --button-bg-{priority}-hover,
--button-bg-{priority}-active, and the --button-fg-* equivalents. Sizes
<Button tone="brand" size="small" label="Small" /><Button tone="brand" size="medium" label="Medium" /> // default<Button tone="brand" size="large" label="Large" />Three sizes (medium is default); only the padding changes, so the label
keeps the same 15px size — readability stays constant while the hit area
grows or shrinks.
Height comes from line-height (18px) plus vertical padding, which is
calibrated for 15px font-size of TT Interphases to get visual vertical
centering.
| size | height | padding-y | text-edge padding-x | icon-edge padding-x |
|---|---|---|---|---|
small | 24px | 3px | 7px | 5px |
medium (default) | 28px | 5px | 9px | 7px |
large | 32px | 7px | 13px | 11px |
The icon-edge padding is the text-edge value minus 2px — an icon at
an edge needs less breathing room than a glyph — so only the side a
(leading or trailing) icon occupies is trimmed; the text side keeps the
full padding. The gap between an icon and the label is 0.5ch at every
size.
Need a different font size? Style the <a-button-label> directly — set
its font-size and line-height, and (if the text then sits a little
high) drop the 1px padding-bottom that optically centers Anta’s
default font.
By default a button packs its content to the start, so when buttons share a width — a vertical menu, equal grid cells — their labels line up on a common left edge, which usually reads best in a list.
To center the label inside a wider button instead, set
justify-content: center on it (the tones grid above does exactly this).
Paddingless
If you’re familiar with the basics, click
If you're familiar with the basics, click<Button tone="critical" priority="quaternary" paddingless label="here" />to skip ahead to the live demo.priority="quaternary". Zeros the outer padding so the
button sits flush with surrounding prose — useful when you want a
button to read as an inline link inside a sentence. Icons
<Button tone="brand" priority="primary" icon="check" label="Confirm" /><Button tone="brand" iconTrailing="external-link" label="Read the docs" /><Button tone="brand" priority="tertiary" icon="filter" iconTrailing="chevron-down" label="Filter" />icon →
label → children → iconTrailing. Icon shape names come from the
IconShape union — see the Icon page for the full
set. children (if any) lands between label and iconTrailing. Icon-only
<Button tone="neutral" icon="dots-vertical" size="small" /><Button tone="critical" icon="trash" /><Button tone="brand" icon="check" size="large" />Pass icon alone — no label, no iconTrailing, no children — and
the button collapses to a square. A min-size is pinned to a square
matching the labeled-button height (24px / 28px / 32px for small /
medium / large) so icon-only and labeled buttons line up, and a tight
flex parent can’t clip the icon.
Icon-only buttons get an accessible name automatically — the wrapper
sets aria-label={icon} so <Button icon="trash" /> ships with the
name “trash”. Pass your own aria-label to override.
Children alongside label & icon
<Button tone="brand" label="Save" icon="check"> <span style={{ opacity: 0.7 }}>⌘S</span></Button>Children render after the label and before the trailing icon, so you can mix custom inline content — keyboard hints, badges, counters — with the prop-driven API.
Children also work without label — pass them as the only content.
Underline
<Button tone="brand" priority="tertiary" underline="solid" label="Solid" /><Button tone="brand" priority="tertiary" underline="dashed" label="Dashed" /><Button tone="brand" priority="tertiary" underline="dotted" label="Dotted" />solid, dashed,
dotted (only valid on priority="tertiary" / "quaternary"). States
<Button tone="brand" loading label="Submitting" /><Button tone="brand" disabled label="Locked" /><Button tone="brand" selected label="Toggled on" /><Button tone="brand" loading disabled label="Critical" />loading— diagonal stripe overlay slides across the button. Stripe color followscurrentColor, so it tracks the tone. Blocks clicks viapointer-events: none.disabled— locks the colors to the disabled palette, setspointer-events: none, and removes the button from the tab order. Beats inline--button-bgoverrides.selected— toggled-on / pressed visual; shares the active state’s look. Useful for filter chips and icon toggles.
Link mode
<Button tone="brand" href="/docs" target="_blank" label="Read the docs" />href switches the rendered tag from <a-button> to
<a role="button">. Styling is identical — both selectors share the
same CSS rules. Linking with routing libraries
Anta’s CSS targets a-button, a[role="button"] — an anchor (<a>) with
role="button" and the right attributes gets the styling. That means anta doesn’t
need an as / asChild / component prop or per-framework integrations.
Compose your own thin wrapper around
your library’s Link component — which renders an <a>, so it matches the
selector.
// LinkButton.tsx — anta-styled link for client-side routingimport { Link, type LinkProps } from 'react-router-dom'import type { IconShape } from '@antadesign/anta'
type LinkButtonProps = LinkProps & { tone?: 'neutral' | 'brand' | 'critical' | 'info' | 'success' | 'warning' priority?: 'primary' | 'secondary' | 'tertiary' | 'quaternary' size?: 'small' | 'medium' | 'large' icon?: IconShape iconTrailing?: IconShape label?: string}
export const LinkButton = ({ tone, priority, size, icon, iconTrailing, label, children, ...rest}: LinkButtonProps) => ( <Link role="button" tone={tone} priority={priority} size={size} {...rest}> {icon && <a-icon shape={icon} aria-hidden="true" />} {label != null && <a-button-label>{label}</a-button-label>} {children} {iconTrailing && <a-icon shape={iconTrailing} aria-hidden="true" />} </Link>)Usage:
<LinkButton to="/dashboard" tone="brand" label="Dashboard" /><LinkButton to="/docs" priority="tertiary" iconTrailing="external-link" label="Docs" />This approach would work for Next.js <Link>, TanStack Router, or any
other routing library.
Special events
Beyond a plain click, a Button can drive a native form or emit your own
event:
Form submission
For non-anchor buttons, type="submit" and type="reset" integrate
with native forms. type="submit" calls form.requestSubmit() and also
dispatches a submitdetailed event on the form with
{ formData, submitter: { tag, attrs } } in detail — handy for
analytics or multi-button forms.
<form id="signup"> <Button tone="brand" type="submit" label="Sign up" /> <Button tone="brand" priority="tertiary" type="reset" label="Clear" /></form>{/* Associate with a form by id when the button is outside */}<Button tone="neutral" type="submit" form="signup" label="Submit from outside" />Custom click events
data-custom-event="<name>" makes the button dispatch a bubbling
CustomEvent("<name>") on click. Use it to instrument analytics
without taking ownership of onClick.
<Button tone="brand" label="Save" data-custom-event="save-clicked" /> Playground
Component props
| Prop | Type | Default | Description |
|---|---|---|---|
| disabled? | boolean | — | Disable the button. |
| download? | stringboolean | — | Anchor download attribute. Empty string / true triggers a download with the resource's default name; a string overrides the filename. |
| form? | string | — | Form id when the button isn't a descendant of its form. |
| href? | string | — | Renders as <a role="button"> instead of <a-button>. |
| icon? | IconShape | — | Leading icon shape. When set alone (no label, no iconTrailing, no
children), the button renders as a square icon-only control and
the wrapper auto-supplies aria-label={icon} (override by passing
your own aria-label). |
| iconTrailing? | IconShape | — | Trailing icon shape. Renders after children, last in the slot order. |
| label? | string | — | Label text. Renders between the leading icon and children. |
| loading? | boolean | — | Show a rotating loading indicator. Blocks clicks. |
| onClick? | (e) => void | — | Click handler. |
| paddingless? | boolean | — | Drops outer padding to zero. |
| ping? | string | — | Space-separated URLs the browser pings on navigation. |
| priority? | primarysecondarytertiaryquaternary | secondary | Visual emphasis. |
| rel? | string | — | Anchor rel. |
| selected? | boolean | — | Toggled-on / pressed state, e.g. for filter chips. |
| size? | smallmediumlarge | medium | Size variant. small=24px, medium=28px, large=32px. Omit the
attribute or pass 'medium' for the default — both render
identically and emit no DOM attribute. |
| tabIndex? | number | 0 | Tab order. The button is keyboard-focusable by default (0) and
becomes -1 automatically while disabled — <a-button> and
<a role="button"> aren't focusable without an explicit tabindex. |
| target? | string | — | Anchor target. |
| tone? | neutralbrandcriticalinfosuccesswarningstring | neutral | Semantic tone, or any literal CSS color ('#ff1493', 'rebeccapurple')
for a one-off custom tone. Primary uses the color as-is; secondary,
tertiary, and quaternary take its hue and pin lightness/chroma to the
brand curve so any input stays legible. |
| type? | buttonsubmitreset | — | Form submission type. |
| underline? | soliddasheddotted | — | Underline style. |
Inherited props (children, className, id, style, title)
| Prop | Type | Default | Description |
|---|---|---|---|
| children? | ReactNode | — | Child elements. When provided, replaces the component's default label/content. |
| className? | string | — | CSS class name. Merged with any internal classes by the component. |
| id? | string | — | HTML id attribute. |
| style? | CSSProperties | — | Inline styles applied to the root element. |
| title? | string | — | HTML title attribute — native browser tooltip on hover. |
Component tokens
Override any of these on a single instance
(style={{ '--button-padding-x': '12px' }}) or on a selector wrapping
the button (e.g. a tone or variant of your own). Color tokens are
dual-declared hex; oklch; alpha fills use color-mix(in oklch, …)
with a hex8 fallback first.
| Token | Description |
|---|---|
--button-fg | Text and icon color. Resolved per tone × priority × state. |
--button-bg | Background fill. |
--button-focus | Focus-ring outline color. |
--button-padding-x--button-padding-y | Inner spacing. |
--button-timing-in--button-timing-out--button-timing-active | Per-state transitions — hover-in 75ms ease-in, return-to-rest 150ms ease-out, press 50ms linear. Quaternary zeroes all three. |
--button-loading-duration | One slide cycle for the loading stripe (default 0.5s). |
--button-loading-angle | Stripe angle (default 125deg). |
--button-loading-period | Stripe + gap along the gradient axis (default 30px). |
--button-loading-stripe--button-loading-stripe-gap | Stripe geometry (default 14px / 16px). |
--button-loading-opacity | Overlay opacity (default 0.25). |
--button-loading-blur | Stripe-edge softening (default 2.5px). |