Unstyled components.
Bring your own design system.
38 accessible, fully-keyboard primitives with zero opinions about how they look — shipped to React, Vue and Solid from a single shared a11y test suite.
$ npm install @borealis/react
also: @borealis/vue · @borealis/solid
Trusted in production at
A Dialog that's all behavior, no chrome.
Borealis ships the focus trap, scroll lock, escape handling and ARIA wiring. The markup and every class name are entirely yours.
1import { Dialog } from '@borealis/react' 2 3export function InvitePanel() { 4 return ( 5 <Dialog.Root> 6 <Dialog.Trigger className="btn-primary">Invite people</Dialog.Trigger> 7 <Dialog.Content className="card w-[440px]"> 8 <Dialog.Title className="text-lg font-semibold"> 9 Invite to workspace 10 </Dialog.Title> 11 <Dialog.Description className="text-muted"> 12 They'll get an email with a join link. 13 </Dialog.Description> 14 <Dialog.Close className="btn-ghost">Done</Dialog.Close> 15 </Dialog.Content> 16 </Dialog.Root> 17 ) 18}
Accessibility is hard.
Shipping it three times is harder.
Things we don't do
-
No bundled CSS, no theme, no
classNamewe picked for you. -
No design tokens, no
<ThemeProvider>, no runtime style engine. - No "kitchen-sink" components — we ship primitives, not page templates.
- No framework favoritism — React, Vue and Solid ship the same release, same day.
- No silent breaking changes — every a11y regression fails CI before it ships.
A button that opens a menu is deceptively expensive. It needs roving tabindex, type-ahead matching, an aria-activedescendant contract, escape-to-close, focus return, and correct behavior under a screen reader's virtual cursor. Styled component kits bundle all of that with a visual design you'll fight forever. Headless libraries unbundle the two: you get the unglamorous, correct behavior — and you write every pixel yourself.
Radix UI proved the headless model is the right one. Composable primitives, real WAI-ARIA semantics, no styling baggage — it reset the industry's expectations. But Radix is React, and only React. Teams on Vue or Solid have been re-implementing the same focus traps and the same listbox keyboard contracts, badly, in isolation, for years — each project paying the accessibility tax from scratch.
Borealis ports those primitives to all three frameworks from one shared specification and one accessibility test suite. The Combobox keyboard contract is defined once; React, Vue and Solid each implement against it; the same 1,900 axe-core and keyboard-interaction assertions run against every framework on every commit. A fix landed for Solid is, by construction, a fix the React and Vue maintainers can't forget to mirror.
38 components. Every one accessible by default.
From Dialog and Combobox to RangeSlider and DatePicker — primitives that handle the hard parts so your design system stays the only thing you maintain.
38 components, 3 frameworks
Dialog, Popover, Combobox, Menu, Tabs, Tooltip, Accordion, Select, Toast and 29 more — identical API surface across React, Vue and Solid.
import {'{'} Combobox {'}'} from '@borealis/vue'
WAI-ARIA compliant
Every primitive implements the matching ARIA Authoring Practices pattern — roles, states and properties wired correctly, verified by axe-core in CI.
role="dialog" · aria-modal="true"
Full keyboard navigation
Roving focus, type-ahead, arrow-key traversal, Home / End, Page Up / Down and screen-reader announcements — the parts everyone forgets, handled.
↑ ↓ ⏎ ⎋ · type-ahead built in
RTL support, built in
Direction-aware focus order and arrow keys. Set dir="rtl" once — Menu, Slider and Tabs flip their interaction model automatically.
<Menu.Root dir="rtl" />
Server-component friendly
Static parts render on the server; only the interactive shell hydrates. No "use client" at the top of your whole tree.
RSC-safe · streams without flicker
Animation primitives
Mount and unmount states stay in the DOM through your exit transition — pair Borealis with CSS, Motion or GSAP without orphaned nodes.
data-state="open" | "closed"
Framework coverage
38 / 38 components per frameworkHeadless is a crowded field. Here's the honest table.
Radix, Ariakit and Headless UI are all excellent. The difference Borealis makes is the same primitives, the same audited contract, across three frameworks.
| Capability |
Borealis
|
Radix UI | Ariakit | Headless UI |
|---|---|---|---|---|
| Cross-framework support | React · Vue · Solid | React only | React only | React · Vue |
| Components shipped | 38 | 28 | 31 | 10 |
| Shared a11y test suite | ||||
| Built-in RTL support | partial | |||
| Server-component safe | partial | partial | ||
| Animation primitives | ||||
| License | MIT | MIT | MIT | MIT |
Component counts as of v2.0 · Mar 2026. Corrections welcome — open a PR against docs/comparison.md.
Hello world in under 60 seconds.
Three steps. No config file, no provider, no build plugin. The example is React — Vue and Solid are line-for-line equivalent.
Install the package
Pull in the build for your framework — they're independent packages, so install only what you use.
$ npm install @borealis/reactCompose a primitive
Import the namespace and assemble its parts. Borealis manages open state — pass your own classes for everything visual.
import { Menu } from '@borealis/react' <Menu.Root> <Menu.Trigger className="btn">Options</Menu.Trigger> <Menu.Items className="menu-panel"> <Menu.Item>Rename</Menu.Item> <Menu.Item>Duplicate</Menu.Item> </Menu.Items> </Menu.Root>
Style it with anything
Tailwind, CSS Modules, vanilla-extract, plain CSS — Borealis exposes data-state and data-highlighted hooks to target.
/* highlighted via keyboard or pointer */ .menu-panel [data-highlighted] { background: #67E8F9; color: #06121A; }
Built in the open, with 240 contributors.
Every primitive's keyboard contract was argued over in a GitHub Discussion before it shipped. Come help shape v2.1.
RFCs, keyboard-contract debates and triage — all public.
Real-time help, a #showcase channel, and weekly office hours.
Core maintainers
+ 240 contributorsBorealis is funded entirely by GitHub Sponsors — no VC, no company behind it.
Become a sponsorFunded by the teams that ship on it.
Every dollar goes to maintainer time and the a11y audit budget. 88 sponsors and counting.
Sponsoring organizations
0 individual sponsors
Questions, answered straight.
Is Borealis production-ready?
How is this different from Radix?
Will React Server Components break it?
"use client" directive, so you mark the component, not your entire page tree. Components stream without hydration flicker and ship correct markup on first paint. The Next.js App Router and Remix are both covered by our example apps and tested in CI.
What about animation?
data-state attribute (open / closed) and keeps unmounting elements in the DOM until their exit transition completes. That means you can animate with plain CSS keyframes, Motion, GSAP or View Transitions — your choice — without the classic problem of a popover vanishing before its fade-out finishes. There is nothing to configure; the mount/unmount lifecycle is handled for you.
Long-term commitment, or corporate sponsor lock-in?
Get started in 60 seconds.
Install one package, compose a primitive, style it your way. No config, no provider, no lock-in.
$ npm install @borealis/react