Yacine Jernite commited on
Commit
72be969
Β·
1 Parent(s): 803ec5a
index.html CHANGED
@@ -3,7 +3,33 @@
3
  <head>
4
  <meta charset="UTF-8">
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
- <title>Machine Learning and Society at πŸ€—</title>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7
  <script src="https://cdn.tailwindcss.com"></script>
8
  <!-- Import map for lit-html -->
9
  <script type="importmap">
@@ -20,7 +46,6 @@
20
  window.navigationAreas = getNavigationData();
21
  window.homeBackgroundImage = homeBackgroundImage;
22
  window.overallBackgroundImage = overallBackgroundImage;
23
- console.log('βœ“ Areas loaded in head:', Object.keys(areasData).length);
24
  </script>
25
  <!-- Alpine.js can now safely use window.navigationAreas -->
26
  <script src="https://cdn.jsdelivr.net/npm/alpinejs@3.x.x/dist/cdn.min.js" defer></script>
@@ -39,11 +64,72 @@
39
  /* Apply fonts to base elements */
40
  body {
41
  font-family: 'Source Sans Pro', sans-serif;
42
- --header-height: 100px; /* Two-line navigation menu */
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
43
  }
44
 
45
  h1, h2, h3, h4, h5, h6 {
46
  font-family: 'Montserrat', sans-serif;
 
 
 
 
 
 
 
 
 
 
 
 
47
  }
48
 
49
  /* Ensure all major containers respect viewport width */
@@ -59,6 +145,23 @@
59
  }
60
  }
61
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
62
  /* Navigation overflow handling */
