@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.
@@ -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>&copy; 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>&copy; 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>&copy; 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.