# Composition and customisation (/docs/composition)



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 [#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.

```tsx
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.

apps/docs/src/samples/styling/primitive.tsx

```tsx
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 [#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.

apps/docs/src/examples/composition/amount-field.tsx

```tsx
import { Field } from '@luke-ui/react/primitives/field';
import {
	InputGroup,
	InputGroupInput,
	InputGroupPrefix,
} from '@luke-ui/react/primitives/input-group';

export default () => {
	return (
		<Field description="Enter an amount in dollars." label="Amount">
			<InputGroup>
				<InputGroupPrefix>$</InputGroupPrefix>
				<InputGroupInput inputMode="decimal" name="amount" placeholder="0.00" />
			</InputGroup>
		</Field>
	);
};
```

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 [#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.

apps/docs/src/examples/composition/animated-button.tsx

```tsx
import { Button } from '@luke-ui/react/primitives/button';
import { mergeStyleProps } from '@luke-ui/react/utils';

export default () => {
	return (
		<Button
			render={(domProps, { isHovered, isPressed }) => {
				const scale: number = (() => {
					if (isPressed) return 0.98;
					if (isHovered) return 1.02;
					return 1;
				})();

				const buttonProps = mergeStyleProps(domProps, {
					style: {
						transform: `scale(${scale})`,
						transition: 'transform 100ms',
					},
				});

				return <button {...buttonProps} />;
			}}
		>
			Continue
		</Button>
	);
};
```

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](/components/layout/box) for the supported elements and the `render`
callback's resolved props.

## Style the new pattern [#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](/docs/styling#use-semantic-variables-for-custom-ui) for when to use `vars`, and the
[token reference](/docs/token-reference) for the contract.

Read the [primitive documentation](/components/primitives/button) before you build a custom
component from a specific primitive.

## Continue learning [#continue-learning]

<Cards>
  <Card href="/docs/styling" title="Styling">
    Choose component props, recipes, layout utilities, or semantic variables.
  </Card>

  <Card href="/components/layout/box" title="Box">
    See Box's elementType and render contract.
  </Card>

  <Card href="/components/primitives/button" title="Button primitive">
    Build a custom button layout on the documented primitive.
  </Card>
</Cards>
