# Button (/docs/components/actions/button) `Button` expects the Luke UI theme class at the app or root level. See [Getting Started](/docs/getting-started). ```tsx ``` ## Best practices [#best-practices] | Guidance | Practices | | -------- | -------------------------------------------------------------------------------------------------------------- | | Do | Use `tone="accent"` for the main action. Use a subtle or ghost appearance for secondary actions. | | Do | Write a label that names the action, such as "Save changes" or "Delete account". Avoid vague labels like "OK". | | Do | Set `isPending` while an action is in flight so the user can see that work is still happening. | | Don't | Use `Button` for navigation. If the control only moves the user to another page, use `Link`. | ## Tone and appearance [#tone-and-appearance] Tone communicates intent: `neutral` (default), `accent`, or `danger`. Appearance controls emphasis: `solid` (default), `subtle`, or `ghost`. Every tone supports every appearance. ## Size [#size] `Button` has two sizes: `medium` (default) and `small`. ## Icons [#icons] Use `startIcon` and `endIcon` to place an icon before or after the label. The icon inherits the button size, so the icon does not need its own `size` prop. ```tsx import { Icon } from '@luke-ui/react/icon'; ; ``` ## Disabled [#disabled] Disabled buttons cannot be focused or pressed. ```tsx ``` ## Pending [#pending] Set `isPending` while an action is in flight. A spinner replaces the label. The button remains focusable and reports its pending state, but cannot be pressed again. It uses the same muted visual treatment as a disabled button while keeping the spinner visible. ## Full width [#full-width] Set `isBlock` to make the button fill the inline size of its container. ```tsx ``` ## Accessibility [#accessibility] `Button` wraps its children in `Text`, so visible text usually provides the accessible name. You normally do not need `aria-label`. The pending spinner is `aria-hidden` and does not announce busy state to screen readers. If screen reader users need to hear the pending state, change the label text itself, for example to "Saving", while `isPending` is set. ## Primitive [#primitive] Use the [button primitive](/docs/components/primitives/button) when you need full control over children, custom loading states, render-prop children, or non-standard content. ## Props [#props] # Icon Button (/docs/components/actions/icon-button) Use `IconButton` for compact actions where an icon can carry the visible UI. Always provide an accessible label. ```tsx ``` ## Best practices [#best-practices] | Guidance | Practices | | -------- | ---------------------------------------------------------------------------- | | Do | Name the action in `aria-label`, such as "Delete row". Do not name the icon. | | Do | Use `tone="danger"` for destructive icon actions such as delete. | ## Tone and appearance [#tone-and-appearance] `IconButton` shares Button's appearance API. Tone can be `neutral` (default), `accent`, or `danger`. Appearance can be `solid` (default), `subtle`, or `ghost`. ## Size [#size] ## Pending [#pending] Set `isPending` while an action is in flight. The button remains focusable but cannot be pressed again. It uses the disabled visual treatment and replaces the icon with a busy cue. ## Accessibility [#accessibility] `IconButton` has no visible text label. Always provide `aria-label` or `aria-labelledby`. Without one, screen readers have no way to announce the button's purpose. ```tsx ``` ## When to use vs Button [#when-to-use-vs-button] Use `IconButton` when the icon alone communicates the action and space is tight, such as in toolbars, table row actions, or close buttons. Use `Button` when you need a visible text label. Labels are clearer for unfamiliar users. ## Props [#props] # Link (/docs/components/actions/link) `Link` expects the Luke UI theme class at the app or root level. See [Getting Started](/docs/getting-started). ```tsx Help center ``` ## Best practices [#best-practices] | Guidance | Practices | | -------- | ---------------------------------------------------------------------------------------------------------------------------------- | | Do | Use `tone="inverted"` only on dark backgrounds. On light backgrounds, it is hard to read. | | Do | Use `isStandalone` for links that are not part of a sentence, such as card links and nav items. Leave it `false` for inline links. | ## Tone [#tone] `Link` has three tones: `brand` (default), `neutral`, and `inverted`. Use `neutral` for a more subtle link. Use `inverted` on dark backgrounds. ## Standalone [#standalone] Use `isStandalone` when a link stands on its own. Leave it `false` for links inside paragraph text. * `isStandalone={true}`: no underline until hover. * `isStandalone={false}`: underlined inline link style. ## Disabled [#disabled] ## Accessibility [#accessibility] Screen readers announce a disabled link as unavailable, but not why. Put the reason in nearby visible text instead of relying on disabled state alone. ## When to use vs Button [#when-to-use-vs-button] Use `Link` to navigate to a new URL or route. Use `Button` for in-page actions, such as saving, submitting, or opening a dialog. ## Props [#props] # Loading Skeleton (/docs/components/feedback/loading-skeleton) `LoadingSkeleton` expects the Luke UI theme class at the app or root level. See [Getting Started](/docs/getting-started). Use it when loading content should keep the same footprint as the loaded state. Text renders as an inline skeleton. Elements keep their layout while the skeleton surface is painted over them. All mounted skeletons use the same pulse timing, even when they mount at different times. ## Best practices [#best-practices] | Guidance | Practices | | -------- | ------------------------------------------------------------------------------------------------------------------- | | Do | Wrap real content so the skeleton matches the final size exactly. | | Don't | Use `LoadingSkeleton` for content whose final size is unknown. Size mismatch causes layout shift when loading ends. | ## Loading state [#loading-state] `isLoading` defaults to `true`. Pass `isLoading={false}` when content is ready. ```tsx ``` ## Multi-line text [#multi-line-text] Wrap text directly when the copy spans more than one line. Each line gets its own skeleton shape. ## Element type [#element-type] `LoadingSkeleton` renders a `span` by default. Use `as` when the surrounding markup needs another element. ## LoadingSkeletonProvider [#loadingskeletonprovider] Use `LoadingSkeletonProvider` when one loading state controls a group of skeletons. The provider value overrides descendant `isLoading` props. ## Border radius [#border-radius] Use `borderRadius` when the wrapped child has no radius, but the visible control inside it does. ## Custom dimensions [#custom-dimensions] Wrap an element with explicit dimensions when you need a placeholder for a fixed shape, such as an avatar. ## Accessibility [#accessibility] While loading, skeleton content is hidden from assistive technology and cannot be focused or clicked. `LoadingSkeleton` sets `aria-hidden`, `inert`, `tabIndex={-1}`, and disables pointer events. ## Props [#props] # Loading Spinner (/docs/components/feedback/loading-spinner) `LoadingSpinner` expects the Luke UI theme class at the app or root level. See [Getting Started](/docs/getting-started). All mounted indeterminate spinners rotate and pulse in sync, even when they mount at different times. ## Progress mode [#progress-mode] Omit `value` for indeterminate progress. Pass `value` for determinate progress. ## Size [#size] ## Color [#color] ## Accessibility [#accessibility] `aria-label` defaults to `"pending"` when omitted. Override it with what is loading, such as "Loading profile", for a clearer announcement. ## Props [#props] # Combobox Field (/docs/components/forms/combobox-field) Use `ComboboxField` for a single-select combobox with label, description, error, popover, listbox, and optional async loading built in. ## Best practices [#best-practices] | Guidance | Practices | | -------- | ------------------------------------------------------------------------------------------------------- | | Do | Use `defaultItems` for a static list. Use `items` with `loadingState` when options load asynchronously. | | Don't | Use `ComboboxField` for multi-select. It is single-select only in v1. | ## API shape [#api-shape] * Root props follow React Aria `ComboBox` naming. * Composed convenience props include `label`, `description`, `errorMessage`, `necessityIndicator`, `size`, and `placeholder`. * `children` renders items from `items` or `defaultItems`. * `listBoxProps` and `loadMoreItem` are lower-level escape hatches. ## Selection indicators [#selection-indicators] The selected option shows a checkmark in the listbox. When the control has a selection, it shows a clear button before the trigger. Pressing the clear button clears the selection and input text. The clear button is omitted when the field is disabled or read-only. ```tsx {(item) => {item.label}} ``` ## Required fields [#required-fields] Use `isRequired` with `necessityIndicator` to show that the field is mandatory. ## Validation [#validation] Pass field-level validation through React Aria `Form`. Use `errorMessage` to render the validation message. ```tsx import { Form } from 'react-aria-components';
validation.validationErrors.join(' ')} > {(item) => {item.label}}
; ``` ## Grouped options [#grouped-options] Use `ComboboxSection` to group related options inside the listbox. ## Async options [#async-options] Pass `loadingState` for built-in loading and empty states. Control the option collection with `items`. ```tsx {(item) => {item.label}} ``` ## Infinite scroll [#infinite-scroll] Use `onLoadMore` for automatic sentinel-based loading, or `loadMoreItem` when you need full control over the load-more row. ```tsx {(item) => {item.label}} ``` ## Mobile tray [#mobile-tray] Below the `small` breakpoint (640px) the popover renders as a bottom tray instead of a positioned popover, following [Adobe Spectrum's combobox pattern](https://react-aria.adobe.com/blog/building-a-combobox). The tray spans the full viewport width, slides up from the bottom edge on open and back down on close, and keeps clear of the on-screen keyboard using the [Visual Viewport API](https://developer.mozilla.org/en-US/docs/Web/API/Visual_Viewport_API). A scrim dims the page behind the tray, and a grab-handle affordance marks its top edge. In browsers that support `calc-size()`, the tray keeps a usable minimum height even when the viewport is cramped, while still hugging shorter lists instead of leaving empty space. ## Accessibility [#accessibility] The tray's slide transition (and the desktop popover's fade) is disabled for users who request `prefers-reduced-motion: reduce`. ## Primitive [#primitive] Use the [combobox primitives](/docs/components/primitives/combobox) when you need a custom combobox layout. ## Props [#props] # Text Field (/docs/components/forms/text-field) Use `TextField` for a single text input with label, description, validation, and optional adornments built in. ## Best practices [#best-practices] | Guidance | Practices | | -------- | --------------------------------------------------------------------------------------------------------------------------- | | Do | Use `label` for every field where possible. It works better with assistive technology and autofill than `aria-label` alone. | | Don't | Use `placeholder` as a label substitute. It disappears after typing and often fails colour contrast requirements. | ## Required fields [#required-fields] Use `isRequired` and `necessityIndicator` to communicate mandatory fields. `'icon'` renders a visual required marker. `'label'` appends "(required)" to the label text. ## Validation [#validation] Pass field-level validation through React Aria `Form`. Use `errorMessage` to render the validation message. ```tsx import { Form } from 'react-aria-components';
validation.validationErrors.join(' ')} /> ; ``` ## Adornments [#adornments] Use `adornmentStart` and `adornmentEnd` to place non-editable content inside the input chrome. Adornments accept any `ReactNode`. If an adornment is interactive, you are responsible for its semantics. ## Size [#size] `size` controls height and typography. The HTML numeric `` attribute is intentionally omitted because `size` is reserved for the design-system variant. | Value | Description | | ---------- | --------------------- | | `'small'` | Compact input height. | | `'medium'` | Default input height. | ## Accessibility [#accessibility] When visual context already communicates purpose, you may omit `label` and provide an accessible name with `aria-label` or `aria-labelledby` on the field. ```tsx ``` ## Primitive [#primitive] Use the [TextInput primitive](/docs/components/primitives/text-input) when you need the input without the label, description, and error slots that `TextField` provides. ## Props [#props] # Button primitive (/docs/components/primitives/button) Use the button primitive when you need Luke UI button styling without the composed `Button` children wrapper, icon props, or pending-label behaviour. ```tsx import { Button } from '@luke-ui/react/button/primitive'; ; ``` ## When to use [#when-to-use] Use `@luke-ui/react/button/primitive` when you need full control over children layout, render-prop children, custom loading states, or non-standard button content. Use [`Button`](/docs/components/actions/button) for most app actions. ## Behaviour [#behaviour] The primitive renders a single `