Reference generated from the custom elements manifest — 16 properties, 3 slots, 0 events.
CODE — HTML / REACT
Guidance
The system's action control. It renders a real <button> — or an <a role="button"> when href is set — so the element the user actually gets, and the keyboard behaviour that comes with it, follows the props rather than the styling. Everything else about a button is ranking: variant says how much weight this action carries against the others on screen.
WHEN TO USE
- The control performs an action in the current page: submits a form, opens a dialog, applies a filter, saves a draft.
- The action has to participate in a form.
type="submit"andtype="reset"are routed through the shared FormController, so a button inside a<form>behaves like a native one even across the shadow boundary. - A dense toolbar needs an icon-only affordance: set
hideText, supplylabel, and put the glyph in thebeforeslot. - You need to rank actions visually —
primary,secondary,tertiary,neutralandbareare the whole ladder, and there is no sixth treatment.
WHEN NOT TO USE
- The control only navigates, and should read as text inside a sentence or a list of destinations.
hrefdoes swap in an<a>, but it keeps button styling androle="button"— use it only when the navigation is meant to look and be announced like a button.USE LINK → - You are putting several buttons side by side and what you actually want is control over their spacing. There is no button-group component: it was removed precisely because it owned arrangement and nothing else.USE LAYOUT →
- The control flips a state that stays visible after the click — a filter pill, a view switch, a formatting toggle. That is selection, not invocation, and it wants a component that owns the selected state.USE TOGGLE-BUTTON →
- The trigger opens a list of navigation destinations. Let the menu own its own trigger relationship rather than wiring a bare button to a floating list by hand.USE MENU →
DO
- For an icon-only button, set
hideText, supplylabel, and place the icon in thebeforeslot. That exact pattern is documented on the component with a worked example, and it is the only combination that produces one accessible name. - Gate the action in your own click handler when the button is disabled. The component renders
aria-disabledbut not the nativedisabledattribute, and its click handler has no disabled branch, so a disabled submit button still submits. - Drive
isExpandedfrom the open state of whatever the button controls, so the control and the thing it discloses never disagree. - Reserve
variant="primary"for the one action you most want taken on a screen. A strongest-emphasis treatment used for merely important actions stops ranking anything. A destructive confirm is a primary button with destructive copy — status lives onal-badgeandal-alert, not on this axis.
DON’T
- Don't combine
hideTextwith visible slot text. The text node is only visually hidden — it stays in the layout and in the accessibility tree, so it duplicateslabelinto a redundant accessible name. - Don't rely on the glyph alone for meaning. With
hideTextset and nolabel, the button ships with no accessible name at all. - Don't assume
ariaControlsreaches the DOM on a plain button. It is rendered on thehrefbranch only; the<button>branch emitsaria-label,aria-disabled,aria-pressedandaria-expandedand stops there. - Don't use
variant="bare"as a way to draw a link. A bare button is still announced as a button, and still activates on Space. - Don't leave a disabled button as the only visible path forward without saying somewhere what would enable it. A disabled control is not an error message.
ACCESSIBILITY IN PRACTICE
labelbecomesaria-labelon both rendered branches, and it is the only accessible name an icon-only button has.isPressedacceptstrue,falseormixedand passes straight through toaria-pressed—mixedis the tri-state case, such as a select-all whose children are partly checked. It is state you must keep in sync, not a style hook.isDisabledrendersaria-disabledonly. The control therefore keeps its tab stop and can still be reached, focused and announced as unavailable — which is the accessible behaviour — but it also remains clickable, so the guard has to live in your handler.- Setting
hrefproduces<a role="button">. Assistive technology will announce a button while the browser still treats it as a link for middle-click, copy-link and Space-vs-Enter purposes.
CONTENT GUIDELINES
- Label the action, not the mechanism — a verb plus the object it acts on: "Save changes", "Delete project", "Invite teammate".
- Let the surrounding UI carry the object when it already does. A dialog titled "Delete project" can end in "Delete"; a button floating in a toolbar cannot.
- Write an icon-only button's
labelas the whole sentence a screen-reader user hears, not as a tooltip fragment: "Open actions menu", not "Actions".
WHERE THIS CAME FROM
libs/al-web-components/components/button/button.tsThe icon-only recipe, its worked example and its explicit do-not, written on the class and previously visible to nobody outside the source file.libs/al-web-components/components/button/button.ts`href` swapping the rendered element from `<button>` to `<a>` — the reason this page distinguishes navigation from action.libs/al-web-components/components/button/button.tsThe disabled treatment is advisory only: `aria-disabled` is emitted, the native `disabled` attribute is not, and the click handler does not check it.libs/al-web-components/components/button/button.tsThe single `aria-controls` render site, inside the link branch — the basis for the do-not about disclosure on a plain button.AGENTS.mdThe rule that removed `al-button-group` and sends a row of buttons to `al-layout` instead.
Each line above names a file and a literal string that must still appear in it.pnpm gate:guidance re-reads them from this built page, so guidance that has stopped being true fails a check rather than misleading you.
FIGMA PARITY
Code and Figma each changed since the last confirmed sync. Neither side should be overwritten blindly.
- DECIDED BY
- SOURCE HASH
- FIGMA SIDE
- OBSERVED
- LAST CONFIRMED SYNC
- 2026-08-22
- PUBLIC SURFACE
- 16 ATTR · 3 SLOT · 0 EVENT · 1 PART
A sha1 of every .ts/.scss byte in the component directory, compared against the hash stored at the last sync. It cannot say what changed, and a comment edit moves it. Re-stamping this component records a contract digest instead.
ACCESSIBILITY
The CI gate disables color-contrast globally (libs/al-web-components/story-fixture/src/main.ts), so its result never reaches a test run. It is enabled for this measurement, and reported here whichever way it comes out.
NO CONTRAST VIOLATIONS ACROSS THIS COMPONENT’S 32 STORIES.
INSTALL
import '@southleft/al-web-components/components/button';
import { ALButton } from '@southleft/al-react';API — 16 PROPS
SLOTS
EVENTS
THIS COMPONENT DECLARES NO EVENTS IN THE MANIFEST.
CSS PARTS
PUBLIC METHODS
SOURCE — components/button/button.ts · Atoms/Button