Every Pro component in Base UI has a test — and CI won't let one ship without it
Most block libraries ship static markup and call it a component. Here's the quality pipeline behind Base UI: 193 test specs, a CI gate that blocks untested Pro components, generated Storybook stories, and axe-core accessibility checks.
There's a category of product that sells "300+ premium components" and delivers a zip of HTML with Tailwind classes on it. It looks incredible in the marketing screenshots. Then you paste a block into your app and discover it's a <div> soup with no state, no keyboard handling, no form integration, and a dropdown that's actually just a hover CSS trick.
You're not buying components. You're buying screenshots.
Base UI is built the other way around, and the difference is enforced mechanically rather than promised in a README. Here's the actual pipeline.
Every Pro component has a colocated test
The library has 193 test specs. More importantly, the distribution isn't backwards.
It's common — and I'd argue backwards — for a library's free primitives to be well tested while the expensive, complicated, paid components have nothing. The paid components are the ones with real state machines: a data table with sorting and pagination, a date range picker with two linked calendars, a command palette with fuzzy search and keyboard navigation, a kanban board with drag and drop.
So Base UI has a CI gate that makes the inverse impossible:
/**
* CI gate: every Pro registry component directory must have a colocated *.spec.ts.
* Tier rules must stay in sync with scripts/build-registry.js (PRO_PREFIXES + PRO_COMPONENTS).
*/
scripts/assert-pro-specs.js walks the tier configuration, checks every Pro component directory for a spec file, and fails the build if any are missing. It runs on every pull request and every push to main:
- name: 🧪 Assert every Pro component has a colocated spec
run: node scripts/assert-pro-specs.js
Currently missing: zero. Not as a point-in-time achievement — as an invariant. A new Pro block without a test cannot reach main.
The full CI pipeline
Every change runs through:
- Library build (
ng build Base) — the published package compiles - Registry build and validation — every component's metadata, dependencies, and tier are checked
- Pro leak assertion — a script confirms no Pro source reached the public registry output. If a paid component ever leaked into the free tier by misconfiguration, the build breaks before it deploys
- Pro spec assertion — the gate above
- Accessibility checks — axe-core, covered below
- Library unit tests and application unit tests in headless Chrome
- CLI type-check, tests, and build — the installer that writes files into your repo is itself tested, including its path-traversal guards
That last one matters more than it sounds. A CLI that writes files into a user's project is a tool with real blast radius. Its path-safety logic has dedicated tests, and those tests gate every merge.
Storybook, generated from the registry
The catalog has Storybook stories for 188 components, and essentially none of them were written by hand.
Hand-written stories for a 187-item catalog are a maintenance fantasy. They're accurate for about a month, then a component gains an input, the story doesn't, and now the story is documentation that lies. Most libraries quietly abandon Storybook at around 10% coverage and leave the link up.
Base UI generates stories from the same registry that drives the CLI:
npm run build:registry # builds the registry, then generates stories
Component metadata is the single source of truth for the CLI, the docs site, the AI catalog, and Storybook simultaneously. Add a component, and its story exists. Change an input, and the story reflects it. There's no drift to manage because there's no second copy to drift from.
Accessibility, checked by a machine
Base UI runs axe-core against a mounted set of components in CI:
npm run test:a11y
The suite mounts real components in a host, runs the WCAG rule set against the rendered output, and fails on violations. Not a linting pass over templates — actual rendered-DOM auditing.
Let me be precise about scope, because this is exactly the area where libraries overclaim: the automated suite currently covers the core free primitives — buttons, cards, alerts, badges, checkboxes, toggles, progress, quotes, icons. It is a foundation and a regression net, not a certificate of full-catalog WCAG conformance, and I'd rather say that plainly than let you find out during an accessibility audit.
What is true across the catalog beyond the automated suite:
- Dialogs use the Angular CDK's
FocusTrapFactoryto trap keyboard focus while open - Drawer panels use
cdkTrapFocuswith auto-capture - Buttons, inputs, and textareas are attribute directives on real native elements, so native semantics, native focus behavior, and native form participation are inherited rather than reimplemented
- Menus, listboxes, comboboxes, trees, and dialogs carry explicit ARIA roles and relationships
Attribute directives over wrapper components is the single highest-leverage accessibility decision in the library, and it's structural — it can't regress.
Documentation that can't go stale
Two more things are generated rather than maintained:
The API reference. Every public input, output, and class carries JSDoc with an example. The reference tables on the docs site are generated from that source, so they can't drift from the actual signatures the way hand-written tables always eventually do.
The changelog. Generated from commit history, so "lifetime updates" comes with an actual record of what shipped — not a version number that increments in silence.
Why this should affect your decision
When you evaluate a component library, the screenshots tell you almost nothing. Every product in this category has good screenshots.
The questions worth asking are: what happens when this component meets a keyboard? What happens when it meets a screen reader, a reactive form, a 40,000-row dataset, a right-to-left locale, an accessibility audit? And when the answer today is "it works" — what stops that from silently becoming false in six months?
For Base UI the answer to the last question is: a CI pipeline that fails the build. Which is the only answer that keeps meaning something after everyone's attention has moved on.
Browse the catalog at base-ui.net →
Base UI is an Angular 21 + Tailwind CSS 4 component library. 187 components and blocks, 102 free forever. Pro is a one-time $99 license with lifetime updates.
Start with the free tier
All primitives, all 19 form blocks, and 390 icons — no account, no licence key.
Get started