vercel on GitHub

vercel/platforms

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

15 files reviewed·July 25, 2026

View on GitHub

Low

Design risk in this codebase.

6issues
Serious & Moderate · top 5 shown below

Top fix

Fix domain suffix text to meet AA contrast against gray background

See the fix

Verdict

Solid structural bones undercut by small unfinished edges: failing contrast, drifted spacing, and straight apostrophes where polish should be. The biggest risk is that spacing values are duplicated raw across components, so one overlooked fix multiplies into two inconsistent places.

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

96/100

Spacing

2 serious
Design SystemSerious

app/subdomain-form.tsx:88

Arbitrary pixel values on the icon preview drift from the spacing scale

The icon preview container uses `min-w-[40px] min-h-[40px] pl-[14px]`, three arbitrary pixel values instead of the Tailwind spacing scale (e.g. w-10, h-10, pl-3.5/pl-4) used elsewhere in the form.

Why it matters

Every arbitrary value here is a number someone has to remember and re-derive by hand next time this component's padding or icon size needs to change, which is how spacing drift accumulates across a codebase.

Fix

Replace arbitrary pixel classes with the nearest Tailwind spacing scale tokens.

<div className="min-w-[40px] min-h-[40px] flex items-center pl-[14px] select-none">
<div className="min-w-10 min-h-10 flex items-center pl-3.5 select-none">
SpacingSerious

app/subdomain-form.tsx:108

Emoji picker dimensions duplicated as raw pixels across two components

`PopoverContent` sets `w-[256px]` and `EmojiPicker` sets `h-[300px] w-[256px]`, the same 256px width hardcoded twice in adjacent JSX instead of a single shared value.

Why it matters

Resizing the emoji picker requires editing two separate class strings in sync; miss one and the popover frame and its inner picker fall out of alignment, which is a maintenance cost for a purely cosmetic dimension.

Fix

Define the picker's width and height once and reference it from both elements instead of repeating the literal.

<PopoverContent
  className="p-0 w-[256px]"
  align="end"
  sideOffset={5}
>
  <EmojiPicker
    className="h-[300px] w-[256px]"
const PICKER_WIDTH = 'w-[256px]';

<PopoverContent
  className={`p-0 ${PICKER_WIDTH}`}
  align="end"
  sideOffset={5}
>
  <EmojiPicker
    className={`h-[300px] ${PICKER_WIDTH}`}
98/100

Typography

1 serious
TypographySerious

app/not-found.tsx:43

Straight apostrophes in "doesn't exist" and "hasn't been created" read as unpolished

The heading text "doesn't exist" (line 36) and the subtext "This subdomain hasn't been created yet." (line 43) both use straight apostrophes ('). This is the only user-facing copy on the entire error page, so its typographic finish carries more weight than usual.

Why it matters

A 404 page is often the first broken-link impression a visitor gets of the product; straight quotes next to a styled heading and gradient background read as an unfinished detail in an otherwise deliberate layout.

Fix

Replace straight apostrophes with curly (') apostrophes in user-facing copy.

This subdomain hasn't been created yet.
This subdomain hasn’t been created yet.

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 free
98/100

Color

1 serious
ColorSerious

app/subdomain-form.tsx:48

Domain suffix text fails AA contrast against its gray background

The `.{rootDomain}` suffix span uses `text-gray-500` on `bg-gray-100`. That pairing computes to roughly 4.39:1, below the 4.5:1 threshold WCAG AA requires for normal-size text. It sits directly beside the subdomain input as part of the same visually merged control, so it reads as primary form content, not decoration.

Why it matters

Users with low vision or in bright ambient light have to work harder to confirm the domain suffix they're about to submit under, which is exactly the kind of detail people double-check before creating a subdomain.

Fix

Darken the suffix text to a shade that clears 4.5:1 against bg-gray-100 for normal-size text.

<span className="bg-gray-100 px-3 border border-l-0 border-input rounded-r-md text-gray-500 min-h-[36px] flex items-center">
  .{rootDomain}
</span>
<span className="bg-gray-100 px-3 border border-l-0 border-input rounded-r-md text-gray-700 min-h-[36px] flex items-center">
  .{rootDomain}
</span>
98/100

UX

1 serious
UXSerious

app/not-found.tsx:30

Full-height wrapper can clip content under mobile Safari's URL bar

The outer container on the not-found page uses `min-h-screen`, which resolves to 100vh, a value taller than the visible viewport on mobile Safari when the address bar is showing. Content centered with `justify-center` inside that box can end up partially hidden or requiring a scroll the user doesn't expect on first load.

Why it matters

On the exact device class most likely to hit a broken subdomain link from a shared URL, the recovery message and "Go to {rootDomain}" action can sit under browser chrome, adding friction right when the user needs the fastest way back.

Fix

Use the dynamic viewport unit so the container height tracks the actual visible area on mobile browsers.

<div className="flex min-h-screen flex-col items-center justify-center bg-gradient-to-b from-blue-50 to-white p-4">
<div className="flex min-h-[100dvh] flex-col items-center justify-center bg-gradient-to-b from-blue-50 to-white p-4">

Accessibility

No issues found

Components

No issues found

Motion

No issues found

Craft

No issues found

Working well

  • The submit button's `text-white` on `bg-blue-600` (5.17:1) and its `hover:bg-blue-700` state (6.70:1) both clear AA comfortably, so the primary CTA stays readable through idle and hover states.
  • Disabling the submit button until an icon is picked (`disabled={isPending || !icon}`) stops an invalid submission at the source instead of letting the user find out after a failed round trip.
  • The empty state ("No subdomains have been created yet.") gives a designed, explicit message instead of leaving a blank grid, which keeps the page from reading as broken.

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

This page is an automated design review of vercel/platforms’s UI code: 15 files read against 309 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