@ericrisco/rsc 3.3.1 → 3.4.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 +2 -2
- package/manifest.json +43 -22
- package/package.json +1 -1
- package/scripts/lib/default-skill-floor.js +5 -1
- package/scripts/lib/domains.js +2 -2
- package/scripts/lib/retired-skills.js +4 -0
- package/skills/design/SKILL.md +4 -3
- package/skills/design/evals/cases.yaml +2 -2
- package/skills/react/SKILL.md +2 -1
- package/skills/ux/SKILL.md +149 -0
- package/skills/ux/THIRD_PARTY.md +32 -0
- package/skills/ux/evals/cases.yaml +40 -0
- package/skills/ux/references/flows.md +124 -0
- package/skills/ux/upstream/UPSTREAM.json +59 -0
- package/skills/ux/upstream/ux-designer/GUIDE.md +180 -0
- package/skills/ux/upstream/ux-designer/LICENSE +21 -0
- package/skills/ux/upstream/ux-designer/references/01-core-principles.md +251 -0
- package/skills/ux/upstream/ux-designer/references/02-laws-of-ux.md +330 -0
- package/skills/ux/upstream/ux-designer/references/03-accessibility.md +411 -0
- package/skills/ux/upstream/ux-designer/references/04-visual-design.md +433 -0
- package/skills/ux/upstream/ux-designer/references/05-information-architecture.md +432 -0
- package/skills/ux/upstream/ux-designer/references/06-interaction-design.md +495 -0
- package/skills/ux/upstream/ux-designer/references/07-forms-and-inputs.md +491 -0
- package/skills/ux/upstream/ux-designer/references/08-mobile-ux.md +539 -0
- package/skills/ux/upstream/ux-designer/references/09-ux-writing.md +493 -0
- package/skills/ux/upstream/ux-designer/references/10-user-research.md +533 -0
- package/skills/ux/upstream/ux-designer/references/11-design-systems.md +618 -0
- package/skills/ux/upstream/ux-designer/references/12a-presence-awareness.md +395 -0
- package/skills/ux/upstream/ux-designer/references/12b-conflict-resolution-sync.md +420 -0
- package/skills/ux/upstream/ux-designer/references/13a-canvas-navigation.md +388 -0
- package/skills/ux/upstream/ux-designer/references/13b-canvas-objects-performance.md +559 -0
- package/skills/ux/upstream/ux-designer/references/14-ai-ux-patterns.md +511 -0
- package/skills/ux/upstream/ux-designer/references/15-ethical-design.md +419 -0
- package/skills/ux/upstream/ux-designer/references/16-onboarding.md +430 -0
- package/skills/ux/upstream/ux-designer/references/17-notifications.md +461 -0
- package/skills/ux/upstream/ux-designer/references/18-data-visualization.md +453 -0
- package/skills/ux/upstream/ux-designer/references/19-search-ux.md +479 -0
- package/skills/ux/upstream/ux-designer/references/20-emotional-design.md +382 -0
- package/skills/ux/upstream/ux-designer/references/21-data-tables.md +629 -0
- package/skills/ux/upstream/ux-designer/references/22-performance-ux.md +626 -0
- package/skills/ux/upstream/ux-designer/references/23-internationalization.md +347 -0
- package/skills/ux/upstream/ux-designer/references/24-voice-and-multimodal.md +308 -0
- package/skills/ux/upstream/ux-designer/references/25a-barcode-camera-scanning.md +342 -0
- package/skills/ux/upstream/ux-designer/references/25b-barcode-scanner-workflows.md +412 -0
- package/skills/ux/upstream/ux-designer/references/25c-barcode-display.md +277 -0
- package/skills/ux/upstream/ux-usability-foundations/GUIDE.md +285 -0
- package/skills/ux/upstream/ux-usability-foundations/LICENSE +21 -0
- package/skills/ux/upstream/ux-usability-foundations/references/anti-patterns.md +267 -0
- package/skills/ux/upstream/ux-usability-foundations/references/checklists.md +130 -0
- package/skills/ux/upstream/ux-usability-foundations/references/decision-prompts.md +340 -0
- package/skills/ux/upstream/ux-usability-foundations/references/principle-cards.md +449 -0
- package/targets/clone-bootstrap.mjs +1 -1
- package/skills/ui-engineering/SKILL.md +0 -113
- package/skills/ui-engineering/evals/cases.yaml +0 -38
|
@@ -0,0 +1,491 @@
|
|
|
1
|
+
# Forms & Input Design
|
|
2
|
+
|
|
3
|
+
Forms are often where users complete their goals—signing up, checking out, submitting data. Good form design directly impacts conversion rates and user satisfaction.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Contents
|
|
8
|
+
|
|
9
|
+
- Form Design Principles
|
|
10
|
+
- Labels and Placeholders
|
|
11
|
+
- Input Types
|
|
12
|
+
- Validation
|
|
13
|
+
- Required Fields
|
|
14
|
+
- Multi-Step Forms
|
|
15
|
+
- Specific Field Patterns
|
|
16
|
+
- Form Layout
|
|
17
|
+
- Submit Buttons
|
|
18
|
+
- Accessibility Checklist
|
|
19
|
+
- Sources
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
## Form Design Principles
|
|
24
|
+
|
|
25
|
+
### Evidence
|
|
26
|
+
|
|
27
|
+
Baymard's checkout research repeatedly finds that the average checkout shows roughly twice as many fields as needed, and that "too long / complicated checkout" is a top-cited abandonment reason. Treat widely quoted single figures (e.g. "81% abandon forms", "multi-step raises completion 86%") as unsourced marketing numbers — measure your own funnel instead.
|
|
28
|
+
|
|
29
|
+
### Fundamental Principles
|
|
30
|
+
|
|
31
|
+
1. **Ask only what you need** - Every field is friction
|
|
32
|
+
2. **Group related fields** - Logical chunking
|
|
33
|
+
3. **One column is usually best** - Easier scanning
|
|
34
|
+
4. **Clear labels** - No placeholder-only labels
|
|
35
|
+
5. **Smart defaults** - Pre-fill when possible
|
|
36
|
+
6. **Progressive disclosure** - Show fields when relevant
|
|
37
|
+
|
|
38
|
+
---
|
|
39
|
+
|
|
40
|
+
## Labels and Placeholders
|
|
41
|
+
|
|
42
|
+
### Labels
|
|
43
|
+
|
|
44
|
+
```html
|
|
45
|
+
<!-- Always use labels (not just placeholders) -->
|
|
46
|
+
<div class="form-field">
|
|
47
|
+
<label for="email">Email address</label>
|
|
48
|
+
<input type="email" id="email" name="email" />
|
|
49
|
+
</div>
|
|
50
|
+
|
|
51
|
+
<!-- Label placement -->
|
|
52
|
+
<!-- Top-aligned: Best for long forms, mobile -->
|
|
53
|
+
<!-- Left-aligned: Good for short forms, scanning -->
|
|
54
|
+
<!-- Right-aligned: Avoid (harder to scan) -->
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
**Best Practices:**
|
|
58
|
+
- Position labels above inputs (top-aligned)
|
|
59
|
+
- Keep labels short and clear
|
|
60
|
+
- Don't end with colons (modern convention)
|
|
61
|
+
- Associate with `for` attribute
|
|
62
|
+
|
|
63
|
+
### Placeholders
|
|
64
|
+
|
|
65
|
+
```html
|
|
66
|
+
<!-- Use for hints, not labels -->
|
|
67
|
+
<input
|
|
68
|
+
type="email"
|
|
69
|
+
placeholder="you@example.com"
|
|
70
|
+
aria-label="Email address"
|
|
71
|
+
/>
|
|
72
|
+
|
|
73
|
+
<!-- Never use as the only label -->
|
|
74
|
+
<!-- Disappears on focus, causing confusion -->
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
**Placeholder Guidelines:**
|
|
78
|
+
- Show example format, not label
|
|
79
|
+
- Use gray text (but not too light)
|
|
80
|
+
- Don't rely on it for critical info
|
|
81
|
+
- Disappears—users may forget what field is
|
|
82
|
+
|
|
83
|
+
---
|
|
84
|
+
|
|
85
|
+
## Input Types
|
|
86
|
+
|
|
87
|
+
### HTML5 Input Types
|
|
88
|
+
|
|
89
|
+
```html
|
|
90
|
+
<!-- Use appropriate types for mobile keyboards and validation -->
|
|
91
|
+
<input type="text" /> <!-- Default -->
|
|
92
|
+
<input type="email" /> <!-- Email keyboard -->
|
|
93
|
+
<input type="tel" /> <!-- Phone keyboard -->
|
|
94
|
+
<input type="number" /> <!-- Numeric keyboard -->
|
|
95
|
+
<input type="url" /> <!-- URL keyboard -->
|
|
96
|
+
<input type="password" /> <!-- Hidden characters -->
|
|
97
|
+
<input type="search" /> <!-- Search field -->
|
|
98
|
+
<input type="date" /> <!-- Date picker -->
|
|
99
|
+
<input type="time" /> <!-- Time picker -->
|
|
100
|
+
<input type="datetime-local" /> <!-- DateTime picker -->
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
### Input Attributes
|
|
104
|
+
|
|
105
|
+
```html
|
|
106
|
+
<input
|
|
107
|
+
type="email"
|
|
108
|
+
name="email"
|
|
109
|
+
id="email"
|
|
110
|
+
required <!-- Validation -->
|
|
111
|
+
aria-required="true" <!-- Accessibility -->
|
|
112
|
+
autocomplete="email" <!-- Autofill hint -->
|
|
113
|
+
inputmode="email" <!-- Keyboard hint -->
|
|
114
|
+
pattern="[^@]+@[^@]+\.[^@]+" <!-- Custom validation -->
|
|
115
|
+
placeholder="you@example.com"
|
|
116
|
+
/>
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
### Autocomplete Values
|
|
120
|
+
|
|
121
|
+
```html
|
|
122
|
+
<!-- Enable browser autofill -->
|
|
123
|
+
<input autocomplete="given-name" /> <!-- First name -->
|
|
124
|
+
<input autocomplete="family-name" /> <!-- Last name -->
|
|
125
|
+
<input autocomplete="email" />
|
|
126
|
+
<input autocomplete="tel" />
|
|
127
|
+
<input autocomplete="street-address" />
|
|
128
|
+
<input autocomplete="postal-code" />
|
|
129
|
+
<input autocomplete="cc-number" /> <!-- Credit card -->
|
|
130
|
+
<input autocomplete="new-password" /> <!-- For password creation -->
|
|
131
|
+
<input autocomplete="current-password" /> <!-- For login -->
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
---
|
|
135
|
+
|
|
136
|
+
## Validation
|
|
137
|
+
|
|
138
|
+
### Validation Timing
|
|
139
|
+
|
|
140
|
+
| Timing | Pros | Cons | Best For |
|
|
141
|
+
|--------|------|------|----------|
|
|
142
|
+
| On blur | Immediate feedback | Can feel aggressive | Most fields |
|
|
143
|
+
| On submit | Low friction | Delayed error discovery | Short forms |
|
|
144
|
+
| On input | Real-time | Annoying while typing | Password strength |
|
|
145
|
+
| Hybrid | Balanced | Complex to implement | Professional forms |
|
|
146
|
+
|
|
147
|
+
### Recommended Approach
|
|
148
|
+
|
|
149
|
+
```javascript
|
|
150
|
+
// Validate on blur (when user leaves field)
|
|
151
|
+
// Don't validate while typing
|
|
152
|
+
// Re-validate on correction
|
|
153
|
+
|
|
154
|
+
input.addEventListener('blur', validate);
|
|
155
|
+
input.addEventListener('input', () => {
|
|
156
|
+
// Only clear errors while typing if previously invalid
|
|
157
|
+
if (hasError) validate();
|
|
158
|
+
});
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
### Error Messages
|
|
162
|
+
|
|
163
|
+
```html
|
|
164
|
+
<!-- Good: Specific, actionable -->
|
|
165
|
+
<span class="error">
|
|
166
|
+
Please enter a valid email address (e.g., name@example.com)
|
|
167
|
+
</span>
|
|
168
|
+
|
|
169
|
+
<!-- Bad: Vague, unhelpful -->
|
|
170
|
+
<span class="error">Invalid input</span>
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
**Error Message Guidelines:**
|
|
174
|
+
- Place near the field (below or inline)
|
|
175
|
+
- Use red color + icon (not color alone)
|
|
176
|
+
- Be specific about what's wrong
|
|
177
|
+
- Suggest how to fix
|
|
178
|
+
- Use human-friendly language
|
|
179
|
+
- Don't blame the user
|
|
180
|
+
|
|
181
|
+
### Visual Error Indicators
|
|
182
|
+
|
|
183
|
+
```css
|
|
184
|
+
.input-error {
|
|
185
|
+
border-color: #dc3545;
|
|
186
|
+
background-color: #fff8f8;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
.error-message {
|
|
190
|
+
color: #dc3545;
|
|
191
|
+
font-size: 0.875rem;
|
|
192
|
+
margin-top: 0.25rem;
|
|
193
|
+
display: flex;
|
|
194
|
+
align-items: center;
|
|
195
|
+
gap: 0.25rem;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
.error-message::before {
|
|
199
|
+
content: "⚠️";
|
|
200
|
+
}
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
### Accessibility for Errors
|
|
204
|
+
|
|
205
|
+
```html
|
|
206
|
+
<div class="form-field">
|
|
207
|
+
<label for="email">Email</label>
|
|
208
|
+
<input
|
|
209
|
+
type="email"
|
|
210
|
+
id="email"
|
|
211
|
+
aria-describedby="email-error"
|
|
212
|
+
aria-invalid="true"
|
|
213
|
+
/>
|
|
214
|
+
<span id="email-error" role="alert">
|
|
215
|
+
Please enter a valid email address
|
|
216
|
+
</span>
|
|
217
|
+
</div>
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
---
|
|
221
|
+
|
|
222
|
+
## Required Fields
|
|
223
|
+
|
|
224
|
+
### Marking Required Fields
|
|
225
|
+
|
|
226
|
+
```html
|
|
227
|
+
<!-- Visual indicator -->
|
|
228
|
+
<label for="name">
|
|
229
|
+
Name <span class="required" aria-hidden="true">*</span>
|
|
230
|
+
<span class="sr-only">(required)</span>
|
|
231
|
+
</label>
|
|
232
|
+
|
|
233
|
+
<!-- Alternative: Mark optional fields -->
|
|
234
|
+
<label for="phone">
|
|
235
|
+
Phone <span class="optional">(optional)</span>
|
|
236
|
+
</label>
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
**Convention:**
|
|
240
|
+
- If most fields required: mark optional
|
|
241
|
+
- If most fields optional: mark required
|
|
242
|
+
- Use asterisk (*) for required
|
|
243
|
+
- Explain asterisk at form start
|
|
244
|
+
|
|
245
|
+
---
|
|
246
|
+
|
|
247
|
+
## Multi-Step Forms
|
|
248
|
+
|
|
249
|
+
### When to Use
|
|
250
|
+
|
|
251
|
+
- Long forms (7+ fields)
|
|
252
|
+
- Complex processes (checkout, applications)
|
|
253
|
+
- Collecting different types of info
|
|
254
|
+
- Reducing cognitive load
|
|
255
|
+
|
|
256
|
+
### Progress Indicator
|
|
257
|
+
|
|
258
|
+
```html
|
|
259
|
+
<nav aria-label="Form progress">
|
|
260
|
+
<ol class="steps">
|
|
261
|
+
<li class="completed" aria-current="false">
|
|
262
|
+
<span class="step-number">1</span>
|
|
263
|
+
<span class="step-label">Personal Info</span>
|
|
264
|
+
</li>
|
|
265
|
+
<li class="current" aria-current="step">
|
|
266
|
+
<span class="step-number">2</span>
|
|
267
|
+
<span class="step-label">Shipping</span>
|
|
268
|
+
</li>
|
|
269
|
+
<li class="upcoming" aria-current="false">
|
|
270
|
+
<span class="step-number">3</span>
|
|
271
|
+
<span class="step-label">Payment</span>
|
|
272
|
+
</li>
|
|
273
|
+
</ol>
|
|
274
|
+
</nav>
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
### Best Practices
|
|
278
|
+
|
|
279
|
+
- Show step count (Step 2 of 4)
|
|
280
|
+
- Allow going back to previous steps
|
|
281
|
+
- Save progress automatically
|
|
282
|
+
- Validate each step before proceeding
|
|
283
|
+
- Keep navigation visible
|
|
284
|
+
- Allow skipping optional sections
|
|
285
|
+
|
|
286
|
+
---
|
|
287
|
+
|
|
288
|
+
## Specific Field Patterns
|
|
289
|
+
|
|
290
|
+
### Password Fields
|
|
291
|
+
|
|
292
|
+
```html
|
|
293
|
+
<div class="password-field">
|
|
294
|
+
<label for="password">Password</label>
|
|
295
|
+
<div class="input-wrapper">
|
|
296
|
+
<input
|
|
297
|
+
type="password"
|
|
298
|
+
id="password"
|
|
299
|
+
autocomplete="new-password"
|
|
300
|
+
minlength="8"
|
|
301
|
+
/>
|
|
302
|
+
<button type="button" class="toggle-visibility">
|
|
303
|
+
Show
|
|
304
|
+
</button>
|
|
305
|
+
</div>
|
|
306
|
+
<div class="password-strength" aria-live="polite">
|
|
307
|
+
<!-- Strength indicator -->
|
|
308
|
+
</div>
|
|
309
|
+
<ul class="password-requirements">
|
|
310
|
+
<li>At least 8 characters</li>
|
|
311
|
+
<li>One uppercase letter</li>
|
|
312
|
+
<li>One number</li>
|
|
313
|
+
</ul>
|
|
314
|
+
</div>
|
|
315
|
+
```
|
|
316
|
+
|
|
317
|
+
### Phone Numbers
|
|
318
|
+
|
|
319
|
+
```html
|
|
320
|
+
<input
|
|
321
|
+
type="tel"
|
|
322
|
+
inputmode="numeric"
|
|
323
|
+
autocomplete="tel"
|
|
324
|
+
placeholder="(555) 123-4567"
|
|
325
|
+
/>
|
|
326
|
+
|
|
327
|
+
<!-- Consider: -->
|
|
328
|
+
<!-- - Auto-format as user types -->
|
|
329
|
+
<!-- - Accept various formats -->
|
|
330
|
+
<!-- - Show expected format -->
|
|
331
|
+
```
|
|
332
|
+
|
|
333
|
+
### Credit Cards
|
|
334
|
+
|
|
335
|
+
```html
|
|
336
|
+
<input
|
|
337
|
+
type="text"
|
|
338
|
+
inputmode="numeric"
|
|
339
|
+
autocomplete="cc-number"
|
|
340
|
+
pattern="[\d ]{13,19}"
|
|
341
|
+
placeholder="1234 5678 9012 3456"
|
|
342
|
+
maxlength="19"
|
|
343
|
+
/>
|
|
344
|
+
|
|
345
|
+
<!-- Auto-format with spaces -->
|
|
346
|
+
<!-- Detect card type from number -->
|
|
347
|
+
<!-- Show card icon -->
|
|
348
|
+
```
|
|
349
|
+
|
|
350
|
+
### Addresses
|
|
351
|
+
|
|
352
|
+
```html
|
|
353
|
+
<input
|
|
354
|
+
type="text"
|
|
355
|
+
autocomplete="address-line1"
|
|
356
|
+
placeholder="Street address"
|
|
357
|
+
/>
|
|
358
|
+
|
|
359
|
+
<!-- Consider: -->
|
|
360
|
+
<!-- - Address autocomplete (Google Places) -->
|
|
361
|
+
<!-- - Country-specific formats -->
|
|
362
|
+
<!-- - Postal code lookup -->
|
|
363
|
+
```
|
|
364
|
+
|
|
365
|
+
### Date Selection
|
|
366
|
+
|
|
367
|
+
```html
|
|
368
|
+
<!-- Native date picker -->
|
|
369
|
+
<input type="date" />
|
|
370
|
+
|
|
371
|
+
<!-- Or custom for better UX -->
|
|
372
|
+
<input
|
|
373
|
+
type="text"
|
|
374
|
+
placeholder="MM/DD/YYYY"
|
|
375
|
+
inputmode="numeric"
|
|
376
|
+
/>
|
|
377
|
+
|
|
378
|
+
<!-- Consider: -->
|
|
379
|
+
<!-- - Relative dates ("Today", "Tomorrow") -->
|
|
380
|
+
<!-- - Date range pickers -->
|
|
381
|
+
<!-- - Mobile-friendly selectors -->
|
|
382
|
+
```
|
|
383
|
+
|
|
384
|
+
---
|
|
385
|
+
|
|
386
|
+
## Form Layout
|
|
387
|
+
|
|
388
|
+
### Single Column
|
|
389
|
+
|
|
390
|
+
```
|
|
391
|
+
┌─────────────────────────────┐
|
|
392
|
+
│ First Name │
|
|
393
|
+
├─────────────────────────────┤
|
|
394
|
+
│ Last Name │
|
|
395
|
+
├─────────────────────────────┤
|
|
396
|
+
│ Email │
|
|
397
|
+
├─────────────────────────────┤
|
|
398
|
+
│ [Submit] │
|
|
399
|
+
└─────────────────────────────┘
|
|
400
|
+
|
|
401
|
+
Best for: Mobile, most web forms
|
|
402
|
+
```
|
|
403
|
+
|
|
404
|
+
### Multi-Column (Use Sparingly)
|
|
405
|
+
|
|
406
|
+
```
|
|
407
|
+
┌──────────────┬──────────────┐
|
|
408
|
+
│ First Name │ Last Name │
|
|
409
|
+
├──────────────┴──────────────┤
|
|
410
|
+
│ Email │
|
|
411
|
+
├──────────────┬──────────────┤
|
|
412
|
+
│ City │ State │ Zip │
|
|
413
|
+
└──────────────┴──────────────┘
|
|
414
|
+
|
|
415
|
+
Use for: Related short fields
|
|
416
|
+
Avoid: Different topics side by side
|
|
417
|
+
```
|
|
418
|
+
|
|
419
|
+
### Grouping
|
|
420
|
+
|
|
421
|
+
```html
|
|
422
|
+
<fieldset>
|
|
423
|
+
<legend>Shipping Address</legend>
|
|
424
|
+
<!-- Address fields -->
|
|
425
|
+
</fieldset>
|
|
426
|
+
|
|
427
|
+
<fieldset>
|
|
428
|
+
<legend>Billing Address</legend>
|
|
429
|
+
<!-- Address fields -->
|
|
430
|
+
</fieldset>
|
|
431
|
+
```
|
|
432
|
+
|
|
433
|
+
---
|
|
434
|
+
|
|
435
|
+
## Submit Buttons
|
|
436
|
+
|
|
437
|
+
### Button Labels
|
|
438
|
+
|
|
439
|
+
```html
|
|
440
|
+
<!-- Specific, action-oriented -->
|
|
441
|
+
<button type="submit">Create Account</button>
|
|
442
|
+
<button type="submit">Place Order</button>
|
|
443
|
+
<button type="submit">Send Message</button>
|
|
444
|
+
|
|
445
|
+
<!-- Avoid generic -->
|
|
446
|
+
<button type="submit">Submit</button>
|
|
447
|
+
```
|
|
448
|
+
|
|
449
|
+
### Button States
|
|
450
|
+
|
|
451
|
+
```html
|
|
452
|
+
<button type="submit" disabled>
|
|
453
|
+
<!-- Before form is valid -->
|
|
454
|
+
Create Account
|
|
455
|
+
</button>
|
|
456
|
+
|
|
457
|
+
<button type="submit" class="loading">
|
|
458
|
+
<span class="spinner" aria-hidden="true"></span>
|
|
459
|
+
<span>Creating Account...</span>
|
|
460
|
+
</button>
|
|
461
|
+
```
|
|
462
|
+
|
|
463
|
+
### Button Placement
|
|
464
|
+
|
|
465
|
+
- Primary action on the right (or full-width on mobile)
|
|
466
|
+
- Cancel/back on the left
|
|
467
|
+
- Don't hide the submit button
|
|
468
|
+
- Ensure visible without scrolling
|
|
469
|
+
|
|
470
|
+
---
|
|
471
|
+
|
|
472
|
+
## Accessibility Checklist
|
|
473
|
+
|
|
474
|
+
- [ ] Labels properly associated with inputs
|
|
475
|
+
- [ ] Error messages linked with `aria-describedby`
|
|
476
|
+
- [ ] Required fields marked with `aria-required`
|
|
477
|
+
- [ ] Invalid fields marked with `aria-invalid`
|
|
478
|
+
- [ ] Errors announced with `role="alert"`
|
|
479
|
+
- [ ] Keyboard navigation works correctly
|
|
480
|
+
- [ ] Focus management on errors
|
|
481
|
+
- [ ] Sufficient color contrast
|
|
482
|
+
- [ ] Touch targets at least 44×44px
|
|
483
|
+
|
|
484
|
+
---
|
|
485
|
+
|
|
486
|
+
## Sources
|
|
487
|
+
|
|
488
|
+
- [Nielsen Norman Group - Form Design](https://www.nngroup.com/articles/web-form-design/)
|
|
489
|
+
- [Baymard Institute - Form Usability](https://baymard.com/blog/current-state-of-checkout-ux)
|
|
490
|
+
- [Luke Wroblewski - Web Form Design](https://www.lukew.com/resources/web_form_design.asp)
|
|
491
|
+
- [GOV.UK Design System - Forms](https://design-system.service.gov.uk/patterns/)
|