Which UI component library would you choose if its polished button still left your finished service inaccessible, or if an AI assistant could generate its markup but couldn’t explain the keyboard behaviour behind it?
That question exposes the gap in most library comparisons. Popularity, component counts, and visual polish tell you what a project offers. They don’t tell you whether your team can integrate it safely, maintain its behaviour, or let humans and AI tools edit it without guesswork. A useful choice starts with the finished interface, not the package page.
The UK public sector provides a strong historical example. The GOV.UK Design System launched in 2018, and by 31 March 2022 its components and patterns had been reused more than 3,300 times. The GOV.UK Frontend package had also been downloaded over 2,700,000 times, while pages built with the system downloaded about twice as fast because they used about half as much code, according to the Government Digital Service report. Reuse can improve consistency and performance, but only when teams validate the complete service.
Table of Contents
- What UI Component Libraries Solve
- The Three Library Models and How They Differ
- Evaluation Criteria That Separate Marketing From Reality
- Headless Web Components With a Vue Wrapper Layer
- Theming Strategies for Design System Consistency
- AI-Readiness as a First-Class Library Requirement
- Adoption and Migration Checklist for Production Rollouts
What UI Component Libraries Solve
Which constraints should a UI component library absorb for your team: delivery speed, design control, cross-framework portability, or the accessibility and editability of the finished product?
A library usually combines three layers:
- Visual markup, including buttons, inputs, menus, tabs, and dialogs.
- Behavioural logic, covering focus management, keyboard handling, state changes, and ARIA wiring.
- A theming contract, defining how teams change colours, spacing, typography, borders, and component states.
Together, these layers reduce the places where an inconsistent or inaccessible control can be introduced. They do not remove the need for review, testing, or context-specific decisions.
Take a combobox. Building one from scratch requires decisions about focus movement, opening and closing, Escape and Arrow key behaviour, option selection, input state announcements, and the relationship between the input and listbox for screen readers. Reusing an old implementation may appear faster, yet it can preserve obsolete assumptions, hidden styling dependencies, or defects that nobody knows how to repair. A library supplies a shared baseline, while also imposing its API, release process, and architectural choices.
Practical rule: Choose the library that makes the safe path the easiest path for your team.
Three broad models appear repeatedly:
- Opinionated frameworks, such as Vuetify, PrimeVue, and Element Plus. They provide styled components and a coherent visual language.
- Headless primitives, such as Reka UI, Radix-style Vue ports, and Headless UI. They provide behaviour and structure while leaving styling to you.
- Web components, such as Shoelace and Spectrum Web Components. They expose framework-agnostic custom elements that can work across application stacks.
Evaluate more than the demo. Check accessibility, bundle cost, theming friction, portability, and AI-editability. A polished component can still create maintenance work if its integration depends on deep selectors, undocumented event wiring, or manual keyboard repairs. AI tools face the same problem: they can edit a visible button easily, but need clear contracts to preserve less visible behaviour.
The GOV.UK Design System shows how reusable architecture can support public services. Its components and patterns cover forms, navigation, panels, tables, and other common needs, and are tested against WCAG 2.0 level AA with commonly used assistive technologies, as described in the GDS introduction to the Design System. Reuse makes good defaults repeatable. It does not guarantee compliance, so teams must still test the complete service in context.
The Three Library Models and How They Differ
A styled framework decides how your button looks. A headless primitive decides how it behaves. A web component decides neither, it draws a boundary that can sit beneath several frameworks. That difference determines where your team spends its time, and whether the finished interface remains accessible and easy for people or AI tools to edit.
| Dimension | Opinionated Frameworks | Headless Primitives | Web Components |
|---|---|---|---|
| Default appearance | Ready-styled and cohesive | None or minimal styling | Varies, often themeable |
| Consumer CSS work | Low at first, higher for deep changes | High, because you own presentation | Moderate, depending on shadow DOM and parts |
| Behaviour ownership | Library | Library | Custom element |
| Main escape hatch | Theme APIs, slots, overrides | Composition and class names | Slots, attributes, properties, and parts |
| Vue friction | Deep selectors, wrapper APIs, version coupling | More markup and styling decisions | Event, slot, and v-model adapters |
Opinionated frameworks
Vuetify, PrimeVue, and Element Plus give Vue teams styled buttons, date pickers, data tables, and dialogs through documented APIs. A coherent visual baseline can shorten delivery for admin tools and internal products. It also gives an AI coding tool a recognisable component vocabulary, provided the API and examples are clear.
The constraint appears when the default visual language no longer fits. Theme configuration, component props, global overrides, and deep selectors may each solve one change, but together they can form a second styling system beside the library. Later upgrades then require tracing specificity rules, generated class names, and wrapper behaviour. Accessibility still belongs to the completed screen, not to the presence of a component package.
Headless primitives
Headless libraries return presentation decisions to the consuming team. They usually provide state, composable behaviour, render structure, and class hooks, while your design system supplies tokens and visual states. This separation works well when the team already owns a design language and wants component markup to reflect it.
The work shifts rather than disappears. Every hover, disabled, loading, error, and focus state needs styling and review. Composition rules must be documented so a teammate, or an AI assistant, can change a class without breaking keyboard behaviour or relationships between elements. Headless means flexible presentation, not automatically complete behaviour.
Web components
Shoelace, Spectrum Web Components, and similar custom-element kits can serve Vue, React, or vanilla HTML applications. They suit organisations with several framework stacks, because the custom element becomes a shared contract. Shadow DOM, slots, attributes, properties, and parts can also protect component internals, though they add rules that consumers must understand.
Vue teams should budget for adapters around reactive properties, custom events, slots, and v-model. If a broader React estate is involved, a specialist React CMS migration partner can help map component ownership and integration constraints during a platform transition. For package and pattern comparisons focused on Vue, the Vue component libraries guide offers useful context.
Choose an opinionated framework when delivery speed and default styling carry the most weight. Choose headless primitives when design control and interaction ownership justify the extra presentation work. Choose web components when cross-framework reuse and standards-based integration make an adapter layer worthwhile. In each case, judge the rendered product, not just the primitive: reusable architecture can support accessible services, but it does not finish the accessibility work for you.
Evaluation Criteria That Separate Marketing From Reality
Shortlist two or three candidates and score them against the same test screen, preferably one containing a dialog, a combobox, and a validation error. The screen acts like a controlled experiment: each library faces the same interaction, styling, and build requirements.

