Accessibility
The target is WCAG 2.2 AA. Every component has an automated accessibility assertion in its test suite and runs under an accessibility checker in the component workshop with findings set to fail rather than warn. Keyboard interaction is tested, not assumed.
None of that is unusual. What follows is the part that is: the places where the accessible choice differs from the common one, and why.
A streaming transcript is not a live region
The obvious implementation marks a chat log aria-live so new content is announced. Under streaming that produces an announcement per token: continuous interruption, and already-read text re-announced as the node mutates. The transcript here is an ordinary list, navigable at the reader's pace, and a separate status region announces state — never content.
See it in contextA loading button keeps its focus
Disabling a button while it works removes it from the focus order and strands the keyboard position of the person who just pressed it. Loading uses aria-disabled and aria-busy with a guarded click handler instead. Genuinely inert controls still use disabled.
See it in contextAlerts are not live regions by default
A live region that already exists when the page paints announces itself for no reason, and trains people to ignore it. Opt in with live="polite" for something that appeared in response to an action, or live="assertive" for an error that must interrupt.
See it in contextThe name goes where the role is
Slider's accessible name is forwarded onto its thumbs, because the thumb is the element with role="slider" — left on the root it names something with no role at all. A range slider names each thumb separately, since two controls called "Price" tell you nothing about which one you are on.
See it in contextScrollable regions are focusable
A table or code block that overflows its container is unreachable by keyboard unless the scroll container can take focus. Both are focusable named regions rather than silent focus stops.
See it in contextStatus is never colour alone
Tool calls and agent states say what they are in words. These states matter most when something has failed, which is exactly when a colour-only signal fails the people who most need to notice it.
See it in contextNothing points at an element that was not rendered
Form fields assemble aria-describedby from the description and error that actually exist. A dangling reference announces nothing and is flagged by automated testing, so presence is tracked rather than assumed.
See it in contextRight to left
Every direction-dependent style is written logically — ps-, me-, text-end — so the layout follows dir on its own. pnpm audit:rtl fails the build on any physical property that has a logical equivalent, and on any icon that points along the reading direction without being mirrored, because logical CSS mirrors the box an icon sits in and not the glyph inside it.
An application in Arabic, Hebrew, Persian or Urdu needs two things:
dir="rtl"on the document, which the styling follows.DirectionProvideraround the tree. The primitives read direction from React context rather than from the document and assume left-to-right without it, which mirrors a page everywhere except its menus, selects and sliders — worse than not mirroring at all, because it looks deliberate.
Two things stay physical on purpose. A Sheet with side="left" and a toast at position="bottom-right" are named after a side, and a control asked for on the left that appears on the right is an API telling a lie. The logical versions of those are start/end props, which would be a rename rather than a restyle.
What this does not claim: the components have not been reviewed by a reader of a right-to-left language. The audit checks that nothing is styled or drawn against the direction, which is necessary and not sufficient.
What automated testing does not cover
Automated checks catch a minority of accessibility problems. They cannot tell whether a label is meaningful, whether a focus order makes sense, or whether an announcement is useful rather than merely present. Where a decision depended on judgement, it is written down on the component’s page so it can be argued with.
Some behaviour also cannot be verified in a test environment without layout — collision -aware positioning, hover grace areas, scroll geometry. Those are exercised in a real browser instead, and the tests say so rather than asserting against a stub.