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

posthog/posthog

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

30 files reviewed·August 1, 2026

View on GitHub

Elevated

Design risk in this codebase.

9issues
Critical, Serious & Moderate · top 6 shown below

Top fix

Add a confirmation step before deleting a subscription

See the fix

Verdict

Good primitives, thin follow-through: components are reused correctly but the interaction layer around risk and feedback gets skipped. The delete-with-no-confirmation pattern is the single biggest risk in a codebase that otherwise knows how to build a loading state.

Files Rams reviewed

products/desktop/apps/mobile/src/app/(tabs)/_layout.tsx

products/desktop/apps/mobile/src/app/_layout.tsx

tools/hedgebox-dummy/src/app/files/[id]/page.tsx

tools/hedgebox-dummy/src/app/login/page.tsx

tools/hedgebox-dummy/src/app/mariustechtips/page.tsx

tools/hedgebox-dummy/src/app/page.tsx

tools/hedgebox-dummy/src/app/pricing/page.tsx

tools/hedgebox-dummy/src/app/signup/page.tsx

frontend/src/lib/components/BridgePage/BridgePage.tsx

frontend/src/lib/components/PanelLayout/PanelLayout.tsx

frontend/src/lib/components/SessionTimeline/timeline/items/page.tsx

packages/quill/packages/charts/src/components/Legend/ChartLegendLayout.tsx

products/desktop/packages/ui/src/features/agent-applications/components/AgentDetailLayout.tsx

products/desktop/packages/ui/src/features/agents/components/AgentsTabLayout.tsx

products/desktop/packages/ui/src/features/code-review/components/CloudReviewPage.tsx

products/desktop/packages/ui/src/features/panels/components/PanelLayout.tsx

services/stripe-app/src/views/FullPage.tsx

products/desktop/apps/mobile/src/app/automation/index.tsx

products/desktop/apps/mobile/src/app/mcp-servers/index.tsx

products/subscriptions/frontend/components/Subscriptions/views/ManageSubscriptions.tsx

products/subscriptions/frontend/components/Subscriptions/views/SubscriptionOverviewComponents.tsx

products/subscriptions/frontend/components/Subscriptions/views/TabbedManageSubscriptions.tsx

tools/hedgebox-dummy/src/app/layout.tsx

frontend/src/lib/components/AccessDenied/index.tsx

frontend/src/lib/components/DateDisplay/index.tsx

frontend/src/lib/components/IngestionControls/index.tsx

frontend/src/lib/components/InsightLabel/index.tsx

frontend/src/lib/components/NotFound/index.tsx

frontend/src/lib/components/RichContentEditor/index.tsx

frontend/src/lib/components/TZLabel/index.tsx

95/100

UX

1 critical1 serious
UXCritical

products/subscriptions/frontend/components/Subscriptions/views/SubscriptionOverviewComponents.tsx:199

Delete subscription fires instantly with zero confirmation step

In the subscription row's action dropdown, the "Delete subscription" LemonButton (icon={<IconTrash />}, status="danger") calls onClick={onDelete} directly. There is no confirmation dialog, undo affordance, or secondary step between the click and the delete action firing.

Why it matters

A single misclick in a dropdown menu permanently destroys a subscription with no recovery path, which is exactly the kind of irreversible action that needs a guard rail.

Fix

Gate irreversible destructive actions behind an explicit confirmation dialog before calling the destructive handler.

<LemonButton
    icon={<IconTrash />}
    onClick={onDelete}
    data-attr="subscription-list-item-delete"
    status="danger"
    fullWidth
>
    Delete subscription
</LemonButton>
<LemonButton
    icon={<IconTrash />}
    onClick={() =>
        LemonDialog.open({
            title: 'Delete this subscription?',
            description: 'This action cannot be undone.',
            primaryButton: { children: 'Delete', status: 'danger', onClick: onDelete },
            secondaryButton: { children: 'Cancel' },
        })
    }
    data-attr="subscription-list-item-delete"
    status="danger"
    fullWidth
>
    Delete subscription
</LemonButton>
UXSerious

products/subscriptions/frontend/components/Subscriptions/views/TabbedManageSubscriptions.tsx:105

Modal list scroll chains into the page behind it at the scroll boundary

The subscription list container uses overflow-y-auto with no overscroll-behavior guard. Scrolling to the top or bottom of this list inside the LemonModal continues scrolling the page underneath.

Why it matters

Scroll chaining inside a modal breaks the expectation that a modal is a self-contained surface, and users can accidentally scroll the background page while trying to browse subscriptions.

