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

vercel/platforms

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

15 files reviewed·July 9, 2026

Elevated

Design risk in this codebase.

6issues
Critical & Serious

Top fix

Add authentication check before rendering admin tenant dashboard

See the fix

Verdict

Visual restraint is genuine, gray typography and simple layouts show real taste, but the interactions guarding tenant data are almost nonexistent. An unauthenticated admin dashboard sitting next to a one-click destructive delete is the biggest risk here, not the gradients or spacing.

Files Rams reviewed

app/page.tsx

app/s/[subdomain]/page.tsx

app/admin/page.tsx

app/layout.tsx

app/admin/dashboard.tsx

app/globals.css

app/not-found.tsx

app/subdomain-form.tsx

components/ui/button.tsx

components/ui/card.tsx

components/ui/dialog.tsx

components/ui/emoji-picker.tsx

components/ui/popover.tsx

components/ui/input.tsx

components/ui/label.tsx

94/100

Accessibility

3 serious
Accessibility·app/subdomain-form.tsx:152Serious

Error message text fails contrast at 3.64:1 against white

The submission error `<div className="text-sm text-red-500">{state.error}</div>` renders red-500 (#ef4444) text-sm on a white form background.

Why it matters

3.64:1 fails WCAG AA's 4.5:1 threshold for small text, so users with low vision struggle to read exactly the message telling them why their subdomain wasn't created.

Fix

Darken the red to a shade that clears 4.5:1 against white, such as red-600 or red-700.

<div className="text-sm text-red-500">{state.error}</div>
<div className="text-sm text-red-700">{state.error}</div>
Accessibility·app/s/[subdomain]/page.tsx:40Serious

Corner navigation link has a tap target far under the 44px touch minimum

The `{rootDomain}` link ("`rootdomain.com`") is rendered as bare `text-sm` text with no padding, inside a `top-4 right-4` positioned wrapper.

Why it matters

At roughly 14-20px tall with no padding, this is well under the 44x44px target size platform guidance recommends, making it hard to tap accurately on mobile, especially in a corner where mis-taps are common.

Fix

Give small interactive links padding so the clickable area reaches at least 44x44px.

className="text-sm text-gray-500 hover:text-gray-700 transition-colors"
className="inline-flex items-center px-3 py-2 text-sm text-gray-500 hover:text-gray-700 transition-colors"
Accessibility·app/not-found.tsx:45Serious

Primary CTA's tap target falls short of comfortable mobile size

The only action on the page, `Create {subdomain}` / `Go to {rootDomain}`, is a `Link` styled with `px-4 py-2 text-sm`, which resolves to roughly 36px tall.

Why it matters

This clears the 24px WCAG AA floor but sits below the 44px platform guidance for primary actions, and this is the single button a user has to tap to recover from a broken subdomain, often from a mobile browser.

Fix

Increase vertical padding on the primary CTA to reach at least a 44px tap height.

className="rounded-md bg-blue-600 px-4 py-2 text-sm font-medium text-white hover:bg-blue-700"
className="rounded-md bg-blue-600 px-4 py-3 text-sm font-medium text-white hover:bg-blue-700"
94/100

UX

2 critical
UX·app/admin/page.tsx:11Critical

Admin dashboard renders tenant data with no authentication check

`AdminPage` calls `getAllSubdomains()` and passes every tenant straight into `<AdminDashboard tenants={tenants} />` with only a `// TODO: You can add authentication here` comment marking the gap. There is no auth check, redirect, or unauthorized state before the full tenant list renders.

Why it matters

Anyone who reaches this route sees the complete subdomain list for every tenant. This is a reachable state (unauthenticated visitor) that the component never accounts for, and it ships in production exactly as written.

Fix

Gate the data fetch behind a server-side session check and redirect or render an unauthorized state before calling getAllSubdomains().

// TODO: You can add authentication here with your preferred auth provider
const tenants = await getAllSubdomains();
const session = await getSession();
if (!session?.isAdmin) redirect('/login');
const tenants = await getAllSubdomains();
UX·app/admin/dashboard.tsx:79Critical

Delete button destroys a tenant subdomain with zero confirmation

The trash icon `<Button variant="ghost" size="icon" type="submit">` inside each tenant card submits `deleteSubdomainAction` directly on click, no confirm dialog, no undo.

Why it matters

A single misclick on any card permanently removes a live subdomain with no recovery path, which is the exact failure mode destructive-action confirmation exists to prevent.

Fix

Wrap the delete trigger in an AlertDialog that requires explicit confirmation before submitting the form.

<Button
  variant="ghost"
  size="icon"
  type="submit"
  disabled={isPending}
  className="text-gray-500 hover:text-gray-700 hover:bg-gray-50"
>
<AlertDialog>
  <AlertDialogTrigger asChild>
    <Button
      variant="ghost"
      size="icon"
      type="button"
      disabled={isPending}
      aria-label={`Delete ${tenant.subdomain}`}
      className="text-gray-500 hover:text-gray-700 hover:bg-gray-50"
    >

Want this on your repo?

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

Review my public repo for free
98/100

Spacing

1 serious
Spacing·app/page.tsx:26Serious

Redundant mt-8 duplicates spacing already set by parent's space-y-8

The form card div has both a parent `space-y-8` (which already applies `margin-top` to non-first children) and its own explicit `mt-8` class. Two rules targeting the same margin-top property on the same element is dead weight in the class list.

Why it matters

Redundant spacing utilities make it unclear which value actually controls the layout, so a future edit to one and not the other silently does nothing, costing debugging time.

Fix

Let the parent's space-y utility own the gap between children and drop the duplicate margin class.

<div className="mt-8 bg-white shadow-md rounded-lg p-6">
<div className="bg-white shadow-md rounded-lg p-6">

Typography

No issues found

Color

No issues found

Components

No issues found

Motion

No issues found

Craft

No issues found

Working well

  • Putting the form inside a single `shadow-md rounded-lg` card against a plain page gives the layout one clear focal point: there's exactly one thing to interact with, and the corner-anchored 'Admin' link stays visually secondary by using plain text instead of a button treatment.
  • The page commits to a single heading and a single supporting line: "Welcome to {subdomain}.{rootDomain}" then "This is your custom subdomain page." There's no competing element fighting for attention, so the emoji, heading, and subtext read in a clean top-to-bottom order.
  • Fetching `tenants` in the server component and handing them to `<AdminDashboard tenants={tenants} />` as a prop keeps data-fetching separate from presentation. This is the right split: the dashboard component stays reusable and testable without owning the fetch logic.
  • The CTA label changes with context: "Create {subdomain}" when a specific subdomain is missing versus "Go to {rootDomain}" for the generic case. Naming the actual outcome instead of a flat "Get Started" is exactly what a recovery action on an error page should do.

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.