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.
Files changed (81) hide show
  1. package/.claude-plugin/plugin.json +7 -3
  2. package/CHANGELOG.md +111 -0
  3. package/README.md +10 -3
  4. package/agents/wp-accessibility-auditor.md +206 -0
  5. package/agents/wp-content-strategist.md +18 -0
  6. package/agents/wp-deployment-engineer.md +34 -2
  7. package/agents/wp-performance-optimizer.md +12 -0
  8. package/agents/wp-security-auditor.md +20 -0
  9. package/agents/wp-security-hardener.md +266 -0
  10. package/agents/wp-site-manager.md +14 -0
  11. package/agents/wp-test-engineer.md +207 -0
  12. package/docs/GUIDE.md +68 -15
  13. package/docs/guides/INDEX.md +46 -0
  14. package/docs/guides/wp-blog.md +590 -0
  15. package/docs/guides/wp-design-system.md +976 -0
  16. package/docs/guides/wp-ecommerce.md +786 -0
  17. package/docs/guides/wp-landing-page.md +762 -0
  18. package/docs/guides/wp-portfolio.md +713 -0
  19. package/docs/plans/2026-02-27-design-system-guide-design.md +30 -0
  20. package/docs/plans/2026-02-27-local-dev-tools-assessment.md +332 -0
  21. package/docs/plans/2026-02-27-local-env-design.md +179 -0
  22. package/docs/plans/2026-02-27-site-type-guides-design.md +44 -0
  23. package/package.json +7 -3
  24. package/skills/wordpress-router/SKILL.md +25 -5
  25. package/skills/wordpress-router/references/decision-tree.md +59 -3
  26. package/skills/wp-accessibility/SKILL.md +170 -0
  27. package/skills/wp-accessibility/references/a11y-audit-tools.md +248 -0
  28. package/skills/wp-accessibility/references/a11y-testing.md +222 -0
  29. package/skills/wp-accessibility/references/block-a11y.md +247 -0
  30. package/skills/wp-accessibility/references/interactive-a11y.md +272 -0
  31. package/skills/wp-accessibility/references/media-a11y.md +254 -0
  32. package/skills/wp-accessibility/references/theme-a11y.md +309 -0
  33. package/skills/wp-audit/SKILL.md +4 -0
  34. package/skills/wp-block-development/SKILL.md +5 -0
  35. package/skills/wp-block-themes/SKILL.md +4 -0
  36. package/skills/wp-deploy/SKILL.md +12 -0
  37. package/skills/wp-e2e-testing/SKILL.md +186 -0
  38. package/skills/wp-e2e-testing/references/ci-integration.md +174 -0
  39. package/skills/wp-e2e-testing/references/jest-wordpress.md +114 -0
  40. package/skills/wp-e2e-testing/references/phpunit-wordpress.md +141 -0
  41. package/skills/wp-e2e-testing/references/playwright-wordpress.md +108 -0
  42. package/skills/wp-e2e-testing/references/test-data-generation.md +127 -0
  43. package/skills/wp-e2e-testing/references/visual-regression.md +107 -0
  44. package/skills/wp-e2e-testing/references/wp-env-setup.md +97 -0
  45. package/skills/wp-e2e-testing/scripts/test_inspect.mjs +375 -0
  46. package/skills/wp-headless/SKILL.md +168 -0
  47. package/skills/wp-headless/references/api-layer-choice.md +160 -0
  48. package/skills/wp-headless/references/cors-config.md +245 -0
  49. package/skills/wp-headless/references/frontend-integration.md +331 -0
  50. package/skills/wp-headless/references/headless-auth.md +286 -0
  51. package/skills/wp-headless/references/webhooks.md +277 -0
  52. package/skills/wp-headless/references/wpgraphql.md +331 -0
  53. package/skills/wp-headless/scripts/headless_inspect.mjs +321 -0
  54. package/skills/wp-i18n/SKILL.md +170 -0
  55. package/skills/wp-i18n/references/js-i18n.md +201 -0
  56. package/skills/wp-i18n/references/multilingual-setup.md +219 -0
  57. package/skills/wp-i18n/references/php-i18n.md +196 -0
  58. package/skills/wp-i18n/references/rtl-support.md +206 -0
  59. package/skills/wp-i18n/references/translation-workflow.md +178 -0
  60. package/skills/wp-i18n/references/wpcli-i18n.md +177 -0
  61. package/skills/wp-i18n/scripts/i18n_inspect.mjs +330 -0
  62. package/skills/wp-interactivity-api/SKILL.md +4 -0
  63. package/skills/wp-local-env/SKILL.md +233 -0
  64. package/skills/wp-local-env/references/localwp-adapter.md +156 -0
  65. package/skills/wp-local-env/references/mcp-adapter-setup.md +153 -0
  66. package/skills/wp-local-env/references/studio-adapter.md +127 -0
  67. package/skills/wp-local-env/references/wpenv-adapter.md +121 -0
  68. package/skills/wp-local-env/scripts/detect_local_env.mjs +404 -0
  69. package/skills/wp-playground/SKILL.md +13 -1
  70. package/skills/wp-plugin-development/SKILL.md +6 -0
  71. package/skills/wp-rest-api/SKILL.md +4 -0
  72. package/skills/wp-security/SKILL.md +179 -0
  73. package/skills/wp-security/references/api-restriction.md +147 -0
  74. package/skills/wp-security/references/authentication-hardening.md +105 -0
  75. package/skills/wp-security/references/filesystem-hardening.md +105 -0
  76. package/skills/wp-security/references/http-headers.md +105 -0
  77. package/skills/wp-security/references/incident-response.md +144 -0
  78. package/skills/wp-security/references/user-capabilities.md +115 -0
  79. package/skills/wp-security/references/wp-config-security.md +129 -0
  80. package/skills/wp-security/scripts/security_inspect.mjs +393 -0
  81. package/skills/wp-wpcli-and-ops/SKILL.md +6 -0
