continuedev on GitHub

continuedev/continue

Reviewed against Rams quality heuristics: accessibility, color, typography, spacing, components, motion, UX, and craft.

30 files reviewed·July 25, 2026

View on GitHub

Elevated

Accessibility needs attention.

7issues
Critical & Serious · top 6 shown below

Top fix

Make tool group header a real focusable, keyboard-operable button

See the fix

Verdict

Polished visuals sit on top of broken interaction plumbing: nested buttons, div-only click targets, and toggles that fight their own headers. The biggest risk is that keyboard and screen-reader users simply can't operate this config UI as built.

Files Rams reviewed

gui/src/pages/config/components/ConfigHeader.tsx

gui/src/pages/config/components/ConfigItemSelect.tsx

gui/src/pages/config/components/ConfigRow.tsx

gui/src/pages/config/components/ModelRoleRow.tsx

gui/src/pages/config/components/ModelRoleSelector.tsx

gui/src/pages/config/components/ToolPoliciesGroup.tsx

gui/src/pages/config/components/ToolPolicyItem.tsx

gui/src/pages/config/components/UserSetting.tsx

docs-site/app/components/NotFoundPage.tsx

gui/src/components/Layout.tsx

gui/src/components/console/Layout.tsx

gui/src/pages/config/components/ConfigSection.tsx

gui/src/pages/config/components/ConfigSubsection.tsx

docs-site/app/[[...slug]]/page.tsx

docs-site/app/layout.tsx

gui/src/pages/config/index.tsx

gui/src/pages/gui/ToolCallDiv/index.tsx

docs-site/app/components/ClientRedirect.tsx

gui/src/components/AssistantAndOrgListbox/index.tsx

gui/src/components/History/index.tsx

gui/src/components/StyledMarkdownPreview/StepContainerPreToolbar/index.tsx

gui/src/components/StyledMarkdownPreview/index.tsx

gui/src/components/dialogs/index.tsx

gui/src/pages/config/configTabs.tsx

gui/src/pages/config/features/indexing/IndexingProgress.tsx

gui/src/pages/config/features/indexing/IndexingProgressErrorText.tsx

gui/src/pages/config/features/indexing/IndexingProgressIndicator.tsx

gui/src/pages/config/features/indexing/IndexingProgressSubtext.tsx

gui/src/pages/config/features/keyboard/KeyboardShortcuts.tsx

gui/src/pages/config/sections/ConfigsSection.tsx

89/100

Accessibility

3 critical1 serious
AccessibilityCritical

gui/src/pages/config/components/ToolPoliciesGroup.tsx:64

Tool group expand header is a div, unreachable by keyboard

The expand/collapse header in ToolPoliciesGroup.tsx is a <div> with onClick={() => setIsExpanded(!isExpanded)} and cursor-pointer styling, showing displayName and the enabled-count badge. It has no role, tabIndex, or onKeyDown, so it isn't in the tab order and Enter/Space do nothing.

Why it matters

Keyboard-only users cannot expand or collapse a tool group at all, permanently hiding the tool list inside it from anyone who can't use a mouse.

Fix

Give any div acting as a toggle control role="button", tabIndex={0}, and an onKeyDown handler for Enter and Space.

<div
  className="flex cursor-pointer items-center justify-between gap-3 rounded px-2 py-2 hover:bg-gray-50 hover:bg-opacity-5"
  onClick={() => setIsExpanded(!isExpanded)}
>
<div
  role="button"
  tabIndex={0}
  aria-expanded={isExpanded}
  className="flex cursor-pointer items-center justify-between gap-3 rounded px-2 py-2 hover:bg-gray-50 hover:bg-opacity-5"
  onClick={() => setIsExpanded(!isExpanded)}
  onKeyDown={(e) => {
    if (e.key === "Enter" || e.key === " ") {
      e.preventDefault();
      setIsExpanded(!isExpanded);
    }
  }}
>
AccessibilityCritical

gui/src/pages/config/components/ConfigHeader.tsx:43

Icon-only add button has no accessible name for screen readers

The add button in ConfigHeader.tsx renders only a PlusIcon inside a <Button variant="icon">, wrapped in a ToolTip that shows addButtonTooltip on hover. Tooltips are not exposed to assistive tech the way an aria-label is, so a screen reader announces just "button" with no indication it adds an item.

Why it matters

Screen reader users can't tell what this control does before activating it, so they either skip it or trigger an unknown action, blocking task completion for a class of users.

Fix

Add aria-label to icon-only buttons using the same text already passed to the tooltip.

<Button
  onClick={onAddClick}
  variant="icon"
  size={isSmall ? "sm" : "lg"}
>
  <PlusIcon className={isSmall ? "h-2.5 w-2.5" : "h-3 w-3"} />
</Button>
<Button
  onClick={onAddClick}
  variant="icon"
  size={isSmall ? "sm" : "lg"}
  aria-label={addButtonTooltip}
>
  <PlusIcon className={isSmall ? "h-2.5 w-2.5" : "h-3 w-3"} />
</Button>
AccessibilityCritical

gui/src/pages/config/components/ConfigRow.tsx:48

Row renders arbitrary interactive children inside a native button

ConfigRow wraps its {children} (which, per the handleClick logic checking for 'button, input, textarea, select, [role="button"], [role="switch"]', is expected to include switches or buttons) inside a native <Button variant="ghost">. Nesting interactive elements inside a <button> is invalid HTML.

