claude-plugin-wordpress-manager 1.4.0 → 1.7.0
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/.claude-plugin/plugin.json +7 -3
- package/CHANGELOG.md +111 -0
- package/README.md +10 -3
- package/agents/wp-accessibility-auditor.md +206 -0
- package/agents/wp-content-strategist.md +18 -0
- package/agents/wp-deployment-engineer.md +34 -2
- package/agents/wp-performance-optimizer.md +12 -0
- package/agents/wp-security-auditor.md +20 -0
- package/agents/wp-security-hardener.md +266 -0
- package/agents/wp-site-manager.md +14 -0
- package/agents/wp-test-engineer.md +207 -0
- package/docs/GUIDE.md +68 -15
- package/docs/guides/INDEX.md +46 -0
- package/docs/guides/wp-blog.md +590 -0
- package/docs/guides/wp-design-system.md +976 -0
- package/docs/guides/wp-ecommerce.md +786 -0
- package/docs/guides/wp-landing-page.md +762 -0
- package/docs/guides/wp-portfolio.md +713 -0
- package/docs/plans/2026-02-27-design-system-guide-design.md +30 -0
- package/docs/plans/2026-02-27-local-dev-tools-assessment.md +332 -0
- package/docs/plans/2026-02-27-local-env-design.md +179 -0
- package/docs/plans/2026-02-27-site-type-guides-design.md +44 -0
- package/package.json +7 -3
- package/skills/wordpress-router/SKILL.md +25 -5
- package/skills/wordpress-router/references/decision-tree.md +59 -3
- package/skills/wp-accessibility/SKILL.md +170 -0
- package/skills/wp-accessibility/references/a11y-audit-tools.md +248 -0
- package/skills/wp-accessibility/references/a11y-testing.md +222 -0
- package/skills/wp-accessibility/references/block-a11y.md +247 -0
- package/skills/wp-accessibility/references/interactive-a11y.md +272 -0
- package/skills/wp-accessibility/references/media-a11y.md +254 -0
- package/skills/wp-accessibility/references/theme-a11y.md +309 -0
- package/skills/wp-audit/SKILL.md +4 -0
- package/skills/wp-block-development/SKILL.md +5 -0
- package/skills/wp-block-themes/SKILL.md +4 -0
- package/skills/wp-deploy/SKILL.md +12 -0
- package/skills/wp-e2e-testing/SKILL.md +186 -0
- package/skills/wp-e2e-testing/references/ci-integration.md +174 -0
- package/skills/wp-e2e-testing/references/jest-wordpress.md +114 -0
- package/skills/wp-e2e-testing/references/phpunit-wordpress.md +141 -0
- package/skills/wp-e2e-testing/references/playwright-wordpress.md +108 -0
- package/skills/wp-e2e-testing/references/test-data-generation.md +127 -0
- package/skills/wp-e2e-testing/references/visual-regression.md +107 -0
- package/skills/wp-e2e-testing/references/wp-env-setup.md +97 -0
- package/skills/wp-e2e-testing/scripts/test_inspect.mjs +375 -0
- package/skills/wp-headless/SKILL.md +168 -0
- package/skills/wp-headless/references/api-layer-choice.md +160 -0
- package/skills/wp-headless/references/cors-config.md +245 -0
- package/skills/wp-headless/references/frontend-integration.md +331 -0
- package/skills/wp-headless/references/headless-auth.md +286 -0
- package/skills/wp-headless/references/webhooks.md +277 -0
- package/skills/wp-headless/references/wpgraphql.md +331 -0
- package/skills/wp-headless/scripts/headless_inspect.mjs +321 -0
- package/skills/wp-i18n/SKILL.md +170 -0
- package/skills/wp-i18n/references/js-i18n.md +201 -0
- package/skills/wp-i18n/references/multilingual-setup.md +219 -0
- package/skills/wp-i18n/references/php-i18n.md +196 -0
- package/skills/wp-i18n/references/rtl-support.md +206 -0
- package/skills/wp-i18n/references/translation-workflow.md +178 -0
- package/skills/wp-i18n/references/wpcli-i18n.md +177 -0
- package/skills/wp-i18n/scripts/i18n_inspect.mjs +330 -0
- package/skills/wp-interactivity-api/SKILL.md +4 -0
- package/skills/wp-local-env/SKILL.md +233 -0
- package/skills/wp-local-env/references/localwp-adapter.md +156 -0
- package/skills/wp-local-env/references/mcp-adapter-setup.md +153 -0
- package/skills/wp-local-env/references/studio-adapter.md +127 -0
- package/skills/wp-local-env/references/wpenv-adapter.md +121 -0
- package/skills/wp-local-env/scripts/detect_local_env.mjs +404 -0
- package/skills/wp-playground/SKILL.md +13 -1
- package/skills/wp-plugin-development/SKILL.md +6 -0
- package/skills/wp-rest-api/SKILL.md +4 -0
- package/skills/wp-security/SKILL.md +179 -0
- package/skills/wp-security/references/api-restriction.md +147 -0
- package/skills/wp-security/references/authentication-hardening.md +105 -0
- package/skills/wp-security/references/filesystem-hardening.md +105 -0
- package/skills/wp-security/references/http-headers.md +105 -0
- package/skills/wp-security/references/incident-response.md +144 -0
- package/skills/wp-security/references/user-capabilities.md +115 -0
- package/skills/wp-security/references/wp-config-security.md +129 -0
- package/skills/wp-security/scripts/security_inspect.mjs +393 -0
- package/skills/wp-wpcli-and-ops/SKILL.md +6 -0
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
# Accessibility Testing Methodology
|
|
2
|
+
|
|
3
|
+
Use this file when planning and executing accessibility testing for WordPress sites.
|
|
4
|
+
|
|
5
|
+
## Testing levels
|
|
6
|
+
|
|
7
|
+
| Level | Method | Coverage | When |
|
|
8
|
+
|-------|--------|----------|------|
|
|
9
|
+
| 1 | Automated scans | ~30-40% of issues | Every commit/build |
|
|
10
|
+
| 2 | Keyboard testing | +20% | Every feature |
|
|
11
|
+
| 3 | Screen reader testing | +15% | Major features |
|
|
12
|
+
| 4 | Manual expert audit | +25% | Releases, redesigns |
|
|
13
|
+
|
|
14
|
+
All four levels are needed for comprehensive coverage.
|
|
15
|
+
|
|
16
|
+
## Level 1: Automated testing
|
|
17
|
+
|
|
18
|
+
### Per-page scan
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
# axe-core CLI
|
|
22
|
+
npx @axe-core/cli http://localhost:8888/ --tags wcag2a,wcag2aa
|
|
23
|
+
|
|
24
|
+
# Playwright with axe
|
|
25
|
+
npx playwright test tests/a11y/
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
### What automated tools catch
|
|
29
|
+
|
|
30
|
+
- Missing alt text
|
|
31
|
+
- Missing form labels
|
|
32
|
+
- Insufficient color contrast
|
|
33
|
+
- Missing document language
|
|
34
|
+
- Missing page title
|
|
35
|
+
- Duplicate IDs
|
|
36
|
+
- Invalid ARIA attributes
|
|
37
|
+
- Missing landmarks
|
|
38
|
+
|
|
39
|
+
### What automated tools miss
|
|
40
|
+
|
|
41
|
+
- Quality of alt text ("image" vs. meaningful description)
|
|
42
|
+
- Logical reading order
|
|
43
|
+
- Meaningful heading hierarchy
|
|
44
|
+
- Keyboard trap issues
|
|
45
|
+
- Focus order correctness
|
|
46
|
+
- Context of links ("click here" vs. descriptive)
|
|
47
|
+
- Screen reader announcement quality
|
|
48
|
+
- Custom widget keyboard patterns
|
|
49
|
+
|
|
50
|
+
## Level 2: Keyboard testing
|
|
51
|
+
|
|
52
|
+
### Test procedure
|
|
53
|
+
|
|
54
|
+
1. **Start at the browser URL bar**
|
|
55
|
+
2. **Press Tab** — does focus move to skip link?
|
|
56
|
+
3. **Press Enter** on skip link — does focus move to main content?
|
|
57
|
+
4. **Tab through the page** — is every interactive element reachable?
|
|
58
|
+
5. **Check focus indicators** — is focus always visible?
|
|
59
|
+
6. **Test interactive widgets**:
|
|
60
|
+
- Buttons: Enter and Space activate
|
|
61
|
+
- Links: Enter activates
|
|
62
|
+
- Dropdowns: Arrow keys navigate, Enter selects
|
|
63
|
+
- Modals: Tab cycles within, Escape closes
|
|
64
|
+
- Menus: Arrow keys navigate submenus
|
|
65
|
+
|
|
66
|
+
### Keyboard testing checklist
|
|
67
|
+
|
|
68
|
+
```
|
|
69
|
+
Page: _______________ Date: ___________
|
|
70
|
+
|
|
71
|
+
Navigation:
|
|
72
|
+
- [ ] Skip link is first focusable element
|
|
73
|
+
- [ ] Skip link moves focus to main content
|
|
74
|
+
- [ ] All navigation links are reachable via Tab
|
|
75
|
+
- [ ] Dropdown menus open with Enter/Space
|
|
76
|
+
- [ ] Dropdown menus navigate with Arrow keys
|
|
77
|
+
- [ ] Escape closes dropdown menus
|
|
78
|
+
|
|
79
|
+
Content:
|
|
80
|
+
- [ ] All links are reachable
|
|
81
|
+
- [ ] All buttons are activatable
|
|
82
|
+
- [ ] Focus order matches visual order
|
|
83
|
+
- [ ] No keyboard traps (can always Tab away)
|
|
84
|
+
- [ ] Focus indicator is always visible
|
|
85
|
+
|
|
86
|
+
Forms:
|
|
87
|
+
- [ ] All inputs are reachable
|
|
88
|
+
- [ ] Labels are associated (focus on label focuses input)
|
|
89
|
+
- [ ] Error messages are reachable
|
|
90
|
+
- [ ] Submit button is reachable
|
|
91
|
+
- [ ] Required fields are announced
|
|
92
|
+
|
|
93
|
+
Interactive Components:
|
|
94
|
+
- [ ] Modals trap focus and close with Escape
|
|
95
|
+
- [ ] Tabs navigate with Arrow keys
|
|
96
|
+
- [ ] Accordions toggle with Enter/Space
|
|
97
|
+
- [ ] Sliders adjust with Arrow keys
|
|
98
|
+
- [ ] Carousels have pause control
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
## Level 3: Screen reader testing
|
|
102
|
+
|
|
103
|
+
### Quick screen reader test flow
|
|
104
|
+
|
|
105
|
+
1. **Open the page** with screen reader active
|
|
106
|
+
2. **Listen to page title** — is it announced correctly?
|
|
107
|
+
3. **Check landmarks** (NVDA: D key) — are header, nav, main, footer present?
|
|
108
|
+
4. **Check headings** (NVDA: H key) — is the hierarchy logical (H1 → H2 → H3)?
|
|
109
|
+
5. **Read through content** — does the reading order make sense?
|
|
110
|
+
6. **Test forms** — are labels announced? Are errors announced?
|
|
111
|
+
7. **Test interactive components** — are state changes announced?
|
|
112
|
+
|
|
113
|
+
### Common screen reader issues in WordPress
|
|
114
|
+
|
|
115
|
+
| Issue | Cause | Fix |
|
|
116
|
+
|-------|-------|-----|
|
|
117
|
+
| "Image" with no description | Missing alt text | Add meaningful alt |
|
|
118
|
+
| "Link" or "Button" with no label | Icon-only elements | Add `aria-label` or `screen-reader-text` |
|
|
119
|
+
| "Group" or "Region" | Unlabeled landmarks | Add `aria-label` to nav, aside |
|
|
120
|
+
| State not announced | Missing aria-expanded/selected | Add ARIA state attributes |
|
|
121
|
+
| Content skipped | `display:none` or `aria-hidden="true"` | Fix visibility or remove aria-hidden |
|
|
122
|
+
| Duplicate announcements | Redundant labels | Remove duplicate text |
|
|
123
|
+
|
|
124
|
+
### WordPress admin screen reader mode
|
|
125
|
+
|
|
126
|
+
WordPress admin has a Screen Reader mode: Users → Profile → Enable "Disable the visual editor when writing."
|
|
127
|
+
|
|
128
|
+
The admin is tested by WordPress core a11y team, but custom admin pages need separate testing.
|
|
129
|
+
|
|
130
|
+
## Level 4: Manual expert audit
|
|
131
|
+
|
|
132
|
+
### WCAG 2.2 AA audit structure
|
|
133
|
+
|
|
134
|
+
Organize by WCAG principle:
|
|
135
|
+
|
|
136
|
+
**1. Perceivable**
|
|
137
|
+
- 1.1 Text Alternatives — alt text for images
|
|
138
|
+
- 1.2 Time-Based Media — captions, transcripts
|
|
139
|
+
- 1.3 Adaptable — semantic structure, meaningful sequence
|
|
140
|
+
- 1.4 Distinguishable — contrast, resize, images of text
|
|
141
|
+
|
|
142
|
+
**2. Operable**
|
|
143
|
+
- 2.1 Keyboard Accessible — all functionality via keyboard
|
|
144
|
+
- 2.2 Enough Time — adjustable time limits
|
|
145
|
+
- 2.3 Seizures — no flashing > 3/sec
|
|
146
|
+
- 2.4 Navigable — skip links, page titles, focus order
|
|
147
|
+
- 2.5 Input Modalities — pointer gestures, motion
|
|
148
|
+
|
|
149
|
+
**3. Understandable**
|
|
150
|
+
- 3.1 Readable — page language, unusual words
|
|
151
|
+
- 3.2 Predictable — consistent navigation, consistent identification
|
|
152
|
+
- 3.3 Input Assistance — error identification, labels, error prevention
|
|
153
|
+
|
|
154
|
+
**4. Robust**
|
|
155
|
+
- 4.1 Compatible — valid HTML, name/role/value
|
|
156
|
+
|
|
157
|
+
## Testing across WordPress contexts
|
|
158
|
+
|
|
159
|
+
### Pages to test
|
|
160
|
+
|
|
161
|
+
| Page | Why |
|
|
162
|
+
|------|-----|
|
|
163
|
+
| Homepage | Most visited, often most complex |
|
|
164
|
+
| Blog archive | Lists, pagination |
|
|
165
|
+
| Single post | Content, comments |
|
|
166
|
+
| Search results | Dynamic content |
|
|
167
|
+
| 404 page | Error handling |
|
|
168
|
+
| Contact/forms | Form accessibility |
|
|
169
|
+
| WooCommerce checkout | Complex forms, payment |
|
|
170
|
+
| Login page | Authentication |
|
|
171
|
+
|
|
172
|
+
### WordPress-specific elements to check
|
|
173
|
+
|
|
174
|
+
- **Block editor output** — each block type should be tested
|
|
175
|
+
- **Widget areas** — sidebar, footer widgets
|
|
176
|
+
- **Navigation menus** — primary, footer, mobile menus
|
|
177
|
+
- **Comment forms** — reply, nested comments
|
|
178
|
+
- **Pagination** — archive navigation
|
|
179
|
+
- **Search** — form and results
|
|
180
|
+
- **Admin bar** — visible to logged-in users
|
|
181
|
+
|
|
182
|
+
## Regression testing strategy
|
|
183
|
+
|
|
184
|
+
```js
|
|
185
|
+
// tests/a11y/critical-pages.spec.js
|
|
186
|
+
import { test, expect } from '@playwright/test';
|
|
187
|
+
import AxeBuilder from '@axe-core/playwright';
|
|
188
|
+
|
|
189
|
+
const pages = [
|
|
190
|
+
{ name: 'Homepage', path: '/' },
|
|
191
|
+
{ name: 'Blog', path: '/blog/' },
|
|
192
|
+
{ name: 'Contact', path: '/contact/' },
|
|
193
|
+
{ name: 'Search', path: '/?s=test' },
|
|
194
|
+
];
|
|
195
|
+
|
|
196
|
+
for (const { name, path } of pages) {
|
|
197
|
+
test(`${name} has no a11y violations`, async ({ page }) => {
|
|
198
|
+
await page.goto(path);
|
|
199
|
+
const results = await new AxeBuilder({ page })
|
|
200
|
+
.withTags(['wcag2a', 'wcag2aa'])
|
|
201
|
+
.analyze();
|
|
202
|
+
expect(results.violations).toEqual([]);
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
Run as part of CI to catch regressions automatically.
|
|
208
|
+
|
|
209
|
+
## Reporting issues
|
|
210
|
+
|
|
211
|
+
### WordPress core
|
|
212
|
+
|
|
213
|
+
File accessibility bugs at https://core.trac.wordpress.org/ with the `accessibility` focus.
|
|
214
|
+
|
|
215
|
+
### Plugin/theme developers
|
|
216
|
+
|
|
217
|
+
Include:
|
|
218
|
+
1. WCAG criterion violated (e.g., "1.1.1 Non-text Content")
|
|
219
|
+
2. Steps to reproduce
|
|
220
|
+
3. Expected behavior
|
|
221
|
+
4. Screen reader / browser / OS used
|
|
222
|
+
5. Severity (who is blocked?)
|
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
# Block Accessibility
|
|
2
|
+
|
|
3
|
+
Use this file when making WordPress blocks accessible.
|
|
4
|
+
|
|
5
|
+
## Block markup requirements
|
|
6
|
+
|
|
7
|
+
### Semantic HTML
|
|
8
|
+
|
|
9
|
+
```js
|
|
10
|
+
// WRONG — generic divs
|
|
11
|
+
export default function Edit() {
|
|
12
|
+
return (
|
|
13
|
+
<div className="my-block">
|
|
14
|
+
<div className="title">Heading</div>
|
|
15
|
+
<div className="content">Paragraph text</div>
|
|
16
|
+
</div>
|
|
17
|
+
);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// CORRECT — semantic elements
|
|
21
|
+
export default function Edit({ attributes }) {
|
|
22
|
+
const { level } = attributes;
|
|
23
|
+
const TagName = `h${level}`;
|
|
24
|
+
return (
|
|
25
|
+
<section className="my-block">
|
|
26
|
+
<TagName>{attributes.title}</TagName>
|
|
27
|
+
<p>{attributes.content}</p>
|
|
28
|
+
</section>
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
### Heading hierarchy
|
|
34
|
+
|
|
35
|
+
Allow users to choose heading level:
|
|
36
|
+
|
|
37
|
+
```js
|
|
38
|
+
import { InspectorControls } from '@wordpress/block-editor';
|
|
39
|
+
import { PanelBody, SelectControl } from '@wordpress/components';
|
|
40
|
+
import { __ } from '@wordpress/i18n';
|
|
41
|
+
|
|
42
|
+
<InspectorControls>
|
|
43
|
+
<PanelBody title={__('Heading Settings')}>
|
|
44
|
+
<SelectControl
|
|
45
|
+
label={__('Heading Level')}
|
|
46
|
+
value={attributes.level}
|
|
47
|
+
options={[
|
|
48
|
+
{ label: 'H2', value: 2 },
|
|
49
|
+
{ label: 'H3', value: 3 },
|
|
50
|
+
{ label: 'H4', value: 4 },
|
|
51
|
+
]}
|
|
52
|
+
onChange={(level) => setAttributes({ level: Number(level) })}
|
|
53
|
+
/>
|
|
54
|
+
</PanelBody>
|
|
55
|
+
</InspectorControls>
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
## ARIA attributes in blocks
|
|
59
|
+
|
|
60
|
+
### Images with alt text
|
|
61
|
+
|
|
62
|
+
```js
|
|
63
|
+
import { MediaUpload, MediaUploadCheck } from '@wordpress/block-editor';
|
|
64
|
+
import { TextControl } from '@wordpress/components';
|
|
65
|
+
|
|
66
|
+
// Always include alt text control when a block has images
|
|
67
|
+
<TextControl
|
|
68
|
+
label={__('Alternative Text')}
|
|
69
|
+
help={__('Describe the image for screen reader users.')}
|
|
70
|
+
value={attributes.alt}
|
|
71
|
+
onChange={(alt) => setAttributes({ alt })}
|
|
72
|
+
/>
|
|
73
|
+
|
|
74
|
+
// In save:
|
|
75
|
+
<img src={attributes.url} alt={attributes.alt} />
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
### Interactive elements
|
|
79
|
+
|
|
80
|
+
```js
|
|
81
|
+
// Buttons must have accessible names
|
|
82
|
+
<button
|
|
83
|
+
className="my-block__toggle"
|
|
84
|
+
aria-expanded={isOpen}
|
|
85
|
+
aria-controls="panel-content"
|
|
86
|
+
onClick={() => setIsOpen(!isOpen)}
|
|
87
|
+
>
|
|
88
|
+
{__('Toggle Details')}
|
|
89
|
+
</button>
|
|
90
|
+
<div id="panel-content" role="region" hidden={!isOpen}>
|
|
91
|
+
{content}
|
|
92
|
+
</div>
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
### Live regions
|
|
96
|
+
|
|
97
|
+
For blocks with dynamic content updates:
|
|
98
|
+
|
|
99
|
+
```js
|
|
100
|
+
// Announce changes to screen readers
|
|
101
|
+
<div aria-live="polite" aria-atomic="true" className="screen-reader-text">
|
|
102
|
+
{statusMessage}
|
|
103
|
+
</div>
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
## Block supports for accessibility
|
|
107
|
+
|
|
108
|
+
### block.json supports
|
|
109
|
+
|
|
110
|
+
```json
|
|
111
|
+
{
|
|
112
|
+
"supports": {
|
|
113
|
+
"color": {
|
|
114
|
+
"text": true,
|
|
115
|
+
"background": true,
|
|
116
|
+
"link": true
|
|
117
|
+
},
|
|
118
|
+
"typography": {
|
|
119
|
+
"fontSize": true,
|
|
120
|
+
"lineHeight": true
|
|
121
|
+
},
|
|
122
|
+
"spacing": {
|
|
123
|
+
"margin": true,
|
|
124
|
+
"padding": true
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
These core supports are pre-tested for accessibility by the WordPress a11y team.
|
|
131
|
+
|
|
132
|
+
### Color contrast
|
|
133
|
+
|
|
134
|
+
When blocks allow color customization, warn about contrast:
|
|
135
|
+
|
|
136
|
+
```js
|
|
137
|
+
import { ContrastChecker } from '@wordpress/block-editor';
|
|
138
|
+
|
|
139
|
+
<ContrastChecker
|
|
140
|
+
textColor={attributes.textColor}
|
|
141
|
+
backgroundColor={attributes.backgroundColor}
|
|
142
|
+
fontSize={attributes.fontSize}
|
|
143
|
+
/>
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
`ContrastChecker` checks WCAG 2.1 AA contrast ratios (4.5:1 normal text, 3:1 large text).
|
|
147
|
+
|
|
148
|
+
## Focus management in the editor
|
|
149
|
+
|
|
150
|
+
### RichText
|
|
151
|
+
|
|
152
|
+
```js
|
|
153
|
+
import { RichText } from '@wordpress/block-editor';
|
|
154
|
+
|
|
155
|
+
// RichText handles focus and keyboard navigation automatically
|
|
156
|
+
<RichText
|
|
157
|
+
tagName="p"
|
|
158
|
+
value={attributes.content}
|
|
159
|
+
onChange={(content) => setAttributes({ content })}
|
|
160
|
+
placeholder={__('Write your content...')}
|
|
161
|
+
allowedFormats={['core/bold', 'core/italic', 'core/link']}
|
|
162
|
+
/>
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
### Custom interactive controls
|
|
166
|
+
|
|
167
|
+
```js
|
|
168
|
+
// Toolbar buttons with proper labels
|
|
169
|
+
import { BlockControls } from '@wordpress/block-editor';
|
|
170
|
+
import { ToolbarGroup, ToolbarButton } from '@wordpress/components';
|
|
171
|
+
|
|
172
|
+
<BlockControls>
|
|
173
|
+
<ToolbarGroup>
|
|
174
|
+
<ToolbarButton
|
|
175
|
+
icon="editor-alignleft"
|
|
176
|
+
title={__('Align Left')}
|
|
177
|
+
isActive={attributes.alignment === 'left'}
|
|
178
|
+
onClick={() => setAttributes({ alignment: 'left' })}
|
|
179
|
+
/>
|
|
180
|
+
</ToolbarGroup>
|
|
181
|
+
</BlockControls>
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
## Save output requirements
|
|
185
|
+
|
|
186
|
+
### Skip links and landmarks
|
|
187
|
+
|
|
188
|
+
For blocks that create page sections:
|
|
189
|
+
|
|
190
|
+
```php
|
|
191
|
+
// save.js
|
|
192
|
+
export default function save({ attributes }) {
|
|
193
|
+
return (
|
|
194
|
+
<nav aria-label={attributes.label || __('Navigation')}>
|
|
195
|
+
<ul>
|
|
196
|
+
{/* navigation items */}
|
|
197
|
+
</ul>
|
|
198
|
+
</nav>
|
|
199
|
+
);
|
|
200
|
+
}
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
### Tables
|
|
204
|
+
|
|
205
|
+
```php
|
|
206
|
+
export default function save({ attributes }) {
|
|
207
|
+
return (
|
|
208
|
+
<table>
|
|
209
|
+
<caption>{attributes.caption}</caption>
|
|
210
|
+
<thead>
|
|
211
|
+
<tr>
|
|
212
|
+
<th scope="col">{__('Name')}</th>
|
|
213
|
+
<th scope="col">{__('Value')}</th>
|
|
214
|
+
</tr>
|
|
215
|
+
</thead>
|
|
216
|
+
<tbody>
|
|
217
|
+
{attributes.rows.map((row, i) => (
|
|
218
|
+
<tr key={i}>
|
|
219
|
+
<th scope="row">{row.name}</th>
|
|
220
|
+
<td>{row.value}</td>
|
|
221
|
+
</tr>
|
|
222
|
+
))}
|
|
223
|
+
</tbody>
|
|
224
|
+
</table>
|
|
225
|
+
);
|
|
226
|
+
}
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
## Verification
|
|
230
|
+
|
|
231
|
+
```bash
|
|
232
|
+
# Run axe-core on a page containing the block
|
|
233
|
+
npx @axe-core/cli https://localhost:8888/test-page/
|
|
234
|
+
|
|
235
|
+
# Check block output for ARIA issues
|
|
236
|
+
curl -s https://site.com/test-page/ | grep -E "role=|aria-" | head -20
|
|
237
|
+
|
|
238
|
+
# Validate HTML output
|
|
239
|
+
curl -s https://site.com/test-page/ | npx html-validate --stdin
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
Manual testing:
|
|
243
|
+
1. Navigate the block using only keyboard (Tab, Enter, Escape, Arrow keys)
|
|
244
|
+
2. Test with a screen reader (NVDA on Windows, VoiceOver on macOS)
|
|
245
|
+
3. Verify heading hierarchy doesn't skip levels
|
|
246
|
+
4. Ensure all images have meaningful alt text
|
|
247
|
+
5. Confirm color contrast passes WCAG AA
|