Rams MCP · The full engine, now in your coding agent
nextui-org on GitHub

nextui-org/nextui

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

30 files reviewed·July 29, 2026

View on GitHub

Elevated

Design risk in this codebase.

2issues
Critical & Serious

Top fix

Add reduced-motion check before firing the spring entrance animation

See the fix

Verdict

A promo banner wants to feel alive but forgets the basics: motion that respects preference and color that respects contrast. Fix the reduced-motion gap first, it's the one accessibility miss that actively harms users who opted out of animation.

Files Rams reviewed

apps/docs/src/app/docs/native-showcase/components/[[...slug]]/page.tsx

apps/docs/src/app/[lang]/(home)/components/demo-showcase.tsx

apps/docs/src/app/[lang]/(home)/components/pro-badge.tsx

apps/docs/src/app/[lang]/(home)/components/pro-banner.tsx

apps/docs/src/app/[lang]/(home)/components/pro-title.tsx

apps/docs/src/app/[lang]/(home)/components/release-badges.tsx

apps/docs/src/app/[lang]/themes/components/accent-color-selector.tsx

apps/docs/src/app/[lang]/themes/components/bottom-sheet.tsx

apps/docs/src/app/[lang]/themes/components/builder-header.tsx

apps/docs/src/app/[lang]/themes/components/chroma-slider.tsx

apps/docs/src/app/[lang]/themes/components/custom-fonts.tsx

apps/docs/src/app/[lang]/themes/components/font-family-popover.tsx

apps/docs/src/app/[lang]/themes/components/lockable-label.tsx

apps/docs/src/app/[lang]/themes/components/mobile-footer.tsx

apps/docs/src/app/[lang]/themes/components/onboarding.tsx

apps/docs/src/app/[lang]/themes/components/preview-container.tsx

apps/docs/src/app/[lang]/themes/components/radius-popover.tsx

apps/docs/src/app/[lang]/themes/components/reset-button.tsx

apps/docs/src/app/[lang]/themes/components/shuffle-button.tsx

apps/docs/src/app/[lang]/themes/components/suggested-fonts.tsx

apps/docs/src/app/[lang]/themes/components/switch-mode.tsx

apps/docs/src/app/[lang]/themes/components/theme-builder-content.tsx

apps/docs/src/app/[lang]/themes/components/theme-code-panel.tsx

apps/docs/src/app/[lang]/themes/components/theme-input.tsx

apps/docs/src/app/[lang]/themes/components/theme-popover.tsx

apps/docs/src/app/[lang]/themes/components/themes-list.tsx

apps/docs/src/app/[lang]/(home)/layout.tsx

apps/docs/src/app/[lang]/(home)/page.tsx

apps/docs/src/app/[lang]/(home)/showcase/layout.tsx

apps/docs/src/app/[lang]/blog/[slug]/page.tsx

97/100

Motion

1 critical
MotionCritical

apps/docs/src/app/[lang]/(home)/components/pro-banner.tsx:216

Pro banner's spring entrance ignores reduced-motion preference

The pro banner card at the bottom-left corner animates in and out with a spring transition that scales from 0.95 to 1 and translates 20px on the y-axis (`initial={{opacity: 0, scale: 0.95, y: 20}}`, `transition={{type: "spring", stiffness: 300, damping: 25}}`). Nothing in this block checks `useReducedMotion` or wraps the animation in a `MotionConfig` that respects `prefers-reduced-motion`, so the scale/translate combination fires unconditionally for every visitor.

Why it matters

Scale and position animations are the two motion types most likely to trigger discomfort for vestibular-sensitive users, and this banner appears on every homepage load until dismissed, so the exposure is not a one-off.

Fix

Gate transform-based motion behind a reduced-motion check and fall back to an opacity-only transition when the user has that preference set.

<motion.div
  animate={{opacity: 1, scale: 1, y: 0}}
  className="fixed bottom-6 left-6 z-50 w-[288px] overflow-hidden rounded-[20px] border border-separator bg-surface shadow-xl"
  exit={{opacity: 0, scale: 0.95, transition: {duration: 0.2, ease: "easeIn"}, y: 20}}
  initial={{opacity: 0, scale: 0.95, y: 20}}
  transition={{damping: 25, delay: 0.5, stiffness: 300, type: "spring"}}
>
const reduceMotion = useReducedMotion();
<motion.div
  animate={{opacity: 1, scale: 1, y: 0}}
  className="fixed bottom-6 left-6 z-50 w-[288px] overflow-hidden rounded-[20px] border border-separator bg-surface shadow-xl"
  exit={reduceMotion ? {opacity: 0} : {opacity: 0, scale: 0.95, transition: {duration: 0.2, ease: "easeIn"}, y: 20}}
  initial={reduceMotion ? {opacity: 0} : {opacity: 0, scale: 0.95, y: 20}}
  transition={reduceMotion ? {duration: 0.2} : {damping: 25, delay: 0.5, stiffness: 300, type: "spring"}}
>
98/100

Color

1 serious
ColorSerious

apps/docs/src/app/[lang]/(home)/components/pro-banner.tsx:343

Countdown label text fails contrast and bypasses the token system

The "Ends in" countdown and the "Now available" fallback label both use `text-[#4E75A5]` on the banner's light-blue gradient background (~#CCE5F1). Computed against that background this pairing is roughly 3.6:1, below the 4.5:1 AA threshold for `text-xs` copy, and the hex is hardcoded rather than pulled from a token, so if the gradient shifts elsewhere this color drifts out of sync.

Why it matters

At sub-4.5:1 contrast, users with low vision struggle to read the days/hours/minutes/seconds countdown, which is the banner's core call to urgency, and a one-off hex means any future gradient tweak silently breaks readability again.

Fix

Darken the label to the same blue hue at a contrast ratio above 4.5:1, and route it through a token instead of a bespoke hex.

<span className="relative text-xs text-[#4E75A5] tabular-nums">
<span className="relative text-xs text-[#2F5480] tabular-nums">

Accessibility

No issues found

Typography

No issues found

Spacing

No issues found

Components

No issues found

UX

No issues found

Craft

No issues found

Working well

  • Reduced-motion is handled thoroughly: the interval-based fallback swaps `animate()` for `setInterval` and drops the y-translate on enter/exit, so vestibular-sensitive users get the same functional rotation without the motion.
  • The gradient stops are defined once per shape via linearGradient defs and reused as fill="url(#...)" references, which is the correct SVG pattern for keeping a consistent brand gradient across multiple paths.
  • This is a self-contained decorative SVG icon component; the hardcoded gradient hex values are appropriate here since they define the badge's own brand artwork, not a UI token that should theme.
  • The hue slider's trackBackground gradient is derived live from the current lightness and chroma via useMemo, so the swatch always reflects the actual selected color instead of a static rainbow.

Scored July 29, 2026 with Rams Engine v0.0.3 · Engine changelog

This page is an automated design review of nextui-org/nextui’s UI code: 30 files read against 258 versioned rules covering accessibility, color, typography, spacing, components, UX, motion, and craft. The score is out of 100; any confirmed critical issue caps it at 59.

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