Why it matters

Nested interactive controls inside a button lose predictable keyboard focus order and some browsers block or double-fire click events on the inner control, so a toggle or button placed in a config row can become unreliable or untappable by keyboard.

Fix

Use a non-button container with role="button" and a keydown handler for the row's own click target, and keep nested controls as true siblings outside any button element.

<Button
  variant="ghost"
  className={cn(
    baseClasses,
    interactiveClasses,
    disabledClasses,
    "!my-0 text-left",
    className,
  )}
  onClick={handleClick}
  disabled={disabled}
  data-config-row
>
  <div className="flex flex-col">
    <span className="text-sm font-medium">{title}</span>
    <p className="mt-0.5 text-xs text-gray-500">{description}</p>
  </div>
  <div className="flex items-center gap-4">
    {children}
    {Icon && <Icon className="h-5 w-5 flex-shrink-0 text-gray-400" />}
  </div>
</Button>
<div
  role="button"
  tabIndex={disabled ? -1 : 0}
  onKeyDown={(e) => (e.key === "Enter" || e.key === " ") && handleClick(e as any)}
  className={cn(
    baseClasses,
    interactiveClasses,
    disabledClasses,
    "!my-0 text-left",
    className,
  )}
  onClick={handleClick}
  aria-disabled={disabled}
  data-config-row
>
  <div className="flex flex-col">
    <span className="text-sm font-medium">{title}</span>
    <p className="mt-0.5 text-xs text-gray-500">{description}</p>
  </div>
  <div className="flex items-center gap-4">
    {children}
    {Icon && <Icon className="h-5 w-5 flex-shrink-0 text-gray-400" />}
  </div>
</div>
AccessibilitySerious

gui/src/pages/config/components/ModelRoleSelector.tsx:153

Invalid model status text renders at 10px, below legible floor

The invalid-config message ('(Missing API Key)', '(Missing env secret)', '(Invalid config)') in ModelRoleSelector's option list renders in a <span className="ml-2 text-[10px] italic">, well under the 12px minimum for comfortable reading.

Why it matters

Text this small is hard to read for any user and fails typical legibility guidance, so the exact reason a model option is disabled becomes difficult to confirm at a glance.

Fix

Raise small status text to at least the 12px (text-xs) scale step.

<span className="ml-2 text-[10px] italic">
  {invalidMessage}
</span>
<span className="ml-2 text-xs italic">
  {invalidMessage}
</span>
98/100

Typography

1 serious
TypographySerious

gui/src/pages/config/components/ConfigHeader.tsx:28

Heading level tied to visual size instead of document structure

ConfigHeader picks HeadingTag as h3 or h2 purely from the variant prop (isSmall), with no way for a consuming page to specify the actual semantic level. Any page that nests a "sm" ConfigHeader under an existing h2 gets an h3 (or vice versa) regardless of the true outline.

Why it matters

When heading level is coupled to visual variant rather than page structure, screen reader users navigating by heading level get an outline that doesn't match the page's real hierarchy, and skipped or duplicated levels compound as this component gets reused.

Fix

Decouple heading semantics from visual variant by exposing an explicit headingLevel prop.

const HeadingTag = isSmall ? "h3" : "h2";
const HeadingTag = headingLevel ?? (isSmall ? "h3" : "h2");

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

UX

1 serious
UXSerious

gui/src/pages/config/components/ToolPoliciesGroup.tsx:93

Group toggle switch click also collapses the header it sits in

The ToggleSwitch that enables/disables all tools in a group sits inside a <div> inside the header row, and that whole header row has onClick={() => setIsExpanded(!isExpanded)}. Toggling the switch doesn't stop propagation, so a click on the switch also fires the header's expand/collapse handler.

Why it matters

A user flipping the group toggle gets an unexpected side effect of the panel collapsing or expanding, making the toggle feel unreliable and forcing a second click just to see the tool list again.

Fix

Stop click propagation on nested interactive controls placed inside a clickable header.

<div>
  <ToggleSwitch
    isToggled={isGroupEnabled}
    onToggle={() => dispatch(toggleToolGroupSetting(groupName))}
    text=""
    size={10}
    disabled={allToolsOff}
  />
</div>
<div onClick={(e) => e.stopPropagation()}>
  <ToggleSwitch
    isToggled={isGroupEnabled}
    onToggle={() => dispatch(toggleToolGroupSetting(groupName))}
    text=""
    size={10}
    disabled={allToolsOff}
  />
</div>

Color

No issues found

Spacing

No issues found

Components

No issues found

Motion

No issues found

Craft

No issues found

Working well

  • Disabled invalid model options in ModelRoleSelector combine a visible '(Missing API Key)' or '(Invalid config)' label with the disabled/italic state, so the reason for unavailability is never conveyed by color or opacity alone.
  • Using ToolTip to surface addButtonTooltip on the icon-only add button is the right instinct for an unlabeled affordance, it just needs to also reach the accessible name via aria-label rather than relying on hover-only text.
  • The badge combining enabled and total tool count ('3/5') in ToolPoliciesGroup gives a compact, scannable summary of group state without forcing the group open.

Scored July 25, 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 continuedev/continue’s UI code: 30 files read against 309 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