mirajchokshi/agentacta
Reviewed against Rams quality heuristics: accessibility, color, typography, spacing, components, motion, UX, and craft.
2 files reviewed·July 22, 2026
Elevated
Design risk in this codebase.
More findings
Verdict
Polished chrome sits on top of a sidebar nav that keyboard and screen-reader users literally cannot use. The token system is sound in theory but leaks undefined variables and near-invisible text, so the biggest risk is that the most-used surface is also the most broken.
Files Rams reviewed
public/index.html
public/style.css
Color
public/style.css:26
Tertiary text color reads at ~2.4:1 on the surface background across a dozen labels
`--text-tertiary` (`#4a5568` in dark theme) is used at 10-11px on `.section-label`, `.config-label`, `.session-id-label`, `.confusion-label`, and `.signal-bar-count`, all sitting on `--bg-surface` (`#0f1420`). Computed contrast is ~2.4:1.
Why it matters
That's below the WCAG AA 4.5:1 floor for small text and even below the 3:1 large-text floor, so these labels are effectively unreadable for a large share of users in the default dark theme, and this token is reused all over the app.
Fix
Raise --text-tertiary's lightness until it clears 4.5:1 against --bg-surface, keeping its hue.
--text-tertiary: #4a5568;--text-tertiary: #7b8798;public/style.css:407
Session project badge uses a one-off blue instead of the existing accent token
`.session-project` hardcodes `color: #7fb4ff` and `background: rgba(39, 94, 182, 0.18)`, while its siblings `.session-model`, `.session-agent`, and `.session-type` all correctly use `var(--purple)`/`var(--teal)`/`var(--amber)` plus their `-soft` background tokens.
Why it matters
A fourth, unrelated blue that doesn't match `--accent` (#6390f0) means the palette now has two competing blues, and this badge won't repaint correctly when the light/oled themes swap tokens.
Fix
Add a project-badge color to the token set (or reuse --accent) instead of a hardcoded one-off hex.
.session-project {
font-size: 11px;
font-weight: 500;
color: #7fb4ff;
background: rgba(39, 94, 182, 0.18);
padding: 2px 10px;
border-radius: 10px;
}.session-project {
font-size: 11px;
font-weight: 500;
color: var(--accent);
background: var(--accent-soft);
padding: 2px 10px;
border-radius: 10px;
}Components
public/index.html:60
Theme, settings, and mobile toolbar buttons sit below the 40px minimum hit area
`.theme-toggle` (used by `#theme-toggle` and `#settings-btn`) renders at 30x30px, and `.mobile-search-btn`/`.theme-toggle-mobile` (used by `#mobile-search-btn`, `#theme-toggle-mobile`, `#settings-btn-mobile`) render at 32x32px per style.css, with no padding-based hit-area extension in the markup.
Why it matters
These are the only controls for switching theme and reaching settings, and the mobile toolbar versions are the sole entry points on touch devices. Sub-40px targets increase mis-taps for touch and motor-impaired users right where the app is hardest to recover from a wrong tap.
Fix
Extend the hit area of small icon buttons with a pseudo-element or padding so the tappable region reaches at least 40x40px without changing the visible icon size.
<button class="mobile-search-btn" id="mobile-search-btn" title="Search" aria-label="Search"><svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="8"/><path d="m21 21-4.35-4.35"/></svg></button><button class="mobile-search-btn hit-area-40" id="mobile-search-btn" title="Search" aria-label="Search"><svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="8"/><path d="m21 21-4.35-4.35"/></svg></button>public/style.css:1002
Three CSS variables are referenced but never defined anywhere in the theme system
`.settings-gear`, `.insight-row:hover`, and `.insight-callout` reference `var(--text-muted)`, `var(--hover-bg)`, and `var(--border-color)`. None of these exist in `:root`, `[data-theme="light"]`, or `[data-theme="oled"]`: the file's real tokens are `--text-tertiary`, `--bg-hover`, and `--border-subtle`/`--border-default`.
Why it matters
An undefined custom property has no fallback, so the browser treats the declaration as invalid and the affected color/background never renders, silently breaking the gear icon color, the insight row hover state, and the callout divider across all three themes.
Fix
Point these declarations at the tokens the rest of the file already uses.
color: var(--text-muted);
display: flex;
align-items: center;
justify-content: center;
transition: color 0.15s, background 0.15s;
}
.settings-gear svg { width: 16px; height: 16px; }
.settings-gear:hover { color: var(--text-primary); background: var(--hover-bg); } color: var(--text-tertiary);
display: flex;
align-items: center;
justify-content: center;
transition: color 0.15s, background 0.15s;
}
.settings-gear svg { width: 16px; height: 16px; }
.settings-gear:hover { color: var(--text-primary); background: var(--bg-hover); }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
public/index.html:35
Sidebar navigation items are unclickable divs, not keyboard-accessible controls
All five primary nav entries ("Overview", "Sessions", "Insights", "Timeline", "Files") are `<div class="nav-item" data-view="...">` elements with no `role`, no `tabindex`, and no keyboard handler visible in the markup. Only a mouse click (wired in app.js) can reach them.
Why it matters
Keyboard users and screen reader users cannot tab to or activate any section of the app. This is the entire navigation system, so it locks out an entire class of users from the product's core interaction.
Fix
Use semantic <button> elements for clickable navigation items so they get focus, keyboard activation, and the correct accessibility role for free.
<div class="nav-item active" data-view="overview">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M3 12h7V3H3z"/><path d="M14 21h7v-7h-7z"/><path d="M14 10h7V3h-7z"/><path d="M3 21h7v-5H3z"/></svg>
<span>Overview</span>
</div><button type="button" class="nav-item active" data-view="overview" aria-current="page">
<svg aria-hidden="true" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M3 12h7V3H3z"/><path d="M14 21h7v-7h-7z"/><path d="M14 10h7V3h-7z"/><path d="M3 21h7v-5H3z"/></svg>
<span>Overview</span>
</button>Motion
public/style.css:583
Three infinite animations run with no reduced-motion guard anywhere in the file
`skeletonShimmer` (skeleton loading), `pulse` (the `.loading::before` dot), and `jumpPulse` (`.jump-to-start-btn.jumping::after`) all run `infinite` with no `@media (prefers-reduced-motion: reduce)` block in the stylesheet at all.
Why it matters
Users with vestibular disorders who have set the OS-level reduce-motion preference still get these looping animations at full strength, which is a direct accessibility failure, not a stylistic nitpick.
Fix
Wrap infinite/looping keyframe animations in a prefers-reduced-motion query and fall back to a static or single-fade state.
@keyframes skeletonShimmer {
100% { transform: translateX(100%); }
}@media (prefers-reduced-motion: no-preference) {
.skeleton-line::after { animation: skeletonShimmer 1.4s ease-in-out infinite; }
}
@keyframes skeletonShimmer {
100% { transform: translateX(100%); }
}Typography
Spacing
UX
Craft
Working well
- The three-theme token architecture (dark default, light, oled) built entirely on CSS custom properties is a genuinely solid foundation: swapping `data-theme` recolors the whole app because nearly every component reads from `--bg-*`/`--text-*`/`--border-*` instead of hardcoding, which is exactly how theming should work.
- Theme toggle and settings buttons (`#theme-toggle`, `#settings-btn`, and their mobile counterparts) all pair a `title` with an `aria-label`, giving both hover and assistive-tech users an identical, unambiguous name for icon-only controls. This is exactly how icon-only affordances should be labeled.
- The shared hover treatment across `.nav-item`, `.filter-chip`, `.stat-card`, `.session-item`, and `.result-item` (consistent `border-color` + `background` shift, or a 1px lift) gives the app one coherent interaction language instead of a different hover idiom per component.
- The `#cmdkBtn` search trigger is a real <button> with a visible "Search" label, a <kbd>⌘K</kbd> hint, and an aria-label. That combination tells sighted, keyboard, and screen-reader users the same thing three ways without adding visual clutter.
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.