My agentic slop goes here. Not intended for anyone else!
at main 6.7 kB view raw
1/** 2 * Arod Menu Bar Component 3 * A responsive navigation menu with search and theme toggle 4 * 5 * Usage: 6 * <arod-menu> 7 * <menu-item href="/papers">Papers</menu-item> 8 * <menu-item href="/notes">Notes</menu-item> 9 * <menu-item href="/about">About</menu-item> 10 * </arod-menu> 11 */ 12 13// ============================================ 14// AROD-MENU - Main Menu Bar Component 15// ============================================ 16class ArodMenu extends HTMLElement { 17 constructor() { 18 super(); 19 this.attachShadow({ mode: 'open' }); 20 } 21 22 connectedCallback() { 23 this.render(); 24 this.setupMobileToggle(); 25 } 26 27 render() { 28 this.shadowRoot.innerHTML = ` 29 <style> 30 :host { 31 --menu-height: 3.5rem; 32 --menu-bg: #fffffc; 33 --menu-border: #666; 34 --menu-text: #1a1a1a; 35 --menu-hover: #090c8d; 36 --menu-mobile-breakpoint: 768px; 37 38 display: block; 39 position: sticky; 40 top: 0; 41 z-index: 1000; 42 background: var(--menu-bg); 43 border-bottom: 1px solid var(--menu-border); 44 height: var(--menu-height); 45 } 46 47 @media (prefers-color-scheme: dark) { 48 :host { 49 --menu-bg: #1a1a1a; 50 --menu-border: #444; 51 --menu-text: #e0e0e0; 52 --menu-hover: #4db8ff; 53 } 54 } 55 56 :host([theme="light"]) { 57 --menu-bg: #fffffc; 58 --menu-border: #666; 59 --menu-text: #1a1a1a; 60 --menu-hover: #090c8d; 61 } 62 63 :host([theme="dark"]) { 64 --menu-bg: #1a1a1a; 65 --menu-border: #444; 66 --menu-text: #e0e0e0; 67 --menu-hover: #4db8ff; 68 } 69 70 .menu-container { 71 display: flex; 72 align-items: center; 73 justify-content: space-between; 74 height: 100%; 75 max-width: 1200px; 76 margin: 0 auto; 77 padding: 0 1rem; 78 } 79 80 .menu-left { 81 display: flex; 82 align-items: center; 83 gap: 2rem; 84 } 85 86 .menu-logo { 87 font-size: 1.2rem; 88 font-weight: 700; 89 color: var(--menu-text); 90 text-decoration: none; 91 } 92 93 .menu-items { 94 display: flex; 95 align-items: center; 96 gap: 1.5rem; 97 list-style: none; 98 } 99 100 .menu-right { 101 display: flex; 102 align-items: center; 103 gap: 1rem; 104 } 105 106 .mobile-toggle { 107 display: none; 108 background: none; 109 border: none; 110 color: var(--menu-text); 111 cursor: pointer; 112 padding: 0.5rem; 113 } 114 115 .mobile-toggle svg { 116 width: 24px; 117 height: 24px; 118 } 119 120 @media (max-width: 768px) { 121 .menu-items { 122 display: none; 123 position: absolute; 124 top: var(--menu-height); 125 left: 0; 126 right: 0; 127 background: var(--menu-bg); 128 border-bottom: 1px solid var(--menu-border); 129 flex-direction: column; 130 padding: 1rem; 131 gap: 0.5rem; 132 align-items: stretch; 133 } 134 135 .menu-items.mobile-open { 136 display: flex; 137 } 138 139 .mobile-toggle { 140 display: block; 141 } 142 143 .menu-right { 144 margin-left: auto; 145 } 146 } 147 148 ::slotted(menu-item) { 149 display: inline-flex !important; 150 align-items: center; 151 cursor: pointer; 152 height: 100%; 153 } 154 </style> 155 156 <nav class="menu-container"> 157 <div class="menu-left"> 158 <a href="/" class="menu-logo">AROD</a> 159 <button class="mobile-toggle" aria-label="Menu"> 160 <svg fill="none" stroke="currentColor" viewBox="0 0 24 24"> 161 <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h16"></path> 162 </svg> 163 </button> 164 <div class="menu-items"> 165 <slot></slot> 166 </div> 167 </div> 168 <div class="menu-right"> 169 <slot name="actions"></slot> 170 </div> 171 </nav> 172 `; 173 } 174 175 setupMobileToggle() { 176 const toggle = this.shadowRoot.querySelector('.mobile-toggle'); 177 const items = this.shadowRoot.querySelector('.menu-items'); 178 179 toggle?.addEventListener('click', () => { 180 items?.classList.toggle('mobile-open'); 181 }); 182 183 // Close menu when clicking outside 184 document.addEventListener('click', (e) => { 185 if (!this.contains(e.target)) { 186 items?.classList.remove('mobile-open'); 187 } 188 }); 189 } 190} 191 192// ============================================ 193// MENU-ITEM - Individual Menu Item Component 194// ============================================ 195class MenuItem extends HTMLElement { 196 constructor() { 197 super(); 198 // Store the original text content 199 this._label = ''; 200 } 201 202 connectedCallback() { 203 // Capture the text content before any rendering 204 this._label = this.textContent.trim(); 205 this.render(); 206 this.setupNavigation(); 207 } 208 209 render() { 210 const href = this.getAttribute('href') || '#'; 211 212 // Style the menu-item element itself 213 this.style.cssText = ` 214 display: inline-flex; 215 align-items: center; 216 height: 100%; 217 `; 218 219 // Create link element 220 const link = document.createElement('a'); 221 link.href = href; 222 link.textContent = this._label; 223 link.style.cssText = ` 224 color: var(--menu-text, #222); 225 text-decoration: none; 226 padding: 0.5rem 0.75rem; 227 border-radius: 4px; 228 transition: all 0.2s ease; 229 display: inline-block; 230 white-space: nowrap; 231 `; 232 233 // Clear and add link 234 this.innerHTML = ''; 235 this.appendChild(link); 236 237 // Add hover effects directly to the link 238 link.addEventListener('mouseenter', () => { 239 link.style.color = 'var(--menu-hover, #0066cc)'; 240 link.style.background = 'rgba(0, 102, 204, 0.1)'; 241 }); 242 243 link.addEventListener('mouseleave', () => { 244 if (!this.hasAttribute('active')) { 245 link.style.color = 'var(--menu-text, #222)'; 246 link.style.background = 'transparent'; 247 } 248 }); 249 } 250 251 setupNavigation() { 252 // Mark active based on current path 253 const href = this.getAttribute('href'); 254 const link = this.querySelector('a'); 255 if (href && window.location.pathname === href) { 256 this.setAttribute('active', ''); 257 if (link) { 258 link.style.color = 'var(--menu-hover, #0066cc)'; 259 link.style.fontWeight = '500'; 260 } 261 } 262 } 263} 264 265// Register components 266customElements.define('arod-menu', ArodMenu); 267customElements.define('menu-item', MenuItem);