index.html (15.5 KB)
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <meta name="viewport" content="width=device-width, initial-scale=1.0"> 6 <link rel="icon" href="data:image/svg+xml,<svg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 100 100%22><text y=%22.9em%22 font-size=%2290%22>💡</text></svg>"> 7 <title>PWA Installation Guide</title> 8 <style> 9 html { 10 scroll-behavior: smooth; 11 } 12 13 body { 14 font-family: sans-serif; 15 max-width: 800px; 16 margin: 0 auto; 17 padding: 20px; 18 background-color: #f5f5f5; 19 color: #333; 20 line-height: 1.6; 21 } 22 23 h1 { 24 text-align: center; 25 border-bottom: 3px double #333; 26 padding-bottom: 10px; 27 margin-bottom: 30px; 28 } 29 30 .subtitle { 31 text-align: center; 32 margin-bottom: 30px; 33 } 34 35 h2 { 36 border-bottom: 1px solid #666; 37 padding-bottom: 5px; 38 margin-top: 0; 39 } 40 41 .toc { 42 border: 1px solid #ccc; 43 padding: 15px; 44 margin: 20px 0; 45 background-color: #fff; 46 } 47 48 .toc h3 { 49 margin-top: 0; 50 text-decoration: underline; 51 } 52 53 .toc ul { 54 margin: 10px 0; 55 padding-left: 20px; 56 } 57 58 59 a { 60 color: #0000EE; 61 text-decoration: underline; 62 } 63 64 a:visited { 65 color: #0000EE; 66 } 67 68 a.auto-detected, 69 a.auto-detected:visited { 70 background: linear-gradient(to top right, #64b5f6 0%, #e3f2fd 100%); 71 padding: 12px; 72 font-weight: bold; 73 font-size: 1.15em; 74 display: none; 75 text-align: center; 76 cursor: pointer; 77 border-radius: 12px; 78 text-decoration: none; 79 color: #ffffff; 80 } 81 82 .auto-detected p { 83 padding: 10px; 84 } 85 86 .auto-detected #detected-link { 87 color: #0000EE; 88 text-decoration: underline; 89 } 90 91 .lightbulb { 92 text-shadow: 0px 0px 1px black; 93 font-size: 1.4em; 94 vertical-align: middle; 95 line-height: 1; 96 user-select: none; 97 } 98 99 100 .section { 101 margin: 30px 0; 102 padding: 20px; 103 padding-top: 16px; 104 background-color: #fff; 105 border: 1px solid #ccc; 106 font-size: 1.1em; 107 } 108 109 .section:first-of-type { 110 margin-top: 3px; 111 padding-top: 4px; 112 padding-bottom: 6px; 113 } 114 115 #pwa-emoji { 116 display: none; 117 float: right; 118 margin: 15px; 119 margin-top: 40px; 120 font-size: 70px; 121 line-height: 1; 122 cursor: default; 123 user-select: none; 124 } 125 126 #pwa-infrared { 127 display: none; 128 float: right; 129 margin: 10px; 130 margin-top: 15px; 131 margin-right: 7px; 132 padding-left: 10px; 133 width: 100px; 134 image-rendering: pixelated; 135 cursor: default; 136 user-select: none; 137 } 138 139 .big-letter { 140 font-size: 1.25em; 141 font-variant: normal; 142 line-height: 0; 143 } 144 145 .section:last-of-type { 146 margin-bottom: 0; 147 padding-bottom: 10px; 148 } 149 150 ol { 151 padding-left: 25px; 152 } 153 154 li { 155 margin: 8px 0; 156 } 157 158 .toc li { 159 margin: 5px 0; 160 } 161 162 code { 163 background-color: #f0f0f0; 164 padding: 2px 4px; 165 font-family: "Courier New", monospace; 166 border: 1px solid #ddd; 167 } 168 169 .arrow { 170 display: inline-block; 171 width: 16px; 172 height: 10px; 173 position: relative; 174 margin: 0 6px; 175 vertical-align: middle; 176 } 177 178 .arrow::before { 179 content: ''; 180 position: absolute; 181 top: 50%; 182 left: 0; 183 width: 7.5px; 184 height: 2px; 185 background-color: #333; 186 transform: translateY(-50%); 187 } 188 189 .arrow::after { 190 content: ''; 191 position: absolute; 192 top: 50%; 193 left: 7.5px; 194 width: 0; 195 height: 0; 196 border-top: 5px solid transparent; 197 border-bottom: 5px solid transparent; 198 border-left: 6px solid #333; 199 transform: translateY(-50%); 200 } 201 202 .ui-element { 203 background-color: #f0f0f0; 204 padding: 2px 6px; 205 border-radius: 4px; 206 border: 1px solid #ddd; 207 font-size: 1em; 208 } 209 210 .dots { 211 display: inline-flex; 212 align-items: center; 213 gap: 3px; 214 height: 1em; 215 vertical-align: middle; 216 } 217 218 .dots::before, 219 .dots::after, 220 .dots span::before { 221 content: ''; 222 display: inline-block; 223 width: 3.5px; 224 height: 3.5px; 225 background-color: #333; 226 border-radius: 50%; 227 } 228 229 .dots span { 230 display: inline-flex; 231 } 232 233 .dots span::before { 234 content: ''; 235 } 236 237 .vdots { 238 display: inline-flex; 239 flex-direction: column; 240 align-items: center; 241 gap: 3px; 242 vertical-align: middle; 243 justify-content: center; 244 min-height: 1.8em; 245 box-sizing: content-box; 246 padding: 2px 0; 247 } 248 249 .vdots::before, 250 .vdots::after, 251 .vdots span::before { 252 content: ''; 253 display: block; 254 width: 3.5px; 255 height: 3.5px; 256 background-color: #333; 257 border-radius: 50%; 258 } 259 260 .vdots span { 261 display: flex; 262 } 263 264 .vdots span::before { 265 content: ''; 266 } 267 268 .ui-element:has(.vdots:only-child) { 269 padding-top: 6px; 270 padding-bottom: 6px; 271 } 272 273 .note { 274 font-style: italic; 275 color: #666; 276 border-left: 3px solid #ccc; 277 padding-left: 10px; 278 margin: 15px 0; 279 } 280 281 .note code { 282 font-style: normal; 283 } 284 285 .walkthrough-image { 286 width: 100%; 287 } 288 289 /* Dark mode styles */ 290 @media (prefers-color-scheme: dark) { 291 body { 292 background-color: #141414; 293 color: #ffffff; 294 } 295 296 h1, h2 { 297 color: #ffffff; 298 border-color: #666; 299 } 300 301 .toc, .section { 302 background-color: #1f1f22; 303 border: none; 304 box-shadow: 0 2px 8px rgba(0, 0, 0, 0.7); 305 } 306 307 a.auto-detected, 308 a.auto-detected:visited { 309 background: linear-gradient(to top right, #1a1a3a 0%, #47496b 100%); 310 text-shadow: 0px 1px 1px rgb(0, 0, 0); 311 color: #ffffff; 312 } 313 314 .pwa-icon { 315 filter: drop-shadow(2px 2px 3px rgba(0, 0, 0, 0.5)); 316 } 317 318 a { 319 color: #80d4ff; 320 } 321 322 a:visited { 323 color: #80d4ff; 324 } 325 326 .auto-detected #detected-link { 327 color: #80d4ff; 328 } 329 330 .ui-element { 331 background-color: #3d3d3d; 332 border-color: #555; 333 color: #e0e0e0; 334 } 335 336 .arrow::before { 337 background-color: #fff; 338 } 339 340 .arrow::after { 341 border-left-color: #fff; 342 } 343 344 .dots::before, 345 .dots::after, 346 .dots span::before { 347 background-color: #e0e0e0; 348 } 349 350 .vdots::before, 351 .vdots::after, 352 .vdots span::before { 353 background-color: #e0e0e0; 354 } 355 356 .note { 357 color: #aaa; 358 border-left-color: #666; 359 } 360 361 footer { 362 border-top-color: #666; 363 color: #aaa; 364 } 365 } 366 367 /* Mobile responsive styles */ 368 @media (max-width: 768px) { 369 body { 370 padding: 0px; 371 } 372 373 h1 { 374 margin-top: 10px; 375 margin-bottom: 20px; 376 font-size: 1.8em; 377 } 378 379 .subtitle { 380 display: none; 381 } 382 383 .section, .toc { 384 margin: 20px 0; 385 padding: 15px 10px; 386 border-left: none; 387 border-right: none; 388 } 389 390 .section:first-of-type { 391 margin-top: 0px; 392 padding-top: .1px; 393 padding-bottom: 6px; 394 } 395 396 a.auto-detected { 397 margin: 20px 0; 398 padding: 12px 16px; 399 border: none; 400 border-radius: 0; 401 } 402 } 403 </style> 404 </head> 405 <body> 406 <div id="about-pwas" class="section"> 407 <span id="pwa-emoji" class="pwa-icon">📲</span> 408 <img id="pwa-infrared" src="infrared.png" alt=""> 409 <p><b><span class="big-letter">P</span>rogressive <span class="big-letter">W</span>eb <span class="big-letter">A</span>pps</b> (PWAs) are apps that can be installed directly from supported websites, independent of centralized app stores.</p> 410 411 <p>You can use the instructions below to install PWAs directly from your web browser.</p> 412 </div> 413 414 <a id="auto-detected" class="auto-detected" href="#"> 415 <p><span class="lightbulb">💡</span> Based on your system, you might want to jump directly to: <span id="detected-link"></span></p> 416 </a> 417 418 <div id="safari-ios" class="section"> 419 <h2>Safari (iOS / iPadOS)</h2> 420 <p class="note">Note: on iOS, Safari is the only browser that can install PWAs.</p> 421 <p>Open Safari and navigate to a website that supports PWA installation, then tap:</p> 422 <p>1. The three dots button in the bottom right corner of the screen:</p> 423 <img src="ios_safari/dots.jpeg" class="walkthrough-image"> 424 <p>2. Share:</p> 425 <img src="ios_safari/share.jpeg" class="walkthrough-image"> 426 <p>3. View More:</p> 427 <img src="ios_safari/view_more.jpeg" class="walkthrough-image"> 428 <p>4. Scroll down to reveal the hidden "Add to Home Screen" button:</p> 429 <video src="ios_safari/scroll_down.mov" autoplay muted loop playsinline class="walkthrough-image"></video> 430 <p>5. Tap the "Add to Home Screen" button:</p> 431 <img src="ios_safari/add_to_home_screen.jpeg" class="walkthrough-image"> 432 <p>6. Tap "Add":</p> 433 <img src="ios_safari/add.jpeg" class="walkthrough-image"> 434 <p>The app will be installed directly to your home screen.</p> 435 436 </div> 437 438 <div id="chrome-android" class="section"> 439 <h2>Chrome (Android)</h2> 440 <p>Navigate to a website that supports PWA installation, then tap:</p> 441 <p><span class="ui-element"><span class="vdots"><span></span></span></span> <span class="arrow"></span> Add to Home screen <span class="arrow"></span> Install.</p> 442 </div> 443 444 <div id="firefox-android" class="section"> 445 <h2>Firefox (Android)</h2> 446 <p>Navigate to a website that supports PWA installation, then tap:</p> 447 <p><span class="ui-element"><span class="vdots"><span></span></span></span> <span class="arrow"></span> <span class="ui-element"><span class="dots"><span></span></span> <span style="position: relative; top: 1px;">More</span></span> <span class="arrow"></span> Add to Home screen <span class="arrow"></span> Add to Home screen.</p> 448 </div> 449 450 <div id="safari-mac" class="section"> 451 <h2>Safari (macOS)</h2> 452 <p>Navigate to a website that supports PWA installation, then click File <span class="arrow"></span> Add to Dock.</p> 453 <p>The app will appear in your Dock and Applications folder, and will open in its own window without browser UI.</p> 454 <!-- <p class="note">Note: Requires macOS Sonoma 14+. Older versions may not support this feature.</p> --> 455 </div> 456 457 <div id="edge-windows" class="section"> 458 <h2>Microsoft Edge (Windows)</h2> 459 <p>Navigate to a website that supports PWA installation, then click:</p> 460 <p><span class="ui-element"><span class="dots"><span></span></span></span> <span class="arrow"></span> Apps <span class="arrow"></span> Install this site as an app.</p> 461 <p>The app will appear in the Start menu.</p> 462 </div> 463 464 <div id="chrome-desktop" class="section"> 465 <h2>Chrome (Desktop)</h2> 466 <p>Navigate to a website that supports PWA installation, click the install icon (downward pointing arrow) in the address bar, then click Install.</p> 467 <!-- <p class="note">Alternatively: <code>⋮</code> ➛ Cast, save, and share ➛ Install page as app</p> --> 468 <p>The app will appear in your applications folder or Start menu.</p> 469 </div> 470 471 <div id="firefox-windows" class="section"> 472 <h2>Firefox (Windows)</h2> 473 <p class="note">Note: Requires Firefox version 143 or later (150 or later if Firefox was installed from the Microsoft Store).</p> 474 <p>Navigate to a website that supports PWA installation, then click the <a href="https://support.mozilla.org/en-US/kb/web-apps-firefox-windows#w_install-web-apps" target="_blank">install button</a> in the address bar.</p> 475 <p>The app will appear in your taskbar and Start menu, and will open in its own window.</p> 476 </div> 477 478 <div id="firefox-desktop" class="section"> 479 <h2>Firefox (macOS / Linux)</h2> 480 <p><strong>⚠️ The macOS and Linux versions of Firefox do not support web app installation.</strong><br>Try <a href="#chrome-desktop">Chrome</a> or <a href="#safari-mac">Safari</a> instead.</p> 481 <p>Firefox for Windows added web app support in <a href="https://support.mozilla.org/en-US/kb/web-apps-firefox-windows" target="_blank">version 143</a> (September 2025), but support for macOS and Linux has not yet been announced.</p> 482 </div> 483 484 <div id="try-these-pwas" class="section"> 485 <h2>Progressive Web Apps You Can Install Right Now</h2> 486 487 <h3><a href="https://hunterirving.github.io/web_workshop/" target="_blank">Web Workshop</a></h3> 488 <p class="note">Craft handmade websites in their natural habitat with this in-browser HTML editor. Works great on desktop or mobile.</p> 489 490 <h3><a href="https://hunterirving.com">HunterIrving.com</a></h3> 491 <p class="note">Experience Huntergram, Gobbler, and more in the dedicated hunterirving.com PWA.</p> 492 493 <h3><a href="https://hunterirving.github.io/qr/">QR</a></h3> 494 <p class="note">A no-nonsense QR code generator. Great for sharing links to PWAs you've developed with nearby friends.</p> 495 496 <h3><a href="https://github.com/hunterirving/mixapps">Mixapps</a></h3> 497 <p class="note">Mixapps are like mixtapes or mix CDs, but packaged as PWAs that you can share with friends and install for offline use. Use <a href="https://github.com/hunterirving/mixapps">this link</a> to learn how to create your own mixapps, or <a href="https://hunterirving.com/worn_grooves/">click here</a> to listen to one assembled from public domain recordings.</p> 498 499 <!-- <h3><a href="https://hacksburg.org">Hacksburg.org</a></h3> 500 <p class="note">Stay up to date with goings on at Hacksburg, a makerspace in Blacksburg, VA.</p> --> 501 502 <!-- <h3><a href="https://hunterirving.github.io/klaxon/" target="_blank">Klaxon</a></h3> 503 <p class="note">A chess clock in your pocket, optimized for touchscreen devices and competitive play.</p> 504 505 <h3><a href="https://hunterirving.github.io/hourglass/">Hourglass</a></h3> 506 <p class="note">Speedrun your life with this simple, focused time tracking tool.</p> 507 508 <h3><a href="https://hunterirving.github.io/pop_viz/">Pop Viz</a></h3> 509 <p class="note">What does 100,000 people look like? Don't ask me — install this Progressive Web App.</p> --> 510 511 <!-- <h3><a href="https://hunterirving.github.io/web_workshop/pages/snake/">SNAKE</a></h3> 512 <p class="note">The classic game, reimagined for the modern era.</p> --> 513 </div> 514 515 <script> 516 function detectPlatform() { 517 const userAgent = navigator.userAgent.toLowerCase(); 518 const platform = navigator.platform.toLowerCase(); 519 520 let detectedOS = ''; 521 let detectedBrowser = ''; 522 let targetSection = ''; 523 524 // Detect OS 525 if (userAgent.includes('android')) { 526 detectedOS = 'Android'; 527 } else if (userAgent.includes('iphone') || userAgent.includes('ipad')) { 528 detectedOS = 'iOS'; 529 } else if (platform.includes('win')) { 530 detectedOS = 'Windows'; 531 } else if (platform.includes('mac')) { 532 detectedOS = 'macOS'; 533 } else if (platform.includes('linux')) { 534 detectedOS = 'Linux'; 535 } 536 537 // Detect Browser - iOS only allows PWA installation through Safari 538 if (detectedOS === 'iOS') { 539 // On iOS, all browsers use Safari's WebKit engine 540 // Only Safari can install PWAs, so always direct to Safari section 541 detectedBrowser = 'Safari'; 542 targetSection = 'safari-ios'; 543 } else if (userAgent.includes('firefox')) { 544 detectedBrowser = 'Firefox'; 545 if (detectedOS === 'Android') { 546 targetSection = 'firefox-android'; 547 } else if (detectedOS === 'Windows') { 548 targetSection = 'firefox-windows'; 549 } else { 550 targetSection = 'firefox-desktop'; 551 } 552 } else if (userAgent.includes('safari') && !userAgent.includes('chrome')) { 553 detectedBrowser = 'Safari'; 554 targetSection = 'safari-mac'; 555 } else if (userAgent.includes('edg')) { 556 detectedBrowser = 'Edge'; 557 targetSection = 'edge-windows'; 558 } else if (userAgent.includes('chrome')) { 559 detectedBrowser = 'Chrome'; 560 if (detectedOS === 'Android') { 561 targetSection = 'chrome-android'; 562 } else { 563 targetSection = 'chrome-desktop'; 564 } 565 } 566 567 const isPhone = (detectedOS === 'Android' || detectedOS === 'iOS'); 568 if (isPhone) { 569 document.getElementById('pwa-emoji').style.display = 'inline'; 570 } else { 571 document.getElementById('pwa-infrared').style.display = 'block'; 572 } 573 574 if (targetSection) { 575 const autoDetectedDiv = document.getElementById('auto-detected'); 576 const detectedLink = document.getElementById('detected-link'); 577 578 autoDetectedDiv.href = `#${targetSection}`; 579 detectedLink.textContent = `${detectedBrowser} on ${detectedOS}`; 580 autoDetectedDiv.style.display = 'block'; 581 } 582 } 583 584 // Run detection when page loads 585 window.addEventListener('load', detectPlatform); 586 </script> 587 </body> 588 </html>