Fix

Add overscroll-behavior: contain to scrollable regions inside modals so scroll input never leaks to the page behind them.

<div className="max-h-[60vh] overflow-y-auto flex flex-col gap-2">
<div className="max-h-[60vh] overflow-y-auto overscroll-contain flex flex-col gap-2">
98/100

Accessibility

1 serious
AccessibilitySerious

tools/hedgebox-dummy/src/app/page.tsx:30

User avatar image has no dimensions, causing layout shift on load

The avatar <img src={user.avatar} alt={user.name} /> inside the animate-fade-in-up hero has no explicit width or height attributes; only the parent div is sized via className="w-8 h-8".

Why it matters

Without intrinsic dimensions on the img itself, the browser can't reserve space before the image loads, so the hero content can shift during the entrance animation.

Fix

Set explicit width and height attributes on the image so the browser reserves layout space before load.

<img src={user.avatar} alt={user.name} />
<img src={user.avatar} alt={user.name} width={32} height={32} />

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

products/subscriptions/frontend/components/Subscriptions/views/SubscriptionOverviewComponents.tsx:213

Conditional class built with a template literal instead of the shared cn() helper

The subscription title div uses className={`font-medium truncate ${enabled ? 'text-link' : 'text-muted'}`}, a raw template literal, while conditional classes elsewhere in this file use LemonTag and other primitives that assume class-merging conventions.

Why it matters

Mixing template-literal classes with a cn()-based convention elsewhere in the codebase makes future class merges (e.g. adding a hover or disabled state) error-prone and inconsistent to maintain across files.

Fix

Use the codebase's cn() helper for conditional classNames instead of manual template literals.

<div className={`font-medium truncate ${enabled ? 'text-link' : 'text-muted'}`}>
    {subscription.title}
</div>
<div className={cn('font-medium truncate', enabled ? 'text-link' : 'text-muted')}>
    {subscription.title}
</div>
98/100

Motion

1 serious
MotionSerious

tools/hedgebox-dummy/src/app/page.tsx:165

Footer logo hover animation never fires, dead code shipped as polish

The footer logo mark carries group-hover:scale-105 transition-transform, but no ancestor element in the footer (the flex row at line 164, or any wrapper) has the `group` class applied.

Why it matters

The transition classes ship dead weight and signal an interactive affordance that never triggers, which misleads anyone reading the code into thinking hover feedback exists here.

Fix

Remove unused hover utilities or add the group class to the intended interactive ancestor so the animation actually fires.

<div className="w-7 h-7 bg-primary rounded-lg flex items-center justify-center group-hover:scale-105 transition-transform">
<div className="w-7 h-7 bg-primary rounded-lg flex items-center justify-center">
98/100

Craft

1 serious
CraftSerious

tools/hedgebox-dummy/src/app/page.tsx:108

Three feature cards share one template with zero visual differentiation

The "Fox-proof", "Instant", and "Warm & fuzzy" cards each wrap their emoji in an identical `w-16 h-16 bg-primary/10 rounded-2xl` container with the same card shell, border, and padding, differing only by emoji and copy.

Why it matters

When every card in a comparison section is visually interchangeable, users scan text top to bottom rather than pattern-matching icons, slowing recognition of which benefit maps to which card.

Fix

Vary icon container weight or tint within the existing primary token so each card reads as distinct without introducing a new hue.

<div className="w-16 h-16 bg-primary/10 rounded-2xl flex items-center justify-center mb-6">
    <span className="text-3xl">🛡️</span>
</div>
<div className="w-16 h-16 bg-primary/15 border border-primary/20 rounded-2xl flex items-center justify-center mb-6">
    <span className="text-3xl">🛡️</span>
</div>

Typography

No issues found

Color

No issues found

Spacing

No issues found

Working well

  • The signed-in hero correctly swaps both copy and CTA label ('Go to files') versus the signed-out state ('Get started free'), giving each user state its own clear next action instead of a generic shared CTA bolted onto both.
  • Reusing SubscriptionListItem, SubscriptionEmptyState, and LemonModal instead of building bespoke list rows or a custom modal keeps SubscriptionOverviewComponents thin and consistent with the rest of the design system.
  • The destination summary correctly sets title={destination.title} to preserve the full value behind a compact truncated label, a pattern the subscription title itself should also follow.
  • Tab labels suppress the '(0)' count while loading (rendering plain 'label' instead of '${label} (0)'), avoiding a false empty-state signal before real data arrives.

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

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