yjernite HF Staff commited on
Commit
cddcbaf
·
verified ·
1 Parent(s): 7ced0e4

Upload 3 files

Browse files
Files changed (3) hide show
  1. js/pages/AreaPage.js +151 -237
  2. js/pages/HomePage.js +147 -118
  3. js/pages/ResourcesPage.js +1 -21
js/pages/AreaPage.js CHANGED
@@ -1,135 +1,175 @@
1
- // pages/AreaPage.js - Reusable area page component
2
  import { areasData } from '../data/areas.js';
3
  import { featuredArtifacts } from '../data/artifacts.js';
4
  import { createArtifactCarousel } from '../cards/ArtifactSummaryCard.js';
5
  import { sampleResources } from '../data/resources.js';
6
  import { createResourceCard, initializeResourceCards } from '../cards/ResourceCard.js';
7
- import { renderSidebar } from '../utils/sidebar.js'; // Import the new sidebar component
8
 
9
- export function renderAreaPage(areaId) {
10
  const area = areasData[areaId];
11
  if (!area) {
12
  return {
13
- content: `<div class="bg-white rounded-lg shadow-sm p-8"><h1 class="text-2xl font-bold text-red-600">Area not found</h1></div>`,
14
  init: () => {}
15
  };
16
  }
17
 
18
- // Filter resources by area
19
- const areaResources = sampleResources.filter(resource =>
20
- resource.areaTags.includes(areaId)
21
- );
22
 
23
- // Get sub-areas as array for easier processing
24
- const subAreas = Object.entries(area.subAreas).map(([key, value]) => ({
 
 
 
 
 
 
 
 
 
 
 
 
 
25
  id: key,
26
  name: typeof value === 'string' ? value : value.name,
 
27
  description: typeof value === 'string' ? '' : (value.description || ''),
28
- openness: typeof value === 'string' ? '' : (value.openness || ''),
29
- gradient: typeof value === 'object' ? value.gradient : null
30
  }));
31
 
32
  const content = `
33
- <!-- Overview Section -->
34
- <section id="overview" class="mb-12 relative z-20">
35
- <!-- Overview Background Image -->
36
- <div class="absolute inset-0 opacity-10 rounded-lg overflow-hidden">
37
- <img src="images/${area.image}" alt="" class="w-full h-full object-cover">
 
 
 
 
 
 
 
 
 
 
38
  </div>
39
- <div class="bg-white bg-opacity-90 backdrop-blur-sm rounded-lg shadow-sm p-8 mb-6 relative z-10">
40
- <h1 class="text-3xl font-bold text-gray-900 mb-6">${area.title}</h1>
 
 
 
 
41
 
42
- <!-- Description -->
43
  <div class="mb-8">
44
- <p class="text-gray-700 leading-relaxed mb-6">${area.description}</p>
45
  </div>
46
 
47
  <!-- Role of Openness -->
48
- <div class="mb-8 px-4 py-4 bg-gradient-to-r from-orange-50 to-yellow-50 border-l-4 border-orange-300 rounded-r-lg">
49
- <p class="font-bold text-orange-900 mb-2">🤗 The Role of Openness</p>
 
50
  <p class="text-orange-800 italic leading-relaxed">${area.openness}</p>
51
  </div>
52
-
53
- <!-- Sub-areas Navigation -->
54
- <div class="mb-8">
55
- <h3 class="text-lg font-semibold text-gray-900 mb-4">Explore Sub-areas</h3>
56
- <div class="grid grid-cols-1 md:grid-cols-${Math.min(subAreas.length, 3)} gap-4">
57
- ${subAreas.map(subArea => `
58
- <a
59
- href="#${subArea.id}"
60
- class="group p-4 bg-gradient-to-br ${subArea.gradient || 'from-gray-50 to-gray-100 hover:from-gray-100 hover:to-gray-200 border-gray-200 hover:border-gray-300 text-gray-900'} rounded-lg border transition-all duration-200 text-left block no-underline"
61
- >
62
- <h4 class="font-semibold text-sm group-hover:scale-105 transition-transform">${subArea.name}</h4>
63
- </a>
64
- `).join('')}
 
 
 
 
 
 
 
 
 
65
  </div>
66
  </div>
67
  </div>
68
  </section>
 
69
 
70
- <!-- Sub-areas Sections -->
71
- ${subAreas.map(subArea => `
72
- <section id="${subArea.id}" class="mb-12 relative z-20">
73
- <div class="bg-white bg-opacity-90 backdrop-blur-sm rounded-lg shadow-sm p-8">
74
- <h2 class="text-2xl font-bold text-gray-900 mb-6">${subArea.name}</h2>
75
-
76
- ${subArea.description ? `
77
- <div class="mb-6">
78
- <p class="text-gray-700 leading-relaxed">${subArea.description}</p>
79
- </div>
80
- ` : ''}
81
 
82
- ${subArea.openness ? `
83
- <div class="mb-6 px-4 py-4 bg-gradient-to-r from-orange-50 to-yellow-50 border-l-4 border-orange-300 rounded-r-lg">
84
- <p class="font-bold text-orange-900 mb-2">🤗 The Role of Openness</p>
85
- <p class="text-orange-800 italic leading-relaxed">${subArea.openness}</p>
86
- </div>
87
- ` : ''}
 
 
 
 
88
 
89
- <!-- Artifacts Carousel -->
90
- <div class="mb-6">
91
- <h3 class="text-lg font-semibold text-gray-900 mb-4">Related Research & Resources</h3>
92
- <div id="${subArea.id}-artifacts-carousel">
93
- <!-- Carousel will be inserted here -->
94
- </div>
95
- </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
96
  </div>
97
- </section>
98
- `).join('')}
99
 
100
- <!-- Resources Section -->
101
- <section id="resources" class="mb-12 relative z-10">
102
- <div class="bg-white bg-opacity-90 backdrop-blur-sm rounded-lg shadow-sm p-8">
103
- <h2 class="text-2xl font-bold text-gray-900 mb-6">Technical Resources</h2>
104
 
105
- <!-- Filter resources by area -->
106
- <div class="mb-6">
107
- <p class="text-gray-700 leading-relaxed">
108
- Technical resources and tools related to ${area.name.toLowerCase()} research and development.
109
- </p>
110
  </div>
 
111
 
112
- <!-- Resource Cards Carousel -->
113
- <div class="relative">
114
- <div class="resource-carousel-container overflow-hidden" style="width: calc(100% - 2rem); margin: 0 auto;">
115
- <div class="resource-carousel flex gap-6 transition-transform duration-300 ease-in-out" style="width: ${areaResources.length * 624}px;">
116
- ${areaResources.map(resource => createResourceCard(resource)).join('')}
117
- </div>
118
- </div>
119
-
120
- <!-- Carousel Navigation -->
121
- <div class="flex justify-center mt-4 space-x-2">
122
- <button class="resource-carousel-prev bg-gray-200 hover:bg-gray-300 text-gray-700 px-3 py-2 rounded-md transition-colors">
123
- <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
124
- <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 19l-7-7 7-7"></path>
125
- </svg>
126
- </button>
127
- <button class="resource-carousel-next bg-gray-200 hover:bg-gray-300 text-gray-700 px-3 py-2 rounded-md transition-colors">
128
- <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
129
- <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7"></path>
130
- </svg>
131
- </button>
132
- </div>
133
  </div>
134
  </div>
135
  </section>
@@ -138,158 +178,32 @@ export function renderAreaPage(areaId) {
138
  return {
139
  content,
140
  init: () => {
141
- // Initialize artifact carousels for each sub-area with a small delay
142
- // to ensure DOM elements are fully rendered
143
  setTimeout(async () => {
144
  try {
145
- console.log('Accessing artifacts from global scope...');
146
- // Access artifacts from global scope
147
  const allArtifacts = window.allArtifacts || [];
148
- if (allArtifacts.length === 0) {
149
- console.warn('Global artifacts data not available or empty. Falling back to featuredArtifacts.');
150
- }
151
 
152
- subAreas.forEach(subArea => {
153
- try {
154
- // Filter artifacts by topics (subAreaTags in JSON)
155
- const filteredArtifacts = allArtifacts.filter(artifact =>
156
- artifact.topics && artifact.topics.includes(subArea.id)
157
- );
158
-
159
- // Transform field names to match expected format
160
- const transformedArtifacts = filteredArtifacts.map(artifact => ({
161
- ...artifact,
162
- areaTags: artifact.areas,
163
- subAreaTags: artifact.topics,
164
- sourceUrl: artifact.url
165
- }));
166
-
167
- console.log(`Filtered artifacts for ${subArea.id}:`, transformedArtifacts.length);
168
- const carouselId = `${subArea.id}-artifacts-carousel`;
169
- createArtifactCarousel(transformedArtifacts, carouselId);
170
- } catch (error) {
171
- console.error(`Error creating carousel for ${subArea.id}:`, error);
172
- // Fallback to featured artifacts if filtering fails
173
- const carouselId = `${subArea.id}-artifacts-carousel`;
174
- createArtifactCarousel(featuredArtifacts, carouselId);
175
- }
176
- });
177
  } catch (error) {
178
- console.error('Error accessing global artifacts data:', error);
179
- // Fallback to featured artifacts for all carousels
180
- subAreas.forEach(subArea => {
181
- const carouselId = `${subArea.id}-artifacts-carousel`;
182
- createArtifactCarousel(featuredArtifacts, carouselId);
183
- });
184
  }
185
  }, 50);
186
-
187
- // Initialize resource cards
188
- initializeResourceCards();
189
-
190
- // Initialize resource carousel if there are resources
191
- if (areaResources.length > 0) {
192
- initializeAreaResourceCarousel(areaResources);
193
- }
194
-
195
- // Initialize background attribution
196
- initializeAreaBackgroundAttribution();
197
  }
198
  };
199
  }
200
-
201
- export function getAreaPageSidebar(areaId) {
202
- const area = areasData[areaId];
203
- if (!area) {
204
- return '<div class="p-4">Area not found</div>';
205
- }
206
-
207
- const subAreas = Object.entries(area.subAreas).map(([key, value]) => ({
208
- id: key,
209
- name: typeof value === 'string' ? value : value.name
210
- }));
211
-
212
- const sidebarItems = [
213
- {
214
- label: 'Overview',
215
- href: '#overview',
216
- isNested: false,
217
- isActive: window.location.hash === '#overview',
218
- },
219
- {
220
- label: 'Topics',
221
- isHeader: true,
222
- isNested: true,
223
- subItems: subAreas.map(subArea => ({
224
- label: subArea.name,
225
- href: `#${subArea.id}`,
226
- isActive: window.location.hash === `#${subArea.id}`,
227
- })),
228
- },
229
- {
230
- label: 'Resources',
231
- href: '#resources',
232
- isNested: false,
233
- isActive: window.location.hash === '#resources',
234
- },
235
- ];
236
-
237
- return renderSidebar('On This Page', sidebarItems);
238
- }
239
-
240
- function initializeAreaResourceCarousel(resources) {
241
- const carousel = document.querySelector('.resource-carousel');
242
- const prevButton = document.querySelector('.resource-carousel-prev');
243
- const nextButton = document.querySelector('.resource-carousel-next');
244
-
245
- if (!carousel || !prevButton || !nextButton) {
246
- return;
247
- }
248
-
249
- let currentIndex = 0;
250
- const cardWidth = 624; // 600px card + 24px gap
251
- const visibleCards = 1.5; // Show 1.5 cards at a time (larger cards)
252
- const maxIndex = resources.length - 1; // Allow going to the last item
253
-
254
- function updateCarousel() {
255
- const translateX = -currentIndex * cardWidth;
256
- carousel.style.transform = `translateX(${translateX}px)`;
257
-
258
- // Update button states - no disabled states for looping
259
- prevButton.classList.remove('opacity-50', 'cursor-not-allowed');
260
- nextButton.classList.remove('opacity-50', 'cursor-not-allowed');
261
- }
262
-
263
- prevButton.addEventListener('click', () => {
264
- currentIndex = currentIndex > 0 ? currentIndex - 1 : maxIndex;
265
- updateCarousel();
266
- });
267
-
268
- nextButton.addEventListener('click', () => {
269
- currentIndex = currentIndex < maxIndex ? currentIndex + 1 : 0;
270
- updateCarousel();
271
- });
272
-
273
- // Initialize carousel state
274
- updateCarousel();
275
- }
276
-
277
- function initializeAreaBackgroundAttribution() {
278
- const backgroundContainer = document.getElementById('area-background-container');
279
- const attribution = document.getElementById('area-bg-attribution');
280
-
281
- if (!backgroundContainer || !attribution) {
282
- return;
283
- }
284
-
285
- // Show attribution on hover over the background container
286
- backgroundContainer.addEventListener('mouseenter', () => {
287
- attribution.style.opacity = '1';
288
- });
289
-
290
- backgroundContainer.addEventListener('mouseleave', () => {
291
- attribution.style.opacity = '0';
292
- });
293
- }
294
-
295
- // Colors are now defined in the areas data and accessed via subArea.gradient
 
