Rams MCP · The full engine, now in your coding agent
ant-design on GitHub

ant-design/ant-design

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

30 files reviewed·August 1, 2026

View on GitHub

Elevated

Design risk in this codebase.

7issues
Critical, Serious & Moderate · top 6 shown below

Top fix

Add accessible name and keyboard focus to copy-theme icon button

See the fix

Verdict

Token discipline is real but selectively applied, and the gaps land exactly where users interact most: icon buttons, error states, hover motion. The Alert bug is a five-minute fix that's currently shipping a broken error message to production.

Files Rams reviewed

.dumi/pages/index/components/PreviewBanner/index.tsx

.dumi/pages/index/components/ThemePreview/index.tsx

.dumi/pages/index/components/BannerRecommends.tsx

.dumi/pages/index/components/BannerSponsors.tsx

.dumi/pages/index/components/ComponentsList.tsx

.dumi/pages/index/components/DesignFramework.tsx

.dumi/pages/index/components/Group.tsx

.dumi/pages/index/components/PreviewBanner/LuminousBg.tsx

.dumi/pages/index/components/PreviewPane/Simple.tsx

components/app/App.tsx

components/app/index.tsx

.dumi/pages/index/components/GroupMaskLayer.tsx

.dumi/pages/index/components/Theme/index.tsx

.dumi/pages/index/components/ThemePreview/svg-component/serene-icon.tsx

components/app/demo/basic.tsx

components/app/demo/config.tsx

components/form/demo/layout.tsx

components/layout/layout.tsx

.dumi/pages/404/index.tsx

.dumi/pages/index/components/PreviewPane/Components.tsx

.dumi/pages/index/index.tsx

.dumi/pages/theme-editor/index.tsx

components/affix/index.tsx

components/back-top/index.tsx

components/carousel/index.tsx

components/checkbox/demo/layout.tsx

components/color-picker/components/PanelPicker/index.tsx

components/date-picker/generatePicker/index.tsx

components/date-picker/index.tsx

components/descriptions/index.tsx

94/100

Motion

3 serious
MotionSerious

.dumi/pages/index/components/PreviewBanner/index.tsx:37

Hero decorative block animates all properties on a sluggish 1s hover

The `.block` element behind the hero slogan transitions with `transition: all 1s cubic-bezier(...)` on line 37, triggered by a scale(0.96) on holder hover. `all` forces the browser to watch every property for changes, and 1s is well past the ~400ms ceiling typical for interface hover feedback, making the response feel delayed relative to the cursor.

Why it matters

A hover response that lags a full second reads as unresponsive rather than intentional, and animating every property (instead of just transform) risks unintended transitions if any other style on `.block` changes.

Fix

Scope the transition to the specific property being animated and shorten the duration to match interactive hover timing.

const block = cx(css`
  position: absolute;
  inset-inline-end: -60px;
  top: -24px;
  transition: all 1s cubic-bezier(0.03, 0.98, 0.52, 0.99);
`);
const block = cx(css`
  position: absolute;
  inset-inline-end: -60px;
  top: -24px;
  transition: transform 0.4s cubic-bezier(0.03, 0.98, 0.52, 0.99);
`);
MotionSerious

.dumi/pages/index/components/BannerRecommends.tsx:116

Cursor-glow effect writes to React state on every pointermove

The recommend card's `onMouseMove` handler on line 116 calls `setMousePosition([x, y])` on every mouse move event, then a separate `useEffect` and rAF loop re-renders again in `transMousePosition`. This drives full component re-renders at cursor speed just to update a hover glow position.

Why it matters

Re-rendering React state on every pointermove adds unnecessary render cost on every card in the carousel simultaneously, and on lower-end devices this shows up as jank in the exact moment the glow is supposed to feel smooth.

Fix

Write pointer position directly to a ref-driven CSS custom property instead of React state so the glow updates without triggering re-renders.

const onMouseMove: React.MouseEventHandler<HTMLAnchorElement> = (e) => {
  if (!cardRef.current) {
    return;
  }

  const rect = cardRef.current.getBoundingClientRect();
  const x = e.clientX - rect.left;
  const y = e.clientY - rect.top;

  setMousePosition([x, y]);
};
const onMouseMove: React.MouseEventHandler<HTMLAnchorElement> = (e) => {
  if (!cardRef.current) {
    return;
  }

  const rect = cardRef.current.getBoundingClientRect();
  const x = e.clientX - rect.left;
  const y = e.clientY - rect.top;

  cardRef.current.style.setProperty('--mouse-x', `${x}px`);
  cardRef.current.style.setProperty('--mouse-y', `${y}px`);
};
MotionSerious

.dumi/pages/index/components/BannerRecommends.tsx:26

Recommend card transitions every property instead of the ones that change

