Composition and customisation
Choose a Luke UI component, primitive, or token for custom interface patterns.
Start with a semantic component when its purpose matches the task. It handles the intended behaviour, accessibility, and visual treatment together. Compose from a documented primitive only when a component's props do not fit.
Start with the component API
Luke UI components handle common complexity together. Use a primitive only when a component's props do not fit a custom composition.
import { Button } from '@luke-ui/react/button';
import { InputGroup } from '@luke-ui/react/primitives/input-group';Button includes its standard label, icon slots, and pending treatment. Use the button primitive
when you need a different child layout or loading treatment while keeping Luke UI button behaviour
and styling.
Each primitive page documents the structure and accessibility responsibilities for that primitive: the label, description, state, and control relationships it expects. Preserve or author those relationships when you compose one. Do not depend on React Aria contexts, slots, or Luke UI implementation details as an application API.
import { Button } from '@luke-ui/react/primitives/button';
export function SaveShortcutButton() {
return (
<Button appearance="solid" tone="accent">
<span>Save changes</span>
<span aria-hidden>⌘S</span>
</Button>
);
}Build a custom composition
Choose the primitive that owns the behaviour you need. Then compose its documented public parts. For
example, use the field primitive for a label, description, and control arrangement. Use the combobox
primitives when ComboboxField does not fit your control, popover, or loading UI.
Give the custom component the same accessibility care as a normal component. Preserve its documented structure. Provide an accessible name. Keep keyboard and focus behaviour intact. Use a normal React element for surrounding content when no primitive is needed.
Render a different DOM component
The Button primitive exposes render when you need a small implementation detail that the Button
component API does not provide. Spread the supplied DOM props onto the expected <button> element
type. Return one DOM root. Custom wrappers must pass the supplied ref to that element.
The second render argument contains interaction state such as isPressed.
For Box, use elementType for a supported structural element. It does not change Box's accepted
DOM props. Use Box's render prop when the callback must own the element and its DOM attributes. Do
not combine render with elementType. Use Link and Button when you need their behaviour.
Read the Box documentation for the supported elements and the render
callback's resolved props.
Style the new pattern
Use public semantic variables from @luke-ui/react/theme for custom surfaces, typography, colour,
radius, and depth. They follow the active theme and colour mode. Read
Styling for when to use vars, and the
token reference for the contract.
Read the primitive documentation before you build a custom component from a specific primitive.