nextui-org/nextui
Reviewed against Rams quality heuristics: accessibility, color, typography, spacing, components, motion, UX, and craft.
30 files reviewed·July 29, 2026
More findings
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
Motion
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"}}
>Color
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
Typography
Spacing
Components
UX
Craft
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.