block/buzz
Reviewed against Rams quality heuristics: accessibility, color, typography, spacing, components, motion, UX, and craft.
30 files reviewed·July 22, 2026
Elevated
Design risk in this codebase.
More findings
Verdict
Craft shows in the pixel-level chrome details, but motion and state feedback were left unfinished. A reduced-motion-blind slide animation is the single biggest risk to ship as-is.
Files Rams reviewed
desktop/src/app/routes/index.tsx
desktop/src/app/AppShellContext.tsx
desktop/src/app/AppShellOverlays.tsx
desktop/src/app/AppTopChrome.tsx
desktop/src/app/RelayConnectionOverlay.tsx
desktop/src/app/routes/ChannelRouteScreen.tsx
desktop/src/app/routes/agents.tsx
desktop/src/app/routes/channels.$channelId.posts.$postId.tsx
desktop/src/app/routes/channels.$channelId.tsx
desktop/src/app/routes/projects.$projectId.tsx
desktop/src/app/routes/pulse.tsx
desktop/src/features/huddle/components/AddAgentDialog.tsx
desktop/src/features/huddle/components/HuddleAttachment.tsx
desktop/src/features/huddle/components/HuddleIndicator.tsx
desktop/src/features/huddle/components/ParticipantList.tsx
web/src/app/routes/index.tsx
desktop/src/app/BuzzThemeSurfaces.tsx
desktop/src/app/ThemeGrainientBackground.tsx
desktop/src/app/router.tsx
desktop/src/app/routes/WorkflowsRouteScreen.tsx
desktop/src/app/routes/messages.new.tsx
desktop/src/app/routes/projects.tsx
desktop/src/app/routes/reminders.tsx
desktop/src/app/routes/root.tsx
desktop/src/app/routes/settings.tsx
desktop/src/app/routes/workflows.$workflowId.tsx
desktop/src/app/routes/workflows.tsx
desktop/src/features/forum/ui/ForumComposerCompactLayout.tsx
web/src/app/App.tsx
web/src/app/router.tsx
Motion
desktop/src/app/RelayConnectionOverlay.tsx:59
Slide-in overlay animates 20px regardless of reduced-motion setting
Both motion.div overlays ("relay-connection-overlay" and "relay-error-overlay") animate `y: -20 → 0` on enter and `y: 0 → 20` on exit with no `useReducedMotion` check or `MotionConfig reducedMotion="user"` anywhere in the file.
Why it matters
Vestibular-sensitive users get the full slide motion every time the relay disconnects or reconnects, regardless of their OS setting: this is exactly the kind of persistent, state-driven motion prefers-reduced-motion exists to suppress.
Fix
Read useReducedMotion() from the motion library and collapse the y-translate to 0 when it is true, keeping only the opacity fade.
<motion.div
animate={{ opacity: 1, y: 0 }}
className={cn(
"pointer-events-none fixed z-50 w-[284px]",
hasCommunityRail ? "left-[60px]" : "left-3",
isHuddleDrawerOpen
? "bottom-[calc(var(--buzz-huddle-drawer-height,0px)+12px)]"
: "bottom-3",
)}
exit={{ opacity: 0, y: 20 }}
initial={{ opacity: 0, y: -20 }}
key="relay-connection-overlay"
transition={{ duration: 0.25, ease: [0.22, 1, 0.36, 1] }}
><motion.div
animate={{ opacity: 1, y: 0 }}
className={cn(
"pointer-events-none fixed z-50 w-[284px]",
hasCommunityRail ? "left-[60px]" : "left-3",
isHuddleDrawerOpen
? "bottom-[calc(var(--buzz-huddle-drawer-height,0px)+12px)]"
: "bottom-3",
)}
exit={{ opacity: 0, y: shouldReduceMotion ? 0 : 20 }}
initial={{ opacity: 0, y: shouldReduceMotion ? 0 : -20 }}
key="relay-connection-overlay"
transition={{ duration: 0.25, ease: [0.22, 1, 0.36, 1] }}
>desktop/src/app/AppShellOverlays.tsx:53
Deferred dialog mount forces a null render with no reduced-motion consideration
The effect always sets `visibleBrowseDialogType` to `null` on every `browseDialogType` change, then reopens on the next frame via `openModalNextFrame`. This unmounts and remounts the dialog's open state on every browse-type switch (e.g. stream to forum), replaying its entrance transition each time.
Why it matters
Users who switch between browse types back-to-back see the dialog flash closed and re-animate open every time, and there's no `prefers-reduced-motion` branch anywhere in this file's control flow to suppress that replay for motion-sensitive users.
Fix
Only defer-remount when transitioning from closed to open, not when switching between two already-open types, and respect prefers-reduced-motion in the underlying dialog's transition.
setVisibleBrowseDialogType(null);
openModalNextFrame(() => {
setVisibleBrowseDialogType(browseDialogType);
});if (visibleBrowseDialogType !== null) {
// already open, just swap the filter without a remount
setVisibleBrowseDialogType(browseDialogType);
return;
}
setVisibleBrowseDialogType(null);
openModalNextFrame(() => {
setVisibleBrowseDialogType(browseDialogType);
});UX
desktop/src/app/RelayConnectionOverlay.tsx:99
Error fallback shows the failure with no way to act on it
The `relay-error-overlay` div renders `role="alert"` with an `AlertCircle` icon and `{errorMessage}` text only: no retry, dismiss, or reopen-sidebar affordance in the markup.
Why it matters
A user who sees an auth-rejection error while the sidebar is collapsed has no next step from this overlay; it sits there indefinitely with nothing to click, which is worse than an error that at least tells you what to do.
Fix
Give the error fallback a visible action (dismiss or reopen sidebar) instead of stopping at the message.
<div
className="pointer-events-auto flex items-center gap-2 rounded-xl bg-background px-3 py-2.5 text-sm text-destructive shadow-md"
data-testid="relay-error-overlay"
role="alert"
>
<AlertCircle aria-hidden="true" className="h-4 w-4 shrink-0" />
<span className="flex-1">{errorMessage}</span>
</div><div
className="pointer-events-auto flex items-center gap-2 rounded-xl bg-background px-3 py-2.5 text-sm text-destructive shadow-md"
data-testid="relay-error-overlay"
role="alert"
>
<AlertCircle aria-hidden="true" className="h-4 w-4 shrink-0" />
<span className="flex-1">{errorMessage}</span>
<button
aria-label="Dismiss error"
className="pointer-events-auto shrink-0 text-destructive/70 hover:text-destructive"
onClick={card.onDismissRelayConnectionCard}
type="button"
>
×
</button>
</div>desktop/src/app/AppTopChrome.tsx:27
Global wheel-blocking on the top chrome swallows scroll over the nav buttons themselves
`preventTopChromeWheel` calls `event.preventDefault()` unconditionally and is attached to the whole `topChromeRef` div with `capture: true`, which contains the back/forward/sidebar buttons.
Why it matters
Any wheel or trackpad gesture that crosses the back/forward buttons is discarded outright, including two-finger swipe-back gestures many users expect from a bar whose entire job is back/forward navigation.
Fix
Skip preventDefault when the wheel event originates over an interactive control so navigation gestures near the buttons keep working.
function preventTopChromeWheel(event: WheelEvent) {
event.preventDefault();
}function preventTopChromeWheel(event: WheelEvent) {
if (event.target instanceof Element && event.target.closest("button")) {
return;
}
event.preventDefault();
}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 freeAccessibility
desktop/src/app/AppTopChrome.tsx:38
Sidebar toggle swaps icons but never announces the resulting state
`TopChromeSidebarTrigger` renders `PanelLeftClose` or `PanelLeftOpen` depending on `sidebar?.open`, but the button carries a static `aria-label="Toggle Sidebar"` with no `aria-expanded`. The visual icon change is the only signal of open/closed state.
Why it matters
Screen reader users hear the same 'Toggle Sidebar, button' before and after activating it, with no way to confirm whether the sidebar is now open or closed without navigating elsewhere to check. That's a real state-value gap under WCAG 4.1.2 (name, role, value).
Fix
Expose the current state on the control itself with aria-expanded tied to the same boolean driving the icon swap.
<Button
aria-label="Toggle Sidebar"
className={TOP_CHROME_ICON_BUTTON_CLASS}
data-sidebar="trigger"
disabled={!sidebar}
onClick={() => {
sidebar?.toggleSidebar();
}}
size="icon"
type="button"
variant="ghost"
><Button
aria-label="Toggle Sidebar"
aria-expanded={sidebar?.open ?? false}
className={TOP_CHROME_ICON_BUTTON_CLASS}
data-sidebar="trigger"
disabled={!sidebar}
onClick={() => {
sidebar?.toggleSidebar();
}}
size="icon"
type="button"
variant="ghost"
>Spacing
desktop/src/app/AppTopChrome.tsx:116
Three nav buttons sit 2px apart, compounding their already-small hit areas
The sidebar trigger (28x28px), "Go back" (24x28px), and "Go forward" (24x28px) buttons sit in a row with `gap-0.5` (2px). All three are already below the 44px comfortable-target size, and they're placed close enough that a slightly off click lands on the wrong control.
Why it matters
Tight spacing between small interactive targets increases mis-click risk for anyone with reduced motor precision, and it compounds rather than offsets the small button size chosen for traffic-light clearance.
Fix
Widen the gap between adjacent small icon buttons so a miss doesn't land on the neighboring control.
<div className={cn("flex items-center gap-0.5", navRowAlignmentClass)}><div className={cn("flex items-center gap-1.5", navRowAlignmentClass)}>Typography
Color
Components
Craft
Working well
- Splitting `pointer-events-none` on the fixed outer wrapper from `pointer-events-auto` on the inner card is the right pattern for a floating overlay: it keeps the dead space around the card from swallowing clicks meant for content underneath, while the card itself stays interactive.
- The error fallback pairs `role="alert"` with `aria-hidden="true"` on the `AlertCircle` icon: the icon is announced as decoration while the alert role itself handles the screen-reader interruption, which is exactly how icon-plus-message error states should be built.
- The comments above TOP_CHROME_ICON_BUTTON_CLASS and macChrome explain exactly why fixed px is used instead of rem (traffic lights don't scale with Cmd +/- zoom). That's the right way to break a system rule: name the constraint, not just override it silently.
- Gating `ChannelManagementSheet` on both `isChannelManagementOpen && activeChannel !== null` avoids a real crash class: rendering a management sheet for a null channel. Checking the data dependency, not just the open flag, is the correct guard here.
Scored July 22, 2026 with Rams Engine v0.0.2 · Engine changelog
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.
Or get this review on every PR.