@@ -0,0 +1,272 @@
1
+ # Interactive Component Accessibility
2
+
3
+ Use this file when building accessible interactive components (modals, tabs, accordions, tooltips) in WordPress.
4
+
5
+ ## Modal / Dialog
6
+
7
+ Based on [ARIA Authoring Practices Guide (APG) Dialog pattern](https://www.w3.org/WAI/ARIA/apg/patterns/dialog-modal/).
8
+
9
+ ```html
10
+ <div role="dialog" aria-modal="true"
11
+ aria-labelledby="dialog-title"
12
+ aria-describedby="dialog-desc">
13
+ <h2 id="dialog-title">Confirm Action</h2>
14
+ <p id="dialog-desc">Are you sure you want to proceed?</p>
15
+ <button>Confirm</button>
16
+ <button>Cancel</button>
17
+ </div>
18
+ ```
19
+
20
+ ### Keyboard behavior
21
+
22
+ | Key | Action |
23
+ |-----|--------|
24
+ | Tab | Move focus to next focusable element inside dialog |
25
+ | Shift+Tab | Move focus to previous focusable element |
26
+ | Escape | Close dialog |
27
+
28
+ ### Focus management
29
+
30
+ ```js
31
+ class AccessibleModal {
32
+ open(trigger) {
33
+ this.trigger = trigger;
34
+ this.dialog.hidden = false;
35
+ this.dialog.setAttribute('aria-modal', 'true');
36
+
37
+ // Trap focus inside dialog
38
+ this.firstFocusable = this.dialog.querySelector(
39
+ 'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
40
+ );
41
+ this.firstFocusable?.focus();
42
+
43
+ // Prevent background scroll
44
+ document.body.style.overflow = 'hidden';
45
+ }
46
+
47
+ close() {
48
+ this.dialog.hidden = true;
49
+ document.body.style.overflow = '';
50
+
51
+ // Return focus to trigger element
52
+ this.trigger?.focus();
53
+ }
54
+
55
+ trapFocus(e) {
56
+ if (e.key !== 'Tab') return;
57
+ const focusable = this.dialog.querySelectorAll(
58
+ 'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
59
+ );
60
+ const first = focusable[0];
61
+ const last = focusable[focusable.length - 1];
62
+
63
+ if (e.shiftKey && document.activeElement === first) {
64
+ e.preventDefault();
65
+ last.focus();
66
+ } else if (!e.shiftKey && document.activeElement === last) {
67
+ e.preventDefault();
68
+ first.focus();
69
+ }
70
+ }
71
+ }
72
+ ```
73
+
74
+ ## Tabs
75
+
76
+ Based on [APG Tabs pattern](https://www.w3.org/WAI/ARIA/apg/patterns/tabs/).
77
+
78
+ ```html
79
+ <div class="tabs">
80
+ <div role="tablist" aria-label="Settings">
81
+ <button role="tab" id="tab-1" aria-selected="true"
82
+ aria-controls="panel-1" tabindex="0">
83
+ General
84
+ </button>
85
+ <button role="tab" id="tab-2" aria-selected="false"
86
+ aria-controls="panel-2" tabindex="-1">
87
+ Advanced
88
+ </button>
89
+ </div>
90
+ <div role="tabpanel" id="panel-1" aria-labelledby="tab-1" tabindex="0">
91
+ General settings content
92
+ </div>
93
+ <div role="tabpanel" id="panel-2" aria-labelledby="tab-2" tabindex="0"
94
+ hidden>
95
+ Advanced settings content
96
+ </div>
97
+ </div>
98
+ ```
99
+
100
+ ### Keyboard behavior
101
+
102
+ | Key | Action |
103
+ |-----|--------|
104
+ | Arrow Right | Activate next tab |
105
+ | Arrow Left | Activate previous tab |
106
+ | Home | Activate first tab |
107
+ | End | Activate last tab |
108
+ | Tab | Move focus into the active tab panel |
109
+
110
+ ```js
111
+ tablist.addEventListener('keydown', (e) => {
112
+ const tabs = [...tablist.querySelectorAll('[role="tab"]')];
113
+ const index = tabs.indexOf(document.activeElement);
114
+
115
+ let newIndex;
116
+ switch (e.key) {
117
+ case 'ArrowRight':
118
+ newIndex = (index + 1) % tabs.length;
119
+ break;
120
+ case 'ArrowLeft':
121
+ newIndex = (index - 1 + tabs.length) % tabs.length;
122
+ break;
123
+ case 'Home':
124
+ newIndex = 0;
125
+ break;
126
+ case 'End':
127
+ newIndex = tabs.length - 1;
128
+ break;
129
+ default:
130
+ return;
131
+ }
132
+ e.preventDefault();
133
+ activateTab(tabs[newIndex]);
134
+ });
135
+
136
+ function activateTab(tab) {
137
+ // Deactivate all
138
+ tablist.querySelectorAll('[role="tab"]').forEach((t) => {
139
+ t.setAttribute('aria-selected', 'false');
140
+ t.setAttribute('tabindex', '-1');
141
+ document.getElementById(t.getAttribute('aria-controls')).hidden = true;
142
+ });
143
+ // Activate selected
144
+ tab.setAttribute('aria-selected', 'true');
145
+ tab.setAttribute('tabindex', '0');
146
+ tab.focus();
147
+ document.getElementById(tab.getAttribute('aria-controls')).hidden = false;
148
+ }
149
+ ```
150
+
151
+ ## Accordion
152
+
153
+ Based on [APG Accordion pattern](https://www.w3.org/WAI/ARIA/apg/patterns/accordion/).
154
+
155
+ ```html
156
+ <div class="accordion">
157
+ <h3>
158
+ <button aria-expanded="true" aria-controls="sect1"
159
+ id="accordion1-header">
160
+ Section 1
161
+ </button>
162
+ </h3>
163
+ <div id="sect1" role="region" aria-labelledby="accordion1-header">
164
+ <p>Section 1 content</p>
165
+ </div>
166
+
167
+ <h3>
168
+ <button aria-expanded="false" aria-controls="sect2"
169
+ id="accordion2-header">
170
+ Section 2
171
+ </button>
172
+ </h3>
173
+ <div id="sect2" role="region" aria-labelledby="accordion2-header"
174
+ hidden>
175
+ <p>Section 2 content</p>
176
+ </div>
177
+ </div>
178
+ ```
179
+
180
+ ### Keyboard behavior
181
+
182
+ | Key | Action |
183
+ |-----|--------|
184
+ | Enter / Space | Toggle section |
185
+ | Arrow Down | Next header |
186
+ | Arrow Up | Previous header |
187
+ | Home | First header |
188
+ | End | Last header |
189
+
190
+ ## Tooltip
191
+
192
+ ```html
193
+ <button aria-describedby="tooltip-1">
194
+ Settings
195
+ </button>
196
+ <div role="tooltip" id="tooltip-1" class="tooltip" hidden>
197
+ Configure application settings
198
+ </div>
199
+ ```
200
+
201
+ ### Behavior
202
+
203
+ - Show on hover and focus
204
+ - Hide on Escape
205
+ - Do not use for essential information (tooltips are supplementary)
206
+ - Keep content brief (one sentence max)
207
+
208
+ ```js
209
+ const trigger = document.querySelector('[aria-describedby]');
210
+ const tooltip = document.getElementById('tooltip-1');
211
+
212
+ trigger.addEventListener('mouseenter', () => tooltip.hidden = false);
213
+ trigger.addEventListener('mouseleave', () => tooltip.hidden = true);
214
+ trigger.addEventListener('focus', () => tooltip.hidden = false);
215
+ trigger.addEventListener('blur', () => tooltip.hidden = true);
216
+ trigger.addEventListener('keydown', (e) => {
217
+ if (e.key === 'Escape') tooltip.hidden = true;
218
+ });
219
+ ```
220
+
221
+ ## Disclosure (show/hide)
222
+
223
+ ```html
224
+ <button aria-expanded="false" aria-controls="details-content">
225
+ Show Details
226
+ </button>
227
+ <div id="details-content" hidden>
228
+ <p>Additional details here.</p>
229
+ </div>
230
+ ```
231
+
232
+ ```js
233
+ button.addEventListener('click', () => {
234
+ const expanded = button.getAttribute('aria-expanded') === 'true';
235
+ button.setAttribute('aria-expanded', String(!expanded));
236
+ content.hidden = expanded;
237
+ button.textContent = expanded ? 'Show Details' : 'Hide Details';
238
+ });
239
+ ```
240
+
241
+ ## WordPress @wordpress/components
242
+
243
+ WordPress core components are pre-built with accessibility:
244
+
245
+ ```js
246
+ import { Modal, TabPanel, Notice } from '@wordpress/components';
247
+
248
+ // Modal — handles focus trap, Escape, aria-modal automatically
249
+ <Modal title="Settings" onRequestClose={closeModal}>
250
+ <p>Modal content</p>
251
+ </Modal>
252
+
253
+ // TabPanel — handles arrow keys, aria-selected automatically
254
+ <TabPanel
255
+ tabs={[
256
+ { name: 'general', title: 'General' },
257
+ { name: 'advanced', title: 'Advanced' },
258
+ ]}
259
+ >
260
+ {(tab) => <p>{tab.name} content</p>}
261
+ </TabPanel>
262
+ ```
263
+
264
+ Prefer `@wordpress/components` over custom implementations when building for the block editor.
265
+
266
+ ## Verification
267
+
268
+ 1. Keyboard-only navigation: can you operate the component without a mouse?
269
+ 2. Screen reader: does it announce state changes (expanded/collapsed, selected tab)?
270
+ 3. Focus management: does focus move to the right place on open/close?
271
+ 4. Escape key: does it close overlays and tooltips?
272
+ 5. No focus traps: can you escape from every component?
@@ -0,0 +1,254 @@
1
+ # Media Accessibility
2
+
3
+ Use this file when making images, video, audio, and embedded media accessible in WordPress.
4
+
5
+ ## Images
6
+
7
+ ### Alt text decision tree
8
+
9
+ 1. **Is the image decorative?** → `alt=""`
10
+ 2. **Does it contain text?** → alt = the text in the image
11
+ 3. **Is it a link/button?** → alt = the link destination or action
12
+ 4. **Does it convey information?** → alt = describe the information
13
+ 5. **Is it complex (chart/graph)?** → alt = brief summary + detailed description nearby
14
+
15
+ ### WordPress implementation
16
+
17
+ ```php
18
+ // Featured image with alt text
19
+ if (has_post_thumbnail()) {
20
+ $alt = get_post_meta(
21
+ get_post_thumbnail_id(), '_wp_attachment_image_alt', true
22
+ );
23
+ the_post_thumbnail('large', ['alt' => $alt ?: get_the_title()]);
24
+ }
25
+
26
+ // Custom image output
27
+ $image_id = get_field('hero_image'); // ACF example
28
+ $alt = get_post_meta($image_id, '_wp_attachment_image_alt', true);
29
+ echo wp_get_attachment_image($image_id, 'full', false, [
30
+ 'alt' => $alt,
31
+ 'loading' => 'lazy',
32
+ ]);
33
+ ```
34
+
35
+ ### Decorative images
36
+
37
+ ```html
38
+ <!-- Decorative: empty alt, presentational role -->
39
+ <img src="divider.svg" alt="" role="presentation">
40
+
41
+ <!-- CSS background for purely decorative -->
42
+ <div class="decorative-bg" aria-hidden="true"></div>
43
+ ```
44
+
45
+ ### Complex images
46
+
47
+ ```html
48
+ <figure>
49
+ <img src="chart.png"
50
+ alt="Q4 revenue chart showing 25% growth"
51
+ aria-describedby="chart-details">
52
+ <figcaption id="chart-details">
53
+ Revenue grew from $2M in Q3 to $2.5M in Q4, driven primarily
54
+ by the European market which accounted for 60% of new sales.
55
+ </figcaption>
56
+ </figure>
57
+ ```
58
+
59
+ ### SVG accessibility
60
+
61
+ ```html
62
+ <!-- Informative SVG -->
63
+ <svg role="img" aria-labelledby="svg-title svg-desc">
64
+ <title id="svg-title">Company Logo</title>
65
+ <desc id="svg-desc">Green shield with leaf motif</desc>
66
+ <!-- svg paths -->
67
+ </svg>
68
+
69
+ <!-- Decorative SVG -->
70
+ <svg aria-hidden="true" focusable="false">
71
+ <!-- svg paths -->
72
+ </svg>
73
+ ```
74
+
75
+ ## Video
76
+
77
+ ### WordPress video block
78
+
79
+ The core Video block supports:
80
+ - Captions (`.vtt` files)
81
+ - Poster image (alt text via the poster)
82
+ - Playback controls (native browser controls are accessible)
83
+
84
+ ### Captions and subtitles
85
+
86
+ ```html
87
+ <video controls>
88
+ <source src="video.mp4" type="video/mp4">
89
+ <track kind="captions" src="captions-en.vtt"
90
+ srclang="en" label="English" default>
91
+ <track kind="captions" src="captions-it.vtt"
92
+ srclang="it" label="Italiano">
93
+ <track kind="descriptions" src="descriptions-en.vtt"
94
+ srclang="en" label="English audio descriptions">
95
+ </video>
96
+ ```
97
+
98
+ ### WebVTT format
99
+
100
+ ```vtt
101
+ WEBVTT
102
+
103
+ 00:00:01.000 --> 00:00:04.000
104
+ Welcome to our product demonstration.
105
+
106
+ 00:00:04.500 --> 00:00:08.000
107
+ Today we'll show you the key features
108
+ of our new application.
109
+
110
+ 00:00:08.500 --> 00:00:12.000
111
+ [Music playing in background]
112
+ ```
113
+
114
+ ### WCAG video requirements
115
+
116
+ | Level | Requirement |
117
+ |-------|------------|
118
+ | A | Captions for prerecorded video |
119
+ | A | Audio description or media alternative for prerecorded video |
120
+ | AA | Captions for live video |
121
+ | AA | Audio descriptions for prerecorded video |
122
+ | AAA | Sign language interpretation |
123
+ | AAA | Extended audio descriptions |
124
+
125
+ ### Autoplay restrictions
126
+
127
+ ```html
128
+ <!-- NEVER autoplay with sound -->
129
+ <video autoplay muted playsinline>
130
+ <!-- Muted autoplay is acceptable for decorative/background video -->
131
+ </video>
132
+
133
+ <!-- Provide pause control for autoplay -->
134
+ <div class="video-wrapper">
135
+ <video autoplay muted loop id="bg-video">
136
+ <source src="bg.mp4" type="video/mp4">
137
+ </video>
138
+ <button aria-label="Pause background video"
139
+ onclick="toggleVideo()">
140
+ Pause
141
+ </button>
142
+ </div>
143
+ ```
144
+
145
+ ## Audio
146
+
147
+ ```html
148
+ <audio controls>
149
+ <source src="podcast.mp3" type="audio/mpeg">
150
+ <a href="podcast.mp3">Download podcast episode</a>
151
+ </audio>
152
+
153
+ <!-- Provide transcript for audio content -->
154
+ <details>
155
+ <summary>Read transcript</summary>
156
+ <div class="transcript">
157
+ <p><strong>Host:</strong> Welcome to the show...</p>
158
+ </div>
159
+ </details>
160
+ ```
161
+
162
+ ### WCAG audio requirements
163
+
164
+ | Level | Requirement |
165
+ |-------|------------|
166
+ | A | Transcript for prerecorded audio-only content |
167
+ | AAA | Sign language for prerecorded audio in video |
168
+
169
+ ## Embedded media (iframes)
170
+
171
+ ```html
172
+ <!-- YouTube/Vimeo embeds need titles -->
173
+ <iframe src="https://www.youtube.com/embed/VIDEO_ID"
174
+ title="Product demonstration video"
175
+ allowfullscreen>
176
+ </iframe>
177
+
178
+ <!-- Maps -->
179
+ <iframe src="https://www.google.com/maps/embed?..."
180
+ title="Store location map - 123 Main Street, Rome"
181
+ allowfullscreen>
182
+ </iframe>
183
+ ```
184
+
185
+ ### WordPress oEmbed
186
+
187
+ WordPress auto-embeds URLs. Add accessible wrappers:
188
+
189
+ ```php
190
+ add_filter('embed_oembed_html', function($html, $url, $attr) {
191
+ // Add responsive wrapper with accessible title
192
+ return sprintf(
193
+ '<div class="responsive-embed" role="group" aria-label="%s">%s</div>',
194
+ esc_attr__('Embedded media', 'my-theme'),
195
+ $html
196
+ );
197
+ }, 10, 3);
198
+ ```
199
+
200
+ ## Animations and motion
201
+
202
+ ```css
203
+ /* Respect prefers-reduced-motion */
204
+ @media (prefers-reduced-motion: reduce) {
205
+ *,
206
+ *::before,
207
+ *::after {
208
+ animation-duration: 0.01ms !important;
209
+ animation-iteration-count: 1 !important;
210
+ transition-duration: 0.01ms !important;
211
+ scroll-behavior: auto !important;
212
+ }
213
+
214
+ video, .animated-element {
215
+ animation: none !important;
216
+ }
217
+ }
218
+ ```
219
+
220
+ ```js
221
+ // Check user preference in JavaScript
222
+ const prefersReducedMotion = window.matchMedia(
223
+ '(prefers-reduced-motion: reduce)'
224
+ ).matches;
225
+
226
+ if (!prefersReducedMotion) {
227
+ // Run animations
228
+ }
229
+ ```
230
+
231
+ WCAG requirements:
232
+ - Content that moves, blinks, or scrolls for more than 5 seconds must have a pause mechanism
233
+ - No content flashes more than 3 times per second
234
+
235
+ ## Verification
236
+
237
+ ```bash
238
+ # Check for images without alt text
239
+ curl -s https://site.com/ | grep -oP '<img[^>]*>' | grep -v 'alt='
240
+
241
+ # Check for iframes without titles
242
+ curl -s https://site.com/ | grep -oP '<iframe[^>]*>' | grep -v 'title='
243
+
244
+ # Check for video without captions
245
+ curl -s https://site.com/ | grep -oP '<video[^>]*>.*?</video>' | grep -v '<track'
246
+ ```
247
+
248
+ Manual checks:
249
+ 1. All informative images have descriptive alt text
250
+ 2. Decorative images have `alt=""`
251
+ 3. Videos have captions
252
+ 4. Audio has transcripts
253
+ 5. No autoplay media with sound
254
+ 6. Animations respect `prefers-reduced-motion`