Accessibility is an integration test
Run each component with a keyboard before reading its accessibility claims. Can a user reach it with Tab? Does Enter or Space activate it? Does Escape close the correct layer? Do Arrow keys move through options predictably? After a dialog or menu closes, does focus return to its trigger?
Inspect the rendered semantics next. Check roles, accessible names, relationships, aria-expanded, aria-controls, error messaging, and focus-visible styling. A library that advertises WCAG support should also provide test pages, assistive-technology notes, and a keyboard map. Without those materials, the team must discover the interaction contract itself.
UK public-sector guidance recommends accessible styles, components, and patterns for services on a .service.gov.uk domain, while warning that reuse alone does not make a complete service accessible. Teams still need service-specific research, design, development, and testing, as described in the UK digital accessibility guidance. This distinction matters for UI component libraries: a sound primitive can support the work, but the finished screen remains the team’s responsibility.
Bundle cost needs a real import test
Do not judge a library from the word “lightweight” alone. Import one button, then a menu and combobox, and inspect the built output. Compare JavaScript, CSS, peer dependencies, and any global runtime included with the components.
Tree-shaking needs the same practical check. A package may expose individual exports while still including shared styles or registration code on every route. Verify whether unused components disappear from the production build, whether CSS is split, and whether custom elements register globally or only when needed.
Theming reveals the architecture
Change a brand colour, focus ring, border radius, disabled state, and dark-mode surface. If each adjustment demands increasingly specific selectors, the theming contract will be difficult to maintain. CSS custom properties, documented tokens, data-* state hooks, and stable parts provide clearer extension points.
Portability has a maintenance cost
A Vue-only component may offer the smoothest local API. A web component can serve more than one framework, but Vue may need adapters for events and property binding. Score the migration effort, adapter ownership, and debugging cost rather than treating framework independence as an automatic benefit.
Use this scoring prompt:
- Accessibility: Can the team demonstrate keyboard, focus, ARIA, and screen-reader behaviour?
- Bundle size: Can the build show the cost of the actual imports?
- Theming: Can designers change states without specificity battles?
- Portability: Can another framework consume the component without rewriting behaviour?
- Tree-shaking: Does an unused component stay out of the route bundle?
A library should answer these questions with runnable examples and source code. Phrases such as “accessible by default” or “optimised for performance” become meaningful only after the rendered output, focused states, and production build confirm them.
Headless Web Components With a Vue Wrapper Layer
<menu-button :open="open" :items="items" @update:open="open = $event">
<button type="button">Account</button>
</menu-button>
The wrapper handles reactive props. The primitive handles keyboard rules. That separation is the whole point. The headless web component owns behaviour, including ARIA attributes, focus movement, and open and close logic. The Vue wrapper owns ergonomics, such as v-model, event translation, and slot composition.
A complete wrapper can stay small:
<script setup>
import { ref } from 'vue'
const open = ref(false)
const items = ['Profile', 'Settings', 'Sign out']
</script>
<template>
<menu-button :open="open" :items="items" @update:open="open = $event">
<button type="button">Account</button>
</menu-button>
</template>
Here, menu-button is assumed to be a registered custom element with its interaction contract already implemented. Vue should translate that public interface into local conventions, not reproduce focus management or ARIA relationships. Primitives provide a foundation, but the finished product still needs accessible markup, predictable states, and an adapter that developers can edit without tracing behaviour across several layers.

