Changelog Azalea Kit UI 101 Usage Customizing Utilities Form controls Laravel Vue FHIR Types Accessibility Icons Actions Button Button group Layout and structure Card Card section Care summary Care timeline Divider Empty state Footer Header Layout Stack Table Selection and input Calendar Checkbox Checkbox group Color picker Combobox Combobox option Input Input mask Memorable date Number input Option Phone input Radio Radio button Radio group Range Select SSN input Tag Textarea Toggle ZIP code input Images and icons Avatar Icon Icon button Logo Feedback indicators Badge Banner Progress bar Skeleton Skeleton body text Skeleton display text Skeleton thumbnail Spinner Typography Text Navigation Breadcrumb Breadcrumb item Dropdown Menu Menu item Menu label Navigation Nav group Nav item Nav toggle Pagination Tab Tab group Tab panel Top bar Tree Tree item User menu Overlays Dialog Popover Tooltip Utilities Collapsible FHIR address FHIR human name FHIR quantity FHIR questionnaire Format bytes Format date Format number Relative time Resize observer Split panel Tooltip Border Color Shadow Spacing Transition Typography Z-index Contributing Visual Tests Light Dark System

Button

<ahi-button> | AhiButton

Buttons are used primarily for actions, such as “Add”, “Close”, “Cancel”, or “Save”. Plain buttons, which look similar to links, are used for less important or less commonly used actions, such as “view additional details“.

The default form of a button, used for most cases. They are not impactful enough to represent the primary action in a container.

Default button
<ahi-button>Default button</ahi-button>

Examples

Primary

Use a primary button to call attention to an action on a form or to highlight the strongest call to action on a page. Primary buttons should only appear once per container (not including the application header or in a modal dialog). Not every screen requires a primary button.

Primary button
<ahi-button variant="primary">Primary button</ahi-button>

Destructive

The destructive button appears as a final confirmation for a destructive action such as deleting. These are found mostly in confirmation modals.

Destructive button
<ahi-button variant="destructive">Destructive button</ahi-button>

Sizes

Use the size attribute to change a button’s size.

Small button Medium button Large button
<ahi-button size="small">Small button</ahi-button>
<ahi-button size="medium">Medium button</ahi-button>
<ahi-button size="large">Large button</ahi-button>

Plain buttons

Use plain buttons for less important or less commonly used actions since they’re less prominent. For example, plain buttons are used as actions in cards.

A plain button is still a button: it keeps its padding and minimum height, so it doesn’t fit inside a sentence. For navigation, or for a link in or after a sentence, use the ahi-link class instead of restyling a plain button: on an <a> for navigation, or on a <button type="button"> for an action inside a sentence.

Plain button
<ahi-button plain>Plain button</ahi-button>

Outline buttons

Use outline buttons against shaded or colorful backgrounds. An outline button will maintain the appropriate visual weight and won’t clash with the background color.

Default button Primary button Destructive button
<ahi-button outline>Default button</ahi-button>
<ahi-button variant="primary" outline>Primary button</ahi-button>
<ahi-button variant="destructive" outline>Destructive button</ahi-button>

States

Disabled

Use for actions that aren’t currently available. The surrounding interface should make it clear why the button is disabled and what needs to be done to enable it.

Disabled button Disabled button Disabled button Disabled button Disabled button
<ahi-button variant="primary" disabled>Disabled button</ahi-button>
<ahi-button variant="destructive" disabled>Disabled button</ahi-button>
<ahi-button outline disabled>Disabled button</ahi-button>
<ahi-button plain disabled>Disabled button</ahi-button>
<ahi-button variant="destructive" plain disabled>Disabled button</ahi-button>

Loading

Use when a button has been pressed and the associated action is in progress.

Loading button
<ahi-button variant="primary" loading>Loading button</ahi-button>

It’s often helpful to have a button that works like a link. This is possible by setting the href attribute, which will make the component render an <a> under the hood. This gives you all the default link behavior the browser provides (e.g. CMD/CTRL/SHIFT + CLICK) and exposes the target and download attributes.

