Download index.html from getreadystack/accessibility-statement-lint: direct link, hf CLI and curl.
- Browser
- Download file 46.6 kB
-
https://huggingface.co/spaces/getreadystack/accessibility-statement-lint/resolve/main/index.html
- Command line
-
hf download hf://spaces/getreadystack/accessibility-statement-lint/index.html
-
curl -L -o index.html https://huggingface.co/spaces/getreadystack/accessibility-statement-lint/resolve/main/index.html
46.6 kB
| <html lang="en"> | |
| <head> | |
| <meta charset="utf-8"> | |
| <meta name="viewport" content="width=device-width,initial-scale=1"> | |
| <title>Website Accessibility Statement template — check your accessibility</title> | |
| <meta name="description" content="This browser tool checks a published accessibility statement against the EU model statement, including its compliance wording, dates, feedback, and enforcement sections."> | |
| <style> | |
| :root{color-scheme:light}body{margin:0;font:15px/1.55 system-ui,-apple-system,"Segoe UI",sans-serif;background:#faf9f5;color:#1a1512} | |
| .wrap{max-width:960px;margin:0 auto;padding:28px 18px 60px}h1{font-size:26px;margin:0 0 6px}.lede{color:#4a403a;margin:0 0 16px} | |
| textarea{width:100%;min-height:260px;font:13px/1.45 ui-monospace,Menlo,Consolas,monospace;padding:12px;border:1px solid #d9d0c4;border-radius:8px;box-sizing:border-box} | |
| .bar{display:flex;gap:10px;align-items:center;margin:12px 0}button{font:600 15px system-ui;padding:10px 18px;border:0;border-radius:8px;background:#1a1512;color:#fff;cursor:pointer} | |
| input[type=date]{font:14px system-ui;padding:8px;border:1px solid #d9d0c4;border-radius:6px} | |
| table{border-collapse:collapse;width:100%;font-size:14px;margin-top:10px}th,td{text-align:left;padding:8px 10px;border-bottom:1px solid #eee;vertical-align:top}th{font-size:12px;letter-spacing:.06em;text-transform:uppercase;color:#7a6e65} | |
| .sev.error{color:#a8551f;font-weight:700}.sev.warn,.sev.warning{color:#96691f}.ok{color:#2a6446;font-weight:600}.sum{font-weight:600} | |
| .note{font-size:13px;color:#7a6e65;margin-top:18px}#paid{margin-top:22px;padding:14px 16px;border:1px solid #e2d8cb;border-radius:10px;background:#fffdfa} | |
| </style> | |
| <script data-grow-initializer="">!(function(){window.growMe||((window.growMe=function(e){window.growMe._.push(e);}),(window.growMe._=[]));var e=document.createElement("script");(e.type="text/javascript"),(e.src="https://faves.grow.me/main.js"),(e.defer=!0),e.setAttribute("data-grow-faves-site-id","U2l0ZTphZjljMzgyMC0yMmU5LTRhYjgtYTIxNS03OTRhMGY1N2ExYzM=");var t=document.getElementsByTagName("script")[0];t.parentNode.insertBefore(e,t);})();</script> | |
| <!--yj-canon-s101--><link rel="canonical" href="https://getreadystack.com/tools/accessibility-statement-lint"></head> | |
| <body> | |
| <div class="wrap"> | |
| <h1>Website Accessibility Statement template — check your accessibility</h1> | |
| <p class="lede">Lints a published accessibility statement against the EU model statement: compliance-status wording, non-accessible content with reasons, preparation and last-review dates, feedback and enforcement sections. Runs entirely in your browser — nothing is uploaded.</p> | |
| <textarea id="src" spellcheck="false"># Accessibility Statement | |
| [insert your organisation name] is committed to making its digital services accessible to | |
| everyone, in line with current national accessibility legislation and with good practice | |
| in the sector. | |
| This statement applies to the website at https://www.example-agency.test/ and was produced | |
| as part of our quality programme. | |
| ## Compliance status | |
| This website is partially compliant with EN 301 549 V2.1.2. Some sections of the site are | |
| not compliant and work to fix them is ongoing. | |
| ## Non-accessible content | |
| - Older PDF documents in the publications library are not tagged. | |
| - Video recordings of public meetings do not carry captions. Adding captions to the full | |
| back catalogue would be a disproportionate burden for a team of our size, so the back | |
| catalogue stays as it is for now. | |
| - Some form fields are missing labels. | |
| ## Preparation of this statement | |
| This statement was prepared on 3 June 2024 following a third-party review of the site. | |
| Last reviewed: 12 March 2024. | |
| ## Feedback | |
| If you find a problem, write to webteam@example-agency.test and we will look into it.</textarea> | |
| <div class="bar"><button id="go">Check</button><label>as of <input type="date" id="today"></label></div> | |
| <div id="out" aria-live="polite"></div> | |
| <div id="paid"><strong>Free here:</strong> Check the accessibility statement open in the editor and name every missing section. <strong>In the editor:</strong> Sweep every locale and sub-site statement in the workspace and write one dated report with a per-file verdict you can hand to the monitoring body — that part asks for a licence key.</div> | |
| <p class="note">Same engine as the VS Code extension, byte for byte.</p> | |
| </div> | |
| <script>var ASTMT_RULES = [ | |
| {"id":"scope_commitment","sev":"high","kind":"require","pat":"is committed to making","flags":"i", | |
| "msg":"No commitment sentence. The EU model statement opens by naming the body and stating it is committed to making the site accessible."}, | |
| {"id":"law_cited","sev":"high","kind":"require","pat":"2016/2102|Web Accessibility Directive|2019/882|European Accessibility Act","flags":"i", | |
| "msg":"No legal basis cited. Name Directive (EU) 2016/2102 (or its national transposition), or Directive (EU) 2019/882 for a private-sector service."}, | |
| {"id":"scope_named","sev":"med","kind":"require","pat":"(applies to|covers)[^\\n]{0,80}(website|web site|app|application|service)|https?://","flags":"i", | |
| "msg":"The statement never says which website, app or service it covers. One statement per covered site is the model's unit."}, | |
| {"id":"compliance_status","sev":"high","kind":"require","pat":"(fully|partially|not) compliant","flags":"i", | |
| "msg":"No compliance status. The model allows exactly three wordings: fully compliant, partially compliant, or not compliant."}, | |
| {"id":"status_single","sev":"high","kind":"exclusive","alts":["fully compliant","partially compliant","not compliant"],"flags":"i", | |
| "msg":"Two different compliance statuses appear in one statement. A monitoring body reads the status as a single declared value."}, | |
| {"id":"standard_cited","sev":"high","kind":"require","pat":"EN 301\\s?549","flags":"i", | |
| "msg":"EN 301 549 is not named. The status has to be measured against the harmonised standard, not against WCAG alone."}, | |
| {"id":"standard_version_stale","sev":"med","kind":"forbid","pat":"EN 301\\s?549[ ,]*(V|version )?(1\\.|2\\.|3\\.1)","flags":"i", | |
| "msg":"An EN 301 549 version older than V3.2.1 is cited. V3.2.1 is the version listed in the Official Journal for the Web Accessibility Directive."}, | |
| {"id":"nonaccessible_section","sev":"high","kind":"require","when":"partially compliant|not compliant","pat":"non-?accessible content","flags":"i", | |
| "msg":"Status is partially or not compliant, but there is no Non-accessible content section. That section is what makes the status verifiable."}, | |
| {"id":"reason_category","sev":"high","kind":"require","when":"non-?accessible content","pat":"non-?compliance|disproportionate burden|not within the scope","flags":"i", | |
| "msg":"Non-accessible content is listed with no reason category. The model has exactly three: non-compliance, disproportionate burden, and content outside the scope of the legislation."}, | |
| {"id":"burden_assessment","sev":"high","kind":"require","when":"disproportionate burden","pat":"assessment","flags":"i", | |
| "msg":"Disproportionate burden is claimed with no assessment behind it. Article 5 of Directive (EU) 2016/2102 allows the claim only after an assessment of the burden."}, | |
| {"id":"burden_alternative","sev":"high","kind":"require","when":"disproportionate burden","pat":"accessible alternative|alternative format|alternative is available","flags":"i", | |
| "msg":"Disproportionate burden is claimed with no accessible alternative offered for the excluded content."}, | |
| {"id":"prepared_date","sev":"high","kind":"date_present","label":"prepar(ed|ation)","flags":"i", | |
| "msg":"No date of preparation. The model requires the date the statement was prepared or last fully revised."}, | |
| {"id":"prepared_date_future","sev":"med","kind":"date_future","label":"prepar(ed|ation)","flags":"i", | |
| "msg":"The date of preparation is in the future, so the statement cannot have been prepared yet."}, | |
| {"id":"method_named","sev":"med","kind":"require","when":"prepar(ed|ation)","pat":"self[- ]assessment|third[- ]party|external (audit|evaluation|review)|expert (review|evaluation)","flags":"i", | |
| "msg":"The preparation method is missing. Say whether this was a self-assessment or an evaluation by a third party."}, | |
| {"id":"review_date","sev":"high","kind":"date_present","label":"last review(ed)?|reviewed on","flags":"i", | |
| "msg":"No date of last review. The statement has to carry the date it was last checked, separate from the date it was written."}, | |
| {"id":"review_stale","sev":"high","kind":"date_max_age","label":"last review(ed)?|reviewed on","months":12,"flags":"i", | |
| "msg":"The last review is more than 12 months old. Directive (EU) 2016/2102 Article 7(1) requires the statement to be kept up to date, and monitoring bodies sample by date."}, | |
| {"id":"feedback_contact","sev":"high","kind":"require","pat":"mailto:|[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}|feedback form|contact form","flags":"i", | |
| "msg":"No feedback mechanism. A reader must be able to report an accessibility barrier and request content in an accessible form."}, | |
| {"id":"enforcement_procedure","sev":"high","kind":"require","pat":"enforcement (procedure|body)|ombud|complaint","flags":"i", | |
| "msg":"No enforcement procedure. The statement must point to the body a reader can escalate to when the feedback goes unanswered."}, | |
| {"id":"placeholder_left","sev":"high","kind":"forbid","pat":"\\[(insert|name of|your |organisation|organization|url|date|add )[^\\]]{0,60}\\]|XX/XX|\\bTBD\\b|lorem ipsum","flags":"i", | |
| "msg":"A template placeholder was never filled in. This is the most common defect in generated statements and it is visible to the public."} | |
| ] | |
| ;</script> | |
| <script>// Accessibility Statement Lint — one brain, used by the extension and by the one-page web tool. | |
| ; | |
| var RULES = (typeof module !== 'undefined' && module.exports) | |
| ? require('./rules.json') | |
| : (typeof window !== 'undefined' ? window.ASTMT_RULES : []); | |
| var MONTHS = ['january','february','march','april','may','june','july','august','september', | |
| 'october','november','december']; | |
| function lineOf(text, index) { | |
| return text.slice(0, index).split('\n').length; | |
| } | |
| // "2026-09-16" | "16 September 2026" | "September 16, 2026" | "16/09/2026" | "16.09.2026" | |
| function findDate(line) { | |
| var m = line.match(/(\d{4})-(\d{2})-(\d{2})/); | |
| if (m) return { y: +m[1], m: +m[2], d: +m[3] }; | |
| m = line.match(/(\d{1,2})[ .]+([A-Za-z]{3,9})[ ,.]+(\d{4})/); | |
| if (m) { | |
| var i = MONTHS.indexOf(m[2].toLowerCase()); | |
| if (i < 0) i = MONTHS.map(function (x) { return x.slice(0, 3); }).indexOf(m[2].toLowerCase().slice(0, 3)); | |
| if (i >= 0) return { y: +m[3], m: i + 1, d: +m[1] }; | |
| } | |
| m = line.match(/([A-Za-z]{3,9})[ ]+(\d{1,2})[ ,]+(\d{4})/); | |
| if (m) { | |
| var j = MONTHS.map(function (x) { return x.slice(0, 3); }).indexOf(m[1].toLowerCase().slice(0, 3)); | |
| if (j >= 0) return { y: +m[3], m: j + 1, d: +m[2] }; | |
| } | |
| m = line.match(/(\d{1,2})[\/.](\d{1,2})[\/.](\d{4})/); | |
| if (m) return { y: +m[3], m: +m[2], d: +m[1] }; | |
| return null; | |
| } | |
| function toNum(d) { return d.y * 10000 + d.m * 100 + d.d; } | |
| function monthsBetween(a, b) { return (b.y - a.y) * 12 + (b.m - a.m) - (b.d < a.d ? 1 : 0); } | |
| // A labelled date lives on the label line or within the three lines that follow it. | |
| function labelledDate(text, labelRe) { | |
| var lines = text.split('\n'); | |
| for (var i = 0; i < lines.length; i++) { | |
| if (!labelRe.test(lines[i])) continue; | |
| for (var k = i; k <= i + 3 && k < lines.length; k++) { | |
| var d = findDate(lines[k]); | |
| if (d) return { date: d, line: k + 1 }; | |
| } | |
| return { date: null, line: i + 1 }; | |
| } | |
| return null; | |
| } | |
| function parseToday(opts) { | |
| var s = (opts && opts.today) || ''; | |
| var d = findDate(String(s)); | |
| if (d) return d; | |
| var n = new Date(); | |
| return { y: n.getFullYear(), m: n.getMonth() + 1, d: n.getDate() }; | |
| } | |
| function check(text, opts) { | |
| text = String(text == null ? '' : text); | |
| var today = parseToday(opts); | |
| var findings = []; | |
| var add = function (r, line, extra) { | |
| findings.push({ check: r.id, sev: r.sev, msg: extra ? r.msg + ' ' + extra : r.msg, line: line || 1 }); | |
| }; | |
| for (var i = 0; i < RULES.length; i++) { | |
| var r = RULES[i]; | |
| var flags = r.flags || 'i'; | |
| if (r.when && !new RegExp(r.when, flags).test(text)) continue; | |
| if (r.kind === 'require') { | |
| var re = new RegExp(r.pat, flags); | |
| if (!re.test(text)) { | |
| var anchor = r.when ? text.search(new RegExp(r.when, flags)) : -1; | |
| add(r, anchor >= 0 ? lineOf(text, anchor) : 1); | |
| } | |
| } else if (r.kind === 'forbid') { | |
| var fre = new RegExp(r.pat, flags.indexOf('g') < 0 ? flags + 'g' : flags); | |
| var hit; | |
| while ((hit = fre.exec(text)) !== null) { | |
| add(r, lineOf(text, hit.index), '(found: "' + hit[0].slice(0, 40) + '")'); | |
| if (hit.index === fre.lastIndex) fre.lastIndex++; | |
| break; | |
| } | |
| } else if (r.kind === 'exclusive') { | |
| var seen = [], first = -1; | |
| for (var a = 0; a < r.alts.length; a++) { | |
| var pos = text.search(new RegExp(r.alts[a], flags)); | |
| if (pos >= 0) { seen.push(r.alts[a]); if (first < 0 || pos < first) first = pos; } | |
| } | |
| if (seen.length > 1) add(r, lineOf(text, first), '(found: ' + seen.join(' + ') + ')'); | |
| } else if (r.kind === 'date_present') { | |
| var got = labelledDate(text, new RegExp(r.label, flags)); | |
| if (!got || !got.date) add(r, got ? got.line : 1); | |
| } else if (r.kind === 'date_future') { | |
| var gf = labelledDate(text, new RegExp(r.label, flags)); | |
| if (gf && gf.date && toNum(gf.date) > toNum(today)) add(r, gf.line); | |
| } else if (r.kind === 'date_max_age') { | |
| var ga = labelledDate(text, new RegExp(r.label, flags)); | |
| if (ga && ga.date) { | |
| var age = monthsBetween(ga.date, today); | |
| if (age > (r.months || 12)) add(r, ga.line, '(last review is ' + age + ' months old)'); | |
| } | |
| } | |
| } | |
| findings.sort(function (x, y) { return x.line - y.line; }); | |
| return { findings: findings }; | |
| } | |
| var API = { engine: { check: check }, RULES: RULES, RULE_COUNT: RULES.length }; | |
| if (typeof module !== 'undefined' && module.exports) module.exports = API; | |
| if (typeof window !== 'undefined') window.ASTMTENGINE = API; | |
| </script> | |
| <script>// ⛔뼈대 — kit/scaffold.py 가 찍는다 (s143). findings [{check,sev,msg,line?}] → 글/HTML. 같은 파일이 Node 와 브라우저에서 산다. | |
| ; | |
| function escapeHtml(s) { return String(s == null ? '' : s).replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"'); } | |
| function summaryLine(res) { | |
| const f = (res && res.findings) || []; const e = f.filter(function (x) { return String(x.sev || 'error').toLowerCase().startsWith('err'); }).length; | |
| return f.length ? (f.length + ' finding' + (f.length === 1 ? '' : 's') + ' (' + e + ' error' + (e === 1 ? '' : 's') + ')') : 'Clean — no findings.'; | |
| } | |
| function toText(res, label) { | |
| const f = (res && res.findings) || []; | |
| const lines = ['== ' + (label || '') + ' — ' + summaryLine(res)]; | |
| f.forEach(function (x) { lines.push(' ' + String(x.sev || 'error').toUpperCase().slice(0, 4) + ' ' + (x.line ? 'L' + x.line + ' ' : '') + (x.check || x.id || '') + ' ' + (x.msg || x.message || '')); }); | |
| return lines.join('\n'); | |
| } | |
| function toHtml(res) { | |
| const f = (res && res.findings) || []; | |
| if (!f.length) return '<p class="ok">' + escapeHtml(summaryLine(res)) + '</p>'; | |
| const rows = f.map(function (x) { return '<tr><td class="sev ' + escapeHtml(String(x.sev || 'error')) + '">' + escapeHtml(String(x.sev || 'error')) + '</td><td>' + (x.line ? 'L' + escapeHtml(x.line) : '') + '</td><td><code>' + escapeHtml(x.check || x.id || '') + '</code></td><td>' + escapeHtml(x.msg || x.message || '') + '</td></tr>'; }).join(''); | |
| return '<p class="sum">' + escapeHtml(summaryLine(res)) + '</p><table><thead><tr><th>Level</th><th>Line</th><th>Check</th><th>What</th></tr></thead><tbody>' + rows + '</tbody></table>'; | |
| } | |
| var ASTMTREPORT = { escapeHtml: escapeHtml, summaryLine: summaryLine, toText: toText, toHtml: toHtml }; | |
| if (typeof module !== 'undefined' && module.exports) module.exports = ASTMTREPORT; | |
| else window.ASTMTREPORT = ASTMTREPORT; | |
| </script> | |
| <script> | |
| (function(){ | |
| var src=document.getElementById('src'), out=document.getElementById('out'), go=document.getElementById('go'), today=document.getElementById('today'); | |
| today.value=new Date().toISOString().slice(0,10); | |
| function run(){ try { var res=window.ASTMTENGINE.engine.check(src.value,{today:today.value}); out.innerHTML=window.ASTMTREPORT.toHtml(res); } catch(e){ out.innerHTML='<p class="sev error">Could not run: '+String(e.message||e)+'</p>'; } } | |
| go.onclick=run; if (src.value.trim()) run(); | |
| })(); | |
| </script> | |
| <section data-yj-install="free" style="margin:2rem auto 1rem;max-width:46rem;padding:1.25rem 1.25rem;border:1px solid #cfe3d6;border-radius:12px;background:#f6fbf8;font:16px/1.6 system-ui,-apple-system,Segoe UI,sans-serif"><h2 style="margin:0 0 .4rem;font-size:1.2rem">Install free</h2><p style="margin:0 0 .8rem;color:#41505f">Free for the file open in your editor - no key, no limit. The workspace sweep and the report ask for a key.</p><a href="https://marketplace.visualstudio.com/items?itemName=ReadyStack.accessibility-statement-lint" rel="noopener" data-yj-install-door="mp" onclick="try{navigator.sendBeacon('/api/ev',new Blob([JSON.stringify({t:'install',slug:'accessibility-statement-lint',src:'mp',path:location.pathname})],{type:'application/json'}))}catch(e){}" style="display:inline-block;margin:0 .5rem .5rem 0;padding:.6rem 1rem;border-radius:8px;background:#0f7b3f;color:#fff;text-decoration:none;font-weight:600">VS Code Marketplace</a><a href="https://open-vsx.org/extension/readystack/accessibility-statement-lint" rel="noopener" data-yj-install-door="ovsx" onclick="try{navigator.sendBeacon('/api/ev',new Blob([JSON.stringify({t:'install',slug:'accessibility-statement-lint',src:'ovsx',path:location.pathname})],{type:'application/json'}))}catch(e){}" style="display:inline-block;margin:0 .5rem .5rem 0;padding:.6rem 1rem;border-radius:8px;background:#0f7b3f;color:#fff;text-decoration:none;font-weight:600">Open VSX (Cursor, VSCodium)</a><a href="https://www.npmjs.com/package/@readystack/accessibility-statement-lint" rel="noopener" data-yj-install-door="npm" onclick="try{navigator.sendBeacon('/api/ev',new Blob([JSON.stringify({t:'install',slug:'accessibility-statement-lint',src:'npm',path:location.pathname})],{type:'application/json'}))}catch(e){}" style="display:inline-block;margin:0 .5rem .5rem 0;padding:.6rem 1rem;border-radius:8px;background:#0f7b3f;color:#fff;text-decoration:none;font-weight:600">npm</a><a href="https://www.npmjs.com/package/@readystack/accessibility-statement-lint" rel="noopener" data-yj-install-door="mcp" onclick="try{navigator.sendBeacon('/api/ev',new Blob([JSON.stringify({t:'install',slug:'accessibility-statement-lint',src:'mcp',path:location.pathname})],{type:'application/json'}))}catch(e){}" style="display:inline-block;margin:0 .5rem .5rem 0;padding:.6rem 1rem;border-radius:8px;background:#0f7b3f;color:#fff;text-decoration:none;font-weight:600">MCP server (npx)</a><a href="https://hub.docker.com/r/getreadystack/accessibility-statement-lint" rel="noopener" data-yj-install-door="docker" onclick="try{navigator.sendBeacon('/api/ev',new Blob([JSON.stringify({t:'install',slug:'accessibility-statement-lint',src:'docker',path:location.pathname})],{type:'application/json'}))}catch(e){}" style="display:inline-block;margin:0 .5rem .5rem 0;padding:.6rem 1rem;border-radius:8px;background:#0f7b3f;color:#fff;text-decoration:none;font-weight:600">Docker Hub</a><pre style="margin:.5rem 0 0;padding:.6rem .8rem;background:#f3f5f8;border-radius:8px;font-size:.9rem;overflow:auto">ext install ReadyStack.accessibility-statement-lint | |
| npx @readystack/accessibility-statement-lint <file> | |
| npx @readystack/accessibility-statement-lint --mcp</pre><p style="margin:.6rem 0 0;font-size:.92rem;color:#41505f"><a href="https://getreadystack.com/api/buy/cl/polar_cl_l6iN1uWt0FwWu7tBsczD0jWpP2vxFM54Wdwqb3KPi1G" rel="nofollow" style="color:#0f7b3f;text-decoration:underline">Team? One key for every ReadyStack linter, 5 seats, $149 once</a></p></section> | |
| <section data-yj-alert="rule" style="margin:1rem auto 1.5rem;max-width:46rem;padding:1.1rem 1.25rem;border:1px solid #d9dee6;border-radius:12px;background:#fbfcfd;font:15px/1.55 system-ui,-apple-system,Segoe UI,sans-serif"><b style="font-size:1.05rem">Get the full rule table as a PDF</b><div style="opacity:.78;margin:.35rem 0 .8rem">All 19 rules Accessibility Statement Lint (EU model) checks, each with its source and date, on printable pages you can hand to your team or auditor. We email it now, and once more only if a rule changes. No newsletter.</div><div style="display:flex;gap:8px;flex-wrap:wrap"><input id="yjae" type="email" placeholder="you@example.com" style="flex:1;min-width:210px;padding:11px 13px;border:1px solid rgba(140,150,165,.34);border-radius:8px;background:#fff;color:inherit"><button id="yjab" style="padding:11px 18px;border-radius:8px;border:0;cursor:pointer;background:#0f7b3f;color:#fff;font:600 15px system-ui,sans-serif">Email me the PDF</button></div><div id="yjao" hidden style="margin-top:.6rem;color:#0f7b3f">Sent. It arrives within the hour - check spam too.</div><script>(function(){var b=document.getElementById("yjab"),e=document.getElementById("yjae");if(!b||!e)return;b.addEventListener("click",function(){var v=e.value.trim();if(!/.+@.+\..+/.test(v))return;try{localStorage.setItem("yj_em","1");}catch(_){}fetch(location.origin+"/api/subscribe",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({email:v,slug:"accessibility-statement-lint",lang:"en",source:"rule_table"+((b.closest&&b.closest("section")&&b.closest("section").getAttribute("data-yj-moved"))?"_at_result":"")})}).then(function(r){var o=document.getElementById("yjao");o.hidden=false;if(!r.ok){o.textContent="Could not save - please try again.";}}).catch(function(){var o=document.getElementById("yjao");o.hidden=false;o.textContent="Could not save - please try again.";});});})();</script><script>(function(){var e=document.getElementById("yjae"),s=e&&e.closest?e.closest("section"):null,o=document.getElementById("out");if(!s||!o)return;var d=false;function mv(ev){if(d)return;if(ev&&ev.target&&s.contains(ev.target))return;d=true;var b=o;while(b.parentNode&&b.parentNode!==document.body&&b.parentNode.tagName!=="MAIN"&&b.parentNode.tagName!=="ARTICLE")b=b.parentNode;if(!b.parentNode||b===s||b.contains(s))return;try{b.parentNode.insertBefore(s,b.nextSibling);s.setAttribute("data-yj-moved","1");}catch(_){}}document.addEventListener("input",mv,true);document.addEventListener("change",mv,true);})();</script></section> | |
| <section data-yj-cta="paid" style="margin:3rem auto;max-width:46rem;padding:1.5rem 1.25rem;border:1px solid #d8dee9;border-radius:12px;background:#fbfcfe;font:16px/1.6 system-ui,-apple-system,Segoe UI,sans-serif"><h2 style="margin:0 0 .5rem;font-size:1.25rem">Get the complete version <span class="yj-price">$29</span></h2><p style="margin:.25rem 0 1rem;color:#41505f">This page is the working piece. The full pack has everything below.</p><p style="margin:0 0 1rem;color:#41505f">Checks the accessibility statement you already published against the EU model statement and its two dates</p><p style="margin:0 0 1rem;color:#5b6b7c;font-size:.94rem">A consultant charges $150-$250 an hour to review one statement; a full agency accessibility audit starts around $2,500.</p><a href="https://getreadystack.com/api/buy/cl/polar_cl_DPpapwyFI9vWmYeB4f49Fp7BoXUo3CEvxfWmn16zHsc" rel="nofollow" style="display:inline-block;padding:.7rem 1.3rem;border-radius:8px;background:#1b64f2;color:#fff;text-decoration:none;font-weight:600">Buy the full version — $29</a></section> | |
| <!--yj-share-s101--><div style="margin:28px 0 8px;text-align:center"><button id="yjcp" type="button" style="font:inherit;padding:10px 18px;border:1px solid #8884;border-radius:10px;background:transparent;cursor:pointer">Copy link</button><button id="yjimg" type="button" style="font:inherit;padding:10px 18px;margin-left:8px;border:1px solid #8884;border-radius:10px;background:transparent;cursor:pointer">Save result as image</button></div><script>(function(){var b=document.getElementById("yjimg");if(!b)return;b.addEventListener("click",function(){var o=document.getElementById("out");var txt=(o&&(o.innerText||o.textContent)||"").trim();if(!txt){b.textContent="(no result yet)";setTimeout(function(){b.textContent="Save result as image";},1500);return;}var lines=txt.split(String.fromCharCode(10)).filter(function(x){return x.trim();}).slice(0,10);var W=1200,P=56,c=document.createElement("canvas");c.width=W;var lh=38,H=P*2+70+lines.length*lh+60;c.height=H;var x=c.getContext("2d");x.fillStyle="#1a1512";x.fillRect(0,0,W,H);x.fillStyle="#f3e9d2";x.font="600 34px system-ui,Segoe UI,Arial";var t=(document.title||"").split("|")[0].trim();x.fillText(t.slice(0,54),P,P+34);x.font="22px ui-monospace,Menlo,Consolas,monospace";x.fillStyle="#e8dcc4";for(var i=0;i<lines.length;i++){x.fillText(lines[i].slice(0,88),P,P+70+i*lh+22);}x.font="600 22px system-ui,Segoe UI,Arial";x.fillStyle="#c9a227";var d=location.hostname.replace("www.","");x.fillText(d,W-P-x.measureText(d).width,H-P+6);c.toBlob(function(bl){var u=URL.createObjectURL(bl),a=document.createElement("a");a.href=u;a.download=(t||"result").replace(/[^a-z0-9]+/gi,"-").toLowerCase()+"-"+d+".png";document.body.appendChild(a);a.click();setTimeout(function(){URL.revokeObjectURL(u);a.remove();},2000);},"image/png");});})();</script><script>(function(){var b=document.getElementById("yjcp");if(!b)return;var t=b.textContent;b.addEventListener("click",function(){var u=location.href.split("#")[0];function d(){b.textContent="Link copied";setTimeout(function(){b.textContent=t;},1800);}if(navigator.clipboard&&navigator.clipboard.writeText){navigator.clipboard.writeText(u).then(d,function(){prompt("",u);});}else{prompt("",u);}});})();</script><div class="yj-home-find"><div class="yj-lgbar"><a href="/" class="yj-lg on">EN</a><a href="/de/" class="yj-lg">DE</a><a href="/ja/" class="yj-lg">JA</a><a href="/es/" class="yj-lg">ES</a><a href="/pt/" class="yj-lg">PT</a></div><section class="yj-find"><h2>Find a tool</h2><input id="yjq" type="search" autocomplete="off" placeholder="Type a product name, format or task"><div id="yjr"></div></section><style>.yj-lgbar{position:absolute;top:14px;right:18px;display:flex;gap:6px;z-index:60}.yj-lg{font:600 12px/1 system-ui,sans-serif;padding:6px 9px;border-radius:4px;text-decoration:none;color:#8b96a3;border:1px solid rgba(140,150,165,.28)}.yj-lg:hover{color:#e8eaed;border-color:rgba(160,175,195,.6)}.yj-lg.on{color:#0e1116;background:#cfd6de;border-color:#cfd6de}.yj-find{max-width:760px;margin:34px auto 8px;padding:0 20px}.yj-find h2{font:600 20px/1.3 system-ui,sans-serif;margin:0 0 10px}#yjq{width:100%;box-sizing:border-box;padding:13px 15px;font:400 15.5px system-ui,sans-serif;border:1px solid rgba(140,150,165,.34);border-radius:8px;background:rgba(255,255,255,.04);color:inherit}#yjq:focus{outline:none;border-color:#7FD1C4}#yjr{margin-top:10px;display:flex;flex-direction:column;gap:6px}#yjr a{display:flex;justify-content:space-between;gap:12px;padding:11px 14px;border:1px solid rgba(140,150,165,.22);border-radius:6px;text-decoration:none;font:400 14.5px/1.4 system-ui,sans-serif;color:inherit}#yjr a:hover{border-color:#7FD1C4}#yjr .pr{font:600 13px system-ui,sans-serif;color:#5fbf9a;white-space:nowrap}</style><script>(function(){var q=document.getElementById("yjq"),r=document.getElementById("yjr"),D=null;function load(){if(D)return Promise.resolve(D);return fetch("/search-index.json").then(function(x){return x.json();}).then(function(j){D=j;return j;}).catch(function(){D=[];return D;});}function esc(t){return String(t).replace(/[&<>"]/g,function(c){return {"&":"&","<":"<",">":">",'"':"""}[c];});}function run(){var v=q.value.trim().toLowerCase();if(v.length<2){r.innerHTML="";return;}load().then(function(j){var w=v.split(/\s+/),o=[];for(var i=0;i<j.length&&o.length<12;i++){var t=j[i].t.toLowerCase();var ok=true;for(var k=0;k<w.length;k++){if(t.indexOf(w[k])<0){ok=false;break;}}if(ok)o.push(j[i]);}r.innerHTML=o.length?o.map(function(x){return "<a href=\""+esc(x.u)+"\">"+"<span>"+esc(x.t)+"</span>"+(x.p?"<span class=\"pr\">$"+x.p+"</span>":"")+"</a>";}).join(""):"";});}q.addEventListener("input",run);q.addEventListener("focus",load);})();</script></div> | |
| <script data-yj="yj-view-beacon">(function(){try{var p=location.pathname;var k='yjv:'+p;if(sessionStorage.getItem(k))return;sessionStorage.setItem(k,'1');fetch('/api/ev',{method:'POST',keepalive:true,headers:{'Content-Type':'application/json'},body:JSON.stringify({path:p,lang:"en",ref:(document.referrer||''),src:(function(){try{return (new URLSearchParams(location.search).get('ref')||'').replace(/[^a-z_]/g,'').slice(0,12)}catch(e){return ''}})()})}).catch(function(){});}catch(e){}})();</script> | |
| <script data-yj="yj-use-beacon">(function(){try{var o=document.getElementById('out');if(!o)return;var p=location.pathname,k='yju:'+p,sent=false;function fire(){if(sent||sessionStorage.getItem(k))return;if(!(o.textContent||'').replace(/\s+/g,'').length)return;sent=true;sessionStorage.setItem(k,'1');fetch('/api/ev',{method:'POST',keepalive:true,headers:{'Content-Type':'application/json'},body:JSON.stringify({path:p,lang:"en",t:'use',src:(function(){try{return (new URLSearchParams(location.search).get('ref')||'').replace(/[^a-z_]/g,'').slice(0,12)}catch(e){return ''}})()})}).catch(function(){});}var mo=new MutationObserver(function(){fire();if(sent)mo.disconnect();});mo.observe(o,{childList:true,characterData:true,subtree:true});}catch(e){}})();</script> | |
| <section data-yj="yj-facts-v1" style="max-width:920px;margin:48px auto 0;padding:0 20px;font-family:system-ui,sans-serif"><style>.yj-article p{font:400 15.5px/1.7 system-ui,sans-serif;color:#26313b;margin:0 0 12px;max-width:72ch}.yj-article h2,.yj-article h3,.yj-article h4{font:600 17px/1.35 system-ui,sans-serif;color:#1b1f24;margin:22px 0 8px}.yj-article table{border-collapse:collapse;margin:10px 0 16px;font:14px/1.45 system-ui,sans-serif}.yj-article th,.yj-article td{border:1px solid #e2e6ea;padding:6px 10px;text-align:left}.yj-article th{background:#f4f6f8}.yj-article pre{background:#0f1720;color:#e6edf3;padding:12px 14px;border-radius:8px;overflow:auto;font:13px/1.5 ui-monospace,Menlo,monospace}.yj-article code{font:13px ui-monospace,Menlo,monospace;background:#f1f3f5;padding:1px 4px;border-radius:3px}.yj-article pre code{background:none;padding:0}.yj-article ul,.yj-article ol{font:15px/1.6 system-ui,sans-serif;color:#26313b}</style><p style="font:400 13px/1.5 system-ui,sans-serif;color:#6b7680;margin:0 0 14px"><time datetime="2026-09-16">Updated 2026-09-16</time> · ReadyStack</p><script type="application/ld+json">{"@context":"https://schema.org","@type":"WebPage","dateModified":"2026-09-16","author":{"@type":"Organization","name":"ReadyStack"}}</script><h2 style="font:600 21px/1.3 system-ui,sans-serif;margin:0 0 6px;color:#1b1f24">Worked example</h2><p style="font:400 14.5px/1.5 system-ui,sans-serif;margin:0 0 16px;color:#5b6570">Real numbers from this tool, line by line.</p><div class="yj-article"><p><img src="https://getreadystack.com/img/promo/sku85572_result_card.jpg" alt="Accessibility Statement Lint (EU model)" loading="lazy" style="max-width:100%;border-radius:6px"></p> | |
| <p>Eight gaps in one accessibility statement, seven of them high severity — that is what a public-sector web team in the EU gets back the first time it runs the kind of statement a chat assistant writes through a check of the EU model statement.</p> | |
| <p>An accessibility statement is not a marketing page. Directive (EU) 2016/2102 obliges every public sector website in the EU to publish one, and the Commission's model statement fixes what goes in it: a commitment sentence, the legal basis, the scope, one compliance status, a Non-accessible content section sorted into three reason categories, a date of preparation with the method used, a date of last review, a feedback channel, and an enforcement procedure. Directive (EU) 2019/882 carried the same habit into private-sector services.</p> | |
| <p>Monitoring bodies sample these statements. They do not begin with your code. They begin with this page, and the cheapest thing to check on it is a date.</p> | |
| <p>Accessibility Statement Lint runs 19 checks over the statement file you already published. Two files ship with it, and every number below was measured on those two files, not estimated.</p> | |
| <p>The first is a complete statement for a fictional Danish municipality. It declares one status, sorts its non-accessible content into the three model categories, backs its disproportionate burden claim with a dated assessment and an accessible alternative, and carries a preparation date of 14 January 2026 and a last review date of 20 August 2026. Checked on 16 September 2026, it returns 0 findings.</p> | |
| <p>Now move only the calendar. Feed the identical file with an audit date of 1 October 2027 and one finding appears: the last review is 13 months old. Article 7(1) of Directive (EU) 2016/2102 requires the statement to be kept up to date, so a statement that was clean a year ago is not clean now. Only the date changed, and that is the failure mode nobody has a reminder for.</p> | |
| <p>The second file is what a chat assistant produces when you ask it for a statement. It looks plausible. It returns 8 findings, 7 high severity and 1 medium:</p> | |
| <p>law_cited two directives exist; neither is named by number status_single "partially compliant" and "not compliant" in one file standard_version_stale EN 301 549 V2.1.2 instead of V3.2.1 burden_assessment a disproportionate burden claim with nothing behind it burden_alternative and no accessible alternative offered review_stale last review 30 months old enforcement_procedure no body to escalate to placeholder_left [insert your organisation name], still there</p> | |
| <p>The last one stings because it is public: a placeholder in square brackets is what a generated document looks like when nobody read it before it went live.</p> | |
| <p>This is the gap a chatbot cannot close. It writes you a fresh statement in seconds, but it cannot open the one already serving on your domain, and it has no reliable sense of today's date against your last review date. Free contrast and WCAG checkers test pages; this is a document, and its defects are missing sections, stale dates and unfilled placeholders.</p> | |
| <p>The same engine runs in the browser and inside VS Code. Checking the file open in your editor is free, needs no key and no account, and finishes the job for one statement. The licensed version answers the next question: which of your locale statements is the stale one? It sweeps every statement in the workspace and writes one dated report with a per-file verdict.</p> | |
| <p>For scale: an accessibility consultant charges $150 to $250 an hour to read one statement against the model, and a full agency accessibility audit starts around $2,500.</p> | |
| <p>Open your own accessibility statement. Check the date of last review first.</p></div><h2 style="font:600 19px/1.3 system-ui,sans-serif;margin:30px 0 10px;color:#1b1f24">15 seconds — what it actually does</h2><video controls muted playsinline preload="none" poster="/img/promo/sku85572_result_card.jpg" style="width:100%;max-width:360px;border-radius:10px;display:block;margin:0 auto"><source src="/media/sku85572_short.mp4" type="video/mp4"></video><h2 style="font:600 21px/1.3 system-ui,sans-serif;margin:34px 0 6px;color:#1b1f24">Questions people ask</h2><details style="border-bottom:1px solid #e6e9ed;padding:10px 0"><summary style="cursor:pointer;font:600 15px/1.4 system-ui,sans-serif;color:#1b1f24">What does Accessibility Statement Lint actually do?</summary><p style="margin:8px 0 2px;font:400 14.5px/1.6 system-ui,sans-serif;color:#33404b">It reads an accessibility statement you have already published and reports what the EU model statement requires and your file does not contain. Nineteen checks cover the commitment sentence, the legal basis, the compliance status wording, the three reason categories for non-accessible content, the disproportionate burden assessment, the date of preparation, the date of last review, the feedback channel and the enforcement procedure.</p></details><details style="border-bottom:1px solid #e6e9ed;padding:10px 0"><summary style="cursor:pointer;font:600 15px/1.4 system-ui,sans-serif;color:#1b1f24">Who is this for?</summary><p style="margin:8px 0 2px;font:400 14.5px/1.6 system-ui,sans-serif;color:#33404b">Public sector web teams in the EU, the digital agencies that publish on their behalf, and private-sector service providers now covered by the European Accessibility Act. If somebody on your team owns a page titled Accessibility statement, and a national monitoring body can sample that page without warning, this extension checks the exact file the body will read.</p></details><details style="border-bottom:1px solid #e6e9ed;padding:10px 0"><summary style="cursor:pointer;font:600 15px/1.4 system-ui,sans-serif;color:#1b1f24">Why can't I just ask a chatbot or use a free accessibility checker?</summary><p style="margin:8px 0 2px;font:400 14.5px/1.6 system-ui,sans-serif;color:#33404b">A chat assistant writes you a brand new statement in seconds, but it cannot open the one already live on your domain, and it has no reliable sense of today's date against your last review date. Free contrast and WCAG checkers test pages, not this document. The defects here are missing sections, stale dates and unfilled placeholders.</p></details><details style="border-bottom:1px solid #e6e9ed;padding:10px 0"><summary style="cursor:pointer;font:600 15px/1.4 system-ui,sans-serif;color:#1b1f24">What is free and what needs a licence key?</summary><p style="margin:8px 0 2px;font:400 14.5px/1.6 system-ui,sans-serif;color:#33404b">Free checks the statement file open in your editor and names every missing section, each finding on its own line in the Problems panel. That finishes the job for one statement. The licensed version sweeps every locale and sub-site statement in the workspace at once and writes one dated report carrying a per-file verdict.</p></details><details style="border-bottom:1px solid #e6e9ed;padding:10px 0"><summary style="cursor:pointer;font:600 15px/1.4 system-ui,sans-serif;color:#1b1f24">What would this cost me otherwise?</summary><p style="margin:8px 0 2px;font:400 14.5px/1.6 system-ui,sans-serif;color:#33404b">An accessibility consultant charges $150 to $250 an hour to read one statement against the model, and a full agency accessibility audit starts around $2,500. The licensed version is $29 once, one licence key per person or team seat,. Checking a single file stays free and needs no key.</p></details><details style="border-bottom:1px solid #e6e9ed;padding:10px 0"><summary style="cursor:pointer;font:600 15px/1.4 system-ui,sans-serif;color:#1b1f24">Why not just ask ChatGPT or another AI chat?</summary><p style="margin:8px 0 2px;font:400 14.5px/1.6 system-ui,sans-serif;color:#33404b">A general AI chat answers from training data with a cutoff date, cannot read your repository and names no rule version. Accessibility Statement Checker (EU model, EN 301 549) checks the file you open against 19 rules from a rule set dated 2026-09-20, and points at the exact line. For a filing, an audit or a client you need that dated result on your own files.</p></details><script type="application/ld+json" data-yj="yj-faq-ld">{"@context": "https://schema.org", "@type": "FAQPage", "mainEntity": [{"@type": "Question", "name": "What does Accessibility Statement Lint actually do?", "acceptedAnswer": {"@type": "Answer", "text": "It reads an accessibility statement you have already published and reports what the EU model statement requires and your file does not contain. Nineteen checks cover the commitment sentence, the legal basis, the compliance status wording, the three reason categories for non-accessible content, the disproportionate burden assessment, the date of preparation, the date of last review, the feedback channel and the enforcement procedure."}}, {"@type": "Question", "name": "Who is this for?", "acceptedAnswer": {"@type": "Answer", "text": "Public sector web teams in the EU, the digital agencies that publish on their behalf, and private-sector service providers now covered by the European Accessibility Act. If somebody on your team owns a page titled Accessibility statement, and a national monitoring body can sample that page without warning, this extension checks the exact file the body will read."}}, {"@type": "Question", "name": "Why can't I just ask a chatbot or use a free accessibility checker?", "acceptedAnswer": {"@type": "Answer", "text": "A chat assistant writes you a brand new statement in seconds, but it cannot open the one already live on your domain, and it has no reliable sense of today's date against your last review date. Free contrast and WCAG checkers test pages, not this document. The defects here are missing sections, stale dates and unfilled placeholders."}}, {"@type": "Question", "name": "What is free and what needs a licence key?", "acceptedAnswer": {"@type": "Answer", "text": "Free checks the statement file open in your editor and names every missing section, each finding on its own line in the Problems panel. That finishes the job for one statement. The licensed version sweeps every locale and sub-site statement in the workspace at once and writes one dated report carrying a per-file verdict."}}, {"@type": "Question", "name": "What would this cost me otherwise?", "acceptedAnswer": {"@type": "Answer", "text": "An accessibility consultant charges $150 to $250 an hour to read one statement against the model, and a full agency accessibility audit starts around $2,500. The licensed version is $29 once, one licence key per person or team seat,. Checking a single file stays free and needs no key."}}, {"@type": "Question", "name": "Why not just ask ChatGPT or another AI chat?", "acceptedAnswer": {"@type": "Answer", "text": "A general AI chat answers from training data with a cutoff date, cannot read your repository and names no rule version. Accessibility Statement Checker (EU model, EN 301 549) checks the file you open against 19 rules from a rule set dated 2026-09-20, and points at the exact line. For a filing, an audit or a client you need that dated result on your own files."}}]}</script><div id="yj-ask" style="margin:36px 0 0;padding:18px 18px 16px;border:1px solid #e2e6ea;border-radius:10px;background:#fbfbfc"><h2 style="font:600 19px/1.3 system-ui,sans-serif;margin:0 0 4px;color:#1b1f24">Ask about this tool</h2><p style="font:400 13.5px/1.5 system-ui,sans-serif;margin:0 0 12px;color:#5b6570">One question, answered by the person who built it. Your email only if you want the answer sent.</p><form onsubmit="return yjAsk(event)" style="display:grid;gap:8px"><textarea name="question" required minlength="10" maxlength="2000" rows="3" placeholder="Your question" style="font:14px/1.5 system-ui,sans-serif;padding:10px;border:1px solid #cfd6dd;border-radius:6px"></textarea><div style="display:flex;gap:8px;flex-wrap:wrap"><input type="email" name="email" placeholder="Email (optional)" style="flex:1;min-width:200px;font:14px system-ui,sans-serif;padding:9px 10px;border:1px solid #cfd6dd;border-radius:6px"><button type="submit" style="font:600 14px system-ui,sans-serif;padding:9px 18px;border:0;border-radius:6px;background:#1b1f24;color:#fff;cursor:pointer">Send</button></div><p id="yj-ask-msg" style="font:13px system-ui,sans-serif;margin:2px 0 0;color:#0a6238;min-height:1em"></p></form><script>function yjAsk(e){e.preventDefault();var f=e.target,m=document.getElementById("yj-ask-msg");m.style.color="#0a6238";fetch(location.origin+"/api/ask",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({sku:85572,slug:"accessibility-statement-lint",lang:"en",question:f.question.value,email:f.email.value,page:location.pathname})}).then(function(r){if(!r.ok)throw 0;m.textContent="Thanks \u2014 noted. Answers land on this page and, if you left an email, in your inbox.";f.question.value="";}).catch(function(){m.style.color="#a8551f";m.textContent="Could not send. Please try again in a minute.";});return false;}</script></div></section><section class="yj-related" style="max-width:920px;margin:56px auto 40px;padding:0 20px"><h2 style="font:600 21px/1.3 system-ui,sans-serif;margin:0 0 6px;color:#1b1f24">Same job, next tool</h2><p style="font:400 14.5px/1.5 system-ui,sans-serif;margin:0 0 20px;color:#5b6570">Free in the browser. The full version is a one-time licence.</p><div style="display:grid;grid-template-columns:repeat(auto-fill,minmax(250px,1fr));gap:14px"><a class="yj-rel-c" href="https://getreadystack.com/tools/en301549-lint"><span class="yj-rel-t">EN 301 549 Lint — European Accessibility Act rules, with the clause number, in</span><span class="yj-rel-p">$29</span></a><a class="yj-rel-c" href="https://getreadystack.com/tools/dsa-terms-lint"><span class="yj-rel-t">DSA Terms Lint - EU Digital Services Act</span><span class="yj-rel-p">$29</span></a><a class="yj-rel-c" href="https://getreadystack.com/tools/model-card-lint-gpai"><span class="yj-rel-t">Model Card Lint - EU GPAI (AI Act Art. 53)</span><span class="yj-rel-p">$29</span></a><a class="yj-rel-c" href="https://getreadystack.com/tools/amazon-fba-calculator-extension"><span class="yj-rel-t">FBA Profit & Break-Even Calculator for Amazon Private-Label Sourcing — Chrome </span><span class="yj-rel-p">$60</span></a><a class="yj-rel-c" href="https://getreadystack.com/tools/ai-model-license-lint"><span class="yj-rel-t">AI Model License Lint</span><span class="yj-rel-p">$29</span></a><a class="yj-rel-c" href="https://getreadystack.com/tools/ai-model-retirement-lint"><span class="yj-rel-t">AI Model Retirement Lint</span><span class="yj-rel-p">$29</span></a></div><style>.yj-rel-c{display:flex;flex-direction:column;gap:8px;padding:15px 16px;border:1px solid #e2e6ea;border-radius:6px;text-decoration:none;background:#fff}.yj-rel-c:hover{border-color:#b9c2cb;background:#fafbfc}.yj-rel-t{font:500 14.5px/1.42 system-ui,sans-serif;color:#1b1f24}.yj-rel-p{font:600 13px system-ui,sans-serif;color:#0a6238}</style></section></body> | |
| </html> | |