@zoyth/simple-site-framework 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +572 -0
- package/bin/create-simple-site.js +390 -0
- package/bin/simple-site.js +664 -0
- package/dist/client.js +135 -0
- package/dist/client.js.map +1 -0
- package/dist/client.mjs +107 -0
- package/dist/client.mjs.map +1 -0
- package/dist/components/index.d.mts +3936 -0
- package/dist/components/index.d.ts +3936 -0
- package/dist/components/index.js +38265 -0
- package/dist/components/index.js.map +1 -0
- package/dist/components/index.mjs +38173 -0
- package/dist/components/index.mjs.map +1 -0
- package/dist/config/index.d.mts +298 -0
- package/dist/config/index.d.ts +298 -0
- package/dist/config/index.js +19 -0
- package/dist/config/index.js.map +1 -0
- package/dist/config/index.mjs +1 -0
- package/dist/config/index.mjs.map +1 -0
- package/dist/index.d.mts +2184 -0
- package/dist/index.d.ts +2184 -0
- package/dist/index.js +1713 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +1605 -0
- package/dist/index.mjs.map +1 -0
- package/dist/lib/i18n/index.js +665 -0
- package/dist/lib/i18n/index.js.map +1 -0
- package/dist/lib/i18n/index.mjs +621 -0
- package/dist/lib/i18n/index.mjs.map +1 -0
- package/docs/DOCUMENTATION-STRUCTURE.md +1156 -0
- package/docs/EXPORTS.md +125 -0
- package/docs/PERFORMANCE.md +757 -0
- package/docs/POLICY-PAGES.md +867 -0
- package/docs/ROADMAP.md +334 -0
- package/docs/SEO.md +455 -0
- package/docs/SITEMAP.md +708 -0
- package/docs/STRUCTURED-DATA.md +671 -0
- package/docs/accessibility/common-patterns.md +529 -0
- package/docs/accessibility/keyboard-navigation.md +263 -0
- package/docs/accessibility/overview.md +122 -0
- package/docs/accessibility/screen-readers.md +311 -0
- package/docs/accessibility/wcag-compliance.md +159 -0
- package/docs/api/README.md +164 -0
- package/docs/api/components/Accessibility.md +356 -0
- package/docs/api/components/Button.md +240 -0
- package/docs/api/components/HeroSection.md +306 -0
- package/docs/architecture/decisions.md +449 -0
- package/docs/components/AnalyticsTracker.md +58 -0
- package/docs/components/AnimatedCounter.md +48 -0
- package/docs/components/AnimatedSection.md +56 -0
- package/docs/components/BlogCard.md +42 -0
- package/docs/components/Checkbox.md +56 -0
- package/docs/components/CodeBlock.md +52 -0
- package/docs/components/ComparisonTable.md +40 -0
- package/docs/components/ComponentDemo.md +38 -0
- package/docs/components/CountdownTimer.md +51 -0
- package/docs/components/ExitIntentModal.md +56 -0
- package/docs/components/FAQAccordion.md +66 -0
- package/docs/components/FeaturesGrid.md +55 -0
- package/docs/components/FileUpload.md +54 -0
- package/docs/components/I18nMetaTags.md +55 -0
- package/docs/components/Icon.md +53 -0
- package/docs/components/LazySection.md +46 -0
- package/docs/components/LiveProof.md +53 -0
- package/docs/components/LoadingSpinner.md +46 -0
- package/docs/components/MultiStepForm.md +48 -0
- package/docs/components/PolicyLayout.md +55 -0
- package/docs/components/PricingTable.md +49 -0
- package/docs/components/Radio.md +59 -0
- package/docs/components/SEOMetaTags.md +58 -0
- package/docs/components/ScriptInjector.md +50 -0
- package/docs/components/Select.md +72 -0
- package/docs/components/Skeleton.md +47 -0
- package/docs/components/StatsSection.md +48 -0
- package/docs/components/StickyBar.md +62 -0
- package/docs/components/StructuredData.md +99 -0
- package/docs/components/StyleGuide.md +46 -0
- package/docs/components/TableOfContents.md +47 -0
- package/docs/components/TestimonialCarousel.md +42 -0
- package/docs/components/Timeline.md +51 -0
- package/docs/components/Toast.md +59 -0
- package/docs/components/TrackedLink.md +62 -0
- package/docs/components/TrustBadges.md +44 -0
- package/docs/components/conversion/MobileCTA.md +363 -0
- package/docs/components/forms/ContactForm.md +75 -0
- package/docs/components/forms/FormField.md +74 -0
- package/docs/components/layout/Footer.md +601 -0
- package/docs/components/layout/Header.md +549 -0
- package/docs/components/layout/LanguageSelector.md +54 -0
- package/docs/components/layout/LanguageSwitcher.md +24 -0
- package/docs/components/overview.md +447 -0
- package/docs/components/sections/AboutSection.md +48 -0
- package/docs/components/sections/CTASection.md +596 -0
- package/docs/components/sections/CaseStudySection.md +47 -0
- package/docs/components/sections/ContactSection.md +599 -0
- package/docs/components/sections/FeatureSection.md +44 -0
- package/docs/components/sections/HeroSection.md +404 -0
- package/docs/components/sections/LogosSection.md +47 -0
- package/docs/components/sections/PersonalTaxesSection.md +23 -0
- package/docs/components/sections/RecruitingSection.md +23 -0
- package/docs/components/sections/SecurePortalSection.md +23 -0
- package/docs/components/sections/ServicePageLayout.md +52 -0
- package/docs/components/sections/ServicesSection.md +49 -0
- package/docs/components/sections/TestimonialSection.md +44 -0
- package/docs/components/sections/WhyChooseUsSection.md +54 -0
- package/docs/components/ui/Breadcrumb.md +70 -0
- package/docs/components/ui/Button.md +514 -0
- package/docs/components/ui/Card.md +501 -0
- package/docs/components/ui/Input.md +54 -0
- package/docs/components/ui/MobileLinks.md +43 -0
- package/docs/components/ui/Modal.md +60 -0
- package/docs/components/ui/Tabs.md +62 -0
- package/docs/components/ui/Textarea.md +52 -0
- package/docs/core-concepts/configuration-driven.md +552 -0
- package/docs/core-concepts/overview.md +351 -0
- package/docs/features/accessibility/README.md +73 -0
- package/docs/features/accessibility/aria-support.md +177 -0
- package/docs/features/accessibility/color-contrast.md +155 -0
- package/docs/features/accessibility/focus-management.md +187 -0
- package/docs/features/accessibility/testing.md +196 -0
- package/docs/features/analytics/README.md +51 -0
- package/docs/features/analytics/ab-testing.md +171 -0
- package/docs/features/analytics/conversion-tracking.md +207 -0
- package/docs/features/analytics/custom-events.md +219 -0
- package/docs/features/analytics/privacy.md +198 -0
- package/docs/features/analytics/setup.md +114 -0
- package/docs/features/analytics/tracking-events.md +224 -0
- package/docs/features/i18n/README.md +51 -0
- package/docs/features/i18n/best-practices.md +273 -0
- package/docs/features/i18n/configuration.md +84 -0
- package/docs/features/i18n/formatting.md +133 -0
- package/docs/features/i18n/locale-detection.md +122 -0
- package/docs/features/i18n/routing.md +99 -0
- package/docs/features/i18n/rtl-support.md +191 -0
- package/docs/features/i18n/translations.md +129 -0
- package/docs/features/internationalization.md +595 -0
- package/docs/features/performance/README.md +77 -0
- package/docs/features/performance/bundle-size.md +134 -0
- package/docs/features/performance/caching.md +131 -0
- package/docs/features/performance/code-splitting.md +121 -0
- package/docs/features/performance/image-optimization.md +110 -0
- package/docs/features/performance/lazy-loading.md +92 -0
- package/docs/features/performance/monitoring.md +148 -0
- package/docs/features/seo/README.md +51 -0
- package/docs/features/seo/best-practices.md +184 -0
- package/docs/features/seo/canonical-urls.md +182 -0
- package/docs/features/seo/meta-tags.md +126 -0
- package/docs/features/seo/open-graph.md +166 -0
- package/docs/features/seo/robots-txt.md +146 -0
- package/docs/features/seo/sitemaps.md +162 -0
- package/docs/features/seo/structured-data.md +166 -0
- package/docs/getting-started/installation.md +292 -0
- package/docs/getting-started/introduction.md +195 -0
- package/docs/getting-started/quick-start.md +460 -0
- package/docs/guides/analytics-setup.md +616 -0
- package/docs/i18n/CONFIGURATION.md +353 -0
- package/docs/i18n/EXAMPLES.md +402 -0
- package/docs/i18n/MIGRATION.md +260 -0
- package/docs/i18n/SEO.md +392 -0
- package/docs/i18n/STATIC-GENERATION-FIX.md +71 -0
- package/docs/migration/changelog.md +136 -0
- package/docs/migration/overview.md +233 -0
- package/docs/recipes/adding-animations.md +475 -0
- package/docs/recipes/forms-with-validation.md +393 -0
- package/package.json +152 -0
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
# Color Contrast
|
|
2
|
+
|
|
3
|
+
Ensure text and interactive elements meet contrast requirements.
|
|
4
|
+
|
|
5
|
+
## WCAG Contrast Requirements
|
|
6
|
+
|
|
7
|
+
### AA (Minimum)
|
|
8
|
+
|
|
9
|
+
| Element | Ratio Required |
|
|
10
|
+
|---------|---------------|
|
|
11
|
+
| Normal text (< 18px) | 4.5:1 |
|
|
12
|
+
| Large text (>= 18px bold, >= 24px) | 3:1 |
|
|
13
|
+
| UI components and graphical objects | 3:1 |
|
|
14
|
+
|
|
15
|
+
### AAA (Enhanced)
|
|
16
|
+
|
|
17
|
+
| Element | Ratio Required |
|
|
18
|
+
|---------|---------------|
|
|
19
|
+
| Normal text | 7:1 |
|
|
20
|
+
| Large text | 4.5:1 |
|
|
21
|
+
|
|
22
|
+
## Framework Theme Defaults
|
|
23
|
+
|
|
24
|
+
The default theme colors are designed to meet WCAG AA:
|
|
25
|
+
|
|
26
|
+
```typescript
|
|
27
|
+
theme: {
|
|
28
|
+
primary: '#0066CC', // Tested against white background
|
|
29
|
+
secondary: '#FF6600', // Tested against white background
|
|
30
|
+
text: '#1a1a1a', // High contrast on white
|
|
31
|
+
background: '#ffffff',
|
|
32
|
+
}
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Checking Contrast
|
|
36
|
+
|
|
37
|
+
### Browser DevTools
|
|
38
|
+
|
|
39
|
+
1. Open DevTools > Elements
|
|
40
|
+
2. Select a text element
|
|
41
|
+
3. Click the color swatch in Styles
|
|
42
|
+
4. DevTools shows contrast ratio and pass/fail
|
|
43
|
+
|
|
44
|
+
### Online Tools
|
|
45
|
+
|
|
46
|
+
- [WebAIM Contrast Checker](https://webaim.org/resources/contrastchecker/)
|
|
47
|
+
- [Coolors Contrast Checker](https://coolors.co/contrast-checker)
|
|
48
|
+
|
|
49
|
+
### Programmatic Checks
|
|
50
|
+
|
|
51
|
+
```typescript
|
|
52
|
+
// Calculate contrast ratio
|
|
53
|
+
function getContrastRatio(color1: string, color2: string): number {
|
|
54
|
+
const lum1 = getLuminance(color1);
|
|
55
|
+
const lum2 = getLuminance(color2);
|
|
56
|
+
const lightest = Math.max(lum1, lum2);
|
|
57
|
+
const darkest = Math.min(lum1, lum2);
|
|
58
|
+
return (lightest + 0.05) / (darkest + 0.05);
|
|
59
|
+
}
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## Common Contrast Issues
|
|
63
|
+
|
|
64
|
+
### Light Gray Text
|
|
65
|
+
|
|
66
|
+
```css
|
|
67
|
+
/* ❌ Fails - 2.4:1 ratio */
|
|
68
|
+
color: #999999;
|
|
69
|
+
background: #ffffff;
|
|
70
|
+
|
|
71
|
+
/* ✅ Passes - 4.6:1 ratio */
|
|
72
|
+
color: #767676;
|
|
73
|
+
background: #ffffff;
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
### Colored Backgrounds
|
|
77
|
+
|
|
78
|
+
```css
|
|
79
|
+
/* ❌ May fail - check contrast */
|
|
80
|
+
color: #ffffff;
|
|
81
|
+
background: #66ccff;
|
|
82
|
+
|
|
83
|
+
/* ✅ Better - darker background */
|
|
84
|
+
color: #ffffff;
|
|
85
|
+
background: #0066cc;
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
### Placeholder Text
|
|
89
|
+
|
|
90
|
+
```css
|
|
91
|
+
/* Input placeholders need 4.5:1 too */
|
|
92
|
+
/* ❌ Too light */
|
|
93
|
+
::placeholder { color: #cccccc; }
|
|
94
|
+
|
|
95
|
+
/* ✅ Sufficient contrast */
|
|
96
|
+
::placeholder { color: #767676; }
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
## Focus Indicators
|
|
100
|
+
|
|
101
|
+
Focus indicators must be visible against all backgrounds:
|
|
102
|
+
|
|
103
|
+
```css
|
|
104
|
+
/* Framework default focus styles */
|
|
105
|
+
:focus-visible {
|
|
106
|
+
outline: 2px solid #0066CC;
|
|
107
|
+
outline-offset: 2px;
|
|
108
|
+
}
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
Ensure the focus indicator has at least 3:1 contrast against adjacent colors.
|
|
112
|
+
|
|
113
|
+
## Theme Customization
|
|
114
|
+
|
|
115
|
+
When customizing theme colors, verify contrast:
|
|
116
|
+
|
|
117
|
+
```typescript
|
|
118
|
+
// ✅ Good - verify before setting
|
|
119
|
+
const theme = {
|
|
120
|
+
primary: '#0055AA', // 7.1:1 on white ✅
|
|
121
|
+
primaryText: '#ffffff', // Check against primary bg
|
|
122
|
+
text: '#333333', // 12.6:1 on white ✅
|
|
123
|
+
mutedText: '#666666', // 5.7:1 on white ✅
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
// ❌ Bad - insufficient contrast
|
|
127
|
+
const theme = {
|
|
128
|
+
primary: '#99ccff', // 1.8:1 on white ❌
|
|
129
|
+
text: '#aaaaaa', // 2.3:1 on white ❌
|
|
130
|
+
};
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
## Dark Mode Considerations
|
|
134
|
+
|
|
135
|
+
If implementing dark mode:
|
|
136
|
+
|
|
137
|
+
- Light text on dark backgrounds still needs 4.5:1
|
|
138
|
+
- Test both themes for contrast compliance
|
|
139
|
+
- Shadows and borders may need adjustment
|
|
140
|
+
- Images with text overlays need extra care
|
|
141
|
+
|
|
142
|
+
## Best Practices
|
|
143
|
+
|
|
144
|
+
- Test all color combinations in your design
|
|
145
|
+
- Don't rely on color alone to convey information (use icons, patterns, or text)
|
|
146
|
+
- Test with color blindness simulators
|
|
147
|
+
- Provide sufficient contrast for all text sizes
|
|
148
|
+
- Check contrast of error states, hover states, and disabled states
|
|
149
|
+
- Verify contrast on actual devices (screens vary)
|
|
150
|
+
|
|
151
|
+
## See Also
|
|
152
|
+
|
|
153
|
+
- [WCAG Compliance](../../accessibility/wcag-compliance.md)
|
|
154
|
+
- [Focus Management](./focus-management.md)
|
|
155
|
+
- [Testing](./testing.md)
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
# Focus Management
|
|
2
|
+
|
|
3
|
+
Handle keyboard focus for accessible navigation.
|
|
4
|
+
|
|
5
|
+
## Focus Indicators
|
|
6
|
+
|
|
7
|
+
All interactive elements must have visible focus indicators:
|
|
8
|
+
|
|
9
|
+
```css
|
|
10
|
+
/* Framework default */
|
|
11
|
+
:focus-visible {
|
|
12
|
+
outline: 2px solid #0066CC;
|
|
13
|
+
outline-offset: 2px;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/* Never remove outlines without replacement */
|
|
17
|
+
/* ❌ Bad */
|
|
18
|
+
:focus { outline: none; }
|
|
19
|
+
|
|
20
|
+
/* ✅ Good - custom focus style */
|
|
21
|
+
:focus-visible {
|
|
22
|
+
outline: none;
|
|
23
|
+
box-shadow: 0 0 0 3px rgba(0, 102, 204, 0.5);
|
|
24
|
+
}
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Focus Trapping
|
|
28
|
+
|
|
29
|
+
### Modal Focus Trap
|
|
30
|
+
|
|
31
|
+
The Modal component automatically traps focus:
|
|
32
|
+
|
|
33
|
+
```typescript
|
|
34
|
+
import { Modal } from '@zoyth/simple-site-framework/components';
|
|
35
|
+
|
|
36
|
+
<Modal isOpen={isOpen} onClose={handleClose}>
|
|
37
|
+
{/* Focus is trapped within the modal */}
|
|
38
|
+
<h2>Modal Title</h2>
|
|
39
|
+
<p>Content</p>
|
|
40
|
+
<Button onClick={handleClose}>Close</Button>
|
|
41
|
+
</Modal>
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
When a modal opens:
|
|
45
|
+
1. Focus moves to first focusable element inside
|
|
46
|
+
2. Tab cycles through modal elements only
|
|
47
|
+
3. Shift+Tab cycles backward
|
|
48
|
+
4. Escape closes the modal
|
|
49
|
+
5. Focus returns to the element that opened it
|
|
50
|
+
|
|
51
|
+
### ExitIntentModal
|
|
52
|
+
|
|
53
|
+
Same focus trapping behavior:
|
|
54
|
+
|
|
55
|
+
```typescript
|
|
56
|
+
<ExitIntentModal isOpen={showModal} onClose={() => setShowModal(false)}>
|
|
57
|
+
<h2>Wait! Before you go...</h2>
|
|
58
|
+
<Button>Get 10% Off</Button>
|
|
59
|
+
</ExitIntentModal>
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## Focus Restoration
|
|
63
|
+
|
|
64
|
+
After closing a modal or dropdown, focus should return to the trigger:
|
|
65
|
+
|
|
66
|
+
```typescript
|
|
67
|
+
function MenuButton() {
|
|
68
|
+
const buttonRef = useRef<HTMLButtonElement>(null);
|
|
69
|
+
const [isOpen, setIsOpen] = useState(false);
|
|
70
|
+
|
|
71
|
+
function handleClose() {
|
|
72
|
+
setIsOpen(false);
|
|
73
|
+
// Return focus to trigger
|
|
74
|
+
buttonRef.current?.focus();
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return (
|
|
78
|
+
<>
|
|
79
|
+
<button ref={buttonRef} onClick={() => setIsOpen(true)}>
|
|
80
|
+
Open Menu
|
|
81
|
+
</button>
|
|
82
|
+
{isOpen && <Menu onClose={handleClose} />}
|
|
83
|
+
</>
|
|
84
|
+
);
|
|
85
|
+
}
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
## Skip Links
|
|
89
|
+
|
|
90
|
+
Allow keyboard users to skip past navigation:
|
|
91
|
+
|
|
92
|
+
```typescript
|
|
93
|
+
// Framework includes skip link functionality
|
|
94
|
+
<a href="#main-content" className="sr-only focus:not-sr-only">
|
|
95
|
+
Skip to main content
|
|
96
|
+
</a>
|
|
97
|
+
|
|
98
|
+
<nav>...</nav>
|
|
99
|
+
|
|
100
|
+
<main id="main-content">
|
|
101
|
+
{/* Page content */}
|
|
102
|
+
</main>
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
## Focus Order
|
|
106
|
+
|
|
107
|
+
Ensure logical tab order matches visual order:
|
|
108
|
+
|
|
109
|
+
```typescript
|
|
110
|
+
// ✅ Good - natural DOM order matches visual layout
|
|
111
|
+
<header>
|
|
112
|
+
<nav>
|
|
113
|
+
<a href="/">Home</a>
|
|
114
|
+
<a href="/about">About</a>
|
|
115
|
+
<a href="/contact">Contact</a>
|
|
116
|
+
</nav>
|
|
117
|
+
</header>
|
|
118
|
+
<main>...</main>
|
|
119
|
+
<footer>...</footer>
|
|
120
|
+
|
|
121
|
+
// ❌ Bad - tabIndex overrides break natural order
|
|
122
|
+
<div tabIndex={3}>Third visually, first by tab</div>
|
|
123
|
+
<div tabIndex={1}>First visually, third by tab</div>
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
### tabIndex Guidelines
|
|
127
|
+
|
|
128
|
+
| Value | Behavior |
|
|
129
|
+
|-------|----------|
|
|
130
|
+
| `0` | Element is focusable in natural DOM order |
|
|
131
|
+
| `-1` | Focusable programmatically, not by Tab key |
|
|
132
|
+
| `> 0` | Avoid - breaks natural order |
|
|
133
|
+
|
|
134
|
+
## Managing Focus on Route Changes
|
|
135
|
+
|
|
136
|
+
In single-page applications, manage focus when content changes:
|
|
137
|
+
|
|
138
|
+
```typescript
|
|
139
|
+
'use client';
|
|
140
|
+
import { useEffect, useRef } from 'react';
|
|
141
|
+
import { usePathname } from 'next/navigation';
|
|
142
|
+
|
|
143
|
+
export function FocusManager() {
|
|
144
|
+
const pathname = usePathname();
|
|
145
|
+
const mainRef = useRef<HTMLElement>(null);
|
|
146
|
+
|
|
147
|
+
useEffect(() => {
|
|
148
|
+
// Move focus to main content on route change
|
|
149
|
+
mainRef.current?.focus();
|
|
150
|
+
}, [pathname]);
|
|
151
|
+
|
|
152
|
+
return <main ref={mainRef} tabIndex={-1}>{/* content */}</main>;
|
|
153
|
+
}
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
## Roving tabIndex
|
|
157
|
+
|
|
158
|
+
For component groups (tabs, toolbars), use roving tabIndex:
|
|
159
|
+
|
|
160
|
+
```typescript
|
|
161
|
+
// Only one item in the group is tabbable at a time
|
|
162
|
+
// Arrow keys move focus within the group
|
|
163
|
+
|
|
164
|
+
<div role="tablist">
|
|
165
|
+
<button role="tab" tabIndex={activeTab === 0 ? 0 : -1}>Tab 1</button>
|
|
166
|
+
<button role="tab" tabIndex={activeTab === 1 ? 0 : -1}>Tab 2</button>
|
|
167
|
+
<button role="tab" tabIndex={activeTab === 2 ? 0 : -1}>Tab 3</button>
|
|
168
|
+
</div>
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
The Tabs component implements this pattern automatically.
|
|
172
|
+
|
|
173
|
+
## Best Practices
|
|
174
|
+
|
|
175
|
+
- Never remove focus indicators without providing a visible alternative
|
|
176
|
+
- Use `:focus-visible` instead of `:focus` to avoid showing outlines on mouse clicks
|
|
177
|
+
- Always return focus to the trigger when closing overlays
|
|
178
|
+
- Keep tab order logical and predictable
|
|
179
|
+
- Use `tabIndex={-1}` for programmatic focus targets
|
|
180
|
+
- Avoid positive `tabIndex` values
|
|
181
|
+
- Test entire pages with keyboard-only navigation
|
|
182
|
+
|
|
183
|
+
## See Also
|
|
184
|
+
|
|
185
|
+
- [Keyboard Navigation](../../accessibility/keyboard-navigation.md)
|
|
186
|
+
- [ARIA Support](./aria-support.md)
|
|
187
|
+
- [Modal Component](../../components/ui/Modal.md)
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
# Accessibility Testing
|
|
2
|
+
|
|
3
|
+
Test your site for accessibility compliance.
|
|
4
|
+
|
|
5
|
+
## Automated Testing
|
|
6
|
+
|
|
7
|
+
### axe-core
|
|
8
|
+
|
|
9
|
+
Run automated accessibility audits:
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
npm install -D @axe-core/react
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
```typescript
|
|
16
|
+
// Enable in development
|
|
17
|
+
if (process.env.NODE_ENV === 'development') {
|
|
18
|
+
const axe = require('@axe-core/react');
|
|
19
|
+
const React = require('react');
|
|
20
|
+
const ReactDOM = require('react-dom');
|
|
21
|
+
axe(React, ReactDOM, 1000);
|
|
22
|
+
}
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
### eslint-plugin-jsx-a11y
|
|
26
|
+
|
|
27
|
+
Catch accessibility issues at lint time:
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
npm install -D eslint-plugin-jsx-a11y
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
```json
|
|
34
|
+
// .eslintrc.json
|
|
35
|
+
{
|
|
36
|
+
"extends": ["plugin:jsx-a11y/recommended"]
|
|
37
|
+
}
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
Catches issues like:
|
|
41
|
+
- Missing alt text on images
|
|
42
|
+
- Missing labels on form inputs
|
|
43
|
+
- Invalid ARIA attributes
|
|
44
|
+
- Incorrect role usage
|
|
45
|
+
|
|
46
|
+
### Lighthouse
|
|
47
|
+
|
|
48
|
+
Built into Chrome DevTools:
|
|
49
|
+
|
|
50
|
+
1. Open DevTools > Lighthouse
|
|
51
|
+
2. Check "Accessibility"
|
|
52
|
+
3. Run audit
|
|
53
|
+
4. Review results and recommendations
|
|
54
|
+
|
|
55
|
+
## Manual Testing
|
|
56
|
+
|
|
57
|
+
Automated tools catch ~30% of accessibility issues. Manual testing is essential.
|
|
58
|
+
|
|
59
|
+
### Keyboard Testing
|
|
60
|
+
|
|
61
|
+
Test every page with keyboard only:
|
|
62
|
+
|
|
63
|
+
1. Unplug your mouse
|
|
64
|
+
2. Navigate using Tab, Shift+Tab, Enter, Space, Escape, Arrow keys
|
|
65
|
+
3. Verify:
|
|
66
|
+
- [ ] Can reach all interactive elements
|
|
67
|
+
- [ ] Focus indicator is visible
|
|
68
|
+
- [ ] Tab order is logical
|
|
69
|
+
- [ ] Modals trap focus
|
|
70
|
+
- [ ] Focus returns after closing overlays
|
|
71
|
+
- [ ] No keyboard traps
|
|
72
|
+
|
|
73
|
+
### Screen Reader Testing
|
|
74
|
+
|
|
75
|
+
Test with actual screen readers:
|
|
76
|
+
|
|
77
|
+
| Platform | Screen Reader | Browser |
|
|
78
|
+
|----------|--------------|---------|
|
|
79
|
+
| macOS | VoiceOver | Safari |
|
|
80
|
+
| Windows | NVDA (free) | Firefox |
|
|
81
|
+
| Windows | JAWS | Chrome/Edge |
|
|
82
|
+
| iOS | VoiceOver | Safari |
|
|
83
|
+
| Android | TalkBack | Chrome |
|
|
84
|
+
|
|
85
|
+
#### VoiceOver Quick Start (macOS)
|
|
86
|
+
|
|
87
|
+
1. Press `Cmd + F5` to enable VoiceOver
|
|
88
|
+
2. Use `VO + Right Arrow` to move forward (`VO` = Ctrl + Option)
|
|
89
|
+
3. Use `VO + Left Arrow` to move backward
|
|
90
|
+
4. Press `VO + Space` to activate
|
|
91
|
+
5. Press `Cmd + F5` again to disable
|
|
92
|
+
|
|
93
|
+
#### What to Check
|
|
94
|
+
|
|
95
|
+
- [ ] Page title is announced on load
|
|
96
|
+
- [ ] Headings create a logical outline
|
|
97
|
+
- [ ] Images have descriptive alt text
|
|
98
|
+
- [ ] Form labels are associated with inputs
|
|
99
|
+
- [ ] Error messages are announced
|
|
100
|
+
- [ ] Dynamic content changes are announced
|
|
101
|
+
- [ ] Navigation landmarks are present
|
|
102
|
+
|
|
103
|
+
### Zoom Testing
|
|
104
|
+
|
|
105
|
+
1. Zoom to 200% (Cmd/Ctrl + =)
|
|
106
|
+
2. Verify no content is cut off
|
|
107
|
+
3. Verify no horizontal scrolling on main content
|
|
108
|
+
4. Zoom to 400% for WCAG AAA
|
|
109
|
+
|
|
110
|
+
### Color Testing
|
|
111
|
+
|
|
112
|
+
Test for color blindness:
|
|
113
|
+
|
|
114
|
+
- Chrome DevTools > Rendering > Emulate vision deficiencies
|
|
115
|
+
- Select each deficiency type and verify usability
|
|
116
|
+
- Ensure information isn't conveyed by color alone
|
|
117
|
+
|
|
118
|
+
## Testing Checklist
|
|
119
|
+
|
|
120
|
+
### Per Component
|
|
121
|
+
|
|
122
|
+
- [ ] Keyboard accessible
|
|
123
|
+
- [ ] Screen reader announces correctly
|
|
124
|
+
- [ ] ARIA attributes present and valid
|
|
125
|
+
- [ ] Focus management works
|
|
126
|
+
- [ ] Color contrast passes 4.5:1
|
|
127
|
+
|
|
128
|
+
### Per Page
|
|
129
|
+
|
|
130
|
+
- [ ] Single `<h1>` element
|
|
131
|
+
- [ ] Heading hierarchy is logical (no skipped levels)
|
|
132
|
+
- [ ] All images have alt text
|
|
133
|
+
- [ ] All form inputs have labels
|
|
134
|
+
- [ ] Language attribute set on `<html>`
|
|
135
|
+
- [ ] Landmark regions present (banner, nav, main, contentinfo)
|
|
136
|
+
- [ ] Skip link works
|
|
137
|
+
- [ ] Page title is descriptive
|
|
138
|
+
|
|
139
|
+
### Per Site
|
|
140
|
+
|
|
141
|
+
- [ ] Consistent navigation across pages
|
|
142
|
+
- [ ] Error pages are accessible
|
|
143
|
+
- [ ] 404 page is accessible
|
|
144
|
+
- [ ] All PDF/document downloads are accessible
|
|
145
|
+
- [ ] Video content has captions
|
|
146
|
+
- [ ] Audio content has transcripts
|
|
147
|
+
|
|
148
|
+
## CI/CD Integration
|
|
149
|
+
|
|
150
|
+
### axe in Tests
|
|
151
|
+
|
|
152
|
+
```typescript
|
|
153
|
+
import { axe, toHaveNoViolations } from 'jest-axe';
|
|
154
|
+
|
|
155
|
+
expect.extend(toHaveNoViolations);
|
|
156
|
+
|
|
157
|
+
it('has no accessibility violations', async () => {
|
|
158
|
+
const { container } = render(<HeroSection heading="Test" />);
|
|
159
|
+
const results = await axe(container);
|
|
160
|
+
expect(results).toHaveNoViolations();
|
|
161
|
+
});
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
### Pa11y in CI
|
|
165
|
+
|
|
166
|
+
```bash
|
|
167
|
+
npm install -D pa11y
|
|
168
|
+
|
|
169
|
+
# Test a URL
|
|
170
|
+
npx pa11y http://localhost:3000
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
Add to CI pipeline:
|
|
174
|
+
|
|
175
|
+
```yaml
|
|
176
|
+
# .github/workflows/a11y.yml
|
|
177
|
+
- name: Accessibility test
|
|
178
|
+
run: npx pa11y http://localhost:3000 --threshold 0
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
## Reporting Issues
|
|
182
|
+
|
|
183
|
+
When you find an accessibility issue:
|
|
184
|
+
|
|
185
|
+
1. **What**: Describe the specific failure
|
|
186
|
+
2. **Where**: Page URL and element location
|
|
187
|
+
3. **WCAG Criterion**: Which guideline is violated
|
|
188
|
+
4. **Impact**: Who is affected and how
|
|
189
|
+
5. **Fix**: Suggested remediation
|
|
190
|
+
|
|
191
|
+
## See Also
|
|
192
|
+
|
|
193
|
+
- [WCAG Compliance](../../accessibility/wcag-compliance.md)
|
|
194
|
+
- [Keyboard Navigation](../../accessibility/keyboard-navigation.md)
|
|
195
|
+
- [Screen Readers](../../accessibility/screen-readers.md)
|
|
196
|
+
- [ARIA Support](./aria-support.md)
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# Analytics
|
|
2
|
+
|
|
3
|
+
Integrated analytics and tracking for conversion optimization.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
Simple Site Framework provides built-in analytics integration for tracking user behavior, conversions, and running A/B tests:
|
|
8
|
+
|
|
9
|
+
- Google Analytics 4 (GA4) integration
|
|
10
|
+
- Google Tag Manager (GTM) support
|
|
11
|
+
- Event tracking utilities
|
|
12
|
+
- A/B testing framework
|
|
13
|
+
- Conversion tracking
|
|
14
|
+
- Privacy-compliant tracking
|
|
15
|
+
|
|
16
|
+
## Quick Start
|
|
17
|
+
|
|
18
|
+
```typescript
|
|
19
|
+
import { AnalyticsTracker } from '@zoyth/simple-site-framework/components';
|
|
20
|
+
|
|
21
|
+
// Add to root layout
|
|
22
|
+
<AnalyticsTracker gtmId="GTM-XXXXXXX" />
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Track events:
|
|
26
|
+
|
|
27
|
+
```typescript
|
|
28
|
+
'use client';
|
|
29
|
+
import { trackEvent, trackCTAClick } from '@zoyth/simple-site-framework/client';
|
|
30
|
+
|
|
31
|
+
// Track custom event
|
|
32
|
+
trackEvent('button_click', { location: 'hero' });
|
|
33
|
+
|
|
34
|
+
// Track CTA click
|
|
35
|
+
trackCTAClick('signup', 'hero');
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Topics
|
|
39
|
+
|
|
40
|
+
- [Setup](./setup.md) - Initial analytics configuration
|
|
41
|
+
- [Tracking Events](./tracking-events.md) - Event tracking patterns
|
|
42
|
+
- [A/B Testing](./ab-testing.md) - Running A/B tests
|
|
43
|
+
- [Conversion Tracking](./conversion-tracking.md) - Track goals and conversions
|
|
44
|
+
- [Custom Events](./custom-events.md) - Define custom events
|
|
45
|
+
- [Privacy](./privacy.md) - GDPR and privacy compliance
|
|
46
|
+
|
|
47
|
+
## See Also
|
|
48
|
+
|
|
49
|
+
- [Analytics Setup Guide](../../guides/analytics-setup.md)
|
|
50
|
+
- [AnalyticsTracker Component](../../components/AnalyticsTracker.md)
|
|
51
|
+
- [TrackedLink Component](../../components/TrackedLink.md)
|