Rams MCP · The full engine, now in your coding agent
triggerdotdev on GitHub

triggerdotdev/trigger.dev

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

30 files reviewed·July 9, 2026

Top fix

Add a semantic main landmark to the app-wide layout wrapper

See the fix

Verdict

Solid composable primitives get undermined by sloppy details: hardcoded hex colors, missing labels, and unset logo sizes. The layout wrapper used by every page in the app has no main landmark, making this the highest-leverage fix available.

Files Rams reviewed

apps/webapp/app/components/LoginPageLayout.tsx

apps/webapp/app/components/layout/AppLayout.tsx

apps/webapp/app/components/primitives/SettingsLayout.tsx

apps/webapp/app/components/BackgroundWrapper.tsx

apps/webapp/app/components/BulkActionFilterSummary.tsx

apps/webapp/app/components/DefinitionTooltip.tsx

apps/webapp/app/components/DevPresence.tsx

apps/webapp/app/components/ErrorDisplay.tsx

apps/webapp/app/components/FeatureBadges.tsx

apps/webapp/app/components/Feedback.tsx

apps/webapp/app/components/GitHubLoginButton.tsx

apps/webapp/app/components/GitMetadata.tsx

apps/webapp/app/components/ListPagination.tsx

apps/webapp/app/components/LogLevelTooltipInfo.tsx

apps/webapp/app/components/LogoType.tsx

apps/webapp/app/components/MachineLabelCombo.tsx

apps/webapp/app/components/MachineTooltipInfo.tsx

apps/webapp/app/components/RuntimeIcon.tsx

apps/webapp/app/components/SetupCommands.tsx

apps/webapp/app/components/Shortcuts.tsx

apps/webapp/app/components/TriggerRotatingLogo.tsx

apps/webapp/app/components/WarmStarts.tsx

apps/webapp/app/components/admin/FeatureFlagsDialog.tsx

apps/webapp/app/components/admin/FlagControls.tsx

apps/webapp/app/components/admin/backOffice/MaxProjectsSection.tsx

apps/webapp/app/components/admin/backOffice/RateLimitSection.tsx

apps/webapp/app/components/admin/debugRun.tsx

apps/webapp/app/components/admin/debugTooltip.tsx

apps/webapp/app/components/billing/AnimatedOrgBannerBar.tsx

apps/webapp/app/components/billing/BillingAlertsSection.tsx

94/100

Spacing

3 serious
Spacing·apps/webapp/app/components/BackgroundWrapper.tsx:27Serious

Table background layer mixes arbitrary inline positioning with no scale reference

The table background div sets `left: "260px"`, `width: "100%"`, `height: "100vh"`, and `backgroundSize: "1200px auto"` all as raw inline style strings rather than Tailwind scale classes or shared constants tied to the sidebar width used elsewhere in this same file.

Why it matters

The `260px` offset here has to stay manually in sync with the `w-[260px]` sidebar width in the two divs above it; there's no shared variable, so a sidebar resize breaks the table background alignment without any compiler warning.

Fix

Derive the offset from one shared constant (e.g. a SIDEBAR_WIDTH value) instead of repeating the literal 260px in a separate inline style.

style={{
  left: "260px",
  backgroundImage: `url(${blurredDashboardBackgroundTable})`,
  width: "100%",
  height: "100vh",
  backgroundSize: "1200px auto",
  backgroundColor: "#101214",
}}
style={{
  left: SIDEBAR_WIDTH,
  backgroundImage: `url(${blurredDashboardBackgroundTable})`,
  width: "100%",
  height: "100vh",
  backgroundSize: "1200px auto",
}}
className="... bg-background"
Spacing·apps/webapp/app/components/LoginPageLayout.tsx:78Serious

Trusted-by logo row sizes one logo explicitly and leaves five unset

In the "Trusted by developers at" row, `LyftLogo` gets `className="w-11"` but `UnkeyLogo`, `MiddayLogo`, `AppsmithLogo`, `CalComLogo`, and `TldrawLogo` render with no size class at all.

Why it matters

Six logos meant to read as one uniform trust strip end up at whatever default width each component ships with, so the row likely shows uneven logo scale and breaks the alignment the flex/gap layout is trying to create.

Fix

Apply the same sizing class to every logo in a repeated row so the group reads as one consistent unit.

<LyftLogo className="w-11" />
<UnkeyLogo />
<MiddayLogo />
<AppsmithLogo />
<CalComLogo />
<TldrawLogo />
<LyftLogo className="w-11" />
<UnkeyLogo className="w-11" />
<MiddayLogo className="w-11" />
<AppsmithLogo className="w-11" />
<CalComLogo className="w-11" />
<TldrawLogo className="w-11" />
Spacing·apps/webapp/app/components/layout/AppLayout.tsx:74Serious

MainCenteredContainer caps width at max-w-xs regardless of content type

