@mks2508/sidebar-headless 0.2.1 → 0.3.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.
Files changed (130) hide show
  1. package/CHANGELOG.md +53 -53
  2. package/LICENSE +21 -21
  3. package/README.md +165 -165
  4. package/dist/components/BottomNavBar/MobileBottomNav.d.ts +65 -0
  5. package/dist/components/BottomNavBar/MobileBottomNav.d.ts.map +1 -0
  6. package/dist/components/BottomNavBar/MobileBottomNav.styles.d.ts +94 -0
  7. package/dist/components/BottomNavBar/MobileBottomNav.styles.d.ts.map +1 -0
  8. package/dist/components/BottomNavBar/index.d.ts +10 -0
  9. package/dist/components/BottomNavBar/index.d.ts.map +1 -0
  10. package/dist/components/BottomNavBar/types.d.ts +363 -0
  11. package/dist/components/BottomNavBar/types.d.ts.map +1 -0
  12. package/dist/components/BottomNavBar/useIOSSafariFix.d.ts +74 -0
  13. package/dist/components/BottomNavBar/useIOSSafariFix.d.ts.map +1 -0
  14. package/dist/components/Sidebar/Sidebar.constants.d.ts +285 -0
  15. package/dist/components/Sidebar/Sidebar.constants.d.ts.map +1 -0
  16. package/dist/components/Sidebar/Sidebar.d.ts +80 -0
  17. package/dist/components/Sidebar/Sidebar.d.ts.map +1 -0
  18. package/dist/components/Sidebar/Sidebar.styles.d.ts +77 -0
  19. package/dist/components/Sidebar/Sidebar.styles.d.ts.map +1 -0
  20. package/dist/components/Sidebar/Sidebar.types.d.ts +638 -0
  21. package/dist/components/Sidebar/Sidebar.types.d.ts.map +1 -0
  22. package/dist/components/Sidebar/SidebarContent.d.ts +46 -0
  23. package/dist/components/Sidebar/SidebarContent.d.ts.map +1 -0
  24. package/dist/components/Sidebar/SidebarContext.d.ts +87 -0
  25. package/dist/components/Sidebar/SidebarContext.d.ts.map +1 -0
  26. package/dist/components/Sidebar/SidebarFluidIndicator.d.ts +37 -0
  27. package/dist/components/Sidebar/SidebarFluidIndicator.d.ts.map +1 -0
  28. package/dist/components/Sidebar/SidebarIndicator.d.ts +59 -0
  29. package/dist/components/Sidebar/SidebarIndicator.d.ts.map +1 -0
  30. package/dist/components/Sidebar/SidebarItem.d.ts +82 -0
  31. package/dist/components/Sidebar/SidebarItem.d.ts.map +1 -0
  32. package/dist/components/Sidebar/SidebarNav.d.ts +48 -0
  33. package/dist/components/Sidebar/SidebarNav.d.ts.map +1 -0
  34. package/dist/components/Sidebar/SidebarSafeArea.d.ts +56 -0
  35. package/dist/components/Sidebar/SidebarSafeArea.d.ts.map +1 -0
  36. package/dist/components/Sidebar/SidebarSubContent.d.ts +10 -0
  37. package/dist/components/Sidebar/SidebarSubContent.d.ts.map +1 -0
  38. package/dist/components/Sidebar/SidebarSubLink.d.ts +18 -0
  39. package/dist/components/Sidebar/SidebarSubLink.d.ts.map +1 -0
  40. package/dist/components/Sidebar/SidebarToggle.d.ts +52 -0
  41. package/dist/components/Sidebar/SidebarToggle.d.ts.map +1 -0
  42. package/dist/components/Sidebar/SidebarTooltip.d.ts +26 -0
  43. package/dist/components/Sidebar/SidebarTooltip.d.ts.map +1 -0
  44. package/dist/components/Sidebar/TooltipTitle3D.d.ts +42 -0
  45. package/dist/components/Sidebar/TooltipTitle3D.d.ts.map +1 -0
  46. package/dist/components/Sidebar/TooltipTitle3DCrossfade.d.ts +46 -0
  47. package/dist/components/Sidebar/TooltipTitle3DCrossfade.d.ts.map +1 -0
  48. package/dist/components/Sidebar/TooltipTitleCylinder.d.ts +9 -0
  49. package/dist/components/Sidebar/TooltipTitleCylinder.d.ts.map +1 -0
  50. package/dist/components/Sidebar/hooks/useSidebarContext.d.ts +48 -0
  51. package/dist/components/Sidebar/hooks/useSidebarContext.d.ts.map +1 -0
  52. package/dist/components/Sidebar/hooks/useSidebarIndicator.d.ts +72 -0
  53. package/dist/components/Sidebar/hooks/useSidebarIndicator.d.ts.map +1 -0
  54. package/dist/components/Sidebar/hooks/useSidebarKeyboard.d.ts +51 -0
  55. package/dist/components/Sidebar/hooks/useSidebarKeyboard.d.ts.map +1 -0
  56. package/dist/components/Sidebar/hooks/useSubContent.d.ts +7 -0
  57. package/dist/components/Sidebar/hooks/useSubContent.d.ts.map +1 -0
  58. package/dist/components/Sidebar/hooks/useTitleHistory.d.ts +41 -0
  59. package/dist/components/Sidebar/hooks/useTitleHistory.d.ts.map +1 -0
  60. package/dist/components/Sidebar/hooks/useTooltipDirection.d.ts +29 -0
  61. package/dist/components/Sidebar/hooks/useTooltipDirection.d.ts.map +1 -0
  62. package/dist/components/Sidebar/hooks/useTooltipTransition.d.ts +60 -0
  63. package/dist/components/Sidebar/hooks/useTooltipTransition.d.ts.map +1 -0
  64. package/dist/components/Sidebar/index.d.ts +163 -0
  65. package/dist/components/Sidebar/index.d.ts.map +1 -0
  66. package/dist/components/Sidebar/sidebar-defaults.d.ts +54 -0
  67. package/dist/components/Sidebar/sidebar-defaults.d.ts.map +1 -0
  68. package/dist/components/animate-ui/components/base/switch.d.ts +11 -0
  69. package/dist/components/animate-ui/components/base/switch.d.ts.map +1 -0
  70. package/dist/components/animate-ui/primitives/base/switch.d.ts +23 -0
  71. package/dist/components/animate-ui/primitives/base/switch.d.ts.map +1 -0
  72. package/dist/components/fluid-hover-indicator-v2.d.ts +46 -0
  73. package/dist/components/fluid-hover-indicator-v2.d.ts.map +1 -0
  74. package/dist/components/fluid-hover-indicator.d.ts +28 -0
  75. package/dist/components/fluid-hover-indicator.d.ts.map +1 -0
  76. package/dist/components/ui/TextCylinder.d.ts +21 -0
  77. package/dist/components/ui/TextCylinder.d.ts.map +1 -0
  78. package/dist/components/ui/button.d.ts +11 -0
  79. package/dist/components/ui/button.d.ts.map +1 -0
  80. package/dist/components/ui/card.d.ts +10 -0
  81. package/dist/components/ui/card.d.ts.map +1 -0
  82. package/dist/components/ui/custom-icon.d.ts +45 -0
  83. package/dist/components/ui/custom-icon.d.ts.map +1 -0
  84. package/dist/components/ui/dotted-glow-background.d.ts +42 -0
  85. package/dist/components/ui/dotted-glow-background.d.ts.map +1 -0
  86. package/dist/components/ui/input.d.ts +4 -0
  87. package/dist/components/ui/input.d.ts.map +1 -0
  88. package/dist/components/ui/label.d.ts +5 -0
  89. package/dist/components/ui/label.d.ts.map +1 -0
  90. package/dist/components/ui/optimized-image.d.ts +12 -0
  91. package/dist/components/ui/optimized-image.d.ts.map +1 -0
  92. package/dist/components/ui/select.d.ts +16 -0
  93. package/dist/components/ui/select.d.ts.map +1 -0
  94. package/dist/components/ui/slider.d.ts +5 -0
  95. package/dist/components/ui/slider.d.ts.map +1 -0
  96. package/dist/components/ui/textarea.d.ts +4 -0
  97. package/dist/components/ui/textarea.d.ts.map +1 -0
  98. package/dist/{index.css → dist/index.css} +945 -908
  99. package/dist/dist/index.css.map +1 -0
  100. package/dist/hooks/use-controlled-state.d.ts +9 -0
  101. package/dist/hooks/use-controlled-state.d.ts.map +1 -0
  102. package/dist/hooks/use-fluid-animation.d.ts +18 -0
  103. package/dist/hooks/use-fluid-animation.d.ts.map +1 -0
  104. package/dist/hooks/use-liquid-glass.d.ts +40 -0
  105. package/dist/hooks/use-liquid-glass.d.ts.map +1 -0
  106. package/dist/hooks/use-sidebar-liquid-glass.d.ts +48 -0
  107. package/dist/hooks/use-sidebar-liquid-glass.d.ts.map +1 -0
  108. package/dist/hooks/use-text-cylinder.d.ts +67 -0
  109. package/dist/hooks/use-text-cylinder.d.ts.map +1 -0
  110. package/dist/index.cjs +13192 -52272
  111. package/dist/index.cjs.map +1 -1
  112. package/dist/index.d.ts +171 -2234
  113. package/dist/index.d.ts.map +1 -0
  114. package/dist/index.js +13131 -52248
  115. package/dist/index.js.map +1 -1
  116. package/dist/lib/get-strict-context.d.ts +10 -0
  117. package/dist/lib/get-strict-context.d.ts.map +1 -0
  118. package/dist/lib/liquid-glass-presets.d.ts +43 -0
  119. package/dist/lib/liquid-glass-presets.d.ts.map +1 -0
  120. package/dist/lib/utils.d.ts +3 -0
  121. package/dist/lib/utils.d.ts.map +1 -0
  122. package/dist/types/sidebar-minimal.d.ts +21 -0
  123. package/dist/types/sidebar-minimal.d.ts.map +1 -0
  124. package/dist/utils/TooltipAnimationController.d.ts +122 -0
  125. package/dist/utils/TooltipAnimationController.d.ts.map +1 -0
  126. package/package.json +113 -106
  127. package/dist/MobileOptimizations.css +0 -570
  128. package/dist/index.css.map +0 -1
  129. package/dist/index.d.cts +0 -2235
  130. package/dist/tooltip-keyframes.css +0 -329