Link button
<ahi-button href="https://example.com/" target="_blank">Link button</ahi-button>

When a target is set, the link will receive rel="noreferrer noopener" for security reasons.

Full width

Use for buttons placed in a narrow column (especially when stacking multiple buttons) or for creating a set of buttons of equal width. Full-width buttons should rarely exceed 320 px wide.

Full width button
<ahi-button style="width: 100%;">Full width button</ahi-button>

Button with icons

Buttons may include an icon before or after the text. Use the prefix and suffix slots to add icons.

Settings Refresh
<!-- Icon before -->
<ahi-button>
  <ahi-icon slot="prefix" name="settings" size="minor"></ahi-icon>
  Settings
</ahi-button>

<!-- Icon after -->
<ahi-button>
  <ahi-icon slot="suffix" name="refresh" size="minor"></ahi-icon>
  Refresh
</ahi-button>

Disclosure

Use the disclosure attribute to add a dropdown indicator when a button will trigger a dropdown, menu, or select.

Dropdown Dropdown Select
<div style="display: flex; gap: var(--ahi-space-2)">
  <ahi-button disclosure>Dropdown</ahi-button>
  <ahi-button disclosure="up">Dropdown</ahi-button>
  <ahi-button disclosure="select">Select</ahi-button>
</div>

Showing and hiding content

When a button shows and hides a section of the page, set aria-expanded on the button, set aria-controls to the section’s id, and update aria-expanded each time the section opens or closes. The button passes both to the <button> it renders, which is the element screen readers announce, so they say whether the section is open. It passes aria-controls as a reference to the section itself, because an id can’t be found from inside the shadow root, so the internal <button>’s own aria-controls attribute reads empty in the browser’s developer tools. The id must be in the same document or shadow root as the button. A section added to the page after the button is linked the next time the button updates or takes focus.

A button that triggers an <ahi-dropdown> or <ahi-popover> doesn’t need aria-expanded: the dropdown or popover sets it, with aria-haspopup, as soon as it has rendered, and updates it as it opens and closes. That holds for the button placed directly in the trigger or anchor slot; when it is wrapped in another element there, the dropdown or popover sets them on the wrapper instead, and the button gets neither.

<ahi-button class="button-disclosure" disclosure aria-expanded="false" aria-controls="button-disclosure-filters">
  Filters
</ahi-button>

<div id="button-disclosure-filters" hidden>
  <p>Show only visits from the last 30 days.</p>
</div>

<script>
  const button = document.querySelector('.button-disclosure');
  const filters = document.getElementById('button-disclosure-filters');

  button.addEventListener('click', () => {
    const open = button.getAttribute('aria-expanded') !== 'true';
    button.setAttribute('aria-expanded', String(open));
    button.disclosure = open ? 'up' : 'down';
    filters.hidden = !open;
  });
</script>

Custom styling

This example demonstrates how to style buttons using a custom class. This is the recommended approach if you need to add additional variations. To customize an existing variation, modify the selector to target the button’s variant attribute instead of a class (e.g. ahi-button[variant="primary"]).

Button
<ahi-button class="magenta">Button</ahi-button>

<style>
  ahi-button.magenta::part(base) {
    background: linear-gradient(
      15deg,
      var(--ahi-brand-navy),
      var(--ahi-color-purple-700),
      var(--ahi-color-purple-400),
      var(--ahi-color-purple-50)
    );
    border-radius: var(--ahi-border-radius-medium);
  }

  ahi-button.magenta::part(base):hover {
    filter: brightness(90%);
  }

  ahi-button.magenta::part(base):active {
    filter: brightness(75%);
  }

  ahi-button.magenta::part(label) {
    color: var(--ahi-text-on-primary);
    font-size: 1.25rem;
  }
</style>

Keyboard support

  • Give buttons focus with the tab key (or shift + tab when tabbing backwards)
  • Activate buttons with the enter / return key or the space key

Best practices

