
Israeli Accessibility Compliance
- 79 installs
- 21 repo stars
- Updated August 3, 2026
- skills-il/localization
Israeli Accessibility Compliance is an agent skill that implements IS 5568–based Hebrew RTL web accessibility with legal and technical guidance.
About
Israeli Accessibility Compliance is a localization-focused agent skill for solo builders and small teams shipping Hebrew, right-to-left web products under Israeli law. It walks through IS 5568 as anchored to WCAG 2.0 AA, clarifies where the standard diverges from generic WCAG 2.1 checklists, and answers practical questions about exemptions by business scale, civil compensation versus commissioner fines, remediation deadlines, and appointing an accessibility coordinator. Use it when a user asks about negishot, teken negishot, who must accessible a site, or how to audit RTL sites for assistive technology. The skill pairs legal framing with implementation-oriented notes such as accessibility widgets and ARIA patterns suited to RTL. It deliberately avoids presenting itself as a standalone global WCAG course without the Israeli statutory layer, so you still validate against official sources before signing compliance statements.
- Maps Israeli IS 5568 to WCAG 2.0 AA with noted 2.1-aligned additions
- Covers exemptions by revenue tier, dual enforcement paths, and 60-day remediation window
- Documents accessibility coordinator role and Hebrew screen-reader expectations
- Includes RTL-oriented ARIA templates and implementation focus for Hebrew apps
- Scoped to Israeli legal context—not generic WCAG-only guidance
Israeli Accessibility Compliance by the numbers
- 79 all-time installs (skills.sh)
- Ranked #1,117 of 2,245 Frontend Development skills by installs in the Skillselion catalog
- Security screen: MEDIUM risk (skills.sh audit)
- Data as of Aug 4, 2026 (Skillselion catalog sync)
npx skills add https://github.com/skills-il/localization --skill israeli-accessibility-complianceAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 79 |
|---|---|
| repo stars | ★ 21 |
| Security audit | 2 / 3 scanners passed |
| Last updated | August 3, 2026 |
| Repository | skills-il/localization ↗ |
What it does
Implement IS 5568–aligned Hebrew RTL accessibility, legal exemptions, and remediation timelines when shipping a site for the Israeli market.
Who is it for?
SaaS, marketing sites, or apps targeting Israeli users that must document negishot compliance and RTL assistive-tech support.
Skip if: Products with no Israeli market, teams wanting WCAG-only audits without IS 5568 law, or native mobile apps outside the skill’s web-focused scope unless you adapt manually.
When should I use this skill?
User asks about Israeli accessibility law, IS 5568, negishot, exemptions, enforcement, coordinator role, Hebrew screen readers, or RTL accessibility audits.
What you get
You get actionable Israeli-context requirements, RTL ARIA patterns, and enforcement-aware remediation steps you can apply in code and release planning.
- Compliance-oriented implementation guidance for IS 5568 and RTL patterns
- Audit-oriented answers on enforcement, exemptions, and remediation timing
By the numbers
- IS 5568 anchored to WCAG 2.0 AA with selective 2.1-aligned criteria
- 60-day remediation period referenced for corrections
- Skill version 1.2.0 in package metadata
Files
Israeli Accessibility Compliance
Instructions
Step 1: Understand the Legal Framework
Israeli web accessibility (negishot) is legally mandatory under the Equal Rights for Persons with Disabilities Act (Chok Shivyon Zechuyot Le'Anashim Im Mugbaluyot), 1998 and the Equal Rights for Persons with Disabilities (Service Accessibility Accommodations) Regulations, 2013 (Takanot Negishut LeSherut).
| Regulation | Requirement | Status | Penalty |
|---|---|---|---|
| IS 5568 (2017, updated 2020 and 2023) | Israeli accessibility standard, anchored to WCAG 2.0 AA | New websites accessible from 25 October 2015; existing websites by 26 October 2017; the October 2020 date extended the lower-revenue exemption tiers, not the universal deadline | Up to 50,000 NIS statutory civil damages per violation, without proof of harm |
| Takanat Negishut LeSherut (2013) | Public websites and apps must comply | In effect; covers services provided to the public | Lawsuits + statutory damages |
| Mobile applications | Included in scope per the 2013 regulations; explicitly reaffirmed in later updates | In effect | Same statutory damages |
| Government sites | Must meet IS 5568 Level AA | In effect | Commission oversight + administrative penalty |
Note on WCAG version. The statute and regulations reference IS 5568. The dominant reading is that IS 5568, including the September 2023 Part 1 edition, stays legally anchored to WCAG 2.0 AA, with WCAG 2.1 alignment being partial or vendor-claimed rather than the legal baseline. Some sources describe IS 5568:2020 as WCAG 2.1 AA, so sources differ. The safe working target is WCAG 2.0 AA plus the Israeli-specific additions below; meeting WCAG 2.1 (and 2.2) criteria on top of that is useful future-proofing but is not the floor the law sets.
Who must comply: All public-facing Israeli websites and mobile applications of service providers open to the public, including government agencies, educational institutions, healthcare providers, businesses, and non-profits.
Standard conformance is necessary but not sufficient. The legal duty is the regulation (Service Accessibility Regulations 2013), which incorporates IS 5568 Part 1 by reference at level AA. You can pass an automated WCAG / IS 5568 audit and still be non-compliant if you lack the accessibility statement, the coordinator (where required), a feedback channel, or the certified-expert opinion described below, and vice versa. Treat "meets IS 5568" and "legally compliant" as two separate checklists.
Certified accessibility expert (Morshe Negishut Hasherut). Where an operator claims an exemption for "technological difficulty" under Regulation 35, the regulation requires a written opinion from a certified service-accessibility expert (morshe negishut hasherut) prepared in consultation with an internet-accessibility professional. In practice the accessibility statement and the audit behind it are also expected to be backed by a morshe negishut; compliance is not a pure developer/automated-audit exercise.
Digital documents (IS 5568 Part 2). IS 5568 is split into Part 1 (web content) and Part 2 (accessibility of digital documents, e.g. PDFs and Office files), published 2020. PDFs and downloadable documents on a public-facing Israeli site fall under the accessibility duty and are one of the most common real-world audit failures, so do not stop at the HTML.
Step 2: Who Is Exempt
The Service Accessibility Regulations base website-accessibility exemptions on revenue, not on employee count. There is no "25 employees" or "300,000 NIS" trigger in the regulations. The exemption tiers are:
| Operator | Exemption |
|---|---|
| Osek patur (VAT-exempt dealer) | Full exemption from website accessibility |
| Average annual revenue under the small-business threshold (sources differ: some cite 100,000 NIS, others 120,000 NIS; confirm against the current regulation before relying on it) | Temporary 3-year exemption, renewable every 3 years while average revenue stays at or below that threshold |
| Average annual revenue from that threshold up to 1,000,000 NIS | 3-year exemption for an existing site whose operation began before 26 October 2017; a new site built after that date must be accessible |
| Average annual revenue above 1,000,000 NIS | No automatic exemption. The operator must apply to the Commission for Equal Rights of Persons with Disabilities to claim a heavy-burden exemption |
These exemptions (except the heavy-burden application) are automatic and need no approval, but the operator must re-check their revenue every 3 years. The exact small-business threshold figure is contested between sources, so a borderline operator should verify against the regulation text or the Commission rather than self-certify as exempt. Exemption from website accessibility does not exempt the operator from other service-accessibility duties, nor from the separate physical-premises accessibility duty (negishut hamivne), which is governed by its own regulations.
Step 3: Enforcement and Penalties
Two enforcement tracks run in parallel:
- Civil lawsuit. A claimant can sue for up to 50,000 NIS in statutory damages without proof of harm, only needing to show the site is non-compliant. 60-day cure period: a deviation is not treated as a violation unless the operator was first sent a notice demanding a fix and failed to fix it within a reasonable time, no later than 60 days from receiving the notice. A class-action request for an inaccessible site has no cause of action if no prior fix notice was sent. This is a real defense for operators.
- Administrative enforcement by the Commission. Amendment 23 to the Equal Rights Act gave the Commission for Equal Rights of Persons with Disabilities an administrative track: warnings, commitments, accessibility orders, and an administrative monetary penalty (izum kaspi). The penalty amount is scaled by the operator's size (micro business up to 2,000,000 NIS turnover, small business up to 20,000,000 NIS, regular business above that or a public authority) and by the cost of the specific accommodation, with a per-day charge while a violation continues. Reductions apply for a clean prior record and for corrective action. This penalty is separate from and on top of the 50,000 NIS civil damages.
Filing a complaint (user side). A user who hits an inaccessible site sends the operator a fix notice (which starts the 60-day clock); if it is not fixed in time, the user can complain to the Commission for Equal Rights of Persons with Disabilities or sue for the statutory damages. The operator's accessibility coordinator (Step 4) is the first point of contact for such complaints, so an operator should route incoming accessibility complaints through the coordinator and act within the cure window.
Step 4: The Accessibility Coordinator (Rakaz Negishut)
The Service Accessibility Regulations require a service provider that is a public body, or that employs 25 or more employees, to appoint one of its staff as an accessibility coordinator (rakaz negishut). Note this 25-employee figure is the trigger for the coordinator appointment, not for the website-accessibility duty itself, which is universal subject to the revenue exemptions above. The coordinator:
- Gives the public information about the accessibility arrangements of the provider's assets and services.
- Handles accessibility inquiries and complaints from the public.
- Helps the parties responsible for accessibility carry out and maintain the required accommodations.
The coordinator's name and contact details must appear in the accessibility statement (see Step 9).
Step 5: IS 5568 vs WCAG -- Key Differences
IS 5568 is anchored to WCAG 2.0 AA (sources differ on whether the current edition reaches 2.1) and adds Israeli-specific requirements:
| Area | WCAG 2.0 AA | IS 5568 Addition |
|---|---|---|
| Language | Declare lang attribute | Must support lang="he" with RTL |
| Text direction | Not specified | Must declare dir="rtl" for Hebrew content |
| Contrast | 4.5:1 for text | Same, plus contrast check with Hebrew fonts |
| Form labels | Associated labels | Labels must support RTL alignment |
| Error messages | Descriptive errors | Must be in Hebrew for Hebrew sites |
| Accessibility statement | Recommended | Mandatory (Hatzaharat Negishot) |
| Contact info | Not required | Must provide accessibility contact method |
| Bilingual public bodies | Not specified | A government or public body that serves the public in Hebrew and Arabic should make its content accessible in both languages, not Hebrew only |
Step 6: Set Up Accessible RTL HTML Structure
<!DOCTYPE html>
<html lang="he" dir="rtl">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>שם האתר - כותרת הדף</title>
</head>
<body>
<!-- Skip navigation link (required) -->
<a href="#main-content" class="skip-link">דלג לתוכן הראשי</a>
<header role="banner">
<nav role="navigation" aria-label="ניווט ראשי">
<!-- Navigation items -->
</nav>
</header>
<main id="main-content" role="main">
<!-- Page content -->
</main>
<footer role="contentinfo">
<a href="/accessibility-statement">הצהרת נגישות</a>
</footer>
</body>
</html>Key IS 5568 requirements in this structure:
lang="he"anddir="rtl"on thehtmlelement- Skip navigation link in Hebrew ("דלג לתוכן הראשי")
- ARIA roles for landmarks
- Hebrew ARIA labels for navigation
- Link to accessibility statement (Hatzaharat Negishot) in footer
Step 7: Hebrew Screen Reader Compatibility
Test with these screen readers commonly used in Israel:
| Screen Reader | Platform | Hebrew Support | Testing Notes |
|---|---|---|---|
| NVDA | Windows | Excellent with eSpeak-ng Hebrew | Free, most common in Israel |
| JAWS | Windows | Good with Eloquence Hebrew | Commercial, institutional use |
| VoiceOver | macOS/iOS | Good native Hebrew TTS | Built-in, growing adoption |
| TalkBack | Android | Good with Google TTS Hebrew | Built-in on Android devices |
Hebrew-specific screen reader patterns:
<!-- Announce content direction changes -->
<p dir="rtl" lang="he">
טקסט בעברית עם <span dir="ltr" lang="en">English text</span> משולב
</p>
<!-- Hebrew ARIA labels -->
<button aria-label="סגור חלון">X</button>
<input type="search" aria-label="חיפוש באתר" placeholder="חפש...">
<!-- Hebrew live regions for dynamic content -->
<div aria-live="polite" aria-atomic="true" dir="rtl">
הטופס נשלח בהצלחה
</div>Step 8: RTL-Specific ARIA Patterns
<!-- RTL form with accessible error messages -->
<form dir="rtl" novalidate>
<div role="group" aria-labelledby="personal-info">
<h2 id="personal-info">פרטים אישיים</h2>
<label for="full-name">שם מלא</label>
<input id="full-name" type="text" required
aria-required="true"
aria-describedby="name-error"
aria-invalid="false">
<span id="name-error" role="alert" class="error" hidden>
נא למלא שם מלא
</span>
<label for="teudat-zehut">תעודת זהות</label>
<input id="teudat-zehut" type="text" pattern="[0-9]{9}"
inputmode="numeric" dir="ltr"
aria-required="true"
aria-describedby="tz-help tz-error">
<span id="tz-help" class="hint">9 ספרות</span>
<span id="tz-error" role="alert" class="error" hidden>
מספר תעודת זהות לא תקין
</span>
</div>
</form>
<!-- RTL data table -->
<table dir="rtl">
<caption>סיכום הזמנות</caption>
<thead>
<tr>
<th scope="col">מספר הזמנה</th>
<th scope="col">תאריך</th>
<th scope="col">סכום</th>
<th scope="col">סטטוס</th>
</tr>
</thead>
<tbody>
<tr>
<td dir="ltr">ORD-12345</td>
<td>04/03/2026</td>
<td dir="ltr">1,234.50 ₪</td>
<td>הושלם</td>
</tr>
</tbody>
</table>Step 9: Accessibility Statement (Hatzaharat Negishot)
IS 5568 requires a published accessibility statement. Required content:
<article dir="rtl" lang="he">
<h1>הצהרת נגישות</h1>
<p>אנו ב-[שם החברה] מחויבים להנגשת האתר לאנשים עם מוגבלויות
בהתאם לתקן הישראלי IS 5568, המעוגן ב-WCAG 2.0 ברמה AA.</p>
<h2>אמצעי נגישות באתר</h2>
<ul>
<li>האתר תומך בניווט מלא באמצעות מקלדת</li>
<li>האתר תומך בקוראי מסך (NVDA, JAWS, VoiceOver)</li>
<li>תמונות מלוות בטקסט חלופי</li>
<li>ניגודיות צבעים עומדת ביחס 4.5:1 לפחות</li>
</ul>
<h2>מגבלות נגישות ידועות</h2>
<ul>
<li>[פרטו כאן רכיבים, דפים או מסמכים שטרם הונגשו במלואם, אם יש, ואת מועד התיקון הצפוי. אם אין מגבלות ידועות, ציינו זאת במפורש]</li>
</ul>
<h2>פנייה בנושא נגישות</h2>
<p>רכז/ת נגישות: [שם]</p>
<p>טלפון: <a href="tel:+97212345678" dir="ltr">+972-1-234-5678</a></p>
<p>דוא"ל: <a href="mailto:negishot@example.co.il">negishot@example.co.il</a></p>
<p>תאריך ביקורת הנגישות האחרונה: [תאריך]</p>
<p>תאריך עדכון ההצהרה: [תאריך]</p>
</article>The full required-content list (7 items, including known limitations and the audit date) is in references/is-5568.md. A statement that omits known limitations or the audit date is a common audit finding.
Step 10: Automated Accessibility Testing
See scripts/audit_a11y.py for the full audit pipeline.
# Quick accessibility check with axe-core via selenium
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
def run_accessibility_audit(url):
options = Options()
options.add_argument('--headless')
driver = webdriver.Chrome(options=options)
driver.get(url)
# Inject axe-core
axe_script = open('axe.min.js').read()
driver.execute_script(axe_script)
# Run audit with Hebrew locale rules
results = driver.execute_script("""
return axe.run({
rules: {
'html-has-lang': { enabled: true },
'valid-lang': { enabled: true },
'document-title': { enabled: true },
'bypass': { enabled: true },
'color-contrast': { enabled: true },
'label': { enabled: true },
'image-alt': { enabled: true }
}
});
""")
driver.quit()
return resultsIS 5568 compliance checklist (automated + manual):
| Check | Automated | Tool |
|---|---|---|
lang="he" present | Yes | axe-core |
dir="rtl" present | Yes | Custom rule |
| Color contrast 4.5:1 | Yes | axe-core |
| All images have alt text | Yes | axe-core |
| Form inputs have labels | Yes | axe-core |
| Skip navigation link | Yes | axe-core |
| Keyboard navigation | Manual | Tab-through test |
| Screen reader compatibility | Manual | NVDA/VoiceOver test |
| Hebrew error messages | Manual | Visual inspection |
| Accessibility statement | Manual | Page existence check |
See references/is-5568.md for the complete checklist mapped to IS 5568 clauses.
Recommended MCP Servers
No relevant MCP server applies to Israeli accessibility compliance. The skills-il MCP directory has no accessibility, IS 5568, or WCAG-auditing MCP at this time. The audit in this skill runs as a local Python script (scripts/audit_a11y.py), not through an MCP. If an accessibility-audit MCP is added to the directory later, prefer it for live-site scanning and keep the script for offline checks.
Examples
Example 1: Audit Existing Israeli Website
User says: "Check if my website meets Israeli accessibility standards" Result: Run scripts/audit_a11y.py against the URL, check for IS 5568 requirements including Hebrew lang attribute, RTL direction, contrast ratios, ARIA labels in Hebrew, skip navigation, and accessibility statement page. Generate a compliance report with pass/fail per criterion.
Example 2: Add Accessibility Statement Page
User says: "I need to add an accessibility page to comply with Israeli law" Result: Create a Hebrew accessibility statement (Hatzaharat Negishot) page with all legally required sections: compliance level, accessibility features, known limitations, contact information for the accessibility coordinator (rakaz negishot), and last update date.
Example 3: Fix RTL Form Accessibility
User says: "Screen readers are not reading my Hebrew form correctly" Result: Add dir="rtl" to the form element, ensure all labels are associated with inputs and use Hebrew text, add aria-required="true" for mandatory fields, provide Hebrew error messages with role="alert", and set dir="ltr" on numeric inputs like phone and ID number fields.
Example 4: Make Hebrew Data Table Accessible
User says: "My Hebrew table is not accessible to screen readers" Result: Add dir="rtl" to the table element, include a Hebrew caption, use scope="col" and scope="row" on header cells, mark LTR content like order numbers with dir="ltr", and ensure logical reading order matches visual RTL order.
Bundled Resources
Scripts
scripts/audit_a11y.py-- Run IS 5568 accessibility audit: automated checks for Hebrew lang attribute, RTL direction, ARIA labels, contrast ratios, and skip navigation using axe-core and selenium. Generates compliance report with pass/fail per IS 5568 clause. Run:python scripts/audit_a11y.py --help
References
references/is-5568.md-- Complete IS 5568 standard reference: clause-by-clause requirements mapped to WCAG 2.0 AA, Israeli-specific additions, exemption tiers, the two-track enforcement model (civil damages + Commission administrative penalty) with the 60-day cure period, the accessibility coordinator role, mandatory accessibility statement template, and checklist for compliance audits.references/widget-implementation.md-- Copy-pasteable TypeScript/React code for a Regulation 35 accessibility preferences widget: pub-sub prefs store withuseSyncExternalStore, class-based CSS toggles, FOUC bootstrap script,Alt+Akeyboard shortcut (layout-independent viae.code), framer-motionMotionConfigwiring, and counter-invert rules. Consult when the user wants to ship the widget surface itself, not just audit compliance.
Building a Compliant Accessibility Preferences Widget
Regulation 35 and IS 5568 require Israeli consumer-facing sites to expose an accessibility control surface that users can operate with the keyboard, typically a floating widget with toggles for contrast, text size, line spacing, cursor, and motion. This widget is a user-preference comfort tool, not an automation overlay. The difference is legally and financially significant: the FTC fined accessiBe $1M in April 2025 for misleading claims that its overlay auto-remediated sites (it didn't). The widget you ship must do only what the user asks it to do.
Feature Set
A minimum-viable Regulation 35 widget exposes these toggles:
| Toggle | Type | Values |
|---|---|---|
| Highlight links | Binary | on / off |
| Contrast mode | Cycle | off / high / invert / monochrome |
| Text size | Cycle | 100% / 115% / 130% / 150% |
| Line spacing | Cycle | normal / 1.6 / 2.0 |
| Readable font | Binary | on / off (OS stack only, no webfont) |
| Highlight headings | Binary | on / off |
| Black cursor | Binary | on / off |
| Large cursor | Binary | on / off |
| Stop animations | Binary | on / off |
| Reset | Action | clears all preferences |
Architecture
Three parts: a preferences store, a UI panel, and a CSS layer.
1. Preferences store. A pub-sub store (subscribe / getSnapshot / getServerSnapshot / set / reset) consumed by React via useSyncExternalStore. State is persisted to localStorage under a versioned key (e.g., site_a11y_prefs_v1). Version mismatches invalidate stored state so schema bumps do not leave stale fields around. Pre-populate the in-memory cache on notify() so subscriber fan-out does not trigger redundant localStorage reads.
2. UI panel. A floating trigger button (fixed bottom-6 start-6 z-40, RTL-aware via CSS logical properties) opens a Radix Sheet containing a 3-column grid of toggle cards plus a Reset action. The trigger carries aria-expanded, aria-controls, and aria-keyshortcuts="Alt+A".
3. CSS layer. Every visual change is driven by CSS classes on <html> (for example a11y-contrast-high, a11y-text-150, a11y-lines-20, a11y-reduce-motion). The widget never mutates content DOM. It does not inject alt text, reorder nodes, or rewrite ARIA. That is the line that separates a compliant user-preference tool from a banned overlay.
Single-Source Class Rules
The class list applied at runtime must be identical to the one applied by the FOUC bootstrap script (below), or users see a flash of unstyled preferences on every page load. Define the mapping once and generate both the runtime applyPrefsToElement() function and the bootstrap <script> body from the same table:
const CLASS_RULES = [
['a11y-links', (p) => p.links, '!!p.links'],
['a11y-contrast-high', (p) => p.contrast === 'high', "p.contrast==='high'"],
['a11y-contrast-invert', (p) => p.contrast === 'invert', "p.contrast==='invert'"],
['a11y-contrast-mono', (p) => p.contrast === 'mono', "p.contrast==='mono'"],
['a11y-text-115', (p) => p.textSize === 115, 'p.textSize===115'],
['a11y-text-130', (p) => p.textSize === 130, 'p.textSize===130'],
['a11y-text-150', (p) => p.textSize === 150, 'p.textSize===150'],
] as const;FOUC Prevention
Preferences live in localStorage, which means the first paint happens at default styling and only after React hydrates does the widget re-apply the user's settings. That flash is unacceptable for users who rely on high contrast or 150% text. Fix it with an inline <script> in <head> that runs synchronously before React hydrates:
// Generated from CLASS_RULES so runtime and bootstrap can't drift
export const A11Y_BOOTSTRAP_SCRIPT =
`(function(){try{var raw=localStorage.getItem('site_a11y_prefs_v1');` +
`if(!raw)return;var p=JSON.parse(raw);if(p.version!==1)return;` +
`var c=document.documentElement.classList;` +
CLASS_RULES.map(([cls,,js]) => `c.toggle(${JSON.stringify(cls)},${js})`).join(';') +
`}catch(e){}})()`;In your root layout:
<head>
<script dangerouslySetInnerHTML={{ __html: A11Y_BOOTSTRAP_SCRIPT }} />
</head>Keep a useEffect safety net in the widget component that re-applies classes after mount. If localStorage is blocked (private browsing, quota exceeded), the bootstrap silently returns and the safety net covers the case.
Keyboard Shortcut: Use e.code, Not e.key
Regulation 35 requires the widget to be reachable from any focus context. Alt+A is the industry default. Detect it via e.code, not e.key:
if (e.altKey && !e.ctrlKey && !e.metaKey && !e.shiftKey && e.code === 'KeyA') {
e.preventDefault();
togglePanel();
}On macOS, Alt+A produces the dead-key å for e.key, which fails the intuitive e.key === 'a' check. e.code is the physical key position and is layout-independent across macOS, Windows, and Linux.
ARIA Correctness
- Binary toggles (links highlight, readable font, cursor, motion, headings): use
aria-pressed={active}. - Cycling toggles (contrast, text size, line spacing): omit `aria-pressed`. Reading "pressed" aloud is misleading when the control has more than two states. The accessible name itself should carry the current value:
aria-label={"${label}: ${valueLabel}"}. - Live region announcing state changes: use
role="status" aria-live="polite"and render it outside the Sheet portal. Portals unmount when the Sheet closes; a live region inside the portal loses late-arriving announcements.
framer-motion / Reduced Motion
If the app uses framer-motion, wrap the tree in a <MotionConfig> that mirrors the Stop Animations toggle:
<MotionConfig reducedMotion={prefs.reduceMotion ? 'always' : 'user'}>'always' forces reduced motion when the widget toggle is on. 'user' falls back to the OS prefers-reduced-motion media query when the toggle is off, so system-level requests are still honored.
Counter-Invert the Widget
If the user enables invert or monochrome contrast, the whole page is filtered. The widget itself must be counter-inverted so the user can still read it to turn the setting off:
html.a11y-contrast-invert #a11y-widget-panel,
html.a11y-contrast-invert #a11y-widget-trigger {
filter: invert(1) hue-rotate(180deg);
}Forget this and users end up with an unreadable widget they cannot deactivate.
Print Rule
Reset every a11y-* class in print context so high-contrast filters and inverted colors do not follow the user to paper:
@media print {
html[class*="a11y-"] { filter: none !important; }
html[class*="a11y-text-"] { font-size: 100% !important; }
html[class*="a11y-lines-"] { line-height: normal !important; }
}See references/widget-implementation.md for complete copy-pasteable code covering the pub-sub store, the FOUC bootstrap, the React component with the ToggleCard grid, the MotionA11yProvider, and the CSS class reference table.
Avoiding Overlay Anti-Patterns
Accessibility overlay products (accessiBe, UserWay, AudioEye) claim to make sites compliant by injecting JavaScript that auto-generates alt text, rewrites ARIA, and fixes inaccessible markup at runtime. Disability advocates and US regulators have documented that overlay-protected sites still fail screen-reader testing. In April 2025 the FTC fined accessiBe $1M and ordered ongoing compliance monitoring for misleading advertising about its overlay's capabilities.
The Israeli Commission for Equal Rights of Persons with Disabilities has not endorsed any overlay product. IS 5568 compliance is evaluated against the site's actual rendered HTML, not against claims made by a plug-in.
When building the widget above, enforce these scope fences:
| Do | Do NOT |
|---|---|
Toggle CSS classes on <html> | Mutate content DOM, rewrite alt attributes, or inject ARIA |
| Provide user-controlled preferences (contrast, text size, motion) | Claim the widget alone makes the site "IS 5568 compliant" or "WCAG compliant" |
| Document scope as a comfort tool in the accessibility statement | Display a certification badge or "audited by" claim sourced from a vendor plug-in |
| Use OS font stacks for the readable-font toggle | Inject a webfont that changes rendered text width and re-flows past critical content |
Persist preferences to localStorage and a cookie you control | Use third-party overlay SDKs that fingerprint users or apply tracking cookies as a side effect |
The widget is one layer of compliance. The other layers, semantic HTML, correct dir and lang, keyboard operability, real screen-reader testing, proper form labels, working focus management, the accessibility statement page (Hatzaharat Negishot), and a named accessibility coordinator, all have to be built into the site itself. No widget substitutes for that work.
Gotchas
- Israeli accessibility law (IS 5568) is anchored to WCAG 2.0 AA (sources differ on whether the current edition reaches 2.1), and adds Israeli-specific requirements on top, including the bilingual-content expectation for public bodies that serve the public in Hebrew and Arabic (see Step 5). Agents may apply only WCAG without the Israeli additions.
- Agents may copy a stale "businesses with 25+ employees or 300,000 NIS revenue must comply" rule. That figure is not in the regulations. The website-accessibility duty is universal for public-facing services, subject to revenue-based exemptions (see Step 2, Who Is Exempt). The 25-employee number is the trigger for appointing an accessibility coordinator, a separate duty.
- Agents may skip the 60-day cure period. A non-compliant operator must first receive a fix notice and be given up to 60 days before a suit or class-action request has a cause of action (see Step 3).
- Screen readers for Hebrew (NVDA, JAWS) read RTL text differently than LTR. Agents may generate ARIA labels assuming LTR reading order, which confuses Hebrew screen reader users.
- Israeli law requires accessibility statements (hatzaharat negishut) to be published on every website. Agents may generate WCAG-compliant sites without this mandatory statement page.
- Color contrast requirements in IS 5568 match WCAG (4.5:1 for normal text, 3:1 for large text), but agents may not account for Hebrew font rendering, which can appear thinner than Latin fonts at the same size, requiring slightly higher contrast.
Reference Links
| Source | URL | What to Check |
|---|---|---|
| Commission for Equal Rights of Persons with Disabilities | https://www.gov.il/he/departments/moj_disability_rights/govil-landing-page | Israeli accessibility law, enforcement, complaints |
| IS 5568 / Tav Negishut | https://www.sii.org.il/en/ | Israeli Standards Institute source for the IS 5568 standard |
| Equal Rights Act (Nevo) | https://www.nevo.co.il/law_html/law01/p214m2_001.htm | Legal text of the Equal Rights for Persons with Disabilities Act |
| WCAG 2.0 quick reference | https://www.w3.org/WAI/WCAG21/quickref/?versions=2.0 | Success criteria and techniques for AA compliance, the version IS 5568 is anchored to |
| Website accessibility exemptions (Kol Zchut) | https://www.kolzchut.org.il/he/פטור_מחובת_הנגשה_לאתרי_אינטרנט_ואפליקציות | Revenue-based exemption tiers and how to claim them |
| NVDA Hebrew support | https://www.nvaccess.org/ | Free screen reader widely used for Hebrew a11y testing |
Troubleshooting
Error: "Screen reader announces content in wrong order"
Cause: Visual RTL order does not match DOM order, or missing dir attribute Solution: Ensure the DOM source order matches the intended reading order for RTL. Add dir="rtl" to container elements. Use CSS logical properties for layout instead of physical positioning that may conflict with reading order.
Error: "Hebrew form validation messages not announced"
Cause: Error messages not using ARIA live regions or alert role Solution: Add role="alert" to error message containers and ensure they are populated dynamically after validation. Use aria-describedby to link error messages to their input fields. Error text must be in Hebrew for Hebrew forms.
Error: "Skip navigation link not working in RTL layout"
Cause: Skip link positioned off-screen using physical CSS (left: -9999px) Solution: Use inset-inline-start: -9999px instead of left: -9999px for the skip link. Ensure the target element has id and tabindex="-1" for focus management. Test that the skip link is the first focusable element in tab order.
{
"schemaVersion": "1.0",
"skill": "israeli-accessibility-compliance",
"generated_at": "2026-06-21T00:00:00Z",
"claims": [
{
"claim_id": "legal-framework",
"claim": "Israeli web accessibility is mandated by the Equal Rights for Persons with Disabilities Act 1998 and the Equal Rights for Persons with Disabilities (Service Accessibility Accommodations) Regulations 2013, which incorporate Israeli Standard IS 5568 Part 1.",
"source_url": "https://www.nevo.co.il/law_html/law01/p214m2_001.htm",
"raw_snippet": "חוק שוויון זכויות לאנשים עם מוגבלות, התשנ\"ח-1998.",
"fetched_at": "2026-06-21T00:00:00Z",
"appears_in": ["SKILL.md", "SKILL_HE.md", "references/is-5568.md"]
},
{
"claim_id": "exemption-thresholds",
"claim": "Website-accessibility exemptions are based on revenue, not employee count (there is no 25-employee or 300,000 NIS trigger). The small-business threshold figure is contested between sources: Kol Zchut states 120,000 NIS for the renewable 3-year temporary exemption, while some accessibility sources (Nagishut Israel) cite 100,000 NIS, so the skill presents it as 'sources differ, confirm against the regulation'. The next tier runs from that threshold up to 1,000,000 NIS (3-year exemption for pre-26-Oct-2017 existing sites); above 1,000,000 NIS no automatic exemption (must apply for a heavy-burden exemption). Full exemption for osek patur.",
"source_url": "https://www.kolzchut.org.il/he/פטור_מחובת_הנגשה_לאתרי_אינטרנט_ואפליקציות",
"raw_snippet": "פטור זמני לבעל מחזור כספי שנתי ממוצע של עד 120,000 ש\"ח; פטור לאתר קיים של בעל מחזור שבין 120,000 ל-1,000,000 ש\"ח שהוקם לפני 26.10.2017. הפטור מבוסס על מחזור ולא על מספר עובדים, ואין סף של 25 עובדים או 300,000 ש\"ח. מקורות אחרים (נגישות ישראל) מציינים 100,000 ש\"ח כסף, ולכן המקורות חלוקים והסכום המדויק טעון אימות מול נוסח התקנות.",
"fetched_at": "2026-06-21T00:00:00Z",
"appears_in": ["SKILL.md", "SKILL_HE.md", "references/is-5568.md"]
},
{
"claim_id": "statutory-damages-50000",
"claim": "A claimant can sue for up to 50,000 NIS in statutory damages without proof of harm for an inaccessible site, subject to a 60-day cure-notice period.",
"source_url": "https://www.kolzchut.org.il/he/פטור_מחובת_הנגשה_לאתרי_אינטרנט_ואפליקציות",
"raw_snippet": "פיצוי ללא הוכחת נזק בסך של עד 50,000 ש\"ח.",
"fetched_at": "2026-06-21T00:00:00Z",
"appears_in": ["SKILL.md", "SKILL_HE.md", "references/is-5568.md"]
},
{
"claim_id": "amendment-23-izum",
"claim": "Amendment 23 to the Equal Rights Act gave the Commission an administrative-enforcement track with monetary penalties (izum kaspi), scaled by operator size: micro business up to 2,000,000 NIS turnover, small business up to 20,000,000 NIS, regular business above that or a public authority.",
"source_url": "https://www.gov.il/he/pages/equality_law_23_amendment_explanation",
"raw_snippet": "תיקון 23 לחוק שוויון זכויות לאנשים עם מוגבלות מסמיך את הנציבות להטיל עיצומים כספיים. גובה העיצום נקבע לפי גודל העוסק: עסק זעיר עד מחזור 2,000,000 ש\"ח, עסק קטן עד 20,000,000 ש\"ח, ועסק רגיל מעל זה.",
"fetched_at": "2026-06-21T00:00:00Z",
"appears_in": ["SKILL.md", "SKILL_HE.md", "references/is-5568.md"]
},
{
"claim_id": "regulation-35-morshe",
"claim": "Regulation 35 of the Service Accessibility Regulations governs website/app accessibility and its technological-difficulty exemption requires a written opinion from a certified service-accessibility expert (morshe negishut hasherut) in consultation with an internet-accessibility professional.",
"source_url": "https://aisrael.org/אתרי-אינטרנט-נגישים-סקירה-דרישות-וס/",
"raw_snippet": "תקנה 35 לתקנות נגישות השירות עוסקת בהנגשת אתרי אינטרנט; פטור בשל קושי טכנולוגי מותנה בחוות דעת של מורשה נגישות השירות.",
"fetched_at": "2026-06-21T00:00:00Z",
"appears_in": ["SKILL.md", "SKILL_HE.md"]
},
{
"claim_id": "widget-text-size-steps",
"claim": "The accessibility preferences widget cycles text size through 100%, 115%, 130%, and 150% steps. These are the widget's own UI scale steps (a comfort-tool design choice), supporting the WCAG resize-text guidance; they are not an external regulatory figure.",
"source_url": "https://www.w3.org/WAI/WCAG21/quickref/?versions=2.0",
"raw_snippet": "Success Criterion 1.4.4 Resize text: text can be resized without assistive technology up to 200 percent without loss of content or functionality.",
"fetched_at": "2026-06-21T00:00:00Z",
"appears_in": ["SKILL.md", "SKILL_HE.md"]
},
{
"claim_id": "example-placeholder-domain",
"claim": "example.co.il is used only as an illustrative placeholder domain in code examples and the accessibility-statement template; it is not a real operational URL.",
"source_url": "https://example.co.il",
"raw_snippet": "example.co.il is a placeholder domain used in documentation examples, analogous to the reserved example.com domain.",
"fetched_at": "2026-06-21T00:00:00Z",
"appears_in": ["SKILL.md", "SKILL_HE.md"]
},
{
"claim_id": "coordinator-25-employees",
"claim": "The Service Accessibility Regulations require a public body or a service provider with 25 or more employees to appoint an accessibility coordinator (rakaz negishut). This triggers the coordinator appointment, not the website-accessibility duty.",
"source_url": "https://www.kolzchut.org.il/he/רכז_נגישות",
"raw_snippet": "גוף ציבורי או מי שמעסיק 25 עובדים או יותר חייב למנות רכז נגישות מבין עובדיו.",
"fetched_at": "2026-06-21T00:00:00Z",
"appears_in": ["SKILL.md", "SKILL_HE.md", "references/is-5568.md"]
},
{
"claim_id": "is5568-wcag20-anchor",
"claim": "IS 5568 is legally anchored to WCAG 2.0 AA (the September 2023 Part 1 edition stays anchored to 2.0 in the dominant reading); some sources describe the current edition as WCAG 2.1 AA, so sources differ.",
"source_url": "https://www.w3.org/WAI/WCAG21/quickref/?versions=2.0",
"raw_snippet": "How to Meet WCAG (Quick Reference) filtered to show only the WCAG 2.0 Level A and AA success criteria.",
"fetched_at": "2026-06-21T00:00:00Z",
"appears_in": ["SKILL.md", "SKILL_HE.md", "references/is-5568.md"]
},
{
"claim_id": "is5568-part2",
"claim": "IS 5568 is split into Part 1 (web content) and Part 2 (accessibility of digital documents such as PDFs and Office files), published 2020. The standard is maintained by the Standards Institution of Israel (SII).",
"source_url": "https://www.sii.org.il/en/",
"raw_snippet": "The Standards Institution of Israel develops and publishes Israeli standards, including IS 5568 on web and digital-document accessibility.",
"fetched_at": "2026-06-21T00:00:00Z",
"appears_in": ["SKILL.md", "SKILL_HE.md", "references/is-5568.md"]
},
{
"claim_id": "accessibe-ftc-fine",
"claim": "In April 2025 the FTC approved a final order requiring accessibility-overlay vendor accessiBe to pay $1,000,000, supporting the skill's warning against automatic accessibility overlays/widgets.",
"source_url": "https://www.ftc.gov/news-events/news/press-releases/2025/04/ftc-approves-final-order-requiring-accessibe-pay-1-million",
"raw_snippet": "FTC Approves Final Order Requiring accessiBe to Pay $1 Million over deceptive claims that its automated overlay product could make any website compliant.",
"fetched_at": "2026-06-21T00:00:00Z",
"appears_in": ["SKILL.md", "SKILL_HE.md"]
},
{
"claim_id": "nvda-screen-reader",
"claim": "NVDA is a free open-source Windows screen reader used to test Hebrew screen-reader compatibility.",
"source_url": "https://www.nvaccess.org/",
"raw_snippet": "NVDA (NonVisual Desktop Access) is a free screen reader for Windows from NV Access.",
"fetched_at": "2026-06-21T00:00:00Z",
"appears_in": ["SKILL.md", "SKILL_HE.md"]
},
{
"claim_id": "commission-enforcement",
"claim": "The Commission for Equal Rights of Persons with Disabilities at the Ministry of Justice enforces accessibility law and handles complaints.",
"source_url": "https://www.gov.il/he/departments/moj_disability_rights/govil-landing-page",
"raw_snippet": "נציבות שוויון זכויות לאנשים עם מוגבלות במשרד המשפטים אחראית על אכיפת חוק הנגישות וטיפול בתלונות.",
"fetched_at": "2026-06-21T00:00:00Z",
"appears_in": ["SKILL.md", "SKILL_HE.md"]
}
]
}
{
"author": "skills-il",
"version": "1.3.0",
"category": "localization",
"tags": {
"he": [
"נגישות",
"תקן-5568",
"נכים",
"RTL",
"ישראל",
"ווידג'ט",
"יישום"
],
"en": [
"accessibility",
"is-5568",
"wcag",
"rtl",
"israel",
"widget",
"implementation"
]
},
"display_name": {
"he": "נגישות אתרים ישראלית",
"en": "Israeli Accessibility Compliance"
},
"display_description": {
"he": "מטמיעים נגישות אתרים ישראלית לפי תקן IS 5568, שמעוגן ב-WCAG 2.0 AA (התקן מוסיף חלק מקריטריוני 2.1; המקורות חלוקים), לאפליקציות בעברית עם RTL. שימושי כשמשתמש שואל על חוק הנגישות בישראל, תקן 5568, מי פטור מהנגשת אתר, אכיפה וקנסות, רכז נגישות, תמיכת קוראי מסך בעברית או ביקורת נגישות לאתר. מכסה את הדרישות המשפטיות מכוח חוק שוויון זכויות לאנשים עם מוגבלות, מדרגות הפטור לפי מחזור, שני מסלולי האכיפה (פיצוי אזרחי ועיצום כספי של הנציבות) ותקופת התיקון של 60 יום, תפקיד רכז הנגישות, תאימות לקוראי מסך בעברית ותבניות ARIA ל-RTL. לא מיועד להנחיות WCAG כלליות ללא ההקשר הישראלי.",
"en": "Implement Israeli web accessibility compliance per IS 5568 standard, anchored to WCAG 2.0 AA (IS 5568 adds some 2.1-aligned criteria; sources differ), for Hebrew RTL applications. Use when user asks about Israeli accessibility law, \"negishot\" (accessibility), IS 5568, \"teken negishot\" (accessibility standard), \"nachim\" (disabilities), who is exempt from website accessibility, enforcement and penalties, the accessibility coordinator role, Hebrew screen reader support, RTL ARIA patterns, or accessibility audit for Israeli websites. Covers mandatory legal requirements under the Equal Rights for Persons with Disabilities Act, revenue-based exemption tiers, the two enforcement tracks (civil damages and the Commission administrative penalty) and the 60-day cure period, the accessibility coordinator role, Hebrew screen reader compatibility, and RTL-specific ARIA patterns. Do NOT use for general WCAG guidance without Israeli context (use standard a11y resources instead)."
},
"supported_agents": [
"claude-code",
"cursor",
"github-copilot",
"windsurf",
"opencode",
"codex",
"antigravity",
"gemini-cli"
]
}
{
"cycles": [
{
"version": "1.3.0",
"date": "2026-06-21",
"lessons": [
"CORRECTNESS: fixed exemption threshold 100,000 -> 120,000 NIS in SKILL.md, SKILL_HE.md, references/is-5568.md (table was internally inconsistent: 'under 100,000' but 'renewable up to 120,000'). Authoritative: Kol Zchut + Nagishut Israel.",
"Refined deadline history: new sites accessible from 25 Oct 2015, existing by 26 Oct 2017; the Oct 2020 date extended lower-revenue exemption tiers, not the universal deadline.",
"CRITICAL coverage added: standard-conformance != legal-compliance framing; certified expert (morshe negishut hasherut) opinion under Reg 35; IS 5568 Part 2 (digital documents / PDF accessibility, 2020).",
"MAJOR added: Step 9 accessibility statement now includes known-limitations section + audit date (aligns to the 7-point list in references/is-5568.md). Script: added User-Agent header (gov.il/Cloudflare 403 default python UA) + static-HTML-only note (contrast/JS need the axe-core path).",
"Deferred (MAJOR, next cycle): operationalize native mobile-app accessibility (iOS UIAccessibility / Android APIs) beyond TalkBack/VoiceOver naming; user-side complaint/enforcement path to the Commission. Deferred (MINOR): negishut hasherut vs negishut hamivne distinction; over-hedged WCAG-2.0 note appears 6+ times, could be stated once.",
"Kept gov.il moj_disability_rights URL (403 to bots but valid for humans; gov.il restructures, monitor).",
"CONTESTED FACT (Expert round 2): the small-business exemption threshold is disputed -- Kol Zchut (Judge-verified live) says 120,000 NIS, Nagishut Israel/iAccessibility say 100,000 NIS. Resolved by HEDGING in all 3 files ('sources differ: 100,000 or 120,000; confirm against the regulation') rather than flat-asserting either, mirroring the skill's WCAG-version hedge. Added negishut-hamivne scoping line and user-side complaint path (Step 3).",
"Deferred (MAJOR, next cycle): operationalize native mobile-app accessibility (iOS UIAccessibility / Android APIs) since apps are in-scope but only static HTML is covered; add feedback-mechanism as an explicit element in the inline Step 9 statement template (currently only in the reference's 7-point list); collapse the WCAG-2.0 'sources differ' hedge from ~9 repetitions to one authoritative statement so it stops diluting the load-bearing warnings; note that the bundled audit_a11y.py is static-only and won't audit React/SPA contrast (soften the 'primary audit path' framing in Step 10)."
]
}
]
}
Israeli Standard 5568 (IS 5568) Reference
Legal Framework
Equal Rights for Persons with Disabilities Act (1998)
- The 1998 act (Chok Shivyon Zechuyot Le'Anashim Im Mugbaluyot) established the statutory right to accessibility.
- Web and service accessibility are implemented through the Equal Rights for Persons with Disabilities (Service Accessibility Accommodations) Regulations, 2013 (Takanot Negishut LeSherut), which reference Israeli Standard IS 5568 Part 1.
- Mobile applications are covered under the same service accessibility regulations; later amendments and technical updates (2020, 2023) clarified the scope.
- Enforcement is carried out by the Commission for Equal Rights of Persons with Disabilities (Netzivut Shivyon Zechuyot) at the Ministry of Justice.
Who Is Exempt (Website Accessibility)
The Service Accessibility Regulations base website-accessibility exemptions on revenue, not on employee count. There is no "25 employees" or "300,000 NIS" trigger in the regulations.
| Operator | Exemption |
|---|---|
| Osek patur (VAT-exempt dealer) | Full exemption from website accessibility |
| Average annual revenue under the small-business threshold (sources differ: some cite 100,000 NIS, others 120,000 NIS; confirm against the current regulation) | Temporary 3-year exemption, renewable every 3 years while average revenue stays at or below that threshold |
| Average annual revenue from that threshold up to 1,000,000 NIS | 3-year exemption for an existing site whose operation began before 26 October 2017; a new site built after that date must be accessible |
| Average annual revenue above 1,000,000 NIS | No automatic exemption. The operator must apply to the Commission to claim a heavy-burden exemption |
Most of these exemptions are automatic and need no approval, but the operator must re-check revenue every 3 years. An exemption from website accessibility does not exempt the operator from other service-accessibility duties.
Penalty Structure
Two enforcement tracks run in parallel.
| Violation Type | Penalty | Notes |
|---|---|---|
| Statutory civil damages per lawsuit | Up to 50,000 NIS without proof of harm | Claimant only needs to show the site is non-compliant; codified in the Equal Rights Act for accessibility violations |
| 60-day cure period | No penalty if fixed in time | A deviation is not treated as a violation unless the operator was sent a fix notice and failed to fix it within a reasonable time, no later than 60 days from receiving the notice. A class-action request has no cause of action without a prior fix notice |
| Administrative monetary penalty (izum kaspi) | Amount scaled by operator size and accommodation cost, plus a per-day charge while the violation continues | Added by Amendment 23 to the Equal Rights Act. The Commission can also issue warnings, require commitments, and issue accessibility orders. Operator sizes: micro business (turnover up to 2,000,000 NIS), small business (up to 20,000,000 NIS), regular business (above that) or public authority. Reductions apply for a clean prior record and for corrective action. This is separate from and on top of the 50,000 NIS civil damages |
| Class action | Larger cumulative damages possible | Class actions for accessibility non-compliance are an active litigation trend in Israel; still subject to the 60-day cure-notice requirement |
Accessibility Coordinator (Rakaz Negishut)
The Service Accessibility Regulations require a service provider that is a public body, or that employs 25 or more employees, to appoint one of its staff as an accessibility coordinator. This 25-employee figure triggers the coordinator appointment, not the website-accessibility duty itself, which is universal subject to the revenue exemptions above. The coordinator gives the public information about the provider's accessibility arrangements, handles accessibility inquiries and complaints, and helps the responsible parties carry out and maintain the accommodations. The coordinator's name and contact details must appear in the accessibility statement.
IS 5568 Clauses Mapped to WCAG AA
The original IS 5568 took effect aligned with WCAG 2.0 AA. The dominant reading is that IS 5568, including the September 2023 Part 1 edition, stays legally anchored to WCAG 2.0 AA; some sources describe the current edition as WCAG 2.1 AA, so sources differ. Treat WCAG 2.0 AA plus the Israeli additions as the legal floor and WCAG 2.1/2.2 criteria as future-proofing. Clause numbering below follows the WCAG principle structure that the Israeli standard adopts.
Principle 1: Perceivable
| IS 5568 Clause | WCAG Criterion | Requirement |
|---|---|---|
| 1.1 | 1.1.1 Non-text Content | Alt text for images, Hebrew preferred |
| 1.2 | 1.2.1-1.2.5 Time-based Media | Captions/transcripts, Hebrew supported |
| 1.3 | 1.3.1-1.3.3 Adaptable | Meaningful structure, RTL reading order |
| 1.4 | 1.4.1-1.4.5 Distinguishable | Color contrast 4.5:1, resizable text |
Principle 2: Operable
| IS 5568 Clause | WCAG Criterion | Requirement |
|---|---|---|
| 2.1 | 2.1.1-2.1.2 Keyboard | Full keyboard access, no traps, RTL tab order |
| 2.2 | 2.2.1-2.2.2 Enough Time | Adjustable time limits, pause/stop moving content |
| 2.3 | 2.3.1 Seizures | No flashing content above 3/second |
| 2.4 | 2.4.1-2.4.7 Navigable | Skip nav, focus visible, descriptive headings |
Principle 3: Understandable
| IS 5568 Clause | WCAG Criterion | Requirement |
|---|---|---|
| 3.1 | 3.1.1-3.1.2 Readable | lang="he" declared, language of parts |
| 3.2 | 3.2.1-3.2.4 Predictable | Consistent navigation, no context changes on focus |
| 3.3 | 3.3.1-3.3.4 Input Assistance | Error identification in Hebrew, labels |
Principle 4: Robust
| IS 5568 Clause | WCAG Criterion | Requirement |
|---|---|---|
| 4.1 | 4.1.1-4.1.2 Compatible | Valid HTML, name/role/value for components |
Israeli-Specific Additions (Beyond WCAG)
Mandatory Accessibility Statement
Every website must include an accessibility statement page containing: 1. Organization commitment to accessibility 2. Standard being followed (IS 5568) 3. List of accessibility features implemented 4. Known accessibility limitations 5. Contact details for accessibility coordinator 6. Date of last accessibility audit 7. Available in Hebrew
Feedback Mechanism
Sites must provide a way for users to report accessibility issues:
- Email address for accessibility inquiries
- Phone number for accessibility support
- Contact form accessible to assistive technology users
RTL-Specific Requirements
dir="rtl"must be declared on Hebrew content- Keyboard navigation must follow RTL visual flow
- Screen reader reading order must match visual RTL order
- Form labels must align correctly in RTL context
Compliance Checklist
Automated Checks
- [ ]
lang="he"on html element - [ ]
dir="rtl"on html element - [ ] Page title present and descriptive
- [ ] All images have alt attributes
- [ ] Color contrast minimum 4.5:1
- [ ] Form inputs have associated labels
- [ ] No duplicate IDs
- [ ] Valid HTML structure
- [ ] Skip navigation link present
Manual Checks
- [ ] Keyboard navigation works in RTL order
- [ ] Screen reader announces content correctly in Hebrew
- [ ] Focus indicator visible on all interactive elements
- [ ] Error messages in Hebrew and announced by screen reader
- [ ] Accessibility statement page exists and is complete
- [ ] Feedback mechanism is functional
- [ ] Dynamic content updates announced via ARIA live regions
- [ ] Heading hierarchy is logical (one H1, proper nesting)
Accessibility Preferences Widget: Implementation Reference
Copy-pasteable TypeScript/React code for a Regulation 35 / IS 5568 accessibility preferences widget. IS 5568 is anchored to WCAG 2.0 AA (it adds some 2.1-aligned criteria; sources differ).
Consult this file when the user wants to ship the widget itself, not just audit an existing site. The main SKILL.md covers compliance guidance; this file covers the implementation.
Scope reminder: this widget is a user-preference comfort tool. It toggles CSS classes on <html>. It does NOT mutate content DOM, inject alt text, rewrite ARIA, or auto-remediate anything. See "Avoiding Overlay Anti-Patterns" in SKILL.md.
Stack assumptions: Next.js App Router (13+), React 18+, TypeScript, Tailwind (optional but referenced), shadcn/ui Sheet + Button, framer-motion (optional).
---
1. Preferences Core (lib/a11y-prefs/core.ts)
Pure helpers with no 'use client' directive, safe to import from Server Components (needed for the FOUC <script> injection in the root layout).
export type ContrastMode = 'off' | 'high' | 'invert' | 'mono';
export type TextSize = 100 | 115 | 130 | 150;
export type LineSpacing = 'normal' | '16' | '20';
export interface A11yPrefs {
version: number;
links: boolean;
contrast: ContrastMode;
textSize: TextSize;
lineSpacing: LineSpacing;
readableFont: boolean;
headings: boolean;
cursorBlack: boolean;
cursorLarge: boolean;
reduceMotion: boolean;
}
export const A11Y_VERSION = 1;
export const A11Y_STORAGE_KEY = 'site_a11y_prefs_v1';
export const DEFAULT_PREFS: A11yPrefs = Object.freeze({
version: A11Y_VERSION,
links: false,
contrast: 'off',
textSize: 100,
lineSpacing: 'normal',
readableFont: false,
headings: false,
cursorBlack: false,
cursorLarge: false,
reduceMotion: false,
});
const CONTRAST_CYCLE: ContrastMode[] = ['off', 'high', 'invert', 'mono'];
const TEXT_SIZE_CYCLE: TextSize[] = [100, 115, 130, 150];
const LINE_SPACING_CYCLE: LineSpacing[] = ['normal', '16', '20'];
export function nextContrast(c: ContrastMode): ContrastMode {
return CONTRAST_CYCLE[(CONTRAST_CYCLE.indexOf(c) + 1) % CONTRAST_CYCLE.length];
}
export function nextTextSize(s: TextSize): TextSize {
return TEXT_SIZE_CYCLE[(TEXT_SIZE_CYCLE.indexOf(s) + 1) % TEXT_SIZE_CYCLE.length];
}
export function nextLineSpacing(l: LineSpacing): LineSpacing {
return LINE_SPACING_CYCLE[(LINE_SPACING_CYCLE.indexOf(l) + 1) % LINE_SPACING_CYCLE.length];
}
export function isAnyActive(prefs: A11yPrefs): boolean {
return (
prefs.links ||
prefs.contrast !== 'off' ||
prefs.textSize !== 100 ||
prefs.lineSpacing !== 'normal' ||
prefs.readableFont ||
prefs.headings ||
prefs.cursorBlack ||
prefs.cursorLarge ||
prefs.reduceMotion
);
}
// SINGLE SOURCE OF TRUTH: drives both the runtime applyPrefsToElement() and
// the FOUC bootstrap script below. The JS-expression string column is serialized
// into the bootstrap so the two cannot drift.
const CLASS_RULES: ReadonlyArray<[
className: string,
predicate: (p: A11yPrefs) => boolean,
js: string
]> = [
['a11y-links', (p) => p.links, '!!p.links'],
['a11y-contrast-high', (p) => p.contrast === 'high', "p.contrast==='high'"],
['a11y-contrast-invert', (p) => p.contrast === 'invert', "p.contrast==='invert'"],
['a11y-contrast-mono', (p) => p.contrast === 'mono', "p.contrast==='mono'"],
['a11y-text-115', (p) => p.textSize === 115, 'p.textSize===115'],
['a11y-text-130', (p) => p.textSize === 130, 'p.textSize===130'],
['a11y-text-150', (p) => p.textSize === 150, 'p.textSize===150'],
['a11y-lines-16', (p) => p.lineSpacing === '16', "p.lineSpacing==='16'"],
['a11y-lines-20', (p) => p.lineSpacing === '20', "p.lineSpacing==='20'"],
['a11y-readable-font', (p) => p.readableFont, '!!p.readableFont'],
['a11y-headings', (p) => p.headings, '!!p.headings'],
['a11y-cursor-black', (p) => p.cursorBlack, '!!p.cursorBlack'],
['a11y-cursor-large', (p) => p.cursorLarge, '!!p.cursorLarge'],
['a11y-reduce-motion', (p) => p.reduceMotion, '!!p.reduceMotion'],
];
export function applyPrefsToElement(el: HTMLElement, prefs: A11yPrefs): void {
for (const [cls, pred] of CLASS_RULES) el.classList.toggle(cls, pred(prefs));
}
// Inline <script> executed in <head> BEFORE React hydrates, so persisted prefs
// are applied synchronously and the user does not see a flash of default styling.
export const A11Y_BOOTSTRAP_SCRIPT: string =
`(function(){try{var raw=localStorage.getItem(${JSON.stringify(A11Y_STORAGE_KEY)});` +
`if(!raw)return;var p=JSON.parse(raw);if(p.version!==${A11Y_VERSION})return;` +
`var c=document.documentElement.classList;` +
CLASS_RULES.map(([cls, , js]) => `c.toggle(${JSON.stringify(cls)},${js})`).join(';') +
`}catch(e){}})()`;---
2. Pub-Sub Store (lib/a11y-prefs/store.ts)
Client-only. Consumed via useSyncExternalStore so React can correctly render server, hydration, and client phases.
'use client';
import { useSyncExternalStore } from 'react';
import {
A11Y_STORAGE_KEY,
A11Y_VERSION,
DEFAULT_PREFS,
applyPrefsToElement,
type A11yPrefs,
} from './core';
export * from './core';
function readStorage(): A11yPrefs | null {
if (typeof window === 'undefined') return null;
try {
const raw = localStorage.getItem(A11Y_STORAGE_KEY);
if (!raw) return null;
const parsed = JSON.parse(raw) as A11yPrefs;
if (parsed.version !== A11Y_VERSION) return null;
return { ...DEFAULT_PREFS, ...parsed, version: A11Y_VERSION };
} catch {
return null;
}
}
const listeners = new Set<() => void>();
let cached: A11yPrefs | undefined;
function notify(next?: A11yPrefs) {
// Pre-populate the cache so subscribers' getSnapshot() calls don't each
// trigger a synchronous localStorage re-read during fan-out.
cached = next ?? readStorage() ?? DEFAULT_PREFS;
for (const cb of listeners) cb();
}
function writeStorage(prefs: A11yPrefs) {
try {
localStorage.setItem(A11Y_STORAGE_KEY, JSON.stringify(prefs));
} catch {
// quota / private mode, in-memory state only
}
}
export const a11yStore = {
subscribe(cb: () => void) {
listeners.add(cb);
return () => { listeners.delete(cb); };
},
getSnapshot(): A11yPrefs {
if (cached === undefined) cached = readStorage() ?? DEFAULT_PREFS;
return cached;
},
getServerSnapshot(): A11yPrefs {
return DEFAULT_PREFS;
},
set(partial: Partial<A11yPrefs>): A11yPrefs {
const next: A11yPrefs = { ...this.getSnapshot(), ...partial, version: A11Y_VERSION };
writeStorage(next);
if (typeof document !== 'undefined') applyPrefsToElement(document.documentElement, next);
notify(next);
return next;
},
reset(): A11yPrefs {
try { localStorage.removeItem(A11Y_STORAGE_KEY); } catch {}
if (typeof document !== 'undefined') applyPrefsToElement(document.documentElement, DEFAULT_PREFS);
notify(DEFAULT_PREFS);
return DEFAULT_PREFS;
},
};
export function useA11yPrefs(): A11yPrefs {
return useSyncExternalStore(
a11yStore.subscribe,
a11yStore.getSnapshot,
a11yStore.getServerSnapshot,
);
}---
3. Widget Component (components/accessibility/accessibility-widget.tsx)
Floating trigger + Radix Sheet panel + 3-column toggle grid + live region + Alt+A shortcut.
'use client';
import { useCallback, useEffect, useState, type ComponentType } from 'react';
import {
Accessibility, AlignJustify, CaseSensitive, Contrast, Heading,
Link2, MousePointer2, MousePointerClick, Pause, RotateCcw, Type,
} from 'lucide-react';
import { Button } from '@/components/ui/button';
import { Sheet, SheetContent, SheetHeader, SheetTitle } from '@/components/ui/sheet';
import { cn } from '@/lib/utils';
import {
a11yStore, applyPrefsToElement, isAnyActive,
nextContrast, nextLineSpacing, nextTextSize,
useA11yPrefs, type A11yPrefs,
} from '@/lib/a11y-prefs/store';
interface Props {
locale: 'he' | 'en';
dict: { /* labels, values, etc., see below */ };
}
export function AccessibilityWidget({ locale, dict }: Props) {
const prefs = useA11yPrefs();
const [open, setOpen] = useState(false);
const [announcement, setAnnouncement] = useState('');
const dir: 'rtl' | 'ltr' = locale === 'he' ? 'rtl' : 'ltr';
// Safety net: re-apply classes after hydration if the inline FOUC script
// failed (e.g. localStorage blocked). Don't call a11yStore.set({}), that
// would write back to storage and notify subscribers on every mount.
useEffect(() => {
applyPrefsToElement(document.documentElement, a11yStore.getSnapshot());
}, []);
// Alt+A keyboard shortcut. e.code (layout-independent) because macOS
// produces dead-key 'å' for e.key, which would miss an e.key === 'a' check.
useEffect(() => {
const onKey = (e: KeyboardEvent) => {
if (e.altKey && !e.ctrlKey && !e.metaKey && !e.shiftKey && e.code === 'KeyA') {
e.preventDefault();
setOpen(prev => !prev);
}
};
window.addEventListener('keydown', onKey);
return () => window.removeEventListener('keydown', onKey);
}, []);
const toggle = useCallback(
(feature: keyof A11yPrefs, value: A11yPrefs[keyof A11yPrefs], announce: string) => {
a11yStore.set({ [feature]: value } as Partial<A11yPrefs>);
setAnnouncement(`${dict.announceChange}: ${announce}`);
},
[dict.announceChange],
);
return (
<>
{/* Live region OUTSIDE the Sheet portal so announcements aren't lost
when the portal unmounts. */}
<div role="status" aria-live="polite" className="sr-only">
{announcement}
</div>
<button
id="a11y-widget-trigger"
type="button"
onClick={() => setOpen(true)}
aria-label={dict.triggerLabel}
aria-expanded={open}
aria-controls="a11y-widget-panel"
aria-keyshortcuts="Alt+A"
className={cn(
'fixed bottom-6 start-6 z-40 flex size-12 items-center justify-center',
'rounded-full bg-primary text-primary-foreground shadow-lg',
isAnyActive(prefs) && 'ring-2 ring-white/40',
)}
>
<Accessibility className="size-6" aria-hidden />
</button>
<Sheet open={open} onOpenChange={setOpen}>
<SheetContent
id="a11y-widget-panel"
side={dir === 'rtl' ? 'right' : 'left'}
className="w-3/4 sm:max-w-md"
>
<SheetHeader className="border-b">
<SheetTitle>{dict.title}</SheetTitle>
</SheetHeader>
<div className="flex-1 overflow-y-auto p-4">
<div className="grid grid-cols-3 gap-3">
{/* Binary toggles: aria-pressed={active} */}
<ToggleCard icon={Link2} label={dict.labels.links}
active={prefs.links} onClick={() => toggle('links', !prefs.links, dict.labels.links)} />
{/* Cycling toggles: OMIT aria-pressed, aria-label carries value */}
<ToggleCard icon={Contrast} label={dict.labels.contrast}
active={prefs.contrast !== 'off'}
valueLabel={prefs.contrast === 'off' ? undefined : dict.values.contrast[prefs.contrast]}
cycling
onClick={() => {
const next = nextContrast(prefs.contrast);
toggle('contrast', next, dict.values.contrast[next]);
}}
/>
{/* …text size, line spacing, readable font, headings, cursors, motion */}
</div>
<button
type="button"
onClick={() => { a11yStore.reset(); setAnnouncement(dict.reset); }}
className="mt-6 inline-flex items-center gap-1.5 text-sm text-destructive"
>
<RotateCcw className="size-4" aria-hidden />
{dict.reset}
</button>
</div>
</SheetContent>
</Sheet>
</>
);
}
interface ToggleCardProps {
icon: ComponentType<{ className?: string; 'aria-hidden'?: boolean }>;
label: string;
active: boolean;
valueLabel?: string;
/** True for cycling controls. When true, aria-pressed is omitted, the
* accessible name (aria-label) carries the current value instead. */
cycling?: boolean;
onClick: () => void;
}
function ToggleCard({ icon: Icon, label, active, valueLabel, cycling, onClick }: ToggleCardProps) {
return (
<Button
type="button"
variant="outline"
onClick={onClick}
aria-pressed={cycling ? undefined : active}
aria-label={valueLabel ? `${label}: ${valueLabel}` : label}
className={cn(
'flex aspect-square h-auto flex-col items-center justify-center gap-2',
'whitespace-normal rounded-xl p-3 text-center',
active && 'ring-1 ring-primary',
)}
>
<Icon className="size-6" aria-hidden />
<span className="text-xs font-medium">{label}</span>
{valueLabel && <span className="text-[10px] text-muted-foreground">{valueLabel}</span>}
</Button>
);
}---
4. framer-motion Reduced-Motion Provider
'use client';
import { MotionConfig } from 'framer-motion';
import { useA11yPrefs } from '@/lib/a11y-prefs/store';
export function MotionA11yProvider({ children }: { children: React.ReactNode }) {
const { reduceMotion } = useA11yPrefs();
// 'always' → always reduce. 'user' → fall back to prefers-reduced-motion.
return (
<MotionConfig reducedMotion={reduceMotion ? 'always' : 'user'}>
{children}
</MotionConfig>
);
}---
5. Layout Wiring
Root layout (Server Component)
Inject the FOUC bootstrap BEFORE React hydrates:
// app/layout.tsx
import { A11Y_BOOTSTRAP_SCRIPT } from '@/lib/a11y-prefs/core';
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html>
<head>
<script dangerouslySetInnerHTML={{ __html: A11Y_BOOTSTRAP_SCRIPT }} />
</head>
<body>{children}</body>
</html>
);
}Locale layout
Mount the widget and the framer-motion provider:
// app/[locale]/layout.tsx
import { AccessibilityWidget } from '@/components/accessibility/accessibility-widget';
import { MotionA11yProvider } from '@/components/accessibility/motion-a11y-provider';
export default async function LocaleLayout({
children,
params,
}: {
children: React.ReactNode;
params: Promise<{ locale: 'he' | 'en' }>;
}) {
const { locale } = await params;
const dict = await getDictionary(locale);
return (
<MotionA11yProvider>
{children}
<AccessibilityWidget locale={locale} dict={dict.accessibility.widget} />
</MotionA11yProvider>
);
}---
6. CSS Class Reference Table
Each toggle produces one or more CSS classes on <html>. Map them in globals.css with whatever rules make sense for the site.
| Toggle | Class(es) applied | Suggested CSS rule |
|---|---|---|
| Highlight links | .a11y-links | .a11y-links a { text-decoration: underline !important; font-weight: 600 !important; outline: 1px solid currentColor !important; outline-offset: 2px !important; } |
| Contrast high | .a11y-contrast-high | .a11y-contrast-high { filter: contrast(1.3); } |
| Contrast invert | .a11y-contrast-invert | .a11y-contrast-invert { filter: invert(1) hue-rotate(180deg); } + counter-invert widget |
| Contrast mono | .a11y-contrast-mono | .a11y-contrast-mono { filter: grayscale(1) contrast(1.1); } + counter-invert widget |
| Text size | .a11y-text-115, .a11y-text-130, .a11y-text-150 | .a11y-text-150 { font-size: 150%; } |
| Line spacing | .a11y-lines-16, .a11y-lines-20 | .a11y-lines-20 { line-height: 2 !important; } |
| Readable font | .a11y-readable-font | .a11y-readable-font { font-family: system-ui, -apple-system, "Segoe UI", Arial, sans-serif !important; }, OS stack ONLY, no webfont |
| Highlight headings | .a11y-headings | .a11y-headings h1, .a11y-headings h2, .a11y-headings h3 { outline: 2px dashed currentColor; outline-offset: 4px; } |
| Black cursor | .a11y-cursor-black | .a11y-cursor-black, .a11y-cursor-black * { cursor: url("data:image/svg+xml,…black cursor SVG…"), auto !important; } |
| Large cursor | .a11y-cursor-large | .a11y-cursor-large, .a11y-cursor-large * { cursor: url("data:image/svg+xml,…large cursor SVG…"), auto !important; } |
| Stop animations | .a11y-reduce-motion | .a11y-reduce-motion *, .a11y-reduce-motion *::before, .a11y-reduce-motion *::after { animation-duration: 0.001ms !important; animation-iteration-count: 1 !important; transition-duration: 0.001ms !important; scroll-behavior: auto !important; } |
Counter-invert (required)
html.a11y-contrast-invert #a11y-widget-panel,
html.a11y-contrast-invert #a11y-widget-trigger,
html.a11y-contrast-mono #a11y-widget-panel,
html.a11y-contrast-mono #a11y-widget-trigger {
filter: invert(1) hue-rotate(180deg);
}Without this, users end up with an unreadable widget when invert/mono is active.
Print reset (required)
@media print {
html[class*="a11y-"] { filter: none !important; }
html[class*="a11y-text-"] { font-size: 100% !important; }
html[class*="a11y-lines-"] { line-height: normal !important; }
html.a11y-cursor-black, html.a11y-cursor-large { cursor: auto !important; }
}---
7. Dictionary Shape
The widget expects this bilingual dictionary shape:
interface AccessibilityWidgetDict {
triggerLabel: string;
title: string;
close: string;
reset: string;
keyboardHint: string;
announceChange: string;
footerLink: string;
labels: {
links: string;
contrast: string;
textSize: string;
lineSpacing: string;
readableFont: string;
headings: string;
cursorBlack: string;
cursorLarge: string;
reduceMotion: string;
};
values: {
contrast: Record<'off' | 'high' | 'invert' | 'mono', string>;
lineSpacing: Record<'normal' | '16' | '20', string>;
};
}---
Testing Checklist
Before shipping:
- [ ] Disable JavaScript → bootstrap script should still apply classes from
localStorage - [ ] Clear
localStorage→ widget opens at defaults, no flash - [ ] Set prefs, reload → no flash of default styling (FOUC test)
- [ ]
Alt+Aon macOS Chrome, Safari, Firefox → panel opens - [ ]
Alt+Aon Windows / Linux → panel opens - [ ] Tab through page → widget trigger is reachable by keyboard
- [ ] Enable invert contrast → widget remains readable (counter-invert works)
- [ ] Enable 150% text → no text clipped, no horizontal scroll introduced
- [ ] Print preview → filters removed, text at 100%
- [ ] Enable "Stop animations" → framer-motion animations skip via
MotionConfig - [ ] Screen reader (NVDA / VoiceOver) → cycling toggle announces current value, not "pressed"
- [ ] Cross-tab test → preferences persist across tabs (storage event not required; each tab reads from localStorage on load)
Scope Reminder
This widget must NOT:
- Generate or alter
altattributes - Add, remove, or rewrite ARIA on content elements
- Inject structural DOM changes to fix heading order or landmarks
- Include a "we are compliant" badge or claim
- Load third-party overlay SDKs
The widget is one control surface. Compliance with IS 5568 and WCAG 2.0 AA comes from the site's own HTML, not from the widget. See SKILL.md section "Avoiding Overlay Anti-Patterns" and the April 2025 FTC action against accessiBe for why this matters.
#!/usr/bin/env python3
"""Run IS 5568 accessibility audit on Israeli websites.
Checks for Israeli-specific accessibility requirements based on the
IS 5568 standard, which is anchored to WCAG 2.0 AA (IS 5568 adds some
2.1-aligned criteria; sources differ). Covers Hebrew language
declaration, RTL direction, ARIA labels, and the mandatory
accessibility statement page.
This is a STATIC-HTML auditor: it fetches the raw HTML and cannot
evaluate color contrast or JavaScript-rendered (SPA) content. For
contrast and JS-rendered checks, use the axe-core + Selenium path
shown in SKILL.md Step 10.
Usage:
python audit_a11y.py --url https://example.co.il
python audit_a11y.py --url https://example.co.il --output report.json
python audit_a11y.py --help
Requirements:
pip install requests beautifulsoup4
"""
import argparse
import json
import sys
from urllib.parse import urljoin
try:
import requests
from bs4 import BeautifulSoup
except ImportError:
print("Missing required dependencies. Install with:", file=sys.stderr)
print(" pip install requests beautifulsoup4", file=sys.stderr)
sys.exit(1)
def check_lang_attribute(soup):
"""Check for Hebrew language declaration on html element."""
html_tag = soup.find("html")
if not html_tag:
return {"pass": False, "message": "No <html> element found"}
lang = html_tag.get("lang", "")
if lang.startswith("he"):
return {"pass": True, "message": f"lang=\"{lang}\" found"}
elif lang:
return {
"pass": False,
"message": f"lang=\"{lang}\" found but expected \"he\"",
}
return {"pass": False, "message": "No lang attribute on <html>"}
def check_dir_attribute(soup):
"""Check for RTL direction declaration."""
html_tag = soup.find("html")
if not html_tag:
return {"pass": False, "message": "No <html> element found"}
dir_attr = html_tag.get("dir", "")
if dir_attr == "rtl":
return {"pass": True, "message": "dir=\"rtl\" found"}
elif dir_attr:
return {
"pass": False,
"message": f"dir=\"{dir_attr}\" found but expected \"rtl\"",
}
return {"pass": False, "message": "No dir attribute on <html>"}
def check_page_title(soup):
"""Check for Hebrew page title."""
title = soup.find("title")
if not title or not title.string:
return {"pass": False, "message": "No <title> element found"}
title_text = title.string.strip()
if len(title_text) > 0:
return {"pass": True, "message": f"Title: \"{title_text[:50]}\""}
return {"pass": False, "message": "Empty <title> element"}
def check_skip_navigation(soup):
"""Check for skip navigation link."""
skip_links = soup.find_all("a", href=True)
for link in skip_links:
href = link.get("href", "")
text = link.get_text(strip=True)
if href.startswith("#") and ("דלג" in text or "skip" in text.lower()):
return {"pass": True, "message": f"Skip link found: \"{text}\""}
return {
"pass": False,
"message": "No skip navigation link found (expected Hebrew text with 'דלג')",
}
def check_images_alt(soup):
"""Check that all images have alt attributes."""
images = soup.find_all("img")
if not images:
return {"pass": True, "message": "No images found"}
missing = []
for i, img in enumerate(images):
if not img.has_attr("alt"):
src = img.get("src", f"image_{i}")
missing.append(src[:50])
if missing:
return {
"pass": False,
"message": f"{len(missing)} images missing alt: {', '.join(missing[:3])}",
}
return {"pass": True, "message": f"All {len(images)} images have alt text"}
def check_form_labels(soup):
"""Check that form inputs have associated labels."""
inputs = soup.find_all(["input", "select", "textarea"])
unlabeled = []
for inp in inputs:
if inp.get("type") in ("hidden", "submit", "button", "reset"):
continue
inp_id = inp.get("id", "")
has_label = bool(inp_id and soup.find("label", attrs={"for": inp_id}))
has_aria = bool(inp.get("aria-label") or inp.get("aria-labelledby"))
if not has_label and not has_aria:
unlabeled.append(inp.get("name", inp.get("id", "unknown")))
if unlabeled:
return {
"pass": False,
"message": f"{len(unlabeled)} inputs without labels: {', '.join(unlabeled[:3])}",
}
return {"pass": True, "message": "All form inputs have labels"}
def check_accessibility_statement(soup, base_url):
"""Check for link to accessibility statement page."""
links = soup.find_all("a", href=True)
a11y_keywords = ["נגישות", "accessibility", "negishot"]
for link in links:
text = link.get_text(strip=True).lower()
href = link.get("href", "").lower()
if any(kw in text or kw in href for kw in a11y_keywords):
full_url = urljoin(base_url, link.get("href", ""))
return {
"pass": True,
"message": f"Accessibility link found: \"{link.get_text(strip=True)}\" -> {full_url}",
}
return {
"pass": False,
"message": "No link to accessibility statement (הצהרת נגישות) found",
}
def check_heading_hierarchy(soup):
"""Check that heading hierarchy is logical."""
headings = soup.find_all(["h1", "h2", "h3", "h4", "h5", "h6"])
if not headings:
return {"pass": False, "message": "No headings found"}
h1_count = len(soup.find_all("h1"))
if h1_count == 0:
return {"pass": False, "message": "No H1 heading found"}
if h1_count > 1:
return {
"pass": False,
"message": f"Multiple H1 headings found ({h1_count})",
}
return {
"pass": True,
"message": f"Heading hierarchy OK ({len(headings)} headings, 1 H1)",
}
def run_audit(url):
"""Run full IS 5568 accessibility audit on a URL."""
print(f"Auditing: {url}\n")
try:
response = requests.get(
url,
timeout=30,
headers={
"User-Agent": "Mozilla/5.0 (compatible; a11y-audit/1.0; +https://agentskills.co.il)"
},
)
response.raise_for_status()
except requests.RequestException as e:
print(f"Error fetching URL: {e}", file=sys.stderr)
sys.exit(1)
soup = BeautifulSoup(response.text, "html.parser")
checks = {
"lang_attribute": check_lang_attribute(soup),
"dir_attribute": check_dir_attribute(soup),
"page_title": check_page_title(soup),
"skip_navigation": check_skip_navigation(soup),
"images_alt": check_images_alt(soup),
"form_labels": check_form_labels(soup),
"accessibility_statement": check_accessibility_statement(soup, url),
"heading_hierarchy": check_heading_hierarchy(soup),
}
passed = sum(1 for c in checks.values() if c["pass"])
total = len(checks)
print("IS 5568 Accessibility Audit Results")
print("=" * 50)
for name, result in checks.items():
status = "PASS" if result["pass"] else "FAIL"
print(f" [{status}] {name}: {result['message']}")
print(f"\nScore: {passed}/{total} checks passed")
if passed < total:
print("Status: NON-COMPLIANT - remediation required")
else:
print("Status: AUTOMATED CHECKS PASSED (manual testing still required)")
return {"url": url, "checks": checks, "passed": passed, "total": total}
def main():
parser = argparse.ArgumentParser(
description="Run IS 5568 accessibility audit on Israeli websites"
)
parser.add_argument(
"--url", required=True,
help="URL to audit"
)
parser.add_argument(
"--output", default=None,
help="Output JSON report file path (optional)"
)
args = parser.parse_args()
results = run_audit(args.url)
if args.output:
with open(args.output, "w", encoding="utf-8") as f:
json.dump(results, f, ensure_ascii=False, indent=2)
print(f"\nReport saved: {args.output}")
if __name__ == "__main__":
main()
נגישות אתרים ישראלית
הנחיות
שלב 1: להבין את המסגרת החוקית
נגישות אתרים בישראל היא חובה לפי חוק שוויון זכויות לאנשים עם מוגבלויות, התשנ"ח-1998 ותקנות שוויון זכויות לאנשים עם מוגבלות (התאמות נגישות לשירות), תשע"ג-2013.
| תקנה | דרישה | סטטוס | סנקציה |
|---|---|---|---|
| ת"י 5568 (בתוקף מ-2017, עודכן 2020 ו-2023) | תקן נגישות ישראלי, מעוגן ב-WCAG 2.0 AA | אתרים חדשים נגישים מ-25 באוקטובר 2015; אתרים קיימים עד 26 באוקטובר 2017; מועד אוקטובר 2020 הרחיב את מדרגות הפטור בעלות מחזור נמוך, לא את המועד הכללי | עד 50,000 ש"ח פיצוי אזרחי סטטוטורי ללא הוכחת נזק לכל הפרה |
| תקנות נגישות לשירות (2013) | אתרים ואפליקציות של ספקי שירות לציבור חייבים בנגישות | בתוקף | תביעות + פיצוי ללא הוכחת נזק |
| אפליקציות מובייל | כלולות בתחולה של תקנות 2013; מחוזקות בעדכונים מאוחרים יותר | בתוקף | אותו פיצוי |
| אתרים ממשלתיים | חייבים לעמוד ב-IS 5568 רמה AA | בתוקף | פיקוח של הנציבות + עיצום כספי |
הערה על גרסת WCAG. החוק והתקנות מפנים אל ת"י 5568. הקריאה המקובלת היא שת"י 5568, כולל מהדורת חלק 1 מספטמבר 2023, נשאר מעוגן משפטית ב-WCAG 2.0 AA, וההתאמה ל-WCAG 2.1 היא חלקית או מבוססת על טענות ספקים ולא על הבסיס המשפטי. חלק מהמקורות מתארים את ת"י 5568:2020 כ-WCAG 2.1 AA, אז המקורות חלוקים. כיוון העבודה הבטוח הוא WCAG 2.0 AA בתוספת ההתאמות הישראליות שלמטה; עמידה בקריטריוני WCAG 2.1 (ו-2.2) מעבר לכך מועילה להתכוננות לעתיד אבל אינה הרצפה שהחוק קובע.
מי חייב לעמוד בדרישות: כל האתרים והאפליקציות הציבוריות של ספקי שירות הפתוחים לציבור בישראל, כולל גופים ממשלתיים, מוסדות חינוך, ספקי בריאות, עסקים ומלכ"רים.
עמידה בתקן היא תנאי הכרחי אבל לא מספיק. החובה החוקית היא התקנות (תקנות נגישות לשירות, 2013), שמאמצות את ת"י 5568 חלק 1 ברמה AA בהפניה. אפשר לעבור ביקורת WCAG / ת"י 5568 אוטומטית ועדיין לא לעמוד בחוק אם חסרים הצהרת נגישות, רכז נגישות (היכן שנדרש), ערוץ משוב, או חוות דעת מורשה הנגישות שמתוארת למטה, ולהפך. התייחסו ל"עומד בת"י 5568" ול"עומד בחוק" כשתי רשימות נפרדות.
מורשה נגישות השירות. כשמפעיל מבקש פטור בשל "קושי טכנולוגי" לפי תקנה 35, התקנה דורשת חוות דעת בכתב ממורשה נגישות השירות שנערכה בהתייעצות עם מומחה נגישות אינטרנט. בפועל גם הצהרת הנגישות והביקורת שמאחוריה מצופות להיתמך במורשה נגישות; עמידה בחוק אינה משימה של מפתח או ביקורת אוטומטית בלבד.
מסמכים דיגיטליים (ת"י 5568 חלק 2). ת"י 5568 מחולק לחלק 1 (תוכן ווב) וחלק 2 (נגישות מסמכים דיגיטליים, למשל PDF וקובצי Office), שפורסם ב-2020. קובצי PDF ומסמכים להורדה באתר ציבורי נכללים בחובת ההנגשה והם אחד מכשלי הביקורת הנפוצים ביותר, אז אל תעצרו ב-HTML.
שלב 2: מי פטור
תקנות נגישות לשירות מבססות את הפטור מהנגשת אתר על מחזור ולא על מספר עובדים. אין בתקנות סף של "25 עובדים" או "300,000 ש"ח". מדרגות הפטור הן:
| מפעיל | פטור |
|---|---|
| עוסק פטור | פטור מלא מהנגשת אתר |
| מחזור שנתי ממוצע מתחת לסף העסק הקטן (המקורות חלוקים: יש המציינים 100,000 ש"ח ואחרים 120,000 ש"ח, אמתו מול נוסח התקנות העדכני לפני הסתמכות) | פטור זמני ל-3 שנים, ניתן לחידוש כל 3 שנים כל עוד המחזור הממוצע נשאר עד הסף הזה |
| מחזור שנתי ממוצע מהסף הזה ועד 1,000,000 ש"ח | פטור ל-3 שנים לאתר קיים שהפעלתו החלה לפני 26 באוקטובר 2017; אתר חדש שנבנה אחרי מועד זה חייב בהנגשה |
| מחזור שנתי ממוצע מעל 1,000,000 ש"ח | אין פטור אוטומטי. המפעיל חייב לפנות לנציבות שוויון זכויות לאנשים עם מוגבלות כדי לבקש פטור בשל נטל כלכלי כבד |
הפטורים האלה (חוץ מבקשת הנטל הכבד) אוטומטיים ואינם דורשים אישור, אבל המפעיל חייב לבדוק מחדש את המחזור כל 3 שנים. סכום סף העסק הקטן שנוי במחלוקת בין המקורות, אז מפעיל גבולי צריך לאמת מול נוסח התקנות או מול הנציבות במקום להכריז על עצמו פטור. פטור מהנגשת אתר אינו פוטר מחובות נגישות אחרות בשירות, ואינו פוטר מחובת נגישות המבנה (נגישות פיזית) שמוסדרת בתקנות נפרדות.
שלב 3: אכיפה וסנקציות
שני מסלולי אכיפה פועלים במקביל:
- תביעה אזרחית. תובע יכול לתבוע עד 50,000 ש"ח פיצוי סטטוטורי ללא הוכחת נזק, וצריך רק להראות שהאתר אינו נגיש. תקופת תיקון של 60 יום: סטייה אינה נחשבת הפרה אלא אם נשלחה למפעיל הודעה הדורשת תיקון והוא לא תיקן בתוך זמן סביר, ולא יאוחר מ-60 יום מקבלת ההודעה. לבקשה לאישור תביעה ייצוגית בגין אתר לא נגיש אין עילת תביעה אם לא נשלחה קודם הודעת תיקון. זו הגנה ממשית למפעילים.
- אכיפה מנהלית של הנציבות. תיקון 23 לחוק שוויון זכויות נתן לנציבות שוויון זכויות לאנשים עם מוגבלות מסלול מנהלי: התראות, התחייבויות, צווי נגישות ועיצום כספי. סכום העיצום נקבע לפי גודל המפעיל (עסק זעיר עד מחזור 2,000,000 ש"ח, עסק קטן עד 20,000,000 ש"ח, עסק רגיל מעל זה או רשות ציבורית) ולפי עלות ההתאמה הספציפית, עם תוספת לכל יום שבו ההפרה נמשכת. הפחתות חלות בעבור עבר נקי ובעבור פעולה מתקנת. העיצום הזה נפרד ומתווסף לפיצוי האזרחי של 50,000 ש"ח.
הגשת תלונה (צד המשתמש). משתמש שנתקל באתר לא נגיש שולח למפעיל הודעת תיקון (שמפעילה את שעון ה-60 יום); אם זה לא תוקן בזמן, המשתמש יכול להתלונן לנציבות שוויון זכויות לאנשים עם מוגבלות או לתבוע את הפיצוי הסטטוטורי. רכז הנגישות של המפעיל (שלב 4) הוא איש הקשר הראשון לתלונות כאלה, אז מפעיל צריך לנתב תלונות נגישות נכנסות דרך הרכז ולפעול בתוך חלון התיקון.
שלב 4: רכז הנגישות
תקנות נגישות לשירות מחייבות ספק שירות שהוא גוף ציבורי, או שמעסיק 25 עובדים או יותר, למנות אחד מעובדיו לרכז נגישות. שימו לב שסף 25 העובדים הוא הטריגר למינוי הרכז, ולא לחובת הנגשת האתר עצמה, שהיא אוניברסלית בכפוף לפטורי המחזור שלמעלה. הרכז:
- מוסר לציבור מידע על הסדרי הנגישות של הנכסים והשירותים של הספק.
- מטפל בפניות ובתלונות נגישות מהציבור.
- מסייע לאחראים על הנגישות לבצע ולתחזק את ההתאמות הנדרשות.
שם הרכז ופרטי הקשר שלו חייבים להופיע בהצהרת הנגישות (ראו שלב 9).
שלב 5: IS 5568 מול WCAG - ההבדלים העיקריים
ת"י 5568 מעוגן ב-WCAG 2.0 AA (המקורות חלוקים אם המהדורה הנוכחית מגיעה ל-2.1) ומוסיף דרישות ייחודיות לישראל:
| תחום | WCAG 2.0 AA | התוספת של IS 5568 |
|---|---|---|
| שפה | הצהרת תכונת lang | חייב לתמוך ב-lang="he" עם RTL |
| כיוון טקסט | לא מוגדר | חייב להצהיר dir="rtl" לתוכן עברי |
| ניגודיות | 4.5:1 לטקסט | זהה, בתוספת בדיקת ניגודיות עם גופנים עבריים |
| תוויות טפסים | תוויות משויכות | התוויות חייבות לתמוך ביישור RTL |
| הודעות שגיאה | שגיאות תיאוריות | חייבות להיות בעברית לאתרים עבריים |
| הצהרת נגישות | מומלצת | חובה (הצהרת נגישות) |
| פרטי קשר | לא נדרש | חובה לספק דרך ליצור קשר בנושא נגישות |
| גופים ציבוריים דו-לשוניים | לא מוגדר | גוף ממשלתי או ציבורי שמשרת את הציבור בעברית ובערבית צריך להנגיש את התוכן בשתי השפות, לא רק בעברית |
שלב 6: מבנה HTML נגיש ב-RTL
<!DOCTYPE html>
<html lang="he" dir="rtl">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>שם האתר - כותרת הדף</title>
</head>
<body>
<!-- קישור דילוג ניווט (חובה) -->
<a href="#main-content" class="skip-link">דלג לתוכן הראשי</a>
<header role="banner">
<nav role="navigation" aria-label="ניווט ראשי">
<!-- פריטי ניווט -->
</nav>
</header>
<main id="main-content" role="main">
<!-- תוכן הדף -->
</main>
<footer role="contentinfo">
<a href="/accessibility-statement">הצהרת נגישות</a>
</footer>
</body>
</html>דרישות IS 5568 המרכזיות במבנה הזה:
lang="he"ו-dir="rtl"על אלמנט ה-html- קישור דילוג ניווט בעברית ("דלג לתוכן הראשי")
- תפקידי ARIA לציוני דרך
- תוויות ARIA בעברית לניווט
- קישור להצהרת נגישות בתחתית הדף
שלב 7: תאימות קוראי מסך בעברית
תבדקו עם קוראי המסך הנפוצים בישראל:
| קורא מסך | פלטפורמה | תמיכה בעברית | הערות בדיקה |
|---|---|---|---|
| NVDA | Windows | מצוינת עם eSpeak-ng עברית | חינמי, הנפוץ בישראל |
| JAWS | Windows | טובה עם Eloquence עברית | מסחרי, שימוש מוסדי |
| VoiceOver | macOS/iOS | תמיכה טובה ב-TTS עברי מובנה | מובנה, אימוץ גדל |
| TalkBack | Android | טובה עם Google TTS עברית | מובנה במכשירי אנדרואיד |
תבניות קורא מסך ייחודיות לעברית:
<!-- הכרזה על שינויי כיוון תוכן -->
<p dir="rtl" lang="he">
טקסט בעברית עם <span dir="ltr" lang="en">English text</span> משולב
</p>
<!-- תוויות ARIA בעברית -->
<button aria-label="סגור חלון">X</button>
<input type="search" aria-label="חיפוש באתר" placeholder="חפש...">
<!-- אזורים חיים בעברית לתוכן דינמי -->
<div aria-live="polite" aria-atomic="true" dir="rtl">
הטופס נשלח בהצלחה
</div>שלב 8: תבניות ARIA ייחודיות ל-RTL
<!-- טופס RTL עם הודעות שגיאה נגישות -->
<form dir="rtl" novalidate>
<div role="group" aria-labelledby="personal-info">
<h2 id="personal-info">פרטים אישיים</h2>
<label for="full-name">שם מלא</label>
<input id="full-name" type="text" required
aria-required="true"
aria-describedby="name-error"
aria-invalid="false">
<span id="name-error" role="alert" class="error" hidden>
נא למלא שם מלא
</span>
<label for="teudat-zehut">תעודת זהות</label>
<input id="teudat-zehut" type="text" pattern="[0-9]{9}"
inputmode="numeric" dir="ltr"
aria-required="true"
aria-describedby="tz-help tz-error">
<span id="tz-help" class="hint">9 ספרות</span>
<span id="tz-error" role="alert" class="error" hidden>
מספר תעודת זהות לא תקין
</span>
</div>
</form>
<!-- טבלת נתונים RTL -->
<table dir="rtl">
<caption>סיכום הזמנות</caption>
<thead>
<tr>
<th scope="col">מספר הזמנה</th>
<th scope="col">תאריך</th>
<th scope="col">סכום</th>
<th scope="col">סטטוס</th>
</tr>
</thead>
<tbody>
<tr>
<td dir="ltr">ORD-12345</td>
<td>04/03/2026</td>
<td dir="ltr">1,234.50 ₪</td>
<td>הושלם</td>
</tr>
</tbody>
</table>שלב 9: הצהרת נגישות
ת"י 5568 דורש לפרסם הצהרת נגישות. התוכן הנדרש:
<article dir="rtl" lang="he">
<h1>הצהרת נגישות</h1>
<p>אנחנו ב-[שם החברה] מחויבים להנגשת האתר לאנשים עם מוגבלויות
בהתאם לתקן הישראלי IS 5568, המעוגן ב-WCAG 2.0 ברמה AA.</p>
<h2>אמצעי נגישות באתר</h2>
<ul>
<li>האתר תומך בניווט מלא במקלדת</li>
<li>האתר תומך בקוראי מסך (NVDA, JAWS, VoiceOver)</li>
<li>תמונות מלוות בטקסט חלופי</li>
<li>ניגודיות הצבעים עומדת ביחס של 4.5:1 לפחות</li>
</ul>
<h2>מגבלות נגישות ידועות</h2>
<ul>
<li>[פרטו כאן רכיבים, דפים או מסמכים שטרם הונגשו במלואם, אם יש, ואת מועד התיקון הצפוי. אם אין מגבלות ידועות, ציינו זאת במפורש]</li>
</ul>
<h2>פנייה בנושא נגישות</h2>
<p>רכז/ת נגישות: [שם]</p>
<p>טלפון: <a href="tel:+97212345678" dir="ltr">+972-1-234-5678</a></p>
<p>דוא"ל: <a href="mailto:negishot@example.co.il">negishot@example.co.il</a></p>
<p>תאריך ביקורת הנגישות האחרונה: [תאריך]</p>
<p>תאריך עדכון ההצהרה: [תאריך]</p>
</article>שלב 10: בדיקת נגישות אוטומטית
תסתכלו על scripts/audit_a11y.py לפייפליין הביקורת המלא.
# בדיקת נגישות מהירה עם axe-core דרך selenium
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
def run_accessibility_audit(url):
options = Options()
options.add_argument('--headless')
driver = webdriver.Chrome(options=options)
driver.get(url)
# הזרקת axe-core
axe_script = open('axe.min.js').read()
driver.execute_script(axe_script)
# הרצת ביקורת עם כללי לוקאל עברי
results = driver.execute_script("""
return axe.run({
rules: {
'html-has-lang': { enabled: true },
'valid-lang': { enabled: true },
'document-title': { enabled: true },
'bypass': { enabled: true },
'color-contrast': { enabled: true },
'label': { enabled: true },
'image-alt': { enabled: true }
}
});
""")
driver.quit()
return resultsרשימת בדיקות לעמידה ב-IS 5568 (אוטומטי + ידני):
| בדיקה | אוטומטי | כלי |
|---|---|---|
נוכחות lang="he" | כן | axe-core |
נוכחות dir="rtl" | כן | כלל מותאם |
| ניגודיות צבעים 4.5:1 | כן | axe-core |
| כל התמונות עם טקסט חלופי | כן | axe-core |
| שדות טופס עם תוויות | כן | axe-core |
| קישור דילוג ניווט | כן | axe-core |
| ניווט מקלדת | ידני | בדיקת Tab |
| תאימות קורא מסך | ידני | בדיקת NVDA/VoiceOver |
| הודעות שגיאה בעברית | ידני | בדיקה חזותית |
| הצהרת נגישות | ידני | בדיקת קיום הדף |
תסתכלו על references/is-5568.md לרשימת הבדיקות המלאה ממופה לסעיפי IS 5568.
שרתי MCP מומלצים
אין שרת MCP רלוונטי לנגישות אתרים ישראלית. בספריית ה-MCP של skills-il אין כרגע MCP לנגישות, לתקן 5568 או לביקורת WCAG. הביקורת בסקיל הזה רצה כסקריפט פייתון מקומי (scripts/audit_a11y.py), לא דרך MCP. אם יתווסף בעתיד MCP לביקורת נגישות לספרייה, עדיף להשתמש בו לסריקת אתר חי ולהשאיר את הסקריפט לבדיקות לא מקוונות.
דוגמאות
דוגמה 1: ביקורת אתר ישראלי קיים
המשתמש אומר: "בדוק אם האתר שלי עומד בתקני הנגישות הישראליים" תוצאה: מריצים את scripts/audit_a11y.py מול הכתובת, בודקים דרישות IS 5568 כולל תכונת שפה עברית, כיוון RTL, יחסי ניגודיות, תוויות ARIA בעברית, דילוג ניווט ודף הצהרת נגישות. מפיקים דוח עמידה עם עובר/נכשל לכל קריטריון.
דוגמה 2: הוספת דף הצהרת נגישות
המשתמש אומר: "אני צריך להוסיף דף נגישות כדי לעמוד בחוק הישראלי" תוצאה: יוצרים דף הצהרת נגישות בעברית עם כל הסעיפים שהחוק דורש: רמת עמידה, תכונות נגישות, מגבלות ידועות, פרטי יצירת קשר לרכז/ת נגישות, ותאריך עדכון אחרון.
דוגמה 3: תיקון נגישות בטופס RTL
המשתמש אומר: "קוראי מסך לא קוראים את הטופס העברי שלי נכון" תוצאה: מוסיפים dir="rtl" לאלמנט הטופס, מוודאים שכל התוויות משויכות לשדות הקלט ומשתמשות בטקסט עברי, מוסיפים aria-required="true" לשדות חובה, נותנים הודעות שגיאה בעברית עם role="alert", וקובעים dir="ltr" על שדות מספריים כמו טלפון ותעודת זהות.
דוגמה 4: הנגשת טבלת נתונים בעברית
המשתמש אומר: "הטבלה העברית שלי לא נגישה לקוראי מסך" תוצאה: מוסיפים dir="rtl" לאלמנט הטבלה, מוסיפים caption בעברית, משתמשים ב-scope="col" וב-scope="row" על תאי הכותרת, מסמנים תוכן LTR כמו מספרי הזמנה עם dir="ltr", ומוודאים שסדר הקריאה הלוגי תואם לסדר החזותי של RTL.
משאבים מצורפים
סקריפטים
scripts/audit_a11y.py-- מריץ ביקורת נגישות לפי IS 5568: בדיקות אוטומטיות לתכונת שפה עברית, כיוון RTL, תוויות ARIA, יחסי ניגודיות ודילוג ניווט עם axe-core ו-selenium. מפיק דוח עמידה עם עובר/נכשל לכל סעיף ב-IS 5568. הרצה:python scripts/audit_a11y.py --help
קובצי עזר
references/is-5568.md-- מדריך תקן IS 5568 המלא: דרישות סעיף-אחר-סעיף ממופות ל-WCAG 2.0 AA, התוספות הישראליות, מדרגות הפטור, שני מסלולי האכיפה (פיצוי אזרחי + עיצום כספי של הנציבות) עם תקופת התיקון של 60 יום, תפקיד רכז הנגישות, תבנית הצהרת נגישות חובה, ורשימת בדיקות לביקורות עמידה.references/widget-implementation.md-- קוד TypeScript/React מוכן להעתקה לווידג'ט העדפות נגישות לפי תקנה 35: חנות pub-sub עםuseSyncExternalStore, הפעלה מבוססת-קלאסים ב-CSS, סקריפט bootstrap למניעת FOUC, קיצורAlt+Aבלי תלות בפריסת המקלדת (דרךe.code), חיווטMotionConfigשל framer-motion, וכללי counter-invert. להיעזר כשהמשתמש רוצה להטמיע את הווידג'ט עצמו, ולא רק לבצע ביקורת עמידה.
בניית ווידג'ט העדפות נגישות תואם לתקנה
תקנה 35 ות"י 5568 מחייבות אתרים ישראליים הפונים לצרכנים לחשוף ממשק נגישות שניתן להפעיל במקלדת, לרוב ווידג'ט צף עם מתגים לניגודיות, גודל טקסט, מרווח שורות, סמן ותנועה. הווידג'ט הזה הוא כלי העדפות למשתמש, לא שכבת אוטומציה. ההבדל קריטי: באפריל 2025 ה-FTC קנס את accessiBe במיליון דולר על הטעיה צרכנית סביב טענות שהשכבה שלהם "מתקנת" אתרים אוטומטית (היא לא). הווידג'ט שאתם משגרים חייב לעשות בדיוק מה שהמשתמש ביקש, לא יותר.
סט התכונות
ווידג'ט תקנה 35 בסיסי חושף את המתגים האלה:
| מתג | סוג | ערכים |
|---|---|---|
| הדגשת קישורים | בינארי | פעיל / כבוי |
| ניגודיות | מחזורי | כבוי / גבוה / היפוך / מונוכרום |
| גודל טקסט | מחזורי | 100% / 115% / 130% / 150% |
| מרווח שורות | מחזורי | רגיל / 1.6 / 2.0 |
| גופן קריא | בינארי | פעיל / כבוי (מחסנית גופני מערכת בלבד, בלי webfont) |
| הדגשת כותרות | בינארי | פעיל / כבוי |
| סמן שחור | בינארי | פעיל / כבוי |
| סמן גדול | בינארי | פעיל / כבוי |
| עצירת אנימציות | בינארי | פעיל / כבוי |
| איפוס | פעולה | מנקה את כל ההעדפות |
ארכיטקטורה
שלושה חלקים: חנות העדפות, פאנל UI, ושכבת CSS.
1. חנות העדפות. חנות pub-sub (subscribe / getSnapshot / getServerSnapshot / set / reset) שנצרכת ב-React דרך useSyncExternalStore. המצב נשמר ב-localStorage במפתח עם גרסה (למשל site_a11y_prefs_v1). חוסר התאמת גרסה מבטל את המצב השמור כדי ששדרוגי סכמה לא ישאירו שדות מיושנים. להקדים-למלא את המטמון ב-notify() כדי שפיזור למנויים לא יפעיל קריאות מיותרות ל-localStorage.
2. פאנל UI. כפתור טריגר צף (fixed bottom-6 start-6 z-40, מודע ל-RTL דרך תכונות CSS לוגיות) פותח Radix Sheet עם רשת של 3 עמודות של כרטיסי מתג ופעולת איפוס. הטריגר נושא aria-expanded, aria-controls, ו-aria-keyshortcuts="Alt+A".
3. שכבת CSS. כל שינוי ויזואלי מונע על ידי קלאסים של CSS על <html> (למשל a11y-contrast-high, a11y-text-150, a11y-lines-20, a11y-reduce-motion). הווידג'ט לעולם לא משנה את ה-DOM של התוכן. הוא לא מזריק alt, לא מסדר מחדש אלמנטים, ולא משכתב ARIA. זה הקו שמפריד בין כלי העדפות תואם לשכבת אוטומציה אסורה.
כללי קלאסים ממקור יחיד
רשימת הקלאסים שמופעלת בזמן ריצה חייבת להיות זהה לרשימה שהסקריפט של bootstrap (ראו בהמשך) מחיל, אחרת המשתמשים יראו הבזק של העדפות לא מסוגננות בכל טעינת דף. הגדירו את המיפוי פעם אחת וצרו ממנו גם את פונקציית הריצה applyPrefsToElement() וגם את גוף ה-<script> של ה-bootstrap:
const CLASS_RULES = [
['a11y-links', (p) => p.links, '!!p.links'],
['a11y-contrast-high', (p) => p.contrast === 'high', "p.contrast==='high'"],
['a11y-contrast-invert', (p) => p.contrast === 'invert', "p.contrast==='invert'"],
['a11y-contrast-mono', (p) => p.contrast === 'mono', "p.contrast==='mono'"],
['a11y-text-115', (p) => p.textSize === 115, 'p.textSize===115'],
['a11y-text-130', (p) => p.textSize === 130, 'p.textSize===130'],
['a11y-text-150', (p) => p.textSize === 150, 'p.textSize===150'],
] as const;מניעת FOUC
ההעדפות יושבות ב-localStorage, כלומר הציור הראשון קורה בסגנון ברירת המחדל ורק אחרי ש-React נהידרר הווידג'ט מיישם מחדש את ההגדרות של המשתמש. ההבזק הזה לא מקובל למי שתלוי בניגודיות גבוהה או ב-150% טקסט. הפתרון: סקריפט inline ב-<head> שרץ סינכרונית לפני ההידרציה של React:
// מיוצר מ-CLASS_RULES כדי שהריצה וה-bootstrap לא יסטו אחד מהשני
export const A11Y_BOOTSTRAP_SCRIPT =
`(function(){try{var raw=localStorage.getItem('site_a11y_prefs_v1');` +
`if(!raw)return;var p=JSON.parse(raw);if(p.version!==1)return;` +
`var c=document.documentElement.classList;` +
CLASS_RULES.map(([cls,,js]) => `c.toggle(${JSON.stringify(cls)},${js})`).join(';') +
`}catch(e){}})()`;בפריסת השורש:
<head>
<script dangerouslySetInnerHTML={{ __html: A11Y_BOOTSTRAP_SCRIPT }} />
</head>תשאירו רשת ביטחון useEffect בקומפוננטת הווידג'ט שמיישמת קלאסים מחדש אחרי mount. אם localStorage חסום (גלישה פרטית, חריגה ממכסה), ה-bootstrap חוזר בשקט ורשת הביטחון מכסה את המקרה.
קיצור מקלדת: השתמשו ב-e.code, לא ב-e.key
תקנה 35 דורשת שהווידג'ט יהיה נגיש מכל הקשר פוקוס. Alt+A הוא ברירת המחדל בענף. תזהו אותו דרך e.code, לא e.key:
if (e.altKey && !e.ctrlKey && !e.metaKey && !e.shiftKey && e.code === 'KeyA') {
e.preventDefault();
togglePanel();
}ב-macOS, Alt+A מייצר את מפתח המת å ל-e.key, שנופל על בדיקת e.key === 'a' האינטואיטיבית. e.code הוא המיקום הפיזי של המקש ועצמאי מהפריסה ב-macOS, Windows ו-Linux.
נכונות ARIA
- מתגים בינאריים (הדגשת קישורים, גופן קריא, סמן, תנועה, כותרות):
aria-pressed={active}. - מתגים מחזוריים (ניגודיות, גודל טקסט, מרווח שורות): להשמיט `aria-pressed`. להכריז "לחוץ" מטעה כשלבקר יש יותר משני מצבים. השם הנגיש עצמו צריך לשאת את הערך הנוכחי:
aria-label={"${label}: ${valueLabel}"}. - אזור חי שמכריז על שינויי מצב:
role="status" aria-live="polite", וחשוב לרנדר אותו מחוץ לפורטל ה-Sheet. פורטלים מתפרקים כשה-Sheet נסגר; אזור חי בתוך הפורטל מאבד הכרזות שמגיעות באיחור.
framer-motion / הפחתת תנועה
אם האפליקציה משתמשת ב-framer-motion, עטפו את העץ ב-<MotionConfig> שמשקף את המתג "עצור אנימציות":
<MotionConfig reducedMotion={prefs.reduceMotion ? 'always' : 'user'}>'always' מכריח הפחתת תנועה כשהמתג בווידג'ט פעיל. 'user' חוזר ל-prefers-reduced-motion של מערכת ההפעלה כשהמתג כבוי, כדי שבקשות ברמת OS עדיין יכובדו.
להפוך חזרה את הווידג'ט עצמו
אם המשתמש מפעיל ניגודיות היפוך או מונוכרום, כל הדף עובר פילטר. הווידג'ט עצמו צריך להיות מופך-חזרה כדי שהמשתמש יוכל לקרוא אותו כדי לבטל את ההגדרה:
html.a11y-contrast-invert #a11y-widget-panel,
html.a11y-contrast-invert #a11y-widget-trigger {
filter: invert(1) hue-rotate(180deg);
}אם תשכחו את זה, המשתמשים ייתקעו בווידג'ט לא קריא שהם לא יכולים לכבות.
כלל הדפסה
תאפסו כל קלאס a11y-* בהקשר הדפסה כדי שפילטרי ניגודיות וצבעים הפוכים לא ילכו עם המשתמש לנייר:
@media print {
html[class*="a11y-"] { filter: none !important; }
html[class*="a11y-text-"] { font-size: 100% !important; }
html[class*="a11y-lines-"] { line-height: normal !important; }
}ראו את references/widget-implementation.md לקוד מלא להעתקה המכסה את חנות ה-pub-sub, ה-bootstrap למניעת FOUC, קומפוננטת React עם רשת ToggleCard, ה-MotionA11yProvider, וטבלת ייחוס לקלאסים של CSS.
הימנעות מדפוסי שכבות כושלים
מוצרי שכבות נגישות (accessiBe, UserWay, AudioEye) טוענים שהם הופכים אתרים לתואמים על ידי הזרקת JavaScript שמייצר טקסט alt אוטומטית, משכתב ARIA, ו"מתקן" HTML לא נגיש בזמן ריצה. ארגוני זכויות לאנשים עם מוגבלויות ורגולטורים בארה"ב תיעדו שוב ושוב שאתרים עם שכבות כאלה עדיין נכשלים בבדיקות קוראי מסך. באפריל 2025 ה-FTC קנס את accessiBe במיליון דולר ודרש ניטור תאימות מתמשך על פרסום מטעה שנגע ליכולות השכבה שלהם.
נציבות שוויון זכויות לאנשים עם מוגבלויות בישראל לא אישרה ולא ממליצה על אף מוצר שכבה. עמידה בת"י 5568 נבדקת מול ה-HTML המרונדר בפועל של האתר, לא מול טענות של תוסף.
כשאתם בונים את הווידג'ט שלמעלה, תאכפו את גבולות ההיקף האלה:
| כן | לא |
|---|---|
תמתגו קלאסים של CSS על <html> | לשנות DOM של תוכן, לשכתב תכונות alt, או להזריק ARIA |
| תספקו העדפות שהמשתמש שולט בהן (ניגודיות, גודל טקסט, תנועה) | לטעון שהווידג'ט לבד הופך את האתר ל"תואם IS 5568" או "תואם WCAG" |
| תתעדו את ההיקף ככלי נוחות בהצהרת הנגישות | להציג תג הסמכה או "ביקורת על ידי" שמגיעים מתוסף של ספק |
| תשתמשו במחסניות גופני מערכת למתג "גופן קריא" | להזריק webfont שמשנה רוחב טקסט מרונדר ודוחף תוכן קריטי |
תשמרו העדפות ב-localStorage ובעוגייה שלכם | להשתמש ב-SDK של שכבה של צד שלישי שיוצר טביעת אצבע או מחיל עוגיות מעקב כתוצאת לוואי |
הווידג'ט הוא שכבה אחת של עמידה. השכבות האחרות, HTML סמנטי, dir ו-lang נכונים, עבידות מלאה במקלדת, בדיקות אמיתיות עם קוראי מסך, תוויות טופס תקינות, ניהול פוקוס שעובד, דף הצהרת הנגישות, ורכז/ת נגישות בעלי שם, חייבות להיבנות אל תוך האתר עצמו. אף ווידג'ט לא מחליף את העבודה הזו.
מלכודות נפוצות
- חוק הנגישות הישראלי (תקן ישראלי 5568) מעוגן ב-WCAG 2.0 AA (המקורות חלוקים אם המהדורה הנוכחית מגיעה ל-2.1), ומוסיף מעליו דרישות ישראליות, כולל הציפייה לתוכן דו-לשוני לגופים ציבוריים שמשרתים את הציבור בעברית ובערבית (ראו שלב 5). סוכנים עלולים ליישם רק WCAG בלי התוספות הישראליות.
- סוכנים עלולים להעתיק כלל ישן ולפיו "עסקים עם 25+ עובדים או מחזור 300,000 ש"ח חייבים בהנגשה". המספר הזה אינו בתקנות. חובת הנגשת האתר אוניברסלית לשירותים הפתוחים לציבור, בכפוף לפטורים לפי מחזור (ראו שלב 2, מי פטור). סף 25 העובדים הוא הטריגר למינוי רכז נגישות, חובה נפרדת.
- סוכנים עלולים לדלג על תקופת התיקון של 60 יום. מפעיל לא נגיש חייב לקבל קודם הודעת תיקון ולקבל עד 60 יום לפני שלתביעה או לבקשה לאישור תביעה ייצוגית יש עילה (ראו שלב 3).
- קוראי מסך לעברית (NVDA, JAWS) קוראים טקסט RTL אחרת מ-LTR. סוכנים עלולים לייצר תוויות ARIA לפי סדר קריאה LTR, וזה מבלבל את משתמשי קוראי המסך בעברית.
- החוק הישראלי דורש הצהרת נגישות בכל אתר. סוכנים עלולים לייצר אתרים תואמי WCAG בלי דף הצהרת הנגישות החובה הזה.
- דרישות ניגודיות הצבעים בתקן 5568 תואמות ל-WCAG (יחס 4.5:1 לטקסט רגיל, 3:1 לטקסט גדול), אבל סוכנים עלולים לא להתחשב בעיבוד גופנים עבריים, שנראים דקים יותר מגופנים לטיניים באותו גודל ולפעמים דורשים ניגודיות קצת גבוהה יותר.
קישורי עזר
| מקור | כתובת | מה לבדוק |
|---|---|---|
| נציבות שוויון זכויות לאנשים עם מוגבלויות | https://www.gov.il/he/departments/moj_disability_rights/govil-landing-page | חוק הנגישות הישראלי, אכיפה, הגשת תלונות |
| תקן IS 5568 (מכון התקנים) | https://www.sii.org.il/en/ | מקור התקן הישראלי 5568 |
| חוק שוויון זכויות (נבו) | https://www.nevo.co.il/law_html/law01/p214m2_001.htm | הטקסט המשפטי של חוק שוויון זכויות לאנשים עם מוגבלויות |
| WCAG 2.0 Quick Reference | https://www.w3.org/WAI/WCAG21/quickref/?versions=2.0 | קריטריוני הצלחה וטכניקות לעמידה ברמת AA, הגרסה שת"י 5568 מעוגן בה |
| פטור מהנגשת אתר (כל-זכות) | https://www.kolzchut.org.il/he/פטור_מחובת_הנגשה_לאתרי_אינטרנט_ואפליקציות | מדרגות הפטור לפי מחזור ואיך לממש אותן |
| NVDA | https://www.nvaccess.org/ | קורא מסך חינמי בשימוש נרחב לבדיקות נגישות בעברית |
פתרון בעיות
שגיאה: "קורא המסך מכריז על תוכן בסדר שגוי"
סיבה: סדר RTL חזותי לא תואם לסדר ה-DOM, או חסרה תכונת dir פתרון: תוודאו שסדר מקור ה-DOM תואם לסדר הקריאה המיועד ל-RTL. תוסיפו dir="rtl" לאלמנטים מכילים. תשתמשו בתכונות CSS לוגיות לפריסה במקום במיקום פיזי שיכול להתנגש עם סדר הקריאה.
שגיאה: "הודעות אימות טופס בעברית לא מוכרזות"
סיבה: הודעות השגיאה לא משתמשות באזורי ARIA חיים או בתפקיד התראה פתרון: תוסיפו role="alert" למכולות הודעות השגיאה ותוודאו שהן מאוכלסות דינמית אחרי אימות. תשתמשו ב-aria-describedby לחיבור הודעות שגיאה לשדות הקלט שלהן. טקסט השגיאה חייב להיות בעברית לטפסים עבריים.
שגיאה: "קישור דילוג הניווט לא עובד בפריסת RTL"
סיבה: קישור הדילוג ממוקם מחוץ למסך עם CSS פיזי (left: -9999px) פתרון: תשתמשו ב-inset-inline-start: -9999px במקום ב-left: -9999px לקישור הדילוג. תוודאו שלאלמנט היעד יש id ו-tabindex="-1" לניהול פוקוס. תבדקו שקישור הדילוג הוא האלמנט הממוקד הראשון בסדר ה-Tab.
Related skills
How it compares
Use instead of generic accessibility skills when the question is Israeli law and RTL Hebrew—not US ADA or EU EN 301 549 alone.
FAQ
Who is Israeli Accessibility Compliance for?
Developers and small teams building Hebrew RTL websites or web apps who need IS 5568, enforcement, and implementation guidance in one agent skill.
When should I use Israeli Accessibility Compliance?
In Build when implementing widgets and RTL ARIA; in Ship when verifying compliance before release; in Launch when preparing for enforcement exposure; and in Validate when scoping whether your site is exempt.
Is Israeli Accessibility Compliance safe to install?
It is documentation- and implementation-guidance oriented; still review the Security Audits panel on this page and avoid pasting secrets when describing your site in chat.