63
  @media (min-width: 769px) {
64
  .nav-line-scroll {
@@ -119,6 +222,62 @@
119
  display: none;
120
  }
121
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
122
  /* Custom scrollbar for research area card descriptions */
123
  .overflow-y-auto::-webkit-scrollbar {
124
  width: 4px;
@@ -198,16 +357,21 @@
198
  <body class="bg-gray-50 text-gray-800">
199
  <!-- Overall Background Image - Only for header and sidebars -->
200
  <div id="overall-background" class="fixed opacity-100 z-40 pointer-events-none" style="top: 0; left: 0; right: 0; height: var(--header-height);">
201
- <img src="/images/background_ai.png" alt="" class="w-full h-full object-cover object-left-top">
202
  </div>
203
 
204
  <!-- Right Sidebar Background (when open) -->
205
  <div id="right-sidebar-background" class="fixed opacity-60 z-0 pointer-events-none hidden" style="top: var(--header-height); right: 0; width: 320px; bottom: 0;">
206
- <img src="/images/background_ai.png" alt="" class="w-full h-full object-cover object-right">
207
  </div>
208
 
 
 
 
 
 
209
  <!-- Top Navigation -->
210
- <header class="bg-white/60 shadow-sm border-b border-gray-200 fixed top-0 left-0 right-0 z-50" style="height: var(--header-height);" x-data="{
211
  mobileMenuOpen: false,
212
  areas: window.navigationAreas || [],
213
  currentArea: null,
@@ -234,8 +398,12 @@
234
  </a>
235
 
236
  <!-- Mobile menu button -->
237
- <button @click="mobileMenuOpen = !mobileMenuOpen" class="xl:hidden p-2 text-gray-600">
238
- <svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
 
 
 
 
239
  <path x-show="!mobileMenuOpen" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h16"></path>
240
  <path x-show="mobileMenuOpen" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"></path>
241
  </svg>
@@ -270,8 +438,11 @@
270
  <a href="/about"
271
  class="flex items-center justify-center px-8 py-2 text-lg font-medium text-gray-700 hover:text-blue-600 hover:bg-blue-50/50 transition-colors border-b border-gray-200 flex-1">Press</a>
272
 
273
- <button id="search-toggle" class="flex items-center justify-center px-8 py-2 text-gray-700 hover:text-blue-600 hover:bg-blue-50/50 transition-colors flex-1">
274
- <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6">
 
 
 
275
  <path stroke-linecap="round" stroke-linejoin="round" d="m21 21-5.197-5.197m0 0A7.5 7.5 0 1 0 5.196 5.196a7.5 7.5 0 0 0 10.607 10.607Z" />
276
  </svg>
277
  </button>
@@ -280,11 +451,13 @@
280
  </div>
281
 
282
  <!-- Mobile Menu (Dropdown) -->
283
- <div x-show="mobileMenuOpen"
284
  x-transition
285
  @click.away="mobileMenuOpen = false"
286
  class="xl:hidden absolute top-full left-0 right-0 bg-white shadow-lg border-b border-gray-200 z-40"
287
- x-data="mobileTopicNav()">
 
 
288
  <div class="px-4 py-3 space-y-2 max-h-[70vh] overflow-y-auto">
289
  <!-- Areas with Topics -->
290
  <div>
@@ -318,7 +491,7 @@
318
  <!-- Main Layout -->
319
  <div id="main-layout" class="flex" style="padding-top: var(--header-height);">
320
  <!-- Main Content -->
321
- <main class="flex-1 transition-all duration-300 overflow-x-hidden" id="main-content">
322
  <div class="w-full py-8">
323
  <!-- Content will be loaded dynamically by the SPA router -->
324
  <div class="bg-white rounded-lg shadow-sm p-8 mx-4 sm:mx-6 lg:mx-8" style="max-width: min(90%, 1400px); margin-left: auto; margin-right: auto;">
@@ -331,10 +504,18 @@
331
  </main>
332
 
333
  <!-- Right Sidebar - Search (Collapsible) -->
334
- <aside id="search-sidebar" class="fixed right-0 h-full w-80 bg-white/85 backdrop-blur-sm shadow-sm border-l border-gray-200 transform translate-x-full z-40 transition-transform duration-300" style="top: var(--header-height);"> <div class="flex items-center justify-between mb-4">
 
 
 
 
 
 
335
  <h3 class="text-lg font-semibold text-gray-900">Search</h3>
336
- <button id="search-close" class="p-1 rounded-md text-gray-400 hover:text-gray-500 transition-colors">
337
- <svg class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
 
 
338
  <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />
339
  </svg>
340
  </button>
@@ -342,21 +523,28 @@
342
 
343
  <!-- Search Description -->
344
  <div class="mb-4">
345
- <p class="text-xs text-gray-500">We have produced or collaborated on a fair amount of papers, writings, and technical artifacts over the years. Use the search functionality here to navigate those!</p>
346
  </div>
347
 
348
  <!-- Search Input -->
349
  <div class="mb-4">
 
350
  <input
351
- type="text"
352
  id="search-input"
353
  placeholder="Search works by the ML & Society team"
354
  class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent"
 
355
  >
356
  </div>
357
 
358
  <!-- Search Results - Fixed height with proper scrolling -->
359
- <div id="search-results" class="overflow-y-auto" style="height: calc(100vh - 193px);">
 
 
 
 
 
360
  <div class="text-gray-500 text-center py-8">
361
  <p>Enter a search term to find related works by the ML & Society team.</p>
362
  </div>
@@ -365,11 +553,15 @@
365
  </aside>
366
  </div>
367
 
368
- <!-- Overlay for mobile -->
369
- <div id="sidebar-overlay" class="fixed inset-0 bg-black bg-opacity-30 z-30 hidden"></div>
 
 
370
 
371
  <!-- Scroll to Top Button -->
372
- <button id="scroll-to-top" class="fixed bottom-6 right-6 w-12 h-12 bg-blue-600 hover:bg-blue-700 text-white rounded-full shadow-lg hover:shadow-xl transition-all duration-300 opacity-0 invisible z-50 flex items-center justify-center group">
 
 
373
  <svg class="w-5 h-5 transform group-hover:scale-110 transition-transform duration-200" fill="none" stroke="currentColor" viewBox="0 0 24 24">
374
  <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 15l7-7 7 7"></path>
375
  </svg>
 
3
  <head>
4
  <meta charset="UTF-8">
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Machine Learning and Society at Hugging Face</title>
7
+
8
+ <!-- SEO Meta Tags -->
9
+ <meta name="description" content="The Machine Learning and Society team at Hugging Face works on the sustainability, agency, and ecosystems of AI systems through open research and collaborative development.">
10
+ <meta name="keywords" content="machine learning, AI ethics, AI sustainability, AI agency, AI ecosystems, open source AI, Hugging Face">
11
+ <meta name="author" content="Hugging Face - Machine Learning and Society Team">
12
+
13
+ <!-- Open Graph / Facebook -->
14
+ <meta property="og:type" content="website">
15
+ <meta property="og:url" content="https://huggingface.co/">
16
+ <meta property="og:title" content="Machine Learning and Society at Hugging Face">
17
+ <meta property="og:description" content="Research on the sustainability, agency, and ecosystems of AI systems through open and collaborative development.">
18
+ <meta property="og:image" content="/images/background_ai.png">
19
+
20
+ <!-- Twitter -->
21
+ <meta property="twitter:card" content="summary_large_image">
22
+ <meta property="twitter:url" content="https://huggingface.co/">
23
+ <meta property="twitter:title" content="Machine Learning and Society at Hugging Face">
24
+ <meta property="twitter:description" content="Research on the sustainability, agency, and ecosystems of AI systems through open and collaborative development.">
25
+ <meta property="twitter:image" content="/images/background_ai.png">
26
+
27
+ <!-- Performance: Preconnect to CDN origins -->
28
+ <link rel="preconnect" href="https://cdn.tailwindcss.com">
29
+ <link rel="preconnect" href="https://cdn.jsdelivr.net">
30
+ <link rel="preconnect" href="https://fonts.googleapis.com">
31
+ <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
32
+
33
  <script src="https://cdn.tailwindcss.com"></script>
34
  <!-- Import map for lit-html -->
35
  <script type="importmap">
 
46
  window.navigationAreas = getNavigationData();
47
  window.homeBackgroundImage = homeBackgroundImage;
48
  window.overallBackgroundImage = overallBackgroundImage;
 
49
  </script>
50
  <!-- Alpine.js can now safely use window.navigationAreas -->
51
  <script src="https://cdn.jsdelivr.net/npm/alpinejs@3.x.x/dist/cdn.min.js" defer></script>
 
64
  /* Apply fonts to base elements */
65
  body {
66
  font-family: 'Source Sans Pro', sans-serif;
67
+ --header-height: 100px; /* Two-line navigation menu - desktop */
68
+
69
+ /* Typography scale - consistent font sizes */
70
+ --text-xs: 0.75rem; /* 12px */
71
+ --text-sm: 0.875rem; /* 14px */
72
+ --text-base: 1rem; /* 16px */
73
+ --text-lg: 1.125rem; /* 18px */
74
+ --text-xl: 1.25rem; /* 20px */
75
+ --text-2xl: 1.5rem; /* 24px */
76
+ --text-3xl: 1.875rem; /* 30px */
77
+ --text-4xl: 2.25rem; /* 36px */
78
+
79
+ /* Line heights for readability */
80
+ --leading-tight: 1.25;
81
+ --leading-normal: 1.5;
82
+ --leading-relaxed: 1.625;
83
+ --leading-loose: 1.75;
84
+ }
85
+
86
+ /* Ensure minimum readable font size on mobile */
87
+ @media (max-width: 767px) {
88
+ body {
89
+ font-size: 14px; /* Minimum 14px on mobile */
90
+ }
91
+
92
+ /* Adjust heading sizes for mobile */
93
+ h1 {
94
+ font-size: var(--text-2xl);
95
+ }
96
+
97
+ h2 {
98
+ font-size: var(--text-xl);
99
+ }
100
+
101
+ h3 {
102
+ font-size: var(--text-lg);
103
+ }
104
+ }
105
+
106
+ /* Responsive header height */
107
+ @media (max-width: 1023px) {
108
+ body {
109
+ --header-height: 80px; /* Tablet */
110
+ }
111
+ }
112
+
113
+ @media (max-width: 767px) {
114
+ body {
115
+ --header-height: 70px; /* Mobile */
116
+ }
117
  }
118
 
119
  h1, h2, h3, h4, h5, h6 {
120
  font-family: 'Montserrat', sans-serif;
121
+ line-height: var(--leading-tight);
122
+ }
123
+
124
+ /* Body text readability */
125
+ p {
126
+ line-height: var(--leading-relaxed);
127
+ }
128
+
129
+ /* Improve readability of long-form content */
130
+ .prose p {
131
+ line-height: var(--leading-loose);
132
+ margin-bottom: 1em;
133
  }
134
 
135
  /* Ensure all major containers respect viewport width */
 
145
  }
146
  }
