Luke UI
Overview

Styling

Choose component APIs, primitives, recipes, variables, or themes without overriding internals.

Luke UI ships static CSS. It does not inject styles at runtime. Import the shared stylesheet and a theme stylesheet once, then use component props for the variations each component supports.

Choose a styling approach

Work from the most specific public API to the broadest one.

  1. Use a component's props for its supported appearance, size, state, and behaviour.
  2. Use a documented primitive when you need a different component structure.
  3. Build custom UI with public recipes and semantic variables when the component is not provided.
  4. Author a custom theme when the product needs a different visual foundation everywhere.

Do not override component recipe selectors or implementation-state attributes such as data-hovered, data-pressed, or data-focus-visible. They are not supported styling hooks and can change without a migration path.

Start with component props

Components are intentionally opinionated. Their public props cover supported variants, states, and behaviour, while the active theme provides the semantic colours, typography, spacing, radii, and depth that make those components work together.

import { Button } from '@luke-ui/react/button';

<Button appearance="subtle" size="small" tone="accent">
	Save changes
</Button>;

Compose from primitives

Use a documented primitive when component props do not provide the structure you need. Primitives keep the accessibility and visual contract for a component family while leaving its children and composition to your application.

import { Button } from '@luke-ui/react/button/primitive';

<Button appearance="solid" tone="accent">
	<span>Save changes</span>
	<span aria-hidden>⌘S</span>
</Button>;

Build custom UI with public recipes and variables

Recipes from @luke-ui/react/recipes are public, component-specific styling APIs. Use one when a custom composition follows that recipe's documented element contract. Do not copy or target its generated selectors.

import { button } from '@luke-ui/react/recipes';

<button className={button({ appearance: 'solid', size: 'medium', tone: 'accent' })} type="button">
	Save changes
</button>;

The public vars token contract lets an application-owned element follow the active theme.

import { vars } from '@luke-ui/react/theme';

<aside
	style={{
		backgroundColor: vars.color.surface.floating,
		borderRadius: vars.radius.surface,
		boxShadow: vars.depth.floating,
		color: vars.color.text.primary,
	}}
/>;

The token contract is public. Component selectors, generated palette values, and theme implementation details are not. See the token reference for the full contract.

Change the visual foundation with a custom theme

Use a custom theme when a product needs a different identity, typeface, or semantic colour system. defineTheme produces a static stylesheet for the full semantic contract from a curated accent and neutral character. It is not a per-component override tool.

Set up static styles

Import @luke-ui/react/stylesheet.css and one bundled theme stylesheet at the application entry point. Apply themeRootClassName and the matching identity class to the same application or subtree root. The reset is scoped to that root, so it does not reset unrelated application content.

import '@luke-ui/react/stylesheet.css';
import '@luke-ui/react/themes/tactile.css';
import { themeRootClassName } from '@luke-ui/react/theme';
import { tactileThemeClassName } from '@luke-ui/react/themes';
import { cx } from '@luke-ui/react/utils';

<div className={cx(themeRootClassName, tactileThemeClassName)}>{children}</div>;

Read Applying a theme for colour modes, portals, and other theme setup details.

Use layout utilities for layout

Box and createSprinkles from @luke-ui/react/styles handle responsive structure, spacing, sizing, positioning, overflow, flex, and grid-child properties. They do not set semantic colour, typography, or pseudo states.

import { Box } from '@luke-ui/react/box';

<Box display="flex" flexDirection={{ xsmall: 'column', medium: 'row' }} gap="400">
	{children}
</Box>;

Box is useful for layout around components. Use createSprinkles when another application-owned element needs the same responsive layout properties. Neither API sets semantic colour, typography, or interaction states.

Use application CSS alongside Luke UI

Luke UI's static CSS uses four cascade layers, ordered from lowest to highest priority: reset, theme, recipes, and utilities. That internal order is stable regardless of stylesheet import order. The reset normalises only the subtree carrying themeRootClassName.

If your application also uses cascade layers, declare its layer order before importing stylesheets.

@layer reset, theme, base, recipes, components, utilities;

@import '@luke-ui/react/stylesheet.css';
@import 'tailwindcss';

Tailwind utilities, CSS Modules, and application CSS work well for application-owned layout and custom elements. Put Tailwind classes on surrounding elements or Box, and use CSS Modules or application styles with public semantic variables for custom UI. Unlayered application CSS sits outside Luke UI's layers, so do not use it to reach into a component's internal DOM.

import { Box } from '@luke-ui/react/box';

<Box className="mx-auto" maxInlineSize="64rem" paddingInline="400">
	{children}
</Box>;

When a CSS Module or application stylesheet needs a token, use the stable --luke-* variable listed in the token reference. Do not couple application CSS to undocumented selectors or implementation attributes.

Continue learning