Stripe — marketing site review — sample audit, scored 6.3/10

← gallery
6.3/10
Open ↗

About this audit

stripe.com scored 6.3/10

Excellent platform engineering — sandboxed third parties, strict email auth, clean AI-crawler surface — undercut by two keyboard blockers sitting on the sales-lead path and a marketing template that emits 153 <h1> elements on the pricing page.

This is a full example audit — 15 evidence-led findings across accessibility, performance, SEO, structured data, privacy and security, each with a measurement, a screenshot and a concrete fix. The headline issue: Contact-sales form: 43 invisible controls still accept keyboard focus — the 8th Tab lands on a hidden 'Back' button.

What we found

  1. criticalContact-sales form: 43 invisible controls still accept keyboard focus — the 8th Tab lands on a hidden 'Back' button

    /contact/sales renders every step of the sales funnel into the DOM at once and hides the inactive ones with opacity: 0; pointer-events: none — nothing else. We counted 11 .ContactSalesFormStep.isHidden containers, every one of them computing to visibility: visible, display: block, with no aria-hidden, no inert and no h

  2. criticalFive buttons across four pages have no accessible name — including the call-booking date arrows

    axe-core reports button-name (critical impact, WCAG 2.0 A, criterion 4.1.2 Name, Role, Value) on 5 elements across 3 audited pages, and we confirmed a sixth instance of the same component live on the English homepage. Each is an icon-only <button> with no aria-label, no title, no text node and no <title> inside its SVG

  3. warning/pricing ships 153 <h1> elements against 4 <h2> — the document outline carries no hierarchy

    Fetching the server-rendered HTML (so this is exactly what a crawler and a screen reader receive), /pricing contains 153 <h1> elements, 4 <h2> and 6 <h3>. /atlas has 36 H1s / 3 H2s, /contact/sales 15 H1s / 4 H2s. The cause is a shared component: Copy__title renders as <h1> regardless of nesting depth — on /pricing alon

  4. warningAtlas: the brand orange puts the 'Start your company' CTA at 2.68:1 — 12 elements below AA

    The Atlas accent #ff7600 fails WCAG AA everywhere it carries text. axe reports 12 failing elements on /atlas (color-contrast, serious, WCAG 2.1 AA, criterion 1.4.3), and a live scan of the page found 41 elements rendering in that colour. The one that matters commercially is the page's primary call to action. Start your

  5. warning98 further contrast failures — 74 of them on the price fine print at /pricing

    Beyond Atlas, axe reports 98 more elements below WCAG AA (color-contrast, serious, criterion 1.4.3): 74 on /pricing, 12 on / and 12 on /br. On /pricing the failures land on the qualifiers that change what a customer actually pays. #707f98 on #fafbfd at 3.91:1 covers + 0.5% for manually entered cards, + 1.5% for interna

  6. warning11 of 23 pricing tooltips wrap a link inside a button — the subscription terms are unreachable by keyboard

    axe reports nested-interactive (serious, WCAG 2.0 A, criterion 4.1.2) on 11 elements of /pricing. We reproduced it live: of the page's 23 .TooltipButton controls, 11 contain an <a href> inside the <button>. The nested links are not decorative. They point at support.stripe.com/questions/subscription-and-cancellation-ter

  7. warningNo <main> landmark on 3 of 6 pages, and no skip link on any of them

    Checking the server-rendered HTML of all six pages: /pricing, /atlas and /contact/sales contain zero <main> elements and zero [role="main"]. /, /br and /personalize each have exactly one. Separately, none of the six pages ships a skip link — no anchor matching "skip to" or "skip nav" exists in any of them. We confirmed

  8. warningSix remaining WCAG violations: unreachable scroll regions, a malformed list, a focusable aria-hidden figure

    Four further axe rules fire across the audited pages, all serious impact, 6 failing elements in total. Each is small and independently fixable. scrollable-region-focusable (WCAG 2.1.1 / 2.1.3) hits .bento-dialog-graphics on the homepage at mobile and .TestimonialCarousel__track on /atlas at all three viewports: both sc

  9. warningHomepage LCP 4,759 ms and /br 5,908 ms on mobile — both roughly double the 2,500 ms target

    Lighthouse (mobile, lab) scores the homepage at 46 and /br at 38. Largest Contentful Paint is 4,759 ms on / and 5,908 ms on /br, against a 2,500 ms "good" threshold; First Contentful Paint is 3,709 ms and 5,458 ms. Total Blocking Time is 1,139 ms and 1,162 ms — main-thread work heavy enough that taps go unanswered — an

  10. warning273 KiB of unused JavaScript on the homepage — two chunks account for 191,889 bytes of it

    Lighthouse's unused-javascript audit reports "Est savings of 273 KiB" on the homepage (279,335 bytes) and "Est savings of 291 KiB" on /br (298,140 bytes). Total page weight is 2,714 KiB and 2,828 KiB respectively. Two files carry most of it. pages/_app-7923da34be0042d5.js is 359,126 bytes with 75,424 unused; pages/inde

  11. mobile/pricing runs 34,849 px tall at 375 px — the first per-product price sits 3,724 px down

    Measured live at a 375 px viewport, /pricing is 34,849 px tall — 42.9 phone screens — and carries 6,204 DOM elements. The summary cards (Standard at 2.9% + 30¢, and Custom) are above the fold and read well. But the first per-product price card, #payments, begins at y = 3,724 px: about 4.6 screens of scrolling. #link fo

  12. warning/pricing is the only page of six whose title omits the brand — 14 characters, 'Pricing & Fees'

    Five of the six audited pages carry a branded title: Stripe | Financial Infrastructure to Grow Your Revenue (54 chars), Stripe Atlas | Incorporate your startup in Delaware: C corp or LLC (66), Stripe | Contact our Sales Team, Stripe | Personalize, and the pt-BR equivalent. /pricing is titled Pricing & Fees — 14 charact

  13. warning/atlas and /contact/sales ship zero JSON-LD, and no page carries a BreadcrumbList

    Structured-data coverage splits along the same template line as everything else. / and /br each ship one valid block containing a WebSite + Organization @graph — and it is genuinely thorough: legalName, both founders as Person, a contactPoint with five availableLanguage values, six office Place entries with full Postal

  14. warning8 stylesheet fetches fail CORS on every Next.js page load — the CDN adds the header for /chunks/ but not /css/

    Every load of / produces 8 identical CORS failures, one per stylesheet, on mobile, tablet and desktop alike: Access to XMLHttpRequest at 'https://b.stripecdn.com/mkt-ssr-statics/assets/_next/static/css/9d3a49263f73db6f.css' from origin 'https://stripe.com' has been blocked by CORS policy: No 'Access-Control-Allow-Origi

  15. warningYour tag-sandbox CSP allowlists *.google.com but not Google's country domains — remarketing pixels are blocked outside .com regions

    Stripe runs all third-party marketing tags inside a CSP-sandboxed iframe at b.stripecdn.com/stripethirdparty-srv/assets/v33.1/GoogleTagManager.html. That is a good design — it is why a scan of the main document finds zero trackers and only two script hosts. But that sandbox's own img-src directive allowlists *.google.c