assistant-ui/assistant-ui
Reviewed against Rams quality heuristics: accessibility, color, typography, spacing, components, motion, UX, and craft.
30 files reviewed·July 25, 2026
Low
Design risk in this codebase.
More findings
Verdict
A well-structured status badge system undone by three small finishing details: weak contrast, an untamed ping animation, and a generic alt fallback. The biggest risk is that these all live on the same status-and-artifact surface, the exact place users look first.
Files Rams reviewed
apps/docs/app/(home)/gallery/components/page.tsx
apps/docs/app/(demos)/demos/[slug]/page.tsx
apps/docs/app/(demos)/heat-graph/page.tsx
apps/docs/app/(demos)/react-o11y/page.tsx
apps/docs/app/(demos)/safe-content-frame/page.tsx
apps/docs/app/(demos)/tw-glass/(home)/page.tsx
apps/docs/app/(docs)/docs/[[...slug]]/page.tsx
apps/docs/app/(docs)/examples/[[...slug]]/page.tsx
apps/docs/app/(home)/blog/[slug]/page.tsx
apps/docs/app/(home)/blog/page.tsx
apps/docs/app/(home)/brand/page.tsx
apps/docs/app/(home)/careers/[slug]/page.tsx
apps/docs/app/(home)/careers/page.tsx
apps/docs/app/(home)/changelog/page.tsx
apps/docs/app/(home)/gallery/[slug]/page.tsx
apps/docs/app/(home)/gallery/page.tsx
apps/docs/app/(home)/packages/page.tsx
apps/docs/app/(home)/page.tsx
apps/docs/app/(home)/pricing/page.tsx
apps/docs/app/(home)/privacy-policy/page.tsx
apps/docs/app/(home)/showcase/page.tsx
apps/docs/app/(home)/traction/page.tsx
apps/docs/app/(products)/cloud-ai-sdk/page.tsx
apps/docs/app/(products)/ink/page.tsx
apps/docs/app/(products)/native/page.tsx
apps/docs/app/(products)/tap/docs/[[...slug]]/page.tsx
apps/docs/app/layout.tsx
examples/with-a2a/app/page.tsx
examples/with-ag-ui/app/page.tsx
examples/with-artifacts/app/page.tsx
Accessibility
examples/with-a2a/app/page.tsx:129
Image artifact falls back to generic alt text of "image"
In `ArtifactPartView`, when `part.url` points to an image and `part.filename` is undefined, the `<img>` renders with `alt="image"` instead of any content-derived description.
Why it matters
Screen reader users hear only the word "image" for every unnamed artifact, giving them no way to distinguish a chart from a screenshot from a diagram when a task returns several images.
Fix
Fall back to a description derived from mediaType or task context instead of the generic word "image" when filename is missing.
<img
src={part.url}
alt={part.filename ?? "image"}
className="max-h-64 rounded border"
/><img
src={part.url}
alt={part.filename ?? `Artifact image (${part.mediaType ?? "unknown type"})`}
className="max-h-64 rounded border"
/>Color
examples/with-a2a/app/page.tsx:22
"Working" status badge text fails contrast against its own fill
The `working` entry in `STATE_CONFIG` pairs `text-amber-600` with `bg-amber-100`. Computed against the actual hex values (#d97706 on #fef3c7), this measures ~2.86:1, below the 3:1 floor for large text and well under the 4.5:1 required for the small badge label.
Why it matters
Users with low vision or in bright ambient light cannot reliably read the task's active state, which is the one badge users check most often mid-task.
Fix
Darken the badge text token so amber text on amber-100 clears 4.5:1, e.g. swap to a 700-weight amber or add a solid dark background.
working: {
label: "Working",
color: "text-amber-600",
bg: "bg-amber-100 dark:bg-amber-900/30",
},working: {
label: "Working",
color: "text-amber-800",
bg: "bg-amber-100 dark:bg-amber-900/30",
},Get this score on every PR.
Rams reviews each pull request on your repo and posts inline one-click fixes — about a minute per review.
Install Rams freeMotion
examples/with-a2a/app/page.tsx:73
Ping animation on active status dot never stops or respects reduced motion
In `StateBadge`, when `isActive` is true (state is `working` or `submitted`), a `<span>` with `animate-ping` renders indefinitely with no `prefers-reduced-motion` guard anywhere in the file.
Why it matters
Users with vestibular disorders who have reduced motion enabled at the OS level still get a looping pulse for the entire duration of any working or submitted task, which can run for minutes.
Fix
Gate the animate-ping class behind a `motion-safe:` variant so reduced-motion users get the static dot instead of an infinite pulse.
<span className="relative flex size-1.5">
<span className="absolute inline-flex size-full animate-ping rounded-full bg-current opacity-75" />
<span className="relative inline-flex size-1.5 rounded-full bg-current" />
</span><span className="relative flex size-1.5">
<span className="absolute inline-flex size-full motion-safe:animate-ping rounded-full bg-current opacity-75" />
<span className="relative inline-flex size-1.5 rounded-full bg-current" />
</span>Typography
Spacing
Components
UX
Craft
Working well
- StateBadge pairs a colored dot with the visible text label (e.g. "Completed", "Failed") rather than relying on color alone, so status is legible for colorblind users even before the contrast fix.
- NewThreadButton uses a real <button> with a visible "New Thread" label rather than a clickable div, keeping the control keyboard-operable and screen-reader friendly by default.
- page.tsx keeps composition thin, wiring up providers and A2A hooks while delegating actual message rendering to Thread, which keeps the file readable as the integration grows.
Scored July 25, 2026 with Rams Engine v0.0.3 · Engine changelog
First scored July 9, 2026: 59/100. This rescore on v0.0.3: 94/100.
This page is an automated design review of assistant-ui/assistant-ui’s UI code: 30 files read against 291 versioned rules covering accessibility, color, typography, spacing, components, UX, motion, and craft. The score is out of 100; confirmed criticals cap it — one at 59, two at 49, three or more at 39.
More design scores
Score your own repo.
Free on public repos, no account. The same engine that scored this page reads your UI code and mints a score page like this one.
Public repos only. The full engine reviews the UI code and mints a public score page — we email you the link too. Already-scored repos open instantly.