Skip to content

v2: mark findings on the user's own page, and gate the rest behind signup #59

Description

@georgekaran

v2. Not a v1 change. Recorded now because it reverses a shipped v1 decision, and that reversal should be a deliberate act rather than something that accretes.

Context

The current home screen (#19) is a URL field that returns a report. This proposes replacing the moment after submit: instead of a list of rule names, you see your own page with its accessibility failures marked on it, and only the first two or three are revealed.

The reasoning is emotional rather than functional, and that is the strongest part of it. A list of axe rule ids is something you evaluate. Your own homepage with a red outline around your own navigation is something you react to — and the reaction is "this is broken and I want to fix all of it". That is the moment to ask for the account, not before it and not after the interest has cooled.

The sketch

Landing — wordmark left, Login and a filled Sign up right. Centred hero, "No more a11y problems. Fix it with tabstop.", a single URL field placeholdered "Paste your URL" and a Fix button. Nothing competing with it.

After submit — split roughly ⅔ / ⅓:

  • Left: the audited site, rendered.
  • On the render: findings marked in place. The sketch shows two — a red dashed outline with a ! around a sidebar of icon buttons, and an amber dashed outline around a clock element.
  • Right: a panel. The findings list, the locked remainder, the count, and the signup CTA.

Two colours read as two severities. If that is intended, note the ! badge is doing load-bearing work as the non-colour signal — #20 already flags this exact trap for the delta badge, and on an accessibility product it is worse here than there.

Scope

In

  • Landing hero reduced to one affordance: paste a URL.
  • Split analysis screen: rendered page left, findings panel right.
  • Findings anchored to the elements that produced them, visually, on the page.
  • Reveal 2–3; show the total; lock the rest behind signup.

Out (for now)

  • Credits or payment. Signup is the gate; buying a report is a separate decision with a whole billing subsystem behind it.
  • Remediation. See the copy question below.
  • Authenticated or crawled pages. Same cut line as v1.

The decision that gates feasibility

How the left pane renders someone else's site. This is the whole ballgame — everything else is straightforward once it is settled.

Approach Reality
iframe the live site The obvious reading, and mostly non-viable. A large share of real sites send X-Frame-Options or CSP frame-ancestors and simply refuse to load. Worse: even when a site does load, it is cross-origin, so we cannot inject the markers into it at all. The one site guaranteed to work is our own.
Screenshot + overlay The worker already drives Playwright. axe hands us nodes[].target — CSS selectors — and Playwright resolves those to bounding boxes. Draw absolutely-positioned outlines over an image. Works on every site, no third-party code on our origin.
Rehydrate a captured DOM on our domain Feels most alive, and means rendering arbitrary third-party markup on our own origin. That collides head-on with an existing rule: violation html is displayed as text, never as markup, which is the same XSS reasoning behind choosing an httpOnly session cookie over a readable token.

Recommendation: screenshot. It loses scrolling the real page and gains working everywhere, on a product whose entire pitch is that it works on your site without setup. The interactivity is worth less than the coverage.

What the audit would have to start capturing

Not a frontend-only feature — the worker changes:

  • Bounding boxes per violation node. axe gives selectors, not coordinates. Resolving them means a Playwright pass after the axe run, at the same viewport, before anything can shift.
  • A screenshot per audit, stored somewhere. There is no blob storage today; the stack is Postgres and Redis. A full-page PNG per audit is a new infrastructure dependency, not a column. This is the largest hidden cost in the idea.
  • No back-fill. Nothing stores coordinates now, so every existing audit would need re-auditing to gain pins. Fine, but state it rather than discover it.
  • Retention and privacy. We would be storing rendered images of arbitrary third-party pages — some behind no login but not meant to be catalogued, some containing personal data. Worth a deliberate retention policy before the first one is written, not after.

The gate reverses a shipped decision

Today an anonymous audit shows the complete result with no account, and /r/:uuid share links are fully public. Both were deliberate. This proposal withholds most of the result from the person who requested it.

Two consequences to settle:

  1. Does the share page gate too? If not, the gate leaks — paste your own share link in an incognito window and the withheld findings are all there.
  2. Which two or three are revealed? Most severe is the most compelling and also gives away the most. Least severe converts worse and feels like a bait. Probably: the most severe, on the grounds that the honest version of this pitch is "here is your worst problem, there are 22 more".

Open questions

  • The copy says "Fix". tabstop detects and reports; it does not fix. Is that aspirational framing, or is remediation guidance where v2 is going? It changes what the locked content is worth — "see the other 21 problems" is a much weaker offer than "see how to fix all 23", and the second one is a different product.
  • Severity colours — confirm red/amber is severity, and that the non-colour signal carries it.
  • Credits — out of scope here, but if it is coming, the gate should be designed as "account, then possibly credits" rather than retrofitted.

Acceptance criteria

  • Landing page is one hero and one URL field.
  • Submitting reaches a split screen with the audited page rendered and findings marked on it.
  • Markers carry severity without relying on colour alone.
  • 2–3 findings revealed, total count shown, remainder gated behind signup.
  • The rendering approach is chosen and recorded in DECISIONS.md, with the framing/XSS reasoning.
  • The reversal of anonymous-full-results is recorded in DECISIONS.md, including what happens to share links.
  • Screenshot storage and its retention policy are decided before the first image is written.

Metadata

Metadata

Assignees

No one assigned

    Labels

    area:serverBackend API and domain code under server/area:webFrontend under web/enhancementNew feature or requesttype:decisionRequires a technical choice, to be logged in DECISIONS.mdv2Deferred past launch; not required for v1

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions