Most online shops have accessibility bugs they have never heard of. Grey text on a white background that people with low vision can’t read. Product photos with no alt text, so a screen reader just says “image”. A checkout button that can’t be reached with the keyboard. WebAIM’s 2025 Million report scanned the home pages of the top one million websites and found detectable WCAG failures on 94.8% of them, with an average of about 51 errors per page.

Until recently, most shop owners didn’t care. Two things changed that:

  • The European Accessibility Act (EAA) started applying on June 28, 2025. E-commerce services sold to consumers in the EU now have to be accessible, and each member state sets its own fines.
  • US lawsuits keep coming. Plaintiffs’ firms file thousands of website accessibility suits under the ADA every year (UsableNet counted over 4,000 in 2024), and e-commerce sites are the most common target: in UsableNet’s 2024 data, about 77% of cases hit e-commerce sites and about two-thirds hit businesses with under $25 million in revenue.

That’s the job: scan a shop’s site with free open-source tools, turn the raw results into a plain-language fix list, and then charge again to actually fix the problems. You can run your first scan within the hour.

This guide covers:

  • what the law actually requires (and what it doesn’t)
  • a tested Node.js scanner using Playwright and axe-core, the same engine inside many paid tools
  • a Gemini step that turns JSON into a report a shop owner can read, for well under a cent
  • what to charge, what it costs you, how long it takes, and the legal traps (including the one that cost a well-known AI company $1 million)

This is not legal advice. You are selling technical testing and fixes, not legal compliance. Say that in every offer.

Why shops will pay for this now

The European Accessibility Act

Directive (EU) 2019/882 covers, among other things, e-commerce services: any online shop selling to consumers in the EU, including shops based outside the EU. The technical benchmark is the harmonised standard EN 301 549, which points to WCAG 2.1 Level AA for web content.

Important details you should know before you pitch anyone:

  • Microenterprises providing services are exempt: fewer than 10 employees and annual turnover or balance sheet total of no more than €2 million. A two-person Shopify store selling to Germany is probably exempt. A 30-person brand is not.
  • Enforcement is national. Each country has its own market surveillance authority and penalties. Germany’s implementing law (BFSG), for example, allows fines of up to €10,000 for most violations and up to €100,000 in serious cases. In Germany, competitors can also send cease-and-desist letters (Abmahnungen) under unfair competition law.
  • It applies to services provided to consumers from June 28, 2025, and that includes existing websites. Service contracts agreed before that date can continue unchanged until they expire (5 years at most), and member states may allow services that use products already in use before that date to keep using them until June 28, 2030.

Don’t tell a microenterprise they are breaking the law. They usually aren’t. Sell them on the other reasons: more customers can buy, better SEO hygiene, and readiness if they grow.

US ADA lawsuits

The US has no specific federal web accessibility regulation for private businesses, but courts in several circuits treat websites of businesses open to the public as covered by Title III of the ADA. Most cases settle. Reported settlements usually range from a few thousand to tens of thousands of dollars, plus legal fees and a promise to fix the site. Any shop shipping to US customers is exposed, wherever it is based.

The $1 million warning: accessibility overlays

In January 2025 the US Federal Trade Commission announced an order against accessiBe, which sells an AI-powered accessibility widget. The FTC said accessiBe misrepresented that its tool could make any website WCAG-compliant, and that it ran reviews that looked independent but weren’t. accessiBe agreed to pay $1 million; the order was finalized in April 2025.

Two lessons for you. First, “one line of JavaScript fixes everything” doesn’t work, and plenty of shops that installed an overlay were sued anyway. That’s an opening: real fixes in the theme code. Second, never promise “compliance”. The FTC went after a company for exactly that claim.

What you are actually selling

Three products, in the order clients usually buy them:

ProductWhat the client getsSuggested priceYour time
Scan + fix listAutomated scan of 5–10 key pages (home, collection, product, cart, checkout, contact) plus a 10-minute keyboard check, delivered as a PDF/Markdown report and a CSV$79–$1491–2 hours
Fix sprintYou fix the issues in the theme: contrast, alt text, labels, focus styles, button names, page language$300–$1,200 for a small Shopify or WordPress shop4–12 hours
Monthly monitoringRe-scan every month after theme updates, new apps and new products, short change report$29–$79/month15–30 min/month

For reference, agency-grade manual WCAG audits commonly start around $1,500–$5,000 per site, and on Fiverr and Upwork you’ll find “accessibility audit” gigs from roughly $50 up to several hundred dollars. Check current listings yourself before you set prices; the point is that your entry offer sits clearly below an agency audit and is honest about being mostly automated.

What you need

ItemCostNotes
Node.js 22.12+$0nodejs.org, any OS. Current @axe-core/playwright requires Node 22.12 or newer
playwright, @axe-core/playwright$0Open source (Apache 2.0 / MPL 2.0)
@google/genai + Gemini API key$0 to startaistudio.google.com
NVDA screen reader (Windows) or VoiceOver (macOS)$0For the manual spot check
A laptop, or a small VPS for monthly re-scans$0–$6/monthHetzner / DigitalOcean entry plans
Optional: Fiverr or Upwork profile20% fee on Fiverr; Upwork charges a variable feeGood for the first reviews

Gemini pricing (check ai.google.dev/pricing, it changes): Gemini 3.5 Flash-Lite on the paid tier is $0.30 per 1M input tokens and $2.50 per 1M output tokens. A 10-page report is roughly 10,000 tokens in and 2,000 out, so about $0.008. Output includes any “thinking” tokens, so the real number can be a bit higher. The free tier is enough for practice, but Google may use free-tier prompts to improve its products, so use the paid tier for client data.

Step 1: Set up the project

mkdir a11y-scan && cd a11y-scan
npm init -y
npm install playwright @axe-core/playwright @google/genai
npx playwright install chromium

Create urls.txt with one URL per line. For practice, use the W3C’s deliberately broken demo site:

https://www.w3.org/WAI/demos/bad/before/home.html
https://www.w3.org/WAI/demos/bad/before/news.html
https://www.w3.org/WAI/demos/bad/before/tickets.html

Never run a full scan on a prospect’s site before they say yes. A single check of their public home page as a sample is fine (see Step 5).

Step 2: The scanner

Save this as scan.mjs:

import fs from "node:fs";
import { chromium } from "playwright";
import AxeBuilder from "@axe-core/playwright";

const urls = fs.readFileSync(process.argv[2] || "urls.txt", "utf8")
  .split("\n").map((s) => s.trim()).filter(Boolean);

// WCAG 2.0, 2.1 and 2.2, levels A and AA
const TAGS = ["wcag2a", "wcag2aa", "wcag21a", "wcag21aa", "wcag22aa"];
const ORDER = { critical: 0, serious: 1, moderate: 2, minor: 3 };
const DELAY_MS = 3000; // be polite: one page every 3 seconds

const browser = await chromium.launch();
const context = await browser.newContext({
  userAgent: "SolvincA11yScan/1.0 (+mailto:you@yourdomain.com)",
});
const results = [];

for (const url of urls) {
  const page = await context.newPage();
  try {
    await page.goto(url, { waitUntil: "networkidle", timeout: 45000 });
    const res = await new AxeBuilder({ page }).withTags(TAGS).analyze();
    const violations = res.violations
      .map((v) => ({
        id: v.id,
        impact: v.impact,
        help: v.help,
        helpUrl: v.helpUrl,
        count: v.nodes.length,
        examples: v.nodes.slice(0, 3).map((n) => ({
          target: n.target.join(" "),
          html: n.html.slice(0, 200),
        })),
      }))
      .sort((a, b) => ORDER[a.impact] - ORDER[b.impact]);
    results.push({ url, violations, incomplete: res.incomplete.length });
    const total = violations.reduce((s, v) => s + v.count, 0);
    console.log(`${url}: ${violations.length} rules failed, ${total} elements`);
  } catch (err) {
    results.push({ url, error: String(err) });
    console.log(`${url}: ERROR ${err.message}`);
  }
  await page.close();
  await new Promise((r) => setTimeout(r, DELAY_MS));
}
await browser.close();

fs.writeFileSync("scan.json", JSON.stringify(results, null, 2));

// Flat CSV fix list: one row per failed rule per page
const esc = (s) => `"${String(s ?? "").replaceAll("\"", "\"\"")}"`;
const rows = [["url", "impact", "rule", "elements", "problem", "example", "how_to_fix"]];
for (const p of results) {
  for (const v of p.violations || []) {
    rows.push([p.url, v.impact, v.id, v.count, v.help, v.examples[0]?.target, v.helpUrl]);
  }
}
fs.writeFileSync("fixlist.csv", rows.map((r) => r.map(esc).join(",")).join("\n"));
console.log("Wrote scan.json and fixlist.csv");

Run it:

node scan.mjs urls.txt

What the code does, and why:

  • Real browser, not raw HTML. Shopify and WordPress themes load content with JavaScript. Playwright renders the page in Chromium first, so axe-core sees what a visitor sees.
  • Only WCAG A/AA rules. That’s the level EN 301 549 and most US settlements refer to. Best-practice rules are left out so the report isn’t padded.
  • Sorted by impact. axe-core labels each failure critical, serious, moderate or minor. Clients should fix the top of the list first.
  • incomplete is counted, not hidden. These are checks axe-core couldn’t decide automatically (for example, text over a background image). They need a human look, and they are part of why your manual check matters.
  • One honest user agent and a delay. You are loading a handful of pages, not crawling. Keep it that way.

Step 3: Turn the JSON into a report a shop owner can read

Shop owners won’t read color-contrast: Elements must meet minimum color contrast ratio thresholds. Save this as report.mjs:

import fs from "node:fs";
import { GoogleGenAI } from "@google/genai";

const MODEL = process.env.REPORT_MODEL || "gemini-3.5-flash-lite";
const PRICE_IN = 0.30, PRICE_OUT = 2.50; // USD per 1M tokens, check ai.google.dev/pricing
const shop = process.argv[2] || "the client";

const scan = JSON.parse(fs.readFileSync("scan.json", "utf8"));
const slim = scan.map((p) => ({
  url: p.url, error: p.error, needs_manual_review: p.incomplete,
  issues: (p.violations || []).map((v) => ({
    rule: v.id, impact: v.impact, problem: v.help, elements: v.count,
    example: v.examples[0],
  })),
}));

const prompt = `You write accessibility reports for small online shop owners.
Shop: ${shop}. Below is automated axe-core scan data (WCAG 2.x A/AA).
Write a Markdown report with:
1. A 3-sentence summary in plain English.
2. Issues grouped by rule, most severe first. For each: what it is, who it
   affects (e.g. screen reader users, low-vision users, keyboard users),
   which pages, and how to fix it in Shopify or WordPress terms.
3. A note that automated tools find only part of WCAG issues and a manual
   check is still required.
Rules: use ONLY issues present in the data. Do not invent counts or pages.
Do not say the site is or will be "compliant". Do not give legal advice.
DATA:
${JSON.stringify(slim)}`;

const ai = new GoogleGenAI({ apiKey: process.env.GEMINI_API_KEY });
const res = await ai.models.generateContent({ model: MODEL, contents: prompt });
fs.writeFileSync("report.md", res.text);

const u = res.usageMetadata;
const out = u.candidatesTokenCount + (u.thoughtsTokenCount ?? 0); // thinking tokens are billed as output
const cost = (u.promptTokenCount * PRICE_IN + out * PRICE_OUT) / 1e6;
console.log(`report.md written. Tokens in/out: ${u.promptTokenCount}/${out}, cost ~$${cost.toFixed(4)}`);
export GEMINI_API_KEY="your-key"   # Windows PowerShell: $env:GEMINI_API_KEY="your-key"
node report.mjs "Toko Contoh"

Read the report before you send it. Compare every issue against fixlist.csv. The model is told not to invent things, but you are the one signing the deliverable. If gemini-3.5-flash-lite is retired, set REPORT_MODEL to the current Flash-Lite model and update the two prices.

Step 4: The 10-minute manual check

Deque, the company behind axe-core, published a 2021 study (based on its own audit data) saying automated testing caught roughly 57% of accessibility issues by volume. That’s a vendor figure, and by WCAG success criteria the coverage is much lower. The rest needs a person. Before delivering, on the home, product and checkout pages:

  1. Keyboard only. Unplug the mouse. Press Tab through the page. Can you see where focus is? Can you open the menu, choose a variant, add to cart and reach checkout?
  2. Zoom to 200%. Does text overlap or get cut off?
  3. Screen reader, 2 minutes. NVDA (free) on Windows or VoiceOver (Cmd+F5) on macOS. Do product images and icon buttons announce something useful?
  4. Pop-ups. Can a keyboard user close the newsletter pop-up and the cookie banner?

Add what you find to the report under “Manual findings”. This section is what separates you from a free browser extension.

Step 5: Find clients (without scare tactics)

Best channel: web agencies and freelance developers. They already have clients with Shopify and WordPress shops, and few of them want to learn WCAG. Offer white-label scans at $49–$79 each; they resell at whatever they like. One agency can send you several jobs a month.

Direct: shops that sell to the EU or US. Indonesian brands exporting fashion, coffee, skincare or crafts through their own English-language store are a good fit, and so is anyone already selling on Etsy who also runs a Shopify site. Scan only the public home page as a sample, then send a short, factual message:

Hi, I ran a free automated accessibility check on your home page. It found 23 elements failing WCAG AA, mostly low-contrast text and product images without descriptions, which affects shoppers using screen readers. Happy to send the one-page result. If useful, I do a full check of your key pages for $99, and can fix the issues in your theme.

Never write “you will be sued” or “you are breaking EU law”. You don’t know their size or legal position, it reads like a shakedown, and in the EU and Indonesia unsolicited emails to identifiable people are subject to data protection rules (GDPR, UU PDP). Contact businesses at their published business address, keep it to one follow-up, and stop when asked.

Step 6: The fixes you’ll do most often

WebAIM’s data shows the same handful of problems on almost every site. Low-contrast text alone shows up on 79.1% of home pages, and 55.5% have images with no alt text. In Shopify and WordPress, most fixes are small:

axe ruleTypical fix
color-contrastDarken text or button colours in theme settings to reach 4.5:1 (check with WebAIM’s free Contrast Checker)
image-altAdd alt text in the product media editor; decorative images get empty alt
labelGive the newsletter and search inputs a visible or screen-reader label
link-name / button-nameAdd aria-label to icon-only cart, search and social links
html-has-langSet <html lang="en"> (or id) in the theme layout file
Focus outline (manual check)Restore the focus outline your theme removed with outline: none

Re-run scan.mjs after fixing and include the before/after numbers in your delivery. Clients love seeing “61 failing elements → 4”.

Cost breakdown

ItemMonthly cost
Playwright, axe-core, NVDA$0
Gemini reports (50 reports)~$0.40
VPS for scheduled monitoring scans (optional)$0–$6
Business email on your own domain (optional)~$1 domain + $0–$7 mailbox
Fiverr fee, if you sell there20% of each order
Total fixed$0–$14

Realistic earnings and time

An illustration, not a promise. Month one for most people looks like 30–50 outreach messages, 1–2 paid scans and maybe one fix job:

  • 2 scans × $99 = $198
  • 1 fix sprint = $450 (about 8 hours)
  • ≈ $650 for 12–15 hours of paid work, plus 10+ unpaid hours of outreach

If it works out, a few months in you might have one or two agency partners sending occasional scans and a handful of monitoring clients: five clients at $49 is $245/month in recurring revenue, with fix sprints on top when they come. Some months will have no fix work. Treat any total as something you build client by client, not a target. Many people won’t get past the outreach stage. The monitoring revenue is the semi-passive part: a cron job re-runs scan.mjs and report.mjs, and you spend 15 minutes reviewing each report.

Risks

Liability for claims. If you write “your site is now WCAG compliant” and the client is sued, you are part of the story. Write “no automated failures remain on the pages tested; manual issues found and fixed are listed below.” Add a line to every invoice: “Technical testing only, not legal advice or a compliance certification.”

Overlays and shortcuts. Don’t resell accessibility widgets as a fix. The accessiBe FTC order is public, and disability advocates actively flag overlay sites.

Breaking the client’s store. Theme edits can break checkout or apps. Work in a duplicated theme (Shopify: Online Store → Themes → Duplicate) or a staging copy, and publish only after the client checks it.

Scanning without permission. Loading one public page like a normal visitor is fine. Running repeated scans across a whole site, or anything behind a login, before the client agrees is not. Ask first.

Platform risk. Fiverr can lower your gig ranking or suspend accounts over off-platform contact. Keep Fiverr clients on Fiverr.

Reality check

The scan alone is a commodity. Free browser extensions (axe DevTools, WAVE) do the same scan. Clients pay for the plain-language report, the manual check, and above all the fixes. If you only sell scans, you will compete with “free”.

Microenterprises mostly don’t have to comply with the EAA. If your pitch is legal fear, half your leads will correctly ignore you. Pitch more buyers, fewer abandoned checkouts and fewer legal headaches later.

Local demand is weak. Indonesian shops selling only domestically have little legal pressure today. Target exporters, agencies and English-language stores.

Zero findings doesn’t mean accessible. Canvas sliders, video without captions, confusing checkout flows: axe-core can’t judge most of these. Always do Step 4.

Third-party widgets. Many failures come from apps (reviews, chat, pop-ups) you can’t edit. Report them, tell the client which vendor to contact, and exclude them from your fix price.

Learn the basics first. Read the W3C’s free WCAG 2 at a Glance and Easy Checks pages before your first paid job. It takes an evening and stops you embarrassing yourself on a sales call.

Final checklist

  • scan.mjs and report.mjs run on the W3C demo site
  • Manual keyboard, zoom and screen reader check practiced on one real shop
  • Offer written as “testing and fixes”, never “compliance” or “guaranteed”
  • Disclaimer on every report and invoice
  • Theme fixes done in a duplicate theme, before/after scan included
  • 3 agencies and 20 exporting shops contacted, politely, with one follow-up

Run the scanner on your own site or a friend’s shop today. Fix what it finds, keep the before/after numbers, and use that as your first case study.