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

Customizing

Azalea Kit UI components can be customized at a high level through design tokens. For more advanced customizations, you can use CSS parts and custom properties to target individual components.

Most apps should not need to customize Azalea Kit UI’s components. However, the library was designed with customization in mind so that we can support things like dark mode, white labeling, or custom network themes in myHealthspot.

Design tokens

Azalea Kit UI uses several design tokens to provide a consistent appearance across components. You can customize them and use them with pure CSS – no preprocessor required.

Design tokens offer a high-level way to customize the library with minimal effort. There are no component-specific variables as design tokens are intended to be generic and highly reusable. To customize an individual component, refer to the section entitled CSS parts.

Design tokens are accessed through CSS custom properties that are defined in the theme. Because design tokens live at the page level, they’re prefixed with --ahi- to avoid collisions with other libraries.

To customize a design token, simply override it in your stylesheet using a :root block. Here’s an example that changes primary buttons to purple, using the palette’s purple ramp.

:root {
  --ahi-action-primary: var(--ahi-color-purple-600);
  --ahi-action-primary-disabled: var(--ahi-color-gray-200);
  --ahi-action-primary-hovered: var(--ahi-color-purple-700);
  --ahi-action-primary-depressed: var(--ahi-color-purple-800);
}

See the “Design Tokens” section in the sidebar for more information about each design token. For a complete list, refer to src/themes/light.css in the project’s source code.

CSS parts

Whereas design tokens offer a high-level way to customize the library, CSS parts offer a low-level way to customize individual components. Again, this is done with pure CSS — no preprocessor required.

Azalea Kit UI components use a shadow DOM to encapsulate their styles and behaviors. As a result, you can’t simply target their internals with the usual CSS selectors. Instead, components expose “parts” that can be targeted with the CSS part selector, or ::part().

Here’s an example that modifies buttons with the amb-btn class.

Button
<ahi-button variant="primary" class="amb-btn">Button</ahi-button>

<style>
  .amb-btn[variant='primary']::part(button) {
    background: var(--ahi-color-blue-500);
    border: 1px solid var(--ahi-color-blue-600);
    border-radius: 2px;
    line-height: 1.1rem;
    height: 2rem;
  }

  .amb-btn[variant='primary']::part(base):hover {
    border: 1px solid var(--ahi-color-blue-700);
    background: var(--ahi-color-blue-600);
    box-shadow: 1px 1px var(--ahi-color-gray-700);
  }

  .amb-btn[variant='primary']::part(base):active {
    box-shadow: 0 0 6px var(--ahi-color-gray-700) inset;
    translate: none;
  }

  .amb-btn[variant='primary']::part(label) {
    color: var(--ahi-text-on-primary);
    font-family: Arial;
    font-size: 1rem;
    font-weight: 700;
    margin: 0 1px;
    text-shadow: 0 1px 0 var(--ahi-color-blue-800);
    padding: 4px 7px;
  }
</style>

At first glance, this approach might seem a bit verbose or even limiting, but it comes with a few important advantages:

  • Customizations can be made to components with explicit selectors, such as ::part(icon), rather than implicit selectors, such as .button > div > span + .icon, that are much more fragile.

  • The internal structure of a component will likely change as it evolves. By exposing component parts through an API, the internals can be reworked without fear of breaking customizations as long as its parts remain intact.

Most (but not all) components expose parts. You can find them in each component’s API documentation under the “CSS Parts” section.

Custom properties

For convenience, some components expose CSS custom properties you can override. These are not design tokens, nor do they have the same --ahi- prefix since they’re scoped to a component.

You can set custom properties on a component in your stylesheet.

ahi-spinner {
  --color: var(--ahi-color-green-600);
}

This will also work if you need to target a subset of components with a specific class.

ahi-spinner.your-class {
  --color: var(--ahi-color-green-600);
}

Alternatively, you can set them inline directly on the element.

<ahi-spinner style="--color: var(--ahi-color-green-600);"></ahi-spinner>

