vuetifyjs/vuetify
Reviewed against Rams quality heuristics: accessibility, color, typography, spacing, components, motion, UX, and craft.
30 files reviewed·July 29, 2026
More findings
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
Accessibility
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"
/>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>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"Color
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 !importantcode,
pre
color: var(--prism-foreground, #ccc) !importantTypography
Spacing
Components
Motion
UX
Craft
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.