@fpkit/acss 6.1.0 → 6.2.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/libs/components/layout/landmarks.css +1 -1
- package/libs/components/layout/landmarks.css.map +1 -1
- package/libs/components/layout/landmarks.min.css +2 -2
- package/libs/index.cjs +20 -19
- package/libs/index.cjs.map +1 -1
- package/libs/index.css +1 -1
- package/libs/index.css.map +1 -1
- package/libs/index.d.cts +38 -1
- package/libs/index.d.ts +38 -1
- package/libs/index.js +9 -9
- package/libs/index.js.map +1 -1
- package/package.json +2 -2
- package/src/components/layout/README.mdx +1117 -0
- package/src/components/layout/STYLES.mdx +159 -4
- package/src/components/layout/fieldset.stories.tsx +387 -0
- package/src/components/layout/landmarks.scss +115 -2
- package/src/components/layout/landmarks.stories.tsx +2 -6
- package/src/components/layout/landmarks.tsx +96 -27
- package/src/styles/index.css +82 -0
- package/src/styles/index.css.map +1 -1
- package/src/styles/layout/landmarks.css +83 -0
- package/src/styles/layout/landmarks.css.map +1 -1
|
@@ -0,0 +1,1117 @@
|
|
|
1
|
+
import { Meta } from "@storybook/addon-docs/blocks";
|
|
2
|
+
|
|
3
|
+
<Meta title="FP.REACT Components/Layout/Landmarks/Readme" />
|
|
4
|
+
|
|
5
|
+
# Landmark Components
|
|
6
|
+
|
|
7
|
+
Semantic HTML5 landmark components for building accessible page layouts with proper document structure. Includes Header, Main, Footer, Aside, Section, Article, and Fieldset components.
|
|
8
|
+
|
|
9
|
+
## Features
|
|
10
|
+
|
|
11
|
+
✅ **Semantic HTML5** - Uses proper landmark elements (`<header>`, `<main>`, `<footer>`, etc.)
|
|
12
|
+
✅ **WCAG 2.1 AA Compliant** - Automatic ARIA landmark roles for screen readers
|
|
13
|
+
✅ **TypeScript Support** - Full type definitions with IntelliSense
|
|
14
|
+
✅ **CSS Custom Properties** - Complete theming control with CSS variables
|
|
15
|
+
✅ **Responsive Layouts** - Built-in article/aside flexbox patterns
|
|
16
|
+
✅ **Hero/Overlay System** - Grid-based hero sections with background images
|
|
17
|
+
✅ **Rem-Based Sizing** - All measurements use rem units (1rem = 16px)
|
|
18
|
+
✅ **Logical Properties** - RTL language support with `padding-inline`, `margin-block`
|
|
19
|
+
✅ **Compound Exports** - Use as `<Landmarks.Header>` or destructured `<Header>`
|
|
20
|
+
✅ **Flexible Content** - Section wrappers for consistent child layout
|
|
21
|
+
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
## Component Architecture
|
|
25
|
+
|
|
26
|
+
The landmarks system consists of seven semantic components:
|
|
27
|
+
|
|
28
|
+
| Component | Element | ARIA Role | Purpose |
|
|
29
|
+
|-----------|---------|-----------|---------|
|
|
30
|
+
| **`Header`** | `<header>` | `banner` (top-level) | Site header, hero sections, page banners |
|
|
31
|
+
| **`Main`** | `<main>` | `main` | Primary page content (use once per page) |
|
|
32
|
+
| **`Footer`** | `<footer>` | `contentinfo` (top-level) | Site footer, copyright, links |
|
|
33
|
+
| **`Aside`** | `<aside>` | `complementary` | Sidebars, related content, callouts |
|
|
34
|
+
| **`Section`** | `<section>` | `region` (with label) | Thematic content grouping |
|
|
35
|
+
| **`Article`** | `<article>` | `article` | Self-contained content (blog posts, cards) |
|
|
36
|
+
| **`Fieldset`** | `<fieldset>` | `group` | Form grouping or content grouping with legend |
|
|
37
|
+
|
|
38
|
+
---
|
|
39
|
+
|
|
40
|
+
## Quick Start
|
|
41
|
+
|
|
42
|
+
### Basic Page Layout
|
|
43
|
+
|
|
44
|
+
```tsx
|
|
45
|
+
import { Landmarks } from "@fpkit/acss";
|
|
46
|
+
|
|
47
|
+
function PageLayout() {
|
|
48
|
+
return (
|
|
49
|
+
<>
|
|
50
|
+
<Landmarks.Header>
|
|
51
|
+
<h1>Welcome to My Site</h1>
|
|
52
|
+
<p>Building accessible web experiences</p>
|
|
53
|
+
</Landmarks.Header>
|
|
54
|
+
|
|
55
|
+
<Landmarks.Main>
|
|
56
|
+
<h2>Main Content</h2>
|
|
57
|
+
<p>Your primary page content goes here.</p>
|
|
58
|
+
</Landmarks.Main>
|
|
59
|
+
|
|
60
|
+
<Landmarks.Footer>
|
|
61
|
+
<p>© 2025 Company Name</p>
|
|
62
|
+
</Landmarks.Footer>
|
|
63
|
+
</>
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
### Destructured Imports
|
|
69
|
+
|
|
70
|
+
```tsx
|
|
71
|
+
import { Header, Main, Footer } from "@fpkit/acss";
|
|
72
|
+
|
|
73
|
+
function App() {
|
|
74
|
+
return (
|
|
75
|
+
<>
|
|
76
|
+
<Header>
|
|
77
|
+
<h1>Site Title</h1>
|
|
78
|
+
</Header>
|
|
79
|
+
|
|
80
|
+
<Main>
|
|
81
|
+
<p>Content here</p>
|
|
82
|
+
</Main>
|
|
83
|
+
|
|
84
|
+
<Footer>
|
|
85
|
+
<p>Footer content</p>
|
|
86
|
+
</Footer>
|
|
87
|
+
</>
|
|
88
|
+
);
|
|
89
|
+
}
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
---
|
|
93
|
+
|
|
94
|
+
## Header Component
|
|
95
|
+
|
|
96
|
+
The Header component creates semantic page headers with built-in hero/overlay support.
|
|
97
|
+
|
|
98
|
+
### Basic Header
|
|
99
|
+
|
|
100
|
+
```tsx
|
|
101
|
+
import { Header } from "@fpkit/acss";
|
|
102
|
+
|
|
103
|
+
<Header>
|
|
104
|
+
<h1>Page Title</h1>
|
|
105
|
+
<p>Subtitle or description</p>
|
|
106
|
+
</Header>
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
### Hero Header with Background
|
|
110
|
+
|
|
111
|
+
```tsx
|
|
112
|
+
import { Header } from "@fpkit/acss";
|
|
113
|
+
import Img from "@fpkit/acss/images";
|
|
114
|
+
|
|
115
|
+
<Header
|
|
116
|
+
headerBackground={<Img src="/hero.jpg" alt="" />}
|
|
117
|
+
styles={{ "--overlay-color": "white" }}
|
|
118
|
+
>
|
|
119
|
+
<h1>Welcome</h1>
|
|
120
|
+
<p>Hero section with background image</p>
|
|
121
|
+
<button>Get Started</button>
|
|
122
|
+
</Header>
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
### Custom Overlay Settings
|
|
126
|
+
|
|
127
|
+
```tsx
|
|
128
|
+
<Header
|
|
129
|
+
styles={{
|
|
130
|
+
"--overlay-height": "60vh",
|
|
131
|
+
"--overlay-bg": "#0066cc",
|
|
132
|
+
"--overlay-color": "white",
|
|
133
|
+
}}
|
|
134
|
+
>
|
|
135
|
+
<h1>Custom Hero</h1>
|
|
136
|
+
<p>Customized height, background, and text color</p>
|
|
137
|
+
</Header>
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
### Header Props
|
|
141
|
+
|
|
142
|
+
| Prop | Type | Default | Description |
|
|
143
|
+
|------|------|---------|-------------|
|
|
144
|
+
| `headerBackground` | `ReactNode` | - | Background image or element (placed in grid overlay) |
|
|
145
|
+
| `children` | `ReactNode` | *required* | Content displayed in header section |
|
|
146
|
+
| `id` | `string` | - | Unique identifier |
|
|
147
|
+
| `styles` | `CSSProperties` | - | Inline CSS custom properties |
|
|
148
|
+
| `classes` | `string` | - | Additional CSS classes |
|
|
149
|
+
|
|
150
|
+
---
|
|
151
|
+
|
|
152
|
+
## Main Component
|
|
153
|
+
|
|
154
|
+
The Main component represents the primary content of the page. Use only **once per page**.
|
|
155
|
+
|
|
156
|
+
### Basic Main
|
|
157
|
+
|
|
158
|
+
```tsx
|
|
159
|
+
import { Main } from "@fpkit/acss";
|
|
160
|
+
|
|
161
|
+
<Main>
|
|
162
|
+
<h2>Page Heading</h2>
|
|
163
|
+
<p>Main content goes here.</p>
|
|
164
|
+
</Main>
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
### Main with Article and Aside (Sidebar Layout)
|
|
168
|
+
|
|
169
|
+
```tsx
|
|
170
|
+
<Main>
|
|
171
|
+
<article>
|
|
172
|
+
<h2>Article Title</h2>
|
|
173
|
+
<p>Primary content takes majority of space...</p>
|
|
174
|
+
</article>
|
|
175
|
+
<aside>
|
|
176
|
+
<h3>Sidebar</h3>
|
|
177
|
+
<p>Related content or navigation...</p>
|
|
178
|
+
</aside>
|
|
179
|
+
</Main>
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
The flexbox layout automatically:
|
|
183
|
+
- Gives article 999 flex-grow (takes most space)
|
|
184
|
+
- Sets aside min-width to 20rem (320px)
|
|
185
|
+
- Wraps aside below article on narrow screens
|
|
186
|
+
- Adds 2rem gap between elements
|
|
187
|
+
|
|
188
|
+
### Main Props
|
|
189
|
+
|
|
190
|
+
| Prop | Type | Default | Description |
|
|
191
|
+
|------|------|---------|-------------|
|
|
192
|
+
| `children` | `ReactNode` | *required* | Main page content |
|
|
193
|
+
| `id` | `string` | - | Unique identifier |
|
|
194
|
+
| `styles` | `CSSProperties` | - | Inline CSS custom properties |
|
|
195
|
+
| `classes` | `string` | - | Additional CSS classes |
|
|
196
|
+
|
|
197
|
+
---
|
|
198
|
+
|
|
199
|
+
## Footer Component
|
|
200
|
+
|
|
201
|
+
The Footer component creates semantic page footers with centered content by default.
|
|
202
|
+
|
|
203
|
+
### Basic Footer
|
|
204
|
+
|
|
205
|
+
```tsx
|
|
206
|
+
import { Footer } from "@fpkit/acss";
|
|
207
|
+
|
|
208
|
+
<Footer>
|
|
209
|
+
<p>© 2025 Company Name. All rights reserved.</p>
|
|
210
|
+
</Footer>
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
### Footer with Sections
|
|
214
|
+
|
|
215
|
+
```tsx
|
|
216
|
+
<Footer>
|
|
217
|
+
<div>
|
|
218
|
+
<h3>Company</h3>
|
|
219
|
+
<ul>
|
|
220
|
+
<li><a href="/about">About</a></li>
|
|
221
|
+
<li><a href="/careers">Careers</a></li>
|
|
222
|
+
</ul>
|
|
223
|
+
</div>
|
|
224
|
+
<div>
|
|
225
|
+
<h3>Support</h3>
|
|
226
|
+
<ul>
|
|
227
|
+
<li><a href="/help">Help</a></li>
|
|
228
|
+
<li><a href="/contact">Contact</a></li>
|
|
229
|
+
</ul>
|
|
230
|
+
</div>
|
|
231
|
+
</Footer>
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
### Footer Props
|
|
235
|
+
|
|
236
|
+
| Prop | Type | Default | Description |
|
|
237
|
+
|------|------|---------|-------------|
|
|
238
|
+
| `children` | `ReactNode` | `"Copyright © 2022"` | Footer content |
|
|
239
|
+
| `id` | `string` | - | Unique identifier |
|
|
240
|
+
| `styles` | `CSSProperties` | - | Inline CSS custom properties |
|
|
241
|
+
| `classes` | `string` | - | Additional CSS classes |
|
|
242
|
+
|
|
243
|
+
---
|
|
244
|
+
|
|
245
|
+
## Aside Component
|
|
246
|
+
|
|
247
|
+
The Aside component creates complementary content sections (sidebars, callouts).
|
|
248
|
+
|
|
249
|
+
### Basic Aside
|
|
250
|
+
|
|
251
|
+
```tsx
|
|
252
|
+
import { Aside } from "@fpkit/acss";
|
|
253
|
+
|
|
254
|
+
<Aside>
|
|
255
|
+
<h3>Related Links</h3>
|
|
256
|
+
<ul>
|
|
257
|
+
<li><a href="#">Link 1</a></li>
|
|
258
|
+
<li><a href="#">Link 2</a></li>
|
|
259
|
+
</ul>
|
|
260
|
+
</Aside>
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
### Aside Props
|
|
264
|
+
|
|
265
|
+
| Prop | Type | Default | Description |
|
|
266
|
+
|------|------|---------|-------------|
|
|
267
|
+
| `children` | `ReactNode` | *required* | Aside content |
|
|
268
|
+
| `id` | `string` | - | Unique identifier |
|
|
269
|
+
| `styles` | `CSSProperties` | - | Inline CSS custom properties |
|
|
270
|
+
| `classes` | `string` | - | Additional CSS classes |
|
|
271
|
+
|
|
272
|
+
---
|
|
273
|
+
|
|
274
|
+
## Section Component
|
|
275
|
+
|
|
276
|
+
The Section component groups thematic content. Use `aria-label` for unnamed sections.
|
|
277
|
+
|
|
278
|
+
### Basic Section
|
|
279
|
+
|
|
280
|
+
```tsx
|
|
281
|
+
import { Section } from "@fpkit/acss";
|
|
282
|
+
|
|
283
|
+
<Section aria-label="Introduction">
|
|
284
|
+
<h2>About Us</h2>
|
|
285
|
+
<p>Company information...</p>
|
|
286
|
+
</Section>
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
### Section Props
|
|
290
|
+
|
|
291
|
+
| Prop | Type | Default | Description |
|
|
292
|
+
|------|------|---------|-------------|
|
|
293
|
+
| `children` | `ReactNode` | *required* | Section content |
|
|
294
|
+
| `id` | `string` | - | Unique identifier |
|
|
295
|
+
| `aria-label` | `string` | - | Accessible label for unnamed sections |
|
|
296
|
+
| `styles` | `CSSProperties` | - | Inline CSS custom properties |
|
|
297
|
+
| `classes` | `string` | - | Additional CSS classes |
|
|
298
|
+
|
|
299
|
+
---
|
|
300
|
+
|
|
301
|
+
## Article Component
|
|
302
|
+
|
|
303
|
+
The Article component represents self-contained, reusable content.
|
|
304
|
+
|
|
305
|
+
### Basic Article
|
|
306
|
+
|
|
307
|
+
```tsx
|
|
308
|
+
import { Article } from "@fpkit/acss";
|
|
309
|
+
|
|
310
|
+
<Article>
|
|
311
|
+
<h2>Blog Post Title</h2>
|
|
312
|
+
<p>Article content that could stand alone...</p>
|
|
313
|
+
</Article>
|
|
314
|
+
```
|
|
315
|
+
|
|
316
|
+
### Article in Card Layout
|
|
317
|
+
|
|
318
|
+
```tsx
|
|
319
|
+
<Article>
|
|
320
|
+
<header>
|
|
321
|
+
<h3>Product Review</h3>
|
|
322
|
+
<time datetime="2025-01-18">January 18, 2025</time>
|
|
323
|
+
</header>
|
|
324
|
+
<p>Review content...</p>
|
|
325
|
+
<footer>
|
|
326
|
+
<a href="/read-more">Read More</a>
|
|
327
|
+
</footer>
|
|
328
|
+
</Article>
|
|
329
|
+
```
|
|
330
|
+
|
|
331
|
+
### Article Props
|
|
332
|
+
|
|
333
|
+
| Prop | Type | Default | Description |
|
|
334
|
+
|------|------|---------|-------------|
|
|
335
|
+
| `children` | `ReactNode` | *required* | Article content |
|
|
336
|
+
| `id` | `string` | - | Unique identifier |
|
|
337
|
+
| `styles` | `CSSProperties` | - | Inline CSS custom properties |
|
|
338
|
+
| `classes` | `string` | - | Additional CSS classes |
|
|
339
|
+
|
|
340
|
+
---
|
|
341
|
+
|
|
342
|
+
## Fieldset Component
|
|
343
|
+
|
|
344
|
+
The Fieldset component provides semantic content grouping with an optional legend.
|
|
345
|
+
|
|
346
|
+
### Basic Fieldset
|
|
347
|
+
|
|
348
|
+
```tsx
|
|
349
|
+
import { Fieldset } from "@fpkit/acss";
|
|
350
|
+
|
|
351
|
+
<Fieldset legend="Personal Information">
|
|
352
|
+
<p>Name: John Doe</p>
|
|
353
|
+
<p>Email: john@example.com</p>
|
|
354
|
+
</Fieldset>
|
|
355
|
+
```
|
|
356
|
+
|
|
357
|
+
### Fieldset Without Legend
|
|
358
|
+
|
|
359
|
+
```tsx
|
|
360
|
+
<Fieldset>
|
|
361
|
+
<p>Grouped content without visible legend</p>
|
|
362
|
+
<p>Still maintains semantic grouping (role="group")</p>
|
|
363
|
+
</Fieldset>
|
|
364
|
+
```
|
|
365
|
+
|
|
366
|
+
### Inline Legend Variant
|
|
367
|
+
|
|
368
|
+
For compact layouts where legend appears inline with content:
|
|
369
|
+
|
|
370
|
+
```tsx
|
|
371
|
+
<Fieldset legend="Status:" data-legend="inline">
|
|
372
|
+
<p>Active</p>
|
|
373
|
+
</Fieldset>
|
|
374
|
+
```
|
|
375
|
+
|
|
376
|
+
### Grouped Variant (Emphasized)
|
|
377
|
+
|
|
378
|
+
For important content sections needing visual emphasis:
|
|
379
|
+
|
|
380
|
+
```tsx
|
|
381
|
+
<Fieldset legend="Account Settings" data-fieldset="grouped">
|
|
382
|
+
<p><strong>Username:</strong> johndoe</p>
|
|
383
|
+
<p><strong>Role:</strong> Admin</p>
|
|
384
|
+
<p><strong>Status:</strong> Active</p>
|
|
385
|
+
</Fieldset>
|
|
386
|
+
```
|
|
387
|
+
|
|
388
|
+
### Fieldset Props
|
|
389
|
+
|
|
390
|
+
| Prop | Type | Default | Description |
|
|
391
|
+
|------|------|---------|-------------|
|
|
392
|
+
| `legend` | `ReactNode` | - | Optional legend content (text or elements) |
|
|
393
|
+
| `description` | `string` | - | Additional description text for complex fieldsets (enhances accessibility via aria-describedby) |
|
|
394
|
+
| `descriptionId` | `string` | - | Custom ID for the description element (auto-generated if description is provided) |
|
|
395
|
+
| `children` | `ReactNode` | *required* | Content inside fieldset section |
|
|
396
|
+
| `data-legend` | `"inline"` | - | Variant: inline legend layout |
|
|
397
|
+
| `data-fieldset` | `"grouped"` | - | Variant: emphasized styling |
|
|
398
|
+
| `disabled` | `boolean` | - | Disables the fieldset and all contained form controls |
|
|
399
|
+
| `id` | `string` | - | Unique identifier |
|
|
400
|
+
| `styles` | `CSSProperties` | - | Inline CSS custom properties |
|
|
401
|
+
| `classes` | `string` | - | Additional CSS classes |
|
|
402
|
+
|
|
403
|
+
---
|
|
404
|
+
|
|
405
|
+
## Fieldset Accessibility Features
|
|
406
|
+
|
|
407
|
+
The Fieldset component is **WCAG 2.1 Level AA compliant** and includes comprehensive accessibility features.
|
|
408
|
+
|
|
409
|
+
### Semantic HTML & ARIA Support
|
|
410
|
+
|
|
411
|
+
#### Native Accessibility
|
|
412
|
+
- Uses native `<fieldset>` element with implicit `role="group"`
|
|
413
|
+
- Native `<legend>` provides accessible name for the group
|
|
414
|
+
- Proper focus management and keyboard navigation
|
|
415
|
+
- Works seamlessly with form controls and screen readers
|
|
416
|
+
|
|
417
|
+
#### Enhanced ARIA Support
|
|
418
|
+
```tsx
|
|
419
|
+
// With description for additional context
|
|
420
|
+
<Fieldset
|
|
421
|
+
id="shipping"
|
|
422
|
+
legend="Shipping Address"
|
|
423
|
+
description="This address will be used for delivery"
|
|
424
|
+
>
|
|
425
|
+
{/* form controls */}
|
|
426
|
+
</Fieldset>
|
|
427
|
+
```
|
|
428
|
+
|
|
429
|
+
The `description` prop automatically:
|
|
430
|
+
- Adds `aria-describedby` to link description to the fieldset
|
|
431
|
+
- Generates unique ID (`{fieldset-id}-desc`) for the description element
|
|
432
|
+
- Announces both legend and description to screen readers
|
|
433
|
+
|
|
434
|
+
### Visual Accessibility
|
|
435
|
+
|
|
436
|
+
#### WCAG 2.1 Compliance
|
|
437
|
+
All visual styles meet or exceed WCAG 2.1 Level AA requirements:
|
|
438
|
+
|
|
439
|
+
**Color Contrast (1.4.3, 1.4.11)**
|
|
440
|
+
- Border contrast: ≥ 3:1 against background (UI components)
|
|
441
|
+
- Text contrast: ≥ 4.5:1 for all text content
|
|
442
|
+
- Semantic color tokens ensure consistent contrast across themes
|
|
443
|
+
|
|
444
|
+
**Focus Visible (2.4.7)**
|
|
445
|
+
- Visible focus indicator when keyboard navigating into fieldset
|
|
446
|
+
- 2px outline with 2px offset for clear visibility
|
|
447
|
+
- Removed for mouse interactions (`:focus-within` with hover detection)
|
|
448
|
+
|
|
449
|
+
**Non-text Contrast (1.4.11)**
|
|
450
|
+
- High contrast mode support (`@media (prefers-contrast: high)`)
|
|
451
|
+
- Windows High Contrast Mode support (`@media (forced-colors: active)`)
|
|
452
|
+
- Thicker borders and bolder text in high contrast environments
|
|
453
|
+
|
|
454
|
+
#### Keyboard Navigation
|
|
455
|
+
```tsx
|
|
456
|
+
<Fieldset legend="Contact Info">
|
|
457
|
+
<label htmlFor="name">Name</label>
|
|
458
|
+
<input id="name" type="text" />
|
|
459
|
+
|
|
460
|
+
<label htmlFor="email">Email</label>
|
|
461
|
+
<input id="email" type="email" />
|
|
462
|
+
</Fieldset>
|
|
463
|
+
```
|
|
464
|
+
|
|
465
|
+
- **Tab**: Moves focus to next form control
|
|
466
|
+
- **Shift+Tab**: Moves focus to previous form control
|
|
467
|
+
- **Focus indicator**: Visible outline on fieldset when any child has focus
|
|
468
|
+
- **Screen reader**: Announces "Contact Info, group" when entering fieldset
|
|
469
|
+
|
|
470
|
+
#### Disabled State
|
|
471
|
+
```tsx
|
|
472
|
+
<Fieldset legend="Unavailable Section" disabled>
|
|
473
|
+
<input type="text" disabled />
|
|
474
|
+
<button disabled>Submit</button>
|
|
475
|
+
</Fieldset>
|
|
476
|
+
```
|
|
477
|
+
|
|
478
|
+
Disabled fieldsets automatically:
|
|
479
|
+
- Reduce opacity to 60% for visual indication
|
|
480
|
+
- Show `not-allowed` cursor
|
|
481
|
+
- Disable all contained form controls
|
|
482
|
+
- Apply muted color to legend text
|
|
483
|
+
- Maintain readable contrast ratios (WCAG 1.4.3)
|
|
484
|
+
|
|
485
|
+
### Best Practices for Accessibility
|
|
486
|
+
|
|
487
|
+
#### Required Fields
|
|
488
|
+
Mark required fields with accessible indicators:
|
|
489
|
+
|
|
490
|
+
```tsx
|
|
491
|
+
<Fieldset legend="Contact Information">
|
|
492
|
+
<label htmlFor="email">
|
|
493
|
+
Email <span aria-label="required">*</span>
|
|
494
|
+
</label>
|
|
495
|
+
<input id="email" type="email" required />
|
|
496
|
+
|
|
497
|
+
<label htmlFor="phone">Phone (optional)</label>
|
|
498
|
+
<input id="phone" type="tel" />
|
|
499
|
+
</Fieldset>
|
|
500
|
+
```
|
|
501
|
+
|
|
502
|
+
#### Error Handling
|
|
503
|
+
Provide clear error messages with proper ARIA attributes:
|
|
504
|
+
|
|
505
|
+
```tsx
|
|
506
|
+
<Fieldset
|
|
507
|
+
legend="Payment Information"
|
|
508
|
+
description="All fields are required for payment processing"
|
|
509
|
+
>
|
|
510
|
+
<label htmlFor="card-number">
|
|
511
|
+
Card Number <span aria-label="required">*</span>
|
|
512
|
+
</label>
|
|
513
|
+
<input
|
|
514
|
+
id="card-number"
|
|
515
|
+
type="text"
|
|
516
|
+
required
|
|
517
|
+
aria-invalid="true"
|
|
518
|
+
aria-describedby="card-error"
|
|
519
|
+
/>
|
|
520
|
+
<span id="card-error" role="alert">
|
|
521
|
+
Please enter a valid card number
|
|
522
|
+
</span>
|
|
523
|
+
</Fieldset>
|
|
524
|
+
```
|
|
525
|
+
|
|
526
|
+
#### Complex Forms
|
|
527
|
+
Use descriptions to provide context for complex forms:
|
|
528
|
+
|
|
529
|
+
```tsx
|
|
530
|
+
<Fieldset
|
|
531
|
+
legend="Privacy Settings"
|
|
532
|
+
description="Control who can see your information and activity"
|
|
533
|
+
>
|
|
534
|
+
<label>
|
|
535
|
+
<input type="checkbox" name="public-profile" />
|
|
536
|
+
Make profile public
|
|
537
|
+
</label>
|
|
538
|
+
<label>
|
|
539
|
+
<input type="checkbox" name="email-visible" />
|
|
540
|
+
Show email address
|
|
541
|
+
</label>
|
|
542
|
+
</Fieldset>
|
|
543
|
+
```
|
|
544
|
+
|
|
545
|
+
#### Multiple Fieldsets
|
|
546
|
+
Group related sections with clear legends:
|
|
547
|
+
|
|
548
|
+
```tsx
|
|
549
|
+
<form>
|
|
550
|
+
<Fieldset
|
|
551
|
+
legend="Personal Information"
|
|
552
|
+
description="Basic information about you"
|
|
553
|
+
>
|
|
554
|
+
{/* name, email fields */}
|
|
555
|
+
</Fieldset>
|
|
556
|
+
|
|
557
|
+
<Fieldset
|
|
558
|
+
legend="Account Preferences"
|
|
559
|
+
description="Customize your account settings"
|
|
560
|
+
data-fieldset="grouped"
|
|
561
|
+
>
|
|
562
|
+
{/* preferences */}
|
|
563
|
+
</Fieldset>
|
|
564
|
+
|
|
565
|
+
<Fieldset
|
|
566
|
+
legend="Communication"
|
|
567
|
+
description="How we can contact you"
|
|
568
|
+
data-fieldset="grouped"
|
|
569
|
+
>
|
|
570
|
+
{/* contact preferences */}
|
|
571
|
+
</Fieldset>
|
|
572
|
+
</form>
|
|
573
|
+
```
|
|
574
|
+
|
|
575
|
+
### Screen Reader Announcements
|
|
576
|
+
|
|
577
|
+
When a screen reader user navigates into a fieldset:
|
|
578
|
+
|
|
579
|
+
**Without description:**
|
|
580
|
+
> "Personal Information, group"
|
|
581
|
+
|
|
582
|
+
**With description:**
|
|
583
|
+
> "Personal Information, group. Please provide your contact details for follow-up"
|
|
584
|
+
|
|
585
|
+
**With required fields:**
|
|
586
|
+
> "Email, required, edit text"
|
|
587
|
+
|
|
588
|
+
**With errors:**
|
|
589
|
+
> "Email, invalid entry, required, edit text. Please enter a valid email address"
|
|
590
|
+
|
|
591
|
+
### Testing Accessibility
|
|
592
|
+
|
|
593
|
+
#### Automated Testing (Storybook)
|
|
594
|
+
All fieldset stories include play functions that test:
|
|
595
|
+
- Presence of `role="group"`
|
|
596
|
+
- Correct `aria-describedby` linkage
|
|
597
|
+
- Keyboard navigation through form controls
|
|
598
|
+
- Required field indicators
|
|
599
|
+
- Disabled state functionality
|
|
600
|
+
|
|
601
|
+
#### Manual Testing Checklist
|
|
602
|
+
- [ ] Keyboard navigate through all form controls (Tab, Shift+Tab)
|
|
603
|
+
- [ ] Verify focus indicators are visible
|
|
604
|
+
- [ ] Test with screen reader (VoiceOver, NVDA, JAWS)
|
|
605
|
+
- [ ] Legend announces as group name
|
|
606
|
+
- [ ] Description is read after legend
|
|
607
|
+
- [ ] Required fields announced correctly
|
|
608
|
+
- [ ] Test in high contrast mode (Windows/macOS)
|
|
609
|
+
- [ ] Verify color contrast with tools:
|
|
610
|
+
- [WebAIM Contrast Checker](https://webaim.org/resources/contrastchecker/)
|
|
611
|
+
- Chrome DevTools Accessibility panel
|
|
612
|
+
- [ ] Test disabled state
|
|
613
|
+
- [ ] Verify responsive behavior across viewports
|
|
614
|
+
|
|
615
|
+
#### Browser Testing
|
|
616
|
+
Tested and verified in:
|
|
617
|
+
- ✅ Chrome (latest)
|
|
618
|
+
- ✅ Firefox (latest)
|
|
619
|
+
- ✅ Safari (latest)
|
|
620
|
+
- ✅ Edge (latest)
|
|
621
|
+
|
|
622
|
+
### CSS Variables for Accessibility
|
|
623
|
+
|
|
624
|
+
```css
|
|
625
|
+
/* Focus indicators */
|
|
626
|
+
--fieldset-focus-outline: 2px solid var(--focus-color, #005a9c);
|
|
627
|
+
--fieldset-focus-offset: 2px;
|
|
628
|
+
|
|
629
|
+
/* Disabled state */
|
|
630
|
+
--fieldset-disabled-opacity: 0.6;
|
|
631
|
+
--legend-disabled-color: var(--text-disabled, #757575);
|
|
632
|
+
|
|
633
|
+
/* Description text */
|
|
634
|
+
--fieldset-description-fs: 0.875rem;
|
|
635
|
+
--fieldset-description-color: var(--text-subtle, #666);
|
|
636
|
+
|
|
637
|
+
/* High contrast overrides */
|
|
638
|
+
@media (prefers-contrast: high) {
|
|
639
|
+
--fieldset-border-width: 2px;
|
|
640
|
+
--legend-fw: 700;
|
|
641
|
+
}
|
|
642
|
+
```
|
|
643
|
+
|
|
644
|
+
### Related WCAG Guidelines
|
|
645
|
+
|
|
646
|
+
The Fieldset component addresses these WCAG 2.1 Level AA success criteria:
|
|
647
|
+
|
|
648
|
+
- **1.4.3 Contrast (Minimum)** - Text and UI component contrast
|
|
649
|
+
- **1.4.11 Non-text Contrast** - UI component contrast
|
|
650
|
+
- **2.4.7 Focus Visible** - Keyboard focus indicators
|
|
651
|
+
- **3.3.2 Labels or Instructions** - Form field labels and descriptions
|
|
652
|
+
- **4.1.2 Name, Role, Value** - Proper semantic HTML and ARIA
|
|
653
|
+
|
|
654
|
+
---
|
|
655
|
+
|
|
656
|
+
## CSS Custom Properties
|
|
657
|
+
|
|
658
|
+
### Header/Overlay Variables
|
|
659
|
+
|
|
660
|
+
```css
|
|
661
|
+
:root {
|
|
662
|
+
/* Grid Layout */
|
|
663
|
+
--overlay-grid-area: overlay;
|
|
664
|
+
--overlay-display: grid;
|
|
665
|
+
--overlay-placement: center;
|
|
666
|
+
|
|
667
|
+
/* Dimensions */
|
|
668
|
+
--overlay-width: 100%;
|
|
669
|
+
--overlay-height: 40vh;
|
|
670
|
+
--overlay-max-height: 500px;
|
|
671
|
+
|
|
672
|
+
/* Content */
|
|
673
|
+
--overlay-content-width: 80%;
|
|
674
|
+
--overlay-gap: 2rem; /* 32px */
|
|
675
|
+
|
|
676
|
+
/* Colors */
|
|
677
|
+
--overlay-bg: whitesmoke;
|
|
678
|
+
--overlay-color: currentColor;
|
|
679
|
+
|
|
680
|
+
/* Spacing */
|
|
681
|
+
--overlay-padding: 2rem; /* 32px */
|
|
682
|
+
--overlay-padding-inline: auto;
|
|
683
|
+
--overlay-padding-block: auto;
|
|
684
|
+
--overlay-margin-inline: auto;
|
|
685
|
+
--overlay-margin-block: auto;
|
|
686
|
+
|
|
687
|
+
/* Typography */
|
|
688
|
+
--header-line-height: 1.1;
|
|
689
|
+
}
|
|
690
|
+
```
|
|
691
|
+
|
|
692
|
+
### Main/Footer Variables
|
|
693
|
+
|
|
694
|
+
```css
|
|
695
|
+
:root {
|
|
696
|
+
/* Content Width */
|
|
697
|
+
--content-width: min(100%, 1480px);
|
|
698
|
+
--content-margin-inline: auto;
|
|
699
|
+
--content-padding-inline: 1rem; /* 16px */
|
|
700
|
+
--content-gap: 2rem; /* 32px - article/aside gap */
|
|
701
|
+
}
|
|
702
|
+
```
|
|
703
|
+
|
|
704
|
+
### Fieldset Variables
|
|
705
|
+
|
|
706
|
+
```css
|
|
707
|
+
:root {
|
|
708
|
+
/* Border - WCAG compliant contrast ratios */
|
|
709
|
+
--fieldset-border: 1px solid var(--border-default, #999);
|
|
710
|
+
--fieldset-border-radius: 0.5rem; /* 8px */
|
|
711
|
+
|
|
712
|
+
/* Spacing */
|
|
713
|
+
--fieldset-padding: 1rem; /* 16px */
|
|
714
|
+
--fieldset-padding-inline: 1.5rem; /* 24px */
|
|
715
|
+
--fieldset-padding-block: 1rem; /* 16px */
|
|
716
|
+
--fieldset-margin-block: 2rem; /* 32px */
|
|
717
|
+
|
|
718
|
+
/* Colors */
|
|
719
|
+
--fieldset-bg: transparent;
|
|
720
|
+
|
|
721
|
+
/* Legend */
|
|
722
|
+
--legend-fs: 1rem; /* 16px */
|
|
723
|
+
--legend-fw: 600;
|
|
724
|
+
--legend-padding-inline: 0.5rem; /* 8px */
|
|
725
|
+
--legend-color: currentColor;
|
|
726
|
+
|
|
727
|
+
/* Description (for aria-describedby) */
|
|
728
|
+
--fieldset-description-fs: 0.875rem; /* 14px */
|
|
729
|
+
--fieldset-description-color: var(--text-subtle, #666);
|
|
730
|
+
|
|
731
|
+
/* Focus indicators - WCAG 2.4.7 */
|
|
732
|
+
--fieldset-focus-outline: 2px solid var(--focus-color, #005a9c);
|
|
733
|
+
--fieldset-focus-offset: 2px;
|
|
734
|
+
|
|
735
|
+
/* Disabled state - WCAG 1.4.3 */
|
|
736
|
+
--fieldset-disabled-opacity: 0.6;
|
|
737
|
+
--legend-disabled-color: var(--text-disabled, #757575);
|
|
738
|
+
}
|
|
739
|
+
|
|
740
|
+
/* Grouped variant */
|
|
741
|
+
[data-fieldset="grouped"] {
|
|
742
|
+
--fieldset-bg: var(--surface-subtle, #f5f5f5);
|
|
743
|
+
--fieldset-border: 2px solid var(--border-focus, #005a9c);
|
|
744
|
+
--legend-fs: 1.125rem; /* 18px */
|
|
745
|
+
--legend-fw: 700;
|
|
746
|
+
}
|
|
747
|
+
```
|
|
748
|
+
|
|
749
|
+
### Customization Example
|
|
750
|
+
|
|
751
|
+
```tsx
|
|
752
|
+
<Header
|
|
753
|
+
styles={{
|
|
754
|
+
"--overlay-height": "70vh",
|
|
755
|
+
"--overlay-bg": "linear-gradient(135deg, #667eea 0%, #764ba2 100%)",
|
|
756
|
+
"--overlay-color": "white",
|
|
757
|
+
"--overlay-content-width": "50rem",
|
|
758
|
+
}}
|
|
759
|
+
>
|
|
760
|
+
<h1>Customized Hero</h1>
|
|
761
|
+
</Header>
|
|
762
|
+
|
|
763
|
+
<Fieldset
|
|
764
|
+
legend="Themed Fieldset"
|
|
765
|
+
styles={{
|
|
766
|
+
"--fieldset-border": "2px dashed #666",
|
|
767
|
+
"--fieldset-bg": "#f0f0f0",
|
|
768
|
+
"--legend-color": "#0066cc",
|
|
769
|
+
"--legend-fs": "1.25rem",
|
|
770
|
+
}}
|
|
771
|
+
>
|
|
772
|
+
<p>Custom styled content</p>
|
|
773
|
+
</Fieldset>
|
|
774
|
+
```
|
|
775
|
+
|
|
776
|
+
---
|
|
777
|
+
|
|
778
|
+
## Accessibility Features
|
|
779
|
+
|
|
780
|
+
### Automatic ARIA Roles
|
|
781
|
+
|
|
782
|
+
HTML5 landmarks automatically provide ARIA roles:
|
|
783
|
+
|
|
784
|
+
| Element | ARIA Role | Condition |
|
|
785
|
+
|---------|-----------|-----------|
|
|
786
|
+
| `<header>` | `banner` | When direct child of `<body>` |
|
|
787
|
+
| `<main>` | `main` | Always |
|
|
788
|
+
| `<footer>` | `contentinfo` | When direct child of `<body>` |
|
|
789
|
+
| `<aside>` | `complementary` | Always |
|
|
790
|
+
| `<section>` | `region` | When has `aria-label` or `aria-labelledby` |
|
|
791
|
+
| `<article>` | `article` | Always |
|
|
792
|
+
| `<fieldset>` | `group` | Always |
|
|
793
|
+
|
|
794
|
+
### Screen Reader Navigation
|
|
795
|
+
|
|
796
|
+
Landmark elements enable screen reader users to:
|
|
797
|
+
- Skip directly to main content
|
|
798
|
+
- Navigate between page sections
|
|
799
|
+
- Understand page structure
|
|
800
|
+
- Jump to complementary content
|
|
801
|
+
|
|
802
|
+
### Skip Links
|
|
803
|
+
|
|
804
|
+
Provide skip navigation for keyboard users:
|
|
805
|
+
|
|
806
|
+
```tsx
|
|
807
|
+
<a href="#main-content" className="skip-link">
|
|
808
|
+
Skip to main content
|
|
809
|
+
</a>
|
|
810
|
+
|
|
811
|
+
<Header>...</Header>
|
|
812
|
+
|
|
813
|
+
<Main id="main-content">
|
|
814
|
+
<h2>Main Content</h2>
|
|
815
|
+
<p>Primary page content...</p>
|
|
816
|
+
</Main>
|
|
817
|
+
```
|
|
818
|
+
|
|
819
|
+
### Heading Hierarchy
|
|
820
|
+
|
|
821
|
+
Maintain proper heading levels within landmarks:
|
|
822
|
+
|
|
823
|
+
```tsx
|
|
824
|
+
<Header>
|
|
825
|
+
<h1>Site Title</h1> {/* Only one h1 per page */}
|
|
826
|
+
</Header>
|
|
827
|
+
|
|
828
|
+
<Main>
|
|
829
|
+
<h2>Section Title</h2>
|
|
830
|
+
<h3>Subsection Title</h3>
|
|
831
|
+
<h4>Detail Heading</h4>
|
|
832
|
+
</Main>
|
|
833
|
+
```
|
|
834
|
+
|
|
835
|
+
### Alternative Text for Hero Images
|
|
836
|
+
|
|
837
|
+
Background images should use empty alt or `role="presentation"`:
|
|
838
|
+
|
|
839
|
+
```tsx
|
|
840
|
+
<Header
|
|
841
|
+
headerBackground={
|
|
842
|
+
<img src="/hero.jpg" alt="" role="presentation" />
|
|
843
|
+
}
|
|
844
|
+
>
|
|
845
|
+
<h1>Descriptive Title</h1>
|
|
846
|
+
</Header>
|
|
847
|
+
```
|
|
848
|
+
|
|
849
|
+
---
|
|
850
|
+
|
|
851
|
+
## Layout Patterns
|
|
852
|
+
|
|
853
|
+
### Full Page Layout
|
|
854
|
+
|
|
855
|
+
```tsx
|
|
856
|
+
function App() {
|
|
857
|
+
return (
|
|
858
|
+
<>
|
|
859
|
+
<Header
|
|
860
|
+
styles={{
|
|
861
|
+
"--overlay-height": "50vh",
|
|
862
|
+
"--overlay-bg": "#0066cc",
|
|
863
|
+
"--overlay-color": "white",
|
|
864
|
+
}}
|
|
865
|
+
>
|
|
866
|
+
<h1>Welcome to Our Site</h1>
|
|
867
|
+
<p>Discover amazing products and services</p>
|
|
868
|
+
<button>Get Started</button>
|
|
869
|
+
</Header>
|
|
870
|
+
|
|
871
|
+
<Main>
|
|
872
|
+
<Article>
|
|
873
|
+
<h2>Main Content</h2>
|
|
874
|
+
<p>Primary content area...</p>
|
|
875
|
+
</Article>
|
|
876
|
+
<Aside>
|
|
877
|
+
<h3>Quick Links</h3>
|
|
878
|
+
<ul>
|
|
879
|
+
<li><a href="#">Link 1</a></li>
|
|
880
|
+
<li><a href="#">Link 2</a></li>
|
|
881
|
+
</ul>
|
|
882
|
+
</Aside>
|
|
883
|
+
</Main>
|
|
884
|
+
|
|
885
|
+
<Footer>
|
|
886
|
+
<p>© 2025 Company Name. All rights reserved.</p>
|
|
887
|
+
</Footer>
|
|
888
|
+
</>
|
|
889
|
+
);
|
|
890
|
+
}
|
|
891
|
+
```
|
|
892
|
+
|
|
893
|
+
### Blog Post Layout
|
|
894
|
+
|
|
895
|
+
```tsx
|
|
896
|
+
<Main>
|
|
897
|
+
<Article>
|
|
898
|
+
<header>
|
|
899
|
+
<h1>Blog Post Title</h1>
|
|
900
|
+
<time datetime="2025-01-18">January 18, 2025</time>
|
|
901
|
+
</header>
|
|
902
|
+
|
|
903
|
+
<Section aria-label="Introduction">
|
|
904
|
+
<p>Post introduction...</p>
|
|
905
|
+
</Section>
|
|
906
|
+
|
|
907
|
+
<Section aria-label="Main Content">
|
|
908
|
+
<h2>First Topic</h2>
|
|
909
|
+
<p>Content...</p>
|
|
910
|
+
</Section>
|
|
911
|
+
|
|
912
|
+
<footer>
|
|
913
|
+
<p>Tags: React, TypeScript, Accessibility</p>
|
|
914
|
+
</footer>
|
|
915
|
+
</Article>
|
|
916
|
+
|
|
917
|
+
<Aside>
|
|
918
|
+
<h2>Related Posts</h2>
|
|
919
|
+
<ul>
|
|
920
|
+
<li><a href="/post-1">Related Post 1</a></li>
|
|
921
|
+
<li><a href="/post-2">Related Post 2</a></li>
|
|
922
|
+
</ul>
|
|
923
|
+
</Aside>
|
|
924
|
+
</Main>
|
|
925
|
+
```
|
|
926
|
+
|
|
927
|
+
### Form with Fieldsets
|
|
928
|
+
|
|
929
|
+
```tsx
|
|
930
|
+
<Main>
|
|
931
|
+
<form>
|
|
932
|
+
<Fieldset legend="Personal Information">
|
|
933
|
+
<label>
|
|
934
|
+
Name: <input type="text" name="name" />
|
|
935
|
+
</label>
|
|
936
|
+
<label>
|
|
937
|
+
Email: <input type="email" name="email" />
|
|
938
|
+
</label>
|
|
939
|
+
</Fieldset>
|
|
940
|
+
|
|
941
|
+
<Fieldset legend="Preferences" data-fieldset="grouped">
|
|
942
|
+
<label>
|
|
943
|
+
<input type="checkbox" name="newsletter" />
|
|
944
|
+
Subscribe to newsletter
|
|
945
|
+
</label>
|
|
946
|
+
<label>
|
|
947
|
+
<input type="checkbox" name="notifications" />
|
|
948
|
+
Enable notifications
|
|
949
|
+
</label>
|
|
950
|
+
</Fieldset>
|
|
951
|
+
|
|
952
|
+
<button type="submit">Submit</button>
|
|
953
|
+
</form>
|
|
954
|
+
</Main>
|
|
955
|
+
```
|
|
956
|
+
|
|
957
|
+
### Multi-Column Card Layout
|
|
958
|
+
|
|
959
|
+
```tsx
|
|
960
|
+
<Main>
|
|
961
|
+
<div style={{
|
|
962
|
+
display: "grid",
|
|
963
|
+
gridTemplateColumns: "repeat(auto-fit, minmax(20rem, 1fr))",
|
|
964
|
+
gap: "2rem"
|
|
965
|
+
}}>
|
|
966
|
+
<Article>
|
|
967
|
+
<h3>Card 1</h3>
|
|
968
|
+
<p>Self-contained content...</p>
|
|
969
|
+
</Article>
|
|
970
|
+
<Article>
|
|
971
|
+
<h3>Card 2</h3>
|
|
972
|
+
<p>Self-contained content...</p>
|
|
973
|
+
</Article>
|
|
974
|
+
<Article>
|
|
975
|
+
<h3>Card 3</h3>
|
|
976
|
+
<p>Self-contained content...</p>
|
|
977
|
+
</Article>
|
|
978
|
+
</div>
|
|
979
|
+
</Main>
|
|
980
|
+
```
|
|
981
|
+
|
|
982
|
+
---
|
|
983
|
+
|
|
984
|
+
## TypeScript Support
|
|
985
|
+
|
|
986
|
+
### Import Types
|
|
987
|
+
|
|
988
|
+
```tsx
|
|
989
|
+
import type {
|
|
990
|
+
HeaderProps,
|
|
991
|
+
MainProps,
|
|
992
|
+
FooterProps,
|
|
993
|
+
AsideProps,
|
|
994
|
+
SectionProps,
|
|
995
|
+
ArticleProps,
|
|
996
|
+
FieldsetProps,
|
|
997
|
+
} from "@fpkit/acss";
|
|
998
|
+
```
|
|
999
|
+
|
|
1000
|
+
### Type-Safe Props
|
|
1001
|
+
|
|
1002
|
+
```tsx
|
|
1003
|
+
const headerProps: HeaderProps = {
|
|
1004
|
+
headerBackground: <img src="/bg.jpg" alt="" />,
|
|
1005
|
+
styles: { "--overlay-height": "60vh" },
|
|
1006
|
+
children: <h1>Title</h1>,
|
|
1007
|
+
};
|
|
1008
|
+
|
|
1009
|
+
<Header {...headerProps} />
|
|
1010
|
+
|
|
1011
|
+
const fieldsetProps: FieldsetProps = {
|
|
1012
|
+
legend: "User Info",
|
|
1013
|
+
"data-fieldset": "grouped",
|
|
1014
|
+
children: <p>Content</p>,
|
|
1015
|
+
};
|
|
1016
|
+
|
|
1017
|
+
<Fieldset {...fieldsetProps} />
|
|
1018
|
+
```
|
|
1019
|
+
|
|
1020
|
+
---
|
|
1021
|
+
|
|
1022
|
+
## Best Practices
|
|
1023
|
+
|
|
1024
|
+
### ✅ Do
|
|
1025
|
+
|
|
1026
|
+
```tsx
|
|
1027
|
+
// Use Header for hero sections
|
|
1028
|
+
<Header>
|
|
1029
|
+
<h1>Page Title</h1>
|
|
1030
|
+
<p>Description</p>
|
|
1031
|
+
</Header>
|
|
1032
|
+
|
|
1033
|
+
// Use Main once per page
|
|
1034
|
+
<Main>
|
|
1035
|
+
<h2>Primary content</h2>
|
|
1036
|
+
</Main>
|
|
1037
|
+
|
|
1038
|
+
// Use Article for self-contained content
|
|
1039
|
+
<Article>
|
|
1040
|
+
<h3>Blog Post</h3>
|
|
1041
|
+
<p>Content that could stand alone...</p>
|
|
1042
|
+
</Article>
|
|
1043
|
+
|
|
1044
|
+
// Provide legend for fieldsets when appropriate
|
|
1045
|
+
<Fieldset legend="Contact Information">
|
|
1046
|
+
<p>Name, email, phone...</p>
|
|
1047
|
+
</Fieldset>
|
|
1048
|
+
|
|
1049
|
+
// Use semantic heading hierarchy
|
|
1050
|
+
<h1>Page Title</h1>
|
|
1051
|
+
<h2>Section</h2>
|
|
1052
|
+
<h3>Subsection</h3>
|
|
1053
|
+
|
|
1054
|
+
// Add aria-label to unnamed sections
|
|
1055
|
+
<Section aria-label="Features">
|
|
1056
|
+
<div>...</div>
|
|
1057
|
+
</Section>
|
|
1058
|
+
```
|
|
1059
|
+
|
|
1060
|
+
### ❌ Don't
|
|
1061
|
+
|
|
1062
|
+
```tsx
|
|
1063
|
+
// Don't use multiple <Main> elements
|
|
1064
|
+
<Main>Content 1</Main>
|
|
1065
|
+
<Main>Content 2</Main> // ❌ Only one per page
|
|
1066
|
+
|
|
1067
|
+
// Don't skip heading levels
|
|
1068
|
+
<h1>Title</h1>
|
|
1069
|
+
<h4>Subsection</h4> // ❌ Skip h2, h3
|
|
1070
|
+
|
|
1071
|
+
// Don't use landmarks for styling only
|
|
1072
|
+
<Header className="blue-box"> // ❌ Use div for pure styling
|
|
1073
|
+
<p>Not actually a header</p>
|
|
1074
|
+
</Header>
|
|
1075
|
+
|
|
1076
|
+
// Don't forget alt text for hero images
|
|
1077
|
+
<Header headerBackground={<img src="/bg.jpg" />}> // ❌ Missing alt
|
|
1078
|
+
|
|
1079
|
+
// Don't use px units in custom properties
|
|
1080
|
+
styles={{ "--overlay-height": "500px" }} // ❌ Use rem or vh
|
|
1081
|
+
```
|
|
1082
|
+
|
|
1083
|
+
---
|
|
1084
|
+
|
|
1085
|
+
## Browser Support
|
|
1086
|
+
|
|
1087
|
+
- ✅ **HTML5 Landmarks**: All modern browsers
|
|
1088
|
+
- ✅ **CSS Grid**: Chrome 57+, Firefox 52+, Safari 10.1+
|
|
1089
|
+
- ✅ **CSS Custom Properties**: Chrome 49+, Firefox 31+, Safari 9.1+
|
|
1090
|
+
- ✅ **Flexbox**: All modern browsers
|
|
1091
|
+
- ✅ **`:has()` Selector**: Chrome 105+, Firefox 121+, Safari 15.4+
|
|
1092
|
+
- ✅ **`min()` Function**: Chrome 79+, Firefox 75+, Safari 11.1+
|
|
1093
|
+
|
|
1094
|
+
---
|
|
1095
|
+
|
|
1096
|
+
## Related Components
|
|
1097
|
+
|
|
1098
|
+
- **[UI](?path=/docs/fp-react-components-ui--docs)** - Base component wrapper
|
|
1099
|
+
- **[Grid](?path=/docs/fp-react-components-grid--docs)** - Grid layout system
|
|
1100
|
+
- **[Flexbox](?path=/docs/fp-react-components-flexbox--docs)** - Flexbox utilities
|
|
1101
|
+
- **[Card](?path=/docs/fp-react-components-card--docs)** - Card components
|
|
1102
|
+
- **[STYLES.mdx](?path=/docs/fp-react-components-layout-styles--docs)** - Detailed CSS documentation
|
|
1103
|
+
|
|
1104
|
+
---
|
|
1105
|
+
|
|
1106
|
+
## Resources
|
|
1107
|
+
|
|
1108
|
+
- [MDN: HTML5 Sectioning Elements](https://developer.mozilla.org/en-US/docs/Web/HTML/Element#content_sectioning)
|
|
1109
|
+
- [W3C ARIA: Landmark Regions](https://www.w3.org/WAI/ARIA/apg/patterns/landmarks/)
|
|
1110
|
+
- [WebAIM: Semantic Structure](https://webaim.org/techniques/semanticstructure/)
|
|
1111
|
+
- [WCAG 2.1 Guidelines](https://www.w3.org/WAI/WCAG21/quickref/)
|
|
1112
|
+
|
|
1113
|
+
---
|
|
1114
|
+
|
|
1115
|
+
## License
|
|
1116
|
+
|
|
1117
|
+
Part of the `@fpkit/acss` component library.
|