@nebula-lab/headless
v1.0.0
Published
Behavior-only, ARIA-complete compound components — no styling opinions. Renamed @nebula-lab/headless -> @nebula-lab/styleless -> back to @nebula-lab/headless; the project owner's confirmed 5-tier model treats headless (this package) and styleless (a disti
Readme
@nebula-lab/headless
Behavior-only, ARIA-complete compound components. No styling opinions — that's @nebula-lab/react-ui's job, built on top of this package and @nebula-lab/styleless. (This package was renamed @nebula-lab/headless → @nebula-lab/styleless → back to @nebula-lab/headless: the project owner confirmed a 5-tier model where headless — this package — and styleless — a distinct layer — are two different things, not one renamed package. See AGENTS.md's "Layer placement" section.)
Installation
npm install @nebula-lab/headless
# or
yarn add @nebula-lab/headless
# or
pnpm add @nebula-lab/headless
# or
bun add @nebula-lab/headlessPeer dependencies: react ^19.0.0, react-dom ^19.0.0. Works with any of the above package managers — pick whichever your project already uses.
Module format: ESM only (no CommonJS build). Works out of the box with any bundler (Vite, Next.js, Webpack 5+, esbuild, Parcel) or native Node.js ESM. A plain CommonJS require('@nebula-lab/headless') is not supported and throws ERR_REQUIRE_ESM — use import (or dynamic import() from a CJS file) instead.
TypeScript / JavaScript: Ships hand-written .d.ts types alongside the JS output, but nothing requires TypeScript — plain JavaScript works identically. TypeScript users get full autocomplete/type-checking for free; JavaScript users just don't see the type annotations.
What's here
Every component below follows its WAI-ARIA APG pattern (roving-tabindex, correct roles/states, full keyboard support) and exposes state via data-state/data-orientation/data-disabled attributes rather than requiring you to track it yourself.
Overlays — Dialog, AlertDialog, Drawer, Popover, HoverCard, Tooltip
Menus — Menu, DropdownMenu, ContextMenu, Menubar, NavigationMenu, Command
Disclosure — Accordion, Collapsible
Selection controls — Checkbox, CheckboxGroup, RadioGroup, Switch, Toggle, ToggleGroup, Select, Listbox, Combobox, Autocomplete, ColorPicker, Rating
Text/numeric input — NumberInput, OtpInput, Slider, FileUpload, Field (label/control/description/error wiring)
Navigation — Tabs, Breadcrumb, Pagination, Stepper
Feedback — Toast, Progress, Spinner, Skeleton
Data — Tree, TreeView
See AGENTS.md's status table for the authoritative, currently-maintained inventory as new components land.
Tabs/TabList/Tab/TabPanel— WAI-ARIA Tabs pattern: roving-tabindex arrow-key navigation (Left/Right or Up/Down depending onorientation, plus Home/End),automaticormanualactivation mode, controlled or uncontrolledvalue. NestedTabsinstances don't collide (scoped context via@nebula-lab/primitives).Checkbox— tri-state (checked/unchecked/indeterminate), hidden-native-input technique so it participates in real<form>/FormDatasubmission and fires genuineclick/input/changeevents.Switch— same hidden-native-input pattern asCheckbox, WAI-ARIA Switch role.RadioGroup/RadioGroupItem— WAI-ARIA Radio Group pattern, built on@nebula-lab/primitives'RovingFocusGroup/FocusItemrather than hand-rolling roving-tabindex again.Accordion/AccordionItem/AccordionHeader/AccordionTrigger/AccordionContent— WAI-ARIA Accordion pattern,type="single"(withcollapsible) ortype="multiple", arrow-key navigation between triggers viaRovingFocusGroup/FocusItem, panel wrapped in@nebula-lab/primitives'Presenceso consumers can animate expand/collapse offdata-stateinstead of it unmounting instantly.Dialog/DialogTrigger/DialogPortal/DialogOverlay/DialogContent/DialogTitle/DialogDescription/DialogClose— WAI-ARIA Dialog (Modal) pattern.DialogContentcomposes@nebula-lab/primitives'Presence+DismissibleLayer(Escape/outside-click) +FocusScope(trappedwhenmodal, the root's default) viaasChildchaining, so it's one real DOM node despite the layered behavior.Popover/PopoverTrigger/PopoverPortal/PopoverContent/PopoverClose— the non-modal sibling ofDialog: anchor-positioned (via@nebula-lab/primitives'Popper, composed intoPopover's own scope so a single__scopePopoverthreads through both),role="dialog"withoutaria-modal,FocusScopewithtrapped={false}(focus moves in on open and restores to the trigger on close, but Tab isn't cycled), dismissed viaEscape/outside-click same asDialog.PopoverTriggertoggles open/closed on click, unlikeDialogTriggerwhich only opens.
Import
import { Tabs, TabList, Tab, TabPanel } from '@nebula-lab/headless';
// or per-component subpath
import { Tabs, TabList, Tab, TabPanel } from '@nebula-lab/headless/tabs';
import { Checkbox } from '@nebula-lab/headless/checkbox';
import { Switch } from '@nebula-lab/headless/switch';
import { RadioGroup, RadioGroupItem } from '@nebula-lab/headless/radio-group';
import {
Accordion,
AccordionItem,
AccordionHeader,
AccordionTrigger,
AccordionContent,
} from '@nebula-lab/headless/accordion';
import {
Dialog,
DialogTrigger,
DialogPortal,
DialogOverlay,
DialogContent,
DialogTitle,
DialogDescription,
DialogClose,
} from '@nebula-lab/headless/dialog';
import {
Popover,
PopoverTrigger,
PopoverPortal,
PopoverContent,
PopoverClose,
} from '@nebula-lab/headless/popover';<Tabs defaultValue="account">
<TabList>
<Tab value="account">Account</Tab>
<Tab value="password">Password</Tab>
</TabList>
<TabPanel value="account">...</TabPanel>
<TabPanel value="password">...</TabPanel>
</Tabs>
<Accordion type="single" collapsible defaultValue="item-1">
<AccordionItem value="item-1">
<AccordionHeader>
<AccordionTrigger>What is nebula?</AccordionTrigger>
</AccordionHeader>
<AccordionContent>A composable React UI platform.</AccordionContent>
</AccordionItem>
</Accordion>
<Dialog>
<DialogTrigger>Delete item</DialogTrigger>
<DialogPortal>
<DialogOverlay />
<DialogContent>
<DialogTitle>Delete item?</DialogTitle>
<DialogDescription>This can't be undone.</DialogDescription>
<DialogClose>Cancel</DialogClose>
</DialogContent>
</DialogPortal>
</Dialog>
<Popover>
<PopoverTrigger>Filters</PopoverTrigger>
<PopoverPortal>
<PopoverContent side="bottom" align="start" sideOffset={4}>
<p>Filter options.</p>
<PopoverClose>Done</PopoverClose>
</PopoverContent>
</PopoverPortal>
</Popover>API reference
Every component here ships with a live Storybook entry (controls, source, interaction tests) — that's the authoritative API reference, not this README: https://tripathirajan.github.io/nebula/
Contributing
See the monorepo's CONTRIBUTING.md.
License
MIT
