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

dubinc/dub

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

30 files reviewed·August 1, 2026

View on GitHub

Elevated

Design risk in this codebase.

3issues
Critical & Serious

Top fix

Add accessible name to reset-login-attempts email input

See the fix

Verdict

Minimalist inputs read clean but lose their accessible names entirely, not just visually. The paste-blocking override is a blunt fix applied everywhere, risking broken clipboard behavior for every user.

Files Rams reviewed

apps/web/app/(ee)/admin.dub.co/(dashboard)/components/ban-link.tsx

apps/web/app/(ee)/admin.dub.co/(dashboard)/components/delete-partner-account.tsx

apps/web/app/(ee)/admin.dub.co/(dashboard)/components/impersonate-user.tsx

apps/web/app/(ee)/admin.dub.co/(dashboard)/components/impersonate-workspace.tsx

apps/web/app/(ee)/admin.dub.co/(dashboard)/components/reset-login-attempts.tsx

apps/web/app/(ee)/admin.dub.co/(dashboard)/components/slack-support-invite.tsx

apps/web/app/(ee)/admin.dub.co/(dashboard)/components/user-info.tsx

apps/web/app/(ee)/admin.dub.co/(dashboard)/domains/components/refresh-domain.tsx

apps/web/app/(ee)/admin.dub.co/(dashboard)/domains/components/register-premium-domain.tsx

apps/web/app/(ee)/admin.dub.co/(dashboard)/domains/components/renew-domain.tsx

apps/web/app/(ee)/admin.dub.co/(dashboard)/commissions/page.tsx

apps/web/app/(ee)/admin.dub.co/(dashboard)/domains/page.tsx

apps/web/app/(ee)/admin.dub.co/(dashboard)/page.tsx

apps/web/app/(ee)/admin.dub.co/(dashboard)/partners/fraud/page.tsx

apps/web/app/(ee)/admin.dub.co/(dashboard)/partners/network/page.tsx

apps/web/app/(ee)/admin.dub.co/(dashboard)/payouts/paypal/page.tsx

apps/web/app/(ee)/admin.dub.co/(dashboard)/payouts/stablecoin/page.tsx

apps/web/app/(ee)/admin.dub.co/(dashboard)/programs/sales/page.tsx

apps/web/app/(ee)/admin.dub.co/(dashboard)/revenue/page.tsx

apps/web/app/(ee)/app.dub.co/embed/referrals/page.tsx

apps/web/app/(ee)/partners.dub.co/(apply)/[programSlug]/(default)/apply/page.tsx

apps/web/app/(ee)/partners.dub.co/(apply)/[programSlug]/(default)/apply/success/page.tsx

apps/web/app/(ee)/partners.dub.co/(apply)/[programSlug]/(default)/layout.tsx

apps/web/app/(ee)/partners.dub.co/(apply)/[programSlug]/(default)/page.tsx

apps/web/app/(ee)/partners.dub.co/(auth-login-register)/(generic)/layout.tsx

apps/web/app/(ee)/partners.dub.co/(auth-login-register)/(generic)/login/page.tsx

apps/web/app/(ee)/partners.dub.co/(auth-login-register)/(generic)/register/page.tsx

apps/web/app/(ee)/partners.dub.co/(auth-other)/invite/page.tsx

apps/web/app/(ee)/partners.dub.co/(auth-other)/layout.tsx

apps/web/app/(ee)/partners.dub.co/(dashboard)/messages/layout.tsx

94/100

Accessibility

2 critical
AccessibilityCritical

apps/web/app/(ee)/admin.dub.co/(dashboard)/components/reset-login-attempts.tsx:41

Email input on reset-login-attempts form has no accessible name

The email input in reset-login-attempts.tsx (id="email", placeholder="user@example.com") has no <label> or aria-label, so its only identification is the placeholder.

Why it matters