A combobox follows the same boundary:
<script setup>
import { ref } from 'vue'
import { defineCustomElement } from 'vue'
const value = ref('')
const ComboboxElement = defineCustomElement({
props: { value: String },
emits: ['input'],
template: '<slot />'
})
</script>
<template>
<x-combobox
:value="value"
@input="value = $event.target.value"
>
<input :value="value" aria-label="Search services">
</x-combobox>
</template>
The registration method depends on the custom-element implementation, while the integration issues remain familiar. Two-way binding commonly uses an input or change event rather than a Vue-native model update. Kebab-case attributes may arrive as strings, so boolean and object properties need explicit coercion. Form-associated elements may also need an upgrade hook before Vue accesses their internals.
The headless web components guide explains how to assign responsibilities between a primitive and its framework adapter.
Wrapper rule: Wrap for ergonomics, not behaviour. Keep the adapter short enough for an auditor to understand its entire contract.
This model works when properties, events, slots, and state are documented and testable. If the wrapper starts rebuilding focus logic, the boundary has failed.
Theming Strategies for Design System Consistency
A dependable theme system exposes --button-background, --button-focus-ring, and --menu-surface at the component boundary, so changing a few values updates every control state consistently. The contract must also cover hover, active, disabled, focus-visible, error, selected, dark-mode, and high-contrast states. Otherwise, each product team ends up patching or forking the component.
CSS custom properties
For one application, scoped custom properties are usually the simplest choice. A component can define defaults at its boundary or :host, while the consuming application supplies values for its own brand and context.
This approach is easy to inspect and suits design tokens that stay within one ownership boundary. Shadow DOM can restrict how far external styles reach, so expose the variables deliberately. Global selectors should not be expected to penetrate every component.
Tailwind layers
Tailwind teams can apply utility-driven styles through component parts, adopted stylesheets, or a controlled layer strategy. Familiar tooling remains available, but the component becomes tied to Tailwind’s generated class contract. Framework-neutral reuse can then require extra translation or conventions.
Build-time design tokens
Several products benefit from tokens exported as JSON and transformed during the build with tools such as Style Dictionary or Token Studio. Design and engineering can share names for colour, typography, spacing, elevation, and motion, rather than maintaining separate values in each codebase.
Governance carries the cost. Someone must version the token schema, review changes, and decide how a product records a local exception. That work supports consistent semantic states across applications without copying CSS between repositories.
Slot-based extension points
Visual Blocks-style extension points let consumers change internal structure through slots instead of forks. They suit recurring needs such as inserting an icon, helper content, or a custom action area. Exposing every internal element creates a different problem: the component loses a stable composition model and becomes difficult to evolve.
| Strategy | Setup Effort | Cross-Boundary | Best For |
|---|---|---|---|
| CSS custom properties | Low | Moderate | One application |
| Tailwind layers | Moderate | Variable | Utility-first teams |
| Design tokens | High | Strong | Multi-product systems |
| Slot overrides | Moderate to high | Strong | Structural extensions |
Use CSS variables for one app, tokens for multiple products, and slot overrides only when structural changes are a recurring requirement. Test the final rendered control, including colour contrast, focus visibility, error feedback, and dark mode. The theme customisation guide provides a practical reference for making those decisions. A library can offer excellent primitives and still produce an inconsistent product if its theme contract stops before the states users encounter.
AI-Readiness as a First-Class Library Requirement
AI-readiness isn’t a claim that a tool can generate attractive markup. It means the library is inspectable enough for an AI assistant to generate, explain, and repair code without hidden contracts.
A machine-readable component specification should describe props, events, slots, state, ARIA roles, keyboard behaviour, and constraints. A human can use that information directly, while an AI system can use it as structured input instead of inferring everything from a screenshot or a loosely written example.