Buttons should:

  • Be clearly and accurately labeled.
  • Lead with a strong, actionable verb.
  • Use established button colors appropriately. For example, only use a danger button style for an action that’s difficult or impossible to undo.
  • Prioritize the most important actions. Too many buttons can cause confusion.
  • Be consistent with the positioning of buttons in the interface. See Order of actions.

Order of actions

Rank a row’s actions first: one primary action (variant="primary"), then secondary actions (the default button), then tertiary actions (plain). Then put the primary action at the edge the row is aligned to, so people find it in the same place on every screen.

Row Order, left to right Used for
End-aligned (right, in left-to-right languages) Tertiary, secondary, primary Card footers, dialog footers, popover footers, header actions
Start-aligned Primary, secondary, tertiary Form buttons that line up with the fields above them, page-level calls to action
Centered Primary, secondary, tertiary Empty states
  • Keep the source order the same as the order on screen. Keyboard focus and screen readers follow the source, so don’t move the primary action with flex-direction: row-reverse or order.
  • When a row stacks into full-width buttons on a narrow screen, put the primary action on top. For an end-aligned row that takes flex-direction: column-reverse, the one place the order on screen may differ from the source.
  • Use one primary action per row.
  • When a row confirms an action that can’t be undone, like Delete in a delete dialog, the destructive button takes the primary position and Cancel is secondary.
  • Back and Next keep their direction whatever the alignment: Back at the start, Next at the end.
  • In a row that spans the full width, a tertiary action such as Clear all can sit alone at the start edge, with the other actions at the end.

A card footer is end-aligned by default:

Two-step authentication asks for a code at every sign-in, so only you can get into your account. Learn more Not now Turn on
<ahi-card title="Secure your account with 2-step authentication">
  <ahi-card-section>Two-step authentication asks for a code at every sign-in, so only you can get into your account.</ahi-card-section>
  <ahi-button slot="footer-actions" plain>Learn more</ahi-button>
  <ahi-button slot="footer-actions">Not now</ahi-button>
  <ahi-button slot="footer-actions" variant="primary">Turn on</ahi-button>
</ahi-card>

Form buttons line up with the fields above them, so the row is start-aligned:

Save Cancel
<div class="ahi-column ahi-gap-4" style="max-width: 24rem">
  <ahi-input label="Preferred name"></ahi-input>
  <div class="ahi-row ahi-gap-2">
    <ahi-button variant="primary">Save</ahi-button>
    <ahi-button>Cancel</ahi-button>
  </div>
</div>

Buttons are used primarily for actions, such as “Add”, “Close”, “Cancel”, or “Save”.

Links are used primarily for navigation, and usually appear within or directly following a sentence. Style them with the ahi-link class, which takes the surrounding font so the link wraps with the sentence.

The HTML that renders for the button component and anchor tags carry meaning. Using these intentionally and consistently results in:

  • a more inclusive experience for assistive technology users
  • a more cohesive visual experience for sighted users
  • products that are easier to maintain at scale

Content guidelines

  • Always write button text in sentence case, which means the first word is capitalized and the rest are lowercase (unless a term is a proper noun).
  • Avoid unnecessary words and articles such as “the,” “an,” or “a.”
  • To combine or lay out multiple buttons, use the button group component
  • For navigational actions that appear within or directly following a sentence, use <a> (anchor) tags.

Importing

Script Bundler

To import this component using a script tag:

<script type="module" src="/path/to/azalea-kit-ui/dist/components/button/button.js"></script>

To import this component using a bundler:

import '@azaleahealth/azalea-kit-ui/dist/components/button/button.js';

Slots

Name Description
(default) The button’s label.
prefix Used to prepend an icon or similar element to the button.
suffix Used to append an icon or similar element to the button.

Learn more about using slots.

Properties