@@ -1,912 +1,949 @@
1
+ /* CSS Custom Properties for dynamic configuration */
2
+ .container {
3
+ position: relative;
4
+ overflow: hidden;
5
+ perspective: var(--cylinder-perspective, 1000px);
6
+ }
7
+
8
+ .cylinder {
9
+ position: relative;
10
+ width: 100%;
11
+ height: 100%;
12
+ transform-style: preserve-3d;
13
+ /* Rotation is controlled by Web Animations API */
14
+ transform: rotateX(var(--cylinder-rotation, 0deg));
15
+ }
16
+
17
+ .face {
18
+ position: absolute;
19
+ inset: 0;
20
+ display: flex;
21
+ align-items: center;
22
+ justify-content: center;
23
+ backface-visibility: hidden;
24
+ /* Transform is set via inline style for dynamic positioning */
25
+ transition: opacity 0.3s ease;
26
+ }
27
+
28
+ .face.visible {
29
+ opacity: 1;
30
+ pointer-events: auto;
31
+ }
32
+
33
+ .face.hidden {
34
+ opacity: 0;
35
+ pointer-events: none;
36
+ }
37
+
38
+ /* Optional: Add will-change for performance on faces that are animating */
39
+ .cylinder.animating .face {
40
+ will-change: opacity;
41
+ }
42
+
43
+ .cylinder.animating {
44
+ will-change: transform;
45
+ }
46
+
47
+ /**
48
+ * Tooltip Transition Keyframes
49
+ *
50
+ * Sistema de animaciones CSS para transiciones de tooltip con:
51
+ * - Items staggered enter/exit
52
+ * - Grid-based height transitions
53
+ * - 3D title rotations
54
+ *
55
+ * Performance: GPU-accelerated (transform + opacity only)
56
+ */
57
+
58
+ /* ============================================================================
59
+ ITEMS ANIMATIONS - Staggered Enter/Exit
60
+ ============================================================================ */
61
+
62
+ @keyframes tooltip-item-enter {
63
+ from {
64
+ opacity: 0;
65
+ transform: translateX(-8px);
66
+ }
67
+ to {
68
+ opacity: 1;
69
+ transform: translateX(0);
70
+ }
71
+ }
72
+
73
+ @keyframes tooltip-item-exit {
74
+ from {
75
+ opacity: 1;
76
+ transform: translateX(0);
77
+ }
78
+ to {
79
+ opacity: 0;
80
+ transform: translateX(-4px);
81
+ }
82
+ }
83
+
84
+ /**
85
+ * Item States con CSS Custom Properties
86
+ *
87
+ * Uso: <div style="--animation-order: 0" data-state="entering">
88
+ */
89
+ .sidebar-sublink[data-state="entering"] {
90
+ animation: tooltip-item-enter 150ms cubic-bezier(0.4, 0, 0.2, 1);
91
+ animation-delay: calc(var(--animation-order, 0) * 30ms + 150ms);
92
+ animation-fill-mode: both;
93
+ }
94
+
95
+ .sidebar-sublink[data-state="leaving"] {
96
+ animation: tooltip-item-exit 100ms cubic-bezier(0.4, 0, 0.6, 1);
97
+ animation-delay: calc(var(--animation-order, 0) * 20ms);
98
+ animation-fill-mode: both;
99
+ }
100
+
101
+ /* ============================================================================
102
+ TITLE 3D ROTATIONS
103
+ ============================================================================ */
104
+
105
+ /**
106
+ * Title Rotate Up (direction-aware)
107
+ * Usado cuando navegamos hacia arriba en el sidebar
108
+ */
109
+ @keyframes tooltip-title-rotate-up {
110
+ from {
111
+ transform: rotateX(0deg);
112
+ opacity: 1;
113
+ }
114
+ to {
115
+ transform: rotateX(-90deg);
116
+ opacity: 0;
117
+ }
118
+ }
119
+
120
+ @keyframes tooltip-title-enter-from-below {
121
+ from {
122
+ transform: rotateX(90deg);
123
+ opacity: 0;
124
+ }
125
+ to {
126
+ transform: rotateX(0deg);
127
+ opacity: 1;
128
+ }
129
+ }
130
+
1
131
  /**
2
- * @mks2508/sidebar-headless - Complete styles
3
- *
4
- * This file contains all styles for Sidebar and MobileBottomNav components:
5
- * - Sidebar tooltip animations
6
- * - Mobile bottom navigation optimizations (iOS 26 Safari compatible)
132
+ * Title Rotate Down (direction-aware)
133
+ * Usado cuando navegamos hacia abajo en el sidebar
7
134
  */
