Yacine Jernite commited on
Commit
f07b5d3
·
1 Parent(s): 1238bb5

sticky_nav

Browse files
js/components/PageNavigation.js CHANGED
@@ -13,20 +13,34 @@ export function renderPageNavigation(label, items) {
13
  }
14
 
15
  return `
16
- <section class="mb-8 relative z-20 px-4 sm:px-6 lg:px-8">
17
- <div class="bg-white/85 backdrop-blur-sm shadow-md rounded-lg px-6 py-4 w-full border border-gray-200" style="max-width: min(90%, 1400px); margin: 0 auto;">
18
- <div class="flex items-center gap-3 overflow-x-auto">
19
- <span class="text-gray-600 font-semibold whitespace-nowrap text-base">${label}</span>
20
- ${items.map((item, index) => {
21
- const activeClasses = item.active
22
- ? 'text-blue-600 font-semibold underline decoration-2 underline-offset-2'
23
- : 'text-gray-700 hover:text-blue-600';
24
-
25
- return `
26
- ${index > 0 ? '<span class="text-gray-300">•</span>' : ''}
27
- <a href="${item.href}" class="px-4 py-2 text-base font-medium ${activeClasses} hover:bg-blue-50 rounded transition-colors whitespace-nowrap">${item.text}</a>
28
- `;
29
- }).join('')}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
30
  </div>
31
  </div>
32
  </section>
 
13
  }
14
 
15
  return `
16
+ <section class="page-navigation mb-8 relative z-20 px-4 sm:px-6 lg:px-8">
17
+ <div id="nav-container" class="bg-white/85 backdrop-blur-sm shadow-md rounded-lg px-6 py-4 w-full border border-gray-200" style="max-width: min(90%, 1400px); margin: 0 auto;">
18
+ <div class="flex items-center justify-between gap-3">
19
+ <!-- Navigation items -->
20
+ <div id="nav-items" class="flex items-center gap-3 overflow-x-auto flex-1">
21
+ <span class="text-gray-600 font-semibold whitespace-nowrap text-base">${label}</span>
22
+ ${items.map((item, index) => {
23
+ const activeClasses = item.active
24
+ ? 'text-blue-600 font-semibold underline decoration-2 underline-offset-2'
25
+ : 'text-gray-700 hover:text-blue-600';
26
+
27
+ return `
28
+ ${index > 0 ? '<span class="text-gray-300">•</span>' : ''}
29
+ <a href="${item.href}" class="px-4 py-2 text-base font-medium ${activeClasses} hover:bg-blue-50 rounded whitespace-nowrap">${item.text}</a>
30
+ `;
31
+ }).join('')}
32
+ </div>
33
+
34
+ <!-- Hamburger toggle -->
35
+ <button id="nav-toggle"
36
+ class="flex-shrink-0 p-2 rounded-lg hover:bg-gray-100"
37
+ onclick="toggleNavigation()"
38
+ aria-label="Toggle navigation menu"
39
+ aria-expanded="true">
40
+ <svg class="w-6 h-6 text-gray-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
41
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h16"></path>
42
+ </svg>
43
+ </button>
44
  </div>
45
  </div>
46
  </section>
js/pages/AreaPage.js CHANGED
@@ -3,6 +3,7 @@ import { getFeaturedArtifacts } from '../init.js';
3
  import { createArtifactCarousel } from '../components/Carousel.js';
4
  import { renderAreaNavigation } from '../components/PageNavigation.js';
5
  import { renderContentSection, renderOpennessCallout } from '../components/ContentSection.js';
 
6
 
7
  // Use global areasData (loaded in index.html <head>)
8
  const areasData = window.areasData;
@@ -109,6 +110,7 @@ function renderFullAreaPage(area, areaId, initialTopicId = null) {
109
  topics.forEach(topic => {
110
  initializeTopicArtifactsCarousel(areaId, topic.id);
111
  });
 
112
  }
113
  };
114
  }
 
3
  import { createArtifactCarousel } from '../components/Carousel.js';
