0xsmw/pulse-uptime
Reviewed against Rams quality heuristics: accessibility, color, typography, spacing, components, motion, UX, and craft.
30 files reviewed·July 22, 2026
More findings
Verdict
Good bones, unfinished edges: the dashboard's stats block and CTA placement show real craft, but two auth states dead-end the user entirely. The biggest risk isn't visual polish, it's flows that promise an action and don't deliver one.
Files Rams reviewed
app/(auth)/join/[token]/page.tsx
app/(auth)/onboarding/page.tsx
app/(dashboard)/incidents/[incidentId]/page.tsx
app/(dashboard)/incidents/page.tsx
app/(dashboard)/incidents/reports/[reportId]/page.tsx
app/(dashboard)/incidents/reports/page.tsx
app/(dashboard)/layout.tsx
app/(dashboard)/monitors/[monitorId]/page.tsx
app/(dashboard)/page.tsx
app/(settings)/settings/layout.tsx
app/(settings)/settings/status-page/page.tsx
app/(settings)/settings/system/page.tsx
app/(settings)/settings/team/page.tsx
app/cli/authorize/page.tsx
app/docs/cli/page.tsx
app/layout.tsx
app/status/layout.tsx
app/status/reports/[reportId]/page.tsx
app/(auth)/layout.tsx
app/(auth)/login/page.tsx
app/(dashboard)/dependencies/[dependencyId]/page.tsx
app/(dashboard)/help/page.tsx
app/(dashboard)/incidents/reports/new/page.tsx
app/(settings)/settings/access/page.tsx
app/(settings)/settings/account/page.tsx
app/(settings)/settings/monitors/page.tsx
app/(settings)/settings/notifications/page.tsx
app/(settings)/settings/page.tsx
app/(settings)/settings/security/page.tsx
app/status/[group]/page.tsx
UX
app/(auth)/join/[token]/page.tsx:18
Invalid invite screen strands the user with no way forward
The expired/revoked invite branch renders only an eyebrow ("Join Pulse"), a heading ("This invite is no longer valid"), and a paragraph telling the user to "ask the person who invited you for a new link." There is no link back to the marketing site, sign-in page, or any other page on the whole screen.
Why it matters
A user who lands here from a stale email has nowhere to click. They either hit the browser back button or leave, and the app never gets a chance to route them anywhere useful.
Fix
Give every dead-end error state at least one recovery link, such as a route back to sign-in or the marketing home.
<div className={styles.flow}>
<p className={styles.eyebrow}>Join Pulse</p>
<h1 className={styles.title}>This invite is no longer valid</h1>
<p className={styles.lede}>
The link may have expired, been revoked, or already been used. Ask the
person who invited you for a new link.
</p>
</div><div className={styles.flow}>
<p className={styles.eyebrow}>Join Pulse</p>
<h1 className={styles.title}>This invite is no longer valid</h1>
<p className={styles.lede}>
The link may have expired, been revoked, or already been used. Ask the
person who invited you for a new link.
</p>
<Card className={styles.card}>
<Link className="text-sm underline underline-offset-4" href="/sign-in">
Back to sign in
</Link>
</Card>
</div>app/(dashboard)/incidents/[incidentId]/page.tsx:83
No handling shown for an incident that resolved with an unknown or empty opening failure
The "Opening Failure" section renders {incident.openingFailure} directly as the only content of the section, with no fallback branch if the value is empty or null.
Why it matters
If openingFailure can be empty for any incident record (e.g. one still being triaged), the section silently renders a blank paragraph under a header, reading as a broken page rather than a designed state.
Fix
Render a designed fallback (e.g. "Cause not yet determined") when openingFailure is empty instead of leaving the paragraph blank.
<p className="mt-2 font-data text-[13px]">{incident.openingFailure}</p><p className="mt-2 font-data text-sm">
{incident.openingFailure || "Cause not yet determined"}
</p>app/(auth)/join/[token]/page.tsx:37
Copy tells the user to sign out, but the only link goes to the dashboard
The already-signed-in state reads "so sign out first to use it," then the sole interactive element is a link labeled "Back to the dashboard" pointing to `/`. No control on this screen actually signs the user out.
Why it matters
The instruction and the only available action don't match, so the user has to hunt elsewhere in the app to do the thing the page just told them to do, adding friction right where the flow should end.
Fix
Make the action's label match the action the copy just asked for, or provide the sign-out control directly.
<Card className={styles.card}>
<Link className="text-[13px] underline underline-offset-4" href="/">
Back to the dashboard
</Link>
</Card><Card className={styles.card}>
<Link className="text-sm underline underline-offset-4" href="/sign-out">
Sign out and use this invite
</Link>
</Card>Accessibility
app/(dashboard)/incidents/[incidentId]/page.tsx:42
"Incidents" back link has no vertical padding, shrinking its tap target below 24px
The Link back to /incidents renders at text-[13px] with a default line-height and no py padding, so its clickable height is roughly 19-20px. This is the primary way back off the page.
Why it matters
At under 20px tall this falls below the WCAG 2.5.8 (AA) 24x24px minimum target size, making it harder to tap accurately on mobile and a frequent source of mis-taps for anyone with reduced motor precision.
Fix
Add vertical padding so the link's hit area reaches at least 24px tall.
<Link
className="mb-5 inline-flex items-center gap-1.5 text-[13px] text-[var(--fg-muted)] hover:text-[var(--fg)]"
href="/incidents"
><Link
className="mb-5 inline-flex items-center gap-1.5 py-1 text-sm text-[var(--fg-muted)] hover:text-[var(--fg)]"
href="/incidents"
>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 freeTypography
app/(auth)/join/[token]/page.tsx:38
Arbitrary text-[13px] bypasses the type scale on the dashboard link
The "Back to the dashboard" link uses `text-[13px]`, a bracket value outside the standard Tailwind type scale, while every other text element on the page (`styles.eyebrow`, `styles.title`, `styles.lede`) comes from the auth module's CSS classes.
Why it matters
A one-off pixel size that doesn't map to a scale step drifts from the rest of the type system and makes future size audits harder, since this element can't be reasoned about alongside the scale.
Fix
Replace arbitrary pixel values with the nearest scale step (text-xs or text-sm).
<Link className="text-[13px] underline underline-offset-4" href="/"><Link className="text-sm underline underline-offset-4" href="/">Color
app/layout.tsx:43
Hardcoded dark theme on <html> fights system-driven theming
`<html data-theme="dark" ...>` is hardcoded while `ThemeProvider` is configured with `defaultTheme="dark" enableSystem`, and `viewport.colorScheme` declares `"dark light"` support for both appearances.
Why it matters
A visitor whose OS is set to light mode gets a server-rendered dark page, then watches it flip to light after ThemeProvider hydrates and reads the system preference: a visible flash on every first load for light-mode users, which undercuts the 'dark light' support you've declared in viewport.
Fix
Set the initial theme attribute to match the declared system-aware default (or set defaultTheme="system") so the server-rendered attribute and the client-resolved theme agree on first paint.
<html data-theme="dark" lang="en" suppressHydrationWarning>
<body className={`${geistSans.variable} ${geistMono.variable}`}>
<ThemeProvider defaultTheme="dark" enableSystem><html lang="en" suppressHydrationWarning>
<body className={`${geistSans.variable} ${geistMono.variable}`}>
<ThemeProvider defaultTheme="system" enableSystem>Spacing
Components
Motion
Craft
Working well
- The empty catch block around the admin-user lookup carries a comment explaining exactly why it's silent ('readiness explains database failures') rather than swallowing the error with no trace. That's the right way to handle an intentional fallback: the reader doesn't have to guess whether the catch is a bug or a decision.
- Optional chaining and nullish coalescing (progress?.draftMonitor as ... ?? undefined, progress?.currentStep || 'monitor') keep the prop derivation readable in one pass instead of a chain of manual null checks: this is the kind of small clarity choice that keeps a data-loading component easy to audit later.
- The redirect logic is layered correctly: unauthenticated users go to /login, already-onboarded users go to /, and only the remaining case renders the flow. Each branch resolves before the next check runs, so there's no risk of flashing the onboarding UI to someone who shouldn't see it.
- The page explicitly branches on invalid invite, already-authenticated, and valid-invite states instead of letting an unhandled case fall through to a blank screen or a crash. Designing for the non-happy paths up front is exactly what keeps auth flows from feeling broken at the edges.
Scored July 22, 2026 with Rams Engine v0.0.2 · Engine changelog
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 this review on every PR.