Headless Primitives

Unstyled component logic with full ARIA compliance and keyboard navigation. Zero styling opinions.

What Are Primitives?

Each primitive is a struct implementing the Component trait from stratum-core. Primitives handle all state, ARIA attributes, keyboard interactions, and focus management. They emit RenderOutput which framework adapters translate to actual DOM elements.

Primitives have zero styles and zero framework dependencies. They work with any Rust frontend framework.

The Component Trait

stratum-core
pub trait Component: Sized + 'static {
    type Props: Props;
    type State: State;

    fn initial_state(props: &Self::Props) -> Self::State;
    fn render(props: &Self::Props, state: &Self::State) -> RenderOutput;
    fn on_event(props: &Self::Props, state: &mut Self::State, event: ComponentEvent) -> EventResult;
}

All 23 Primitives

PrimitiveARIA RoleKeyboard
PressablebuttonEnter, Space
CheckboxcheckboxSpace
RadioGroupradiogroup + radioArrow keys, Space
SwitchswitchSpace, Enter
Disclosurebutton + regionEnter, Space
DialogdialogEscape, Tab trap
AlertDialogalertdialogEscape, Tab trap
TooltiptooltipEscape
PopoverdialogEscape
Tabstablist + tab + tabpanelArrow, Home, End
AccordionregionEnter, Space, Arrow, Home, End
Menumenu + menuitemArrow, Enter, Space, Escape, Type-ahead
Selectcombobox + listboxArrow, Enter, Escape, Space
TextInputtextboxStandard text input
TextAreatextboxStandard text input
Toaststatus / alertEscape
Progressprogressbar
Formform
Togglebutton (aria-pressed)Enter, Space
Separatorseparator / none
CollapsibleregionEnter, Space
Portal
FocusScopeTab trap
VisuallyHidden

Controlled vs Uncontrolled

Every stateful primitive supports both patterns. In uncontrolled mode, the primitive manages its own state. In controlled mode, the consumer provides the state via props and receives change callbacks.

controlled.rs
// Uncontrolled — primitive manages state
<Disclosure default_open=false>...</Disclosure>

// Controlled — consumer manages state
<Disclosure open=is_open on_open_change=set_open>
    ...</Disclosure>
Next: Styled Components