omnidesign 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/QUICKREF.md +150 -0
- package/README.md +576 -0
- package/bin/cli.js +390 -0
- package/bin/detect-ide.js +50 -0
- package/bin/install.js +8 -0
- package/logo.jpg +0 -0
- package/package.json +84 -0
- package/recipes/components/README.md +29 -0
- package/recipes/components/agent-card.md +314 -0
- package/recipes/components/ai-chat.md +252 -0
- package/recipes/components/bento-grid.md +186 -0
- package/recipes/components/code-block.md +503 -0
- package/recipes/components/file-upload.md +483 -0
- package/recipes/components/forms.md +238 -0
- package/recipes/components/hero-section.md +161 -0
- package/recipes/components/navbar.md +214 -0
- package/recipes/components/prompt-input.md +293 -0
- package/recipes/components/thinking-indicator.md +372 -0
- package/recipes/motion/README.md +3 -0
- package/recipes/motion/motion-system.md +437 -0
- package/recipes/patterns/README.md +3 -0
- package/skills/aider/omnidesign.md +67 -0
- package/skills/amp/SKILL.md +114 -0
- package/skills/antigravity/SKILL.md +114 -0
- package/skills/claude/omnidesign.md +111 -0
- package/skills/continue/omnidesign.yaml +29 -0
- package/skills/cursor/omnidesign.md +110 -0
- package/skills/kilo/SKILL.md +114 -0
- package/skills/opencode/omnidesign.md +110 -0
- package/skills/vscode/package.json +66 -0
- package/skills/zed/omnidesign.json +7 -0
- package/tokens/motion/README.md +3 -0
- package/tokens/primitives/README.md +3 -0
- package/tokens/primitives/color.json +219 -0
- package/tokens/primitives/motion.json +56 -0
- package/tokens/primitives/radii.json +37 -0
- package/tokens/primitives/shadows.json +34 -0
- package/tokens/primitives/spacing.json +67 -0
- package/tokens/primitives/typography.json +127 -0
- package/tokens/semantic/README.md +3 -0
- package/tokens/semantic/color.json +114 -0
- package/tokens/semantic/motion.json +44 -0
- package/tokens/semantic/radii.json +29 -0
- package/tokens/semantic/shadows.json +24 -0
- package/tokens/semantic/spacing.json +69 -0
- package/tokens/semantic/typography.json +118 -0
- package/tokens/shadows/README.md +3 -0
- package/tokens/themes/README.md +3 -0
- package/tokens/themes/berry.json +143 -0
- package/tokens/themes/brutalist.json +143 -0
- package/tokens/themes/coral.json +143 -0
- package/tokens/themes/corporate.json +143 -0
- package/tokens/themes/cream.json +143 -0
- package/tokens/themes/cyberpunk.json +143 -0
- package/tokens/themes/daylight.json +143 -0
- package/tokens/themes/deep-space.json +143 -0
- package/tokens/themes/forest.json +143 -0
- package/tokens/themes/graphite.json +143 -0
- package/tokens/themes/lavender.json +143 -0
- package/tokens/themes/midnight.json +143 -0
- package/tokens/themes/mint.json +143 -0
- package/tokens/themes/navy.json +143 -0
- package/tokens/themes/noir.json +143 -0
- package/tokens/themes/obsidian.json +143 -0
- package/tokens/themes/ocean.json +143 -0
- package/tokens/themes/paper.json +143 -0
- package/tokens/themes/ruby.json +143 -0
- package/tokens/themes/slate.json +143 -0
- package/tokens/themes/snow.json +143 -0
- package/tokens/themes/solar.json +143 -0
- package/tokens/themes/spring.json +143 -0
- package/tokens/themes/starry-night.json +143 -0
- package/tokens/themes/sunset.json +143 -0
- package/tokens/typography/FONT_GUIDE.md +381 -0
- package/tokens/typography/README.md +37 -0
- package/tokens/typography/font-collection.json +221 -0
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
# Form Patterns
|
|
2
|
+
|
|
3
|
+
Comprehensive form component patterns including input states (default, focus, error, disabled, loading), error message styling, helper text, and label positioning strategies.
|
|
4
|
+
|
|
5
|
+
## When to Use
|
|
6
|
+
|
|
7
|
+
**Use form patterns for:**
|
|
8
|
+
- User input collection (login, signup, contact)
|
|
9
|
+
- Data entry and validation
|
|
10
|
+
- Search interfaces
|
|
11
|
+
- Filter and sort controls
|
|
12
|
+
- Multi-step wizards
|
|
13
|
+
|
|
14
|
+
**Don't use for:**
|
|
15
|
+
- Simple yes/no questions (use toggle or checkbox)
|
|
16
|
+
- Single-field searches (use search input pattern)
|
|
17
|
+
- Read-only data display (use text, not form fields)
|
|
18
|
+
|
|
19
|
+
## Component Anatomy
|
|
20
|
+
|
|
21
|
+
```
|
|
22
|
+
┌─────────────────────────────────────────┐
|
|
23
|
+
│ [Label] │
|
|
24
|
+
│ [Input Field] │
|
|
25
|
+
│ [Helper Text / Error Message] │
|
|
26
|
+
│ │
|
|
27
|
+
│ Input States: │
|
|
28
|
+
│ - Default: Neutral appearance │
|
|
29
|
+
│ - Focus: Border highlight, shadow │
|
|
30
|
+
│ - Error: Red border, error icon │
|
|
31
|
+
│ - Disabled: Grayed out, no interaction │
|
|
32
|
+
│ - Loading: Spinner, disabled state │
|
|
33
|
+
└─────────────────────────────────────────┘
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## State Matrix
|
|
37
|
+
|
|
38
|
+
| State | Visual Treatment | Tokens Used |
|
|
39
|
+
|-------|------------------|-------------|
|
|
40
|
+
| **Default** | Border `color.border.default`, background `color.surface.sunken`, text `color.text.default` | `color.border.default`, `color.surface.sunken`, `color.text.default` |
|
|
41
|
+
| **Focus** | Border `color.interactive.primary`, shadow with blue tint, outline none | `color.interactive.primary`, `color.focus.ring`, `motion.hover` |
|
|
42
|
+
| **Error** | Border `color.status.error`, background tinted red, error icon visible | `color.status.error`, `color.surface.sunken`, `color.text.default` |
|
|
43
|
+
| **Disabled** | Border `color.border.subtle`, background `color.surface.sunken`, text `color.text.muted`, cursor not-allowed | `color.border.subtle`, `color.text.muted` |
|
|
44
|
+
| **Loading** | Spinner icon visible, input disabled, opacity 80% | `color.interactive.primary`, `motion.hover` |
|
|
45
|
+
| **Success** | Border `color.status.success`, checkmark icon visible | `color.status.success` |
|
|
46
|
+
|
|
47
|
+
## Token Usage
|
|
48
|
+
|
|
49
|
+
### Colors
|
|
50
|
+
- **Default Border**: `color.border.default` (neutral 200)
|
|
51
|
+
- **Focus Border**: `color.interactive.primary` (blue 500)
|
|
52
|
+
- **Error Border**: `color.status.error` (red 500)
|
|
53
|
+
- **Success Border**: `color.status.success` (green 500)
|
|
54
|
+
- **Input Background**: `color.surface.sunken` (neutral 50)
|
|
55
|
+
- **Label Text**: `color.text.default` (neutral 900)
|
|
56
|
+
- **Helper Text**: `color.text.muted` (neutral 500)
|
|
57
|
+
- **Error Text**: `color.status.error` (red 500)
|
|
58
|
+
|
|
59
|
+
### Spacing
|
|
60
|
+
- **Label Margin**: `space.stackSm` (2px) below label
|
|
61
|
+
- **Input Padding**: `space.inputPadding` (3px) internal padding
|
|
62
|
+
- **Form Field Gap**: `space.formGap` (4px) between label and input
|
|
63
|
+
- **Error Message Margin**: `space.stackSm` (2px) above error text
|
|
64
|
+
- **Helper Text Margin**: `space.stackSm` (2px) above helper text
|
|
65
|
+
|
|
66
|
+
### Typography
|
|
67
|
+
- **Label**: `typography.ui.label` (sm, medium)
|
|
68
|
+
- **Input**: `typography.body.default` (base, normal)
|
|
69
|
+
- **Helper Text**: `typography.ui.caption` (xs, normal)
|
|
70
|
+
- **Error Text**: `typography.ui.caption` (xs, normal) with `color.status.error`
|
|
71
|
+
|
|
72
|
+
### Shadows & Radii
|
|
73
|
+
- **Input Radius**: `radius.button` for rounded corners
|
|
74
|
+
- **Focus Shadow**: Subtle shadow with blue tint (0 0 0 3px rgba(59, 130, 246, 0.1))
|
|
75
|
+
|
|
76
|
+
## Implementation Notes
|
|
77
|
+
|
|
78
|
+
### CSS/HTML Approach
|
|
79
|
+
|
|
80
|
+
**Structure:**
|
|
81
|
+
```
|
|
82
|
+
<div class="form-field">
|
|
83
|
+
<label for="email" class="form-field__label">Email Address</label>
|
|
84
|
+
<div class="form-field__input-wrapper">
|
|
85
|
+
<input
|
|
86
|
+
type="email"
|
|
87
|
+
id="email"
|
|
88
|
+
class="form-field__input"
|
|
89
|
+
placeholder="you@example.com"
|
|
90
|
+
aria-describedby="email-helper"
|
|
91
|
+
required
|
|
92
|
+
>
|
|
93
|
+
<span class="form-field__icon"></span>
|
|
94
|
+
</div>
|
|
95
|
+
<p id="email-helper" class="form-field__helper">We'll never share your email</p>
|
|
96
|
+
</div>
|
|
97
|
+
|
|
98
|
+
<!-- Error state -->
|
|
99
|
+
<div class="form-field form-field--error">
|
|
100
|
+
<label for="password" class="form-field__label">Password</label>
|
|
101
|
+
<div class="form-field__input-wrapper">
|
|
102
|
+
<input
|
|
103
|
+
type="password"
|
|
104
|
+
id="password"
|
|
105
|
+
class="form-field__input"
|
|
106
|
+
aria-describedby="password-error"
|
|
107
|
+
aria-invalid="true"
|
|
108
|
+
>
|
|
109
|
+
<span class="form-field__icon form-field__icon--error"></span>
|
|
110
|
+
</div>
|
|
111
|
+
<p id="password-error" class="form-field__error">Password must be at least 8 characters</p>
|
|
112
|
+
</div>
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
**Key CSS patterns:**
|
|
116
|
+
- Use `display: flex; flex-direction: column` for vertical label-input stacking
|
|
117
|
+
- Apply `border: 1px solid color.border.default` to input
|
|
118
|
+
- Use `padding: space.inputPadding` for internal spacing
|
|
119
|
+
- Implement focus state with `border-color: color.interactive.primary` and `box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1)`
|
|
120
|
+
- Use `transition: border-color 200ms ease-out, box-shadow 200ms ease-out` for smooth focus
|
|
121
|
+
- Apply `background: color.surface.sunken` for input background
|
|
122
|
+
- Use `color: color.text.muted` for placeholder text
|
|
123
|
+
- Implement error state with `border-color: color.status.error` and `color: color.status.error` for error text
|
|
124
|
+
- Use `cursor: not-allowed; opacity: 0.6` for disabled state
|
|
125
|
+
- Implement loading state with spinner icon and `pointer-events: none`
|
|
126
|
+
|
|
127
|
+
**Validation patterns:**
|
|
128
|
+
- Show error message only when field is invalid AND touched (not on initial load)
|
|
129
|
+
- Use `aria-invalid="true"` to announce error state to screen readers
|
|
130
|
+
- Use `aria-describedby` to link input to helper or error text
|
|
131
|
+
- Validate on blur (not on every keystroke) to avoid overwhelming user
|
|
132
|
+
|
|
133
|
+
**Label positioning:**
|
|
134
|
+
- Default: Label above input (vertical stack)
|
|
135
|
+
- Alternative: Label inside input (floating label) — requires more complex CSS
|
|
136
|
+
- Alternative: Label to left of input (horizontal) — use for compact forms
|
|
137
|
+
|
|
138
|
+
### React Approach
|
|
139
|
+
|
|
140
|
+
**Component structure:**
|
|
141
|
+
- Create `<FormField>` wrapper managing label, input, helper text, and error state
|
|
142
|
+
- Create `<Input>` component accepting `type`, `placeholder`, `error`, `disabled`, `loading` props
|
|
143
|
+
- Use `useState` for input value and touched state
|
|
144
|
+
- Implement `onBlur` to set touched state and trigger validation
|
|
145
|
+
- Use `useCallback` for validation logic
|
|
146
|
+
|
|
147
|
+
**State management:**
|
|
148
|
+
- Track input value with `useState`
|
|
149
|
+
- Track touched state with `useState` (show errors only when touched)
|
|
150
|
+
- Track validation errors with `useState` or validation library (Zod, Yup)
|
|
151
|
+
- Use `useEffect` to validate on value change (debounced)
|
|
152
|
+
|
|
153
|
+
**Validation:**
|
|
154
|
+
- Validate on blur (not on every keystroke)
|
|
155
|
+
- Show error message only when field is touched AND invalid
|
|
156
|
+
- Use validation library (Zod, React Hook Form) for consistency
|
|
157
|
+
- Provide real-time feedback for async validation (email availability, username)
|
|
158
|
+
|
|
159
|
+
**Accessibility:**
|
|
160
|
+
- Use `<label>` with `htmlFor` attribute
|
|
161
|
+
- Use `aria-invalid="true"` when field has error
|
|
162
|
+
- Use `aria-describedby` to link input to helper or error text
|
|
163
|
+
- Use `aria-required="true"` for required fields
|
|
164
|
+
- Implement focus management for error messages
|
|
165
|
+
|
|
166
|
+
## Acceptance Criteria
|
|
167
|
+
|
|
168
|
+
- [ ] Label uses `typography.ui.label` token
|
|
169
|
+
- [ ] Input uses `typography.body.default` token
|
|
170
|
+
- [ ] Default border uses `color.border.default` token
|
|
171
|
+
- [ ] Focus border uses `color.interactive.primary` token
|
|
172
|
+
- [ ] Error border uses `color.status.error` token
|
|
173
|
+
- [ ] Input background uses `color.surface.sunken` token
|
|
174
|
+
- [ ] Input padding uses `space.inputPadding` token
|
|
175
|
+
- [ ] Form field gap uses `space.formGap` token
|
|
176
|
+
- [ ] Helper text uses `typography.ui.caption` token
|
|
177
|
+
- [ ] Error text uses `color.status.error` token
|
|
178
|
+
- [ ] Focus ring visible with 3px blue shadow
|
|
179
|
+
- [ ] Error message shows only when field is touched AND invalid
|
|
180
|
+
- [ ] Disabled state shows cursor not-allowed and reduced opacity
|
|
181
|
+
- [ ] Loading state shows spinner and disables input
|
|
182
|
+
- [ ] Respects `prefers-reduced-motion` (instant state changes)
|
|
183
|
+
- [ ] All text passes color contrast ratio (4.5:1 for body, 3:1 for large text)
|
|
184
|
+
|
|
185
|
+
## Accessibility
|
|
186
|
+
|
|
187
|
+
### ARIA & Semantics
|
|
188
|
+
- Use `<label>` with `htmlFor` attribute (not placeholder-only labels)
|
|
189
|
+
- Use `<input>` with appropriate `type` attribute (email, password, number, etc.)
|
|
190
|
+
- Use `aria-invalid="true"` when field has validation error
|
|
191
|
+
- Use `aria-describedby` to link input to helper or error text
|
|
192
|
+
- Use `aria-required="true"` for required fields
|
|
193
|
+
- Use `aria-label` for icon-only inputs (if no visible label)
|
|
194
|
+
|
|
195
|
+
### Keyboard Navigation
|
|
196
|
+
- Tab order: Label (not focusable) → Input → Helper/Error text (not focusable)
|
|
197
|
+
- Focus ring must be visible with 3px shadow, `color.focus.ring`
|
|
198
|
+
- Ensure focus ring has sufficient contrast against input background
|
|
199
|
+
- Support arrow keys for number inputs
|
|
200
|
+
- Support Escape to clear input (optional)
|
|
201
|
+
|
|
202
|
+
### Screen Readers
|
|
203
|
+
- Label text must be descriptive and associated with input
|
|
204
|
+
- Error message must be announced when field becomes invalid
|
|
205
|
+
- Helper text must be announced to provide context
|
|
206
|
+
- Required field must be announced with `aria-required="true"`
|
|
207
|
+
- Input type must be announced (email, password, etc.)
|
|
208
|
+
|
|
209
|
+
### Color & Contrast
|
|
210
|
+
- Text must meet WCAG AA (4.5:1 for body, 3:1 for large text)
|
|
211
|
+
- Error color must have sufficient contrast against background
|
|
212
|
+
- Don't rely on color alone to indicate error (use icon + text)
|
|
213
|
+
- Ensure sufficient contrast between input text and background
|
|
214
|
+
|
|
215
|
+
### Motion
|
|
216
|
+
- Respect `prefers-reduced-motion: reduce` by disabling animations
|
|
217
|
+
- Focus transition should be instant if reduced motion is preferred
|
|
218
|
+
- Loading spinner should not animate if reduced motion is preferred
|
|
219
|
+
|
|
220
|
+
### Touch Targets
|
|
221
|
+
- Input minimum height 44px for touch targets
|
|
222
|
+
- Label minimum 44px tall for touch targets
|
|
223
|
+
- Ensure sufficient spacing between form fields for touch accuracy
|
|
224
|
+
|
|
225
|
+
### Validation Patterns
|
|
226
|
+
- Show errors only when field is touched (not on initial load)
|
|
227
|
+
- Provide clear, actionable error messages
|
|
228
|
+
- Suggest corrections when possible (e.g., "Did you mean...?")
|
|
229
|
+
- Allow user to submit form even with errors (show summary at top)
|
|
230
|
+
- Validate on blur, not on every keystroke
|
|
231
|
+
- Support async validation (email availability, username) with loading state
|
|
232
|
+
|
|
233
|
+
### Mobile Considerations
|
|
234
|
+
- Use appropriate input types (email, tel, number) for mobile keyboards
|
|
235
|
+
- Ensure input height minimum 44px for touch
|
|
236
|
+
- Avoid auto-capitalization for email/username fields
|
|
237
|
+
- Disable auto-zoom on focus (use font-size >= 16px)
|
|
238
|
+
- Test on actual mobile devices for keyboard behavior
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
# Hero Section
|
|
2
|
+
|
|
3
|
+
A cinematic landing page hero with compelling typography hierarchy, dual call-to-action buttons, and atmospheric background treatment. Designed to capture attention and drive conversion.
|
|
4
|
+
|
|
5
|
+
## When to Use
|
|
6
|
+
|
|
7
|
+
**Use hero sections for:**
|
|
8
|
+
- Landing page above-the-fold content
|
|
9
|
+
- Product launch announcements
|
|
10
|
+
- Campaign entry points
|
|
11
|
+
- High-impact page introductions
|
|
12
|
+
|
|
13
|
+
**Don't use for:**
|
|
14
|
+
- Secondary pages or internal tools
|
|
15
|
+
- Content-heavy pages requiring immediate scrolling
|
|
16
|
+
- Mobile-first experiences with limited viewport height
|
|
17
|
+
- Accessibility-critical interfaces (use simpler patterns)
|
|
18
|
+
|
|
19
|
+
## Component Anatomy
|
|
20
|
+
|
|
21
|
+
```
|
|
22
|
+
┌─────────────────────────────────────────┐
|
|
23
|
+
│ [Gradient/Atmospheric Background] │
|
|
24
|
+
│ │
|
|
25
|
+
│ [Hero Heading] │
|
|
26
|
+
│ (typography.heading.hero) │
|
|
27
|
+
│ │
|
|
28
|
+
│ [Subtitle/Subheading] │
|
|
29
|
+
│ (typography.body.large, muted) │
|
|
30
|
+
│ │
|
|
31
|
+
│ [Body Copy - Optional] │
|
|
32
|
+
│ (typography.body.default, muted) │
|
|
33
|
+
│ │
|
|
34
|
+
│ [Primary CTA] [Secondary CTA] │
|
|
35
|
+
│ │
|
|
36
|
+
│ [Background Image/Gradient Overlay] │
|
|
37
|
+
└─────────────────────────────────────────┘
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## State Matrix
|
|
41
|
+
|
|
42
|
+
| State | Visual Treatment | Tokens Used |
|
|
43
|
+
|-------|------------------|-------------|
|
|
44
|
+
| **Default** | Hero text centered, CTAs at standard opacity, background at full saturation | `color.text.default`, `color.interactive.primary`, `color.surface.default` |
|
|
45
|
+
| **Hover (CTA)** | Primary CTA background darkens, secondary CTA gains border, slight scale (1.02x) | `color.interactive.primary.hover`, `motion.hover` |
|
|
46
|
+
| **Focus (CTA)** | 2px focus ring around button, keyboard navigation visible | `color.focus.ring`, `space.inlineSm` |
|
|
47
|
+
| **Reduced Motion** | No scale/translate animations, instant opacity changes | `motion.hover` (duration: 0ms) |
|
|
48
|
+
| **Dark Mode** | Text inverted to `color.text.inverted`, background darkened, contrast maintained | `color.text.inverted`, `color.surface.overlay` |
|
|
49
|
+
|
|
50
|
+
## Token Usage
|
|
51
|
+
|
|
52
|
+
### Typography
|
|
53
|
+
- **Hero Heading**: `typography.heading.hero` — 6xl, bold, tight line-height for maximum impact
|
|
54
|
+
- **Subtitle**: `typography.body.large` with `color.text.muted` — supports hero without overwhelming
|
|
55
|
+
- **Body Copy**: `typography.body.default` with `color.text.muted` — optional supporting text, max 120 characters
|
|
56
|
+
|
|
57
|
+
### Colors
|
|
58
|
+
- **Text**: `color.text.default` for primary heading, `color.text.muted` for supporting copy
|
|
59
|
+
- **Buttons**: Primary uses `color.interactive.primary`, secondary uses `color.interactive.secondary` with border
|
|
60
|
+
- **Background**: Gradient from `color.surface.default` to semi-transparent overlay
|
|
61
|
+
|
|
62
|
+
### Spacing
|
|
63
|
+
- **Section Padding**: `space.sectionY` (16px) top/bottom, `space.sectionX` (6px) horizontal
|
|
64
|
+
- **Text Stack**: `space.stackLg` (6px) between heading and subtitle, `space.stackMd` (4px) between subtitle and body
|
|
65
|
+
- **Button Gap**: `space.inlineMd` (4px) between primary and secondary CTA
|
|
66
|
+
|
|
67
|
+
### Shadows & Radii
|
|
68
|
+
- **Button Radius**: `radius.button` for CTA buttons
|
|
69
|
+
- **Card Shadow** (optional): `shadow.card` if hero contains a card element
|
|
70
|
+
|
|
71
|
+
## Implementation Notes
|
|
72
|
+
|
|
73
|
+
### CSS/HTML Approach
|
|
74
|
+
|
|
75
|
+
**Structure:**
|
|
76
|
+
```
|
|
77
|
+
<section class="hero">
|
|
78
|
+
<div class="hero__background"></div>
|
|
79
|
+
<div class="hero__content">
|
|
80
|
+
<h1 class="hero__heading">Main message</h1>
|
|
81
|
+
<p class="hero__subtitle">Supporting message</p>
|
|
82
|
+
<p class="hero__body">Optional body copy</p>
|
|
83
|
+
<div class="hero__ctas">
|
|
84
|
+
<button class="button button--primary">Primary CTA</button>
|
|
85
|
+
<button class="button button--secondary">Secondary CTA</button>
|
|
86
|
+
</div>
|
|
87
|
+
</div>
|
|
88
|
+
</section>
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
**Key CSS patterns:**
|
|
92
|
+
- Use `position: relative` on hero container, `position: absolute` on background for layering
|
|
93
|
+
- Apply `background: linear-gradient(135deg, color1, color2)` or `background-image: url()` with overlay
|
|
94
|
+
- Center content with `display: flex; flex-direction: column; align-items: center; justify-content: center`
|
|
95
|
+
- Use `max-width: space.containerMax` (24rem/384px) for text content to maintain readability
|
|
96
|
+
- Apply `prefers-reduced-motion: reduce` media query to disable animations
|
|
97
|
+
|
|
98
|
+
**Responsive considerations:**
|
|
99
|
+
- Desktop: Full viewport height (min-height: 100vh), centered content
|
|
100
|
+
- Tablet (768px): Reduce heading size to `typography.heading.page`, maintain full height
|
|
101
|
+
- Mobile (375px): Reduce to 60-70vh height, stack CTAs vertically, use `typography.heading.section` for heading
|
|
102
|
+
|
|
103
|
+
### React Approach
|
|
104
|
+
|
|
105
|
+
**Component structure:**
|
|
106
|
+
- Create a `<Hero>` wrapper component accepting `heading`, `subtitle`, `body`, `primaryCta`, `secondaryCta` props
|
|
107
|
+
- Use `<img>` or CSS background for background image with proper `alt` text or `aria-hidden`
|
|
108
|
+
- Implement CTA buttons as separate `<Button>` components with variant props (primary/secondary)
|
|
109
|
+
- Use CSS modules or Tailwind for styling to maintain token consistency
|
|
110
|
+
- Wrap in `<section>` with proper ARIA landmarks
|
|
111
|
+
- Consider using `IntersectionObserver` for scroll-triggered animations (fade-in on viewport entry)
|
|
112
|
+
|
|
113
|
+
**State management:**
|
|
114
|
+
- Track hover state on CTAs for visual feedback
|
|
115
|
+
- Use CSS `:hover` and `:focus-visible` for button states (prefer CSS over React state)
|
|
116
|
+
- Implement `prefers-reduced-motion` detection for animation preferences
|
|
117
|
+
|
|
118
|
+
## Acceptance Criteria
|
|
119
|
+
|
|
120
|
+
- [ ] Hero heading uses `typography.heading.hero` token
|
|
121
|
+
- [ ] Subtitle uses `typography.body.large` with `color.text.muted`
|
|
122
|
+
- [ ] Primary CTA uses `color.interactive.primary` with hover state
|
|
123
|
+
- [ ] Secondary CTA has visible border and hover treatment
|
|
124
|
+
- [ ] Focus ring visible on keyboard navigation (2px, `color.focus.ring`)
|
|
125
|
+
- [ ] Responsive: Full height on desktop, 60-70vh on mobile
|
|
126
|
+
- [ ] Text content max-width 384px (space.containerMax) for readability
|
|
127
|
+
- [ ] Background image has proper contrast with text (WCAG AA minimum)
|
|
128
|
+
- [ ] Respects `prefers-reduced-motion` media query
|
|
129
|
+
- [ ] All text passes color contrast ratio (4.5:1 for body, 3:1 for large text)
|
|
130
|
+
|
|
131
|
+
## Accessibility
|
|
132
|
+
|
|
133
|
+
### ARIA & Semantics
|
|
134
|
+
- Use `<section>` with `role="region"` and `aria-label="Hero section"` for landmark navigation
|
|
135
|
+
- Heading must be `<h1>` (only one per page)
|
|
136
|
+
- CTAs must be `<button>` or `<a>` with clear, descriptive text (not "Click here")
|
|
137
|
+
|
|
138
|
+
### Keyboard Navigation
|
|
139
|
+
- Tab order: Heading (not focusable) → Subtitle (not focusable) → Primary CTA → Secondary CTA
|
|
140
|
+
- Focus ring must be visible with minimum 2px width, `color.focus.ring`
|
|
141
|
+
- Ensure focus ring has sufficient contrast against background
|
|
142
|
+
|
|
143
|
+
### Screen Readers
|
|
144
|
+
- Provide `alt` text for background images or use `aria-hidden="true"` if decorative
|
|
145
|
+
- CTA text must be descriptive: "Get Started" not "Click"
|
|
146
|
+
- Use `aria-label` for icon-only buttons if present
|
|
147
|
+
|
|
148
|
+
### Color & Contrast
|
|
149
|
+
- Text must meet WCAG AA (4.5:1 for body, 3:1 for large text)
|
|
150
|
+
- Don't rely on color alone to convey information
|
|
151
|
+
- Ensure sufficient contrast between text and background gradient
|
|
152
|
+
- Test with tools like WebAIM Contrast Checker
|
|
153
|
+
|
|
154
|
+
### Motion
|
|
155
|
+
- Respect `prefers-reduced-motion: reduce` by disabling animations
|
|
156
|
+
- Animations should not auto-play; trigger on user interaction
|
|
157
|
+
- Avoid flashing or rapid animations (>3 per second)
|
|
158
|
+
|
|
159
|
+
### Touch Targets
|
|
160
|
+
- CTAs must be minimum 48px × 48px (touch-friendly)
|
|
161
|
+
- Maintain `space.inlineMd` (4px) gap between buttons for touch accuracy
|
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
# Navigation Bar
|
|
2
|
+
|
|
3
|
+
A responsive header component with transparent-to-solid background transition on scroll, active link styling, mobile hamburger menu, and flexible logo/nav/CTA layout.
|
|
4
|
+
|
|
5
|
+
## When to Use
|
|
6
|
+
|
|
7
|
+
**Use navbar for:**
|
|
8
|
+
- Primary site navigation on all pages
|
|
9
|
+
- Persistent header across page sections
|
|
10
|
+
- Brand/logo placement and recognition
|
|
11
|
+
- Call-to-action button placement
|
|
12
|
+
- Mobile menu access
|
|
13
|
+
|
|
14
|
+
**Don't use for:**
|
|
15
|
+
- Breadcrumb navigation (use separate component)
|
|
16
|
+
- Pagination (use separate component)
|
|
17
|
+
- Sidebar navigation (use separate component)
|
|
18
|
+
- Floating action buttons (use separate pattern)
|
|
19
|
+
|
|
20
|
+
## Component Anatomy
|
|
21
|
+
|
|
22
|
+
```
|
|
23
|
+
┌─────────────────────────────────────────────────────┐
|
|
24
|
+
│ [Logo] [Nav Items] [Secondary CTA] [Mobile Menu]│
|
|
25
|
+
│ │
|
|
26
|
+
│ Desktop Layout: │
|
|
27
|
+
│ Logo (left) | Nav Items (center) | CTA (right) │
|
|
28
|
+
│ │
|
|
29
|
+
│ Mobile Layout: │
|
|
30
|
+
│ Logo (left) | Hamburger Menu (right) │
|
|
31
|
+
│ [Expanded Menu Overlay] │
|
|
32
|
+
│ - Nav Items (stacked) │
|
|
33
|
+
│ - Secondary CTA │
|
|
34
|
+
└─────────────────────────────────────────────────────┘
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## State Matrix
|
|
38
|
+
|
|
39
|
+
| State | Visual Treatment | Tokens Used |
|
|
40
|
+
|-------|------------------|-------------|
|
|
41
|
+
| **Default (Transparent)** | Background transparent, text `color.text.default`, no shadow | `color.surface.default` (transparent), `color.text.default` |
|
|
42
|
+
| **Scrolled (Solid)** | Background `color.surface.raised`, shadow applied, text remains `color.text.default` | `color.surface.raised`, `shadow.card`, `motion.hover` |
|
|
43
|
+
| **Active Link** | Text color `color.interactive.primary`, underline or bottom border | `color.interactive.primary`, `color.border.strong` |
|
|
44
|
+
| **Hover Link** | Text color `color.interactive.primary.hover`, slight opacity increase | `color.interactive.primary.hover`, `motion.hover` |
|
|
45
|
+
| **Focus Link** | Focus ring around link (2px), keyboard navigation visible | `color.focus.ring`, `space.inlineSm` |
|
|
46
|
+
| **Mobile Menu Open** | Overlay background `color.surface.overlay` with 80% opacity, menu slides in from right | `color.surface.overlay`, `motion.enter` |
|
|
47
|
+
|
|
48
|
+
## Token Usage
|
|
49
|
+
|
|
50
|
+
### Colors
|
|
51
|
+
- **Background (Transparent)**: `color.surface.default` with 0% opacity initially
|
|
52
|
+
- **Background (Solid)**: `color.surface.raised` after scroll threshold
|
|
53
|
+
- **Text**: `color.text.default` for nav items, `color.text.muted` for secondary text
|
|
54
|
+
- **Active/Hover**: `color.interactive.primary` and `color.interactive.primary.hover`
|
|
55
|
+
- **Mobile Overlay**: `color.surface.overlay` with 80% opacity
|
|
56
|
+
|
|
57
|
+
### Spacing
|
|
58
|
+
- **Navbar Height**: 64px (desktop), 56px (mobile) — standard touch target
|
|
59
|
+
- **Horizontal Padding**: `space.sectionX` (6px) on left/right
|
|
60
|
+
- **Nav Item Gap**: `space.inlineMd` (4px) between nav items
|
|
61
|
+
- **Logo Margin**: `space.inlineLg` (6px) right margin from nav items
|
|
62
|
+
|
|
63
|
+
### Typography
|
|
64
|
+
- **Logo**: `typography.heading.card` (xl, semibold) or custom brand font
|
|
65
|
+
- **Nav Items**: `typography.ui.button` (base, medium) for consistency
|
|
66
|
+
- **Mobile Menu Items**: `typography.body.default` for readability
|
|
67
|
+
|
|
68
|
+
### Shadows & Radii
|
|
69
|
+
- **Navbar Shadow**: `shadow.card` applied when scrolled (solid state)
|
|
70
|
+
- **Mobile Menu**: No radius on overlay, but menu content can use `radius.card`
|
|
71
|
+
|
|
72
|
+
## Implementation Notes
|
|
73
|
+
|
|
74
|
+
### CSS/HTML Approach
|
|
75
|
+
|
|
76
|
+
**Structure:**
|
|
77
|
+
```
|
|
78
|
+
<nav class="navbar" role="navigation" aria-label="Main navigation">
|
|
79
|
+
<div class="navbar__container">
|
|
80
|
+
<a href="/" class="navbar__logo">Logo</a>
|
|
81
|
+
|
|
82
|
+
<ul class="navbar__items">
|
|
83
|
+
<li><a href="/about" class="navbar__link">About</a></li>
|
|
84
|
+
<li><a href="/products" class="navbar__link">Products</a></li>
|
|
85
|
+
<li><a href="/blog" class="navbar__link">Blog</a></li>
|
|
86
|
+
</ul>
|
|
87
|
+
|
|
88
|
+
<button class="navbar__cta">Get Started</button>
|
|
89
|
+
|
|
90
|
+
<button class="navbar__toggle" aria-label="Toggle menu" aria-expanded="false">
|
|
91
|
+
<span class="navbar__hamburger"></span>
|
|
92
|
+
</button>
|
|
93
|
+
</div>
|
|
94
|
+
|
|
95
|
+
<div class="navbar__mobile-menu" hidden>
|
|
96
|
+
<ul class="navbar__mobile-items">
|
|
97
|
+
<li><a href="/about">About</a></li>
|
|
98
|
+
<li><a href="/products">Products</a></li>
|
|
99
|
+
<li><a href="/blog">Blog</a></li>
|
|
100
|
+
<li><button class="navbar__cta">Get Started</button></li>
|
|
101
|
+
</ul>
|
|
102
|
+
</div>
|
|
103
|
+
</nav>
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
**Key CSS patterns:**
|
|
107
|
+
- Use `position: fixed` or `sticky` for navbar positioning
|
|
108
|
+
- Apply `background: transparent` initially, transition to `color.surface.raised` on scroll
|
|
109
|
+
- Use `transition: background-color 200ms ease-out` for smooth background change
|
|
110
|
+
- Implement scroll detection with `window.addEventListener('scroll', ...)` or `IntersectionObserver`
|
|
111
|
+
- Use `display: flex` for horizontal layout, `justify-content: space-between` for spacing
|
|
112
|
+
- Hide nav items on mobile with `display: none`, show hamburger menu
|
|
113
|
+
- Implement mobile menu with `position: fixed`, `right: 0`, `top: navbar-height`, `width: 100vw`
|
|
114
|
+
- Use `transform: translateX(100%)` for off-screen menu, `translateX(0)` when open
|
|
115
|
+
|
|
116
|
+
**Scroll detection threshold:**
|
|
117
|
+
- Trigger solid background after 50-100px scroll (adjust based on design)
|
|
118
|
+
- Use `IntersectionObserver` on a sentinel element below navbar for better performance
|
|
119
|
+
|
|
120
|
+
**Active link styling:**
|
|
121
|
+
- Compare current URL with nav item `href` to determine active state
|
|
122
|
+
- Apply `color.interactive.primary` and bottom border (2px, `color.border.strong`)
|
|
123
|
+
- Update on page load and route changes
|
|
124
|
+
|
|
125
|
+
### React Approach
|
|
126
|
+
|
|
127
|
+
**Component structure:**
|
|
128
|
+
- Create `<Navbar>` wrapper managing scroll state and mobile menu visibility
|
|
129
|
+
- Create `<NavItem>` component for individual nav links with active state detection
|
|
130
|
+
- Use `useLocation()` or similar router hook to determine active link
|
|
131
|
+
- Implement `useEffect` with scroll listener for background transition
|
|
132
|
+
- Use `useState` for mobile menu open/close state
|
|
133
|
+
|
|
134
|
+
**State management:**
|
|
135
|
+
- Track scroll position with `useEffect` and `window.addEventListener('scroll')`
|
|
136
|
+
- Use `useCallback` to debounce scroll handler (throttle to 16ms for 60fps)
|
|
137
|
+
- Track mobile menu open state with `useState`
|
|
138
|
+
- Use `useLocation()` to detect active nav item
|
|
139
|
+
|
|
140
|
+
**Responsive behavior:**
|
|
141
|
+
- Use `useMediaQuery` hook to detect mobile breakpoint (768px)
|
|
142
|
+
- Show/hide nav items and hamburger menu based on breakpoint
|
|
143
|
+
- Adjust navbar height and padding for mobile
|
|
144
|
+
|
|
145
|
+
**Accessibility:**
|
|
146
|
+
- Use `<nav>` with `role="navigation"` and `aria-label="Main navigation"`
|
|
147
|
+
- Use `<a>` for nav items with proper `href` attributes
|
|
148
|
+
- Implement `aria-current="page"` for active link
|
|
149
|
+
- Use `aria-expanded` on hamburger button to indicate menu state
|
|
150
|
+
- Manage focus when mobile menu opens (trap focus inside menu)
|
|
151
|
+
|
|
152
|
+
## Acceptance Criteria
|
|
153
|
+
|
|
154
|
+
- [ ] Navbar uses `position: fixed` or `sticky` for persistent positioning
|
|
155
|
+
- [ ] Background transitions from transparent to `color.surface.raised` on scroll
|
|
156
|
+
- [ ] Scroll threshold is 50-100px (configurable)
|
|
157
|
+
- [ ] Active link styled with `color.interactive.primary` and bottom border
|
|
158
|
+
- [ ] Hover state uses `color.interactive.primary.hover` with `motion.hover`
|
|
159
|
+
- [ ] Focus ring visible on keyboard navigation (2px, `color.focus.ring`)
|
|
160
|
+
- [ ] Mobile menu hidden by default, visible on hamburger click
|
|
161
|
+
- [ ] Mobile menu overlay uses `color.surface.overlay` with 80% opacity
|
|
162
|
+
- [ ] Navbar height: 64px desktop, 56px mobile
|
|
163
|
+
- [ ] Horizontal padding uses `space.sectionX` token
|
|
164
|
+
- [ ] Nav item gap uses `space.inlineMd` token
|
|
165
|
+
- [ ] Logo uses `typography.heading.card` or custom brand font
|
|
166
|
+
- [ ] Nav items use `typography.ui.button` token
|
|
167
|
+
- [ ] Respects `prefers-reduced-motion` (instant background change)
|
|
168
|
+
- [ ] All text passes color contrast ratio (4.5:1 for body, 3:1 for large text)
|
|
169
|
+
|
|
170
|
+
## Accessibility
|
|
171
|
+
|
|
172
|
+
### ARIA & Semantics
|
|
173
|
+
- Use `<nav>` with `role="navigation"` and `aria-label="Main navigation"`
|
|
174
|
+
- Use `<a>` for nav items with descriptive link text
|
|
175
|
+
- Use `<button>` for hamburger menu toggle with `aria-label="Toggle menu"`
|
|
176
|
+
- Implement `aria-expanded="true/false"` on hamburger button
|
|
177
|
+
- Use `aria-current="page"` for active nav item
|
|
178
|
+
|
|
179
|
+
### Keyboard Navigation
|
|
180
|
+
- Tab order: Logo (not focusable) → Nav items → CTA → Hamburger menu
|
|
181
|
+
- Focus ring must be visible with 2px width, `color.focus.ring`
|
|
182
|
+
- Ensure focus ring has sufficient contrast against navbar background
|
|
183
|
+
- Hamburger menu must be keyboard accessible
|
|
184
|
+
- Mobile menu must be closable with Escape key
|
|
185
|
+
|
|
186
|
+
### Screen Readers
|
|
187
|
+
- Nav item text must be descriptive (not "Click here")
|
|
188
|
+
- Logo link must have descriptive text or `aria-label`
|
|
189
|
+
- Hamburger button must have `aria-label="Toggle menu"`
|
|
190
|
+
- Active nav item must be announced with `aria-current="page"`
|
|
191
|
+
- Mobile menu items must be announced when menu opens
|
|
192
|
+
|
|
193
|
+
### Color & Contrast
|
|
194
|
+
- Text must meet WCAG AA (4.5:1 for body, 3:1 for large text)
|
|
195
|
+
- Active link color must have sufficient contrast
|
|
196
|
+
- Ensure sufficient contrast between text and background (both transparent and solid states)
|
|
197
|
+
- Test with WebAIM Contrast Checker
|
|
198
|
+
|
|
199
|
+
### Motion
|
|
200
|
+
- Respect `prefers-reduced-motion: reduce` by disabling animations
|
|
201
|
+
- Background transition should be instant if reduced motion is preferred
|
|
202
|
+
- Mobile menu should slide without animation if reduced motion is preferred
|
|
203
|
+
|
|
204
|
+
### Touch Targets
|
|
205
|
+
- Navbar height minimum 56px for touch targets
|
|
206
|
+
- Nav items minimum 48px × 48px for touch targets
|
|
207
|
+
- Hamburger menu button minimum 48px × 48px
|
|
208
|
+
- Maintain `space.inlineMd` (4px) gap between nav items for touch accuracy
|
|
209
|
+
|
|
210
|
+
### Mobile Considerations
|
|
211
|
+
- Ensure navbar doesn't obscure important content
|
|
212
|
+
- Mobile menu should not auto-close on link click (let user decide)
|
|
213
|
+
- Implement focus trap in mobile menu (Tab cycles within menu)
|
|
214
|
+
- Close menu when user navigates to new page
|