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

vuetifyjs/vuetify

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.

4issues
Critical & Serious

Top fix

Make copy-source button reachable and visible via keyboard

See the fix

Verdict

Hover is the design language here, and it quietly locks out anyone not using a mouse. The component and token architecture is solid, the biggest risk is that its accessible paths were never finished, just started.

Files Rams reviewed

packages/docs/src/components/app/Heading.vue

packages/docs/src/components/app/Link.vue

packages/docs/src/components/app/Markdown.vue

packages/docs/src/components/app/Markup.vue

packages/docs/src/components/app/Toc.vue

packages/docs/src/components/app/TooltipBtn.vue

packages/docs/src/components/app/bar/Bar.vue

packages/docs/src/components/app/bar/EcosystemMenu.vue

packages/docs/src/components/app/bar/LearnMenu.vue

packages/docs/src/components/app/bar/NotificationsMenu.vue

packages/docs/src/components/app/bar/SupportMenu.vue

packages/docs/src/components/app/drawer/Drawer.vue

packages/docs/src/components/app/drawer/PinnedItems.vue

packages/docs/src/components/app/list/List.vue

packages/docs/src/components/app/search/Search.vue

packages/docs/src/components/app/search/SearchDialog.vue

packages/docs/src/components/app/search/SearchGroup.vue

packages/docs/src/components/app/search/SearchResults.vue

packages/docs/src/components/app/settings/options/OfflineOption.vue

packages/docs/src/components/app/settings/options/ThemeOption.vue

packages/docs/src/components/app/BackToTop.vue

packages/docs/src/components/app/Btn.vue

packages/docs/src/components/app/Caption.vue

packages/docs/src/components/app/CommitBtn.vue

packages/docs/src/components/app/Divider.vue

packages/docs/src/components/app/Figure.vue

packages/docs/src/components/app/Headline.vue

packages/docs/src/components/app/Sheet.vue

packages/docs/src/components/app/Table.vue

packages/docs/src/components/app/TextField.vue

93/100

Accessibility

1 critical2 serious
AccessibilityCritical

packages/docs/src/components/app/Markup.vue:34

Copy-source button has no keyboard path since it only renders on hover

The copy button inside the `v-tooltip` activator carries `v-if="isHovering"`, so it doesn't exist in the DOM until a mouse hover sets `isHovering` true. There's no `@focus`/`@focusin` handler on the code block to flip that same flag, so keyboard-only users tabbing through the page never cause the button to mount and can never reach it.

Why it matters

Keyboard-only users cannot copy the code sample at all; the action is fully invisible and unreachable for that entire user class, not just harder to find.

Fix

Never gate an interactive control's existence on a mouse-only event; toggle visibility with a class controlled by both hover and focus-within so the control stays in the DOM and reachable by keyboard.

<v-btn
              v-if="isHovering"
              :key="icon"
              :icon="icon"
              class="text-disabled me-3 mt-2 app-markup-btn position-absolute right-0 top-0"
              density="comfortable"
              size="small"
              v-bind="activatorProps"
              variant="text"
              @click="copy"
            />
<v-btn
              :key="icon"
              :icon="icon"
              class="text-disabled me-3 mt-2 app-markup-btn position-absolute right-0 top-0 app-markup-btn--reveal"
              density="comfortable"
              size="small"
              v-bind="activatorProps"
              variant="text"
              @click="copy"
            />
AccessibilitySerious

packages/docs/src/components/app/Heading.vue:6

Permalink icon stays keyboard-focusable but invisible to screen readers

The '#' permalink anchor in Heading.vue is a real router-link (native <a>), so it stays in the native tab order regardless of aria-hidden. But aria-hidden="true" is applied unconditionally, so a screen reader landing on it during tab navigation gets no role, no name, and no announcement at all. The paired CSS (`&:not(:hover):not(:focus) opacity: 0`) does make the link visually reappear for sighted keyboard users on focus, so the mismatch is specifically between what sighted keyboard users see (a visible '#' link) and what screen reader users hear (nothing).

