Adapting headless components
Ownership boundary
| Concern | Owner |
|---|---|
| Semantics, ARIA, focus, keyboard input | Native or headless primitive |
| Controlled state and emitted events | Existing application/component API |
| Geometry, surfaces, typography, motion | ak-ui CSS and tokens |
| Product copy, brand, information priority | Host application |
Do not replace behavior to make styling easier. Style the rendered parts and public state attributes.
Adapter workflow
- Inventory the primitive's rendered parts, portal behavior, states, CSS variables, and keyboard contract.
- Write the intended component anatomy: root, trigger, surface/content, title, description, controls, and optional metadata.
- Keep the primitive's required parts and accessible names.
- Add stable project-owned or
.ak-*classes to rendered DOM parts. - Map state attributes such as
data-state,data-disabled,data-invalid,aria-selected, andaria-checkedto visible ak-ui states. - Apply semantic tokens and the selected style intensity.
- Verify the real portalled/teleported DOM, focus order, escape behavior, outside interaction, and reduced motion.
State mapping
| Primitive state | Required visual result |
|---|---|
open, expanded, active | Stronger signal plus a structural or position change |
selected, checked, current | Persistent fill/line and a non-color indicator |
disabled | Reduced emphasis and disabled pointer/keyboard behavior from the primitive |
invalid | Danger signal plus text or icon explanation |
loading | Preserve dimensions, expose status text, and avoid blocking motion |
focus-visible | Unclipped, high-contrast focus indicator |
Do not invent a new state store when the primitive already exposes these states.
Styling pattern
Use a narrow opt-in scope and semantic tokens:
[data-ak-ui] .command-option {
min-height: var(--ak-density-control-height);
padding: var(--ak-space-3) var(--ak-space-4);
border: var(--ak-line-hairline) solid transparent;
color: var(--ak-text-primary);
background: var(--ak-surface-raised);
transition:
background var(--ak-motion-fast) var(--ak-ease-standard),
transform var(--ak-motion-fast) var(--ak-ease-standard);
}
[data-ak-ui] .command-option[data-state='checked'] {
border-color: var(--ak-signal-action);
background: color-mix(in srgb, var(--ak-signal-action) 18%, var(--ak-surface-raised));
transform: translateX(var(--ak-space-1));
}
[data-ak-ui] .command-option:focus-visible {
outline: var(--ak-focus-width) solid var(--ak-focus-color);
outline-offset: var(--ak-focus-offset);
}For teleported content, put the opt-in attribute/class on the teleported surface itself or use a global stylesheet. Do not assume it remains a descendant of the application root.
Selecting a behavior foundation
Prefer, in order:
- The headless library already used by the project.
- Native browser semantics when they satisfy the interaction.
- A framework-appropriate headless library already accepted by the user.
- Reka UI for missing complex Vue behavior.
Do not add a headless dependency for cards, decorative panels, labels, basic buttons, or other presentation-only primitives.
Completion criteria
An adapter is complete only when it preserves the original behavior contract, responds to every relevant state, uses public tokens, survives its portal location, works at narrow widths, and exposes visible keyboard focus.
Interfaces and naming
ak-ui treats CSS classes and --ak-* variables as public interfaces. Framework adapters handle properties, events and state mapping without copying a second visual system.
Class structure
Components use .ak-{component} as their root class, __ for internal elements and -- for states and variants:
<button class="ak-button ak-button--action">Start operation</button>
<div class="ak-input-number">
<input class="ak-input-number__inner">
</div>This resembles BEM without adding meaningless levels for naming's sake. Consumers should depend on public classes, not the layout containers of example pages.
Visual variables
To change colors, dimensions or backgrounds, first override the component's public --ak-* variables:
.deployment-panel {
--ak-card-place-color: var(--ak-color-advanced);
--ak-loading-color: var(--ak-color-primary);
}See Design tokens for semantic color, typography, spacing, geometry, motion and focus variables. Component-specific variables are documented on each component page.
Adapter boundaries
- CSS Core is the single source of visual styles and stable class names for existing official components.
- Vue adapters may encapsulate props, slots, events and keyboard interaction, but do not redeclare
<style>. - HTML and Vue use the same
.ak-*classes, so Core fixes to spacing, typography and states apply to both. - Override variables first for visual changes. Edit the copied adapter source when structure or behavior needs to change.
- If ak-ui has no matching component, you may add local styles to an existing native/headless primitive. Reuse
tokens.cssand follow the headless adapter contract.
See the Reka UI example for a complete Vue implementation.