`MainCenteredContainer` hardcodes `max-w-xs` (320px) for its inner wrapper in both the `default` and `onboarding` variants, with no way to widen it besides the passed `className` override fighting the base class.

Why it matters

A fixed 320px cap on a generic centered container used across sign-in, onboarding, and other flows constrains any content wider than a simple form (e.g. a multi-field card or wider copy) and forces every consumer to pass an override className to fight the base, increasing chance of visual drift between pages that remember to override and pages that don't.

Fix

Move the max-width to a sensible default that can be cleanly extended via className without overriding a fixed value.

"mx-auto max-w-xs p-1",
"mx-auto max-w-xs p-1", // consider widening default or documenting when callers should override
96/100

Accessibility

2 serious
Accessibility·apps/webapp/app/components/layout/AppLayout.tsx:19Serious

App-wide layout wrapper has no landmark region for screen readers

`AppContainer`, `MainBody`, and `PageContainer` wrap the entire application in plain `<div>` elements. None of these render `<main>`, `role="main"`, or any landmark. Since this file is the single layout primitive every page passes through, no page built on top of it gets a main landmark unless each page re-adds one individually.

Why it matters

Screen reader users rely on landmark navigation ("skip to main content") to jump past repeated chrome. Without a `<main>` region anywhere in the shared layout, every page in the app forces assistive tech users to tab through nav and headers linearly.

Fix

Render the primary content wrapper as a semantic `<main>` element instead of a generic `<div>`.

export function MainBody({ children }: { children: React.ReactNode }) {
  return <div className={cn("grid grid-rows-1 overflow-hidden")}>{children}</div>;
}
export function MainBody({ children }: { children: React.ReactNode }) {
  return <main className={cn("grid grid-rows-1 overflow-hidden")}>{children}</main>;
}
Accessibility·apps/webapp/app/components/primitives/SettingsLayout.tsx:91Serious

SettingsRowTitle only becomes an accessible label when callers remember htmlFor

`SettingsRowTitle` renders a `<label htmlFor={htmlFor}>` only if `htmlFor` is explicitly passed; otherwise it silently falls back to a bare `<span>`. `SettingsRow` forwards `htmlFor` but nothing in the API requires it when `action` is an interactive control (switch, select, input).

Why it matters

Every settings row that pairs a title like "Two-factor authentication" with a toggle in `action` needs that title programmatically associated with the control, or screen reader users hear an unlabeled switch. Because the association is opt-in, it's easy for a future row to ship with a visually-labeled but semantically unlabeled control.

Fix

Require or default the row's label association whenever an interactive action is present, instead of leaving htmlFor as an easy-to-skip optional prop.

const classes = cn("font-sans text-sm font-semibold leading-tight text-text-bright", className);
return htmlFor ? (
const classes = cn("font-sans text-sm font-semibold leading-tight text-text-bright", className);
// Prefer always passing htmlFor from SettingsRow when `action` renders an input/switch/select,
// so the title is never silently unassociated with its control.
return htmlFor ? (

Want this on your repo?

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

Review my public repo for free
98/100

Color

1 serious
Design System·apps/webapp/app/components/BackgroundWrapper.tsx:36Serious

Hardcoded hex #101214 breaks theming for the table background layer

The third background div sets `backgroundColor: "#101214"` inline, while the wrapper around it uses the token class `bg-background-dimmed`. One layer is on the token system, the other is a raw hex value nobody can find or update from one place.

Why it matters

If the background token changes (theme tweak, new dark mode value), this layer silently drifts out of sync with the rest of the dashboard chrome, and there is no single source of truth to update.

Fix

Replace hardcoded hex values with the existing background design token.

  backgroundSize: "1200px auto",
  backgroundColor: "#101214",
}}
    backgroundSize: "1200px auto",
  }}
className="... bg-background"

Typography

No issues found

Components

No issues found

Motion

No issues found

UX

No issues found

Craft

No issues found

Working well

  • Divider color (`border-grid-dimmed`) and heading text color (`text-text-bright`) are used consistently across SettingsHeader, SettingsRow, and SettingsBlock instead of being redeclared with raw hex or ad hoc grays in each component. That's exactly what makes a token system pay off: one value to change, not three.
  • SettingsSection's `[&:not(:first-child)]:mt-12` pattern gives consistent vertical rhythm between sections without double-margins on the first one. This is a smarter default than a plain `space-y-12` wrapper because it lets a single `SettingsSection` be dropped anywhere without knowing what comes before it.
  • SettingsRow's `children` escape hatch (skipping the built-in title/description layout when children is provided) is a good API decision: it covers the composable common case while still giving callers a way out for custom left-hand content, instead of forcing a rigid title+description shape everywhere.
  • PageBody correctly forwards a ref via forwardRef and toggles between `overflow-y-auto` and `overflow-hidden` based on a `scrollable` prop instead of hardcoding scroll behavior. This keeps the same primitive usable for both scrollable page bodies and fixed-height panels without a second component.

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.