Front end
Accessible UI components
Unstyled component primitives and editors that give correct keyboard and screen reader behaviour, styled to fit the project.
Dropdowns, dialogs, tabs and carousels look simple and are not. A correct dialog traps focus, restores it on close, responds to Escape, marks the rest of the page as inert and announces itself. Almost nobody gets that right by hand, and the ones who do get it right once then reimplement it on the next project. Primitive libraries such as Radix supply the behaviour and the ARIA wiring while shipping no visual design at all, so the appearance stays entirely yours. Alongside them sit purpose-built widgets like the Embla carousel and the Editor.js block editor, which do one thing each rather than trying to be a design system.
How we use it
Taxcare Accountancy is the build on this line. It is a public site for an accountancy practice, so real people arrive on it, including people using a keyboard or a screen reader, and it is heavy on interactive furniture: an FAQ accordion, service navigation, a Making Tax Digital calculator with staged inputs, a hero carousel that mixes an award video with image slides, a partner logo carousel, and a Google reviews carousel loaded through a serverless function. The primitives sit under a component layer of the shadcn kind, styled with Tailwind, so the accordion behaves correctly and looks like the rest of the site. The carousels are Embla, which is small and unopinionated. The bundle also carries Editor.js utilities, which points to a block editor in the admin area where staff write the roughly 85 articles.
We will say the uncomfortable part about carousels. The autoplaying hero is a client requirement, not our recommendation; an autoplaying carousel needs a pause control and a reduced-motion path, it hides content behind a timer, and most visitors never see slide three.
What it costs you
Unstyled means unstyled. You are getting behaviour, not a look, so budget the design time you thought you were saving. A dropdown that works perfectly and has no padding is not a finished component.
The shadcn pattern is a copy, not a dependency: the source is pasted into your repository and becomes yours. That is genuinely good for control and genuinely bad for upgrades, because an upstream accessibility fix reaches you only if somebody notices and re-applies it by hand. A year in, nobody has.
Editor.js stores documents as its own block JSON. That is a lock-in you should choose with your eyes open, since moving to another editor later means writing a converter for every block type you ever used. Portal-based components have their own tax: dialogs and popovers render outside the DOM position you wrote them in, which breaks naive CSS, confuses some test selectors, and takes a moment to get right inside a scroll container.
And the primitives cover keyboard and ARIA only. They do not check your colour contrast, your heading order or your form labelling, and none of those are optional under the accessibility rules a UK business is subject to.
When we would choose something else
For a component that already exists in HTML, we use the HTML. <details> is an accordion, <dialog> has a modal mode with focus handling in every current browser, and a <select> is better on a phone than anything you will build. Popover and anchor positioning have landed too, and each of those is a dependency avoided.
Where the requirement is styling rather than behaviour, that is Tailwind CSS on its own, and where the interactive parts are a small share of a mostly static page we would rather write two components by hand than adopt a kit, which is the plain React route. A full design system with its own visual language, Material or Fluent, is a different purchase: you get more for free and you look like everyone else who bought it.
Where we have used it
Every build below lists this in its stack, so the claim is checkable.