sota.io
Join the waitlist
2026-09-06·13 min read·sota.io team

Carrefour Lost an Accessibility Case Over '71% Compliant' — Why Your Severity-Based CI Gate Has the Same Flaw

Carrefour Lost an Accessibility Case Over '71% Compliant' — Why Your Severity-Based CI Gate Has the Same Flaw

On 4 June 2026, the Tribunal judiciaire de Caen ordered Carrefour to make carrefour.fr and its mobile app fully accessible to blind and visually impaired users within six months, under a €500-per-day penalty, plus €10,000 in damages (faire-face.fr, 11 June 2026). The case was brought by two disability-rights associations, apiDV and Droit Pluriel, backed by the legal collective Intérêt à Agir, after roughly ten months of trying to get the retailer to fix known accessibility gaps (droitpluriel.fr).

Carrefour's defense was that it already met 71% of the applicable RGAA criteria. The court rejected it outright: "Le site de e-commerce concerné ne peut pas être seulement un peu accessible, il doit l'être totalement" — the e-commerce site in question cannot be only somewhat accessible, it must be totally accessible (ecommercemag.fr). French digital accessibility law, the court held, is "une obligation de résultat exigeant le respect de 100 % des critères du RGAA" — an obligation of result requiring compliance with 100% of the applicable RGAA criteria, not an obligation to make a good-faith effort.

That distinction — result vs. effort, 100% vs. "mostly" — is worth sitting with if you ship software into the EU, because it is also, almost word for word, the design of the accessibility gate most teams run in CI today.

What the ruling actually covers

Two independent legal layers apply here, and it's worth being precise about which is which:

LayerBasisWhat it covers
French national lawArticle 47 of Loi n°2005-102 (11 Feb 2005), as amendedPublic bodies + private companies above the revenue threshold
French national lawArticle L.412-13 of the Code de la consommation, added by Ordonnance n°2023-859 (transposing Directive (EU) 2019/882)Economic operators providing in-scope B2C services (e-commerce, banking, transport, telephony, audiovisual media), with a microenterprise exemption under €2M revenue/10 employees
EU lawDirective (EU) 2019/882, the European Accessibility Act (EAA)Sets the substantive accessibility requirements (Art.4) and obligations for service providers (Art.13) that the French transposition implements

Carrefour was cited on both French-law grounds (faire-face.fr) — no ambiguity that carrefour.fr, as an e-commerce operator well above both the €2M and €250M thresholds, is squarely in scope. Article L.412-13 itself just states the obligation ("economic operators shall place on the market products and provide services that comply with the accessibility requirements") (Légifrance, Art. L.412-13); the technical substance is delegated to Décret n°2023-931 (9 October 2023), which designates Arcom as the control authority for digital services and RGAA 4.1.2 as the applicable technical reference (access42.net).

At the EU level, this all sits under the EAA: Article 4 sets the accessibility requirements, Article 13 the service-provider obligations, Article 22 exempts genuine microenterprises, and Article 19/24 give market surveillance authorities inspection and enforcement powers. Article 2(2)(f) of the Directive explicitly lists e-commerce services in scope (eur-lex.europa.eu, Directive (EU) 2019/882). None of that is new — the EAA has applied to new products and services since 28 June 2025. What's new is a French court putting a number on what "compliant" means, and rejecting a specific number a defendant actually offered.

A caveat worth stating plainly: several legal commentary sites are already framing this as "the first EAA-regime ruling to explicitly cover a mobile app" and "the first national-transposition ruling against a retailer." Multiple secondary sources repeat that framing, but it traces back to legal-commentary synthesis rather than a court press release, and we could not independently verify "first" against an authoritative EU-wide case registry in the time available. Treat the superlative with the skepticism it deserves — the substance of the ruling (100%-of-criteria standard, mobile app explicitly in scope, six-month remediation clock) is independently confirmed across multiple outlets and is what matters for your engineering decisions either way.

The number that should worry you: 106, not "critical vs. minor"

RGAA 4.1.2 — the French national reference, built on WCAG 2.1 AA — breaks accessibility down into 106 operational criteria and 258 tests across 13 themes (numerique.gouv.fr / DesignGouv). A criterion either passes or it doesn't. There's no severity dial on a criterion. "Non-text content has an alternative" is not 71% true.

Compare that to how most teams actually gate accessibility in CI/CD today. Automated scanners like axe-core report violations, and violations carry an impact label — critical, serious, moderate, or minor. The overwhelmingly common pattern (recommended in plenty of accessibility guides, including our own EAA testing-stack guide) is to block the merge on critical/serious and downgrade moderate/minor to warnings that don't stop delivery. It's sound engineering advice for shipping velocity — most teams cannot realistically hold every merge hostage to every automated finding, and axe-core's own severity classification is a reasonable, widely-adopted triage heuristic.

