hoppscotch/hoppscotch
Reviewed against Rams quality heuristics: accessibility, color, typography, spacing, components, motion, UX, and craft.
30 files reviewed·August 1, 2026
Elevated
Accessibility needs attention.
More findings
Verdict
Power-user density without the accessibility scaffolding to support it, icon triggers, hidden divs, and custom menus all skip basic semantics. The biggest risk is a broken keyboard shortcut that throws on click, right where speed matters most.
Files Rams reviewed
packages/hoppscotch-common/src/components/app/ActionHandler.vue
packages/hoppscotch-common/src/components/app/Banner.vue
packages/hoppscotch-common/src/components/app/ContextMenu.vue
packages/hoppscotch-common/src/components/app/DeveloperOptions.vue
packages/hoppscotch-common/src/components/app/Footer.vue
packages/hoppscotch-common/src/components/app/Inspection.vue
packages/hoppscotch-common/src/components/app/KernelInterceptor.vue
packages/hoppscotch-common/src/components/app/Logo.vue
packages/hoppscotch-common/src/components/app/Options.vue
packages/hoppscotch-common/src/components/app/PaneLayout.vue
packages/hoppscotch-common/src/components/app/Share.vue
packages/hoppscotch-common/src/components/app/Shortcuts.vue
packages/hoppscotch-common/src/components/app/ShortcutsPrompt.vue
packages/hoppscotch-common/src/components/app/Sidenav.vue
packages/hoppscotch-common/src/components/app/Support.vue
packages/hoppscotch-common/src/components/app/WhatsNewDialog.vue
packages/hoppscotch-common/src/components/app/spotlight/Entry.vue
packages/hoppscotch-common/src/components/app/spotlight/entry/GQLRequest.vue
packages/hoppscotch-common/src/components/app/spotlight/entry/RESTHistory.vue
packages/hoppscotch-common/src/components/app/spotlight/entry/RESTRequest.vue
packages/hoppscotch-common/src/components/app/spotlight/index.vue
packages/hoppscotch-sh-admin/src/components/app/Header.vue
packages/hoppscotch-sh-admin/src/components/app/Login.vue
packages/hoppscotch-sh-admin/src/components/app/Logout.vue
packages/hoppscotch-sh-admin/src/components/app/Modal.vue
packages/hoppscotch-sh-admin/src/components/app/Sidebar.vue
packages/hoppscotch-common/src/components/app/GitHubStarButton.vue
packages/hoppscotch-common/src/components/app/Markdown.vue
packages/hoppscotch-common/src/components/app/ShortcutsEntry.vue
packages/hoppscotch-common/src/components/app/SpotlightSearch.vue
Accessibility
packages/hoppscotch-common/src/components/app/Footer.vue:131
Developer options only reachable via double-click on a plain div
The version label in Footer.vue is a plain `<div class="flex px-4 py-2 opacity-50" @dblclick="...showDeveloperOptionModal()...">`, with no `role`, `tabindex`, or keyboard handler.
Why it matters
A keyboard-only user can never trigger developer options since the div has no focus stop and no key event, locking that entry point out entirely for non-mouse users.
Fix
Convert the trigger to a real `<button>` (or add `tabindex="0"`, `role="button"`, and a keydown handler) so it's reachable and operable by keyboard.
<div
class="flex px-4 py-2 opacity-50"
@dblclick="
() => {
showDeveloperOptionModal()
hide()
}
"
>
{{ `${t("app.name")} v${version}` }}
</div><button
type="button"
class="flex px-4 py-2 opacity-50"
@dblclick="
() => {
showDeveloperOptionModal()
hide()
}
"
>
{{ `${t("app.name")} v${version}` }}
</button>packages/hoppscotch-common/src/components/app/Banner.vue:18
Banner dismiss icon has no keyboard path or accessible name
The dismiss control in Banner.vue is an `icon-lucide-x` with a raw `@click="emit('dismiss')"`, not a `<button>`. It carries no `role`, no `tabindex`, and no `aria-label`, so keyboard users can't tab to it and screen reader users hear nothing when it's announced.
Why it matters
A user who navigates by keyboard or screen reader cannot dismiss this banner at all, so it stays on screen and blocks the layout below it indefinitely for that user.
Fix
Wrap the dismiss icon in a real `<button>` with an `aria-label` so it gets native keyboard focus and a screen-reader name for free.
<icon-lucide-x
v-if="banner.dismissible"
class="opacity-50 hover:cursor-pointer hover:opacity-100"
@click="emit('dismiss')"
/><button
v-if="banner.dismissible"
type="button"
:aria-label="t('action.dismiss')"
class="opacity-50 hover:cursor-pointer hover:opacity-100"
@click="emit('dismiss')"
>
<icon-lucide-x />
</button>packages/hoppscotch-common/src/components/app/Inspection.vue:6
Icon-only inspection trigger has no accessible name
The `HoppButtonSecondary` in Inspection.vue that opens the inspections popover renders `:icon="IconAlertTriangle"` with only a `v-tippy` hover tooltip and a `:title` attribute, no visible label and no `aria-label`.
Why it matters
A screen reader user tabbing to this button hears nothing describing what it does, so they can't discover or open the inspections panel at all.
Fix
Add an explicit `aria-label` bound to the same translation string used for the tooltip so the button has a real accessible name independent of hover state.
<HoppButtonSecondary
v-tippy="{ theme: 'tooltip' }"
:icon="IconAlertTriangle"
:class="severityColor(getHighestSeverity.severity)"
:title="t('inspections.description')"
/><HoppButtonSecondary
v-tippy="{ theme: 'tooltip' }"
:icon="IconAlertTriangle"
:class="severityColor(getHighestSeverity.severity)"
:title="t('inspections.description')"
:aria-label="t('inspections.description')"
/>packages/hoppscotch-common/src/components/app/ContextMenu.vue:2
Context menu has no menu role, no Escape key, no focus entry
The root `<div ref="contextMenuRef">` in ContextMenu.vue renders `HoppSmartItem` options with click handlers, but the container has no `role="menu"`, no keydown handler for Escape, and nothing moves focus into it when it opens.
Why it matters
A keyboard-only user who opens this context menu has no way to close it or move between its options with the keyboard, and a screen reader gives no indication a menu opened at all.
Fix
Give the container `role="menu"`, move focus to the first item on open, and add an Escape handler that closes it and returns focus to the trigger.
<div
ref="contextMenuRef"
class="fixed transform -translate-x-10 -translate-y-8 rounded border border-dividerDark bg-popover p-2 shadow-lg"
:style="`top: ${position.top}px; left: ${position.left}px; z-index: 100;`"
><div
ref="contextMenuRef"
role="menu"
tabindex="-1"
class="fixed transform -translate-x-10 -translate-y-8 rounded border border-dividerDark bg-popover p-2 shadow-lg"
:style="`top: ${position.top}px; left: ${position.left}px; z-index: 100;`"
@keydown.esc="closeMenu"
>UX
packages/hoppscotch-common/src/components/app/Footer.vue:157
Footer 'c' shortcut calls click on an undefined chat ref and throws
Footer.vue binds `@keyup.c="chat!.$el.click()"`, but no element in the template carries `ref="chat"`, so `chat` is `undefined` and the non-null assertion `chat!` throws at runtime the moment a user presses 'c'.
Why it matters
Any user who presses 'c' while the footer is focused hits a JS error instead of the intended chat action, breaking the shortcut and potentially the surrounding interaction.
Fix
Guard the call with optional chaining so a missing ref fails silently instead of throwing, and attach the missing ref to its intended element.
@keyup.c="chat!.$el.click()"@keyup.c="chat?.$el?.click()"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 freeCraft
packages/hoppscotch-common/src/components/app/ContextMenu.vue:6
Context menu z-index is hardcoded outside any layering scale
ContextMenu.vue sets `z-index: 100` directly inside an inline `:style` string on the root div, rather than through a shared stacking token.
Why it matters
A hardcoded z-index competing with modals, toasts, or tooltips defined elsewhere in the app leads to menus getting hidden behind or on top of the wrong surface, and each fix becomes a guess-and-check across files.
Fix
Move the z-index into the class list using an existing stacking utility or design-system z-index token instead of an inline literal.
:style="`top: ${position.top}px; left: ${position.left}px; z-index: 100;`":style="`top: ${position.top}px; left: ${position.left}px;`"
class="fixed z-menu transform -translate-x-10 -translate-y-8 rounded border border-dividerDark bg-popover p-2 shadow-lg"Typography
Color
Spacing
Components
Motion
Working well
- Arrow-key navigation (onArrowUp/onArrowDown) wraps correctly across section boundaries and resets to [0,0] on new search, giving keyboard users full parity with mouse hover selection.
- bannerRole correctly maps error banners to role="alert" and info/warning to role="status", giving screen reader users appropriate interruption levels per banner type.
- The copy button correctly swaps icon state via refAutoReset with a 1000ms reset, giving clear temporary visual confirmation without extra state management.
- The sidebar-toggle icon buttons rotate their icon in sync with state (`-rotate-180` when collapsed) which gives a clear visual cue of the toggle direction.
Scored August 1, 2026 with Rams Engine v0.0.3 · Engine changelog
First scored July 9, 2026: 59/100. This rescore on v0.0.3: 59/100.
This page is an automated design review of hoppscotch/hoppscotch’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.
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.