4
  import { renderAreaNavigation } from '../components/PageNavigation.js';
5
  import { renderContentSection, renderOpennessCallout } from '../components/ContentSection.js';
6
+ import { initializeStickyNavigation } from '../utils/stickyNavigation.js';
7
 
8
  // Use global areasData (loaded in index.html <head>)
9
  const areasData = window.areasData;
 
110
  topics.forEach(topic => {
111
  initializeTopicArtifactsCarousel(areaId, topic.id);
112
  });
113
+ initializeStickyNavigation('nav-container');
114
  }
115
  };
116
  }
js/pages/HomePage.js CHANGED
@@ -6,6 +6,7 @@ import { teamMembers } from '../data/team.js';
6
  import { renderAreaCard } from '../components/Card.js';
7
  import { renderHomeNavigation } from '../components/PageNavigation.js';
8
  import { renderContentSection, renderOpennessCallout } from '../components/ContentSection.js';
 
9
 
10
  // Use global areasData and backgrounds (loaded in index.html <head>)
11
  const areasData = window.areasData;
@@ -94,6 +95,7 @@ export function renderHomePage() {
94
  initializeHomeAreaCards();
95
  initializeArtifactCarousels();
96
  initializeBackgroundAttribution();
 
97
  }
98
  };
99
  }
 
6
  import { renderAreaCard } from '../components/Card.js';
7
  import { renderHomeNavigation } from '../components/PageNavigation.js';
8
  import { renderContentSection, renderOpennessCallout } from '../components/ContentSection.js';
9
+ import { initializeStickyNavigation } from '../utils/stickyNavigation.js';
10
 
11
  // Use global areasData and backgrounds (loaded in index.html <head>)
12
  const areasData = window.areasData;
 
95
  initializeHomeAreaCards();
96
  initializeArtifactCarousels();
97
  initializeBackgroundAttribution();
98
+ initializeStickyNavigation('nav-container');
99
  }
100
  };
101
  }
js/utils/stickyNavigation.js ADDED
@@ -0,0 +1,59 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // stickyNavigation.js - Sticky navigation behavior and hamburger menu
2
+ // Handles: sticky positioning on scroll + collapse/expand toggle
3
+
4
+ /**
5
+ * Initializes sticky navigation behavior for a page
6
+ * @param {string} navElementId - ID of the navigation element (without #)
7
+ */
8
+ export function initializeStickyNavigation(navElementId) {
9
+ const navElement = document.getElementById(navElementId);
10
+ if (!navElement) {
11
+ console.warn(`Navigation element #${navElementId} not found`);
12
+ return;
13
+ }
14
+
15
+ const navSection = navElement.closest('section.page-navigation');
16
+ if (!navSection) {
17
+ console.warn('Navigation section wrapper not found');
18
+ return;
19
+ }
20
+
21
+ // Calculate sticky threshold: when nav reaches bottom of header
22
+ const headerHeight = parseInt(getComputedStyle(document.documentElement)
23
+ .getPropertyValue('--header-height')) || 100;
24
+ const stickyThreshold = navSection.offsetTop - headerHeight;
25
+
26
+ // Scroll handler with throttling
27
+ let scrollTimeout;
28
+ const handleScroll = () => {
29
+ const scrollPosition = window.pageYOffset || document.documentElement.scrollTop;
30
+ navSection.classList.toggle('sticky-nav', scrollPosition >= stickyThreshold);
31
+ };
32
+
33
+ window.addEventListener('scroll', () => {
34
+ if (scrollTimeout) cancelAnimationFrame(scrollTimeout);
35
+ scrollTimeout = requestAnimationFrame(handleScroll);
36
+ });
37
+
38
+ handleScroll(); // Initial check
39
+ }
40
+
41
+ /**
42
+ * Toggle navigation visibility (hamburger menu)
43
+ * Exposed globally for onclick handler
44
+ */
45
+ window.toggleNavigation = function() {
46
+ const navItems = document.getElementById('nav-items');
47
+ const navToggle = document.getElementById('nav-toggle');
48
+ const navContainer = document.getElementById('nav-container');
49
+
50
+ if (!navItems || !navToggle) return;
51
+
52
+ const isCollapsed = navItems.classList.contains('nav-collapsed');
53
+
54
+ // Toggle classes and ARIA attribute
55
+ navItems.classList.toggle('nav-collapsed', !isCollapsed);
56
+ navToggle.setAttribute('aria-expanded', isCollapsed ? 'true' : 'false');
57
+ navContainer?.classList.toggle('nav-container-collapsed', !isCollapsed);
58
+ };
59
+
styles.css CHANGED
@@ -147,6 +147,91 @@ p {
147
  }
