vercel on GitHub

vercel/commerce

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

30 files reviewed·July 25, 2026

View on GitHub

Top fix

Add a sized placeholder for the title and price column

See the fix

Verdict

Structural instincts are strong, but spacing discipline is an afterthought: real breakpoints get skipped in favor of one-off pixel values. The biggest risk is inconsistent loading placeholders, where some elements reserve space and others just pop in.

Files Rams reviewed

app/[page]/page.tsx

app/layout.tsx

app/product/[handle]/page.tsx

app/search/[collection]/page.tsx

app/search/layout.tsx

app/search/page.tsx

app/[page]/layout.tsx

app/page.tsx

components/layout/navbar/index.tsx

components/layout/search/filter/index.tsx

components/carousel.tsx

components/cart/add-to-cart.tsx

components/cart/cart-context.tsx

components/cart/delete-item-button.tsx

components/cart/edit-item-quantity-button.tsx

components/cart/modal.tsx

components/grid/three-items.tsx

components/grid/tile.tsx

components/label.tsx

components/layout/footer-menu.tsx

components/layout/footer.tsx

components/layout/navbar/mobile-menu.tsx

components/layout/navbar/search.tsx

components/layout/product-grid-items.tsx

components/layout/search/collections.tsx

components/layout/search/filter/dropdown.tsx

components/layout/search/filter/item.tsx

components/opengraph-image.tsx

components/product/gallery.tsx

components/product/product-description.tsx

96/100

Spacing

2 serious
SpacingSerious

app/product/[handle]/page.tsx:125

Related product tiles use an ad hoc 475px breakpoint outside the scale

The related product `<li>` at line 125 uses `min-[475px]:w-1/2`, and the matching `sizes` attribute on `GridTileImage` (line 141) repeats the same `475px` value inline. Neither Tailwind's default screens (`sm: 640px`, `md: 768px`, etc.) nor a configured custom screen defines this breakpoint, so it's a one-off number duplicated in two places.

Why it matters

A breakpoint invented once and repeated as a string in two attributes will drift out of sync the first time someone edits one but not the other, breaking the tile grid's responsive alignment with its own `sizes` hint and causing the browser to fetch the wrong image size.

Fix

Define the custom breakpoint once in the Tailwind config as a named screen so both the class and the sizes attribute reference the same source.

className="aspect-square w-full flex-none min-[475px]:w-1/2 sm:w-1/3 md:w-1/4 lg:w-1/5"
className="aspect-square w-full flex-none xs:w-1/2 sm:w-1/3 md:w-1/4 lg:w-1/5" // add `xs: '475px'` to theme.screens in tailwind.config
Design SystemSerious

app/product/[handle]/page.tsx:88

Gallery loading skeleton locked to an arbitrary 550px height

The Suspense fallback for the product gallery, `<div className="relative aspect-square h-full max-h-[550px] w-full overflow-hidden" />` (line 88), pins the loading placeholder to a one-off pixel value that exists nowhere else in the scale. `aspect-square` already derives height from width, so the arbitrary cap is redundant and untracked.

Why it matters

A bespoke pixel value like this drifts the moment someone resizes the real Gallery component or changes the container padding, and the skeleton silently stops matching the loaded content, causing a layout jump on hydration.

Fix

Let aspect-square control the fallback's height instead of pinning an arbitrary pixel cap.

<div className="relative aspect-square h-full max-h-[550px] w-full overflow-hidden" />
<div className="relative aspect-square w-full overflow-hidden" />
98/100

UX

1 serious
UXSerious

app/product/[handle]/page.tsx:101

Product title and price column pops in with no loading placeholder

The `ProductDescription` Suspense boundary at line 101 uses `fallback={null}`, so while the title, price, and options stream in, the right-hand column (`basis-full lg:basis-2/6`) renders completely empty. The gallery on the left has a sized skeleton, but this column has nothing holding its space.

Why it matters

The description column has no reserved height while streaming, so when it resolves the layout shifts and the page reflows around it, which reads as janky next to the gallery's already-sized fallback.

Fix

Give the ProductDescription boundary a skeleton fallback that reserves the same width and approximate height as the loaded content.

<Suspense fallback={null}>
  <ProductDescription product={product} />
</Suspense>
<Suspense fallback={<div className="h-[400px] w-full animate-pulse rounded-lg bg-neutral-100 dark:bg-neutral-900" />}>
  <ProductDescription product={product} />
</Suspense>

Accessibility

No issues found

Typography

No issues found

Color

No issues found

Components

No issues found

Motion

No issues found

Craft

No issues found

Working well

  • GridTileImage in the related products list receives a real `alt={product.title}` and the entire tile is wrapped in a single `Link`, giving users one clear, keyboard-reachable hit target instead of nested clickable regions.
  • The gallery Suspense boundary ships a sized skeleton (`aspect-square`, `max-h-[550px]`) rather than `null`, which is the right instinct for avoiding layout shift even though the exact value needs cleanup.
  • `RelatedProducts` returns `null` early when there are no recommendations (`if (!relatedProducts.length) return null;`), avoiding an empty heading and empty list from rendering for no reason.

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

This page is an automated design review of vercel/commerce’s UI code: 30 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