Inspectability beats autocomplete
A useful component library should expose signals such as:
- An open manifest: Each component lists its properties, events, slots, states, and dependencies in a predictable format.
- Versioned schemas: Changes to a prop or event contract are visible and reviewable rather than hidden in implementation details.
- A structured catalogue: Storybook or Histoire stories should expose examples that tools can index, not just prose pages.
- Stable hooks: Class names, parts, data attributes, or semantic selectors should survive internal refactors where possible.
- Behavioural contracts: The documentation should state what Tab, Enter, Escape, and Arrow keys do, including disabled and invalid states.
- Public test surfaces: An agent should be able to observe a real component, inspect its DOM, and compare states rather than copy an incomplete snippet.
The UK experience gives this requirement practical weight. The GOV.UK Design System team validates new components and patterns through accessibility audits and usability testing with users, including disabled users. GOV.UK also documented a conditional-reveal issue that failed WCAG 2.1 success criterion 4.1.2 until research identified and corrected it, as recorded in the GOV.UK Design System live assessment.
That example matters because a primitive can carry good intent while an integration still fails. If a generated application copies only the visible markup and misses the interaction contract, AI accelerates the wrong implementation.
AI-ready doesn’t mean AI-autonomous. It means a human can inspect the generated result quickly because the library makes its rules explicit.
Demand documentation that describes behaviour, not only appearance. A library that supplies metadata, stable contracts, and observable test pages gives both developers and AI tools a much safer starting point.
Adoption and Migration Checklist for Production Rollouts
Start with a low-risk component, such as a Button or Combobox on a real route. Measure accessibility, bundle cost, theming, and integration friction there before expanding the rollout. A production pilot reveals assumptions that an isolated component showcase can hide.

Phase one establishes the baseline
Record the route’s current bundle output, design-token coverage, keyboard flows, screen-reader announcements, and known WCAG issues. Define what the replacement must preserve. This baseline turns “the new component feels faster” into evidence the team can review.
Phase two proves the integration
Connect the chosen component to the product’s token layer and test it in real screens. Use keyboard-only navigation, a screen reader, browser zoom, dark mode, and high-contrast settings. Check validation messages and focus restoration alongside the default state.
UK monitoring provides a useful warning. The 2024 accessibility monitoring report found a 70% compliance rate for public-sector websites and mobile apps, up from 59% in the previous monitoring period, according to the UK government accessibility monitoring report. Progress does not make adoption and compliance interchangeable. A library must still produce an accessible finished service.
Phase three compares production output
Render the old and new versions on the same route. Review visual regressions, interaction differences, CSS leakage, hydration behaviour, and actual build output. Set an explicit bundle gate, such as 15KB after tree-shaking, only when that threshold matches your product’s performance budget. Treat the number as a team decision, not a universal promise.
Phase four protects the rollback
Use a feature flag, isolate the new CSS where possible, and document a kill switch for the wrapper layer. Keep the old implementation available until the pilot passes accessibility and regression review. Assign one owner to the migration contract, including dependency updates and audit follow-up.
Before launch, require sign-off on:
- Accessibility: Keyboard, screen-reader, focus, contrast, and error-state checks completed.
- Performance: Route-level JavaScript and CSS measured against the agreed budget.
- Design parity: Tokens, responsive states, dark mode, and interaction states reviewed.
- AI-readiness: Manifest, examples, stable hooks, and behavioural documentation available.
- Operations: Rollback flag, owner, issue tracker, and upgrade process documented.
A library earns its place by improving the finished service. Pilot one real component, test it with the people and tools the product must support, and expand when the evidence is clear.
DOM Studio offers headless web component primitives with a thin Vue integration layer, including reactive props, v-model support, slots, and Tailwind CSS 4 styling. Its component catalogue, embedded documentation, and Visual Blocks theming approach support an accessible, AI-editable component workflow. Review them at DOM Studio.