1
+ // pages/AreaPage.js - Single-topic view for area pages
2
  import { areasData } from '../data/areas.js';
3
  import { featuredArtifacts } from '../data/artifacts.js';
4
  import { createArtifactCarousel } from '../cards/ArtifactSummaryCard.js';
5
  import { sampleResources } from '../data/resources.js';
6
  import { createResourceCard, initializeResourceCards } from '../cards/ResourceCard.js';
 
7
 
8
+ export function renderAreaPage(areaId, topicId = null) {
9
  const area = areasData[areaId];
10
  if (!area) {
11
  return {
12
+ content: `<div class="bg-white/90 backdrop-blur-md rounded-lg shadow-sm p-8 mx-4"><h1 class="text-2xl font-bold text-red-600">Area not found</h1></div>`,
13
  init: () => {}
14
  };
15
  }
16
 
17
+ // If no topic specified, show overview of the area
18
+ if (!topicId) {
19
+ return renderAreaOverview(area, areaId);
20
+ }
21
 
22
+ // Show specific topic
23
+ const topic = area.subAreas[topicId];
24
+ if (!topic) {
25
+ return {
26
+ content: `<div class="bg-white/90 backdrop-blur-md rounded-lg shadow-sm p-8 mx-4"><h1 class="text-2xl font-bold text-red-600">Topic not found</h1></div>`,
27
+ init: () => {}
28
+ };
29
+ }
30
+
31
+ return renderTopicView(area, areaId, topic, topicId);
32
+ }
33
+
34
+ function renderAreaOverview(area, areaId) {
35
+ // Get sub-areas as array
36
+ const topics = Object.entries(area.subAreas).map(([key, value]) => ({
37
  id: key,
38
  name: typeof value === 'string' ? value : value.name,
39
+ navName: typeof value === 'string' ? value : (value.navName || value.name),
40
  description: typeof value === 'string' ? '' : (value.description || ''),
41
+ color: typeof value === 'object' ? value.color : 'bg-gray-200 text-gray-700'
 
42
  }));
43
 
44
  const content = `
45
+ <!-- Top spacing -->
46
+ <div class="mb-8"></div>
47
+
48
+ <!-- Page Navigation Card -->
49
+ <section class="mb-8 relative z-20 px-4 sm:px-6 lg:px-8">
50
+ <div class="bg-white/70 backdrop-blur-sm shadow-sm rounded-lg px-6 py-4 w-full" style="max-width: min(90%, 1400px); margin: 0 auto;">
51
+ <div class="flex items-center gap-3 overflow-x-auto">
52
+ <span class="text-gray-600 font-semibold whitespace-nowrap">${area.navTitle}:</span>
53
+ <a href="/${areaId}" class="px-3 py-1.5 text-sm font-semibold text-blue-600 underline decoration-2 underline-offset-2 hover:bg-blue-50 rounded transition-colors whitespace-nowrap">Overview</a>
54
+ ${topics.map((topic, index) => `
55
+ ${index === 0 ? '<span class="text-gray-300">•</span>' : ''}
56
+ <a href="/${areaId}/${topic.id}" class="px-3 py-1.5 text-sm font-medium text-gray-700 hover:text-blue-600 hover:bg-blue-50 rounded transition-colors whitespace-nowrap">${topic.navName}</a>
57
+ ${index < topics.length - 1 ? '<span class="text-gray-300">•</span>' : ''}
58
+ `).join('')}
59
+ </div>
60
  </div>
61
+ </section>
62
+
63
+ <!-- Area Overview Section -->
64
+ <section id="overview" class="mb-16 relative z-20 px-4 sm:px-6 lg:px-8">
65
+ <div class="bg-white/90 backdrop-blur-md shadow-sm rounded-lg p-6 md:p-10 w-full" style="max-width: min(90%, 1400px); margin: 0 auto;">
66
+ <h1 class="text-3xl md:text-4xl font-bold text-gray-900 mb-6">${area.title}</h1>
67
 
68
+ <!-- Area Description -->
69
  <div class="mb-8">
70
+ <p class="text-gray-700 leading-relaxed">${area.description}</p>
71
  </div>
72
 
73
  <!-- Role of Openness -->
74
+ ${area.openness ? `
75
+ <div class="px-4 sm:px-6 py-5 bg-gradient-to-r from-orange-50/95 to-yellow-50/95 backdrop-blur-sm border-l-4 border-orange-400 rounded-r-lg mb-8">
76
+ <p class="font-bold text-orange-900 mb-3 text-lg">🤗 The Role of Openness</p>
77
  <p class="text-orange-800 italic leading-relaxed">${area.openness}</p>
78
  </div>
79
+ ` : ''}
80
+
81
+ <!-- Topics within this area -->
82
+ <div>
83
+ <p class="text-xs font-semibold text-gray-600 uppercase tracking-wide mb-3">Topics in this area:</p>
84
+ <div class="flex flex-wrap gap-2">
85
+ ${topics.map(topic => {
86
+ // Extract colors from topic.color
87
+ let bgColor = 'bg-gray-200';
88
+ let textColor = 'text-gray-700';
89
+ if (topic.color) {
90
+ const bgMatch = topic.color.match(/bg-(\w+)-(\d+)/);
91
+ const textMatch = topic.color.match(/text-(\w+)-(\d+)/);
92
+ if (bgMatch) bgColor = `bg-${bgMatch[1]}-${bgMatch[2]}`;
93
+ if (textMatch) textColor = `text-${textMatch[1]}-700`;
94
+ }
95
+ return `
96
+ <a href="/${areaId}/${topic.id}" class="inline-block px-3 py-1.5 text-sm ${bgColor} ${textColor} rounded hover:opacity-80 transition-opacity">
97
+ ${topic.navName}
98
+ </a>
99
+ `;
100
+ }).join('')}
101
  </div>
102
  </div>
103
  </div>
104
  </section>
105
+ `;
106
 
107
+ return {
108
+ content,
109
+ init: () => {}
110
+ };
111
+ }
 
 
 
 
 
 
112
 