Placeholder-only fields disappear from the accessibility tree once a value is entered, leaving screen reader users with no way to confirm which field they're editing on a form that resets a user's login attempts.

Fix

Give every form input an accessible name via a linked label or aria-label, not just a placeholder.

<input
  name="email"
  id="email"
  type="email"
  required
  disabled={pending}
  autoComplete="off"
  className={cn(...)}
  placeholder="user@example.com"
  aria-invalid="true"
/>
<input
  name="email"
  id="email"
  type="email"
  required
  disabled={pending}
  autoComplete="off"
  aria-label="Email address"
  className={cn(...)}
  placeholder="user@example.com"
/>
AccessibilityCritical

apps/web/app/(ee)/admin.dub.co/(dashboard)/components/ban-link.tsx:53

Key input has no accessible name for screen reader users

The short-link key input in ban-link.tsx (id="key", placeholder="IG47WZs") has no <label htmlFor="key"> and no aria-label. The adjacent "dub.sh" span is purely visual and isn't wired to the input with aria-labelledby.

Why it matters

A screen reader user tabbing into this field hears only "text, required, invalid" with no indication it's a link key, so they can't confirm what they're entering before submitting a ban action.

Fix

Give every form input an accessible name via a linked label or aria-label, not just a placeholder.

<input
  name="key"
  id="key"
  type="text"
  required
  disabled={pending}
  autoComplete="off"
  className={cn(...)}
  placeholder="IG47WZs"
  aria-invalid="true"
<input
  name="key"
  id="key"
  type="text"
  required
  disabled={pending}
  autoComplete="off"
  aria-label="Short link key"
  className={cn(...)}
  placeholder="IG47WZs"
98/100

UX

1 serious
UXSerious

apps/web/app/(ee)/admin.dub.co/(dashboard)/components/impersonate-user.tsx:92

Email onPaste blocks normal paste for every string, not just mailto links

In impersonate-user.tsx, the email input's onPaste always calls e.preventDefault() before checking the pasted text, so even a plain email pasted from anywhere goes through the manual rewrite path instead of the browser's native paste.

Why it matters

Any quirk in the manual value assignment (no undo history, no IME composition support) now affects every paste, not just the mailto: edge case the handler was written for.

Fix

Only intercept and rewrite paste behavior for the specific case that needs it; let the default paste proceed otherwise.

onPaste={(e: React.ClipboardEvent<HTMLInputElement>) => {
  // remove mailto: on paste
  e.preventDefault();
  const text = e.clipboardData.getData("text/plain");
  if (text.startsWith("mailto:")) {
    e.currentTarget.value = text.replace("mailto:", "");
  } else {
    e.currentTarget.value = text;
  }
}}
onPaste={(e: React.ClipboardEvent<HTMLInputElement>) => {
  const text = e.clipboardData.getData("text/plain");
  if (text.startsWith("mailto:")) {
    e.preventDefault();
    e.currentTarget.value = text.replace("mailto:", "");
  }
  // otherwise let the default paste happen
}}

Typography

No issues found

Color

No issues found

Spacing

No issues found

Components

No issues found

Motion

No issues found

Craft

No issues found

Working well

  • The submit button clearly reflects pending state with both a disabled attribute and label text swap to "Sending…", giving good feedback against double-submission (UX-001/UX-003).
  • The onPaste handler stripping 'mailto:' prefixes is a thoughtful, specific touch that solves a real copy-paste annoyance without blocking normal paste behavior.
  • BanButton correctly uses variant="danger" with a loading state tied to useFormStatus, giving clear visual weight and feedback to the destructive action.
  • The pending state disables the input and swaps in a LoadingSpinner, giving clear feedback during the async impersonate request.

Scored August 1, 2026 with Rams Engine v0.0.3 · Engine changelog
First scored July 9, 2026: 59/100. This rescore on v0.0.3: 59/100.

This page is an automated design review of dubinc/dub’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.

Or get a design review on every pull requestInstall Rams