Why it matters

Screen reader users tabbing through a heading-heavy docs page hit a silent, unlabeled stop at every heading permalink, breaking the predictability of keyboard navigation and making it unclear whether focus moved at all.

Fix

Never apply aria-hidden to a natively focusable element; give it an accessible name instead and hide only the decorative glyph.

<router-link
      v-if="href"
      :to="href"
      aria-hidden="true"
      class="text-decoration-none text-end text-md-start d-none d-sm-inline-block"
      style="user-select: none"
    >
      <span class="text-primary">#</span>
    </router-link>
<router-link
      v-if="href"
      :to="href"
      :aria-label="t('permalink')"
      class="text-decoration-none text-end text-md-start d-none d-sm-inline-block"
      style="user-select: none"
    >
      <span aria-hidden="true" class="text-primary">#</span>
    </router-link>
AccessibilitySerious

packages/docs/src/components/app/Markup.vue:36

Icon-only copy button has no accessible name outside the tooltip

The `v-btn` bound to `:icon="icon"` at line 36 has no `aria-label` of its own; its only text, "copy-source" from `t('copy-source')`, lives inside the `v-tooltip` default slot at line 47. Tooltips are commonly hover/focus-triggered popups rendered separately in the DOM and are not guaranteed to be exposed as the button's accessible name by every screen reader and Vuetify version.

Why it matters

A screen reader user who does reach the button hears only "button" with no indication it copies the source, forcing them to guess its function or skip it entirely.

Fix

Give icon-only buttons an explicit aria-label that duplicates the tooltip text rather than relying on the tooltip alone for the accessible name.

:icon="icon"
              class="text-disabled me-3 mt-2 app-markup-btn position-absolute right-0 top-0"
              density="comfortable"
              size="small"
              v-bind="activatorProps"
              variant="text"
              @click="copy"
:icon="icon"
              :aria-label="t('copy-source')"
              class="text-disabled me-3 mt-2 app-markup-btn position-absolute right-0 top-0"
              density="comfortable"
              size="small"
              v-bind="activatorProps"
              variant="text"
              @click="copy"
98/100

Color

1 serious
Design SystemSerious

packages/docs/src/components/app/Markup.vue:267

Dark-theme syntax colors hardcode hex values beside an existing prism token system

The dark/blackguard theme block already defines and consumes CSS custom properties (`--prism-interpolation: var(--prism-operator)`), proving a token layer exists for this syntax palette. Yet the same block sets `code, pre { color: #ccc !important }` and the token rules further down (`.token.tag`, `.token.function-name`, etc., not shown past line 260) use raw hex like `#e2777a`, `#6196cc`, `#f08d49` instead of `--prism-*` variables.

Why it matters

Updating the dark theme's syntax palette now means hunting for hex literals scattered through this file instead of changing one variable, and any theme other than the two hardcoded here can't reuse this palette at all.

Fix

Route every syntax-highlighting color through the existing --prism-* custom property layer instead of a literal hex value.

code,
      pre
        color: #ccc !important
code,
      pre
        color: var(--prism-foreground, #ccc) !important

Typography

No issues found

Spacing

No issues found

Components

No issues found

Motion

No issues found

UX

No issues found

Craft

No issues found

Working well

  • The gh_links markdown rule explicitly skips link_close context before autolinking commit hashes and issue numbers, correctly avoiding the classic bug where naive regex linking double-wraps text that's already inside a markdown link.
  • Heading levels map from a single HEADING_CLASSES object keyed by level (h1 through h5), so the type scale for every heading size lives in one place and stays auditable instead of being repeated per usage.
  • The hover-revealed copy and open-in-playground buttons use v-fade-transition, giving the reveal a soft entrance instead of an abrupt pop, which reads as deliberate polish rather than a leftover default.

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

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