Luke UI
GitHub repository

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.

Composition and customisation — Build a custom composition
$
Enter an amount in dollars.

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.

Composition and customisation — Render a different DOM component

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.

Continue learning