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

nocodb/nocodb

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 & Moderate · top 4 shown below

Top fix

Fix runtime-built Tailwind class so the color swatch renders

See the fix

Verdict

A copy-pasted debug template spread its flaws across nearly every file: unlabeled inputs, unguarded fade-ins, repeated ad-hoc sizing. The real fire is a runtime-built Tailwind class that silently kills the color swatch, the harness's only visible output.

Files Rams reviewed

packages/nc-gui/pages/playground/components/MockInjection.vue

packages/nc-gui/pages/playground/components/filter/filter-group/index.vue

packages/nc-gui/pages/playground/components/filter/filter-row/index.vue

packages/nc-gui/pages/playground/components/row-color-picker-toolbar/filter-type-option/index.vue

packages/nc-gui/pages/playground/components/row-color-picker-toolbar/using-filter-panel/index.vue

packages/nc-gui/components/dashboard/TreeView/Views/List.vue

packages/nc-gui/pages/playground/components/advance-color-picker/index.vue

packages/nc-gui/pages/playground/components/base-icon-color-picker/index.vue

packages/nc-gui/pages/playground/components/filter/index.vue

packages/nc-gui/pages/playground/components/index.vue

packages/nc-gui/pages/playground/components/row-color-picker-toolbar/index.vue

packages/nc-gui/pages/playground/components/row-color-picker-toolbar/using-single-select-panel/index.vue

packages/nc-gui/components/dashboard/TreeView/Views/index.vue

packages/nocodb/src/services/mail/templates/components/ContentWrapper.tsx

packages/nocodb/src/services/mail/templates/components/Footer.tsx

packages/nocodb/src/services/mail/templates/components/RootWrapper.tsx

packages/nc-gui/components/dashboard/TreeView/Views/Node.vue

packages/nc-gui/pages/account/index.vue

packages/nc-gui/pages/admin/index.vue

packages/nc-gui/pages/forgot-password.vue

packages/nc-gui/pages/index.vue

packages/nc-gui/pages/index/[typeOrId]/[baseId]/index.vue

packages/nc-gui/pages/index/[typeOrId]/[baseId]/index/index.vue

packages/nc-gui/pages/index/[typeOrId]/[baseId]/index/index/index.vue

packages/nc-gui/pages/index/[typeOrId]/form/[viewId].vue

packages/nc-gui/pages/index/[typeOrId]/form/[viewId]/index.vue

packages/nc-gui/pages/index/[typeOrId]/form/[viewId]/index/index.vue

packages/nc-gui/pages/index/[typeOrId]/shared/[erdUuid]/index.vue

packages/nc-gui/pages/index/[typeOrId]/view/[viewId].vue

packages/nc-gui/pages/playground/icons/index.vue

97/100

Components

1 critical
ComponentsCritical

packages/nc-gui/pages/playground/components/advance-color-picker/index.vue:11

Color swatch never renders because the Tailwind class is built at runtime

The swatch div at line 11 in advance-color-picker/index.vue binds `:class="[`bg-${color1}`]"` to construct a background class from the `color1` ref. Tailwind's JIT scanner only generates classes it can find as complete strings in source, so `bg-${color1}` is invisible to the build and no CSS rule is ever emitted for it, no matter what color1 resolves to.

Why it matters

The swatch stays blank regardless of what color is picked, so this demo component fails at its one job: showing the selected color.

Fix

Bind the resolved color as an inline style instead of interpolating it into a Tailwind class name.

<div class="inline-block min-h-[24px] min-w-[24px] h-[24px] w-[24px] rounded-md" :class="[`bg-${color1}`]"></div>
<div class="inline-block min-h-[24px] min-w-[24px] h-[24px] w-[24px] rounded-md" :style="{ backgroundColor: color1 }"></div>
98/100

Accessibility

1 serious
AccessibilitySerious

packages/nc-gui/pages/playground/components/filter/filter-group/index.vue:137

Debug number inputs for Index, NestedLevel, and filter limits have no accessible label

The inputs bound to `options1.index`, `options1.nestedLevel`, `options1.filterPerViewLimit`, and `options1.filtersCount` (lines 137-153) are each preceded only by plain text like "Index:" with no `<label for>` or `aria-label` tying it to the control.

Why it matters

A screen reader announces these as unlabeled number fields, so a user relying on assistive tech can't tell what each input controls without visually scanning the surrounding text.

Fix

Associate each input with its text using aria-label or a proper label element.

Index: <input v-model="options1.index" type="number" class="text-xs p-1 border-nc-border-gray-medium" /><br />
<label for="filter-group-index">Index:</label>
<input id="filter-group-index" v-model="options1.index" type="number" class="text-xs p-1 border-nc-border-gray-medium" aria-label="Index" /><br />

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

Spacing

1 serious
Design SystemSerious

packages/nc-gui/pages/playground/components/filter/filter-group/index.vue:159

Debug JSON panels repeat the same arbitrary sizing values instead of a shared class

Both event-log panels at lines 159 and 162 use the identical `min-w-[300px] max-h-[200px]` combination with `bg-nc-bg-gray-dark overflow-y-scroll`.

Why it matters

Repeating the same arbitrary pixel values in two places instead of naming them once means any future adjustment to this panel size has to be made twice, and the pattern will keep spreading as more debug panels get copy-pasted.

Fix

Extract the repeated arbitrary size and treatment into one shared utility class or component.

<div class="min-w-[300px] max-h-[200px] overflow-wrap bg-nc-bg-gray-dark overflow-y-scroll">
  <pre>{{ JSON.stringify(lastRowChangeEvent1, null, 2) }}</pre>
</div>
<div class="debug-panel">
  <pre>{{ JSON.stringify(lastRowChangeEvent1, null, 2) }}</pre>
</div>
<!-- .debug-panel { @apply min-w-[300px] max-h-[200px] overflow-wrap bg-nc-bg-gray-dark overflow-y-scroll; } -->
98/100

Motion

1 serious
MotionSerious

packages/nc-gui/pages/playground/components/row-color-picker-toolbar/filter-type-option/index.vue:18

Fade-in animation on both filter and select panels ignores reduced-motion preference

Both the filter panel (line 18) and the select panel (line 24) apply `animate-animated animate-fadeIn` with `animation-duration: 0.3s` and neither is wrapped in a `prefers-reduced-motion` check.

Why it matters

Users who set the OS-level reduced-motion preference to avoid vestibular symptoms still get the fade playing on both panels every time they open this control, since the animation is unconditional.

Fix

Gate decorative animation classes behind a prefers-reduced-motion media query so motion-sensitive users get an instant transition instead.

<div class="bg-nc-bg-default animate-animated animate-fadeIn" style="animation-duration: 0.3s">
<div class="bg-nc-bg-default motion-safe:animate-animated motion-safe:animate-fadeIn" style="animation-duration: 0.3s">

Typography

No issues found

Color

No issues found

UX

No issues found

Craft

No issues found

Working well

  • Every debug output panel (filter1, lastChangeEvent1, deleted1LastEvent) shares the same bg-nc-bg-gray-dark and overflow treatment, so the state panels read as one consistent system rather than one-off styling per panel.
  • Grouping the toggle switches ("disabled", "isLockedView", "disableAddNewFilter") and selects into a compact two-column layout keeps this debug harness scannable even with a large number of options exposed at once.
  • Using v-model:row-coloring-mode on SmartsheetToolbarRowColorFilterTypeOption keeps the toolbar's state binding idiomatic Vue rather than manually wiring props and emits.

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

This page is an automated design review of nocodb/nocodb’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