@@ -55,18 +55,18 @@ const resources = [
5555 Skip to main content
5656</a >
5757
58- <header class =" sticky top-0 bg-white dark:bg-gray-900 shadow-sm z-30" >
58+ <header class =" sticky top-0 bg-white dark:bg-gray-900 shadow-sm z-30" role = " banner " >
5959 <div class =" max-w-7xl mx-auto px-4 sm:px-6 lg:px-8" >
6060 <div class =" flex justify-between h-16" >
6161 <!-- Logo/Brand -->
6262 <div class =" flex items-center" >
63- <a href =" /" class =" flex-shrink-0 flex items-center" >
63+ <a href =" /" class =" flex-shrink-0 flex items-center" aria-label = " OSS Communities Homepage " >
6464 <span class =" text-xl font-bold bg-gradient-to-r from-purple-700 to-pink-700 bg-clip-text text-transparent" >OSS Communities</span >
6565 </a >
6666 </div >
6767
6868 <!-- Desktop Navigation -->
69- <nav class =" hidden md:flex space-x-8 items-center" >
69+ <nav class =" hidden md:flex space-x-8 items-center" aria-label = " Main navigation " role = " navigation " >
7070 <a href =" /#tips" class =" text-gray-800 dark:text-gray-200 hover:text-purple-700 dark:hover:text-purple-300 px-3 py-2 text-sm font-medium" >Weekly Tips</a >
7171 <a href =" /#shoutouts" class =" text-gray-800 dark:text-gray-200 hover:text-purple-700 dark:hover:text-purple-300 px-3 py-2 text-sm font-medium" >Shoutouts</a >
7272 <a href =" /#courses" class =" text-gray-800 dark:text-gray-200 hover:text-purple-700 dark:hover:text-purple-300 px-3 py-2 text-sm font-medium" >Courses</a >
@@ -76,10 +76,17 @@ const resources = [
7676
7777 <!-- Mobile menu button -->
7878 <div class =" md:hidden flex items-center" >
79- <button type =" button" id =" mobile-menu-button" class =" text-gray-500 hover:text-gray-600 focus:outline-none focus:ring-2 focus:ring-purple-500" >
79+ <button
80+ type =" button"
81+ id =" mobile-menu-button"
82+ class =" text-gray-500 hover:text-gray-600 focus:outline-none focus:ring-2 focus:ring-purple-500"
83+ aria-label =" Open main menu"
84+ aria-expanded =" false"
85+ aria-controls =" mobile-menu"
86+ >
8087 <span class =" sr-only" >Open main menu</span >
8188 <!-- Icon for menu (3 horizontal lines) -->
82- <svg class =" h-6 w-6" xmlns =" http://www.w3.org/2000/svg" fill =" none" viewBox =" 0 0 24 24" stroke =" currentColor" >
89+ <svg class =" h-6 w-6" xmlns =" http://www.w3.org/2000/svg" fill =" none" viewBox =" 0 0 24 24" stroke =" currentColor" aria-hidden = " true " >
8390 <path stroke-linecap =" round" stroke-linejoin =" round" stroke-width =" 2" d =" M4 6h16M4 12h16M4 18h16" />
8491 </svg >
8592 </button >
@@ -88,7 +95,7 @@ const resources = [
8895 </div >
8996
9097 <!-- Mobile Navigation -->
91- <div class =" hidden md:hidden" id =" mobile-menu" >
98+ <div class =" hidden md:hidden" id =" mobile-menu" role = " navigation " aria-label = " Mobile navigation " >
9299 <div class =" px-2 pt-2 pb-3 space-y-1 sm:px-3" >
93100 <a href =" /#tips" class =" block px-3 py-2 rounded-md text-base font-medium text-gray-800 dark:text-gray-200 hover:bg-gray-50 dark:hover:bg-gray-800" >Weekly Tips</a >
94101 <a href =" /#shoutouts" class =" block px-3 py-2 rounded-md text-base font-medium text-gray-800 dark:text-gray-200 hover:bg-gray-50 dark:hover:bg-gray-800" >Shoutouts</a >
@@ -100,14 +107,33 @@ const resources = [
100107</header >
101108
102109<script >
103- // Simple toggle for mobile menu
110+ // Enhanced mobile menu toggle with ARIA state management
104111 document.addEventListener('DOMContentLoaded', () => {
105112 const mobileMenuButton = document.getElementById('mobile-menu-button');
106113 const mobileMenu = document.getElementById('mobile-menu');
107114
108115 mobileMenuButton?.addEventListener('click', () => {
116+ const isExpanded = mobileMenuButton.getAttribute('aria-expanded') === 'true';
117+ mobileMenuButton.setAttribute('aria-expanded', (!isExpanded).toString());
109118 mobileMenu?.classList.toggle('hidden');
110119 });
120+
121+ // Close mobile menu when clicking outside
122+ document.addEventListener('click', (event) => {
123+ if (!mobileMenuButton?.contains(event.target as Node) && !mobileMenu?.contains(event.target as Node)) {
124+ mobileMenu?.classList.add('hidden');
125+ mobileMenuButton?.setAttribute('aria-expanded', 'false');
126+ }
127+ });
128+
129+ // Close mobile menu on escape key
130+ document.addEventListener('keydown', (event) => {
131+ if (event.key === 'Escape' && !mobileMenu?.classList.contains('hidden')) {
132+ mobileMenu?.classList.add('hidden');
133+ mobileMenuButton?.setAttribute('aria-expanded', 'false');
134+ mobileMenuButton?.focus();
135+ }
136+ });
111137 });
112138</script >
113139
@@ -142,27 +168,27 @@ const resources = [
142168</script >
143169
144170<!-- Sticky Work in Progress Banner -->
145- <div class =" sticky top-0 bg-gradient-to-r from-orange-500 to-red-500 text-white py-6 px-6 text-center font-medium z-50 shadow-lg" >
171+ <div class =" sticky top-0 bg-gradient-to-r from-orange-500 to-red-500 text-white py-6 px-6 text-center font-medium z-50 shadow-lg" role = " alert " aria-live = " polite " >
146172 <div class =" max-w-6xl mx-auto flex items-center justify-center gap-2 text-sm md:text-base" >
147- <span class =" text-lg" >⚠️</span >
173+ <span class =" text-lg" aria-hidden = " true " >⚠️</span >
148174 <span class =" font-semibold text-lg" >PREVIEW SITE:</span >
149175 <span class =" text-lg" >This is a work-in-progress with placeholder data. Nothing is functional yet!</span >
150- <span class =" text-lg" >⚠️</span >
176+ <span class =" text-lg" aria-hidden = " true " >⚠️</span >
151177 </div >
152178</div >
153179
154180<!-- Main Content -->
155- <main id =" main-content" >
181+ <main id =" main-content" role = " main " >
156182<!-- Hero Section -->
157- <section class =" min-h-[80vh] flex flex-col justify-center items-center text-center px-4 relative overflow-hidden" >
158- <div class =" absolute inset-0 bg-gradient-to-br from-purple-500/20 to-pink-500/20 z-0" ></div >
183+ <section class =" min-h-[80vh] flex flex-col justify-center items-center text-center px-4 relative overflow-hidden" aria-labelledby = " hero-heading " >
184+ <div class =" absolute inset-0 bg-gradient-to-br from-purple-500/20 to-pink-500/20 z-0" aria-hidden = " true " ></div >
159185 <!-- Simple animated element -->
160- <div class =" absolute inset-0 z-0" >
186+ <div class =" absolute inset-0 z-0" aria-hidden = " true " >
161187 <div class =" animate-float absolute top-1/4 left-1/4 w-64 h-64 rounded-full bg-blue-400/10 blur-3xl" ></div >
162188 <div class =" animate-float-delayed absolute bottom-1/4 right-1/3 w-96 h-96 rounded-full bg-purple-500/10 blur-3xl" ></div >
163189 </div >
164190 <div class =" relative z-10 max-w-4xl mx-auto" >
165- <h1 class =" text-5xl md:text-7xl font-bold mb-6 bg-gradient-to-r from-purple-700 to-pink-700 bg-clip-text text-transparent" >
191+ <h1 id = " hero-heading " class =" text-5xl md:text-7xl font-bold mb-6 bg-gradient-to-r from-purple-700 to-pink-700 bg-clip-text text-transparent" >
166192 Open Source Communities
167193 </h1 >
168194 <p class =" text-xl md:text-2xl text-gray-900 dark:text-black mb-8 max-w-2xl mx-auto" >
@@ -237,6 +263,8 @@ const resources = [
237263 <a
238264 href =" https://airtable.com/embed/YOUR_FORM_ID"
239265 target =" _blank"
266+ rel =" noopener noreferrer"
267+ aria-label =" Submit a shoutout (opens in new window)"
240268 class =" px-6 py-3 rounded-lg bg-gradient-to-r from-purple-700 to-pink-700 text-white font-medium hover:opacity-90 transition-all"
241269 >
242270 Submit a Shoutout
@@ -259,7 +287,7 @@ const resources = [
259287 <p class = " text-sm text-gray-700 dark:text-gray-300" >to { shoutout .to } </p >
260288 </div >
261289 { shoutout .githubLink && (
262- <a href = { shoutout .githubLink } target = " _blank" class = " text-purple-700 hover:text-purple-900 dark:text-purple-300 dark:hover:text-purple-200" >
290+ <a href = { shoutout .githubLink } target = " _blank" rel = " noopener noreferrer " aria-label = { ` View ${ shoutout . to } on GitHub ` } class = " text-purple-700 hover:text-purple-900 dark:text-purple-300 dark:hover:text-purple-200" >
263291 <span class = " sr-only" >GitHub</span >
264292 <svg class = " w-5 h-5" fill = " currentColor" viewBox = " 0 0 24 24" aria-hidden = " true" >
265293 <path fill-rule = " evenodd" d = " M12 2C6.477 2 2 6.484 2 12.017c0 4.425 2.865 8.18 6.839 9.504.5.092.682-.217.682-.483 0-.237-.008-.868-.013-1.703-2.782.605-3.369-1.343-3.369-1.343-.454-1.158-1.11-1.466-1.11-1.466-.908-.62.069-.608.069-.608 1.003.07 1.531 1.032 1.531 1.032.892 1.53 2.341 1.088 2.91.832.092-.647.35-1.088.636-1.338-2.22-.253-4.555-1.113-4.555-4.951 0-1.093.39-1.988 1.029-2.688-.103-.253-.446-1.272.098-2.65 0 0 .84-.27 2.75 1.026A9.564 9.564 0 0112 6.844c.85.004 1.705.115 2.504.337 1.909-1.296 2.747-1.027 2.747-1.027.546 1.379.202 2.398.1 2.651.64.7 1.028 1.595 1.028 2.688 0 3.848-2.339 4.695-4.566 4.943.359.309.678.92.678 1.855 0 1.338-.012 2.419-.012 2.747 0 .268.18.58.688.482A10.019 10.019 0 0022 12.017C22 6.484 17.522 2 12 2z" clip-rule = " evenodd" ></path >
@@ -287,6 +315,8 @@ const resources = [
287315 <a
288316 href = { course .url }
289317 target = " _blank"
318+ rel = " noopener noreferrer"
319+ aria-label = { ` ${course .title }: ${course .description } (opens in new window) ` }
290320 class = " bg-white dark:bg-gray-800 rounded-xl shadow-lg overflow-hidden transform transition-all hover:scale-[1.02] group"
291321 >
292322 <div class = " p-8" >
@@ -320,6 +350,8 @@ const resources = [
320350 <a
321351 href = { resource .url }
322352 target = " _blank"
353+ rel = " noopener noreferrer"
354+ aria-label = { ` Visit ${resource .name } (opens in new window) ` }
323355 class = " bg-white dark:bg-gray-800 rounded-lg p-4 flex justify-center items-center border-2 border-transparent hover:border-purple-500 transition-all hover:shadow-md"
324356 >
325357 <span class = " font-medium text-gray-900 dark:text-white" >{ resource .name } </span >
0 commit comments