Rams MCP · The full engine, now in your coding agent
hatchet-dev on GitHub

hatchet-dev/hatchet

Reviewed against Rams quality heuristics: accessibility, color, typography, spacing, components, motion, UX, and craft.

30 files reviewed·July 9, 2026

Elevated

Design risk in this codebase.

6issues
Critical & Serious

Top fix

Add a proper label and autocomplete to the SSO email input

See the fix

Verdict

Structural bones are clean, sensible layouts, token discipline, gated validation, but the auth flow silently fails assistive tech users at every critical moment. The SSO email field's missing label is the single sharpest risk, blocking screen reader users right at sign-in.

Files Rams reviewed

frontend/app/src/pages/auth/components/auth-layout.tsx

frontend/app/src/pages/auth/components/auth-page.tsx

frontend/app/src/pages/error/components/layout.tsx

frontend/app/src/pages/auth/components/social-auth.tsx

frontend/app/src/pages/auth/login/components/user-login-form.tsx

frontend/app/src/pages/auth/register/components/user-register-form.tsx

frontend/app/src/pages/error/components/generic-error.tsx

frontend/app/src/pages/error/components/new-version-available.tsx

frontend/app/src/pages/error/components/not-found.tsx

frontend/app/src/pages/error/components/resource-not-found.tsx

frontend/app/src/pages/error/components/tenant-forbidden.tsx

frontend/app/src/pages/main/info/components/version-info.tsx

frontend/app/src/pages/main/v1/events/components/additional-metadata.tsx

frontend/app/src/pages/main/v1/events/components/event-columns.tsx

frontend/app/src/pages/main/v1/filters/components/filter-columns.tsx

frontend/app/src/pages/main/v1/filters/components/filter-create-form.tsx

frontend/app/src/pages/main/v1/filters/components/filter-detail-view.tsx

frontend/app/src/pages/main/v1/logs/components/logs-chart.tsx

frontend/app/src/pages/main/v1/managed-workers/$managed-worker/components/github-button.tsx

frontend/app/src/pages/main/v1/managed-workers/$managed-worker/components/managed-worker-activity.tsx

frontend/app/src/pages/main/v1/managed-workers/$managed-worker/components/managed-worker-build-logs.tsx

frontend/app/src/pages/main/v1/managed-workers/$managed-worker/components/managed-worker-build.tsx

frontend/app/src/pages/main/v1/managed-workers/$managed-worker/components/managed-worker-iac-logs.tsx

frontend/app/src/pages/main/v1/managed-workers/$managed-worker/components/managed-worker-instances-table.tsx

frontend/app/src/pages/main/v1/managed-workers/$managed-worker/components/managed-worker-logs.tsx

frontend/app/src/pages/main/v1/managed-workers/$managed-worker/components/managed-worker-metrics.tsx

frontend/app/src/pages/main/v1/managed-workers/components/billing-required.tsx

frontend/app/src/pages/main/v1/managed-workers/components/managed-workers-gate.tsx

frontend/app/src/pages/main/v1/managed-workers/components/managed-workers-table.tsx

frontend/app/src/pages/main/v1/managed-workers/components/monthly-usage-card.tsx

91/100

Accessibility

1 critical3 serious
Accessibility·frontend/app/src/pages/auth/components/social-auth.tsx:78Critical

SSO email field has no label and no autocomplete hook

The expanded SSO input (`placeholder="Enter your email"`) has no `<label>`, `aria-label`, or `autoComplete` attribute. Screen readers announce it as an unlabeled textbox, and browsers/password managers can't autofill it.

Why it matters

Screen reader users get no indication of what the field expects once the placeholder disappears on focus, and users lose autofill, adding friction right at the point where they're trying to sign in fastest.

Fix

Add a visually-hidden label tied to the input via htmlFor/id, plus autoComplete="email".

<input
  type="text"
  value={email}
  onChange={(e) => setEmail(e.target.value)}
  placeholder="Enter your email"
  className="h-10 flex-1 rounded-md border border-muted-foreground/20 bg-background px-3 text-sm placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring"
/>
<label htmlFor="sso-email" className="sr-only">Email</label>
<input
  id="sso-email"
  type="email"
  autoComplete="email"
  value={email}
  onChange={(e) => setEmail(e.target.value)}
  placeholder="Enter your email"
  className="h-10 flex-1 rounded-md border border-muted-foreground/20 bg-background px-3 text-base placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring"
/>
Accessibility·frontend/app/src/pages/auth/login/components/user-login-form.tsx:68Serious

Red error text fails contrast at small size against the white card

Both error messages use `text-sm text-red-500` on what renders as a white form background (no ancestor background override in this file).

Why it matters

