Headless: one behaviour hook, two completely different looks
Each line shows the focused tab and every tab's aria-selected/tabIndex. Only the selected tab is reachable with Tab (roving tabindex), arrows wrap around, and the visible panel is labelled by its tab. Both skins style the same aria-selected attribute. Click into a tab bar and try the arrow keys, Home and End yourself.
Component libraries give you ready-made, accessible buttons, dialogs, menus and tabs: styled kits like MUI and Chakra come with a look, headless libraries like Radix and React Aria give behaviour and accessibility with no styles, and shadcn/ui copies styled source files into your project so you own them.
Change the code and press Run (Ctrl+Enter). Predict what will happen first, then break it on purpose and read what changes. Your edits are saved and match the lesson page.
Ready for more? This phase has 5 hands-on exercises with automatic checks.
Practice questions
Write the code in the editor, run it, then open the model answer to compare.
Write a headless useToggle hook for a switch that returns getSwitchProps() with role="switch", aria-checked and an onClick, and use it in a Switch component styled through [aria-checked="true"].
Using the mini cva from the second example, create badgeVariants with tone (neutral, success, danger) and size (sm, md), defaulting to neutral and sm. What does badgeVariants({ tone: "success" }) return?
Explain it without notes
Why use a component library for dialogs, menus or comboboxes instead of building them yourself?
What is the difference between a styled library, a headless library and copy-in components? Give an example of each.
Explain the prop-getter pattern of headless hooks.
How would you choose a component library for a new product?
Preview
Console
Nothing logged yet.Expected console output
ArrowRight -> Specs false/-1 true/0 false/-1
ArrowRight -> Reviews false/-1 false/-1 true/0
ArrowRight -> Overview true/0 false/-1 false/-1
visible panel: What the product does. | labelled by: Overview