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

outline/outline

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

30 files reviewed·August 1, 2026

View on GitHub

Top fix

Add an undo step before permanently removing the icon

See the fix

Verdict

A well-structured icon picker undone by small broken promises: a shortcut hint that lies, a prop that ignores the caller. The biggest risk is the instant, undo-less delete sitting next to tap targets too small for mobile thumbs.

Files Rams reviewed

app/components/AuthenticatedLayout.tsx

app/components/InputSearchPage.tsx

app/components/Layout.tsx

app/scenes/Document/components/SidebarLayout.tsx

app/components/IconPicker/index.tsx

app/components/Sharing/components/index.tsx

app/components/SplitView/index.tsx

app/components/TemplatizeDialog/index.tsx

app/components/primitives/Menu/index.tsx

app/components/ActionButton.tsx

app/components/Analytics.tsx

app/components/ArrowKeyNavigation.tsx

app/components/Authenticated.tsx

app/components/Avatar/Avatar.tsx

app/components/Avatar/AvatarWithPresence.tsx

app/components/Breadcrumb.tsx

app/components/Button.tsx

app/components/CenteredContent.tsx

app/components/CircularProgressBar.tsx

app/components/Collaborators.tsx

app/components/Collapsible.tsx

app/components/Collection/CollectionForm.tsx

app/components/Collection/CollectionNew.tsx

app/components/CollectionBreadcrumb.tsx

app/components/CollectionDeleteDialog.tsx

app/components/CollectionDuplicateDialog.tsx

app/components/ColorButton.tsx

app/components/CommandBar/CommandBar.tsx

app/components/CommandBar/CommandBarItem.tsx

app/components/CommandBar/CommandBarResults.tsx

96/100

UX

2 serious
UXSerious

app/components/IconPicker/index.tsx:258

"Remove" button deletes the icon instantly with no undo

The RemoveButton labeled "Remove" calls onIconRemove directly on click, with no confirmation dialog or undo step. It sits directly next to the "Icons"/"Emojis" tab controls in TabActionsWrapper, so a misclick while switching tabs triggers immediate, irreversible removal.

Why it matters

A destructive action placed beside frequently-clicked tab controls turns an ordinary misclick into permanent data loss, since there's no recovery path once onIconRemove fires.

Fix

Gate destructive single-click actions behind a confirmation step or provide an undo affordance immediately after the action.

{allowDelete && (
          <RemoveButton onClick={onIconRemove}>{t("Remove")}</RemoveButton>
        )}
{allowDelete && (
          <RemoveButton
            onClick={() => {
              if (window.confirm(t("Remove this icon?"))) {
                onIconRemove();
              }
            }}
          >
            {t("Remove")}
          </RemoveButton>
        )}
UXSerious

app/components/InputSearchPage.tsx:51

Visible "⌘K" hint doesn't match the actual registered shortcut

The Shortcut span in InputSearchPage always renders "⌘K" (or Ctrl+K) as the search shortcut hint, but useKeyDown registers "f" with metaKey, meaning the real hotkey is Cmd/Ctrl+F. Pressing K does nothing, and the F shortcut that actually works is never shown to the user.

Why it matters

Users who trust the on-screen hint press ⌘K expecting the input to focus and nothing happens, while the shortcut that does work stays undiscovered, so power users never learn it and the hint itself becomes noise that trains people to distrust it.

Fix

Register the shortcut key to match the visible hint, or update the hint text to reflect the key actually bound.

useKeyDown(
    "f",
    (ev: KeyboardEvent) => {
      if (document.activeElement !== inputRef.current) {
        ev.preventDefault();
        inputRef.current?.focus();
      }
    },
    { metaKey: true }
  );
useKeyDown(
    "k",
    (ev: KeyboardEvent) => {
      if (document.activeElement !== inputRef.current) {
        ev.preventDefault();
        inputRef.current?.focus();
      }
    },
    { metaKey: true }
  );
98/100

Accessibility

1 serious
AccessibilitySerious

app/components/IconPicker/index.tsx:311

Tab and Remove buttons render tap targets well under 44px on mobile

StyledTab ("Icons"/"Emojis") and RemoveButton ("Remove") both use font-size: 14px with padding: 8px 12px, which yields a tap target roughly 30px tall. This same IconPicker content also renders inside a mobile Drawer via the useMobile check, meaning these controls are touch targets on phones.

Why it matters

Touch targets under the ~44px minimum increase mistap rate for users with limited dexterity or larger fingers, and a mistap on "Remove" specifically risks the irreversible icon deletion noted above.

Fix

Increase vertical padding on tab and button controls that render in touch contexts to meet a 44px minimum tap target.

const StyledTab = styled(Tabs.Trigger)<{ $active: boolean }>`
  position: relative;
  font-weight: 500;
  font-size: 14px;
  cursor: var(--pointer);
  background: none;
  border: 0;
  padding: 8px 12px;
const StyledTab = styled(Tabs.Trigger)<{ $active: boolean }>`
  position: relative;
  font-weight: 500;
  font-size: 14px;
  cursor: var(--pointer);
  background: none;
  border: 0;
  padding: 14px 12px;
  min-height: 44px;

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

Components

1 serious
ComponentsSerious

app/components/InputSearchPage.tsx:107

labelHidden is hardcoded true, so callers can never show the label

InputMaxWidth (rendered via the styled Input) always passes the literal attribute `labelHidden` regardless of what the caller intends, even though Props declares a `label` value meant to be shown or hidden per use case. Any consumer wanting a visible label on this search input has no way to get one.

Why it matters

The component silently overrides caller intent, so future call sites that need a visible label ship with a hidden one, and the inconsistency has to be diagnosed by reading the styled-component source rather than the prop interface, which raises maintenance cost every time this input is reused.

Fix

Wire the existing labelHidden intent through a real prop instead of hardcoding the attribute to true.

label={label}
      onFocus={setFocused}
      onBlur={setUnfocused}
      margin={0}
      labelHidden
    >
label={label}
      onFocus={setFocused}
      onBlur={setUnfocused}
      margin={0}
      labelHidden={labelHidden ?? true}
    >

Typography

No issues found

Color

No issues found

Spacing

No issues found

Motion

No issues found

Craft

No issues found

Working well

  • Swapping Popover for Drawer based on useMobile in the icon picker is the right call: it gives touch users a bottom-sheet pattern instead of forcing a small anchored popover onto a phone screen.
  • Enter-to-search and blur handling on InputSearchPage are wrapped with preventDefault, giving predictable, non-surprising keyboard behavior around the search field.
  • Separator and Wrapper source their colors through the theme's s() accessor rather than hardcoded hex, keeping the palette consistent across this file.

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

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