1<!DOCTYPE html> 2<html lang="en"> 3<head> 4 <meta charset="utf-8"> 5 <title>JavaScript Weekly</title> 6 <meta name="viewport" content="width=device-width, initial-scale=1"> 7 <meta name="description" content="A newsletter of JavaScript articles, news and cool projects."> 8 <meta property="og:title" content="JavaScript Weekly"> 9 <meta property="og:description" content="A newsletter of JavaScript articles, news and cool projects."> 10 <meta property="og:type" content="website"> 11 <meta name="twitter:card" content="summary"> 12 <meta name="twitter:title" content="JavaScript Weekly"> 13 <meta name="twitter:description" content="A newsletter of JavaScript articles, news and cool projects."> 14 <link rel="alternate" type="application/rss+xml" title="JavaScript Weekly" href="/rss/"> 15 <link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600&display=swap" rel="stylesheet"> 16 17 <style> 18 *, *::before, *::after { box-sizing: border-box; } 19 body { 20 margin: 0; padding: 0; 21 font-family: Inter, -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif; 22 font-size: 16px; color: #222; line-height: 1.5; 23 background: #fff; 24 } 25 a { color: #f7df1e; text-decoration: none; } 26 a:hover { text-decoration: underline; } 27 28 main { 29 display: flex; 30 min-height: 100vh; 31 } 32 @media (max-width: 900px) { 33 main { flex-direction: column; } 34 } 35 36 37 .info { 38 flex: 3; 39 display: flex; 40 flex-direction: column; 41 justify-content: center; 42 padding: 6vh 5vw; 43 max-width: 640px; 44 } 45 @media (max-width: 900px) { 46 .info { 47 max-width: 100%; 48 padding: 4vh 6vw; 49 text-align: center; 50 } 51 footer .publisher a { margin-left: auto; margin-right: auto; } 52 } 53 .info h1 { 54 font-size: 2.6em; 55 font-weight: 600; 56 color: #323330; 57 background: #f7df1e; 58 padding: 0.15em 0.3em; 59 align-self: flex-start; 60 width: fit-content; 61 margin: 0 0 0.35em; 62 line-height: 1.05; 63 } 64 @media (max-width: 900px) { 65 .info h1 { align-self: center; } 66 } 67 .info .subtitle { 68 font-size: 1.25em; 69 font-weight: 300; 70 color: #444; 71 margin: 0 0 0.4em; 72 line-height: 1.4; 73 text-wrap: pretty; 74 } 75 .info nav { 76 display: flex; 77 gap: 1.5em; 78 flex-wrap: wrap; 79 margin-bottom: 2em; 80 } 81 @media (max-width: 900px) { 82 .info nav { justify-content: center; } 83 } 84 .info nav a { 85 font-weight: 500; 86 font-size: 1.05em; 87 padding: 0.4em 1em; 88 color: #323330; 89 border: 2px solid #f7df1e; 90 border-radius: 6px; 91 transition: background 0.15s, color 0.15s; 92 } 93 .info nav a:hover { 94 background: #f7df1e; 95 color: #323330; 96 text-decoration: none; 97 } 98 .info nav a.primary { 99 background: #f7df1e; 100 color: #323330; 101 } 102 .info nav a.primary:hover { 103 filter: brightness(0.8); 104 } 105 .rss-link { 106 display: inline-flex; 107 align-items: center; 108 gap: 0.3em; 109 color: #f58113 !important; 110 border-color: #f58113 !important; 111 } 112 .rss-link:hover { background: #f58113 !important; color: #fff !important; } 113 .rss-link:hover svg { fill: #fff; } 114 .rss-link svg { width: 14px; height: 14px; fill: #f58113; } 115 116 .archived-note { 117 margin-bottom: 1.5em; 118 max-width: 420px; 119 padding: 0.75em 1em; 120 border-left: 3px solid #f7df1e; 121 background: rgba(0, 0, 0, 0.04); 122 font-size: 0.95em; 123 line-height: 1.4; 124 } 125 @media (max-width: 900px) { 126 .archived-note { margin-left: auto; margin-right: auto; } 127 } 128 129 .subscribe-form { 130 margin-bottom: 1.5em; 131 max-width: 420px; 132 } 133 @media (max-width: 900px) { 134 .subscribe-form { margin-left: auto; margin-right: auto; } 135 } 136 .subscribe-form .input-row { 137 display: flex; 138 gap: 0; 139 } 140 .subscribe-form input[type="email"] { 141 flex: 1; 142 padding: 0.7em 1em; 143 font-size: 1em; 144 border: 2px solid #ddd; 145 border-right: none; 146 border-radius: 6px 0 0 6px; 147 outline: none; 148 font-family: inherit; 149 transition: border-color 0.15s; 150 } 151 .subscribe-form input[type="email"]:focus { 152 border-color: #f7df1e;
153 } 154 .subscribe-form button { 155 padding: 0.7em 1.4em; 156 font-size: 1em; 157 font-weight: 600; 158 font-family: inherit; 159 background: #f7df1e; 160 color: #323330; 161 border: 2px solid #f7df1e; 162 border-radius: 0 6px 6px 0; 163 cursor: pointer; 164 transition: filter 0.15s; 165 white-space: nowrap; 166 } 167 .subscribe-form button:hover { 168 filter: brightness(0.85); 169 } 170 .subscribe-form .cf-turnstile { 171 margin-top: 8px; 172 } 173 174 .meta { 175 font-size: 0.85em; 176 color: #777; 177 margin-bottom: 1.8em; 178 } 179 .meta a { color: #666; border-bottom: 1px dashed #bbb; } 180 .meta a:hover { text-decoration: none; } 181 182 footer { 183 margin-top: 2em; 184 font-size: 0.8em; 185 color: #999; 186 line-height: 1.5; 187 } 188 footer a { color: inherit; border-bottom: 1px dashed #ccc; } 189 footer a:hover { text-decoration: none; } 190 footer .publisher { margin-top: 2.5em; } 191 footer .publisher a { border-bottom: none; display: block; width: fit-content; margin-top: 0.5em; background: #f7df1e; padding: 8px 14px; border-radius: 3px; } 192 footer .publisher img { display: block; height: auto; width: auto; max-width: 160px; filter: brightness(0); } 193 footer .trademark { margin-top: 2em; font-size: 0.85em; color: #aaa; line-height: 1.5; } 194 195 196 .showcase { 197 flex: 2; 198 background: linear-gradient(135deg, #f7df1e 0%, #f9e85c 100%); 199 display: flex; 200 align-items: center; 201 justify-content: center; 202 padding: 5vh 3vw; 203 min-height: 100vh; 204 } 205 @media (max-width: 900px) { 206 .showcase { 207 min-height: auto; 208 padding: 5vh 0; 209 } 210 } 211 212 213 .phone { 214 position: relative; 215 width: 390px; 216 height: 780px; 217 background: #1a1a1a; 218 border-radius: 54px; 219 padding: 14px; 220 box-shadow: 221 0 0 0 2px #333, 222 0 0 0 4px #1a1a1a, 223 8px 12px 36px rgba(0,0,0,0.45); 224 } 225 @media (max-width: 900px) { 226 .phone { 227 width: 320px; 228 height: 640px; 229 border-radius: 44px; 230 padding: 12px; 231 } 232 } 233 .phone-screen { 234 width: 100%; 235 height: 100%; 236 border-radius: 42px; 237 overflow: hidden; 238 background: #fff; 239 position: relative; 240 } 241 @media (max-width: 900px) { 242 .phone-screen { border-radius: 34px; } 243 } 244 245 .phone-island { 246 position: absolute; 247 top: 14px; 248 left: 50%; 249 transform: translateX(-50%); 250 width: 120px; 251 height: 34px; 252 background: #1a1a1a; 253 border-radius: 20px; 254 z-index: 2; 255 } 256 @media (max-width: 900px) { 257 .phone-island { 258 width: 100px; 259 height: 28px; 260 top: 12px; 261 border-radius: 16px; 262 } 263 } 264 .phone-screen iframe { 265 width: 125%; 266 height: 125%; 267 border: none; 268 background: #fff; 269 transform: scale(0.8); 270 transform-origin: top left; 271 margin-top: 60px; 272 } 273 .phone-overlay { 274 position: absolute; 275 top: 0; 276 left: 0; 277 right: 0; 278 bottom: 0; 279 z-index: 3; 280 cursor: pointer; 281 } 282 283 .phone::before { 284 content: ''; 285 position: absolute; 286 right: -3px; 287 top: 180px; 288 width: 4px; 289 height: 80px; 290 background: #333; 291 border-radius: 0 2px 2px 0; 292 } 293 .phone::after { 294 content: ''; 295 position: absolute; 296 left: -3px; 297 top: 160px; 298 width: 4px; 299 height: 40px; 300 background: #333; 301 border-radius: 2px 0 0 2px; 302 box-shadow: 0 56px 0 #333, 0 96px 0 #333; 303 } 304 </style> 305</head> 306<body> 307 <main> 308 <div class="info"> 309 <h1>JavaScript Weekly</h1> 310 <p class="subtitle">A newsletter of JavaScript articles, news and cool projects.</p> 311 312 <p class="meta"> 313 <a href="/issues">803 issues</a> and counting (since November 2010) 314 </p> 315 316 317 318 <form class="subscribe-form" method="GET" action="https://subscribe.cooperpress.email/start"> 319 <div class="input-row"> 320 <input type="hidden" name="pub" value="jw"> 321 <input type="email" name="email" placeholder="[email protected]" required autofocus> 322 <button type="submit">Subscribe</button> 323 </div> 324 </form> 325 326 327 <nav> 328 <a href="/latest" class="primary">Latest Issue</a> 329 <a href="/issues">All Issues</a> 330 <a href="/rss/" class="rss-link"><svg viewBox="0 0 448 512"><path d="M64 32C28.7 32 0 60.7 0 96v320c0 35.3 28.7 64 64 64h320c35.3 0 64-28.7 64-64V96c0-35.3-28.7-64-64-64H64zm48 368a48 48 0 1 1 0-96 48 48 0 0 1 0 96zm142.5 0c-3.3 0-6-2.5-6.3-5.8C240.4 300 163.1 224 69.8 216.3c-3.2-.3-5.8-3-5.8-6.3v-36.7c0-3.7 3.2-6.6 6.9-6.3 120.1 8.2 216.4 104.5 224.6 224.6.3 3.7-2.6 6.9-6.3 6.9h-36.7zm96 0c-3.3 0-6-2.4-6.2-5.7C335.8 246.2 217.8 128.2 69.7 119.7c-3.3-.2-5.7-2.9-5.7-6.2V76.8c0-3.7 3.1-6.7 6.8-6.5C239.5 78.8 385.2 224.5 393.7 393.2c.2 3.7-2.8 6.8-6.5 6.8h-36.7z"/></svg>RSS</a> 331 </nav> 332 333 <footer> 334 Our <a href="https://cooperpress.com/legal/privacy/">privacy</a>, <a href="https://cooperpress.com/legal/spam/">anti-spam</a>, 335 and <a href="https://cooperpress.com/legal/gdpr/">GDPR</a> policies. We take these things seriously. 336 <div class="publisher"> 337 Published by 338 <a href="https://cooperpress.com/"><img src="/logo.png" alt="Cooperpress"></a> 339 </div> 340 <div class="trademark">JavaScript is a trademark of Oracle Corporation in the US. We are not endorsed by or affiliated with Oracle.</div> 341 </footer> 342 </div> 343 344 <div class="showcase"> 345 <div class="phone"> 346 <div class="phone-screen"> 347 <div class="phone-island"></div> 348 <a href="/latest" class="phone-overlay"></a> 349 <iframe src="/issues/803?layout=bare" loading="lazy"></iframe> 350 </div> 351 </div> 352 </div> 353 </main> 354
354<script> 355 (function() { 356 var overlay = document.querySelector('.phone-overlay'); 357 var iframe = document.querySelector('.phone-screen iframe'); 358 if (!overlay || !iframe) return; 359 360 overlay.addEventListener('wheel', function(e) { 361 e.preventDefault(); 362 iframe.contentWindow.scrollBy(0, e.deltaY); 363 }, {passive: false}); 364 365 var touchY = null; 366 overlay.addEventListener('touchstart', function(e) { 367 if (e.touches.length === 1) touchY = e.touches[0].clientY; 368 }); 369 overlay.addEventListener('touchmove', function(e) { 370 if (touchY === null || e.touches.length !== 1) return; 371 e.preventDefault(); 372 var dy = touchY - e.touches[0].clientY; 373 touchY = e.touches[0].clientY; 374 iframe.contentWindow.scrollBy(0, dy); 375 }, {passive: false}); 376 overlay.addEventListener('touchend', function() { touchY = null; }); 377 })(); 378 </script>
378 379</body> 380</html>
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.