147
 
148
+ /* Mobile search sidebar - full screen on mobile */
149
+ @media (max-width: 767px) {
150
+ #search-sidebar {
151
+ width: 100vw !important;
152
+ right: 0;
153
+ }
154
+
155
+ #search-sidebar > div {
156
+ padding: 1rem;
157
+ }
158
+
159
+ /* Adjust search results height for mobile */
160
+ #search-results {
161
+ height: calc(100vh - 180px) !important;
162
+ }
163
+ }
164
+
165
  /* Navigation overflow handling */
166
  @media (min-width: 769px) {
167
  .nav-line-scroll {
 
222
  display: none;
223
  }
224
 
225
+ /* Screen reader only utility */
226
+ .sr-only {
227
+ position: absolute;
228
+ width: 1px;
229
+ height: 1px;
230
+ padding: 0;
231
+ margin: -1px;
232
+ overflow: hidden;
233
+ clip: rect(0, 0, 0, 0);
234
+ white-space: nowrap;
235
+ border-width: 0;
236
+ }
237
+
238
+ .sr-only:focus {
239
+ position: static;
240
+ width: auto;
241
+ height: auto;
242
+ padding: inherit;
243
+ margin: inherit;
244
+ overflow: visible;
245
+ clip: auto;
246
+ white-space: normal;
247
+ }
248
+
249
+ /* Mobile carousel improvements */
250
+ @media (max-width: 767px) {
251
+ /* Hide carousel navigation arrows on mobile */
252
+ .carousel-arrow {
253
+ display: none;
254
+ }
255
+
256
+ /* Smooth touch scrolling for carousels */
257
+ .carousel-scroll {
258
+ -webkit-overflow-scrolling: touch;
259
+ scroll-behavior: smooth;
260
+ }
261
+
262
+ /* Add padding to carousel for better touch targets */
263
+ .carousel-scroll > * {
264
+ scroll-snap-align: start;
265
+ }
266
+ }
267
+
268
+ /* Carousel arrow styling */
269
+ .carousel-arrow {
270
+ transition: opacity 0.2s, transform 0.2s;
271
+ }
272
+
273
+ .carousel-arrow:hover {
274
+ transform: scale(1.1);
275
+ }
276
+
277
+ .carousel-arrow:active {
278
+ transform: scale(0.95);
279
+ }
280
+
281
  /* Custom scrollbar for research area card descriptions */
282
  .overflow-y-auto::-webkit-scrollbar {
283
  width: 4px;
 
357
  <body class="bg-gray-50 text-gray-800">
358
  <!-- Overall Background Image - Only for header and sidebars -->
359
  <div id="overall-background" class="fixed opacity-100 z-40 pointer-events-none" style="top: 0; left: 0; right: 0; height: var(--header-height);">
360
+ <img src="/images/background_ai.png" alt="" class="w-full h-full object-cover object-left-top" loading="eager">
361
  </div>
362
 
363
  <!-- Right Sidebar Background (when open) -->
364
  <div id="right-sidebar-background" class="fixed opacity-60 z-0 pointer-events-none hidden" style="top: var(--header-height); right: 0; width: 320px; bottom: 0;">
365
+ <img src="/images/background_ai.png" alt="" class="w-full h-full object-cover object-right" loading="lazy">
366
  </div>
367
 
368
+ <!-- Skip to main content link for accessibility -->
369
+ <a href="#main-content" class="sr-only focus:not-sr-only focus:absolute focus:top-2 focus:left-2 focus:z-50 focus:px-4 focus:py-2 focus:bg-blue-600 focus:text-white focus:rounded">
370
+ Skip to main content
371
+ </a>
372
+
373
  <!-- Top Navigation -->
374
+ <header role="banner" aria-label="Main navigation" class="bg-white/60 shadow-sm border-b border-gray-200 fixed top-0 left-0 right-0 z-50" style="height: var(--header-height);" x-data="{
375
  mobileMenuOpen: false,
376
  areas: window.navigationAreas || [],
377
  currentArea: null,
 
398
  </a>
399
 
400
  <!-- Mobile menu button -->
401
+ <button @click="mobileMenuOpen = !mobileMenuOpen"
402
+ class="xl:hidden p-2 text-gray-600"
403
+ aria-label="Toggle mobile menu"
404
+ aria-expanded="false"
405
+ :aria-expanded="mobileMenuOpen.toString()">
406
+ <svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true">
407
  <path x-show="!mobileMenuOpen" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h16"></path>
408
  <path x-show="mobileMenuOpen" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"></path>
409
  </svg>
 
438
  <a href="/about"
439
  class="flex items-center justify-center px-8 py-2 text-lg font-medium text-gray-700 hover:text-blue-600 hover:bg-blue-50/50 transition-colors border-b border-gray-200 flex-1">Press</a>
440
 
441
+ <button id="search-toggle"
442
+ class="flex items-center justify-center px-8 py-2 text-gray-700 hover:text-blue-600 hover:bg-blue-50/50 transition-colors flex-1"
443
+ aria-label="Open search"
444
+ aria-controls="search-sidebar">
445
+ <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6" aria-hidden="true">
446
  <path stroke-linecap="round" stroke-linejoin="round" d="m21 21-5.197-5.197m0 0A7.5 7.5 0 1 0 5.196 5.196a7.5 7.5 0 0 0 10.607 10.607Z" />
447
  </svg>
448
  </button>
 
451
  </div>
452
 
453
  <!-- Mobile Menu (Dropdown) -->
454
+ <nav x-show="mobileMenuOpen"
455
  x-transition
456
  @click.away="mobileMenuOpen = false"
457
  class="xl:hidden absolute top-full left-0 right-0 bg-white shadow-lg border-b border-gray-200 z-40"
458
+ x-data="mobileTopicNav()"
459
+ role="navigation"
460
+ aria-label="Mobile navigation menu">
461
  <div class="px-4 py-3 space-y-2 max-h-[70vh] overflow-y-auto">
462
  <!-- Areas with Topics -->
463
  <div>
 
491
  <!-- Main Layout -->
492
  <div id="main-layout" class="flex" style="padding-top: var(--header-height);">
493
  <!-- Main Content -->
494
+ <main role="main" class="flex-1 transition-all duration-300 overflow-x-hidden" id="main-content">
495
  <div class="w-full py-8">
496
  <!-- Content will be loaded dynamically by the SPA router -->
497
  <div class="bg-white rounded-lg shadow-sm p-8 mx-4 sm:mx-6 lg:mx-8" style="max-width: min(90%, 1400px); margin-left: auto; margin-right: auto;">
 
504
  </main>
505
 
506
  <!-- Right Sidebar - Search (Collapsible) -->
507
+ <aside id="search-sidebar"
508
+ role="search"
509
+ aria-label="Search sidebar"
510
+ class="fixed right-0 h-full w-80 bg-white/85 backdrop-blur-sm shadow-sm border-l border-gray-200 transform translate-x-full z-40 transition-transform duration-300"
511
+ style="top: var(--header-height);">
512
+ <div class="p-6">
513
+ <div class="flex items-center justify-between mb-4">
514
  <h3 class="text-lg font-semibold text-gray-900">Search</h3>
515
+ <button id="search-close"
516
+ class="p-1 rounded-md text-gray-400 hover:text-gray-500 transition-colors"
517
+ aria-label="Close search">
518
+ <svg class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor" aria-hidden="true">
519
  <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />
520
  </svg>
521
  </button>
 
523
 
524
  <!-- Search Description -->
525
  <div class="mb-4">
526
+ <p class="text-sm text-gray-600" style="line-height: var(--leading-relaxed);">We have produced or collaborated on a fair amount of papers, writings, and technical artifacts over the years. Use the search functionality here to navigate those!</p>
527
  </div>
528
 
529
  <!-- Search Input -->
530
  <div class="mb-4">
531
+ <label for="search-input" class="sr-only">Search works by the ML & Society team</label>
532
  <input
533
+ type="search"
534
  id="search-input"
535
  placeholder="Search works by the ML & Society team"
536
  class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent"
537
+ aria-describedby="search-description"
538
  >
539
  </div>
540
 
541
  <!-- Search Results - Fixed height with proper scrolling -->
542
+ <div id="search-results"
543
+ class="overflow-y-auto"
544
+ style="height: calc(100vh - 193px);"
545
+ role="region"
546
+ aria-live="polite"
547
+ aria-label="Search results">
548
  <div class="text-gray-500 text-center py-8">
549
  <p>Enter a search term to find related works by the ML & Society team.</p>
550
  </div>
 
553
  </aside>
554
  </div>
555
 
556
+ <!-- Overlay for mobile (darker on mobile for better visibility) -->
557
+ <div id="sidebar-overlay"
558
+ class="fixed inset-0 bg-black bg-opacity-30 md:bg-opacity-30 z-30 hidden"
559
+ aria-hidden="true"></div>
560
 
561
  <!-- Scroll to Top Button -->
562
+ <button id="scroll-to-top"
563
+ class="fixed bottom-6 right-6 w-12 h-12 bg-blue-600 hover:bg-blue-700 text-white rounded-full shadow-lg hover:shadow-xl transition-all duration-300 opacity-0 invisible z-50 flex items-center justify-center group"
564
+ aria-label="Scroll to top">
565
  <svg class="w-5 h-5 transform group-hover:scale-110 transition-transform duration-200" fill="none" stroke="currentColor" viewBox="0 0 24 24">
566
  <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 15l7-7 7 7"></path>
567
  </svg>
js/bootstrap.js CHANGED
@@ -3,30 +3,20 @@
3
  // All initialization happens here in a well-defined sequence
4
 
5
  async function bootstrap() {
6
- console.log('=== Bootstrap Started ===');
7
-
8
  try {
9
  // Step 1: Load artifacts (areas already loaded in <head>)
10
- console.log('Loading artifacts...');
11
  const { loadArtifacts } = await import('./init.js');
12
  await loadArtifacts();
13
- console.log('βœ“ Artifacts loaded:', window.allArtifacts?.length || 0);
14
 
15
  // Step 2: Initialize router (will render initial page)
16
- console.log('Initializing router...');
17
  const { router } = await import('./utils/router.js');
18
  // Router auto-initializes in constructor, no need to call init()
19
- console.log('βœ“ Router initialized');
20
 
21
  // Step 3: Initialize UI components (search, scroll-to-top, etc.)
22
- console.log('Initializing UI...');
23
  const { initializeUI } = await import('./main.js');
24
  await initializeUI();
25
- console.log('βœ“ UI initialized');
26
-
27
- console.log('=== Bootstrap Complete ===');
28
  } catch (error) {
29
- console.error('!!! Bootstrap Failed:', error);
30
  // Show user-friendly error
31
  const mainContent = document.getElementById('main-content');
32
  if (mainContent) {
 
3
  // All initialization happens here in a well-defined sequence
4
 
5
  async function bootstrap() {
 
 
6
  try {
7
  // Step 1: Load artifacts (areas already loaded in <head>)
 
8
  const { loadArtifacts } = await import('./init.js');
9
  await loadArtifacts();
 
10
 
11
  // Step 2: Initialize router (will render initial page)
 
12
  const { router } = await import('./utils/router.js');
13
  // Router auto-initializes in constructor, no need to call init()
 
14
 
15
  // Step 3: Initialize UI components (search, scroll-to-top, etc.)
 
16
  const { initializeUI } = await import('./main.js');
17
  await initializeUI();
 
 
 
18
  } catch (error) {
19
+ console.error('Bootstrap failed:', error);
20
  // Show user-friendly error
21
  const mainContent = document.getElementById('main-content');
22
  if (mainContent) {
js/cards/ArtifactSummaryCard.js CHANGED
@@ -15,18 +15,22 @@ export function createArtifactCarousel(artifacts, containerId) {
15
  container.innerHTML = `
16
  <div class="relative">
17
  <!-- Carousel container -->
18
- <div class="flex overflow-x-auto scrollbar-hide space-x-4 pb-4" id="${containerId}-scroll">
19
  ${cardsHtml}
20
  </div>
21
 
22
- <!-- Navigation arrows -->
23
- <button class="absolute left-0 top-1/2 transform -translate-y-1/2 bg-white shadow-lg rounded-full p-2 hover:bg-gray-50 transition-colors z-10" onclick="scrollCarousel('${containerId}-scroll', -320)">
24
- <svg class="w-4 h-4 text-gray-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
 
 
25
  <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 19l-7-7 7-7"></path>
26
  </svg>
27
  </button>
28
- <button class="absolute right-0 top-1/2 transform -translate-y-1/2 bg-white shadow-lg rounded-full p-2 hover:bg-gray-50 transition-colors z-10" onclick="scrollCarousel('${containerId}-scroll', 320)">
29
- <svg class="w-4 h-4 text-gray-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
 
 
30
  <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7"></path>
31
  </svg>
32
  </button>
 
15
  container.innerHTML = `
16
  <div class="relative">
17
  <!-- Carousel container -->
18
+ <div class="flex overflow-x-auto scrollbar-hide carousel-scroll space-x-4 pb-4" id="${containerId}-scroll">
19
  ${cardsHtml}
20
  </div>
21
 
22
+ <!-- Navigation arrows (hidden on mobile) -->
23
+ <button class="carousel-arrow absolute left-0 top-1/2 transform -translate-y-1/2 bg-white shadow-lg rounded-full p-2 hover:bg-gray-50 transition-colors z-10"
24
+ onclick="scrollCarousel('${containerId}-scroll', -320)"
25
+ aria-label="Scroll carousel left">
26
+ <svg class="w-4 h-4 text-gray-600" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true">
27
  <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 19l-7-7 7-7"></path>
28
  </svg>
29
  </button>
30
+ <button class="carousel-arrow absolute right-0 top-1/2 transform -translate-y-1/2 bg-white shadow-lg rounded-full p-2 hover:bg-gray-50 transition-colors z-10"
31
+ onclick="scrollCarousel('${containerId}-scroll', 320)"
32
+ aria-label="Scroll carousel right">
33
+ <svg class="w-4 h-4 text-gray-600" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true">
34
  <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7"></path>
35
  </svg>
36
  </button>
js/components/Card.js CHANGED
@@ -71,7 +71,7 @@ function renderArtifactCard(artifact, options = {}) {
71
  <!-- Background image -->
72
  ${backgroundImage ? `
73
  <div class="absolute inset-0 opacity-10">
74
- <img src="/images/${backgroundImage}" alt="" class="w-full h-full object-cover">
75
  </div>
76
  ` : ''}
77
 
@@ -95,14 +95,14 @@ function renderArtifactCard(artifact, options = {}) {
95
  <div class="flex justify-between items-start mb-3 pr-8">
96
  <div class="flex items-center space-x-2">
97
  <span class="text-lg">${style.icon}</span>
98
- <span class="text-xs font-medium font-bold uppercase tracking-wide">${type}</span>
99
  </div>
100
- <span class="text-xs text-gray-600">${date}</span>
101
  </div>
102
 
103
  <!-- Title -->
104
  <div class="mb-4 flex-grow min-h-0">
105
- <h3 class="font-semibold text-gray-900 text-sm leading-tight line-clamp-3">${title}</h3>
106
  </div>
107
 
108
  <!-- Bottom section with tags and image -->
@@ -126,7 +126,7 @@ function renderArtifactCard(artifact, options = {}) {
126
  ${backgroundImage ? `
127
  <div class="relative group/image">
128
  <div class="w-12 h-12 rounded-lg overflow-hidden bg-gray-100 flex items-center justify-center cursor-help" title="${imageCredit}">
129
- <img src="/images/${backgroundImage}" alt="${primaryAreaData.name}" class="w-full h-full object-cover opacity-80">
130
  </div>
131
  </div>
132
  ` : ''}
@@ -137,12 +137,12 @@ function renderArtifactCard(artifact, options = {}) {
137
  <div class="description-view hidden h-full flex flex-col min-h-0">
138
  <!-- Title (single line with overflow) -->
139
  <div class="mb-3 flex-shrink-0">
140
- <h3 class="font-semibold text-gray-900 text-sm leading-tight truncate" title="${title}">${title}</h3>
141
  </div>
142
 
143
  <!-- Description (scrollable, takes remaining space) -->
144
  <div class="flex-grow overflow-y-auto min-h-0">
145
- <p class="text-xs text-gray-700 leading-relaxed">${description}</p>
146
  </div>
147
  </div>
148
 
@@ -201,7 +201,7 @@ function renderAreaCard(area, options = {}) {
201
  <!-- Background image with low opacity -->
202
  ${area.image ? `
203
  <div class="absolute inset-0 opacity-15 group-hover:opacity-30 transition-opacity">
204
- <img src="/images/${area.image}" alt="" class="w-full h-full object-cover">
205
  </div>
206
  ` : ''}
207
 
@@ -209,23 +209,23 @@ function renderAreaCard(area, options = {}) {
209
  <div class="relative p-5 h-full flex flex-col">
210
  <!-- Header -->
211
  <div class="flex justify-between items-start mb-3 flex-shrink-0">
212
- <h3 class="text-lg font-bold text-gray-900 leading-tight">${area.navTitle}</h3>
213
  <svg class="w-5 h-5 text-gray-400 group-hover:text-blue-600 transition-colors flex-shrink-0 ml-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
214
  <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7"></path>
215
  </svg>
216
  </div>
217
 
218
  <!-- Description - scrollable -->
219
- <div class="text-md text-gray-700 mb-4 flex-grow overflow-y-auto pr-2">
220
- <p class="leading-relaxed">${shortDesc}</p>
221
  </div>
222
 
223
  <!-- Topics with colors from subAreas -->
224
  <div class="flex-shrink-0">
225
- <p class="text-xs font-semibold text-gray-600 uppercase tracking-wide mb-2">Topics:</p>
226
  <div class="flex flex-wrap gap-2">
227
  ${topics.map(topic => `
228
- <span class="inline-block px-2 py-0.5 text-sm ${topic.bgColor} ${topic.textColor} rounded">
229
  ${topic.name}
230
  </span>
231
  `).join('')}
 
71
  <!-- Background image -->
72
  ${backgroundImage ? `
73
  <div class="absolute inset-0 opacity-10">
74
+ <img src="/images/${backgroundImage}" alt="" class="w-full h-full object-cover" loading="lazy">
75
  </div>
76
  ` : ''}
77
 
 
95
  <div class="flex justify-between items-start mb-3 pr-8">
96
  <div class="flex items-center space-x-2">
97
  <span class="text-lg">${style.icon}</span>
98
+ <span class="text-xs font-bold uppercase tracking-wide">${type}</span>
99
  </div>
100
+ <span class="text-xs text-gray-500">${date}</span>
101
  </div>
102
 
103
  <!-- Title -->
104
  <div class="mb-4 flex-grow min-h-0">
105
+ <h3 class="font-semibold text-gray-900 text-base leading-snug line-clamp-3">${title}</h3>
106
  </div>
107
 
108
  <!-- Bottom section with tags and image -->
 
126
  ${backgroundImage ? `
127
  <div class="relative group/image">
128
  <div class="w-12 h-12 rounded-lg overflow-hidden bg-gray-100 flex items-center justify-center cursor-help" title="${imageCredit}">
129
+ <img src="/images/${backgroundImage}" alt="${primaryAreaData.name}" class="w-full h-full object-cover opacity-80" loading="lazy">
130
  </div>
131
  </div>
132
  ` : ''}
 
137
  <div class="description-view hidden h-full flex flex-col min-h-0">
138
  <!-- Title (single line with overflow) -->
139
  <div class="mb-3 flex-shrink-0">
140
+ <h3 class="font-semibold text-gray-900 text-base leading-tight truncate" title="${title}">${title}</h3>
141
  </div>
142
 
143
  <!-- Description (scrollable, takes remaining space) -->
144
  <div class="flex-grow overflow-y-auto min-h-0">
145
+ <p class="text-sm text-gray-700" style="line-height: 1.6;">${description}</p>
146
  </div>
147
  </div>
148
 
 
201
  <!-- Background image with low opacity -->
202
  ${area.image ? `
203
  <div class="absolute inset-0 opacity-15 group-hover:opacity-30 transition-opacity">
204
+ <img src="/images/${area.image}" alt="" class="w-full h-full object-cover" loading="lazy">
205
  </div>
206
  ` : ''}
207
 
 
209
  <div class="relative p-5 h-full flex flex-col">
210
  <!-- Header -->
211
  <div class="flex justify-between items-start mb-3 flex-shrink-0">
212
+ <h3 class="text-xl font-bold text-gray-900 leading-tight">${area.navTitle}</h3>
213
  <svg class="w-5 h-5 text-gray-400 group-hover:text-blue-600 transition-colors flex-shrink-0 ml-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
214
  <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7"></path>
215
  </svg>
216
  </div>
217
 
218
  <!-- Description - scrollable -->
219
+ <div class="text-base text-gray-700 mb-4 flex-grow overflow-y-auto pr-2">
220
+ <p style="line-height: 1.6;">${shortDesc}</p>
221
  </div>
222
 
223
  <!-- Topics with colors from subAreas -->
224
  <div class="flex-shrink-0">
225
+ <p class="text-xs font-semibold text-gray-500 uppercase tracking-wide mb-2">Topics:</p>
226
  <div class="flex flex-wrap gap-2">
227
  ${topics.map(topic => `
228
+ <span class="inline-block px-2.5 py-1 text-sm ${topic.bgColor} ${topic.textColor} rounded">
229
  ${topic.name}
230
  </span>
231
  `).join('')}
js/main.js CHANGED
@@ -43,6 +43,7 @@ export function createTeamMember(name, role, hfUsername, tags) {
43
  src="/images/${hfUsername}.jpeg"
44
  alt="${name}"
45
  class="w-full h-full object-cover"
 
46
  onerror="this.style.display='none'; this.nextElementSibling.style.display='flex';"
47
  />
48
  <span class="text-white font-semibold text-base hidden w-full h-full items-center justify-center absolute">${initials}</span>
@@ -142,17 +143,29 @@ function initializeSearchToggle() {
142
  function toggleSearch() {
143
  const isOpen = !searchSidebar.classList.contains('translate-x-full');
144
  const rightSidebarBg = document.getElementById('right-sidebar-background');
 
145
 
146
  if (isOpen) {
147
  searchSidebar.classList.add('translate-x-full');
148
  rightSidebarBg.classList.add('hidden');
149
- mainContent.classList.remove('mr-80');
 
 
 
150
  overlay.classList.add('hidden');
 
151
  } else {
152
  searchSidebar.classList.remove('translate-x-full');
153
  rightSidebarBg.classList.remove('hidden');
154
- mainContent.classList.add('mr-80');
 
 
 
155
  overlay.classList.remove('hidden');
 
 
 
 
156
  }
157
  }
158
 
 
43
  src="/images/${hfUsername}.jpeg"
44
  alt="${name}"
45
  class="w-full h-full object-cover"
46
+ loading="lazy"
47
  onerror="this.style.display='none'; this.nextElementSibling.style.display='flex';"
48
  />
49
  <span class="text-white font-semibold text-base hidden w-full h-full items-center justify-center absolute">${initials}</span>
 
143
  function toggleSearch() {
144
  const isOpen = !searchSidebar.classList.contains('translate-x-full');
145
  const rightSidebarBg = document.getElementById('right-sidebar-background');
146
+ const isMobile = window.innerWidth < 768;
147
 
148
  if (isOpen) {
149
  searchSidebar.classList.add('translate-x-full');
150
  rightSidebarBg.classList.add('hidden');
151
+ // Only adjust main content margin on desktop
152
+ if (!isMobile) {
153
+ mainContent.classList.remove('mr-80');
154
+ }
155
  overlay.classList.add('hidden');
156
+ document.body.style.overflow = ''; // Re-enable scroll
157
  } else {
158
  searchSidebar.classList.remove('translate-x-full');
159
  rightSidebarBg.classList.remove('hidden');
160
+ // Only adjust main content margin on desktop
161
+ if (!isMobile) {
162
+ mainContent.classList.add('mr-80');
163
+ }
164
  overlay.classList.remove('hidden');
165
+ // Prevent body scroll on mobile when search is open
166
+ if (isMobile) {
167
+ document.body.style.overflow = 'hidden';
168
+ }
169
  }
170
  }
171
 
js/utils/search.js CHANGED
@@ -19,10 +19,7 @@ function createMiniSearchIndex(data, storeFields) {
19
 
20
  // Initialize search
21
  export async function initializeSearch(artifactsData) {
22
- // Assign the passed artifactsData to allArtifacts
23
  allArtifacts = artifactsData;
24
-
25
- // The fetch for artifacts.json is removed from here since it's now loaded in main.js
26
 
27
  // Prepare data for MiniSearch
28
  const searchData = allArtifacts.map((artifact, index) => ({
@@ -70,9 +67,7 @@ function getSubAreaDisplayName(areaId, subArea) {
70
 
71
  // Search UI
72
  export function initializeSearchUI(artifactsData) {
73
- initializeSearch(artifactsData).then(() => {
74
- console.log('Search initialized');
75
- });
76
 
77
  const searchInput = document.getElementById('search-input');
78
  const searchResults = document.getElementById('search-results');
@@ -149,23 +144,23 @@ export function displaySearchResults(results, query) {
149
  const { areaLinksHtml: areaLinks, topicLinksHtml: subAreaLinks } = renderSearchTags(areas, topics);
150
 
151
  html += `
152
- <div class="p-3 bg-gray-50 rounded-lg border">
153
  <div class="flex items-center justify-between mb-2">
154
- <h5 class="font-medium text-sm text-gray-900">${result.title}</h5>
155
- <div class="flex items-center gap-2">
156
  <span class="text-xs text-gray-500">${score}%</span>
157
- <a href="${result.url}" target="_blank" class="text-gray-400 hover:text-gray-600 transition-colors">
158
- <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
159
  <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14"></path>
160
  </svg>
161
  </a>
162
  </div>
163
  </div>
164
  <div class="flex items-center gap-2 mb-2 text-xs text-gray-600">
165
- <span class="px-2 py-1 bg-gray-200 text-gray-700 rounded">${result.type}</span>
166
- <span>${result.date}</span>
167
  </div>
168
- <div class="flex flex-wrap gap-1">
169
  ${areaLinks}
170
  ${subAreaLinks}
171
  </div>
 
19
 
20
  // Initialize search
21
  export async function initializeSearch(artifactsData) {
 
22
  allArtifacts = artifactsData;
 
 
23
 
24
  // Prepare data for MiniSearch
25
  const searchData = allArtifacts.map((artifact, index) => ({
 
67
 
68
  // Search UI
69
  export function initializeSearchUI(artifactsData) {
70
+ initializeSearch(artifactsData);
 
 
71
 
72
  const searchInput = document.getElementById('search-input');
73
  const searchResults = document.getElementById('search-results');
 
144
  const { areaLinksHtml: areaLinks, topicLinksHtml: subAreaLinks } = renderSearchTags(areas, topics);
145
 
146
  html += `
147
+ <div class="p-3 bg-gray-50 rounded-lg border border-gray-200">
148
  <div class="flex items-center justify-between mb-2">
149
+ <h5 class="font-medium text-sm text-gray-900 leading-snug">${result.title}</h5>
150
+ <div class="flex items-center gap-2 flex-shrink-0 ml-2">
151
  <span class="text-xs text-gray-500">${score}%</span>
152
+ <a href="${result.url}" target="_blank" class="text-gray-400 hover:text-gray-600 transition-colors" aria-label="Open ${result.title}">
153
+ <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true">
154
  <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14"></path>
155
  </svg>
156
  </a>
157
  </div>
158
  </div>
159
  <div class="flex items-center gap-2 mb-2 text-xs text-gray-600">
160
+ <span class="px-2 py-0.5 bg-gray-200 text-gray-700 rounded font-medium">${result.type}</span>
161
+ <span class="text-gray-500">${result.date}</span>
162
  </div>
163
+ <div class="flex flex-wrap gap-1.5">
164
  ${areaLinks}
165
  ${subAreaLinks}
166
  </div>
js/utils/tags.js CHANGED
@@ -34,7 +34,6 @@ export function renderTopicTag(areaId, topicId) {
34
  }
35
 
36
  const topicName = typeof topic === 'string' ? topic : (topic.name || topic.navName || topicId);
37
- console.log(`renderTopicTag: areaId="${areaId}", topicId="${topicId}", topicName="${topicName}"`, topic);
38
  let bgColor = 'bg-gray-200';
39
  let textColor = 'text-gray-700';
40
 
 
34
  }
35
 
36
  const topicName = typeof topic === 'string' ? topic : (topic.name || topic.navName || topicId);
 
37
  let bgColor = 'bg-gray-200';
38
  let textColor = 'text-gray-700';
39