@mks2508/sidebar-headless 0.1.0 â 0.2.1
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/CHANGELOG.md +126 -0
- package/LICENSE +21 -21
- package/README.md +652 -120
- package/dist/MobileOptimizations.css +570 -0
- package/dist/index.cjs +53167 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.css +911 -237
- package/dist/index.css.map +1 -1
- package/dist/index.d.cts +578 -6
- package/dist/index.d.ts +578 -6
- package/dist/index.js +9252 -667
- package/dist/index.js.map +1 -1
- package/dist/tooltip-keyframes.css +329 -0
- package/package.json +106 -99
package/README.md
CHANGED
|
@@ -1,21 +1,22 @@
|
|
|
1
|
-
# @mks2508/sidebar-headless
|
|
2
|
-
|
|
3
|
-
Headless sidebar component for React with advanced animations, keyboard navigation, and full WAI-ARIA accessibility.
|
|
4
|
-
|
|
5
|
-
[](https://www.npmjs.com/package/@mks2508/sidebar-headless)
|
|
6
|
-
[](https://opensource.org/licenses/MIT)
|
|
7
|
-
|
|
1
|
+
# @mks2508/sidebar-headless
|
|
2
|
+
|
|
3
|
+
Headless sidebar component for React with advanced animations, keyboard navigation, and full WAI-ARIA accessibility.
|
|
4
|
+
|
|
5
|
+
[](https://www.npmjs.com/package/@mks2508/sidebar-headless)
|
|
6
|
+
[](https://opensource.org/licenses/MIT)
|
|
7
|
+
|
|
8
8
|
## Features
|
|
9
9
|
|
|
10
10
|
- đ¨ **Headless Architecture** - Complete control over styling and behavior
|
|
11
11
|
- â¨ī¸ **Keyboard Navigation** - Full arrow key, Home/End support
|
|
12
12
|
- âŋ **WAI-ARIA Compliant** - Full accessibility out of the box
|
|
13
13
|
- đ **Advanced Animations** - Fluid hover indicators, glassmorphism effects, 3D transforms
|
|
14
|
+
- đą **Mobile-First Bottom Navigation** - iOS 26 Safari compatible, safe area support
|
|
14
15
|
- đ¯ **TypeScript First** - Full type safety with comprehensive declarations
|
|
15
16
|
- đĒļ **Lightweight** - Minimal dependencies (only clsx + tailwind-merge)
|
|
16
17
|
- đ¨ **Dark/Light Mode** - Automatic theme support via CSS tokens
|
|
17
18
|
- đ§ **Highly Configurable** - Extensive customization options
|
|
18
|
-
|
|
19
|
+
|
|
19
20
|
## Installation
|
|
20
21
|
|
|
21
22
|
```bash
|
|
@@ -30,10 +31,10 @@ bun add @mks2508/sidebar-headless
|
|
|
30
31
|
|
|
31
32
|
### Peer Dependencies
|
|
32
33
|
|
|
33
|
-
This package requires React 18
|
|
34
|
+
This package requires React 18+ and Framer Motion:
|
|
34
35
|
|
|
35
36
|
```bash
|
|
36
|
-
npm install react react-dom
|
|
37
|
+
npm install react react-dom motion
|
|
37
38
|
```
|
|
38
39
|
|
|
39
40
|
### Optional Dependencies
|
|
@@ -43,7 +44,7 @@ For advanced liquid glass effects (optional):
|
|
|
43
44
|
```bash
|
|
44
45
|
npm install @liquid-svg-glass/core @liquid-svg-glass/react
|
|
45
46
|
```
|
|
46
|
-
|
|
47
|
+
|
|
47
48
|
## Quick Start
|
|
48
49
|
|
|
49
50
|
```tsx
|
|
@@ -53,189 +54,719 @@ import {
|
|
|
53
54
|
SidebarToggle,
|
|
54
55
|
SidebarContent,
|
|
55
56
|
SidebarItem,
|
|
56
|
-
SidebarIconLibrary
|
|
57
|
+
SidebarIconLibrary,
|
|
58
|
+
MobileBottomNav,
|
|
59
|
+
NavVariant,
|
|
60
|
+
NavItemState
|
|
57
61
|
} from '@mks2508/sidebar-headless'
|
|
58
62
|
import '@mks2508/sidebar-headless/styles.css'
|
|
59
63
|
|
|
60
64
|
function App() {
|
|
61
65
|
return (
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
<
|
|
66
|
-
<
|
|
67
|
-
<
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
66
|
+
<>
|
|
67
|
+
{/* Desktop Sidebar */}
|
|
68
|
+
<Sidebar defaultOpen={true}>
|
|
69
|
+
<SidebarNav>
|
|
70
|
+
<SidebarToggle iconLibrary={SidebarIconLibrary.LUCIDE} />
|
|
71
|
+
<SidebarContent>
|
|
72
|
+
<SidebarItem href="/" icon={<HomeIcon />} label="Home" />
|
|
73
|
+
<SidebarItem href="/settings" icon={<SettingsIcon />} label="Settings" />
|
|
74
|
+
</SidebarContent>
|
|
75
|
+
</SidebarNav>
|
|
76
|
+
</Sidebar>
|
|
77
|
+
|
|
78
|
+
{/* Mobile Bottom Navigation */}
|
|
79
|
+
<MobileBottomNav.Root variant={NavVariant.GLASS}>
|
|
80
|
+
<MobileBottomNav.NavList>
|
|
81
|
+
<MobileBottomNav.NavItem
|
|
82
|
+
icon={<HomeIcon />}
|
|
83
|
+
label="Home"
|
|
84
|
+
state={NavItemState.ACTIVE}
|
|
85
|
+
/>
|
|
86
|
+
<MobileBottomNav.NavItem
|
|
87
|
+
icon={<SearchIcon />}
|
|
88
|
+
label="Search"
|
|
89
|
+
/>
|
|
90
|
+
</MobileBottomNav.NavList>
|
|
91
|
+
</MobileBottomNav.Root>
|
|
92
|
+
</>
|
|
71
93
|
)
|
|
72
94
|
}
|
|
73
95
|
```
|
|
74
96
|
|
|
75
|
-
##
|
|
97
|
+
## Mobile Bottom Navigation
|
|
76
98
|
|
|
77
|
-
###
|
|
99
|
+
### MobileBottomNav (Mobile Navigation)
|
|
78
100
|
|
|
79
|
-
|
|
101
|
+
A mobile-optimized bottom navigation component with slot architecture and iOS 26 compatibility.
|
|
80
102
|
|
|
81
103
|
```tsx
|
|
82
|
-
<
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
104
|
+
<MobileBottomNav.Root
|
|
105
|
+
variant={NavVariant.GLASS}
|
|
106
|
+
size={NavSize.MEDIUM}
|
|
107
|
+
visible={true}
|
|
86
108
|
>
|
|
87
|
-
|
|
88
|
-
|
|
109
|
+
<MobileBottomNav.NavList>
|
|
110
|
+
<MobileBottomNav.NavItem
|
|
111
|
+
icon={<HomeIcon />}
|
|
112
|
+
label="Home"
|
|
113
|
+
state={NavItemState.ACTIVE}
|
|
114
|
+
onClick={() => navigate('/home')}
|
|
115
|
+
/>
|
|
116
|
+
<MobileBottomNav.NavItem
|
|
117
|
+
icon={<SearchIcon />}
|
|
118
|
+
label="Search"
|
|
119
|
+
badge={5}
|
|
120
|
+
/>
|
|
121
|
+
</MobileBottomNav.NavList>
|
|
122
|
+
</MobileBottomNav.Root>
|
|
89
123
|
```
|
|
90
124
|
|
|
91
|
-
###
|
|
125
|
+
### Navigation Variants
|
|
92
126
|
|
|
93
|
-
|
|
127
|
+
#### Glass Variant (iOS 26 Compatible)
|
|
128
|
+
```tsx
|
|
129
|
+
<MobileBottomNav.Root
|
|
130
|
+
variant={NavVariant.GLASS}
|
|
131
|
+
glassConfig={{
|
|
132
|
+
blur: BlurIntensity.MEDIUM,
|
|
133
|
+
opacity: 0.72,
|
|
134
|
+
borderOpacity: 0.18
|
|
135
|
+
}}
|
|
136
|
+
/>
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
#### Solid Variant
|
|
140
|
+
```tsx
|
|
141
|
+
<MobileBottomNav.Root
|
|
142
|
+
variant={NavVariant.SOLID}
|
|
143
|
+
/>
|
|
144
|
+
```
|
|
94
145
|
|
|
146
|
+
#### Headless Variant (Custom Styling)
|
|
95
147
|
```tsx
|
|
96
|
-
<
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
</SidebarContent>
|
|
101
|
-
</SidebarNav>
|
|
148
|
+
<MobileBottomNav.Root
|
|
149
|
+
variant={NavVariant.HEADLESS}
|
|
150
|
+
className="custom-nav-style"
|
|
151
|
+
/>
|
|
102
152
|
```
|
|
103
153
|
|
|
104
|
-
###
|
|
154
|
+
### Mobile Bottom Nav Features
|
|
155
|
+
|
|
156
|
+
- **iOS 26 Safari Compatible**: Fixes viewport bugs with address bar
|
|
157
|
+
- **Safe Area Support**: Handles notched devices (iPhone X+)
|
|
158
|
+
- **Glassmorphism Effects**: Beautiful blur and transparency
|
|
159
|
+
- **Motion Animations**: Spring and tween animations with Framer Motion
|
|
160
|
+
- **Accessibility**: Full WAI-ARIA support with keyboard navigation
|
|
161
|
+
- **Badge Support**: Show notifications and counts
|
|
162
|
+
- **Size Variants**: SMALL (56px), MEDIUM (72px), LARGE (88px)
|
|
163
|
+
- **Label Positions**: BELOW, BESIDE, HIDDEN
|
|
164
|
+
- **Touch Optimized**: WCAG 2.2 compliant touch targets (44px+)
|
|
105
165
|
|
|
106
|
-
|
|
166
|
+
### Mobile Navigation APIs
|
|
107
167
|
|
|
168
|
+
#### Root Props
|
|
108
169
|
```tsx
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
170
|
+
interface RootProps {
|
|
171
|
+
variant?: NavVariant
|
|
172
|
+
size?: NavSize
|
|
173
|
+
visible?: boolean
|
|
174
|
+
glassConfig?: GlassConfig
|
|
175
|
+
animationConfig?: AnimationConfig
|
|
176
|
+
rounded?: NavBorderRadius | number
|
|
177
|
+
full?: boolean
|
|
178
|
+
gap?: boolean
|
|
179
|
+
zIndex?: ZIndexLevel | number
|
|
180
|
+
}
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
#### Nav Item Props
|
|
184
|
+
```tsx
|
|
185
|
+
interface NavItemProps {
|
|
186
|
+
icon: ReactNode
|
|
187
|
+
label: string
|
|
188
|
+
state?: NavItemState
|
|
189
|
+
onClick?: () => void
|
|
190
|
+
href?: string
|
|
191
|
+
badge?: number | boolean
|
|
192
|
+
disabled?: boolean
|
|
193
|
+
labelPosition?: LabelPosition
|
|
194
|
+
iconSize?: IconSize | number
|
|
195
|
+
}
|
|
119
196
|
```
|
|
120
197
|
|
|
121
|
-
|
|
198
|
+
#### Mobile Utilities
|
|
199
|
+
```tsx
|
|
200
|
+
// Spacer to prevent content overlap
|
|
201
|
+
<MobileBottomNav.Spacer size={NavSize.MEDIUM} />
|
|
202
|
+
|
|
203
|
+
// iOS Safari fix utilities
|
|
204
|
+
import {
|
|
205
|
+
useIOSSafariFix,
|
|
206
|
+
useIOSFixedReset,
|
|
207
|
+
detectIOSVersion
|
|
208
|
+
} from '@mks2508/sidebar-headless'
|
|
209
|
+
```
|
|
122
210
|
|
|
123
|
-
###
|
|
211
|
+
### iOS Safari Compatibility Hooks
|
|
124
212
|
|
|
213
|
+
#### `useIOSSafariFix()` - iOS Detection
|
|
125
214
|
```tsx
|
|
126
|
-
|
|
215
|
+
const { iosInfo, hasViewportBugs } = useIOSSafariFix({
|
|
216
|
+
enableScrollFix: true,
|
|
217
|
+
enableKeyboardFix: true,
|
|
218
|
+
})
|
|
219
|
+
|
|
220
|
+
interface IOSInfo {
|
|
221
|
+
isIOS: boolean
|
|
222
|
+
majorVersion: number | null
|
|
223
|
+
hasViewportBugs: boolean
|
|
224
|
+
device: 'iphone' | 'ipad' | 'unknown'
|
|
225
|
+
isStandalone: boolean
|
|
226
|
+
}
|
|
227
|
+
```
|
|
127
228
|
|
|
128
|
-
|
|
129
|
-
|
|
229
|
+
#### `useIOSFixedReset()` - Position Reset
|
|
230
|
+
```tsx
|
|
231
|
+
const ref = useRef<HTMLElement>(null)
|
|
232
|
+
const { forceReset } = useIOSFixedReset(ref)
|
|
233
|
+
|
|
234
|
+
// Call when keyboard dismisses or viewport changes
|
|
235
|
+
useEffect(() => {
|
|
236
|
+
if (keyboardDismissed) {
|
|
237
|
+
forceReset()
|
|
238
|
+
}
|
|
239
|
+
}, [keyboardDismissed])
|
|
240
|
+
```
|
|
130
241
|
|
|
131
|
-
|
|
132
|
-
|
|
242
|
+
#### `detectIOSVersion()` - Version Detection
|
|
243
|
+
```tsx
|
|
244
|
+
const version = detectIOSVersion()
|
|
245
|
+
// Returns: { major: 26, minor: 0, patch: 1, full: "26.0.1" }
|
|
133
246
|
```
|
|
134
247
|
|
|
135
|
-
###
|
|
248
|
+
### iOS 26 Safari Known Issues & Fixes
|
|
249
|
+
|
|
250
|
+
#### Issues Fixed:
|
|
251
|
+
- **Address bar shifting**: Fixed elements drift when URL bar hides
|
|
252
|
+
- **Keyboard dismissal**: VisualViewport doesn't reset after keyboard
|
|
253
|
+
- **100dvh gaps**: Dynamic viewport units don't account for floating tab bar
|
|
254
|
+
|
|
255
|
+
#### Automatic Fixes Applied:
|
|
256
|
+
- Hardware acceleration (`translateZ(0)`)
|
|
257
|
+
- Scroll containment (`contain: layout style`)
|
|
258
|
+
- Proper stacking context creation
|
|
259
|
+
- CSS custom properties for safe areas
|
|
260
|
+
- Responsive design with fallbacks
|
|
136
261
|
|
|
262
|
+
#### Manual Fixes Available:
|
|
137
263
|
```tsx
|
|
138
|
-
|
|
264
|
+
// Enable debug mode to see iOS detection
|
|
265
|
+
<MobileBottomNav.Root
|
|
266
|
+
data-ios-debug="true"
|
|
267
|
+
variant={NavVariant.GLASS}
|
|
268
|
+
>
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
### Advanced Configuration
|
|
272
|
+
|
|
273
|
+
#### Animation Configuration
|
|
274
|
+
```tsx
|
|
275
|
+
<MobileBottomNav.Root
|
|
276
|
+
animationConfig={{
|
|
277
|
+
type: AnimationType.SPRING,
|
|
278
|
+
pressScale: 0.95,
|
|
279
|
+
hoverScale: 1.05,
|
|
280
|
+
spring: {
|
|
281
|
+
stiffness: 400,
|
|
282
|
+
damping: 30,
|
|
283
|
+
},
|
|
284
|
+
}}
|
|
285
|
+
/>
|
|
286
|
+
```
|
|
139
287
|
|
|
140
|
-
|
|
141
|
-
|
|
288
|
+
#### Glass Configuration
|
|
289
|
+
```tsx
|
|
290
|
+
<MobileBottomNav.Root
|
|
291
|
+
variant={NavVariant.GLASS}
|
|
292
|
+
glassConfig={{
|
|
293
|
+
blur: BlurIntensity.HEAVY, // or number (px)
|
|
294
|
+
opacity: 0.8, // 0-1
|
|
295
|
+
saturation: 180, // 100-200%
|
|
296
|
+
borderOpacity: 0.15, // 0-1
|
|
297
|
+
}}
|
|
298
|
+
/>
|
|
299
|
+
```
|
|
142
300
|
|
|
143
|
-
|
|
144
|
-
|
|
301
|
+
#### Responsive Design
|
|
302
|
+
```tsx
|
|
303
|
+
// Size variants affect height and touch targets
|
|
304
|
+
<MobileBottomNav.Root size={NavSize.SMALL}> {/* 56px */}
|
|
305
|
+
<MobileBottomNav.Root size={NavSize.MEDIUM}> {/* 72px */}
|
|
306
|
+
<MobileBottomNav.Root size={NavSize.LARGE}> {/* 88px */}
|
|
307
|
+
|
|
308
|
+
// Custom border radius
|
|
309
|
+
<MobileBottomNav.Root rounded={1.5} /> {/* rem */}
|
|
310
|
+
<MobileBottomNav.Root rounded={NavBorderRadius.ROUNDED} />
|
|
145
311
|
```
|
|
146
312
|
|
|
147
|
-
|
|
313
|
+
## Core Components
|
|
314
|
+
|
|
315
|
+
### Sidebar (Root Provider)
|
|
316
|
+
|
|
317
|
+
The main container that provides context to all child components.
|
|
318
|
+
|
|
319
|
+
```tsx
|
|
320
|
+
<Sidebar
|
|
321
|
+
defaultOpen={true}
|
|
322
|
+
collapseMode="hide"
|
|
323
|
+
layoutBehaviour="floating"
|
|
324
|
+
>
|
|
325
|
+
{/* ... */}
|
|
326
|
+
</Sidebar>
|
|
327
|
+
```
|
|
328
|
+
|
|
329
|
+
### SidebarNav
|
|
330
|
+
|
|
331
|
+
Navigation container with event management.
|
|
332
|
+
|
|
333
|
+
```tsx
|
|
334
|
+
<SidebarNav>
|
|
335
|
+
<SidebarToggle />
|
|
336
|
+
<SidebarContent>
|
|
337
|
+
{/* items */}
|
|
338
|
+
</SidebarContent>
|
|
339
|
+
</SidebarNav>
|
|
340
|
+
```
|
|
341
|
+
|
|
342
|
+
### SidebarItem
|
|
343
|
+
|
|
344
|
+
Individual navigation item with optional sub-content.
|
|
345
|
+
|
|
346
|
+
```tsx
|
|
347
|
+
<SidebarItem
|
|
348
|
+
href="/dashboard"
|
|
349
|
+
icon={<DashboardIcon />}
|
|
350
|
+
label="Dashboard"
|
|
351
|
+
>
|
|
352
|
+
<SidebarSubContent title="Dashboard">
|
|
353
|
+
<SidebarSubLink href="/analytics">Analytics</SidebarSubLink>
|
|
354
|
+
<SidebarSubLink href="/reports">Reports</SidebarSubLink>
|
|
355
|
+
</SidebarSubContent>
|
|
356
|
+
</SidebarItem>
|
|
357
|
+
```
|
|
358
|
+
|
|
359
|
+
## Configuration Options
|
|
360
|
+
|
|
361
|
+
### Collapse Modes
|
|
362
|
+
|
|
363
|
+
```tsx
|
|
364
|
+
import { SidebarCollapseMode } from '@mks2508/sidebar-headless'
|
|
365
|
+
|
|
366
|
+
// Collapse to icon-only width
|
|
367
|
+
<Sidebar collapseMode={SidebarCollapseMode.COLLAPSE} />
|
|
368
|
+
|
|
369
|
+
// Hide completely
|
|
370
|
+
<Sidebar collapseMode={SidebarCollapseMode.HIDE} />
|
|
371
|
+
```
|
|
372
|
+
|
|
373
|
+
### Layout Behaviours
|
|
374
|
+
|
|
375
|
+
```tsx
|
|
376
|
+
import { SidebarLayoutBehaviour } from '@mks2508/sidebar-headless'
|
|
377
|
+
|
|
378
|
+
// Floating sidebar (doesn't push content)
|
|
379
|
+
<Sidebar layoutBehaviour={SidebarLayoutBehaviour.FLOATING} />
|
|
380
|
+
|
|
381
|
+
// Inline sidebar (pushes content)
|
|
382
|
+
<Sidebar layoutBehaviour={SidebarLayoutBehaviour.INLINE} />
|
|
383
|
+
```
|
|
384
|
+
|
|
385
|
+
### Custom Dimensions
|
|
386
|
+
|
|
387
|
+
```tsx
|
|
388
|
+
<Sidebar
|
|
389
|
+
dimensions={{
|
|
390
|
+
collapsedWidth: "4rem",
|
|
391
|
+
expandedWidth: "18rem",
|
|
392
|
+
indicatorHeight: "3rem",
|
|
393
|
+
tooltipDistance: "16px"
|
|
394
|
+
}}
|
|
395
|
+
/>
|
|
396
|
+
```
|
|
397
|
+
|
|
398
|
+
## Advanced Features
|
|
399
|
+
|
|
400
|
+
### Fluid Indicators
|
|
401
|
+
|
|
402
|
+
Enable glassmorphism-style hover indicators:
|
|
403
|
+
|
|
404
|
+
```tsx
|
|
405
|
+
<Sidebar
|
|
406
|
+
enableFluidIndicator={true}
|
|
407
|
+
liquidGlass={{
|
|
408
|
+
enableLiquidGlassV2: true,
|
|
409
|
+
enableChromaticAberration: true
|
|
410
|
+
}}
|
|
411
|
+
/>
|
|
412
|
+
```
|
|
413
|
+
|
|
414
|
+
### Keyboard Navigation
|
|
415
|
+
|
|
416
|
+
Built-in keyboard shortcuts:
|
|
417
|
+
|
|
418
|
+
- **Arrow Up/Down**: Navigate between items
|
|
419
|
+
- **Home**: Jump to first item
|
|
420
|
+
- **End**: Jump to last item
|
|
421
|
+
- **Enter**: Activate focused item
|
|
422
|
+
|
|
423
|
+
### Render Props
|
|
424
|
+
|
|
425
|
+
Access sidebar state via render props:
|
|
426
|
+
|
|
427
|
+
```tsx
|
|
428
|
+
<Sidebar>
|
|
429
|
+
{({ open, collapsed }) => (
|
|
430
|
+
<div>
|
|
431
|
+
<SidebarNav />
|
|
432
|
+
<p>Sidebar is {open ? 'open' : 'closed'}</p>
|
|
433
|
+
</div>
|
|
434
|
+
)}
|
|
435
|
+
</Sidebar>
|
|
436
|
+
```
|
|
437
|
+
|
|
438
|
+
## Styling
|
|
439
|
+
|
|
440
|
+
The sidebar uses shadcn-style CSS tokens for theming:
|
|
441
|
+
|
|
442
|
+
```css
|
|
443
|
+
:root {
|
|
444
|
+
--sidebar: oklch(98% 0 0);
|
|
445
|
+
--sidebar-foreground: oklch(20% 0 0);
|
|
446
|
+
--sidebar-primary: oklch(55% 0.2 250);
|
|
447
|
+
--sidebar-accent: oklch(95% 0.01 250);
|
|
448
|
+
--sidebar-border: oklch(90% 0.01 250);
|
|
449
|
+
--sidebar-ring: oklch(55% 0.2 250);
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
.dark {
|
|
453
|
+
--sidebar: oklch(17% 0 0);
|
|
454
|
+
--sidebar-foreground: oklch(90% 0 0);
|
|
455
|
+
/* ... */
|
|
456
|
+
}
|
|
457
|
+
```
|
|
458
|
+
|
|
459
|
+
## TypeScript Support
|
|
460
|
+
|
|
461
|
+
Full TypeScript support with comprehensive type definitions:
|
|
462
|
+
|
|
463
|
+
```tsx
|
|
464
|
+
import type {
|
|
465
|
+
SidebarProps,
|
|
466
|
+
SidebarItemProps,
|
|
467
|
+
SidebarContextValue
|
|
468
|
+
} from '@mks2508/sidebar-headless'
|
|
469
|
+
```
|
|
470
|
+
|
|
471
|
+
## Performance & Troubleshooting
|
|
472
|
+
|
|
473
|
+
### Performance Tips
|
|
474
|
+
|
|
475
|
+
#### Glass Variant Performance
|
|
476
|
+
```tsx
|
|
477
|
+
// Reduce blur for better performance on older devices
|
|
478
|
+
<MobileBottomNav.Root
|
|
479
|
+
variant={NavVariant.GLASS}
|
|
480
|
+
glassConfig={{
|
|
481
|
+
blur: BlurIntensity.LIGHT, // 10px vs 20px
|
|
482
|
+
saturation: 150, // Less saturation
|
|
483
|
+
}}
|
|
484
|
+
/>
|
|
485
|
+
```
|
|
148
486
|
|
|
487
|
+
#### Animation Performance
|
|
149
488
|
```tsx
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
489
|
+
// Disable animations for reduced motion
|
|
490
|
+
const prefersReduced = useMediaQuery('(prefers-reduced-motion: reduce)')
|
|
491
|
+
|
|
492
|
+
<MobileBottomNav.Root
|
|
493
|
+
animationConfig={{
|
|
494
|
+
type: prefersReduced ? AnimationType.NONE : AnimationType.SPRING,
|
|
156
495
|
}}
|
|
157
496
|
/>
|
|
158
497
|
```
|
|
159
498
|
|
|
160
|
-
|
|
499
|
+
#### Bundle Size Optimization
|
|
500
|
+
```tsx
|
|
501
|
+
// Import only what you need
|
|
502
|
+
import {
|
|
503
|
+
MobileBottomNav,
|
|
504
|
+
NavVariant,
|
|
505
|
+
NavItemState
|
|
506
|
+
} from '@mks2508/sidebar-headless'
|
|
507
|
+
|
|
508
|
+
// Tree-shake CSS imports
|
|
509
|
+
import '@mks2508/sidebar-headless/mobile-optimizations.css'
|
|
510
|
+
```
|
|
511
|
+
|
|
512
|
+
### Common Issues & Solutions
|
|
513
|
+
|
|
514
|
+
#### iOS Safari Issues
|
|
515
|
+
```tsx
|
|
516
|
+
// Problem: Navigation shifts when scrolling
|
|
517
|
+
// Solution: Enable scroll fix
|
|
518
|
+
const { hasViewportBugs } = useIOSSafariFix({
|
|
519
|
+
enableScrollFix: true
|
|
520
|
+
})
|
|
521
|
+
|
|
522
|
+
// Problem: Navigation offset after keyboard
|
|
523
|
+
// Solution: Force reset
|
|
524
|
+
const { forceReset } = useIOSFixedReset(navRef)
|
|
525
|
+
useEffect(() => {
|
|
526
|
+
if (keyboardDismissed) forceReset()
|
|
527
|
+
}, [keyboardDismissed])
|
|
528
|
+
```
|
|
161
529
|
|
|
162
|
-
|
|
530
|
+
#### Touch Target Issues
|
|
531
|
+
```tsx
|
|
532
|
+
// Problem: Touch targets too small
|
|
533
|
+
// Solution: Use larger size or custom styling
|
|
534
|
+
<MobileBottomNav.Root size={NavSize.LARGE} /> // 88px
|
|
163
535
|
|
|
164
|
-
|
|
536
|
+
// Or custom minimum touch targets
|
|
537
|
+
<MobileBottomNav.NavItem
|
|
538
|
+
className="min-touch-target" // CSS: min-width: 48px; min-height: 48px;
|
|
539
|
+
/>
|
|
540
|
+
```
|
|
165
541
|
|
|
542
|
+
#### Animation Conflicts
|
|
166
543
|
```tsx
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
544
|
+
// Problem: Conflicts with parent animations
|
|
545
|
+
// Solution: Use NONE animation type
|
|
546
|
+
<MobileBottomNav.Root
|
|
547
|
+
animationConfig={{
|
|
548
|
+
type: AnimationType.NONE,
|
|
172
549
|
}}
|
|
173
550
|
/>
|
|
174
551
|
```
|
|
175
552
|
|
|
176
|
-
###
|
|
553
|
+
### Debug Mode
|
|
177
554
|
|
|
178
|
-
|
|
555
|
+
Enable debug mode to see internal state and iOS detection:
|
|
556
|
+
```tsx
|
|
557
|
+
<MobileBottomNav.Root
|
|
558
|
+
data-ios-debug="true"
|
|
559
|
+
data-mobile-nav-debug="true"
|
|
560
|
+
variant={NavVariant.GLASS}
|
|
561
|
+
>
|
|
562
|
+
```
|
|
563
|
+
|
|
564
|
+
## Browser Support
|
|
565
|
+
|
|
566
|
+
- Chrome/Edge (latest)
|
|
567
|
+
- Firefox (latest)
|
|
568
|
+
- Safari (latest) - Full iOS 26 Safari compatibility
|
|
569
|
+
- Modern browsers with ES2020 support
|
|
570
|
+
|
|
571
|
+
## Mobile Compatibility
|
|
572
|
+
|
|
573
|
+
- â
iOS 15+ (including iOS 26 Safari viewport fixes)
|
|
574
|
+
- â
Android 8+
|
|
575
|
+
- â
Safe area support (iPhone X+ notched devices)
|
|
576
|
+
- â
Foldable devices support
|
|
577
|
+
- â
Touch gesture navigation
|
|
578
|
+
- â
Keyboard dismissal handling
|
|
579
|
+
|
|
580
|
+
### Device-Specific Notes
|
|
581
|
+
|
|
582
|
+
#### iPhone X and newer (with notch)
|
|
583
|
+
- Safe area padding automatically applied via CSS `env()`
|
|
584
|
+
- No additional configuration needed
|
|
585
|
+
- Works with both portrait and landscape orientations
|
|
586
|
+
|
|
587
|
+
#### iPad with Home Indicator
|
|
588
|
+
- Bottom safe area respected automatically
|
|
589
|
+
- Optimal touch targets for finger navigation
|
|
590
|
+
|
|
591
|
+
#### Foldable Devices (Samsung Galaxy Fold, etc.)
|
|
592
|
+
- Uses `horizontal-viewport-segments` CSS media query
|
|
593
|
+
- Automatic layout adjustment for fold position
|
|
594
|
+
|
|
595
|
+
#### Android Devices
|
|
596
|
+
- Material Design navigation patterns compatible
|
|
597
|
+
- Back gesture handling respected
|
|
598
|
+
- Dynamic viewport units supported
|
|
599
|
+
|
|
600
|
+
## API Reference
|
|
601
|
+
|
|
602
|
+
### MobileBottomNav Components
|
|
603
|
+
|
|
604
|
+
#### `MobileBottomNav.Root`
|
|
605
|
+
Main container for mobile bottom navigation.
|
|
606
|
+
|
|
607
|
+
**Props:** `RootProps`
|
|
608
|
+
```tsx
|
|
609
|
+
<MobileBottomNav.Root
|
|
610
|
+
variant={NavVariant.GLASS}
|
|
611
|
+
size={NavSize.MEDIUM}
|
|
612
|
+
visible={true}
|
|
613
|
+
glassConfig={glassConfig}
|
|
614
|
+
animationConfig={animationConfig}
|
|
615
|
+
rounded={NavBorderRadius.ROUNDED}
|
|
616
|
+
full={false}
|
|
617
|
+
gap={true}
|
|
618
|
+
zIndex={50}
|
|
619
|
+
aria-label="Main navigation"
|
|
620
|
+
data-testid="mobile-nav"
|
|
621
|
+
className="custom-nav"
|
|
622
|
+
>
|
|
623
|
+
{children}
|
|
624
|
+
</MobileBottomNav.Root>
|
|
625
|
+
```
|
|
626
|
+
|
|
627
|
+
#### `MobileBottomNav.NavList`
|
|
628
|
+
Container for navigation items with flexbox layout.
|
|
629
|
+
|
|
630
|
+
**Props:** `NavListProps`
|
|
631
|
+
```tsx
|
|
632
|
+
<MobileBottomNav.NavList
|
|
633
|
+
justify="around" // "start" | "center" | "end" | "around" | "between"
|
|
634
|
+
gap={1} // number (rem)
|
|
635
|
+
className="nav-list"
|
|
636
|
+
>
|
|
637
|
+
{children}
|
|
638
|
+
</MobileBottomNav.NavList>
|
|
639
|
+
```
|
|
640
|
+
|
|
641
|
+
#### `MobileBottomNav.NavItem`
|
|
642
|
+
Individual navigation item with icon, label, and optional badge.
|
|
643
|
+
|
|
644
|
+
**Props:** `NavItemProps`
|
|
645
|
+
```tsx
|
|
646
|
+
<MobileBottomNav.NavItem
|
|
647
|
+
icon={<HomeIcon />}
|
|
648
|
+
label="Home"
|
|
649
|
+
state={NavItemState.ACTIVE}
|
|
650
|
+
onClick={() => navigate('/home')}
|
|
651
|
+
href="/home" // Optional: renders as <a>
|
|
652
|
+
badge={5} // number | boolean
|
|
653
|
+
disabled={false}
|
|
654
|
+
labelPosition={LabelPosition.BELOW}
|
|
655
|
+
iconSize={IconSize.MEDIUM}
|
|
656
|
+
aria-label="Go to home"
|
|
657
|
+
data-testid="nav-item-home"
|
|
658
|
+
className="nav-item"
|
|
659
|
+
/>
|
|
660
|
+
```
|
|
179
661
|
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
- **End**: Jump to last item
|
|
183
|
-
- **Enter**: Activate focused item
|
|
662
|
+
#### `MobileBottomNav.Spacer`
|
|
663
|
+
Invisible spacer to prevent content from being hidden under navigation.
|
|
184
664
|
|
|
185
|
-
|
|
665
|
+
**Props:** `SpacerProps`
|
|
666
|
+
```tsx
|
|
667
|
+
<MobileBottomNav.Spacer
|
|
668
|
+
size={NavSize.MEDIUM}
|
|
669
|
+
className="content-spacer"
|
|
670
|
+
/>
|
|
671
|
+
```
|
|
186
672
|
|
|
187
|
-
|
|
673
|
+
### Enums & Types
|
|
188
674
|
|
|
675
|
+
#### `NavVariant`
|
|
189
676
|
```tsx
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
</div>
|
|
196
|
-
)}
|
|
197
|
-
</Sidebar>
|
|
677
|
+
enum NavVariant {
|
|
678
|
+
GLASS = "glass", // Glassmorphism with blur
|
|
679
|
+
SOLID = "solid", // Solid background
|
|
680
|
+
HEADLESS = "headless" // No default styles
|
|
681
|
+
}
|
|
198
682
|
```
|
|
199
683
|
|
|
200
|
-
|
|
684
|
+
#### `NavSize`
|
|
685
|
+
```tsx
|
|
686
|
+
enum NavSize {
|
|
687
|
+
SMALL = "sm", // 56px height
|
|
688
|
+
MEDIUM = "md", // 72px height
|
|
689
|
+
LARGE = "lg" // 88px height
|
|
690
|
+
}
|
|
691
|
+
```
|
|
201
692
|
|
|
202
|
-
|
|
693
|
+
#### `NavItemState`
|
|
694
|
+
```tsx
|
|
695
|
+
enum NavItemState {
|
|
696
|
+
INACTIVE = "inactive",
|
|
697
|
+
ACTIVE = "active",
|
|
698
|
+
DISABLED = "disabled"
|
|
699
|
+
}
|
|
700
|
+
```
|
|
203
701
|
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
--sidebar-border: oklch(90% 0.01 250);
|
|
211
|
-
--sidebar-ring: oklch(55% 0.2 250);
|
|
702
|
+
#### `LabelPosition`
|
|
703
|
+
```tsx
|
|
704
|
+
enum LabelPosition {
|
|
705
|
+
BELOW = "below", // Icon above text
|
|
706
|
+
BESIDE = "beside", // Icon beside text
|
|
707
|
+
HIDDEN = "hidden" // Icon only
|
|
212
708
|
}
|
|
709
|
+
```
|
|
213
710
|
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
711
|
+
#### `IconSize`
|
|
712
|
+
```tsx
|
|
713
|
+
enum IconSize {
|
|
714
|
+
SMALL = 18, // 18px
|
|
715
|
+
MEDIUM = 24, // 24px
|
|
716
|
+
LARGE = 28 // 28px
|
|
218
717
|
}
|
|
219
718
|
```
|
|
220
719
|
|
|
221
|
-
|
|
720
|
+
#### `BlurIntensity`
|
|
721
|
+
```tsx
|
|
722
|
+
enum BlurIntensity {
|
|
723
|
+
LIGHT = 10, // 10px blur
|
|
724
|
+
MEDIUM = 20, // 20px blur
|
|
725
|
+
HEAVY = 30 // 30px blur
|
|
726
|
+
}
|
|
727
|
+
```
|
|
222
728
|
|
|
223
|
-
|
|
729
|
+
### Configuration Interfaces
|
|
224
730
|
|
|
731
|
+
#### `GlassConfig`
|
|
225
732
|
```tsx
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
733
|
+
interface GlassConfig {
|
|
734
|
+
blur: BlurIntensity | number // Blur amount in pixels
|
|
735
|
+
saturation: number // 100-200%
|
|
736
|
+
opacity: number // 0-1
|
|
737
|
+
borderOpacity: number // 0-1
|
|
738
|
+
}
|
|
231
739
|
```
|
|
232
740
|
|
|
233
|
-
|
|
741
|
+
#### `AnimationConfig`
|
|
742
|
+
```tsx
|
|
743
|
+
interface AnimationConfig {
|
|
744
|
+
type: AnimationType // SPRING | TWEEN | NONE
|
|
745
|
+
pressScale?: number // Scale when pressed (0-1)
|
|
746
|
+
hoverScale?: number // Scale when hovered (>1)
|
|
747
|
+
spring?: SpringConfig // Spring physics
|
|
748
|
+
tween?: TweenConfig // Tween animation
|
|
749
|
+
}
|
|
234
750
|
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
751
|
+
interface SpringConfig {
|
|
752
|
+
stiffness?: number // Spring stiffness (100-1000)
|
|
753
|
+
damping?: number // Spring damping (10-100)
|
|
754
|
+
}
|
|
755
|
+
|
|
756
|
+
interface TweenConfig {
|
|
757
|
+
duration?: number // Duration in seconds (0.1-2)
|
|
758
|
+
ease?: string // CSS easing function
|
|
759
|
+
}
|
|
760
|
+
```
|
|
761
|
+
|
|
762
|
+
#### `IOSSafariFixConfig`
|
|
763
|
+
```tsx
|
|
764
|
+
interface IOSSafariFixConfig {
|
|
765
|
+
enableScrollFix?: boolean // Fix scroll-induced drift
|
|
766
|
+
enableKeyboardFix?: boolean // Fix keyboard dismissal issues
|
|
767
|
+
debug?: boolean // Enable debug logging
|
|
768
|
+
}
|
|
769
|
+
```
|
|
239
770
|
|
|
240
771
|
## License
|
|
241
772
|
|
|
@@ -249,3 +780,4 @@ Contributions are welcome! Please feel free to submit a Pull Request.
|
|
|
249
780
|
|
|
250
781
|
- [GitHub Repository](https://github.com/mks2508/sidebar-headless)
|
|
251
782
|
- [Report Issues](https://github.com/mks2508/sidebar-headless/issues)
|
|
783
|
+
- [npm Package](https://npmjs.com/package/@mks2508/sidebar-headless)
|