text-red-500 (#ef4444) on white computes to roughly 3.8:1, below the 4.5:1 AA threshold for text under 18px. Users with low vision may not be able to read the very message telling them what went wrong with their email or password.

Fix

Darken the error text to a red shade with at least 4.5:1 contrast on white, such as Tailwind's red-600 or red-700.

<div className="text-sm text-red-500">{emailError}</div>
<div className="text-sm text-red-600">{emailError}</div>
Accessibility·frontend/app/src/pages/auth/register/components/user-register-form.tsx:89Serious

Field errors appear on screen with no announcement to screen readers

`nameError`, `emailError`, and `passwordError` render as plain `<div className="text-sm text-red-500">` with no `role="alert"` or `aria-live`. Sighted users see red text appear under the field; screen reader users get nothing.

Why it matters

A screen reader user submits the form, hears no feedback, and has no way to know which field failed or why, so they can't complete registration without sighted help.

Fix

Add role='alert' (or aria-live='polite') to each inline error message so assistive tech announces it the moment it renders.

{nameError && (
  <div className="text-sm text-red-500">{nameError}</div>
)}
{nameError && (
  <div role="alert" className="text-sm text-red-500">{nameError}</div>
)}
Accessibility·frontend/app/src/pages/error/components/layout.tsx:30Serious

Error page title renders as a div, not a heading

The `title` prop renders inside a plain `<div className="text-xl font-semibold tracking-tight sm:text-2xl">` rather than an `<h1>` or `<h2>`. Since `ErrorPageLayout` is the root layout for full error pages (404, 500, etc.), this is effectively the page's only heading.

Why it matters

Screen reader users navigate error pages by heading structure. With no real heading in the DOM, assistive tech announces the title as plain text and users lose the fastest way to confirm what error they've landed on.

Fix

Wrap the visible page title in a semantic heading element instead of a styled div.

<div className="text-xl font-semibold tracking-tight sm:text-2xl">
  {title}
</div>
<h1 className="text-xl font-semibold tracking-tight sm:text-2xl">
  {title}
</h1>
98/100

Spacing

1 serious
Spacing·frontend/app/src/pages/auth/components/auth-page.tsx:76Serious

Wrapping AuthLegalText in a spacing container with a single child

`<div className="pt-3 space-y-5"><AuthLegalText /></div>` applies `space-y-5` for vertical spacing between children, but there is only one child, so the class does nothing and only `pt-3` has any effect.

Why it matters

Dead utility classes accumulate as noise in the codebase and make it harder to tell which spacing rules are load-bearing when someone edits this section later.

Fix

Drop spacing utilities that have no sibling to apply against.

<div className="pt-3 space-y-5">
  <AuthLegalText />
</div>
<div className="pt-3">
  <AuthLegalText />
</div>

Want this on your repo?

Rams reviews your next PR automatically and posts inline fix suggestions.

Review my public repo for free
98/100

Craft

1 serious
Craft·frontend/app/src/pages/auth/components/auth-layout.tsx:10Serious

Grid template and background effect built entirely from arbitrary bracket syntax

The outer grid uses `lg:grid-cols-[1fr_round(nearest,50%,24px)]`, and the hero background stacks a dozen more bracket values (`[height:clamp(50rlh,30vh,50rlh)]`, `[mask-image:...]`, `[mask-position:...]`, `[--c:#fff7_25%,#0000_0]`) in one unbroken `className` string.

Why it matters

Every value here sits outside the spacing/color scale, so nobody can reuse, theme, or safely edit this effect without re-deriving the whole mask/gradient math from scratch, and any future change risks silently breaking the visual.

Fix

Extract dense one-off arbitrary CSS into a dedicated CSS file or `@layer components` class so the utility markup stays legible and the effect is reusable.

<div className="min-h-screen w-full lg:grid lg:grid-cols-[1fr_round(nearest,50%,24px)]">
<div className="min-h-screen w-full lg:grid lg:grid-cols-auth">

Typography

No issues found

Color

No issues found

Components

No issues found

Motion

No issues found

UX

No issues found

Working well

  • The title/altAction pairing (`text-2xl font-semibold tracking-tight` next to `text-sm text-muted-foreground`) gives the page an unambiguous primary/secondary read: the eye lands on the page title first, and the alt action reads as a supporting link rather than competing for attention. That's the right way to size two adjacent text elements that carry different roles.
  • Building `sections` as a filtered array of conditionals, then mapping with an `OrContinueWith` divider inserted only between actual neighbors (`index < sections.length - 1`), avoids the nested-ternary mess this kind of conditional composition usually produces, and keeps the separator logic correct regardless of which combination of schemes is enabled.
  • The actions footer uses `flex flex-col gap-2 sm:flex-row sm:justify-center`, stacking recovery actions vertically on mobile and centering them horizontally on wider screens. This is the correct responsive pattern for CTA rows: it keeps tap targets full-width on small screens without extra media-query overrides.
  • Every color in this component runs through tokens: `border-border/60`, `bg-background`, `text-foreground/80`, `bg-muted/30`, `text-muted-foreground`. No hardcoded hex or arbitrary values anywhere, which means this layout inherits theme and dark-mode changes for free instead of drifting from the design system.

Score your own repo with Rams.

Free on public repos. Rams reviews every pull request for accessibility, color, type, spacing, components, motion, UX, and craft, and posts inline fixes.