@fpkit/acss 3.7.0 → 3.9.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/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@fpkit/acss",
3
3
  "description": "A lightweight React UI library for building modern and accessible components that leverage CSS custom properties for reactive Styles.",
4
4
  "private": false,
5
- "version": "3.7.0",
5
+ "version": "3.9.0",
6
6
  "engines": {
7
7
  "node": ">=22.12.0",
8
8
  "npm": ">=8.0.0"
@@ -126,5 +126,5 @@
126
126
  "publishConfig": {
127
127
  "access": "public"
128
128
  },
129
- "gitHead": "1049811ad793c7a3b0bb37c3d7abdbf171e0a9eb"
129
+ "gitHead": "87b188279548219c8d93538b58504077aef71a71"
130
130
  }
@@ -1,22 +1,27 @@
1
- import { Meta } from "@storybook/addon-docs/blocks";
2
-
3
1
  <Meta title="FP.REACT Forms/Form/Readme" />
4
2
 
5
3
  # Form Components
6
4
 
7
- A comprehensive set of accessible React form components built with TypeScript, designed for building robust, WCAG 2.1 AA compliant forms with proper validation, error handling, and status management.
5
+ A comprehensive set of accessible React form components built with TypeScript,
6
+ designed for building robust, WCAG 2.1 AA compliant forms with proper
7
+ validation, error handling, and status management.
8
8
 
9
- > 💡 **Interactive Examples**: See the [Form stories](./?path=/docs/fp-react-forms-form--docs) for live, interactive examples with automated accessibility testing.
9
+ > 💡 **Interactive Examples**: See the
10
+ > [Form stories](./?path=/docs/fp-react-forms-form--docs) for live, interactive
11
+ > examples with automated accessibility testing.
10
12
 
11
13
  ## Features
12
14
 
13
- - ✅ **WCAG 2.1 AA Compliant** - Full accessibility support with proper ARIA attributes
14
- - ✅ **Compound Component Pattern** - Intuitive API with Form.Field, Form.Input, etc.
15
+ - ✅ **WCAG 2.1 AA Compliant** - Full accessibility support with proper ARIA
16
+ attributes
17
+ - ✅ **Compound Component Pattern** - Intuitive API with Form.Field, Form.Input,
18
+ etc.
15
19
  - ✅ **TypeScript First** - Full type safety with comprehensive interfaces
16
20
  - ✅ **Status Management** - Built-in loading states and submission tracking
17
21
  - ✅ **Validation Support** - Client-side and server-side validation patterns
18
22
  - ✅ **Flexible** - Supports both controlled and uncontrolled form patterns
19
- - ✅ **Keyboard Navigation** - Full keyboard accessibility including Enter key handlers
23
+ - ✅ **Keyboard Navigation** - Full keyboard accessibility including Enter key
24
+ handlers
20
25
 
21
26
  ---
22
27
 
@@ -24,22 +29,22 @@ A comprehensive set of accessible React form components built with TypeScript, d
24
29
 
25
30
  ### Core Components
26
31
 
27
- | Component | Purpose | Key Props |
28
- |-----------|---------|-----------|
29
- | **Form** | Form wrapper with submission handling | `onSubmit`, `status`, `noValidate` |
30
- | **Form.Field** | Label + input wrapper for accessibility | `label`, `labelFor`, `required`, `optional` |
31
- | **Form.Input** | Text input with validation | `type`, `validationState`, `onEnter` |
32
- | **Form.Textarea** | Multi-line text input | `rows`, `cols`, `onEnter` |
33
- | **Form.Select** | Dropdown select with options | `onSelectionChange`, `onEnter` |
32
+ | Component | Purpose | Key Props |
33
+ | ----------------- | --------------------------------------- | ------------------------------------------- |
34
+ | **Form** | Form wrapper with submission handling | `onSubmit`, `status`, `noValidate` |
35
+ | **Form.Field** | Label + input wrapper for accessibility | `label`, `labelFor`, `required`, `optional` |
36
+ | **Form.Input** | Text input with validation | `type`, `validationState`, `onEnter` |
37
+ | **Form.Textarea** | Multi-line text input | `rows`, `cols`, `onEnter` |
38
+ | **Form.Select** | Dropdown select with options | `onSelectionChange`, `onEnter` |
34
39
 