135
+ @keyframes tooltip-title-rotate-down {
136
+ from {
137
+ transform: rotateX(0deg);
138
+ opacity: 1;
139
+ }
140
+ to {
141
+ transform: rotateX(90deg);
142
+ opacity: 0;
143
+ }
144
+ }
145
+
146
+ @keyframes tooltip-title-enter-from-above {
147
+ from {
148
+ transform: rotateX(-90deg);
149
+ opacity: 0;
150
+ }
151
+ to {
152
+ transform: rotateX(0deg);
153
+ opacity: 1;
154
+ }
155
+ }
156
+
157
+ /**
158
+ * Title States
159
+ */
160
+ .tooltip-title[data-direction="up"][data-state="leaving"] {
161
+ animation: tooltip-title-rotate-up 250ms cubic-bezier(0.4, 0, 0.2, 1) forwards;
162
+ }
163
+
164
+ .tooltip-title[data-direction="up"][data-state="entering"] {
165
+ animation: tooltip-title-enter-from-below 250ms cubic-bezier(0.4, 0, 0.2, 1) forwards;
166
+ }
167
+
168
+ .tooltip-title[data-direction="down"][data-state="leaving"] {
169
+ animation: tooltip-title-rotate-down 250ms cubic-bezier(0.4, 0, 0.2, 1) forwards;
170
+ }
171
+
172
+ .tooltip-title[data-direction="down"][data-state="entering"] {
173
+ animation: tooltip-title-enter-from-above 250ms cubic-bezier(0.4, 0, 0.2, 1) forwards;
174
+ }
175
+
176
+ /* ============================================================================
177
+ 3D CUBE FACES (Enfoque A: Multi-cara)
178
+ ============================================================================ */
179
+
180
+ /**
181
+ * Cubo 3D con 6 caras para títulos
182
+ *
183
+ * Estructura:
184
+ * .title-scene (perspective)
185
+ * └─ .title-cube (preserve-3d)
186
+ * ├─ .title-face--front
187
+ * ├─ .title-face--back
188
+ * ├─ .title-face--top
189
+ * ├─ .title-face--bottom
190
+ * ├─ .title-face--left
191
+ * └─ .title-face--right
192
+ */
193
+
194
+ .title-scene {
195
+ perspective: 600px;
196
+ position: relative;
197
+ width: 100%;
198
+ height: 2rem;
199
+ }
200
+
201
+ .title-cube {
202
+ width: 100%;
203
+ height: 100%;
204
+ position: relative;
205
+ transform-style: preserve-3d;
206
+ transition: transform 250ms cubic-bezier(0.4, 0, 0.2, 1);
207
+ /* Empujar hacia atrás para evitar blur en texto */
208
+ transform: translateZ(-1rem);
209
+ }
210
+
211
+ .title-face {
212
+ position: absolute;
213
+ width: 100%;
214
+ height: 100%;
215
+ backface-visibility: hidden;
216
+ display: flex;
217
+ align-items: center;
218
+ justify-content: flex-start;
219
+ padding: 0 1rem;
220
+ font-weight: 500;
221
+ }
222
+
223
+ /* Posicionamiento de caras - Vertical rotation (rotateX) */
224
+ .title-face--front {
225
+ transform: rotateX(0deg) translateZ(1rem);
226
+ }
227
+
228
+ .title-face--top {
229
+ transform: rotateX(90deg) translateZ(1rem);
230
+ }
231
+
232
+ .title-face--bottom {
233
+ transform: rotateX(-90deg) translateZ(1rem);
234
+ }
235
+
236
+ .title-face--back {
237
+ transform: rotateX(180deg) translateZ(1rem);
238
+ }
239
+
240
+ /* Estados del cubo - Direction-aware */
241
+ .title-cube[data-face="front"] {
242
+ transform: translateZ(-1rem) rotateX(0deg);
243
+ }
244
+
245
+ .title-cube[data-face="top"] {
246
+ transform: translateZ(-1rem) rotateX(-90deg);
247
+ }
248
+
249
+ .title-cube[data-face="bottom"] {
250
+ transform: translateZ(-1rem) rotateX(90deg);
251
+ }
252
+
253
+ .title-cube[data-face="back"] {
254
+ transform: translateZ(-1rem) rotateX(180deg);
255
+ }
256
+
257
+ /* ============================================================================
258
+ GRID HEIGHT TRANSITION (Grid Trick)
259
+ ============================================================================ */
260
+
261
+ /**
262
+ * Grid trick para height: auto transitions
263
+ *
264
+ * Técnica: grid-template-rows: 0fr → 1fr
265
+ * Browser support: Chrome 107+, Firefox 117+, Safari 16.4+
266
+ */
267
+
268
+ .tooltip-content-grid {
269
+ display: grid;
270
+ grid-template-rows: 0fr;
271
+ transition: grid-template-rows 200ms cubic-bezier(0.4, 0, 0.2, 1);
272
+ transition-delay: 50ms; /* Empieza después del fade-out de items */
273
+ }
274
+
275
+ .tooltip-content-grid[data-state="open"] {
276
+ grid-template-rows: 1fr;
277
+ }
278
+
279
+ .tooltip-content-inner {
280
+ overflow: hidden;
281
+ min-height: 0; /* Crítico para que grid trick funcione */
282
+ }
283
+
284
+ /* ============================================================================
285
+ UTILITY CLASSES
286
+ ============================================================================ */
287
+
288
+ /**
289
+ * Contenedor con perspective para títulos crossfade
290
+ */
291
+ .tooltip-title-perspective {
292
+ perspective: 600px;
293
+ position: relative;
294
+ width: 100%;
295
+ height: 2rem;
296
+ overflow: hidden;
297
+ }
298
+
299
+ /**
300
+ * Disable animations durante hover rápido (opcional)
301
+ * Usar con cuidado: puede causar flickering
302
+ */
303
+ .tooltip-no-animations * {
304
+ animation: none !important;
305
+ transition: none !important;
306
+ }
307
+
308
+ /* ============================================================================
309
+ DEBUG HELPERS
310
+ ============================================================================ */
311
+
312
+ /**
313
+ * Visualización de estados en debug mode
314
+ */
315
+ [data-tooltip-debug="true"] .sidebar-sublink[data-state]::before {
316
+ content: attr(data-state);
317
+ position: absolute;
318
+ top: 0;
319
+ right: 0;
320
+ font-size: 8px;
321
+ background: rgba(255, 0, 0, 0.8);
322
+ color: white;
323
+ padding: 2px 4px;
324
+ border-radius: 2px;
325
+ pointer-events: none;
326
+ z-index: 1000;
327
+ }
328
+
329
+ [data-tooltip-debug="true"] .tooltip-title[data-direction]::after {
330
+ content: "dir:" attr(data-direction);
331
+ position: absolute;
332
+ bottom: 0;
333
+ left: 0;
334
+ font-size: 8px;
335
+ background: rgba(0, 0, 255, 0.8);
336
+ color: white;
337
+ padding: 2px 4px;
338
+ border-radius: 2px;
339
+ pointer-events: none;
340
+ z-index: 1000;
341
+ }
342
+
343
+ /* ============================================================================
344
+ PERFORMANCE OPTIMIZATIONS
345
+ ============================================================================ */
346
+
347
+ /**
348
+ * GPU acceleration hints
349
+ */
350
+ .sidebar-sublink[data-state],
351
+ .tooltip-title[data-state],
352
+ .title-cube {
353
+ will-change: transform, opacity;
354
+ }
355
+
356
+ /**
357
+ * Contain layout/paint/style para mejor performance
358
+ */
359
+ .tooltip-content-grid {
360
+ contain: layout style paint;
361
+ }
362
+
363
+ /**
364
+ * Reduce motion para accesibilidad
365
+ */
366
+ @media (prefers-reduced-motion: reduce) {
367
+ .sidebar-sublink[data-state],
368
+ .tooltip-title[data-state],
369
+ .title-cube,
370
+ .tooltip-content-grid {
371
+ animation-duration: 0.01ms !important;
372
+ animation-iteration-count: 1 !important;
373
+ transition-duration: 0.01ms !important;
374
+ }
375
+ }
376
+
377
+ /**
378
+ * Mobile Optimizations CSS
379
+ *
380
+ * This file contains CSS optimizations for mobile devices,
381
+ * specifically targeting iOS Safari and Android Chrome.
382
+ * Updated for 2025 with modern viewport units and safe area handling.
383
+ *
384
+ * CRITICAL: iOS 26 Safari has major bugs with fixed/sticky positioning.
385
+ * This file includes workarounds for these issues.
386
+ *
387
+ * Known iOS 26 Issues (as of December 2025):
388
+ * - Fixed elements shift when address bar shrinks/expands
389
+ * - visualViewport.offsetTop doesn't reset after keyboard dismissal
390
+ * - 100dvh creates gaps at bottom for overlays
391
+ * - position: fixed breaks after keyboard interaction
392
+ *
393
+ * @fileoverview Mobile-first CSS optimizations for bottom navigation
394
+ * @author v0
395
+ * @version 2.0.0 - iOS 26 compatibility update
396
+ */
397
+
398
+ /* ============================================
399
+ CSS Custom Properties for Mobile Navigation
400
+ ============================================ */
401
+
402
+ :root {
403
+ /* Navigation dimensions */
404
+ --mobile-nav-height: 4.5rem; /* 72px - optimal touch target */
405
+ --mobile-nav-padding-x: 1rem;
406
+ --mobile-nav-padding-y: 0.5rem;
407
+ --mobile-nav-gap: 0.25rem;
408
+
409
+ /* Safe area handling for notched devices */
410
+ --safe-area-bottom: env(safe-area-inset-bottom, 0px);
411
+ --safe-area-left: env(safe-area-inset-left, 0px);
412
+ --safe-area-right: env(safe-area-inset-right, 0px);
413
+
414
+ /* Touch target sizes (WCAG 2.2 Level AAA) */
415
+ --touch-target-min: 44px;
416
+ --touch-target-optimal: 48px;
417
+
418
+ /* Glassmorphism properties */
419
+ --glass-blur: 20px;
420
+ --glass-saturation: 180%;
421
+ --glass-bg-light: rgba(255, 255, 255, 0.72);
422
+ --glass-bg-dark: rgba(17, 17, 17, 0.72);
423
+ --glass-border-light: rgba(255, 255, 255, 0.18);
424
+ --glass-border-dark: rgba(255, 255, 255, 0.08);
425
+
426
+ /* Animation timing */
427
+ --nav-transition-duration: 200ms;
428
+ --nav-transition-easing: cubic-bezier(0.4, 0, 0.2, 1);
429
+
430
+ /* iOS 26 Safari workaround: extra padding for floating bar */
431
+ --ios26-bottom-offset: 0px;
432
+ }
433
+
434
+ /* ============================================
435
+ iOS 26 Safari Critical Workarounds
436
+ ============================================
437
+
438
+ iOS 26 has major bugs with position: fixed elements:
439
+ 1. Elements shift when scrolling and address bar changes
440
+ 2. After keyboard dismissal, visualViewport doesn't reset
441
+ 3. 100dvh doesn't cover full screen in some cases
442
+
443
+ The workaround from Apple's own website:
444
+ - Use a wrapper with fixed positioning
445
+ - Apply 100vh to inner content
446
+ - This creates proper stacking under the floating URL bar
447
+ */
448
+
449
+ /* Detect iOS Safari using feature queries */
450
+ @supports (-webkit-touch-callout: none) {
451
+ /*
452
+ * iOS 26 Safari Fix:
453
+ * Move scroll from window to body to prevent fixed element drift.
454
+ * This is the most reliable workaround as of iOS 26.1
455
+ */
456
+ html.ios-safari-fix {
457
+ height: 100%;
458
+ height: 100dvh;
459
+ overflow: hidden;
460
+ }
461
+
462
+ html.ios-safari-fix body {
463
+ height: 100%;
464
+ height: 100dvh;
465
+ overflow: auto;
466
+ overscroll-behavior: contain;
467
+ /* Prevent momentum scroll affecting fixed elements */
468
+ -webkit-overflow-scrolling: touch;
469
+ }
470
+ }
471
+
472
+ /* ============================================
473
+ Modern Viewport Units Support
474
+ ============================================ */
475
+
476
+ /**
477
+ * Dynamic Viewport Height (dvh) - Adjusts as browser UI appears/disappears
478
+ * Small Viewport Height (svh) - Minimum viewport when browser UI is visible
479
+ * Large Viewport Height (lvh) - Maximum viewport when browser UI is hidden
480
+ *
481
+ * IMPORTANT for iOS 26:
482
+ * - dvh has inconsistent behavior after keyboard dismissal
483
+ * - svh is more reliable for bottom navigation
484
+ * - Consider using 100% with proper parent height chains
485
+ */
486
+
487
+ /* Fallback for older browsers (iOS < 15.4) */
488
+ @supports not (height: 100dvh) {
489
+ .mobile-nav-container {
490
+ bottom: 0;
491
+ /* iOS 11.0-11.2 legacy support */
492
+ bottom: constant(safe-area-inset-bottom);
493
+ }
494
+ }
495
+
496
+ /* Modern browsers with dynamic viewport units */
497
+ @supports (height: 100dvh) {
498
+ .mobile-nav-container {
499
+ position: fixed;
500
+ bottom: 0;
501
+ left: 0;
502
+ right: 0;
503
+ }
504
+ }
505
+
506
+ /* ============================================
507
+ Base Mobile Navigation Styles
508
+ ============================================ */
509
+
510
+ .mobile-nav-root {
511
+ /* Positioning - using fixed with iOS 26 considerations */
512
+ position: fixed;
513
+ bottom: 0;
514
+ left: 0;
515
+ right: 0;
516
+ z-index: 50;
517
+
518
+ /* Safe area padding for notched devices */
519
+ padding-bottom: var(--safe-area-bottom);
520
+ padding-left: var(--safe-area-left);
521
+ padding-right: var(--safe-area-right);
522
+
523
+ /*
524
+ * iOS 26 Fix: Force GPU layer to prevent drift
525
+ * translateZ(0) creates a new stacking context and compositing layer
526
+ */
527
+ transform: translateZ(0);
528
+ -webkit-transform: translateZ(0);
529
+
530
+ /* Hardware acceleration hints */
531
+ will-change: transform;
532
+ backface-visibility: hidden;
533
+ -webkit-backface-visibility: hidden;
534
+
535
+ /*
536
+ * iOS 26 Fix: contain property helps isolate layout
537
+ * This prevents the element from being affected by parent layout changes
538
+ */
539
+ contain: layout style;
540
+ }
541
+
542
+ /*
543
+ * Alternative iOS 26 workaround using sticky positioning
544
+ * Some developers report sticky works better than fixed in iOS 26
545
+ * Uncomment if fixed positioning continues to have issues
546
+ */
547
+ .mobile-nav-root--sticky-fallback {
548
+ position: sticky;
549
+ bottom: 0;
550
+ /* Sticky requires a positioned ancestor or viewport */
551
+ }
552
+
553
+ /* ============================================
554
+ Glassmorphism Effect
555
+ ============================================ */
556
+
557
+ .mobile-nav-glass {
558
+ /* Glassmorphism background */
559
+ background: var(--glass-bg-light);
560
+
561
+ /*
562
+ * Backdrop blur with vendor prefix for Safari
563
+ * Note: backdrop-filter can cause performance issues on older devices
564
+ */
565
+ -webkit-backdrop-filter: blur(var(--glass-blur)) saturate(var(--glass-saturation));
566
+ backdrop-filter: blur(var(--glass-blur)) saturate(var(--glass-saturation));
567
+
568
+ /* Border for depth perception */
569
+ border-top: 1px solid var(--glass-border-light);
570
+
571
+ /* Text rendering optimization */
572
+ -webkit-font-smoothing: antialiased;
573
+ -moz-osx-font-smoothing: grayscale;
574
+ }
575
+
576
+ /* Dark mode glassmorphism */
577
+ .dark .mobile-nav-glass,
578
+ [data-theme="dark"] .mobile-nav-glass {
579
+ background: var(--glass-bg-dark);
580
+ border-top-color: var(--glass-border-dark);
581
+ }
582
+
583
+ /* Fallback for browsers without backdrop-filter support */
584
+ @supports not (backdrop-filter: blur(1px)) {
585
+ .mobile-nav-glass {
586
+ background: rgba(255, 255, 255, 0.95);
587
+ }
588
+
589
+ .dark .mobile-nav-glass,
590
+ [data-theme="dark"] .mobile-nav-glass {
591
+ background: rgba(17, 17, 17, 0.95);
592
+ }
593
+ }
594
+
595
+ /* ============================================
596
+ Navigation List Styles
597
+ ============================================ */
598
+
599
+ .mobile-nav-list {
600
+ /* Flexbox layout */
601
+ display: flex;
602
+ align-items: center;
603
+ justify-content: space-around;
604
+
605
+ /* Dimensions */
606
+ height: var(--mobile-nav-height);
607
+ width: 100%;
608
+ max-width: 100%;
609
+
610
+ /* Padding */
611
+ padding: var(--mobile-nav-padding-y) var(--mobile-nav-padding-x);
612
+
613
+ /* Reset list styles */
614
+ list-style: none;
615
+ margin: 0;
616
+ }
617
+
618
+ /* ============================================
619
+ Navigation Item Styles
620
+ ============================================ */
621
+
622
+ .mobile-nav-item {
623
+ /* Flexbox centering */
624
+ display: flex;
625
+ flex-direction: column;
626
+ align-items: center;
627
+ justify-content: center;
628
+ gap: var(--mobile-nav-gap);
629
+
630
+ /* Touch target (minimum 44x44px per WCAG 2.2) */
631
+ min-width: var(--touch-target-min);
632
+ min-height: var(--touch-target-min);
633
+ padding: 0.5rem 0.75rem;
634
+
635
+ /* Reset button/anchor styles */
636
+ background: transparent;
637
+ border: none;
638
+ cursor: pointer;
639
+ text-decoration: none;
640
+
641
+ /* Typography - 16px minimum to prevent iOS zoom on focus */
642
+ font-size: 0.75rem; /* 12px for labels */
643
+ line-height: 1;
644
+
645
+ /* Transition */
646
+ transition: color var(--nav-transition-duration) var(--nav-transition-easing), transform
647
+ var(--nav-transition-duration) var(--nav-transition-easing);
648
+
649
+ /* Prevent text selection on touch */
650
+ -webkit-user-select: none;
651
+ user-select: none;
652
+
653
+ /* Prevent double-tap zoom */
654
+ touch-action: manipulation;
655
+
656
+ /* Remove tap highlight on mobile */
657
+ -webkit-tap-highlight-color: transparent;
658
+ }
659
+
660
+ /* Active/pressed state */
661
+ .mobile-nav-item:active {
662
+ transform: scale(0.95);
663
+ }
664
+
665
+ /* Focus visible for accessibility (keyboard navigation) */
666
+ .mobile-nav-item:focus-visible {
667
+ outline: 2px solid currentColor;
668
+ outline-offset: 2px;
669
+ border-radius: 0.5rem;
670
+ }
671
+
672
+ /* Icon container */
673
+ .mobile-nav-icon {
674
+ display: flex;
675
+ align-items: center;
676
+ justify-content: center;
677
+ width: 1.5rem; /* 24px */
678
+ height: 1.5rem; /* 24px */
679
+ }
680
+
681
+ /* Label text */
682
+ .mobile-nav-label {
683
+ font-weight: 500;
684
+ white-space: nowrap;
685
+ overflow: hidden;
686
+ text-overflow: ellipsis;
687
+ max-width: 4rem;
688
+ }
689
+
690
+ /* ============================================
691
+ iOS Safari Specific Fixes (All versions)
692
+ ============================================ */
693
+
694
+ @supports (-webkit-touch-callout: none) {
695
+ .mobile-nav-root {
696
+ /* Prevent rubber-banding effect on the nav */
697
+ overscroll-behavior: none;
698
+
699
+ /* Ensure proper stacking above Safari's UI */
700
+ position: fixed;
701
+ bottom: 0;
702
+ }
703
+
704
+ /* Spacer component to prevent content from going under navigation */
705
+ .mobile-nav-spacer {
706
+ height: calc(var(--mobile-nav-height) + var(--safe-area-bottom));
707
+ /* Add extra space for iOS 26 floating bar when needed */
708
+ padding-bottom: var(--ios26-bottom-offset);
709
+ }
710
+ }
711
+
712
+ /* ============================================
713
+ iOS 26+ Specific Workarounds
714
+ ============================================
715
+
716
+ These are specifically for iOS 26 Safari bugs.
717
+ The main issues are:
718
+ 1. Fixed elements drift when address bar shrinks
719
+ 2. visualViewport doesn't reset after keyboard
720
+ 3. 100dvh doesn't account for floating tab bar
721
+ */
722
+
723
+ /*
724
+ * iOS 26 Detection Hack:
725
+ * iOS 26 introduced the floating tab bar which affects layout.
726
+ * We use a combination of feature detection and JS-added classes.
727
+ */
728
+
729
+ /* When JS detects iOS 26, it adds this class to html */
730
+ html.ios-26-fix .mobile-nav-root {
731
+ /*
732
+ * Apple's workaround: Use a pseudo-element that extends beyond
733
+ * the visible area to account for the floating bar
734
+ */
735
+ position: fixed;
736
+ bottom: 0;
737
+ }
738
+
739
+ html.ios-26-fix .mobile-nav-root::after {
740
+ content: "";
741
+ position: absolute;
742
+ bottom: calc(-1 * var(--safe-area-bottom) - 20px);
743
+ left: 0;
744
+ right: 0;
745
+ height: calc(var(--safe-area-bottom) + 20px);
746
+ background: inherit;
747
+ -webkit-backdrop-filter: inherit;
748
+ backdrop-filter: inherit;
749
+ }
750
+
751
+ /*
752
+ * Alternative: Force recalculation on scroll
753
+ * This class is toggled by JS when scroll events occur
754
+ */
755
+ .mobile-nav-root--ios26-scroll-fix {
756
+ /* Trigger repaint */
757
+ transform: translateZ(0) translateY(0);
758
+ }
759
+
760
+ /* ============================================
761
+ Android Chrome Specific Fixes
762
+ ============================================ */
763
+
764
+ @supports not (-webkit-touch-callout: none) {
765
+ .mobile-nav-root {
766
+ /* Android uses standard fixed positioning well */
767
+ bottom: 0;
768
+ }
769
+
770
+ /* Android gesture navigation safe area */
771
+ .mobile-nav-spacer {
772
+ height: calc(var(--mobile-nav-height) + var(--safe-area-bottom));
773
+ }
774
+ }
775
+
776
+ /* ============================================
777
+ Keyboard Visibility Handling
778
+ ============================================
779
+
780
+ iOS 26 Bug: After keyboard dismissal, fixed elements
781
+ remain offset. This requires JS intervention.
782
+ */
783
+
784
+ /* When keyboard is visible, optionally hide nav */
785
+ html.keyboard-visible .mobile-nav-root--hide-on-keyboard {
786
+ transform: translateY(100%);
787
+ transition: transform 0.2s ease-out;
788
+ }
789
+
790
+ /* Force reset after keyboard dismissal (JS adds this class) */
791
+ html.keyboard-dismissed .mobile-nav-root {
792
+ /* Force layout recalculation */
793
+ transform: translateZ(0);
794
+ animation: ios26-reset 0.01s forwards;
795
+ }
796
+
797
+ @keyframes ios26-reset {
798
+ from {
799
+ transform: translateZ(0) translateY(0.01px);
800
+ }
801
+ to {
802
+ transform: translateZ(0) translateY(0);
803
+ }
804
+ }
805
+
806
+ /* ============================================
807
+ Reduced Motion Support
808
+ ============================================ */
809
+
810
+ @media (prefers-reduced-motion: reduce) {
811
+ .mobile-nav-item {
812
+ transition: none;
813
+ }
814
+
815
+ .mobile-nav-item:active {
816
+ transform: none;
817
+ }
818
+
819
+ html.keyboard-visible .mobile-nav-root--hide-on-keyboard {
820
+ transition: none;
821
+ }
822
+ }
823
+
824
+ /* ============================================
825
+ High Contrast Mode Support
826
+ ============================================ */
827
+
828
+ @media (prefers-contrast: high) {
829
+ .mobile-nav-glass {
830
+ background: Canvas;
831
+ border-top: 2px solid CanvasText;
832
+ -webkit-backdrop-filter: none;
833
+ backdrop-filter: none;
834
+ }
835
+
836
+ .mobile-nav-item {
837
+ color: CanvasText;
838
+ }
839
+
840
+ .mobile-nav-item:focus-visible {
841
+ outline-width: 3px;
842
+ }
843
+ }
844
+
845
+ /* ============================================
846
+ Landscape Orientation Adjustments
847
+ ============================================ */
848
+
849
+ @media (orientation: landscape) and (max-height: 500px) {
850
+ :root {
851
+ --mobile-nav-height: 3.5rem;
852
+ }
853
+
854
+ .mobile-nav-label {
855
+ display: none;
856
+ }
857
+
858
+ .mobile-nav-item {
859
+ padding: 0.25rem 0.5rem;
860
+ }
861
+ }
862
+
863
+ /* ============================================
864
+ Foldable/Dual Screen Support
865
+ ============================================ */
866
+
867
+ @media (horizontal-viewport-segments: 2) {
868
+ .mobile-nav-root {
869
+ width: 100%;
870
+ }
871
+
872
+ .mobile-nav-list {
873
+ /* Adjust for fold in the middle */
874
+ padding-left: max(var(--mobile-nav-padding-x), env(fold-left, 0px));
875
+ padding-right: max(var(--mobile-nav-padding-x), env(fold-right, 0px));
876
+ }
877
+ }
878
+
879
+ /* Samsung Galaxy Fold specific */
880
+ @media (min-width: 280px) and (max-width: 320px) {
881
+ .mobile-nav-list {
882
+ padding-left: 0.5rem;
883
+ padding-right: 0.5rem;
884
+ }
885
+ }
886
+
887
+ /* ============================================
888
+ Very Small Screens (older devices)
889
+ ============================================ */
890
+
891
+ @media (max-width: 320px) {
892
+ :root {
893
+ --mobile-nav-height: 4rem;
894
+ --mobile-nav-padding-x: 0.5rem;
895
+ }
896
+
897
+ .mobile-nav-label {
898
+ font-size: 0.625rem;
899
+ max-width: 3rem;
900
+ }
901
+ }
902
+
903
+ /* ============================================
904
+ Print Styles
905
+ ============================================ */
906
+
907
+ @media print {
908
+ .mobile-nav-root,
909
+ .mobile-nav-spacer {
910
+ display: none !important;
911
+ }
912
+ }
913
+
914
+ /* ============================================
915
+ Utility Classes
916
+ ============================================ */
917
+
918
+ /* Hide visually but keep accessible to screen readers */
919
+ .mobile-nav-sr-only {
920
+ position: absolute;
921
+ width: 1px;
922
+ height: 1px;
923
+ padding: 0;
924
+ margin: -1px;
925
+ overflow: hidden;
926
+ clip: rect(0, 0, 0, 0);
927
+ white-space: nowrap;
928
+ border: 0;
929
+ }
930
+
931
+ /* Skip to content link for keyboard users */
932
+ .mobile-nav-skip-link {
933
+ position: absolute;
934
+ top: -100%;
935
+ left: 50%;
936
+ transform: translateX(-50%);
937
+ z-index: 100;
938
+ padding: 0.5rem 1rem;
939
+ background: var(--glass-bg-light);
940
+ border-radius: 0.5rem;
941
+ transition: top 0.2s;
942
+ }
943
+
944
+ .mobile-nav-skip-link:focus {
945
+ top: 0.5rem;
946
+ }
8
947
 
