Rams MCP · The full engine, now in your coding agent
excalidraw on GitHub

excalidraw/excalidraw

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.

6issues
Critical & Serious

Top fix

Make the Stats panel close control keyboard reachable

See the fix

Verdict

Good bones, sloppy finish: a token-based spacing system undercut by inline margins, and structurally clean components still carrying Docusaurus boilerplate copy. The real risk is the unreachable Stats panel close control, a one-line fix that's currently a hard keyboard lockout.

Files Rams reviewed

dev-docs/src/pages/index.tsx

examples/with-nextjs/src/app/layout.tsx

examples/with-nextjs/src/app/page.tsx

dev-docs/src/components/Homepage/index.tsx

dev-docs/src/pages/index.module.css

examples/with-nextjs/src/pages/excalidraw-in-pages.tsx

packages/excalidraw/components/Stats/index.tsx

dev-docs/src/components/Homepage/styles.module.css

examples/with-script-in-browser/components/CustomFooter.tsx

examples/with-script-in-browser/components/ExampleApp.scss

excalidraw-app/components/AI.tsx

excalidraw-app/components/AppMainMenu.tsx

excalidraw-app/components/AppSidebar.scss

excalidraw-app/components/AppSidebar.tsx

excalidraw-app/components/AppWelcomeScreen.tsx

excalidraw-app/components/DebugCanvas.tsx

excalidraw-app/components/ExportToExcalidrawPlus.tsx

excalidraw-app/components/TopErrorBoundary.tsx

packages/excalidraw/components/Actions.scss

packages/excalidraw/components/ActiveConfirmDialog.tsx

packages/excalidraw/components/BraveMeasureTextError.tsx

packages/excalidraw/components/Button.tsx

packages/excalidraw/components/Card.scss

packages/excalidraw/components/CheckboxItem.scss

packages/excalidraw/components/ColorPicker/ColorInput.tsx

packages/excalidraw/components/ColorPicker/ColorPicker.scss

packages/excalidraw/components/ColorPicker/ColorPicker.tsx

packages/excalidraw/components/ColorPicker/CustomColorList.tsx

packages/excalidraw/components/ColorPicker/Picker.tsx

packages/excalidraw/components/ColorPicker/PickerColorList.tsx

93/100

Accessibility

1 critical2 serious
AccessibilityCritical

packages/excalidraw/components/Stats/index.tsx:220

Stats panel close control is unreachable by keyboard

The close control in the Stats panel is a plain <div className="close" onClick={onClose}>{CloseIcon}</div> with no tabIndex, role, keydown handler, or aria-label. It's an icon-only div, not a <button>.

Why it matters

Keyboard-only and screen reader users cannot tab to this control or activate it at all, so they have no way to close the stats panel once it's open.

Fix

Use a native <button> for any clickable control so it gets keyboard focus, Enter/Space activation, and an accessible name for free.

<div className="close" onClick={onClose}>
  {CloseIcon}
</div>
<button type="button" className="close" onClick={onClose} aria-label={t("buttons.close")}>
  {CloseIcon}
</button>
AccessibilitySerious

examples/with-script-in-browser/components/CustomFooter.tsx:66

Comment tool button relies on a tooltip for its only accessible name

The second footer Button renders only {COMMENT_SVG} as its child and depends on title="Comments!" for identification, with no aria-label or visible text.

Why it matters

The title attribute is inconsistently exposed across screen readers and never appears for keyboard users until a mouse hovers, so this control can go unannounced or unexplained for assistive tech users trying to find the comment tool.

Fix

Pair icon-only controls with an explicit aria-label rather than relying on title alone for the accessible name.

  title="Comments!"
>
  {COMMENT_SVG}
</Button>
  title="Comments!"
  aria-label="Comments!"
>
  {COMMENT_SVG}
</Button>
AccessibilitySerious

dev-docs/src/components/Homepage/index.tsx:47

Feature icons announce as unlabeled images to screen readers

Each Feature icon in the homepage grid renders as <Svg className={styles.featureSvg} role="img" /> with no aria-label or <title>. Three icons (mountain, tree, react) sit above the headings 'Easy to Use', 'Focus on What Matters', and 'Powered by React' but carry no accessible name of their own.

Why it matters

A screen reader hits role="img" and announces 'image' with no description, giving blind users a dead stop before reaching the heading that actually explains the feature.

Fix

Give every element carrying role="img" an aria-label that matches its adjacent heading.

<Svg className={styles.featureSvg} role="img" />
<Svg className={styles.featureSvg} role="img" aria-label={title} />
96/100

Spacing

2 serious
SpacingSerious

packages/excalidraw/components/Stats/index.tsx:285

Inline marginTop: 12 on elementStats bypasses the spacing scale

The #elementStats wrapper sets style={{ marginTop: 12 }} directly inline instead of a class, in a file where Card.scss and Actions.scss expose custom-property spacing tokens.

Why it matters

A hardcoded pixel value here means this one panel's spacing drifts independently the next time the token scale changes, and nobody editing the shared tokens will catch it.

Fix

Move one-off inline spacing values into a class or custom property so they track the shared spacing scale.

<div
  id="elementStats"
  style={{
    marginTop: 12,
  }}
>
<div
  id="elementStats"
  className="ElementStats"
>
SpacingSerious

packages/excalidraw/components/Stats/index.tsx:331

Arbitrary rem margin on stats-element-type row skips the spacing scale

The StatsRow with data-testid="stats-element-type" sets style={{ margin: "0.3125rem 0" }} inline, an odd non-scale value (5px) that isn't reused as a named token anywhere else in the file.

Why it matters

A one-off decimal rem value like this is easy to lose track of and hard to reconcile with the rest of the panel's spacing, so future edits to this row's spacing won't propagate anywhere else it's needed.

Fix

Replace arbitrary inline margin values with a shared spacing class or token from the existing scale.

<StatsRow
  heading
  data-testid="stats-element-type"
  style={{ margin: "0.3125rem 0" }}
>
<StatsRow
  heading
  data-testid="stats-element-type"
  className="StatsRow--tight"
>

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

Craft

1 serious
CraftSerious

dev-docs/src/components/Homepage/index.tsx:13

All three feature headings are unedited Docusaurus boilerplate copy

FeatureList titles read "Easy to Use", "Focus on What Matters", and "Powered by React", paired with descriptions still referencing "Docusaurus" directly (e.g. 'Docusaurus was designed from the ground up...').

Why it matters

Generic scaffold copy on a homepage tells visitors nothing about what this specific project does, so the section fails to communicate any real value before they scroll past it.

Fix

Replace template copy with headings and descriptions specific to this project's actual features.

title: "Easy to Use",
title: "<project-specific feature title>",

Typography

No issues found

Color

No issues found

Components

No issues found

Motion

No issues found

UX

No issues found

Working well

  • The Stats panel's Collapsible sections use one consistent bitmask pattern (STATS_PANELS.generalStats, elementProperties) for both open/close states, so the expand logic reads the same way across panels instead of each one inventing its own toggle convention.
  • The Feature component in dev-docs/src/components/Homepage/index.tsx maps over a typed FeatureList array instead of hand-copying three near-identical JSX blocks, which keeps future feature additions to a single data entry rather than a duplicated component.

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

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