113
+ function renderTopicView(area, areaId, topic, topicId) {
114
+ const topicName = topic.navName || topic.name;
115
+ const topicDescription = topic.description || '';
116
+ const topicOpenness = topic.openness || '';
117
+
118
+ // Get all topics for navigation
119
+ const allTopics = Object.entries(area.subAreas).map(([key, value]) => ({
120
+ id: key,
121
+ navName: typeof value === 'string' ? value : (value.navName || value.name)
122
+ }));
123
 
124
+ const content = `
125
+ <!-- Top spacing -->
126
+ <div class="mb-8"></div>
127
+
128
+ <!-- Page Navigation Card -->
129
+ <section class="mb-8 relative z-20 px-4 sm:px-6 lg:px-8">
130
+ <div class="bg-white/70 backdrop-blur-sm shadow-sm rounded-lg px-6 py-4 w-full" style="max-width: min(90%, 1400px); margin: 0 auto;">
131
+ <div class="flex items-center gap-3 overflow-x-auto">
132
+ <span class="text-gray-600 font-semibold whitespace-nowrap">${area.navTitle}:</span>
133
+ <a href="/${areaId}" class="px-3 py-1.5 text-sm font-medium text-gray-700 hover:text-blue-600 hover:bg-blue-50 rounded transition-colors whitespace-nowrap">Overview</a>
134
+ ${allTopics.map((t, index) => `
135
+ ${index === 0 ? '<span class="text-gray-300">•</span>' : ''}
136
+ <a href="/${areaId}/${t.id}" class="px-3 py-1.5 text-sm font-medium transition-colors whitespace-nowrap rounded ${t.id === topicId ? 'text-blue-600 font-semibold underline decoration-2 underline-offset-2' : 'text-gray-700 hover:text-blue-600 hover:bg-blue-50'}">${t.navName}</a>
137
+ ${index < allTopics.length - 1 ? '<span class="text-gray-300">•</span>' : ''}
138
+ `).join('')}
139
+ </div>
140
+ </div>
141
+ </section>
142
+
143
+ <!-- Topic Content Section -->
144
+ <section id="${topicId}" class="mb-16 relative z-20 px-4 sm:px-6 lg:px-8">
145
+ <div class="bg-white/80 backdrop-blur-md shadow-sm rounded-lg p-6 md:p-10 w-full" style="max-width: min(90%, 1400px); margin: 0 auto;">
146
+ <!-- Breadcrumb -->
147
+ <div class="mb-4 text-sm text-gray-600">
148
+ <a href="/${areaId}" class="hover:text-blue-600 transition-colors">${area.navTitle}</a>
149
+ <span class="mx-2">›</span>
150
+ <span class="text-gray-900 font-medium">${topicName}</span>
151
  </div>
 
 
152
 
153
+ <h1 class="text-3xl md:text-4xl font-bold text-gray-900 mb-6">${topic.name}</h1>
 
 
 
154
 
155
+ <!-- Topic Description -->
156
+ ${topicDescription ? `
157
+ <div class="mb-8">
158
+ <p class="text-gray-700 leading-relaxed">${topicDescription}</p>
 
159
  </div>
160
+ ` : ''}
161
 
162
+ <!-- Role of Openness for this topic -->
163
+ ${topicOpenness ? `
164
+ <div class="px-4 sm:px-6 py-5 bg-gradient-to-r from-orange-50/95 to-yellow-50/95 backdrop-blur-sm border-l-4 border-orange-400 rounded-r-lg mb-8">
165
+ <p class="font-bold text-orange-900 mb-3 text-lg">🤗 The Role of Openness</p>
166
+ <p class="text-orange-800 italic leading-relaxed">${topicOpenness}</p>
167
+ </div>
168
+ ` : ''}
169
+
170
+ <h3 class="text-xl md:text-2xl font-bold text-gray-900 mb-6">Related Research & Resources</h3>
171
+ <div id="${topicId}-artifacts-carousel" class="overflow-x-auto -mx-2">
172
+ <!-- Carousel will be inserted here -->
 
 
 
 
 
 
 
 
 
 
173
  </div>
174
  </div>
175
  </section>
 
178
  return {
179
  content,
180
  init: () => {
181
+ // Initialize artifact carousel for this topic
 
182
  setTimeout(async () => {
183
  try {
 
 
184
  const allArtifacts = window.allArtifacts || [];
 
 
 
185
 
186
+ // Filter artifacts by this specific topic
187
+ const filteredArtifacts = allArtifacts.filter(artifact =>
188
+ artifact.topics && artifact.topics.includes(topicId)
189
+ );
190
+
191
+ // Transform field names to match expected format
192
+ const transformedArtifacts = filteredArtifacts.map(artifact => ({
193
+ ...artifact,
194
+ areaTags: artifact.areas,
195
+ subAreaTags: artifact.topics,
196
+ sourceUrl: artifact.url
197
+ }));
198
+
199
+ const carouselId = `${topicId}-artifacts-carousel`;
200
+ createArtifactCarousel(transformedArtifacts.length > 0 ? transformedArtifacts : featuredArtifacts, carouselId);
 
 
 
 
 
 
 
 
 
 
201
  } catch (error) {
202
+ console.error(`Error creating carousel for ${topicId}:`, error);
203
+ const carouselId = `${topicId}-artifacts-carousel`;
204
+ createArtifactCarousel(featuredArtifacts, carouselId);
 
 
 
205
  }
206
  }, 50);
 
 
 
 
 
 
 
 
 
 
 
207
  }
208
  };
209
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
js/pages/HomePage.js CHANGED
@@ -5,83 +5,89 @@ import { createArtifactCarousel } from '../cards/ArtifactSummaryCard.js';
5
  import { areasData, homeBackgroundImage } from '../data/areas.js';
6
  import { featuredArtifacts } from '../data/artifacts.js';
7
  import { teamMembers } from '../data/team.js';
8
- import { renderSidebar } from '../utils/sidebar.js'; // Import the new sidebar component
9
 
10
  export function renderHomePage() {
11
  const content = `
12
- <!-- Team Introduction Section -->
13
- <section id="intro" class="mb-12 relative z-20">
14
- <!-- Part 1: Team Introduction and Research Areas -->
15
- <div class="bg-white rounded-lg shadow-sm p-8 mb-6">
16
- <h3 class="text-3xl font-bold text-gray-900 mb-6">Team Introduction</h3>
17
 
18
- <!-- Research Areas Introduction -->
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
19
  <div class="mb-8">
20
- <p class="text-gray-700 leading-relaxed mb-6">We're a multidisciplinary team working on research and regulatory questions related to AI systems &mdash; their (open) development, governance, and impact on society at large. Our work spans four key inter-connected areas where AI technology intersects with society:</p>
21
-
22
- <!-- Research Areas Navigation -->
23
- <div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4">
24
- ${Object.values(areasData).map(area => `
25
- <a
26
- href="#${area.id}"
27
- class="group p-4 bg-gradient-to-br ${area.colors.gradient} rounded-lg border transition-all duration-200 text-left block no-underline"
28
- >
29
- <h4 class="font-semibold text-sm mb-1 group-hover:scale-105 transition-transform">${area.title}</h4>
30
- <p class="text-xs opacity-80">${getAreaShortDescription(area.id)}</p>
31
- </a>
32
- `).join('')}
33
- </div>
34
-
35
- <!-- Perspective and Positionality -->
36
- <div class="mt-8 px-4 py-4 bg-gradient-to-r from-orange-50 to-yellow-50 border-l-4 border-orange-300 rounded-r-lg">
37
- <p class="font-bold text-center text-orange-900 mb-2">🤗 Perspective and Positionality</p>
38
- <p class="text-orange-800 italic leading-relaxed">
39
- Our work on these topics is shaped by the context of our work at Hugging Face, which is the main platform for open and collaborative development and sharing of Artificial Intelligence artifacts. As a result, much of our work discusses the specific roles of openness and transparency in shaping AI technology into a more equitable and better-governed category of technology.
40
  </p>
41
  </div>
42
  </div>
43
 
44
-
45
-
46
- <!-- Recent & Featured Works -->
47
- <div id="recent-featured-works" class="mb-12 border-t border-gray-200 pt-6">
48
- <h4 class="text-lg font-semibold text-gray-900 mb-4">Recent & Featured Works</h4>
49
- <div class="mb-8">
50
- <div id="featured-artifacts-carousel">
51
- <!-- Carousel will be inserted here -->
52
- </div>
53
- </div>
54
  </div>
 
 
55
 
56
-
57
- <!-- Team and Collaborators -->
58
- <div id="team-members" class="border-t border-gray-200 pt-6">
59
- <h4 class="text-lg font-semibold text-gray-900 mb-4">Team Members</h4>
60
- <div class="mb-4">
61
- <div id="team-grid" class="grid grid-cols-1 md:grid-cols-2 gap-4">
62
- <!-- Team members will be inserted here by JavaScript -->
63
- </div>
64
- </div>
65
- <div class="prose text-gray-700">
66
- <p>We also work closely with
67
- <a href="https://huggingface.co/irenesolaiman" class="text-blue-600 hover:text-blue-800 transition-colors" target="_blank">Irene Solaiman</a> (Chief Policy Officer),
68
- <a href="https://huggingface.co/evijit" class="text-blue-600 hover:text-blue-800 transition-colors" target="_blank">Avijit Ghosh</a> (Applied Policy Researcher) in the policy team,
69
- and with <a href="https://huggingface.co/meg" class="text-blue-600 hover:text-blue-800 transition-colors" target="_blank">Meg Mitchell</a> (Chief Ethics Scientist),
70
- and with <a href="https://huggingface.co/brunatrevelin" class="text-blue-600 hover:text-blue-800 transition-colors" target="_blank">Bruna Trevelin</a> (Legal Counsel)!
71
- </p>
72
- </div>
73
  </div>
74
  </div>
75
  </section>
76
 
77
- <!-- Research Areas Header -->
78
- <div id="research-areas-header" class="text-center mb-12 relative z-20">
79
- <h2 class="text-3xl font-bold text-gray-900">Research Areas</h2>
80
- </div>
 
 
 
 
 
81
 
82
- <!-- Research Areas -->
83
- <section id="research-areas" class="space-y-8 relative z-20">
84
- <!-- Area cards will be inserted here by JavaScript -->
 
 
 
 
 
 
 
 
 
 
 
 
 
85
  </section>
86
  `;
87
 
@@ -96,59 +102,6 @@ export function renderHomePage() {
96
  };
97
  }
98
 
99
- export function getHomePageSidebar() {
100
- const sidebarItems = [
101
- {
102
- label: 'Team Introduction',
103
- href: '#intro',
104
- isNested: false,
105
- isActive: window.location.hash === '#intro',
106
- subItems: [
107
- {
108
- label: 'Recent & Featured Works',
109
- href: '#recent-featured-works',
110
- isActive: window.location.hash === '#recent-featured-works',
111
- },
112
- {
113
- label: 'Team Members',
114
- href: '#team-members',
115
- isActive: window.location.hash === '#team-members',
116
- },
117
- ],
118
- },
119
- {
120
- label: 'Research Areas',
121
- href: '#research-areas-header',
122
- isNested: false,
123
- isActive: window.location.hash === '#research-areas-header',
124
- subItems: [
125
- {
126
- label: 'Efficiency & Environment',
127
- href: '#efficiency',
128
- isActive: window.location.hash === '#efficiency',
129
- },
130
- {
131
- label: 'Consent & Personal Interactions',
132
- href: '#personal',
133
- isActive: window.location.hash === '#personal',
134
- },
135
- {
136
- label: 'Rights & Regulation',
137
- href: '#rights',
138
- isActive: window.location.hash === '#rights',
139
- },
140
- {
141
- label: 'Socio-Economic & Technical Ecosystems',
142
- href: '#ecosystems',
143
- isActive: window.location.hash === '#ecosystems',
144
- },
145
- ],
146
- },
147
- ];
148
-
149
- return renderSidebar('On This Page', sidebarItems);
150
- }
151
-
152
  function initializeTeamMembers() {
153
  const teamContainer = document.getElementById('team-grid');
154
  if (!teamContainer) {
@@ -171,13 +124,89 @@ function getAreaShortDescription(areaId) {
171
  }
172
 
173
  function initializeHomeAreaCards() {
174
- const areasContainer = document.getElementById('research-areas');
175
  if (!areasContainer) return;
176
 
177
  const areas = Object.values(areasData);
178
- areasContainer.innerHTML = areas.map(area =>
179
- createHomeAreaCard(area.id, area.title, area.description, area.openness, Object.values(area.subAreas).map(sub => typeof sub === 'string' ? sub : sub.name), area.image, area.imagePosition, area.imageAttribution, area.imageAltText)
180
- ).join('');
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
181
  }
182
 
183
  function initializeArtifactCarousels() {
 
5
  import { areasData, homeBackgroundImage } from '../data/areas.js';
6
  import { featuredArtifacts } from '../data/artifacts.js';
7
  import { teamMembers } from '../data/team.js';
 
8
 
9
  export function renderHomePage() {
10
  const content = `
11
+ <!-- Top spacing -->
12
+ <div class="mb-8"></div>
 
 
 
13
 
14
+ <!-- Page Navigation Card -->
15
+ <section class="mb-8 relative z-20 px-4 sm:px-6 lg:px-8">
16
+ <div class="bg-white/70 backdrop-blur-sm shadow-sm rounded-lg px-6 py-4 w-full" style="max-width: min(90%, 1400px); margin: 0 auto;">
17
+ <div class="flex items-center gap-3 overflow-x-auto">
18
+ <span class="text-gray-600 font-semibold whitespace-nowrap">On this page:</span>
19
+ <a href="/#about" class="px-3 py-1.5 text-sm font-medium text-gray-700 hover:text-blue-600 hover:bg-blue-50 rounded transition-colors whitespace-nowrap">About</a>
20
+ <span class="text-gray-300">•</span>
21
+ <a href="/#recent-works" class="px-3 py-1.5 text-sm font-medium text-gray-700 hover:text-blue-600 hover:bg-blue-50 rounded transition-colors whitespace-nowrap">Recent Works</a>
22
+ <span class="text-gray-300">•</span>
23
+ <a href="/#research-areas" class="px-3 py-1.5 text-sm font-medium text-gray-700 hover:text-blue-600 hover:bg-blue-50 rounded transition-colors whitespace-nowrap">Research Areas</a>
24
+ <span class="text-gray-300">•</span>
25
+ <a href="/#team" class="px-3 py-1.5 text-sm font-medium text-gray-700 hover:text-blue-600 hover:bg-blue-50 rounded transition-colors whitespace-nowrap">Team Members</a>
26
+ </div>
27
+ </div>
28
+ </section>
29
+
30
+ <!-- About Section - Responsive Width with Frosted Glass -->
31
+ <section id="about" class="mb-16 relative z-20 px-4 sm:px-6 lg:px-8">
32
+ <div class="bg-white/40 backdrop-blur-sm shadow-sm rounded-lg p-6 md:p-10 w-full" style="max-width: min(90%, 1400px); margin: 0 auto;">
33
+ <h2 class="text-3xl md:text-4xl font-bold text-gray-900 mb-6">About</h2>
34
+
35
+ <!-- Introduction Text - Single column -->
36
  <div class="mb-8">
37
+ <div>
38
+ <p class="text-gray-700 leading-relaxed">
39
+ We're a multidisciplinary team working on research and regulatory questions related to AI systems &mdash; their (open) development, governance, and impact on society at large.
40
+ Our work spans four key inter-connected areas where AI technology intersects with society:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
41
  </p>
42
  </div>
43
  </div>
44
 
45
+ <!-- Perspective and Positionality -->
46
+ <div class="px-4 sm:px-6 py-5 bg-gradient-to-r from-orange-50/95 to-yellow-50/95 backdrop-blur-sm border-l-4 border-orange-400 rounded-r-lg">
47
+ <p class="font-bold text-orange-900 mb-3 text-lg">🤗 Perspective and Positionality</p>
48
+ <p class="text-orange-800 italic leading-relaxed">
49
+ Our work on these topics is shaped by the context of our work at Hugging Face, which is the main platform for open and collaborative development and sharing of Artificial Intelligence artifacts. As a result, much of our work discusses the specific roles of openness and transparency in shaping AI technology into a more equitable and better-governed category of technology.
50
+ </p>
 
 
 
 
51
  </div>
52
+ </div>
53
+ </section>
54
 
55
+ <!-- Featured Works Section - Transparent Card -->
56
+ <section id="recent-works" class="mb-16 relative z-20 px-4 sm:px-6 lg:px-8">
57
+ <div class="bg-white/20 backdrop-blur-sm shadow-sm rounded-lg p-6 md:p-10 w-full" style="max-width: min(90%, 1400px); margin: 0 auto;">
58
+ <h2 class="text-3xl md:text-4xl font-bold text-gray-900 mb-6">Recent & Featured Works</h2>
59
+ <div id="featured-artifacts-carousel" class="overflow-x-auto -mx-2">
60
+ <!-- Carousel will be inserted here -->
 
 
 
 
 
 
 
 
 
 
 
61
  </div>
62
  </div>
63
  </section>
64
 
65
+ <!-- Research Areas Section - Transparent Card with 2x2 Grid -->
66
+ <section id="research-areas" class="mb-16 relative z-20 px-4 sm:px-6 lg:px-8">
67
+ <div class="bg-white/20 backdrop-blur-sm shadow-sm rounded-lg p-6 md:p-10 w-full" style="max-width: min(90%, 1400px); margin: 0 auto;">
68
+ <h2 class="text-3xl md:text-4xl font-bold text-gray-900 mb-8 text-center">Research Areas</h2>
69
+ <div id="research-areas-grid" class="grid grid-cols-1 md:grid-cols-2 gap-4 sm:gap-6">
70
+ <!-- Area cards will be inserted here by JavaScript -->
71
+ </div>
72
+ </div>
73
+ </section>
74
 
75
+ <!-- Team Members Section - Frosted Background -->
76
+ <section id="team" class="mb-16 relative z-20 px-4 sm:px-6 lg:px-8">
77
+ <div class="bg-white/40 backdrop-blur-sm shadow-sm rounded-lg p-6 md:p-10 w-full" style="max-width: min(90%, 1400px); margin: 0 auto;">
78
+ <h2 class="text-3xl md:text-4xl font-bold text-gray-900 mb-6">Team Members</h2>
79
+ <div id="team-grid" class="grid grid-cols-1 md:grid-cols-2 gap-4 sm:gap-6 mb-6">
80
+ <!-- Team members will be inserted here by JavaScript -->
81
+ </div>
82
+ <div class="prose max-w-none text-gray-700 pt-4 border-t border-gray-200">
83
+ <p>We also work closely with
84
+ <a href="https://huggingface.co/irenesolaiman" class="text-blue-600 hover:text-blue-800 transition-colors" target="_blank">Irene Solaiman</a> (Chief Policy Officer),
85
+ <a href="https://huggingface.co/evijit" class="text-blue-600 hover:text-blue-800 transition-colors" target="_blank">Avijit Ghosh</a> (Applied Policy Researcher) in the policy team,
86
+ and with <a href="https://huggingface.co/meg" class="text-blue-600 hover:text-blue-800 transition-colors" target="_blank">Meg Mitchell</a> (Chief Ethics Scientist),
87
+ and with <a href="https://huggingface.co/brunatrevelin" class="text-blue-600 hover:text-blue-800 transition-colors" target="_blank">Bruna Trevelin</a> (Legal Counsel)!
88
+ </p>
89
+ </div>
90
+ </div>
91
  </section>
92
  `;
93
 
 
102
  };
103
  }
104
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
105
  function initializeTeamMembers() {
106
  const teamContainer = document.getElementById('team-grid');
107
  if (!teamContainer) {
 
124
  }
125
 
126
  function initializeHomeAreaCards() {
127
+ const areasContainer = document.getElementById('research-areas-grid');
128
  if (!areasContainer) return;
129
 
130
  const areas = Object.values(areasData);
131
+ areasContainer.innerHTML = areas.map(area => createResearchAreaCard(area)).join('');
132
+ }
133
+
134
+ function createResearchAreaCard(area) {
135
+ // Get first sentence of description
136
+ const shortDesc = area.description.split('.')[0] + '.';
137
+
138
+ // Get topic names and colors from the subAreas - same logic as ArtifactSummaryCard
139
+ const topics = Object.values(area.subAreas).map(subArea => {
140
+ const topicName = subArea.navName || subArea.name;
141
+ let bgColor = 'bg-gray-200';
142
+ let textColor = 'text-gray-700';
143
+
144
+ // Extract background and text color from the subArea color class
145
+ if (subArea.color) {
146
+ // Match pattern like "bg-emerald-100 text-emerald-800"
147
+ const bgMatch = subArea.color.match(/bg-(\w+)-(\d+)/);
148
+ const textMatch = subArea.color.match(/text-(\w+)-(\d+)/);
149
+
150
+ if (bgMatch) {
151
+ bgColor = `bg-${bgMatch[1]}-${bgMatch[2]}`;
152
+ }
153
+ if (textMatch) {
154
+ textColor = `text-${textMatch[1]}-700`; // Use consistent 700 shade like artifact cards
155
+ }
156
+ }
157
+
158
+ return {
159
+ name: topicName,
160
+ bgColor,
161
+ textColor
162
+ };
163
+ });
164
+
165
+ return `
166
+ <a href="/${area.id}"
167
+ class="group relative block border border-gray-200 rounded-lg overflow-hidden bg-white hover:shadow-lg transition-all duration-200 h-64">
168
+
169
+ <!-- Background image with low opacity -->
170
+ ${area.image ? `
171
+ <div class="absolute inset-0 opacity-15 group-hover:opacity-30 transition-opacity">
172
+ <img src="/images/${area.image}" alt="" class="w-full h-full object-cover">
173
+ </div>
174
+ ` : ''}
175
+
176
+ <!-- Content -->
177
+ <div class="relative p-5 h-full flex flex-col">
178
+ <!-- Header -->
179
+ <div class="flex justify-between items-start mb-3 flex-shrink-0">
180
+ <h3 class="text-lg font-bold text-gray-900 leading-tight">${area.navTitle}</h3>
181
+ <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">
182
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7"></path>
183
+ </svg>
184
+ </div>
185
+
186
+ <!-- Description - scrollable -->
187
+ <div class="text-md text-gray-700 mb-4 flex-grow overflow-y-auto pr-2">
188
+ <p class="leading-relaxed">${area.description}</p>
189
+ </div>
190
+
191
+ <!-- Topics with colors from subAreas - same style as artifact cards -->
192
+ <div class="flex-shrink-0">
193
+ <p class="text-xs font-semibold text-gray-600 uppercase tracking-wide mb-2">Topics:</p>
194
+ <div class="flex flex-wrap gap-2">
195
+ ${topics.map(topic => `
196
+ <span class="inline-block px-2 py-0.5 text-sm ${topic.bgColor} ${topic.textColor} rounded">
197
+ ${topic.name}
198
+ </span>
199
+ `).join('')}
200
+ </div>
201
+ </div>
202
+
203
+ <!-- Image Attribution -->
204
+ ${area.imageAttribution ? `
205
+ <p class="text-xs text-gray-500 mt-3 pt-2 border-t border-gray-100 flex-shrink-0">${area.imageAttribution}</p>
206
+ ` : ''}
207
+ </div>
208
+ </a>
209
+ `;
210
  }
211
 
212
  function initializeArtifactCarousels() {
js/pages/ResourcesPage.js CHANGED
@@ -3,7 +3,6 @@ import { homeBackgroundImage, areasData } from '../data/areas.js';
3
  import { pressMentions } from '../data/press.js';
4
  import { sampleResources } from '../data/resources.js';
5
  import { createResourceCard, initializeResourceCards } from '../cards/ResourceCard.js';
6
- import { renderSidebar } from '../utils/sidebar.js'; // Import the new sidebar component
7
 
8
  export function renderResourcesPage() {
9
  const content = `
@@ -107,7 +106,7 @@ export function renderResourcesPage() {
107
  <div class="bg-gray-50 rounded-lg p-4 ${borderSide} ${borderColor} relative overflow-hidden">
108
  <!-- Background image for this article -->
109
  <div class="absolute inset-0 opacity-5 pointer-events-none">
110
- <img src="images/${primaryArea?.image || 'ai.png'}" alt="" class="w-full h-full object-cover">
111
  </div>
112
 
113
  <div class="relative z-10">
@@ -156,25 +155,6 @@ export function renderResourcesPage() {
156
  };
157
  }
158
 
159
- export function getResourcesPageSidebar() {
160
- const sidebarItems = [
161
- {
162
- label: 'Technical Resources',
163
- href: '#technical-resources',
164
- isNested: false,
165
- isActive: window.location.hash === '#technical-resources',
166
- },
167
- {
168
- label: 'Press Mentions',
169
- href: '#press-mentions',
170
- isNested: false,
171
- isActive: window.location.hash === '#press-mentions',
172
- },
173
- ];
174
-
175
- return renderSidebar('Resources', sidebarItems);
176
- }
177
-
178
  function initializeResourcesBackgroundAttribution() {
179
  const backgroundContainer = document.getElementById('resources-background-container');
180
  const attribution = document.getElementById('resources-bg-attribution');
 
3
  import { pressMentions } from '../data/press.js';
4
  import { sampleResources } from '../data/resources.js';
5
  import { createResourceCard, initializeResourceCards } from '../cards/ResourceCard.js';
 
6
 
7
  export function renderResourcesPage() {
8
  const content = `
 
106
  <div class="bg-gray-50 rounded-lg p-4 ${borderSide} ${borderColor} relative overflow-hidden">
107
  <!-- Background image for this article -->
108
  <div class="absolute inset-0 opacity-5 pointer-events-none">
109
+ <img src="/images/${primaryArea?.image || 'ai.png'}" alt="" class="w-full h-full object-cover">
110
  </div>
111
 
112
  <div class="relative z-10">
 
155
  };
156
  }
157
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
158
  function initializeResourcesBackgroundAttribution() {
159
  const backgroundContainer = document.getElementById('resources-background-container');
160
  const attribution = document.getElementById('resources-bg-attribution');