@softspark/ai-toolkit 2.4.1 → 2.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +33 -20
- package/CHANGELOG.md +57 -0
- package/README.md +29 -13
- package/app/.claude-plugin/plugin.json +3 -2
- package/app/ARCHITECTURE.md +11 -0
- package/app/agents/code-reviewer.md +6 -7
- package/app/agents/frontend-specialist.md +33 -2
- package/app/agents/seo-specialist.md +1 -1
- package/app/personas/frontend-lead.md +48 -5
- package/app/skills/a11y-validate/SKILL.md +377 -0
- package/app/skills/a11y-validate/reference/aria-patterns.md +259 -0
- package/app/skills/a11y-validate/reference/eaa-compliance.md +252 -0
- package/app/skills/a11y-validate/reference/mobile-eaa.md +329 -0
- package/app/skills/a11y-validate/reference/wcag-2-1-aa.md +285 -0
- package/app/skills/a11y-validate/reference/wcag-2-2-aa.md +221 -0
- package/app/skills/a11y-validate/scripts/a11y-scanner.py +639 -0
- package/app/skills/clean-code/reference/python.md +3 -3
- package/app/skills/design-engineering/SKILL.md +2 -5
- package/app/skills/hipaa-validate/SKILL.md +39 -23
- package/app/skills/hipaa-validate/scripts/hipaa_scan.py +64 -7
- package/app/skills/review/SKILL.md +30 -6
- package/app/skills/seo-validate/SKILL.md +460 -0
- package/app/skills/seo-validate/reference/core-web-vitals.md +445 -0
- package/app/skills/seo-validate/reference/geo-aeo-patterns.md +259 -0
- package/app/skills/seo-validate/reference/geo-guidelines.md +248 -0
- package/app/skills/seo-validate/reference/schema-types.md +465 -0
- package/app/skills/seo-validate/reference/spa-ssg-patterns.md +351 -0
- package/app/skills/seo-validate/reference/w3c-guidelines.md +289 -0
- package/app/skills/seo-validate/scripts/seo-scanner.py +549 -0
- package/bin/ai-toolkit.js +24 -9
- package/kb/reference/architecture-overview.md +1 -1
- package/kb/reference/comparison.md +1 -1
- package/kb/reference/opencode-compatibility.md +161 -0
- package/kb/reference/skills-catalog.md +3 -1
- package/llms-full.txt +177 -6
- package/llms.txt +1 -0
- package/manifest.json +3 -3
- package/package.json +6 -3
- package/scripts/config_cli.py +4 -10
- package/scripts/doctor.py +3 -3
- package/scripts/generate_opencode.py +117 -0
- package/scripts/generate_opencode_agents.py +126 -0
- package/scripts/generate_opencode_commands.py +158 -0
- package/scripts/generate_opencode_json.py +133 -0
- package/scripts/generate_opencode_plugin.py +169 -0
- package/scripts/install_steps/ai_tools.py +117 -1
- package/scripts/install_steps/install_state.py +1 -1
- package/scripts/plugin.py +1 -1
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
# European Accessibility Act (EAA) — Compliance Reference
|
|
2
|
+
|
|
3
|
+
Reference for `a11y-validate` Category 8e (EAA documentation). Legal framework, enforcement dates, member-state transposition deltas, and accessibility-statement templates.
|
|
4
|
+
|
|
5
|
+
**Not legal advice.** Consult counsel in your target jurisdiction.
|
|
6
|
+
|
|
7
|
+
## Directive (EU) 2019/882
|
|
8
|
+
|
|
9
|
+
**Full title**: Directive (EU) 2019/882 of the European Parliament and of the Council of 17 April 2019 on the accessibility requirements for products and services.
|
|
10
|
+
|
|
11
|
+
**Common name**: European Accessibility Act (EAA).
|
|
12
|
+
|
|
13
|
+
**Published**: Official Journal of the European Union, 7 June 2019.
|
|
14
|
+
|
|
15
|
+
**Adopted**: 17 April 2019.
|
|
16
|
+
|
|
17
|
+
**Deadline for member-state transposition into national law**: 28 June 2022.
|
|
18
|
+
|
|
19
|
+
**Deadline for business compliance**: **28 June 2025** (products placed on the market after this date; existing contracts up to 28 June 2030; self-service terminals already in use may have until 2045).
|
|
20
|
+
|
|
21
|
+
## Scope
|
|
22
|
+
|
|
23
|
+
### Products in scope (Article 2(1))
|
|
24
|
+
|
|
25
|
+
- Consumer general-purpose computer hardware and OS (desktops, laptops, tablets, smartphones).
|
|
26
|
+
- Self-service terminals: ATMs, ticket machines, check-in kiosks, interactive info points.
|
|
27
|
+
- Consumer terminal equipment with interactive computing capability (e.g., smart TVs, set-top boxes).
|
|
28
|
+
- E-readers.
|
|
29
|
+
|
|
30
|
+
### Services in scope (Article 2(2))
|
|
31
|
+
|
|
32
|
+
- Electronic communications (telephony, VoIP, text messaging).
|
|
33
|
+
- Services providing access to audiovisual media (streaming, VOD).
|
|
34
|
+
- **Elements of passenger transport services** (websites, mobile apps, ticketing, information services, e-tickets).
|
|
35
|
+
- Consumer banking services (online banking, mobile banking apps).
|
|
36
|
+
- E-books and dedicated e-book software.
|
|
37
|
+
- **E-commerce services** (online stores, marketplaces — consumer-facing).
|
|
38
|
+
|
|
39
|
+
### Out of scope
|
|
40
|
+
|
|
41
|
+
- Microenterprises (<10 employees AND <€2 million annual turnover) providing services. Still apply if selling products.
|
|
42
|
+
- Private websites/apps not providing covered services.
|
|
43
|
+
- B2B-only services.
|
|
44
|
+
- Pre-recorded AV media published before 28 June 2025.
|
|
45
|
+
- Content from third parties not controlled by the service provider.
|
|
46
|
+
|
|
47
|
+
## Legal Requirements (Chapter III)
|
|
48
|
+
|
|
49
|
+
### Article 4 — Accessibility requirements
|
|
50
|
+
|
|
51
|
+
Products and services must meet the accessibility requirements in **Annex I** of the directive. Annex I is intentionally functional/outcome-based (e.g., "information must be presentable in more than one sensory modality"). Technical implementation is defined by **harmonized standards** — primarily **EN 301 549 v3.2.1**, which aligns with WCAG 2.1 Level AA.
|
|
52
|
+
|
|
53
|
+
### Article 13 — Obligations of service providers
|
|
54
|
+
|
|
55
|
+
Service providers must:
|
|
56
|
+
1. Design and provide services in accordance with accessibility requirements.
|
|
57
|
+
2. Prepare the necessary information in the form of an **accessibility statement** in the general terms and conditions.
|
|
58
|
+
3. Preserve this information for as long as the service is in operation.
|
|
59
|
+
|
|
60
|
+
### Article 14 — Accessibility statement
|
|
61
|
+
|
|
62
|
+
The core documentary obligation. Statements must include:
|
|
63
|
+
|
|
64
|
+
1. A description of how the service meets the accessibility requirements.
|
|
65
|
+
2. If the service does not fully comply, the reasons for disproportionate burden or fundamental alteration claims.
|
|
66
|
+
3. Contact information for accessibility feedback.
|
|
67
|
+
4. Reference to the enforcement procedure in the user's member state.
|
|
68
|
+
|
|
69
|
+
Statements are typically published at a URL like `/accessibility` or `/accessibility-statement` and linked from the footer of every public page.
|
|
70
|
+
|
|
71
|
+
### Article 23 — Penalties
|
|
72
|
+
|
|
73
|
+
Member states define penalties. Typical ranges:
|
|
74
|
+
|
|
75
|
+
- **Germany (BFSG)**: up to €100,000 fine per violation; market surveillance authority: Bundesanstalt für Arbeitsschutz und Arbeitsmedizin (BAuA).
|
|
76
|
+
- **France (Loi 2023-171)**: up to €250,000 fine per violation; enforcement: Arcom + DGCCRF.
|
|
77
|
+
- **Italy (Legislative Decree 82/2022)**: fines between €5,000 and 5% of annual turnover; enforcement: AgID.
|
|
78
|
+
- **Spain (Royal Decree 193/2023)**: up to €1,000,000 fine; enforcement: Autoridad Independiente de Control Administrativo.
|
|
79
|
+
- **Netherlands (Uitvoeringswet)**: fines up to €103,000; enforcement: Agentschap Telecom.
|
|
80
|
+
- **Poland (Ustawa o zapewnianiu spełniania wymagań dostępności)**: fines up to 10× monthly average wage (~PLN 75,000); enforcement: Prezes Zarządu PFRON + UOKiK.
|
|
81
|
+
|
|
82
|
+
Beyond fines, regulators can order service withdrawal from the EU market.
|
|
83
|
+
|
|
84
|
+
## Harmonized Standard: EN 301 549 v3.2.1
|
|
85
|
+
|
|
86
|
+
Full title: "Accessibility requirements for ICT products and services."
|
|
87
|
+
|
|
88
|
+
Published by ETSI (European Telecommunications Standards Institute), CEN, and CENELEC. Current version: **v3.2.1**, March 2021.
|
|
89
|
+
|
|
90
|
+
**Compliance with EN 301 549 is presumed to satisfy the EAA.** Deviations require individual justification.
|
|
91
|
+
|
|
92
|
+
Structure:
|
|
93
|
+
|
|
94
|
+
| Chapter | Scope |
|
|
95
|
+
|---------|-------|
|
|
96
|
+
| 4 | General / generic requirements |
|
|
97
|
+
| 5 | Generic requirements on all ICT |
|
|
98
|
+
| 6 | ICT with two-way voice communication |
|
|
99
|
+
| 7 | ICT with video capabilities |
|
|
100
|
+
| 8 | Hardware |
|
|
101
|
+
| 9 | Web (full WCAG 2.1 AA) |
|
|
102
|
+
| 10 | Non-web documents (PDF, etc.) |
|
|
103
|
+
| 11 | Software (native apps — mobile, desktop) |
|
|
104
|
+
| 12 | Documentation and support services |
|
|
105
|
+
| 13 | ICT providing relay / emergency services |
|
|
106
|
+
|
|
107
|
+
For websites and mobile apps, chapters 9 and 11 are most relevant.
|
|
108
|
+
|
|
109
|
+
## Timeline
|
|
110
|
+
|
|
111
|
+
| Date | Event |
|
|
112
|
+
|------|-------|
|
|
113
|
+
| 17 April 2019 | Directive adopted |
|
|
114
|
+
| 7 June 2019 | Published in Official Journal |
|
|
115
|
+
| 28 June 2022 | Member-state transposition deadline |
|
|
116
|
+
| **28 June 2025** | **Business compliance deadline — products/services placed on market** |
|
|
117
|
+
| 28 June 2030 | End of transition for existing contracts |
|
|
118
|
+
| 28 June 2045 | Outer limit for self-service terminals in service before 2025 |
|
|
119
|
+
|
|
120
|
+
**If you are selling to EU consumers and reading this after 28 June 2025: you are already required to comply.**
|
|
121
|
+
|
|
122
|
+
## Accessibility Statement Template
|
|
123
|
+
|
|
124
|
+
Most member states publish an official template. Common structural elements (minimum):
|
|
125
|
+
|
|
126
|
+
```markdown
|
|
127
|
+
# Accessibility Statement
|
|
128
|
+
|
|
129
|
+
[Organization name] is committed to making [service name / website URL] accessible in accordance with [Directive (EU) 2019/882 / national transposing law, e.g., Loi 2023-171 for France].
|
|
130
|
+
|
|
131
|
+
## Conformance Status
|
|
132
|
+
|
|
133
|
+
This [website / mobile application] is **[fully | partially | non-]compliant** with [EN 301 549 v3.2.1 / WCAG 2.1 Level AA].
|
|
134
|
+
|
|
135
|
+
[If partially / non-compliant:] The following content is not accessible:
|
|
136
|
+
|
|
137
|
+
- [Non-conformant element 1 — e.g., "PDF invoices from before 2023 lack tagged structure"]
|
|
138
|
+
- [Non-conformant element 2]
|
|
139
|
+
|
|
140
|
+
These non-conformances are due to [disproportionate burden reasoning / third-party content / technology limitations].
|
|
141
|
+
|
|
142
|
+
## Preparation of This Statement
|
|
143
|
+
|
|
144
|
+
This statement was prepared on [ISO date, e.g., 2025-06-15] and last reviewed on [date].
|
|
145
|
+
|
|
146
|
+
The assessment method was:
|
|
147
|
+
- [self-assessment / third-party audit by <firm>]
|
|
148
|
+
- [manual testing / automated tooling: axe-core, Lighthouse, Pa11y]
|
|
149
|
+
- [assistive technology testing: NVDA, JAWS, VoiceOver, TalkBack]
|
|
150
|
+
|
|
151
|
+
## Feedback and Contact Information
|
|
152
|
+
|
|
153
|
+
If you encounter any accessibility barriers on [service name], please contact us:
|
|
154
|
+
|
|
155
|
+
- Email: [accessibility@example.com]
|
|
156
|
+
- Phone: [+XX ...]
|
|
157
|
+
- Postal: [address]
|
|
158
|
+
|
|
159
|
+
We aim to respond within [14 days / as per national requirement].
|
|
160
|
+
|
|
161
|
+
## Enforcement Procedure
|
|
162
|
+
|
|
163
|
+
If you are not satisfied with our response, you may contact the national enforcement body:
|
|
164
|
+
|
|
165
|
+
- [Member state enforcement body name]
|
|
166
|
+
- [URL / phone / email]
|
|
167
|
+
|
|
168
|
+
## Technical Specifications
|
|
169
|
+
|
|
170
|
+
Accessibility of [service] relies on the following technologies:
|
|
171
|
+
|
|
172
|
+
- HTML5
|
|
173
|
+
- CSS
|
|
174
|
+
- JavaScript
|
|
175
|
+
- [WAI-ARIA 1.2]
|
|
176
|
+
- [PDF/UA for document accessibility]
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
## Member-State Enforcement URLs (to cite in statements)
|
|
180
|
+
|
|
181
|
+
Replace placeholders in statement templates with these URLs:
|
|
182
|
+
|
|
183
|
+
| Member state | Enforcement body | URL |
|
|
184
|
+
|---|---|---|
|
|
185
|
+
| Austria | Sozialministeriumservice (BSB) | https://www.sozialministeriumservice.at/ |
|
|
186
|
+
| Belgium | BOSA DG for Digital Transformation | https://bosa.belgium.be/ |
|
|
187
|
+
| Bulgaria | Ministry of Transport and Comms | https://www.mtc.government.bg/ |
|
|
188
|
+
| Croatia | Ministry of Physical Planning | https://mpgi.gov.hr/ |
|
|
189
|
+
| Cyprus | Department for Social Inclusion | http://www.mlsi.gov.cy/ |
|
|
190
|
+
| Czechia | Ministry of Industry and Trade | https://www.mpo.gov.cz/ |
|
|
191
|
+
| Denmark | Digitaliseringsstyrelsen | https://digst.dk/ |
|
|
192
|
+
| Estonia | Riigi Infosüsteemi Amet (RIA) | https://www.ria.ee/ |
|
|
193
|
+
| Finland | Regional State Administrative Agency | https://www.saavutettavuusvaatimukset.fi/ |
|
|
194
|
+
| France | Arcom + DGCCRF | https://www.arcom.fr/ |
|
|
195
|
+
| Germany | BFIT-Bund | https://www.bfit-bund.de/ |
|
|
196
|
+
| Greece | General Secretariat for Digital Governance | https://gov.gr/ |
|
|
197
|
+
| Hungary | Nemzeti Média- és Hírközlési Hatóság (NMHH) | https://nmhh.hu/ |
|
|
198
|
+
| Ireland | National Disability Authority (NDA) | https://nda.ie/ |
|
|
199
|
+
| Italy | AgID | https://www.agid.gov.it/ |
|
|
200
|
+
| Latvia | Ministry of Welfare | https://www.lm.gov.lv/ |
|
|
201
|
+
| Lithuania | Ministry of Social Security | https://socmin.lrv.lt/ |
|
|
202
|
+
| Luxembourg | SIP (Service information et presse) | https://sip.gouvernement.lu/ |
|
|
203
|
+
| Malta | Commission for the Rights of Persons with Disabilities (CRPD) | https://www.crpd.org.mt/ |
|
|
204
|
+
| Netherlands | Agentschap Telecom | https://www.agentschaptelecom.nl/ |
|
|
205
|
+
| Poland | PFRON + UOKiK | https://www.pfron.org.pl/ |
|
|
206
|
+
| Portugal | AMA (Agência para a Modernização Administrativa) | https://www.ama.gov.pt/ |
|
|
207
|
+
| Romania | Autoritatea Națională pentru Drepturile Persoanelor cu Dizabilități | https://andpdca.gov.ro/ |
|
|
208
|
+
| Slovakia | Ministry of Investments, Regional Development and Informatization | https://www.mirri.gov.sk/ |
|
|
209
|
+
| Slovenia | Ministry of Public Administration | https://www.gov.si/ |
|
|
210
|
+
| Spain | Autoridad Independiente de Control Administrativo | https://administracionelectronica.gob.es/ |
|
|
211
|
+
| Sweden | Myndigheten för digital förvaltning (DIGG) | https://www.digg.se/ |
|
|
212
|
+
|
|
213
|
+
## Common Member-State Route Conventions
|
|
214
|
+
|
|
215
|
+
The skill detects accessibility statements at any of these URL patterns:
|
|
216
|
+
|
|
217
|
+
- `/accessibility` — English (generic)
|
|
218
|
+
- `/accessibility-statement` — English (explicit)
|
|
219
|
+
- `/a11y` — English (shortform)
|
|
220
|
+
- `/dostepnosc` — Polish
|
|
221
|
+
- `/deklaracja-dostepnosci` — Polish (fuller)
|
|
222
|
+
- `/barrierefreiheit` — German
|
|
223
|
+
- `/barrierefreiheitserklaerung` — German (fuller)
|
|
224
|
+
- `/declaration-accessibilite` — French
|
|
225
|
+
- `/declaración-accesibilidad` — Spanish
|
|
226
|
+
- `/dichiarazione-accessibilita` — Italian
|
|
227
|
+
- `/dichiarazione-di-accessibilita` — Italian (fuller)
|
|
228
|
+
- `/toegankelijkheidsverklaring` — Dutch
|
|
229
|
+
- `/tilgangelighetserklaering` — Norwegian (non-EU but adjacent)
|
|
230
|
+
- `/tillganglighetsutlatande` — Swedish
|
|
231
|
+
- `/saavutettavuusseloste` — Finnish
|
|
232
|
+
- `/prohlaseni-o-pristupnosti` — Czech
|
|
233
|
+
- `/vyhlasenie-o-pristupnosti` — Slovak
|
|
234
|
+
- `/nyilatkozat-az-akadalymentesitesrol` — Hungarian
|
|
235
|
+
|
|
236
|
+
## Accessibility Conformance Report (ACR) / VPAT
|
|
237
|
+
|
|
238
|
+
Beyond the public accessibility statement, many organizations produce a machine-readable or structured **Accessibility Conformance Report** — often using the **VPAT® 2.5 Rev** template (Voluntary Product Accessibility Template) from the Information Technology Industry Council (ITI).
|
|
239
|
+
|
|
240
|
+
VPAT sections for EU markets use the "INT" or "EU" variant, which maps to EN 301 549.
|
|
241
|
+
|
|
242
|
+
VPAT is not legally required under EAA but is common in B2B / procurement contexts.
|
|
243
|
+
|
|
244
|
+
Template: https://www.itic.org/policy/accessibility/vpat
|
|
245
|
+
|
|
246
|
+
## References
|
|
247
|
+
|
|
248
|
+
- EAA directive (full text): https://eur-lex.europa.eu/eli/dir/2019/882/oj
|
|
249
|
+
- EN 301 549 v3.2.1 (PDF): https://www.etsi.org/deliver/etsi_en/301500_301599/301549/03.02.01_60/en_301549v030201p.pdf
|
|
250
|
+
- European Commission EAA page: https://ec.europa.eu/social/main.jsp?catId=1202
|
|
251
|
+
- W3C WAI EAA overview: https://www.w3.org/WAI/policies/european-union/#eaa
|
|
252
|
+
- W3C accessibility statement generator: https://www.w3.org/WAI/planning/statements/generator/
|
|
@@ -0,0 +1,329 @@
|
|
|
1
|
+
# Mobile Accessibility — EN 301 549 & EAA
|
|
2
|
+
|
|
3
|
+
Reference for `a11y-validate` Category 8d. Platform a11y APIs and cross-platform framework patterns (React Native, Flutter).
|
|
4
|
+
|
|
5
|
+
**EAA scope includes consumer mobile apps** (Article 2(2) — electronic communications, banking, e-commerce, transport). EN 301 549 Chapter 11 covers software / native apps.
|
|
6
|
+
|
|
7
|
+
## Platform APIs
|
|
8
|
+
|
|
9
|
+
### iOS — UIAccessibility (UIKit/SwiftUI)
|
|
10
|
+
|
|
11
|
+
Core properties on `UIView` / `UIAccessibilityElement`:
|
|
12
|
+
|
|
13
|
+
- `isAccessibilityElement: Bool` — is this an atomic element for VoiceOver?
|
|
14
|
+
- `accessibilityLabel: String?` — concise name.
|
|
15
|
+
- `accessibilityValue: String?` — current value (for sliders, inputs).
|
|
16
|
+
- `accessibilityHint: String?` — what happens on activation.
|
|
17
|
+
- `accessibilityTraits: UIAccessibilityTraits` — button, link, header, selected, adjustable, etc.
|
|
18
|
+
- `accessibilityIdentifier: String?` — for UI testing (not user-facing).
|
|
19
|
+
|
|
20
|
+
SwiftUI modifiers:
|
|
21
|
+
- `.accessibilityLabel(_:)`
|
|
22
|
+
- `.accessibilityValue(_:)`
|
|
23
|
+
- `.accessibilityHint(_:)`
|
|
24
|
+
- `.accessibilityAddTraits(_:)` / `.accessibilityRemoveTraits(_:)`
|
|
25
|
+
- `.accessibilityElement(children:)`
|
|
26
|
+
- `.accessibilityHidden(_:)`
|
|
27
|
+
|
|
28
|
+
### Android — AccessibilityNodeInfo
|
|
29
|
+
|
|
30
|
+
Core XML attributes:
|
|
31
|
+
- `android:contentDescription` — equivalent to `accessibilityLabel`.
|
|
32
|
+
- `android:labelFor` — for inputs.
|
|
33
|
+
- `android:importantForAccessibility` — yes / no / auto / noHideDescendants.
|
|
34
|
+
- `android:accessibilityHeading` — heading level.
|
|
35
|
+
- `android:screenReaderFocusable` — focusable by TalkBack.
|
|
36
|
+
- `android:accessibilityLiveRegion` — none / polite / assertive.
|
|
37
|
+
|
|
38
|
+
Jetpack Compose modifiers:
|
|
39
|
+
- `Modifier.semantics { contentDescription = "..." }`
|
|
40
|
+
- `Modifier.clearAndSetSemantics { ... }`
|
|
41
|
+
- `Modifier.semantics(mergeDescendants = true) { ... }`
|
|
42
|
+
|
|
43
|
+
## React Native
|
|
44
|
+
|
|
45
|
+
```jsx
|
|
46
|
+
import { TouchableOpacity, Pressable, Text, Image, AccessibilityInfo } from 'react-native';
|
|
47
|
+
|
|
48
|
+
// Button equivalent
|
|
49
|
+
<TouchableOpacity
|
|
50
|
+
accessible={true}
|
|
51
|
+
accessibilityRole="button"
|
|
52
|
+
accessibilityLabel="Submit form"
|
|
53
|
+
accessibilityHint="Sends your data for processing"
|
|
54
|
+
accessibilityState={{ disabled: loading, busy: submitting }}
|
|
55
|
+
onPress={handleSubmit}
|
|
56
|
+
>
|
|
57
|
+
<Text>Submit</Text>
|
|
58
|
+
</TouchableOpacity>
|
|
59
|
+
|
|
60
|
+
// Image with a11y
|
|
61
|
+
<Image
|
|
62
|
+
source={require('./photo.jpg')}
|
|
63
|
+
accessible={true}
|
|
64
|
+
accessibilityLabel="Team photo from 2025 conference"
|
|
65
|
+
/>
|
|
66
|
+
|
|
67
|
+
// Decorative image — hide from AT
|
|
68
|
+
<Image
|
|
69
|
+
source={require('./decorative-pattern.jpg')}
|
|
70
|
+
accessibilityElementsHidden={true}
|
|
71
|
+
importantForAccessibility="no"
|
|
72
|
+
/>
|
|
73
|
+
|
|
74
|
+
// Announce dynamic change
|
|
75
|
+
AccessibilityInfo.announceForAccessibility('Item added to cart');
|
|
76
|
+
|
|
77
|
+
// Check if screen reader is active
|
|
78
|
+
const isEnabled = await AccessibilityInfo.isScreenReaderEnabled();
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
Common React Native a11y props:
|
|
82
|
+
- `accessible` — whether element is a11y element (default: true for Text, Image, TouchableOpacity).
|
|
83
|
+
- `accessibilityLabel` — required on all interactive elements without visible text.
|
|
84
|
+
- `accessibilityHint` — what happens on activation.
|
|
85
|
+
- `accessibilityRole` — button / link / image / header / adjustable / summary / alert / checkbox / radio / togglebutton / tab / tablist / search / combobox / menu / menuitem / menubar / progressbar / radiogroup / scrollbar / spinbutton / switch / timer / toolbar.
|
|
86
|
+
- `accessibilityState` — `{ disabled, selected, checked, busy, expanded }`.
|
|
87
|
+
- `accessibilityValue` — `{ min, max, now, text }` for sliders/progress.
|
|
88
|
+
- `accessibilityLiveRegion` (Android) / `accessibilityElementsHidden` (iOS) — live updates.
|
|
89
|
+
- `onAccessibilityTap` — alt handler for VoiceOver double-tap.
|
|
90
|
+
- `onMagicTap` — iOS double-tap with two fingers.
|
|
91
|
+
|
|
92
|
+
### Common RN a11y violations (flagged by Category 8d)
|
|
93
|
+
|
|
94
|
+
```jsx
|
|
95
|
+
// Bad — no accessibilityLabel
|
|
96
|
+
<TouchableOpacity onPress={close}>
|
|
97
|
+
<Icon name="close" />
|
|
98
|
+
</TouchableOpacity>
|
|
99
|
+
|
|
100
|
+
// Good
|
|
101
|
+
<TouchableOpacity
|
|
102
|
+
onPress={close}
|
|
103
|
+
accessibilityRole="button"
|
|
104
|
+
accessibilityLabel="Close dialog"
|
|
105
|
+
>
|
|
106
|
+
<Icon name="close" accessibilityElementsHidden importantForAccessibility="no" />
|
|
107
|
+
</TouchableOpacity>
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
```jsx
|
|
111
|
+
// Bad — decorative image announced by TalkBack
|
|
112
|
+
<Image source={require('./pattern.png')} />
|
|
113
|
+
|
|
114
|
+
// Good
|
|
115
|
+
<Image
|
|
116
|
+
source={require('./pattern.png')}
|
|
117
|
+
accessibilityElementsHidden={true}
|
|
118
|
+
importantForAccessibility="no"
|
|
119
|
+
/>
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
### Key RN a11y libraries
|
|
123
|
+
|
|
124
|
+
- `@react-native-community/hooks` — `useAccessibilityInfo()`.
|
|
125
|
+
- `react-native-reanimated` — respects `reduceMotion` via `useReducedMotion()`.
|
|
126
|
+
- `@testing-library/react-native` — has `getByRole`, `getByA11yLabel` queries for testing.
|
|
127
|
+
|
|
128
|
+
### Testing
|
|
129
|
+
|
|
130
|
+
- **iOS**: Xcode Accessibility Inspector (⌥⌘8 in Simulator).
|
|
131
|
+
- **Android**: Accessibility Scanner app + TalkBack.
|
|
132
|
+
- **Automated**: Lighthouse for WebView content; platform-specific tools for native.
|
|
133
|
+
|
|
134
|
+
## Flutter
|
|
135
|
+
|
|
136
|
+
```dart
|
|
137
|
+
import 'package:flutter/material.dart';
|
|
138
|
+
import 'package:flutter/semantics.dart';
|
|
139
|
+
|
|
140
|
+
// Button with semantics
|
|
141
|
+
Semantics(
|
|
142
|
+
label: 'Submit form',
|
|
143
|
+
hint: 'Sends your data for processing',
|
|
144
|
+
button: true,
|
|
145
|
+
enabled: !loading,
|
|
146
|
+
child: GestureDetector(
|
|
147
|
+
onTap: handleSubmit,
|
|
148
|
+
child: Container(
|
|
149
|
+
child: Text('Submit'),
|
|
150
|
+
),
|
|
151
|
+
),
|
|
152
|
+
)
|
|
153
|
+
|
|
154
|
+
// Simpler for standard widgets — most have built-in semantics
|
|
155
|
+
ElevatedButton(
|
|
156
|
+
onPressed: handleSubmit,
|
|
157
|
+
child: Text('Submit'), // Text becomes accessible name automatically
|
|
158
|
+
)
|
|
159
|
+
|
|
160
|
+
// Icon-only button (needs explicit label)
|
|
161
|
+
IconButton(
|
|
162
|
+
icon: Icon(Icons.close),
|
|
163
|
+
onPressed: close,
|
|
164
|
+
tooltip: 'Close', // Tooltip doubles as accessibility label
|
|
165
|
+
)
|
|
166
|
+
|
|
167
|
+
// Image with a11y
|
|
168
|
+
Image.asset(
|
|
169
|
+
'assets/team.jpg',
|
|
170
|
+
semanticLabel: 'Team photo from 2025 conference',
|
|
171
|
+
)
|
|
172
|
+
|
|
173
|
+
// Decorative image — exclude from semantics tree
|
|
174
|
+
Image.asset(
|
|
175
|
+
'assets/pattern.jpg',
|
|
176
|
+
excludeFromSemantics: true,
|
|
177
|
+
)
|
|
178
|
+
|
|
179
|
+
// Announce dynamic change
|
|
180
|
+
SemanticsService.announce('Item added to cart', TextDirection.ltr);
|
|
181
|
+
|
|
182
|
+
// Merge child semantics into one (for grouped interactive elements)
|
|
183
|
+
MergeSemantics(
|
|
184
|
+
child: Row(
|
|
185
|
+
children: [
|
|
186
|
+
Checkbox(value: isChecked, onChanged: setChecked),
|
|
187
|
+
Text('Agree to terms'),
|
|
188
|
+
],
|
|
189
|
+
),
|
|
190
|
+
)
|
|
191
|
+
|
|
192
|
+
// Exclude interactive semantics from child
|
|
193
|
+
ExcludeSemantics(
|
|
194
|
+
child: AnimatedIcon(...), // purely decorative
|
|
195
|
+
)
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
Key Flutter semantic properties:
|
|
199
|
+
- `label` — accessible name.
|
|
200
|
+
- `hint` — action hint.
|
|
201
|
+
- `value` — current value (slider / input).
|
|
202
|
+
- `increasedValue` / `decreasedValue` — for adjustable widgets.
|
|
203
|
+
- `button` / `link` / `header` / `textField` / `checked` / `selected` — role booleans.
|
|
204
|
+
- `enabled` / `focused` / `focusable` — state.
|
|
205
|
+
- `image` — is an image (for decorative vs. informative).
|
|
206
|
+
- `liveRegion` — announce changes.
|
|
207
|
+
- `hidden` — excluded from a11y tree (different from `excludeFromSemantics` which merges up).
|
|
208
|
+
|
|
209
|
+
### Common Flutter a11y violations (flagged)
|
|
210
|
+
|
|
211
|
+
```dart
|
|
212
|
+
// Bad — GestureDetector with no semantics
|
|
213
|
+
GestureDetector(
|
|
214
|
+
onTap: close,
|
|
215
|
+
child: Icon(Icons.close),
|
|
216
|
+
)
|
|
217
|
+
|
|
218
|
+
// Good
|
|
219
|
+
Semantics(
|
|
220
|
+
label: 'Close dialog',
|
|
221
|
+
button: true,
|
|
222
|
+
child: GestureDetector(
|
|
223
|
+
onTap: close,
|
|
224
|
+
child: Icon(Icons.close),
|
|
225
|
+
),
|
|
226
|
+
)
|
|
227
|
+
|
|
228
|
+
// Or use IconButton which has built-in semantics
|
|
229
|
+
IconButton(
|
|
230
|
+
icon: Icon(Icons.close),
|
|
231
|
+
onPressed: close,
|
|
232
|
+
tooltip: 'Close dialog',
|
|
233
|
+
)
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
```dart
|
|
237
|
+
// Bad — Image without label
|
|
238
|
+
Image.network('https://example.com/product.jpg')
|
|
239
|
+
|
|
240
|
+
// Good
|
|
241
|
+
Image.network(
|
|
242
|
+
'https://example.com/product.jpg',
|
|
243
|
+
semanticLabel: 'Red leather handbag, size medium',
|
|
244
|
+
)
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
### Flutter a11y testing
|
|
248
|
+
|
|
249
|
+
- `flutter test --tags accessibility` with `SemanticsTester`.
|
|
250
|
+
- `flutter_driver` with `SerializableFinder.bySemanticsLabel(...)`.
|
|
251
|
+
- Accessibility Inspector on iOS simulator / Accessibility Scanner on Android.
|
|
252
|
+
|
|
253
|
+
## Cross-Platform Patterns
|
|
254
|
+
|
|
255
|
+
### Reduced motion
|
|
256
|
+
|
|
257
|
+
**React Native**:
|
|
258
|
+
```jsx
|
|
259
|
+
import { useReducedMotion } from 'react-native-reanimated';
|
|
260
|
+
|
|
261
|
+
const reducedMotion = useReducedMotion();
|
|
262
|
+
const duration = reducedMotion ? 0 : 300;
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
**Flutter**:
|
|
266
|
+
```dart
|
|
267
|
+
final reduceMotion = MediaQuery.of(context).disableAnimations;
|
|
268
|
+
final duration = reduceMotion ? Duration.zero : Duration(milliseconds: 300);
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
### Font scaling
|
|
272
|
+
|
|
273
|
+
**React Native**: text scales with system font size by default. Set `allowFontScaling={false}` only with explicit reason (often inappropriate — WCAG 1.4.4).
|
|
274
|
+
|
|
275
|
+
**Flutter**: `MediaQuery.of(context).textScaler` (replaces `textScaleFactor` in Flutter 3.16+). Use `Text(..., textScaler: MediaQuery.textScalerOf(context))` explicitly or rely on default inheritance.
|
|
276
|
+
|
|
277
|
+
### Contrast mode
|
|
278
|
+
|
|
279
|
+
**iOS**: `UIAccessibilityDarkerSystemColorsEnabled` / `accessibilityContrastChanged` notification.
|
|
280
|
+
|
|
281
|
+
**Android**: `AccessibilityManager.isHighTextContrastEnabled()`.
|
|
282
|
+
|
|
283
|
+
**React Native**: `AccessibilityInfo.isHighTextContrastEnabled()` (Android only) / check `useColorScheme()` + manual high-contrast theme.
|
|
284
|
+
|
|
285
|
+
**Flutter**: `MediaQuery.of(context).highContrast`.
|
|
286
|
+
|
|
287
|
+
### Screen reader detection
|
|
288
|
+
|
|
289
|
+
**React Native**:
|
|
290
|
+
```jsx
|
|
291
|
+
AccessibilityInfo.isScreenReaderEnabled().then(enabled => { ... });
|
|
292
|
+
AccessibilityInfo.addEventListener('screenReaderChanged', handler);
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
**Flutter**:
|
|
296
|
+
```dart
|
|
297
|
+
MediaQuery.of(context).accessibleNavigation // true if TalkBack/VoiceOver active
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
## EN 301 549 Mobile Chapter 11 — Key Clauses
|
|
301
|
+
|
|
302
|
+
Maps WCAG criteria to native software context:
|
|
303
|
+
|
|
304
|
+
| Clause | WCAG | Implementation |
|
|
305
|
+
|--------|------|----------------|
|
|
306
|
+
| 11.1.1.1 | 1.1.1 | All non-text content has text alternative |
|
|
307
|
+
| 11.1.2.1 | 1.2.1 | Prerecorded audio/video alternatives |
|
|
308
|
+
| 11.1.2.2 | 1.2.2 | Captions for prerecorded video |
|
|
309
|
+
| 11.1.3.1 | 1.3.1 | Info and relationships via accessibility API |
|
|
310
|
+
| 11.1.3.2 | 1.3.2 | Meaningful sequence exposed to AT |
|
|
311
|
+
| 11.1.4.1 | 1.4.1 | No color-only signalling |
|
|
312
|
+
| 11.1.4.2 | 1.4.2 | Audio control |
|
|
313
|
+
| 11.1.4.3 | 1.4.3 | Contrast minimum (4.5:1) |
|
|
314
|
+
| 11.1.4.4 | 1.4.4 | Text resize to 200% without loss |
|
|
315
|
+
| 11.2.1.1 | 2.1.1 | All functionality keyboard-accessible (external keyboard) |
|
|
316
|
+
| 11.2.1.2 | 2.1.2 | No keyboard trap |
|
|
317
|
+
| 11.2.2.1 | 2.2.1 | Timing adjustable |
|
|
318
|
+
| 11.4.1.1 | 4.1.1 | Parsing (platform UI tree correctness) |
|
|
319
|
+
| 11.4.1.2 | 4.1.2 | Name, role, value exposed to AT |
|
|
320
|
+
|
|
321
|
+
## References
|
|
322
|
+
|
|
323
|
+
- Apple Accessibility (iOS): https://developer.apple.com/accessibility/ios/
|
|
324
|
+
- Android Accessibility: https://developer.android.com/guide/topics/ui/accessibility
|
|
325
|
+
- React Native A11y API: https://reactnative.dev/docs/accessibility
|
|
326
|
+
- Flutter Semantics: https://api.flutter.dev/flutter/widgets/Semantics-class.html
|
|
327
|
+
- Flutter Accessibility guide: https://docs.flutter.dev/ui/accessibility-and-internationalization/accessibility
|
|
328
|
+
- EN 301 549 v3.2.1: https://www.etsi.org/deliver/etsi_en/301500_301599/301549/03.02.01_60/en_301549v030201p.pdf
|
|
329
|
+
- Mobile a11y best practices (BBC): https://www.bbc.co.uk/accessibility/forproducts/mobile/
|