Name Description Type Default
disabled Disables the button. boolean false
disclosure Draws the button with a disclosure icon. Used to indicate that the button triggers a dropdown menu or similar behavior. 'up' | 'down' | 'select' | undefined -
download Tells the browser to download the linked file as this filename. Only used when href is present. string | undefined -
formAction
formaction
Used to override the form owner’s action attribute. string -
formEnctype
formenctype
Used to override the form owner’s enctype attribute. 'application/x-www-form-urlencoded' | 'multipart/form-data' | 'text/plain' -
formMethod
formmethod
Used to override the form owner’s method attribute. 'post' | 'get' -
formNoValidate
formnovalidate
Used to override the form owner’s novalidate attribute. boolean -
formTarget
formtarget
Used to override the form owner’s target attribute. '_self' | '_blank' | '_parent' | '_top' | string -
href When set, the underlying button will be rendered as an <a> with this href instead of a <button>. string ''
loading Replaces button text with a spinner while a background action is being performed. boolean false
name The name of the button, submitted as a name/value pair with form data, but only when this button is the submitter. This attribute is ignored when href is present. string ''
outline Draws an outlined button. boolean false
plain Draws a button that looks like a link boolean false
size The button’s size. 'small' | 'medium' | 'large' 'medium'
target Tells the browser where to open the link. Only used when href is present. '_blank' | '_parent' | '_self' | '_top' -
type The type of button. Note that the default value is button instead of submit, which is opposite of how native <button> elements behave. When the type is submit, the button will submit the surrounding form. 'button' | 'submit' | 'reset' 'button'
rel When using href, this attribute will map to the underlying link’s rel attribute. Unlike regular links, the default is noreferrer noopener to prevent security exploits. However, if you’re using target to point to a specific tab/window, this will prevent that from working correctly. You can remove or change the default value by setting the attribute to an empty string or a value of your choice, respectively. string 'noreferrer noopener'
value The value of the button, submitted as a pair with the button’s name as part of the form data, but only when this button is the submitter. This attribute is ignored when href is present. string ''
variant The button’s theme variant. 'default' | 'destructive' | 'primary' 'default'
validity Gets the validity state object - -
validationMessage Gets the validation message - -
formId
form
By default, form controls are associated with the nearest containing <form> element. This attribute allows you to place the form control outside of a form and associate it with the form that has this id. The form must be in the same document or shadow root for this to work. string ''
form The form the control is associated with, following the native form-control contract: reading returns the associated <form> element (or null if there is none), assigning takes a form id — assign an empty string to restore association with the nearest containing form. HTMLFormElement | null -
updateComplete A read-only promise that resolves when the component has finished updating.

Learn more about properties and attributes.

Events

Name Description Event Detail
ahi-blur Emitted when the button loses focus. -
ahi-focus Emitted when the button gains focus. -
ahi-invalid Emitted when the form control has been checked for validity and its constraints aren’t satisfied. -

Learn more about events.

Methods

Name Description Arguments
click() Simulates a click on the button. -
focus() Sets focus on the button. options: FocusOptions
blur() Removes focus from the button. -
checkValidity() Checks for validity but does not show a validation message. Returns true when valid and false when invalid. -
reportValidity() Checks for validity and shows the browser’s validation message if the control is invalid. -
setCustomValidity() Sets a custom validation message. If message is not empty, the field will be considered invalid. message: string
resetValidity() Hides validity again, returning the control to its pristine state: it stops matching :user-invalid and drops its error styling until the user next interacts with it. The value and the validity are both untouched — an empty required control is still invalid and still blocks submission. This only changes whether that invalidity is shown. Interaction state otherwise has exactly one automatic exit, form.reset(). Call this when the application replaces a value itself and the old error no longer describes anything the user did: loading a draft, switching which record is being edited, or clearing a filter row. -

Learn more about methods.

CSS Parts

Name Description
base Deprecated. Use the button part instead.
button The component’s outer wrapper.
prefix The prefix slot’s container.
label The button’s label.
suffix The suffix slot’s container.
disclosure The button’s disclosure icon, an <ahi-icon> element.
spinner The spinner that shows when the button is in the loading state.

Learn more about customizing CSS parts.

Dependencies

This component automatically imports the following dependencies.

  • <ahi-icon>
  • <ahi-spinner>