35
40
  ---
36
41
 
37
42
  ## Installation & Import
38
43
 
39
44
  ```tsx
40
- import Form from '@fpkit/acss';
45
+ import Form from "@fpkit/acss";
41
46
  // Or import specific components
42
- import { Form, Input, Field } from '@fpkit/acss';
47
+ import { Form, Input, Field } from "@fpkit/acss";
43
48
  ```
44
49
 
45
50
  ---
@@ -48,10 +53,11 @@ import { Form, Input, Field } from '@fpkit/acss';
48
53
 
49
54
  ### Simple Contact Form
50
55
 
51
- A basic form with required fields, proper label associations, and submission handling.
56
+ A basic form with required fields, proper label associations, and submission
57
+ handling.
52
58
 
53
59
  ```tsx
54
- import Form from '@fpkit/acss';
60
+ import Form from "@fpkit/acss";
55
61
 
56
62
  function ContactForm() {
57
63
  const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
@@ -85,33 +91,34 @@ function ContactForm() {
85
91
 
86
92
  ### Form with Status Management
87
93
 
88
- Use the `status` prop to manage form submission states. This example shows how the form automatically sets `aria-busy` and disables fields during submission.
94
+ Use the `status` prop to manage form submission states. This example shows how
95
+ the form automatically sets `aria-busy` and disables fields during submission.
89
96
 
90
97
  ```tsx
91
- import { useState } from 'react';
92
- import Form, { FormStatus } from '@fpkit/acss';
98
+ import { useState } from "react";
99
+ import Form, { FormStatus } from "@fpkit/acss";
93
100
 