Not all components expose CSS custom properties. For those that do, they can be found in the component’s API documentation.

The host contract: text size

Azalea Kit UI isolates itself from the page around it — line height, letter spacing, text transform and font weight are all pinned inside each component, so dropping one into your app does not inherit your page’s typography by accident.

font-size is pinned too, through a token you can re-point. A component whose text does not have its own explicit size renders at --ahi-font-size-400 (16px, scaled by --ahi-font-scale), whatever font-size your page sets around it. An app whose root is not 16px — html { font-size: 10pt }, say — still gets AKU text at the sizes shown in these docs.

The consequence is worth stating plainly, because it changed in 5.0.0:

Text you size around a component no longer reaches inside it. A 14px table cell holding an <ahi-collapsible> renders its label at 16px, and the same goes for slotted content. Before 5.0.0, AKU components inherited their size from the page.

The exception is inline text that is meant to be typeset with the copy around it — the format-* components, <ahi-relative-time>, <ahi-fhir-human-name> and <ahi-fhir-quantity>. Those always inherit, so a name in a heading is set at the heading’s size, and --ahi-component-font-size does not reach them: to size one, set font-size on it or on the text around it.

Where you want components to take their size from the text around them, set --ahi-component-font-size to 1em on the container:

.compact-table {
  --ahi-component-font-size: 1em;
}

Every AKU component inside that container then takes its size from its parent, slotted content included. Set it on any ancestor you like, and re-point it per region — the nearest declaration wins. Use 1em, not inherit: a CSS-wide keyword on a custom property applies to the property itself, so inherit leaves the default in place.

To have AKU text follow the density tier instead, point the token at the body type role on the element that carries the tier class, or inside it:

.ahi-density-spacious {
  --ahi-component-font-size: var(--ahi-type-body-font-size);
}

Declared above the tier, the body role has already resolved to the compact 14px by the time the tier is reached.

Two notes on scope:

  • It is inherited, so it reaches components nested inside other components, and it reaches slotted content.
  • It never overrides a size you set yourself. A rule on the element — ahi-collapsible { font-size: 18px } — always wins, because declarations from outside a component beat the component’s own :host rules regardless of specificity.

Animations

Some components use animation, such as when a dialog is shown or hidden. Animations are performed using the Web Animations API rather than CSS. However, you can still customize them through Azalea Kit UI’s animation registry. If a component has customizable animations, they’ll be listed in the “Animation” section of its documentation.

To customize a default animation, use the setDefaultAnimation() method. The function accepts an animation name (found in the component’s docs) and an object with keyframes, and options or null to disable the animation.

This example will make all dialogs use a custom show animation.

import { setDefaultAnimation } from '@azaleahealth/azalea-kit-ui/utilities/animation-registry.js';

// Change the default animation for all dialogs
setDefaultAnimation('dialog.show', {
  keyframes: [
    { transform: 'rotate(-10deg) scale(0.5)', opacity: '0' },
    { transform: 'rotate(0deg) scale(1)', opacity: '1' }
  ],
  options: {
    duration: 500
  }
});

If you only want to target a single component, use the setAnimation() method instead. This function accepts an element, an animation name, and an object comprised of animation keyframes and options.

In this example, only the target dialog will use a custom show animation.

import { setAnimation } from '@azaleahealth/azalea-kit-ui/utilities/animation-registry.js';

// Change the animation for a single dialog
const dialog = document.querySelector('#my-dialog');

setAnimation(dialog, 'dialog.show', {
  keyframes: [
    { transform: 'rotate(-10deg) scale(0.5)', opacity: '0' },
    { transform: 'rotate(0deg) scale(1)', opacity: '1' }
  ],
  options: {
    duration: 500
  }
});

To learn more about creating Web Animations, refer to the documentation for Element.animate().

Animations respect the users prefers-reduced-motion setting. When this setting is enabled, animations will not be played. To disable animations for all users, pass in null instead of a keyframes/options object.