claude-agent-framework 1.0.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/README.md +128 -0
- package/bin/claude-framework +3 -0
- package/framework/agents/design-lead.md +240 -0
- package/framework/agents/product-owner.md +179 -0
- package/framework/agents/tech-lead.md +226 -0
- package/framework/commands/ayuda.md +127 -0
- package/framework/commands/a/303/261adir.md +98 -0
- package/framework/commands/backup.md +397 -0
- package/framework/commands/cambiar.md +110 -0
- package/framework/commands/cloud.md +457 -0
- package/framework/commands/code.md +142 -0
- package/framework/commands/debug.md +334 -0
- package/framework/commands/deploy.md +383 -0
- package/framework/commands/deshacer.md +120 -0
- package/framework/commands/estado.md +218 -0
- package/framework/commands/explica.md +227 -0
- package/framework/commands/feature.md +120 -0
- package/framework/commands/git.md +427 -0
- package/framework/commands/historial.md +202 -0
- package/framework/commands/learn.md +408 -0
- package/framework/commands/movil.md +245 -0
- package/framework/commands/nuevo.md +118 -0
- package/framework/commands/plan.md +134 -0
- package/framework/commands/prd.md +113 -0
- package/framework/commands/probar.md +148 -0
- package/framework/commands/revisar.md +208 -0
- package/framework/commands/seeds.md +230 -0
- package/framework/commands/seguridad.md +226 -0
- package/framework/commands/tasks.md +157 -0
- package/framework/skills/architecture/algorithms.md +970 -0
- package/framework/skills/architecture/clean-code.md +1080 -0
- package/framework/skills/architecture/design-patterns.md +1984 -0
- package/framework/skills/architecture/functional-programming.md +972 -0
- package/framework/skills/architecture/solid.md +991 -0
- package/framework/skills/cloud/cloud-aws.md +848 -0
- package/framework/skills/cloud/cloud-azure.md +931 -0
- package/framework/skills/cloud/cloud-gcp.md +848 -0
- package/framework/skills/cloud/message-queues.md +1229 -0
- package/framework/skills/core/accessibility.md +401 -0
- package/framework/skills/core/api.md +474 -0
- package/framework/skills/core/authentication.md +306 -0
- package/framework/skills/core/authorization.md +388 -0
- package/framework/skills/core/background-jobs.md +341 -0
- package/framework/skills/core/caching.md +473 -0
- package/framework/skills/core/code-review.md +341 -0
- package/framework/skills/core/controllers.md +290 -0
- package/framework/skills/core/cua.md +285 -0
- package/framework/skills/core/documentation.md +472 -0
- package/framework/skills/core/file-uploads.md +351 -0
- package/framework/skills/core/hotwire-native.md +296 -0
- package/framework/skills/core/hotwire.md +278 -0
- package/framework/skills/core/i18n.md +334 -0
- package/framework/skills/core/imports-exports.md +750 -0
- package/framework/skills/core/infrastructure.md +337 -0
- package/framework/skills/core/models.md +228 -0
- package/framework/skills/core/notifications.md +672 -0
- package/framework/skills/core/payments.md +581 -0
- package/framework/skills/core/performance.md +361 -0
- package/framework/skills/core/rails-scaffold.md +131 -0
- package/framework/skills/core/search.md +518 -0
- package/framework/skills/core/security.md +565 -0
- package/framework/skills/core/seeds.md +307 -0
- package/framework/skills/core/seo.md +542 -0
- package/framework/skills/core/testing.md +393 -0
- package/framework/skills/core/views.md +260 -0
- package/framework/skills/core/websockets.md +564 -0
- package/framework/skills/data/advanced-sql.md +1204 -0
- package/framework/skills/data/nosql.md +1141 -0
- package/framework/skills/devops/containers-advanced.md +1237 -0
- package/framework/skills/devops/debugging.md +834 -0
- package/framework/skills/devops/git-workflow.md +752 -0
- package/framework/skills/devops/networking.md +932 -0
- package/framework/skills/devops/shell-scripting.md +1132 -0
- package/framework/sub-agents/architecture-patterns-agent.md +1450 -0
- package/framework/sub-agents/cloud-agent.md +677 -0
- package/framework/sub-agents/data.md +504 -0
- package/framework/sub-agents/debugging-agent.md +554 -0
- package/framework/sub-agents/devops.md +483 -0
- package/framework/sub-agents/docs.md +176 -0
- package/framework/sub-agents/frontend-dev.md +349 -0
- package/framework/sub-agents/git-workflow-agent.md +697 -0
- package/framework/sub-agents/integrations.md +630 -0
- package/framework/sub-agents/native-dev.md +434 -0
- package/framework/sub-agents/qa.md +138 -0
- package/framework/sub-agents/rails-dev.md +375 -0
- package/framework/sub-agents/security.md +526 -0
- package/framework/sub-agents/ui.md +437 -0
- package/framework/sub-agents/ux.md +284 -0
- package/framework/templates/api-spec.md +500 -0
- package/framework/templates/component-spec.md +248 -0
- package/framework/templates/feature.json +13 -0
- package/framework/templates/model-spec.md +318 -0
- package/framework/templates/prd-template.md +80 -0
- package/framework/templates/task-plan.md +122 -0
- package/framework/templates/task-user-story.md +52 -0
- package/framework/templates/technical-spec.md +260 -0
- package/framework/templates/user-story.md +95 -0
- package/package.json +42 -0
- package/project-templates/CLAUDE.md +42 -0
- package/project-templates/contexts/architecture.md +25 -0
- package/project-templates/contexts/conventions.md +46 -0
- package/project-templates/contexts/design-system.md +47 -0
- package/project-templates/contexts/requirements.md +38 -0
- package/project-templates/contexts/stack.md +30 -0
- package/project-templates/history/active/models.md +11 -0
- package/project-templates/history/changelog.md +15 -0
- package/project-templates/workspace/.gitkeep +0 -0
- package/src/cli.js +52 -0
- package/src/init.js +104 -0
- package/src/status.js +75 -0
- package/src/update.js +88 -0
|
@@ -0,0 +1,401 @@
|
|
|
1
|
+
# Skill: Accessibility
|
|
2
|
+
|
|
3
|
+
## Purpose
|
|
4
|
+
Ensure applications meet WCAG 2.1 AA standards and are usable by everyone.
|
|
5
|
+
|
|
6
|
+
## Core Principles (POUR)
|
|
7
|
+
|
|
8
|
+
1. **Perceivable** - Information must be presentable
|
|
9
|
+
2. **Operable** - Interface must be navigable
|
|
10
|
+
3. **Understandable** - Content must be readable
|
|
11
|
+
4. **Robust** - Compatible with assistive technologies
|
|
12
|
+
|
|
13
|
+
## Semantic HTML
|
|
14
|
+
|
|
15
|
+
### Document Structure
|
|
16
|
+
```erb
|
|
17
|
+
<!DOCTYPE html>
|
|
18
|
+
<html lang="<%= I18n.locale %>">
|
|
19
|
+
<head>
|
|
20
|
+
<meta charset="UTF-8">
|
|
21
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
22
|
+
<title><%= content_for(:title) || "App Name" %></title>
|
|
23
|
+
</head>
|
|
24
|
+
<body>
|
|
25
|
+
<a href="#main-content" class="sr-only focus:not-sr-only">
|
|
26
|
+
Skip to main content
|
|
27
|
+
</a>
|
|
28
|
+
|
|
29
|
+
<header role="banner">
|
|
30
|
+
<nav role="navigation" aria-label="Main navigation">
|
|
31
|
+
<!-- Navigation -->
|
|
32
|
+
</nav>
|
|
33
|
+
</header>
|
|
34
|
+
|
|
35
|
+
<main id="main-content" role="main">
|
|
36
|
+
<%= yield %>
|
|
37
|
+
</main>
|
|
38
|
+
|
|
39
|
+
<footer role="contentinfo">
|
|
40
|
+
<!-- Footer -->
|
|
41
|
+
</footer>
|
|
42
|
+
</body>
|
|
43
|
+
</html>
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
### Headings Hierarchy
|
|
47
|
+
```erb
|
|
48
|
+
<%# Correct hierarchy - never skip levels %>
|
|
49
|
+
<h1>Page Title</h1>
|
|
50
|
+
<h2>Section</h2>
|
|
51
|
+
<h3>Subsection</h3>
|
|
52
|
+
<h3>Subsection</h3>
|
|
53
|
+
<h2>Section</h2>
|
|
54
|
+
<h3>Subsection</h3>
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
### Landmarks
|
|
58
|
+
```erb
|
|
59
|
+
<header> <%# role="banner" implied %>
|
|
60
|
+
<nav> <%# role="navigation" implied %>
|
|
61
|
+
<main> <%# role="main" implied %>
|
|
62
|
+
<aside> <%# role="complementary" implied %>
|
|
63
|
+
<footer> <%# role="contentinfo" implied %>
|
|
64
|
+
<section> <%# role="region" when has accessible name %>
|
|
65
|
+
<article> <%# role="article" implied %>
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
## Forms
|
|
69
|
+
|
|
70
|
+
### Accessible Form
|
|
71
|
+
```erb
|
|
72
|
+
<%= form_with model: @user, class: "space-y-4" do |f| %>
|
|
73
|
+
<%# Error summary %>
|
|
74
|
+
<% if @user.errors.any? %>
|
|
75
|
+
<div role="alert" aria-labelledby="error-heading" class="bg-red-50 p-4 rounded">
|
|
76
|
+
<h2 id="error-heading" class="text-red-800 font-bold">
|
|
77
|
+
Please fix <%= pluralize(@user.errors.count, "error") %>:
|
|
78
|
+
</h2>
|
|
79
|
+
<ul class="list-disc list-inside text-red-700">
|
|
80
|
+
<% @user.errors.full_messages.each do |error| %>
|
|
81
|
+
<li><%= error %></li>
|
|
82
|
+
<% end %>
|
|
83
|
+
</ul>
|
|
84
|
+
</div>
|
|
85
|
+
<% end %>
|
|
86
|
+
|
|
87
|
+
<%# Text input with label %>
|
|
88
|
+
<div>
|
|
89
|
+
<%= f.label :name, class: "block font-medium" %>
|
|
90
|
+
<%= f.text_field :name,
|
|
91
|
+
required: true,
|
|
92
|
+
aria: { describedby: "name-hint" },
|
|
93
|
+
class: "mt-1 block w-full rounded border-gray-300" %>
|
|
94
|
+
<p id="name-hint" class="mt-1 text-sm text-gray-500">
|
|
95
|
+
Your full name as it appears on official documents.
|
|
96
|
+
</p>
|
|
97
|
+
</div>
|
|
98
|
+
|
|
99
|
+
<%# Email with error %>
|
|
100
|
+
<div>
|
|
101
|
+
<%= f.label :email, class: "block font-medium" %>
|
|
102
|
+
<%= f.email_field :email,
|
|
103
|
+
required: true,
|
|
104
|
+
aria: {
|
|
105
|
+
invalid: @user.errors[:email].any?,
|
|
106
|
+
describedby: @user.errors[:email].any? ? "email-error" : nil
|
|
107
|
+
},
|
|
108
|
+
class: "mt-1 block w-full rounded #{@user.errors[:email].any? ? 'border-red-500' : 'border-gray-300'}" %>
|
|
109
|
+
<% if @user.errors[:email].any? %>
|
|
110
|
+
<p id="email-error" class="mt-1 text-sm text-red-600" role="alert">
|
|
111
|
+
<%= @user.errors[:email].first %>
|
|
112
|
+
</p>
|
|
113
|
+
<% end %>
|
|
114
|
+
</div>
|
|
115
|
+
|
|
116
|
+
<%# Checkbox group %>
|
|
117
|
+
<fieldset>
|
|
118
|
+
<legend class="font-medium">Notification preferences</legend>
|
|
119
|
+
<div class="mt-2 space-y-2">
|
|
120
|
+
<div class="flex items-center gap-2">
|
|
121
|
+
<%= f.check_box :email_notifications, class: "rounded" %>
|
|
122
|
+
<%= f.label :email_notifications, "Email notifications" %>
|
|
123
|
+
</div>
|
|
124
|
+
<div class="flex items-center gap-2">
|
|
125
|
+
<%= f.check_box :sms_notifications, class: "rounded" %>
|
|
126
|
+
<%= f.label :sms_notifications, "SMS notifications" %>
|
|
127
|
+
</div>
|
|
128
|
+
</div>
|
|
129
|
+
</fieldset>
|
|
130
|
+
|
|
131
|
+
<%= f.submit "Save", class: "bg-blue-600 text-white px-4 py-2 rounded" %>
|
|
132
|
+
<% end %>
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
### Required Fields
|
|
136
|
+
```erb
|
|
137
|
+
<%# Visual indicator %>
|
|
138
|
+
<label for="email">
|
|
139
|
+
Email <span class="text-red-500" aria-hidden="true">*</span>
|
|
140
|
+
<span class="sr-only">(required)</span>
|
|
141
|
+
</label>
|
|
142
|
+
<input type="email" id="email" required aria-required="true">
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
## Images
|
|
146
|
+
|
|
147
|
+
### Informative Images
|
|
148
|
+
```erb
|
|
149
|
+
<%# Meaningful image - describe it %>
|
|
150
|
+
<%= image_tag "chart.png", alt: "Sales increased 25% from January to March 2024" %>
|
|
151
|
+
|
|
152
|
+
<%# Decorative image - empty alt %>
|
|
153
|
+
<%= image_tag "decoration.png", alt: "", role: "presentation" %>
|
|
154
|
+
|
|
155
|
+
<%# Complex image %>
|
|
156
|
+
<figure>
|
|
157
|
+
<%= image_tag "infographic.png", alt: "Company growth infographic", aria: { describedby: "infographic-desc" } %>
|
|
158
|
+
<figcaption id="infographic-desc">
|
|
159
|
+
Detailed description of the infographic...
|
|
160
|
+
</figcaption>
|
|
161
|
+
</figure>
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
### Icons
|
|
165
|
+
```erb
|
|
166
|
+
<%# Icon with text - hide icon %>
|
|
167
|
+
<button>
|
|
168
|
+
<svg aria-hidden="true"><!-- icon --></svg>
|
|
169
|
+
<span>Delete</span>
|
|
170
|
+
</button>
|
|
171
|
+
|
|
172
|
+
<%# Icon only - add label %>
|
|
173
|
+
<button aria-label="Delete item">
|
|
174
|
+
<svg aria-hidden="true"><!-- icon --></svg>
|
|
175
|
+
</button>
|
|
176
|
+
|
|
177
|
+
<%# Icon with visible text alternative %>
|
|
178
|
+
<button>
|
|
179
|
+
<svg aria-hidden="true"><!-- icon --></svg>
|
|
180
|
+
<span class="sr-only">Delete</span>
|
|
181
|
+
</button>
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
## Interactive Elements
|
|
185
|
+
|
|
186
|
+
### Buttons vs Links
|
|
187
|
+
```erb
|
|
188
|
+
<%# Link - navigates somewhere %>
|
|
189
|
+
<%= link_to "View article", article_path(@article) %>
|
|
190
|
+
|
|
191
|
+
<%# Button - performs action %>
|
|
192
|
+
<button type="button" onclick="toggleMenu()">Menu</button>
|
|
193
|
+
|
|
194
|
+
<%# Button that looks like link %>
|
|
195
|
+
<button type="button" class="text-blue-600 underline">
|
|
196
|
+
Show more
|
|
197
|
+
</button>
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
### Custom Controls
|
|
201
|
+
```erb
|
|
202
|
+
<%# Custom checkbox %>
|
|
203
|
+
<div role="checkbox"
|
|
204
|
+
aria-checked="false"
|
|
205
|
+
tabindex="0"
|
|
206
|
+
data-controller="checkbox"
|
|
207
|
+
data-action="click->checkbox#toggle keydown->checkbox#handleKey">
|
|
208
|
+
<!-- Custom styling -->
|
|
209
|
+
</div>
|
|
210
|
+
|
|
211
|
+
<%# Disclosure/accordion %>
|
|
212
|
+
<div data-controller="disclosure">
|
|
213
|
+
<button type="button"
|
|
214
|
+
aria-expanded="false"
|
|
215
|
+
aria-controls="panel-1"
|
|
216
|
+
data-action="disclosure#toggle">
|
|
217
|
+
Section Title
|
|
218
|
+
</button>
|
|
219
|
+
<div id="panel-1" hidden data-disclosure-target="panel">
|
|
220
|
+
Content here...
|
|
221
|
+
</div>
|
|
222
|
+
</div>
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
### Modal Dialog
|
|
226
|
+
```erb
|
|
227
|
+
<div role="dialog"
|
|
228
|
+
aria-modal="true"
|
|
229
|
+
aria-labelledby="modal-title"
|
|
230
|
+
data-controller="modal"
|
|
231
|
+
tabindex="-1">
|
|
232
|
+
<h2 id="modal-title">Confirm Action</h2>
|
|
233
|
+
<p>Are you sure you want to proceed?</p>
|
|
234
|
+
<button type="button" data-action="modal#close">Cancel</button>
|
|
235
|
+
<button type="button" data-action="modal#confirm">Confirm</button>
|
|
236
|
+
</div>
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
## Keyboard Navigation
|
|
240
|
+
|
|
241
|
+
### Focus Management
|
|
242
|
+
```javascript
|
|
243
|
+
// controllers/modal_controller.js
|
|
244
|
+
import { Controller } from "@hotwired/stimulus"
|
|
245
|
+
|
|
246
|
+
export default class extends Controller {
|
|
247
|
+
static targets = ["dialog", "firstFocusable"]
|
|
248
|
+
|
|
249
|
+
open() {
|
|
250
|
+
this.previousFocus = document.activeElement
|
|
251
|
+
this.dialogTarget.hidden = false
|
|
252
|
+
this.firstFocusableTarget.focus()
|
|
253
|
+
this.trapFocus()
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
close() {
|
|
257
|
+
this.dialogTarget.hidden = true
|
|
258
|
+
this.previousFocus?.focus()
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
trapFocus() {
|
|
262
|
+
const focusable = this.dialogTarget.querySelectorAll(
|
|
263
|
+
'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
|
|
264
|
+
)
|
|
265
|
+
const first = focusable[0]
|
|
266
|
+
const last = focusable[focusable.length - 1]
|
|
267
|
+
|
|
268
|
+
this.dialogTarget.addEventListener('keydown', (e) => {
|
|
269
|
+
if (e.key === 'Tab') {
|
|
270
|
+
if (e.shiftKey && document.activeElement === first) {
|
|
271
|
+
e.preventDefault()
|
|
272
|
+
last.focus()
|
|
273
|
+
} else if (!e.shiftKey && document.activeElement === last) {
|
|
274
|
+
e.preventDefault()
|
|
275
|
+
first.focus()
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
if (e.key === 'Escape') {
|
|
279
|
+
this.close()
|
|
280
|
+
}
|
|
281
|
+
})
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
### Skip Links
|
|
287
|
+
```erb
|
|
288
|
+
<body>
|
|
289
|
+
<a href="#main-content"
|
|
290
|
+
class="sr-only focus:not-sr-only focus:absolute focus:top-4 focus:left-4
|
|
291
|
+
bg-white p-2 rounded shadow">
|
|
292
|
+
Skip to main content
|
|
293
|
+
</a>
|
|
294
|
+
|
|
295
|
+
<a href="#navigation"
|
|
296
|
+
class="sr-only focus:not-sr-only focus:absolute focus:top-4 focus:left-40
|
|
297
|
+
bg-white p-2 rounded shadow">
|
|
298
|
+
Skip to navigation
|
|
299
|
+
</a>
|
|
300
|
+
</body>
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
## ARIA Live Regions
|
|
304
|
+
|
|
305
|
+
```erb
|
|
306
|
+
<%# Polite announcements %>
|
|
307
|
+
<div aria-live="polite" aria-atomic="true" class="sr-only">
|
|
308
|
+
<%= flash[:notice] %>
|
|
309
|
+
</div>
|
|
310
|
+
|
|
311
|
+
<%# Urgent announcements %>
|
|
312
|
+
<div role="alert" aria-live="assertive">
|
|
313
|
+
<%= flash[:alert] %>
|
|
314
|
+
</div>
|
|
315
|
+
|
|
316
|
+
<%# Status updates %>
|
|
317
|
+
<div role="status" aria-live="polite">
|
|
318
|
+
Loading... <%= @progress %>% complete
|
|
319
|
+
</div>
|
|
320
|
+
```
|
|
321
|
+
|
|
322
|
+
## Color and Contrast
|
|
323
|
+
|
|
324
|
+
### Tailwind Accessible Colors
|
|
325
|
+
```erb
|
|
326
|
+
<%# Good contrast ratios %>
|
|
327
|
+
<p class="text-gray-900 bg-white">High contrast (21:1)</p>
|
|
328
|
+
<p class="text-gray-700 bg-white">Good contrast (8.5:1)</p>
|
|
329
|
+
<p class="text-gray-600 bg-white">Minimum for large text (5.7:1)</p>
|
|
330
|
+
|
|
331
|
+
<%# Don't rely on color alone %>
|
|
332
|
+
<span class="text-red-600">
|
|
333
|
+
<svg aria-hidden="true"><!-- error icon --></svg>
|
|
334
|
+
Error: Invalid input
|
|
335
|
+
</span>
|
|
336
|
+
```
|
|
337
|
+
|
|
338
|
+
## Testing Accessibility
|
|
339
|
+
|
|
340
|
+
### Automated Testing
|
|
341
|
+
```ruby
|
|
342
|
+
# Gemfile
|
|
343
|
+
gem "axe-core-rspec"
|
|
344
|
+
|
|
345
|
+
# spec/system/accessibility_spec.rb
|
|
346
|
+
require "rails_helper"
|
|
347
|
+
|
|
348
|
+
RSpec.describe "Accessibility", type: :system do
|
|
349
|
+
it "homepage is accessible" do
|
|
350
|
+
visit root_path
|
|
351
|
+
expect(page).to be_axe_clean
|
|
352
|
+
end
|
|
353
|
+
|
|
354
|
+
it "article page is accessible" do
|
|
355
|
+
article = create(:article, :published)
|
|
356
|
+
visit article_path(article)
|
|
357
|
+
expect(page).to be_axe_clean
|
|
358
|
+
end
|
|
359
|
+
end
|
|
360
|
+
```
|
|
361
|
+
|
|
362
|
+
## Screen Reader Only Class
|
|
363
|
+
|
|
364
|
+
```css
|
|
365
|
+
/* Tailwind's sr-only */
|
|
366
|
+
.sr-only {
|
|
367
|
+
position: absolute;
|
|
368
|
+
width: 1px;
|
|
369
|
+
height: 1px;
|
|
370
|
+
padding: 0;
|
|
371
|
+
margin: -1px;
|
|
372
|
+
overflow: hidden;
|
|
373
|
+
clip: rect(0, 0, 0, 0);
|
|
374
|
+
white-space: nowrap;
|
|
375
|
+
border-width: 0;
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
.focus\:not-sr-only:focus {
|
|
379
|
+
position: static;
|
|
380
|
+
width: auto;
|
|
381
|
+
height: auto;
|
|
382
|
+
padding: inherit;
|
|
383
|
+
margin: inherit;
|
|
384
|
+
overflow: visible;
|
|
385
|
+
clip: auto;
|
|
386
|
+
white-space: normal;
|
|
387
|
+
}
|
|
388
|
+
```
|
|
389
|
+
|
|
390
|
+
## Checklist
|
|
391
|
+
|
|
392
|
+
- [ ] Page has single `<h1>`
|
|
393
|
+
- [ ] Heading hierarchy is correct
|
|
394
|
+
- [ ] All images have appropriate alt text
|
|
395
|
+
- [ ] Forms have labels and error messages
|
|
396
|
+
- [ ] Color contrast meets 4.5:1 minimum
|
|
397
|
+
- [ ] Interactive elements are keyboard accessible
|
|
398
|
+
- [ ] Focus is visible and logical
|
|
399
|
+
- [ ] Page works at 200% zoom
|
|
400
|
+
- [ ] No content relies on color alone
|
|
401
|
+
- [ ] Animations respect prefers-reduced-motion
|