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 analtattribute; 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.
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 analtmirroring the link purpose; the wrapping<Link>carries anaria-labelwith 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.