The `itemBase` card style on line 26 sets `transition: all ${cssVar.motionDurationSlow};` for a card that only visibly changes a border-hover glow (the `:before` radial-gradient block right below it). `all` means the browser recalculates transitions for background, backdrop-filter, and every other property on this card even when they aren't animating.

Why it matters

Watching every CSS property for changes on a frequently-rendered carousel card adds unnecessary paint and composite work, and it also means an unrelated future style change starts animating unintentionally.

Fix

Scope `transition` to the specific properties being animated instead of `all`.

transition: all ${cssVar.motionDurationSlow};
transition: border-color ${cssVar.motionDurationSlow}, box-shadow ${cssVar.motionDurationSlow};
97/100

Accessibility

1 critical
AccessibilityCritical

.dumi/pages/index/components/ThemePreview/index.tsx:289

Copy-theme icon button is invisible to keyboard and screen reader users

The copy-theme control at line 289 is a plain <div className={styles.buttonBlock} onClick={handleCopyTheme}> wrapping a <CopyOutlined /> icon, with only a Tooltip for a visible label. It has no role, no tabIndex, no keydown handler, and no aria-label, so it never enters the tab order and exposes no accessible name. The same buttonBlock pattern repeats for the AI-generate trigger elsewhere in this Flex, so the same lockout applies to both icon-only actions in this toolbar. The adjacent theme-swatch div in the same file does this correctly with role="tab", tabIndex, aria-selected and onKeyDown, proving the fix pattern already exists a few lines away.

Why it matters

A keyboard-only or screen-reader user cannot tab to or trigger the copy-theme action at all, and even a mouse user relying on assistive tech hears nothing when focus lands here, so the feature is functionally unusable for that group.

Fix

Use a real <button> with an aria-label for icon-only actions so they get native keyboard and screen-reader support for free.

<div className={styles.buttonBlock} onClick={handleCopyTheme}>
  <CopyOutlined />
</div>
<button
  type="button"
  className={styles.buttonBlock}
  onClick={handleCopyTheme}
  aria-label={locale.copyTheme}
>
  <CopyOutlined />
</button>

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

.dumi/pages/index/components/ComponentsList.tsx:86

Card's decorative circle uses a raw brand hex instead of the theme token

`cardCircle` on line 86 sets `background: #1677ff;` directly, while the sibling `card` style two blocks above resolves both border and background through `cssVar` (`cssVar.colorBorder`, `cssVar.colorBgContainer`) and switches by `isDark`. The circle's color is fixed regardless of theme.

Why it matters

A hardcoded brand blue doesn't adapt if the primary color token changes or if dark-mode contrast needs adjusting, so this decorative element silently drifts out of sync with the rest of the themed card the next time the palette is updated.

Fix

Replace the raw hex with the same cssVar primary-color token already used elsewhere in the file.

cardCircle: css`
  position: absolute;
  width: 120px;
  height: 120px;
  background: #1677ff;
  border-radius: 50%;
  filter: blur(40px);
  opacity: 0.1;
`,
cardCircle: css`
  position: absolute;
  width: 120px;
  height: 120px;
  background: ${cssVar.colorPrimary};
  border-radius: 50%;
  filter: blur(40px);
  opacity: 0.1;
`,
98/100

UX

1 serious
UXSerious

.dumi/pages/index/components/BannerRecommends.tsx:238

Error state Alert passes 'title' instead of 'message', so error text never renders

In the error branch (line 238-239), `<Alert showIcon type="error" title={error.message} ...>` uses a `title` prop, but antd's Alert component reads its body text from `message`, not `title`. When `error` is truthy, users see an error Alert with an icon but no visible text explaining what failed or how to recover.

Why it matters

A user who hits this error state sees an empty red banner with no explanation, so they can't tell what broke or what to do next, and there is no recovery path shown at all.

Fix

Use antd Alert's actual `message` prop to render the error text.

<Alert
  showIcon
  type="error"
  title={error.message}
<Alert
  showIcon
  type="error"
  message={error.message}

Typography

No issues found

Spacing

No issues found

Components

No issues found

Craft

No issues found

Working well

  • Wrapping LuminousBg in Suspense with fallback={null} means the hero slogan and 'Getting Started' CTA stay interactive immediately even if the background effect is still loading, which keeps the primary action from being blocked by a decorative asset.
  • The card style in ComponentsList branches border and background through cssVar tokens keyed on isDark rather than a blanket filter or invert, showing the file has an intentional theming system even where the decorative circle strays from it.
  • The theme swatch div in ThemePreview correctly implements role="tab", tabIndex, aria-selected, and an onKeyDown handler, giving it real keyboard semantics that the copy-theme and AI-generate buttons right next to it should be matching.

Scored August 1, 2026 with Rams Engine v0.0.3 · Engine changelog
First scored July 29, 2026: 83/100. This rescore on v0.0.3: 59/100.

This page is an automated design review of ant-design/ant-design’s UI code: 30 files read against 291 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