148
  }
149
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
150
  /* ============================================
151
  Visual Effects
152
  ============================================ */
 
147
  }
148
  }
149
 
150
+ /* Sticky Page Navigation */
151
+ .page-navigation {
152
+ transition: all 0.3s ease;
153
+ }
154
+
155
+ #nav-container {
156
+ transition: all 0.3s ease;
157
+ }
158
+
159
+ #nav-items {
160
+ transition: all 0.3s ease;
161
+ }
162
+
163
+ #nav-toggle {
164
+ transition: all 0.2s ease;
165
+ }
166
+
167
+ .page-navigation.sticky-nav {
168
+ position: fixed;
169
+ top: var(--header-height);
170
+ left: 0;
171
+ right: 0;
172
+ z-index: 30;
173
+ padding: 0.5rem 0;
174
+ }
175
+
176
+ .page-navigation.sticky-nav #nav-container {
177
+ background: rgba(255, 255, 255, 0.95);
178
+ backdrop-filter: blur(16px);
179
+ -webkit-backdrop-filter: blur(16px);
180
+ padding: 0.75rem 1.5rem;
181
+ box-shadow: 0 6px 12px -2px rgba(0, 0, 0, 0.12);
182
+ border-bottom: 1px solid rgba(0, 0, 0, 0.06);
183
+ }
184
+
185
+ /* Add breathing room between sticky nav and content */
186
+ .page-navigation.sticky-nav ~ section:first-of-type,
187
+ .page-navigation.sticky-nav ~ div:first-of-type {
188
+ margin-top: 0.75rem;
189
+ }
190
+
191
+ /* Navigation collapse/expand */
192
+ #nav-items.nav-collapsed {
193
+ max-height: 0;
194
+ opacity: 0;
195
+ margin: 0;
196
+ padding: 0;
197
+ width: 0;
198
+ }
199
+
200
+ /* Compact container when collapsed - just the hamburger icon */
201
+ .nav-container-collapsed {
202
+ padding: 0.5rem !important;
203
+ width: auto !important;
204
+ max-width: fit-content !important;
205
+ margin-left: auto !important;
206
+ margin-right: 1.5rem !important;
207
+ }
208
+
209
+ /* Also make the parent section compact when collapsed */
210
+ .page-navigation.sticky-nav:has(.nav-container-collapsed) {
211
+ padding: 0.25rem 0;
212
+ }
213
+
214
+ .page-navigation.sticky-nav .nav-container-collapsed {
215
+ margin: 0 1.5rem 0 auto;
216
+ }
217
+
218
+ /* Mobile adjustments for sticky nav */
219
+ @media (max-width: 767px) {
220
+ .page-navigation.sticky-nav {
221
+ top: var(--header-height);
222
+ padding: 0.25rem 0; /* Less padding on mobile */
223
+ }
224
+
225
+ .page-navigation.sticky-nav #nav-container {
226
+ padding: 0.5rem 1rem;
227
+ }
228
+
229
+ .page-navigation.sticky-nav ~ section:first-of-type,
230
+ .page-navigation.sticky-nav ~ div:first-of-type {
231
+ margin-top: 0.5rem; /* Less space on mobile */
232
+ }
233
+ }
234
+
235
  /* ============================================
236
  Visual Effects
237
  ============================================ */