SaaS marketing + docs portal

SaaS product accessibility

ADA, with finding concentration on the public surface.

SaaS accessibility leads ship the audit-grade artifact from a different surface than ecommerce does — split across two pages the buyer reads before any auth wall: the marketing site (hero images, pricing CTA, alt-text) and the developer docs portal the engineering buyer navigates next (sidebar disclosure trees on grouped API sections). The remediation posture is the same — WCAG 2.2 + EN 301 549, the pull request as the audit-grade artifact — but the finding concentration now sits where the ADA demand letter reads, on the public surface.

Why this vertical, this quarter

Three pressure points shape the run.

SaaS product accessibility reads from the public-facing surface, split across two pages: the marketing site a buyer reads first (hero image, pricing CTA, alt-text) and the developer docs portal the engineering buyer navigates next (sidebar disclosure-tree on grouped API sections). The ADA demand-letter factory has noticed both surfaces.

  • ADA demand letters — public-surface scope

    SaaS plaintiffs now target the marketing hero CTA, the public docs-portal sidebar tree, and the docs-portal table-of-contents — the surfaces the engineering buyer navigates before any login. Findings concentrate on the public-facing surface where the buyer sits, not on a logged-in product form the audit never reaches.

  • Settlement band — per SaaS tenant

    Per-tenant settlement ranges cluster between roughly $5K and $90K, with volume discounts when the demand-letter factory targets a category. The defense is the audit-grade pull request — a redacted diff, an axe-core diagnostic, a WCAG 2.2 SC, an EN 301 549 clause.

  • Two surfaces, one finding concentration

    The densest finding concentration splits across two SaaS-facing pages: the marketing page (hero image alt-text, pricing-CTA wrapping link) and the developer docs portal (sidebar disclosure-tree on grouped API sections). The locator bar lives on the public surface — before any auth wall — so the buyer reads it first.

A worked redacted PR

One finding, one redacted pull request, end to end.

Below is a redacted PR diff for a SaaS marketing-page <Hero> — a hero <Image> is wrapped in a <Link> that routes to the signup page, but ships without alt on the image and without a link-purpose label, so an AT user hears a destination URL with no destination context and a silent image. Identifiers are redacted; the rule spans any marketing-page hero whose CTA is an image-only link.

axe-core finding

serious

Rule
image-alt
Impacted surface
SaaS marketing page hero — Start your trial CTA wrapped around a hero image
Surface type
Public marketing-page hero (buyer-facing, unauthenticated)
Description
The hero <Image> on the marketing page renders without an alt attribute; it is wrapped in a <Link href="/signup"> with no other accessible name. An AT user traversing the hero hears a bare destination URL with no purpose and a silent image — failing SC 1.1.1 (non-text content needs a text alternative) and SC 2.4.4 (link purpose must be determinable from the link text in its context), equivalent to EN 301 549 §9.1.1.1 + §9.2.4.4.
app/(marketing)/_components/hero.tsx:1–32JSX diff

before

/* app/(marketing)/_components/hero.tsx */
import Image from "next/image";
import Link from "next/link";

export function Hero() {
  return (
    <section aria-labelledby="hero-heading">
      <Link href="/signup" className="block">
        <Image
          src="/marketing/hero-trial.png"
          width={1200}
          height={600}
          priority
        />
      </Link>
    </section>
  );
}

after

/* app/(marketing)/_components/hero.tsx */
import Image from "next/image";
import Link from "next/link";

export function Hero() {
  return (
    <section aria-labelledby="hero-heading">
      <Link
        href="/signup"
        aria-label="Start your trial — opens the signup page"
        className="block focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-brand-700"
      >
        <Image
          src="/marketing/hero-trial.png"
          width={1200}
          height={600}
          priority
          alt="Start your trial — opens the signup page"
        />
      </Link>
    </section>
  );
}
  • WCAG1.1.1(level A)
  • WCAG2.4.4(level A)
  • EN 301 549§9.1.1.1
  • EN 301 549§9.2.4.4
  • axe-coreimage-alt

Run a Sigil pilot

Stop filing ADA exceptions. File a pull request.

The free WCAG scan is one URL. The pilot files the same redacted PR against your own repository on every deploy — the audit-grade artifact your counsel, your product team, and your accessibility lead all read from the same source across the SaaS marketing page (hero alt-text + pricing CTA), the developer docs portal, and the sidebar disclosure-tree the run concentrates on.

EAA inquiry response · source-level answer

When the regulator asks how a SaaS marketing page preserves a text alternative on its hero image and a link purpose on the wrapping Start your trial CTA, the same diff above is what gets read back. In the registration language a 2025 EAA questionnaire row expects:

Q: How does the marketing-page hero <Image> wrapped in a <Link> satisfy non-text-content + link-purpose on the SaaS surface?

A: the hero <Image> carries an alt mirroring the link purpose; the wrapping <Link> carries an aria-label with the same text, and a visible focus ring on keyboard activation. The AT virtual cursor reads a single accessible name from the link element rather than a destination URL with no context, so the sighted and AT traversal match. EN 301 549 §9.1.1.1 + §9.2.4.4, mapped to WCAG 2.2 SC 1.1.1 + SC 2.4.4, are met at the source — the diff is the artifact, committed to the project repository alongside the merge.

The same source-level shape is traversed end-to-end on /integrations/github, and surfaces as a recurring fix class on /changelog — the audit-grade record procurement reads from the same source as the regulator request.