But look at what that gate structure actually asserts: "our service is accessible enough." Not "100% of applicable criteria pass" — "the parts we decided matter most pass, and we're tracking the rest as backlog." That is, functionally, the same posture the Caen court just rejected. A codebase can pass a critical/serious-only gate indefinitely while carrying dozens of open moderate/minor findings that map directly onto RGAA criteria a court would count as unmet. Your CI pipeline would say green. An RGAA audit — the kind Arcom or a claimant's expert report will run — would not.

There's a second, structural gap underneath the severity problem: axe-core's own rule set only covers automated testing of roughly 57% of WCAG 2.1 AA success criteria; the rest require manual review (keyboard traversal, screen-reader semantics, meaningful alt text, cognitive load) (per our existing testing-stack guide's own coverage figure). A severity-only gate has literally nothing to say about the other ~43% of criteria. "0 critical axe violations" and "100% of applicable RGAA criteria met" are not the same claim, and only one of them is a defense in front of a judge who has just said the second one is the only one that counts.

Building a criteria-coverage gate instead of a severity gate

The fix isn't to throw out axe-core or Pa11y — it's to stop gating on impact and start gating on criterion coverage, using the WCAG success-criterion tags that axe-core already attaches to every rule (wcag111, wcag143, wcag412, etc.). Below is a script that ingests standard axe-core JSON output and fails the build if any applicable, testable criterion has an open violation — regardless of whether axe labeled it critical or minor.

// rgaa-criteria-gate.mjs
// Consumes axe-core's standard JSON output (results.violations[]).
// Fails the build on ANY WCAG/RGAA criterion with an unresolved finding,
// instead of filtering by axe's impact label.

import { readFileSync } from "node:fs";

// Criteria your team has explicitly reviewed and confirmed are Not Applicable
// for this page (e.g. no video content -> 1.2.x criteria don't apply).
// This list must be maintained deliberately, not used to silence findings.
const NOT_APPLICABLE = new Set(process.env.RGAA_NA_CRITERIA?.split(",") ?? []);

function extractWcagTags(tags) {
  return tags.filter((t) => /^wcag\d{3,4}$/.test(t));
}

function run(axeJsonPath) {
  const results = JSON.parse(readFileSync(axeJsonPath, "utf8"));
  const failingCriteria = new Map(); // wcagTag -> [{ruleId, nodeCount}]

  for (const violation of results.violations ?? []) {
    const wcagTags = extractWcagTags(violation.tags);
    for (const tag of wcagTags) {
      if (NOT_APPLICABLE.has(tag)) continue;
      const entry = failingCriteria.get(tag) ?? [];
      entry.push({ ruleId: violation.id, nodeCount: violation.nodes.length });
      // Note: no filtering on violation.impact here. A "minor" axe finding
      // still means the mapped criterion has NOT been met.
      failingCriteria.set(tag, entry);
    }
  }

  const testedCriteria = new Set(
    [...(results.passes ?? []), ...(results.violations ?? [])]
      .flatMap((r) => extractWcagTags(r.tags))
  );
  const coveredPassing = [...testedCriteria].filter((t) => !failingCriteria.has(t));

  console.log(`Automatable WCAG/RGAA criteria checked: ${testedCriteria.size}`);
  console.log(`Passing: ${coveredPassing.length}  Failing: ${failingCriteria.size}`);

  if (failingCriteria.size > 0) {
    console.error("\nCRITERIA WITH OPEN FINDINGS (any severity):");
    for (const [tag, hits] of failingCriteria) {
      console.error(`  ${tag}: ${hits.map((h) => `${h.ruleId} (${h.nodeCount} nodes)`).join(", ")}`);
    }
    console.error(
      "\nBuild blocked: 100% of automatable criteria must pass. " +
      "If a criterion genuinely does not apply to this page, add it to RGAA_NA_CRITERIA " +
      "with a documented justification — do not silence it by severity."
    );
    process.exit(1);
  }
  console.log("All automatable criteria pass. Remember: this covers ~57% of WCAG 2.1 AA " +
    "success criteria. The remaining ~43% require the manual audit log below.");
}

run(process.argv[2] ?? "axe-results.json");

The key design decision: there is no impact filter anywhere in this script. A minor-labeled contrast issue on a single button blocks the build exactly like a critical missing-label issue on a checkout form, because both map to an RGAA criterion that either passes or fails — courts don't grade on a curve, and axe's severity label was never meant to be a legal compliance signal in the first place (it's a UX-triage signal, and a good one — just not for this purpose).

Tracking the ~43% no scanner can see

A criteria-coverage gate on automated tooling still only gets you to the criteria axe-core can test. The RGAA's other criteria — meaningful alt text, logical reading order, keyboard operability of custom widgets, whether a screen reader announces a live region sensibly — need a human, and a paper trail a market surveillance authority (or a claimant's expert) can inspect. A minimal structured log, checked into the repo alongside the code it certifies, closes that gap:

# rgaa-manual-audit.yml — one entry per manually-verified criterion, per page/component
- criterion: "RGAA 1.1 — Every image has a text alternative"
  wcag_ref: "1.1.1"
  page_or_component: "checkout/PaymentForm"
  verified_by: "a.dupont"
  verified_on: "2026-08-14"
  method: "NVDA 2026.1 + manual reading-order check"
  result: "pass"
  evidence: "docs/accessibility/audit-2026-08-14-checkout.pdf"

- criterion: "RGAA 12.7 — Skip link to main content"
  wcag_ref: "2.4.1"
  page_or_component: "global/Header"
  verified_by: "a.dupont"
  verified_on: "2026-08-14"
  method: "Keyboard-only navigation, Chrome + VoiceOver"
  result: "fail"
  ticket: "A11Y-412"
  remediation_deadline: "2026-09-20"

This isn't extra bureaucracy for its own sake — it's the exact artifact the EAA already expects. Article 13 requires service providers to draw up information demonstrating conformity and to maintain procedures to keep the service in conformity over time; Article 19 gives market surveillance authorities the power to inspect and demand that documentation. A green CI badge with no manual-audit trail behind it answers neither obligation.

The mobile app is not an afterthought

The Caen ruling explicitly covers the Carrefour mobile app, not just carrefour.fr. If your CI pipeline's accessibility gate only runs against the web build, you have the exact asymmetry a plaintiff would look for. RGAA's technical reference (via EN 301 549) maps web content to Chapter 9 and native software/mobile apps to Chapter 11 — a genuinely different rule set, not a subset of the web one. Native accessibility gating needs native tooling:

None of these tools speak "RGAA" natively; the mapping work (which native accessibility property satisfies which RGAA criterion) is the same manual-audit-log exercise as above, just for the mobile surface.

What the six-month clock actually means for an engineering team

Carrefour has until roughly early December 2026 to comply, or the astreinte starts accruing per day the violation continues (faire-face.fr). If you're an EU-facing e-commerce, banking, transport, telephony, or audiovisual-media operator reading this because your legal team just forwarded you the same headline, six months is not a lot of runway to go from "we track critical/serious axe findings" to "100% of applicable RGAA criteria, evidenced":

  1. Week 1–2: Run a full RGAA 4.1.2 audit (106 criteria) against your production pages/screens — not a scanner run, an actual audit, ideally by someone RGAA-certified or a specialized firm. This is your baseline gap list, not your CI gate output.
  2. Week 2–4: Triage the gap list by criterion, not by axe severity. A "minor" finding blocking a whole criterion moves ahead of a "critical" finding that's one of several contributing to an already-partially-met criterion.
  3. Month 2–4: Remediate, with the criteria-coverage gate above running in CI from day one of this phase so you can't regress a criterion you've just fixed.
  4. Month 4–6: Manual audit pass on the ~43% automated tools can't reach, logged per the schema above. Publish or update your Accessibility Statement (required under most national EAA transpositions) reflecting the actual, current criterion coverage — not a rounded-up percentage.

One nuance worth flagging honestly: the EAA allows a transition period for existing services (provided without substantial modification before 28 June 2025) running until 28 June 2030, separate from the general 2025 start date for new products and services. Whether Carrefour's site benefited from — or was found to have forfeited by ongoing modification — that transition isn't clear from the coverage we reviewed, and we're not going to guess at a court's reasoning we haven't read in full. Don't assume a "legacy service" label protects you either; if a French court will reject "71%," it's a reasonable bet the transition argument gets similarly narrow scrutiny.

The takeaway

"Obligation de résultat" is a specific French legal concept, but the engineering lesson travels: a CI gate that only stops the worst accessibility regressions is a gate designed to answer "did we make anything worse," not "are we compliant." Those are different questions, and as of 4 June 2026, at least one French court has made clear which one it's asking.


Sources: faire-face.fr, 11 June 2026 · ecommercemag.fr · droitpluriel.fr · Légifrance, Art. L.412-13 Code de la consommation · access42.net — legal framework summary · numerique.gouv.fr / DesignGouv — RGAA structure · eur-lex.europa.eu, Directive (EU) 2019/882

See also: EAA Testing & Audit Guide 2026 for the full automated + manual testing stack this gate builds on · EAA Mobile App Accessibility Checklist for a deeper native-platform walkthrough · European Accessibility Act 2019/882 SaaS Compliance Guide for the baseline Art.4/13/22 obligations referenced above.

EU-Native Hosting

Ready to move to EU-sovereign infrastructure?

sota.io is a German-hosted PaaS — no CLOUD Act exposure, no US jurisdiction, full GDPR compliance by design. Deploy your first app in minutes.