94
101
  function RegistrationForm() {
95
- const [status, setStatus] = useState<FormStatus>('idle');
102
+ const [status, setStatus] = useState<FormStatus>("idle");
96
103
 
97
104
  const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
98
105
  e.preventDefault();
99
106
 
100
- setStatus('submitting');
107
+ setStatus("submitting");
101
108
 
102
109
  try {
103
110
  const formData = new FormData(e.currentTarget);
104
- await fetch('/api/register', {
105
- method: 'POST',
111
+ await fetch("/api/register", {
112
+ method: "POST",
106
113
  body: formData,
107
114
  });
108
- setStatus('success');
115
+ setStatus("success");
109
116
  } catch (error) {
110
- setStatus('error');
117
+ setStatus("error");
111
118
  }
112
119
  };
113
120
 
114
- const isSubmitting = status === 'submitting';
121
+ const isSubmitting = status === "submitting";
115
122
 
116
123
  return (
117
124
  <Form
@@ -140,11 +147,11 @@ function RegistrationForm() {
140
147
  </Form.Field>
141
148
 
142
149
  <button type="submit" disabled={isSubmitting}>
143
- {isSubmitting ? 'Submitting...' : 'Create Account'}
150
+ {isSubmitting ? "Submitting..." : "Create Account"}
144
151
  </button>
145
152
 
146
- {status === 'success' && <p>Account created successfully!</p>}
147
- {status === 'error' && <p>Error creating account. Please try again.</p>}
153
+ {status === "success" && <p>Account created successfully!</p>}
154
+ {status === "error" && <p>Error creating account. Please try again.</p>}
148
155
  </Form>
149
156
  );
150
157
  }
@@ -152,23 +159,24 @@ function RegistrationForm() {
152
159
 
153
160
  ### Form with Validation
154
161
 
155
- Implement client-side validation with error messages. Notice how the input uses `validationState` to show visual feedback and `aria-invalid` for screen readers.
162
+ Implement client-side validation with error messages. Notice how the input uses
163
+ `validationState` to show visual feedback and `aria-invalid` for screen readers.
156
164
 
157
165
  ```tsx
158
- import { useState } from 'react';
159
- import Form from '@fpkit/acss';
166
+ import { useState } from "react";
167
+ import Form from "@fpkit/acss";
160
168
 
161
169
  function ValidatedForm() {
162
- const [email, setEmail] = useState('');
163
- const [emailError, setEmailError] = useState('');
170
+ const [email, setEmail] = useState("");
171
+ const [emailError, setEmailError] = useState("");
164
172
 
165
173
  const validateEmail = (value: string) => {
166
174
  if (!value) {
167
- setEmailError('Email is required');
175
+ setEmailError("Email is required");
168
176
  } else if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value)) {
169
- setEmailError('Please enter a valid email address');
177
+ setEmailError("Please enter a valid email address");
170
178
  } else {
171
- setEmailError('');
179
+ setEmailError("");
172
180
  }
173
181
  };
174
182
 
@@ -194,7 +202,7 @@ function ValidatedForm() {
194
202
  value={email}
195
203
  onChange={(e) => setEmail(e.target.value)}
196
204
  onBlur={(e) => validateEmail(e.target.value)}
197
- validationState={emailError ? 'invalid' : email ? 'valid' : 'none'}
205
+ validationState={emailError ? "invalid" : email ? "valid" : "none"}
198
206
  required
199
207
  />
200
208
  </Form.Field>
@@ -207,17 +215,18 @@ function ValidatedForm() {
207
215
 
208
216
  ### Keyboard-Driven Workflows
209
217
 
210
- Use the `onEnter` prop for keyboard-friendly interactions. This example demonstrates `onEnter` on inputs, textareas, and selects.
218
+ Use the `onEnter` prop for keyboard-friendly interactions. This example
219
+ demonstrates `onEnter` on inputs, textareas, and selects.
211
220
 
212
221
  ```tsx
213
- import { useState } from 'react';
214
- import Form from '@fpkit/acss';
222
+ import { useState } from "react";
223
+ import Form from "@fpkit/acss";
215
224
 
216
225
  function SearchForm() {
217
- const [query, setQuery] = useState('');
226
+ const [query, setQuery] = useState("");
218
227
 
219
228
  const handleSearch = () => {
220
- console.log('Searching for:', query);
229
+ console.log("Searching for:", query);
221
230
  // Perform search
222
231
  };
223
232
 
@@ -268,23 +277,29 @@ function ServerSideForm() {
268
277
 
269
278
  ## Disabled State
270
279
 
271
- Form components support an accessible disabled state using the `aria-disabled` pattern, which provides better accessibility than the native HTML `disabled` attribute.
280
+ Form components support an accessible disabled state using the `aria-disabled`
281
+ pattern, which provides better accessibility than the native HTML `disabled`
282
+ attribute.
272
283
 
273
284
  ### Why aria-disabled?
274
285
 
275
286
  The `aria-disabled` pattern offers key advantages:
276
287
 
277
- - **Keyboard Accessibility**: Disabled elements remain in the tab order, allowing keyboard users to discover them
278
- - **Screen Reader Discovery**: Screen readers can announce the disabled state and read associated tooltips or help text
279
- - **WCAG Compliance**: Meets WCAG 2.1.1 (Keyboard) and 4.1.2 (Name, Role, Value) requirements
280
- - **Interactive Help**: Enables tooltips or contextual help on disabled elements to explain why they're disabled
288
+ - **Keyboard Accessibility**: Disabled elements remain in the tab order,
289
+ allowing keyboard users to discover them
290
+ - **Screen Reader Discovery**: Screen readers can announce the disabled state
291
+ and read associated tooltips or help text
292
+ - **WCAG Compliance**: Meets WCAG 2.1.1 (Keyboard) and 4.1.2 (Name, Role, Value)
293
+ requirements
294
+ - **Interactive Help**: Enables tooltips or contextual help on disabled elements
295
+ to explain why they're disabled
281
296
 
282
297
  ### Basic Usage
283
298
 
284
299
  Use the `disabled` prop to disable form inputs, textareas, and selects:
285
300
 
286
301
  ```tsx
287
- import Form from '@fpkit/acss';
302
+ import Form from "@fpkit/acss";
288
303
 
289
304
  function DisabledInputExample() {
290
305
  return (
@@ -300,11 +315,7 @@ function DisabledInputExample() {
300
315
  </Form.Field>
301
316
 
302
317
  <Form.Field label="Comments" labelFor="comments">
303
- <Form.Textarea
304
- id="comments"
305
- name="comments"
306
- disabled={true}
307
- />
318
+ <Form.Textarea id="comments" name="comments" disabled={true} />
308
319
  </Form.Field>
309
320
 
310
321
  <Form.Field label="Country" labelFor="country">
@@ -320,48 +331,52 @@ function DisabledInputExample() {
320
331
 
321
332
  ### Migration from isDisabled
322
333
 
323
- For backward compatibility, the deprecated `isDisabled` prop is still supported but will be removed in a future version.
334
+ For backward compatibility, the deprecated `isDisabled` prop is still supported
335
+ but will be removed in a future version.
324
336
 
325
337
  **Before (deprecated):**
338
+
326
339
  ```tsx
327
340
  <Form.Input isDisabled={true} />
328
341
  ```
329
342
 
330
343
  **After (recommended):**
344
+
331
345
  ```tsx
332
346
  <Form.Input disabled={true} />
333
347
  ```
334
348
 
335
- Both props work identically, but `disabled` follows standard HTML conventions and should be used for all new code.
349
+ Both props work identically, but `disabled` follows standard HTML conventions
350
+ and should be used for all new code.
336
351
 
337
352
  ### Disabled State During Form Submission
338
353
 
339
354
  A common pattern is disabling all form fields while a form is submitting:
340
355
 
341
356
  ```tsx
342
- import { useState } from 'react';
343
- import Form, { FormStatus } from '@fpkit/acss';
357
+ import { useState } from "react";
358
+ import Form, { FormStatus } from "@fpkit/acss";
344
359
 
345
360
  function SubmitDisabledForm() {
346
- const [status, setStatus] = useState<FormStatus>('idle');
361
+ const [status, setStatus] = useState<FormStatus>("idle");
347
362
 
348
363
  const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
349
364
  e.preventDefault();
350
- setStatus('submitting');
365
+ setStatus("submitting");
351
366
 
352
367
  try {
353
368
  const formData = new FormData(e.currentTarget);
354
- await fetch('/api/save', {
355
- method: 'POST',
369
+ await fetch("/api/save", {
370
+ method: "POST",
356
371
  body: formData,
357
372
  });
358
- setStatus('success');
373
+ setStatus("success");
359
374
  } catch (error) {
360
- setStatus('error');
375
+ setStatus("error");
361
376
  }
362
377
  };
363
378
 
364
- const isSubmitting = status === 'submitting';
379
+ const isSubmitting = status === "submitting";
365
380
 
366
381
  return (
367
382
  <Form status={status} onSubmit={handleSubmit}>
@@ -375,15 +390,11 @@ function SubmitDisabledForm() {
375
390
  </Form.Field>
376
391
 
377
392
  <Form.Field label="Bio" labelFor="bio">
378
- <Form.Textarea
379
- id="bio"
380
- name="bio"
381
- disabled={isSubmitting}
382
- />
393
+ <Form.Textarea id="bio" name="bio" disabled={isSubmitting} />
383
394
  </Form.Field>
384
395
 
385
396
  <button type="submit" disabled={isSubmitting}>
386
- {isSubmitting ? 'Saving...' : 'Save Profile'}
397
+ {isSubmitting ? "Saving..." : "Save Profile"}
387
398
  </button>
388
399
  </Form>
389
400
  );
@@ -394,10 +405,14 @@ function SubmitDisabledForm() {
394
405
 
395
406
  When a form control is disabled:
396
407
 
397
- - **Keyboard Navigation**: Element remains focusable via Tab key (maintains tab order)
398
- - **Interaction Prevention**: All interaction events are prevented (click, change, keydown, etc.)
399
- - **Focus Events**: Focus events still work, allowing screen readers to discover and announce the element
400
- - **Visual Styling**: `.is-disabled` class and `aria-disabled="true"` attribute are applied
408
+ - **Keyboard Navigation**: Element remains focusable via Tab key (maintains tab
409
+ order)
410
+ - **Interaction Prevention**: All interaction events are prevented (click,
411
+ change, keydown, etc.)
412
+ - **Focus Events**: Focus events still work, allowing screen readers to discover
413
+ and announce the element
414
+ - **Visual Styling**: `.is-disabled` class and `aria-disabled="true"` attribute
415
+ are applied
401
416
  - **Screen Readers**: Announce "disabled" state when focused
402
417
 
403
418
  ### Styling
@@ -406,9 +421,9 @@ Disabled elements can be styled using CSS custom properties:
406
421
 
407
422
  ```css
408
423
  :root {
409
- --disabled-opacity: 0.6; /* Visual opacity for disabled state */
410
- --disabled-cursor: not-allowed; /* Cursor style */
411
- --disabled-color: hsl(0 0% 40%); /* Text color (3:1 contrast minimum) */
424
+ --disabled-opacity: 0.6; /* Visual opacity for disabled state */
425
+ --disabled-cursor: not-allowed; /* Cursor style */
426
+ --disabled-color: hsl(0 0% 40%); /* Text color (3:1 contrast minimum) */
412
427
  }
413
428
  ```
414
429
 
@@ -418,13 +433,14 @@ Override these properties for custom styling:
418
433
  <Form.Input
419
434
  disabled={true}
420
435
  styles={{
421
- '--disabled-opacity': '0.5',
422
- '--disabled-color': '#666666',
436
+ "--disabled-opacity": "0.5",
437
+ "--disabled-color": "#666666",
423
438
  }}
424
439
  />
425
440
  ```
426
441
 
427
442
  **Selectors Available:**
443
+
428
444
  - `.is-disabled` - Class added to disabled elements
429
445
  - `[aria-disabled="true"]` - Attribute selector
430
446
 
@@ -433,9 +449,11 @@ Override these properties for custom styling:
433
449
  The `aria-disabled` pattern ensures compliance with:
434
450
 
435
451
  - **WCAG 2.1.1 (Keyboard)**: Elements remain in keyboard tab order for discovery
436
- - **WCAG 4.1.2 (Name, Role, Value)**: `aria-disabled` announces state to screen readers
452
+ - **WCAG 4.1.2 (Name, Role, Value)**: `aria-disabled` announces state to screen
453
+ readers
437
454
  - **WCAG 1.4.3 (Contrast Minimum)**: Disabled text maintains 3:1 contrast ratio
438
- - **WCAG 2.4.7 (Focus Visible)**: Focus indicators preserved on disabled elements
455
+ - **WCAG 2.4.7 (Focus Visible)**: Focus indicators preserved on disabled
456
+ elements
439
457
 
440
458
  ---
441
459
 
@@ -447,18 +465,18 @@ The main form wrapper component.
447
465
 
448
466
  #### Props
449
467
 
450
- | Prop | Type | Default | Description |
451
- |------|------|---------|-------------|
452
- | `onSubmit` | `(event: FormEvent) => void` | - | Form submission handler (prevents default) |
453
- | `status` | `'idle' \| 'submitting' \| 'success' \| 'error'` | `'idle'` | Current form status |
454
- | `action` | `string` | - | Form submission URL |
455
- | `formMethod` | `'get' \| 'post'` | `'post'` | HTTP method |
456
- | `noValidate` | `boolean` | `false` | Disable HTML5 validation |
457
- | `target` | `string` | - | Form submission target |
458
- | `id` | `string` | - | Unique form identifier |
459
- | `name` | `string` | - | Form name attribute |
460
- | `classes` | `string` | - | CSS class names |
461
- | `styles` | `CSSProperties` | - | Inline styles |
468
+ | Prop | Type | Default | Description |
469
+ | ------------ | ------------------------------------------------ | -------- | ------------------------------------------ |
470
+ | `onSubmit` | `(event: FormEvent) => void` | - | Form submission handler (prevents default) |
471
+ | `status` | `'idle' \| 'submitting' \| 'success' \| 'error'` | `'idle'` | Current form status |
472
+ | `action` | `string` | - | Form submission URL |
473
+ | `formMethod` | `'get' \| 'post'` | `'post'` | HTTP method |
474
+ | `noValidate` | `boolean` | `false` | Disable HTML5 validation |
475
+ | `target` | `string` | - | Form submission target |
476
+ | `id` | `string` | - | Unique form identifier |
477
+ | `name` | `string` | - | Form name attribute |
478
+ | `classes` | `string` | - | CSS class names |
479
+ | `styles` | `CSSProperties` | - | Inline styles |
462
480
 
463
481
  #### Accessibility
464
482
 
@@ -472,14 +490,14 @@ Wrapper component that associates labels with inputs for accessibility.
472
490
 
473
491
  #### Props
474
492
 
475
- | Prop | Type | Default | Description |
476
- |------|------|---------|-------------|
477
- | `label` | `ReactNode` | - | **Required.** Label text or element |
478
- | `labelFor` | `string` | - | ID of associated input (for `htmlFor`) |
479
- | `required` | `boolean` | `false` | Show required indicator (*) |
480
- | `optional` | `boolean` | `false` | Show optional indicator |
481
- | `errorMessage` | `string` | - | Error message to display |
482
- | `hintText` | `string` | - | Helper text below input |
493
+ | Prop | Type | Default | Description |
494
+ | -------------- | ----------- | ------- | -------------------------------------- |
495
+ | `label` | `ReactNode` | - | **Required.** Label text or element |
496
+ | `labelFor` | `string` | - | ID of associated input (for `htmlFor`) |
497
+ | `required` | `boolean` | `false` | Show required indicator (\*) |
498
+ | `optional` | `boolean` | `false` | Show optional indicator |
499
+ | `errorMessage` | `string` | - | Error message to display |
500
+ | `hintText` | `string` | - | Helper text below input |
483
501
 
484
502
  ### Form.Input
485
503
 
@@ -487,17 +505,17 @@ Text input component with validation support.
487
505
 
488
506
  #### Props
489
507
 
490
- | Prop | Type | Default | Description |
491
- |------|------|---------|-------------|
492
- | `type` | `string` | `'text'` | Input type (text, email, password, etc.) |
493
- | `validationState` | `'none' \| 'valid' \| 'invalid'` | `'none'` | Validation state |
494
- | `errorMessage` | `string` | - | Error message for `aria-describedby` |
495
- | `hintText` | `string` | - | Hint text for `aria-describedby` |
496
- | `onEnter` | `(event: KeyboardEvent) => void` | - | Handler for Enter key press |
497
- | `disabled` | `boolean` | `false` | Disable input using `aria-disabled` pattern (remains focusable) |
498
- | `isDisabled` | `boolean` | `false` | **Deprecated.** Use `disabled` instead |
499
- | `readOnly` | `boolean` | `false` | Make input read-only |
500
- | `required` | `boolean` | `false` | Mark input as required |
508
+ | Prop | Type | Default | Description |
509
+ | ----------------- | -------------------------------- | -------- | --------------------------------------------------------------- |
510
+ | `type` | `string` | `'text'` | Input type (text, email, password, etc.) |
511
+ | `validationState` | `'none' \| 'valid' \| 'invalid'` | `'none'` | Validation state |
512
+ | `errorMessage` | `string` | - | Error message for `aria-describedby` |
513
+ | `hintText` | `string` | - | Hint text for `aria-describedby` |
514
+ | `onEnter` | `(event: KeyboardEvent) => void` | - | Handler for Enter key press |
515
+ | `disabled` | `boolean` | `false` | Disable input using `aria-disabled` pattern (remains focusable) |
516
+ | `isDisabled` | `boolean` | `false` | **Deprecated.** Use `disabled` instead |
517
+ | `readOnly` | `boolean` | `false` | Make input read-only |
518
+ | `required` | `boolean` | `false` | Mark input as required |
501
519
 
502
520
  ### Form.Textarea
503
521
 
@@ -505,13 +523,13 @@ Multi-line text input component.
505
523
 
506
524
  #### Props
507
525
 
508
- | Prop | Type | Default | Description |
509
- |------|------|---------|-------------|
510
- | `rows` | `number` | `5` | Number of visible rows |
511
- | `cols` | `number` | `25` | Number of visible columns |
512
- | `onEnter` | `(event: KeyboardEvent) => void` | - | Handler for Enter (without Shift) |
513
- | `disabled` | `boolean` | `false` | Disable textarea using `aria-disabled` pattern (remains focusable) |
514
- | `isDisabled` | `boolean` | `false` | **Deprecated.** Use `disabled` instead |
526
+ | Prop | Type | Default | Description |
527
+ | ------------ | -------------------------------- | ------- | ------------------------------------------------------------------ |
528
+ | `rows` | `number` | `5` | Number of visible rows |
529
+ | `cols` | `number` | `25` | Number of visible columns |
530
+ | `onEnter` | `(event: KeyboardEvent) => void` | - | Handler for Enter (without Shift) |
531
+ | `disabled` | `boolean` | `false` | Disable textarea using `aria-disabled` pattern (remains focusable) |
532
+ | `isDisabled` | `boolean` | `false` | **Deprecated.** Use `disabled` instead |
515
533
 
516
534
  **Note**: Shift+Enter adds a new line without triggering `onEnter`.
517
535
 
@@ -521,13 +539,13 @@ Dropdown select component with keyboard support.
521
539
 
522
540
  #### Props
523
541
 
524
- | Prop | Type | Default | Description |
525
- |------|------|---------|-------------|
526
- | `onSelectionChange` | `(event: ChangeEvent) => void` | - | Selection change handler |
527
- | `onEnter` | `(event: KeyboardEvent) => void` | - | Handler for Enter key press |
528
- | `required` | `boolean` | `false` | Mark select as required |
529
- | `disabled` | `boolean` | `false` | Disable select using `aria-disabled` pattern (remains focusable) |
530
- | `isDisabled` | `boolean` | `false` | **Deprecated.** Use `disabled` instead |
542
+ | Prop | Type | Default | Description |
543
+ | ------------------- | -------------------------------- | ------- | ---------------------------------------------------------------- |
544
+ | `onSelectionChange` | `(event: ChangeEvent) => void` | - | Selection change handler |
545
+ | `onEnter` | `(event: KeyboardEvent) => void` | - | Handler for Enter key press |
546
+ | `required` | `boolean` | `false` | Mark select as required |
547
+ | `disabled` | `boolean` | `false` | Disable select using `aria-disabled` pattern (remains focusable) |
548
+ | `isDisabled` | `boolean` | `false` | **Deprecated.** Use `disabled` instead |
531
549
 
532
550
  ---
533
551
 
@@ -538,21 +556,26 @@ Dropdown select component with keyboard support.
538
556
  All form components meet WCAG 2.1 Level AA standards:
539
557
 
540
558
  1. **WCAG 3.3.1 Error Identification** ✅
559
+
541
560
  - Error messages clearly associated with inputs via `aria-describedby`
542
561
  - Validation states communicated via `aria-invalid`
543
562
 
544
563
  2. **WCAG 3.3.2 Labels or Instructions** ✅
564
+
545
565
  - All inputs have associated labels via `<label htmlFor>`
546
566
  - Required fields marked with visual and programmatic indicators
547
567
 
548
568
  3. **WCAG 4.1.2 Name, Role, Value** ✅
569
+
549
570
  - All inputs have proper `role`, `name`, and `aria-` attributes
550
571
  - Form status communicated via `aria-busy`
551
572
 
552
573
  4. **WCAG 2.4.7 Focus Visible** ✅
574
+
553
575
  - Focus indicators styled via `:focus-visible` in SCSS
554
576
 
555
577
  5. **WCAG 2.1.1 Keyboard** ✅
578
+
556
579
  - All interactive elements keyboard accessible
557
580
  - `onEnter` prop for custom keyboard workflows
558
581
 
@@ -640,20 +663,20 @@ input[data-validation="valid"] {
640
663
  All components are fully typed with comprehensive interfaces:
641
664
 
642
665
  ```tsx
643
- import Form, {
644
- FormProps,
645
- FormStatus,
646
- InputProps
647
- } from '@fpkit/acss';
666
+ import Form, { FormProps, FormStatus, InputProps } from "@fpkit/acss";
648
667
 
649
668
  const MyForm: React.FC = () => {
650
- const [status, setStatus] = useState<FormStatus>('idle');
669
+ const [status, setStatus] = useState<FormStatus>("idle");
651
670
 
652
- const handleSubmit: FormProps['onSubmit'] = (e) => {
671
+ const handleSubmit: FormProps["onSubmit"] = (e) => {
653
672
  // Fully typed event
654
673
  };
655
674
 
656
- return <Form status={status} onSubmit={handleSubmit}>...</Form>;
675
+ return (
676
+ <Form status={status} onSubmit={handleSubmit}>
677
+ ...
678
+ </Form>
679
+ );
657
680
  };
658
681
  ```
659
682
 
@@ -664,10 +687,10 @@ const MyForm: React.FC = () => {
664
687
  Form components are fully tested with Vitest and React Testing Library:
665
688
 
666
689
  ```tsx
667
- import { render, screen, userEvent } from '@testing-library/react';
668
- import Form from '@fpkit/acss';
690
+ import { render, screen, userEvent } from "@testing-library/react";
691
+ import Form from "@fpkit/acss";
669
692
 
670
- test('submits form data', async () => {
693
+ test("submits form data", async () => {
671
694
  const handleSubmit = vi.fn();
672
695
  const user = userEvent.setup();
673
696
 
@@ -678,8 +701,8 @@ test('submits form data', async () => {
678
701
  </Form>
679
702
  );
680
703
 
681
- await user.type(screen.getByRole('textbox'), 'test@example.com');
682
- await user.click(screen.getByRole('button'));
704
+ await user.type(screen.getByRole("textbox"), "test@example.com");
705
+ await user.click(screen.getByRole("button"));
683
706
 
684
707
  expect(handleSubmit).toHaveBeenCalled();
685
708
  });
@@ -711,11 +734,13 @@ test('submits form data', async () => {
711
734
 
712
735
  ## More Examples
713
736
 
714
- See the [Form Interactive Guide](./?path=/docs/fp-react-forms-form--docs) for complete examples including:
737
+ See the [Form Interactive Guide](./?path=/docs/fp-react-forms-form--docs) for
738
+ complete examples including:
715
739
 
716
740
  ### Form with Hint Text
717
741
 
718
- Guide users with helpful hint text below fields, properly associated using `aria-describedby`.
742
+ Guide users with helpful hint text below fields, properly associated using
743
+ `aria-describedby`.
719
744
 
720
745
  ### Form with Select Dropdown
721
746
 
@@ -727,7 +752,8 @@ Clearly distinguish between required and optional fields for better UX.
727
752
 
728
753
  ### Complete Registration Form
729
754
 
730
- A comprehensive example combining all features: validation, hints, different field types, and proper accessibility.
755
+ A comprehensive example combining all features: validation, hints, different
756
+ field types, and proper accessibility.
731
757
 
732
758
  ---
733
759
 
@@ -743,7 +769,8 @@ A comprehensive example combining all features: validation, hints, different fie
743
769
 
744
770
  ## Contributing
745
771
 
746
- Found an issue or have a suggestion? Please [open an issue](https://github.com/your-repo/issues) or submit a pull request.
772
+ Found an issue or have a suggestion? Please
773
+ [open an issue](https://github.com/your-repo/issues) or submit a pull request.
747
774
 
748
775
  ---
749
776