9
- /* Sidebar Tooltip Animations */
10
- /**
11
- * Tooltip Transition Keyframes
12
- *
13
- * Sistema de animaciones CSS para transiciones de tooltip con:
14
- * - Items staggered enter/exit
15
- * - Grid-based height transitions
16
- * - 3D title rotations
17
- *
18
- * Performance: GPU-accelerated (transform + opacity only)
19
- */
20
-
21
- /* ============================================================================
22
- ITEMS ANIMATIONS - Staggered Enter/Exit
23
- ============================================================================ */
24
-
25
- @keyframes tooltip-item-enter {
26
- from {
27
- opacity: 0;
28
- transform: translateX(-8px);
29
- }
30
- to {
31
- opacity: 1;
32
- transform: translateX(0);
33
- }
34
- }
35
-
36
- @keyframes tooltip-item-exit {
37
- from {
38
- opacity: 1;
39
- transform: translateX(0);
40
- }
41
- to {
42
- opacity: 0;
43
- transform: translateX(-4px);
44
- }
45
- }
46
-
47
- /**
48
- * Item States con CSS Custom Properties
49
- *
50
- * Uso: <div style="--animation-order: 0" data-state="entering">
51
- */
52
- .sidebar-sublink[data-state="entering"] {
53
- animation: tooltip-item-enter 150ms cubic-bezier(0.4, 0, 0.2, 1);
54
- animation-delay: calc(var(--animation-order, 0) * 30ms + 150ms);
55
- animation-fill-mode: both;
56
- }
57
-
58
- .sidebar-sublink[data-state="leaving"] {
59
- animation: tooltip-item-exit 100ms cubic-bezier(0.4, 0, 0.6, 1);
60
- animation-delay: calc(var(--animation-order, 0) * 20ms);
61
- animation-fill-mode: both;
62
- }
63
-
64
- /* ============================================================================
65
- TITLE 3D ROTATIONS
66
- ============================================================================ */
67
-
68
- /**
69
- * Title Rotate Up (direction-aware)
70
- * Usado cuando navegamos hacia arriba en el sidebar
71
- */
72
- @keyframes tooltip-title-rotate-up {
73
- from {
74
- transform: rotateX(0deg);
75
- opacity: 1;
76
- }
77
- to {
78
- transform: rotateX(-90deg);
79
- opacity: 0;
80
- }
81
- }
82
-
83
- @keyframes tooltip-title-enter-from-below {
84
- from {
85
- transform: rotateX(90deg);
86
- opacity: 0;
87
- }
88
- to {
89
- transform: rotateX(0deg);
90
- opacity: 1;
91
- }
92
- }
93
-
94
- /**
95
- * Title Rotate Down (direction-aware)
96
- * Usado cuando navegamos hacia abajo en el sidebar
97
- */
98
- @keyframes tooltip-title-rotate-down {
99
- from {
100
- transform: rotateX(0deg);
101
- opacity: 1;
102
- }
103
- to {
104
- transform: rotateX(90deg);
105
- opacity: 0;
106
- }
107
- }
108
-
109
- @keyframes tooltip-title-enter-from-above {
110
- from {
111
- transform: rotateX(-90deg);
112
- opacity: 0;
113
- }
114
- to {
115
- transform: rotateX(0deg);
116
- opacity: 1;
117
- }
118
- }
119
-
120
- /**
121
- * Title States
122
- */
123
- .tooltip-title[data-direction="up"][data-state="leaving"] {
124
- animation: tooltip-title-rotate-up 250ms cubic-bezier(0.4, 0, 0.2, 1) forwards;
125
- }
126
-
127
- .tooltip-title[data-direction="up"][data-state="entering"] {
128
- animation: tooltip-title-enter-from-below 250ms cubic-bezier(0.4, 0, 0.2, 1) forwards;
129
- }
130
-
131
- .tooltip-title[data-direction="down"][data-state="leaving"] {
132
- animation: tooltip-title-rotate-down 250ms cubic-bezier(0.4, 0, 0.2, 1) forwards;
133
- }
134
-
135
- .tooltip-title[data-direction="down"][data-state="entering"] {
136
- animation: tooltip-title-enter-from-above 250ms cubic-bezier(0.4, 0, 0.2, 1) forwards;
137
- }
138
-
139
- /* ============================================================================
140
- 3D CUBE FACES (Enfoque A: Multi-cara)
141
- ============================================================================ */
142
-
143
- /**
144
- * Cubo 3D con 6 caras para títulos
145
- *
146
- * Estructura:
147
- * .title-scene (perspective)
148
- * └─ .title-cube (preserve-3d)
149
- * ├─ .title-face--front
150
- * ├─ .title-face--back
151
- * ├─ .title-face--top
152
- * ├─ .title-face--bottom
153
- * ├─ .title-face--left
154
- * └─ .title-face--right
155
- */
156
-
157
- .title-scene {
158
- perspective: 600px;
159
- position: relative;
160
- width: 100%;
161
- height: 2rem;
162
- }
163
-
164
- .title-cube {
165
- width: 100%;
166
- height: 100%;
167
- position: relative;
168
- transform-style: preserve-3d;
169
- transition: transform 250ms cubic-bezier(0.4, 0, 0.2, 1);
170
- /* Empujar hacia atrás para evitar blur en texto */
171
- transform: translateZ(-1rem);
172
- }
173
-
174
- .title-face {
175
- position: absolute;
176
- width: 100%;
177
- height: 100%;
178
- backface-visibility: hidden;
179
- display: flex;
180
- align-items: center;
181
- justify-content: flex-start;
182
- padding: 0 1rem;
183
- font-weight: 500;
184
- }
185
-
186
- /* Posicionamiento de caras - Vertical rotation (rotateX) */
187
- .title-face--front {
188
- transform: rotateX(0deg) translateZ(1rem);
189
- }
190
-
191
- .title-face--top {
192
- transform: rotateX(90deg) translateZ(1rem);
193
- }
194
-
195
- .title-face--bottom {
196
- transform: rotateX(-90deg) translateZ(1rem);
197
- }
198
-
199
- .title-face--back {
200
- transform: rotateX(180deg) translateZ(1rem);
201
- }
202
-
203
- /* Estados del cubo - Direction-aware */
204
- .title-cube[data-face="front"] {
205
- transform: translateZ(-1rem) rotateX(0deg);
206
- }
207
-
208
- .title-cube[data-face="top"] {
209
- transform: translateZ(-1rem) rotateX(-90deg);
210
- }
211
-
212
- .title-cube[data-face="bottom"] {
213
- transform: translateZ(-1rem) rotateX(90deg);
214
- }
215
-
216
- .title-cube[data-face="back"] {
217
- transform: translateZ(-1rem) rotateX(180deg);
218
- }
219
-
220
- /* ============================================================================
221
- GRID HEIGHT TRANSITION (Grid Trick)
222
- ============================================================================ */
223
-
224
- /**
225
- * Grid trick para height: auto transitions
226
- *
227
- * Técnica: grid-template-rows: 0fr → 1fr
228
- * Browser support: Chrome 107+, Firefox 117+, Safari 16.4+
229
- */
230
-
231
- .tooltip-content-grid {
232
- display: grid;
233
- grid-template-rows: 0fr;
234
- transition: grid-template-rows 200ms cubic-bezier(0.4, 0, 0.2, 1);
235
- transition-delay: 50ms; /* Empieza después del fade-out de items */
236
- }
237
-
238
- .tooltip-content-grid[data-state="open"] {
239
- grid-template-rows: 1fr;
240
- }
241
-
242
- .tooltip-content-inner {
243
- overflow: hidden;
244
- min-height: 0; /* Crítico para que grid trick funcione */
245
- }
246
-
247
- /* ============================================================================
248
- UTILITY CLASSES
249
- ============================================================================ */
250
-
251
- /**
252
- * Contenedor con perspective para títulos crossfade
253
- */
254
- .tooltip-title-perspective {
255
- perspective: 600px;
256
- position: relative;
257
- width: 100%;
258
- height: 2rem;
259
- overflow: hidden;
260
- }
261
-
262
- /**
263
- * Disable animations durante hover rápido (opcional)
264
- * Usar con cuidado: puede causar flickering
265
- */
266
- .tooltip-no-animations * {
267
- animation: none !important;
268
- transition: none !important;
269
- }
270
-
271
- /* ============================================================================
272
- DEBUG HELPERS
273
- ============================================================================ */
274
-
275
- /**
276
- * Visualización de estados en debug mode
277
- */
278
- [data-tooltip-debug="true"] .sidebar-sublink[data-state]::before {
279
- content: attr(data-state);
280
- position: absolute;
281
- top: 0;
282
- right: 0;
283
- font-size: 8px;
284
- background: rgba(255, 0, 0, 0.8);
285
- color: white;
286
- padding: 2px 4px;
287
- border-radius: 2px;
288
- pointer-events: none;
289
- z-index: 1000;
290
- }
291
-
292
- [data-tooltip-debug="true"] .tooltip-title[data-direction]::after {
293
- content: "dir:" attr(data-direction);
294
- position: absolute;
295
- bottom: 0;
296
- left: 0;
297
- font-size: 8px;
298
- background: rgba(0, 0, 255, 0.8);
299
- color: white;
300
- padding: 2px 4px;
301
- border-radius: 2px;
302
- pointer-events: none;
303
- z-index: 1000;
304
- }
305
-
306
- /* ============================================================================
307
- PERFORMANCE OPTIMIZATIONS
308
- ============================================================================ */
309
-
310
- /**
311
- * GPU acceleration hints
312
- */
313
- .sidebar-sublink[data-state],
314
- .tooltip-title[data-state],
315
- .title-cube {
316
- will-change: transform, opacity;
317
- }
318
-
319
- /**
320
- * Contain layout/paint/style para mejor performance
321
- */
322
- .tooltip-content-grid {
323
- contain: layout style paint;
324
- }
325
-
326
- /**
327
- * Reduce motion para accesibilidad
328
- */
329
- @media (prefers-reduced-motion: reduce) {
330
- .sidebar-sublink[data-state],
331
- .tooltip-title[data-state],
332
- .title-cube,
333
- .tooltip-content-grid {
334
- animation-duration: 0.01ms !important;
335
- animation-iteration-count: 1 !important;
336
- transition-duration: 0.01ms !important;
337
- }
338
- }
339
-
340
-
341
- /* Mobile Bottom Navigation Optimizations */
342
- /**
343
- * Mobile Optimizations CSS
344
- *
345
- * This file contains CSS optimizations for mobile devices,
346
- * specifically targeting iOS Safari and Android Chrome.
347
- * Updated for 2025 with modern viewport units and safe area handling.
348
- *
349
- * CRITICAL: iOS 26 Safari has major bugs with fixed/sticky positioning.
350
- * This file includes workarounds for these issues.
351
- *
352
- * Known iOS 26 Issues (as of December 2025):
353
- * - Fixed elements shift when address bar shrinks/expands
354
- * - visualViewport.offsetTop doesn't reset after keyboard dismissal
355
- * - 100dvh creates gaps at bottom for overlays
356
- * - position: fixed breaks after keyboard interaction
357
- *
358
- * @fileoverview Mobile-first CSS optimizations for bottom navigation
359
- * @author v0
360
- * @version 2.0.0 - iOS 26 compatibility update
361
- */
362
-
363
- /* ============================================
364
- CSS Custom Properties for Mobile Navigation
365
- ============================================ */
366
-
367
- :root {
368
- /* Navigation dimensions */
369
- --mobile-nav-height: 4.5rem; /* 72px - optimal touch target */
370
- --mobile-nav-padding-x: 1rem;
371
- --mobile-nav-padding-y: 0.5rem;
372
- --mobile-nav-gap: 0.25rem;
373
-
374
- /* Safe area handling for notched devices */
375
- --safe-area-bottom: env(safe-area-inset-bottom, 0px);
376
- --safe-area-left: env(safe-area-inset-left, 0px);
377
- --safe-area-right: env(safe-area-inset-right, 0px);
378
-
379
- /* Touch target sizes (WCAG 2.2 Level AAA) */
380
- --touch-target-min: 44px;
381
- --touch-target-optimal: 48px;
382
-
383
- /* Glassmorphism properties */
384
- --glass-blur: 20px;
385
- --glass-saturation: 180%;
386
- --glass-bg-light: rgba(255, 255, 255, 0.72);
387
- --glass-bg-dark: rgba(17, 17, 17, 0.72);
388
- --glass-border-light: rgba(255, 255, 255, 0.18);
389
- --glass-border-dark: rgba(255, 255, 255, 0.08);
390
-
391
- /* Animation timing */
392
- --nav-transition-duration: 200ms;
393
- --nav-transition-easing: cubic-bezier(0.4, 0, 0.2, 1);
394
-
395
- /* iOS 26 Safari workaround: extra padding for floating bar */
396
- --ios26-bottom-offset: 0px;
397
- }
398
-
399
- /* ============================================
400
- iOS 26 Safari Critical Workarounds
401
- ============================================
402
-
403
- iOS 26 has major bugs with position: fixed elements:
404
- 1. Elements shift when scrolling and address bar changes
405
- 2. After keyboard dismissal, visualViewport doesn't reset
406
- 3. 100dvh doesn't cover full screen in some cases
407
-
408
- The workaround from Apple's own website:
409
- - Use a wrapper with fixed positioning
410
- - Apply 100vh to inner content
411
- - This creates proper stacking under the floating URL bar
412
- */
413
-
414
- /* Detect iOS Safari using feature queries */
415
- @supports (-webkit-touch-callout: none) {
416
- /*
417
- * iOS 26 Safari Fix:
418
- * Move scroll from window to body to prevent fixed element drift.
419
- * This is the most reliable workaround as of iOS 26.1
420
- */
421
- html.ios-safari-fix {
422
- height: 100%;
423
- height: 100dvh;
424
- overflow: hidden;
425
- }
426
-
427
- html.ios-safari-fix body {
428
- height: 100%;
429
- height: 100dvh;
430
- overflow: auto;
431
- overscroll-behavior: contain;
432
- /* Prevent momentum scroll affecting fixed elements */
433
- -webkit-overflow-scrolling: touch;
434
- }
435
- }
436
-
437
- /* ============================================
438
- Modern Viewport Units Support
439
- ============================================ */
440
-
441
- /**
442
- * Dynamic Viewport Height (dvh) - Adjusts as browser UI appears/disappears
443
- * Small Viewport Height (svh) - Minimum viewport when browser UI is visible
444
- * Large Viewport Height (lvh) - Maximum viewport when browser UI is hidden
445
- *
446
- * IMPORTANT for iOS 26:
447
- * - dvh has inconsistent behavior after keyboard dismissal
448
- * - svh is more reliable for bottom navigation
449
- * - Consider using 100% with proper parent height chains
450
- */
451
-
452
- /* Fallback for older browsers (iOS < 15.4) */
453
- @supports not (height: 100dvh) {
454
- .mobile-nav-container {
455
- bottom: 0;
456
- /* iOS 11.0-11.2 legacy support */
457
- bottom: constant(safe-area-inset-bottom);
458
- }
459
- }
460
-
461
- /* Modern browsers with dynamic viewport units */
462
- @supports (height: 100dvh) {
463
- .mobile-nav-container {
464
- position: fixed;
465
- bottom: 0;
466
- left: 0;
467
- right: 0;
468
- }
469
- }
470
-
471
- /* ============================================
472
- Base Mobile Navigation Styles
473
- ============================================ */
474
-
475
- .mobile-nav-root {
476
- /* Positioning - using fixed with iOS 26 considerations */
477
- position: fixed;
478
- bottom: 0;
479
- left: 0;
480
- right: 0;
481
- z-index: 50;
482
-
483
- /* Safe area padding for notched devices */
484
- padding-bottom: var(--safe-area-bottom);
485
- padding-left: var(--safe-area-left);
486
- padding-right: var(--safe-area-right);
487
-
488
- /*
489
- * iOS 26 Fix: Force GPU layer to prevent drift
490
- * translateZ(0) creates a new stacking context and compositing layer
491
- */
492
- transform: translateZ(0);
493
- -webkit-transform: translateZ(0);
494
-
495
- /* Hardware acceleration hints */
496
- will-change: transform;
497
- backface-visibility: hidden;
498
- -webkit-backface-visibility: hidden;
499
-
500
- /*
501
- * iOS 26 Fix: contain property helps isolate layout
502
- * This prevents the element from being affected by parent layout changes
503
- */
504
- contain: layout style;
505
- }
506
-
507
- /*
508
- * Alternative iOS 26 workaround using sticky positioning
509
- * Some developers report sticky works better than fixed in iOS 26
510
- * Uncomment if fixed positioning continues to have issues
511
- */
512
- .mobile-nav-root--sticky-fallback {
513
- position: sticky;
514
- bottom: 0;
515
- /* Sticky requires a positioned ancestor or viewport */
516
- }
517
-
518
- /* ============================================
519
- Glassmorphism Effect
520
- ============================================ */
521
-
522
- .mobile-nav-glass {
523
- /* Glassmorphism background */
524
- background: var(--glass-bg-light);
525
-
526
- /*
527
- * Backdrop blur with vendor prefix for Safari
528
- * Note: backdrop-filter can cause performance issues on older devices
529
- */
530
- -webkit-backdrop-filter: blur(var(--glass-blur)) saturate(var(--glass-saturation));
531
- backdrop-filter: blur(var(--glass-blur)) saturate(var(--glass-saturation));
532
-
533
- /* Border for depth perception */
534
- border-top: 1px solid var(--glass-border-light);
535
-
536
- /* Text rendering optimization */
537
- -webkit-font-smoothing: antialiased;
538
- -moz-osx-font-smoothing: grayscale;
539
- }
540
-
541
- /* Dark mode glassmorphism */
542
- .dark .mobile-nav-glass,
543
- [data-theme="dark"] .mobile-nav-glass {
544
- background: var(--glass-bg-dark);
545
- border-top-color: var(--glass-border-dark);
546
- }
547
-
548
- /* Fallback for browsers without backdrop-filter support */
549
- @supports not (backdrop-filter: blur(1px)) {
550
- .mobile-nav-glass {
551
- background: rgba(255, 255, 255, 0.95);
552
- }
553
-
554
- .dark .mobile-nav-glass,
555
- [data-theme="dark"] .mobile-nav-glass {
556
- background: rgba(17, 17, 17, 0.95);
557
- }
558
- }
559
-
560
- /* ============================================
561
- Navigation List Styles
562
- ============================================ */
563
-
564
- .mobile-nav-list {
565
- /* Flexbox layout */
566
- display: flex;
567
- align-items: center;
568
- justify-content: space-around;
569
-
570
- /* Dimensions */
571
- height: var(--mobile-nav-height);
572
- width: 100%;
573
- max-width: 100%;
574
-
575
- /* Padding */
576
- padding: var(--mobile-nav-padding-y) var(--mobile-nav-padding-x);
577
-
578
- /* Reset list styles */
579
- list-style: none;
580
- margin: 0;
581
- }
582
-
583
- /* ============================================
584
- Navigation Item Styles
585
- ============================================ */
586
-
587
- .mobile-nav-item {
588
- /* Flexbox centering */
589
- display: flex;
590
- flex-direction: column;
591
- align-items: center;
592
- justify-content: center;
593
- gap: var(--mobile-nav-gap);
594
-
595
- /* Touch target (minimum 44x44px per WCAG 2.2) */
596
- min-width: var(--touch-target-min);
597
- min-height: var(--touch-target-min);
598
- padding: 0.5rem 0.75rem;
599
-
600
- /* Reset button/anchor styles */
601
- background: transparent;
602
- border: none;
603
- cursor: pointer;
604
- text-decoration: none;
605
-
606
- /* Typography - 16px minimum to prevent iOS zoom on focus */
607
- font-size: 0.75rem; /* 12px for labels */
608
- line-height: 1;
609
-
610
- /* Transition */
611
- transition: color var(--nav-transition-duration) var(--nav-transition-easing), transform
612
- var(--nav-transition-duration) var(--nav-transition-easing);
613
-
614
- /* Prevent text selection on touch */
615
- -webkit-user-select: none;
616
- user-select: none;
617
-
618
- /* Prevent double-tap zoom */
619
- touch-action: manipulation;
620
-
621
- /* Remove tap highlight on mobile */
622
- -webkit-tap-highlight-color: transparent;
623
- }
624
-
625
- /* Active/pressed state */
626
- .mobile-nav-item:active {
627
- transform: scale(0.95);
628
- }
629
-
630
- /* Focus visible for accessibility (keyboard navigation) */
631
- .mobile-nav-item:focus-visible {
632
- outline: 2px solid currentColor;
633
- outline-offset: 2px;
634
- border-radius: 0.5rem;
635
- }
636
-
637
- /* Icon container */
638
- .mobile-nav-icon {
639
- display: flex;
640
- align-items: center;
641
- justify-content: center;
642
- width: 1.5rem; /* 24px */
643
- height: 1.5rem; /* 24px */
644
- }
645
-
646
- /* Label text */
647
- .mobile-nav-label {
648
- font-weight: 500;
649
- white-space: nowrap;
650
- overflow: hidden;
651
- text-overflow: ellipsis;
652
- max-width: 4rem;
653
- }
654
-
655
- /* ============================================
656
- iOS Safari Specific Fixes (All versions)
657
- ============================================ */
658
-
659
- @supports (-webkit-touch-callout: none) {
660
- .mobile-nav-root {
661
- /* Prevent rubber-banding effect on the nav */
662
- overscroll-behavior: none;
663
-
664
- /* Ensure proper stacking above Safari's UI */
665
- position: fixed;
666
- bottom: 0;
667
- }
668
-
669
- /* Spacer component to prevent content from going under navigation */
670
- .mobile-nav-spacer {
671
- height: calc(var(--mobile-nav-height) + var(--safe-area-bottom));
672
- /* Add extra space for iOS 26 floating bar when needed */
673
- padding-bottom: var(--ios26-bottom-offset);
674
- }
675
- }
676
-
677
- /* ============================================
678
- iOS 26+ Specific Workarounds
679
- ============================================
680
-
681
- These are specifically for iOS 26 Safari bugs.
682
- The main issues are:
683
- 1. Fixed elements drift when address bar shrinks
684
- 2. visualViewport doesn't reset after keyboard
685
- 3. 100dvh doesn't account for floating tab bar
686
- */
687
-
688
- /*
689
- * iOS 26 Detection Hack:
690
- * iOS 26 introduced the floating tab bar which affects layout.
691
- * We use a combination of feature detection and JS-added classes.
692
- */
693
-
694
- /* When JS detects iOS 26, it adds this class to html */
695
- html.ios-26-fix .mobile-nav-root {
696
- /*
697
- * Apple's workaround: Use a pseudo-element that extends beyond
698
- * the visible area to account for the floating bar
699
- */
700
- position: fixed;
701
- bottom: 0;
702
- }
703
-
704
- html.ios-26-fix .mobile-nav-root::after {
705
- content: "";
706
- position: absolute;
707
- bottom: calc(-1 * var(--safe-area-bottom) - 20px);
708
- left: 0;
709
- right: 0;
710
- height: calc(var(--safe-area-bottom) + 20px);
711
- background: inherit;
712
- -webkit-backdrop-filter: inherit;
713
- backdrop-filter: inherit;
714
- }
715
-
716
- /*
717
- * Alternative: Force recalculation on scroll
718
- * This class is toggled by JS when scroll events occur
719
- */
720
- .mobile-nav-root--ios26-scroll-fix {
721
- /* Trigger repaint */
722
- transform: translateZ(0) translateY(0);
723
- }
724
-
725
- /* ============================================
726
- Android Chrome Specific Fixes
727
- ============================================ */
728
-
729
- @supports not (-webkit-touch-callout: none) {
730
- .mobile-nav-root {
731
- /* Android uses standard fixed positioning well */
732
- bottom: 0;
733
- }
734
-
735
- /* Android gesture navigation safe area */
736
- .mobile-nav-spacer {
737
- height: calc(var(--mobile-nav-height) + var(--safe-area-bottom));
738
- }
739
- }
740
-
741
- /* ============================================
742
- Keyboard Visibility Handling
743
- ============================================
744
-
745
- iOS 26 Bug: After keyboard dismissal, fixed elements
746
- remain offset. This requires JS intervention.
747
- */
748
-
749
- /* When keyboard is visible, optionally hide nav */
750
- html.keyboard-visible .mobile-nav-root--hide-on-keyboard {
751
- transform: translateY(100%);
752
- transition: transform 0.2s ease-out;
753
- }
754
-
755
- /* Force reset after keyboard dismissal (JS adds this class) */
756
- html.keyboard-dismissed .mobile-nav-root {
757
- /* Force layout recalculation */
758
- transform: translateZ(0);
759
- animation: ios26-reset 0.01s forwards;
760
- }
761
-
762
- @keyframes ios26-reset {
763
- from {
764
- transform: translateZ(0) translateY(0.01px);
765
- }
766
- to {
767
- transform: translateZ(0) translateY(0);
768
- }
769
- }
770
-
771
- /* ============================================
772
- Reduced Motion Support
773
- ============================================ */
774
-
775
- @media (prefers-reduced-motion: reduce) {
776
- .mobile-nav-item {
777
- transition: none;
778
- }
779
-
780
- .mobile-nav-item:active {
781
- transform: none;
782
- }
783
-
784
- html.keyboard-visible .mobile-nav-root--hide-on-keyboard {
785
- transition: none;
786
- }
787
- }
788
-
789
- /* ============================================
790
- High Contrast Mode Support
791
- ============================================ */
792
-
793
- @media (prefers-contrast: high) {
794
- .mobile-nav-glass {
795
- background: Canvas;
796
- border-top: 2px solid CanvasText;
797
- -webkit-backdrop-filter: none;
798
- backdrop-filter: none;
799
- }
800
-
801
- .mobile-nav-item {
802
- color: CanvasText;
803
- }
804
-
805
- .mobile-nav-item:focus-visible {
806
- outline-width: 3px;
807
- }
808
- }
809
-
810
- /* ============================================
811
- Landscape Orientation Adjustments
812
- ============================================ */
813
-
814
- @media (orientation: landscape) and (max-height: 500px) {
815
- :root {
816
- --mobile-nav-height: 3.5rem;
817
- }
818
-
819
- .mobile-nav-label {
820
- display: none;
821
- }
822
-
823
- .mobile-nav-item {
824
- padding: 0.25rem 0.5rem;
825
- }
826
- }
827
-
828
- /* ============================================
829
- Foldable/Dual Screen Support
830
- ============================================ */
831
-
832
- @media (horizontal-viewport-segments: 2) {
833
- .mobile-nav-root {
834
- width: 100%;
835
- }
836
-
837
- .mobile-nav-list {
838
- /* Adjust for fold in the middle */
839
- padding-left: max(var(--mobile-nav-padding-x), env(fold-left, 0px));
840
- padding-right: max(var(--mobile-nav-padding-x), env(fold-right, 0px));
841
- }
842
- }
843
-
844
- /* Samsung Galaxy Fold specific */
845
- @media (min-width: 280px) and (max-width: 320px) {
846
- .mobile-nav-list {
847
- padding-left: 0.5rem;
848
- padding-right: 0.5rem;
849
- }
850
- }
851
-
852
- /* ============================================
853
- Very Small Screens (older devices)
854
- ============================================ */
855
-
856
- @media (max-width: 320px) {
857
- :root {
858
- --mobile-nav-height: 4rem;
859
- --mobile-nav-padding-x: 0.5rem;
860
- }
861
-
862
- .mobile-nav-label {
863
- font-size: 0.625rem;
864
- max-width: 3rem;
865
- }
866
- }
867
-
868
- /* ============================================
869
- Print Styles
870
- ============================================ */
871
-
872
- @media print {
873
- .mobile-nav-root,
874
- .mobile-nav-spacer {
875
- display: none !important;
876
- }
877
- }
878
-
879
- /* ============================================
880
- Utility Classes
881
- ============================================ */
882
-
883
- /* Hide visually but keep accessible to screen readers */
884
- .mobile-nav-sr-only {
885
- position: absolute;
886
- width: 1px;
887
- height: 1px;
888
- padding: 0;
889
- margin: -1px;
890
- overflow: hidden;
891
- clip: rect(0, 0, 0, 0);
892
- white-space: nowrap;
893
- border: 0;
894
- }
895
-
896
- /* Skip to content link for keyboard users */
897
- .mobile-nav-skip-link {
898
- position: absolute;
899
- top: -100%;
900
- left: 50%;
901
- transform: translateX(-50%);
902
- z-index: 100;
903
- padding: 0.5rem 1rem;
904
- background: var(--glass-bg-light);
905
- border-radius: 0.5rem;
906
- transition: top 0.2s;
907
- }
908
-
909
- .mobile-nav-skip-link:focus {
910
- top: 0.5rem;
911
- }
912
948
 
949
+ /*# sourceMappingURL=index.css.map*/