index.html (7301B)
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"> 6 <title>onlyn00bs badge · photos</title> 7 <meta name="description" content="OnlyN00bs badges in the wild at DEF CON 34."> 8 <meta name="color-scheme" content="dark light"> 9 <meta property="og:title" content="onlyn00bs badge · photos"> 10 <meta property="og:description" content="OnlyN00bs badges in the wild at DEF CON 34."> 11 <meta property="og:type" content="website"> 12 <meta property="og:site_name" content="virtualshack"> 13 <meta property="og:url" content="https://badge.virtualshack.io/gallery/"> 14 <meta property="og:image" content="https://badge.virtualshack.io/assets/img/og-badge.jpg"> 15 <meta property="og:image:width" content="1200"> 16 <meta property="og:image:height" content="630"> 17 <meta name="twitter:card" content="summary_large_image"> 18 <meta name="twitter:image" content="https://badge.virtualshack.io/assets/img/og-badge.jpg"> 19 <!-- ?v= must match CACHE in ../sw.js — same rule as index.html --> 20 <link rel="stylesheet" href="../assets/badge.css?v=39"> 21 <link rel="icon" type="image/svg+xml" href="../assets/icon.svg"> 22 <link rel="manifest" href="../manifest.webmanifest"> 23 <meta name="theme-color" content="#212121"> 24 </head> 25 <body> 26 <main class="wrapper" id="top"> 27 28 <h1>// photos</h1> 29 <p class="tagline">onlyn00bs badges in the wild · def con 34</p> 30 <p class="small muted"><a href="https://virtualshack.io/">virtualshack.io</a> / 31 <a href="../">badge</a> / photos</p> 32 33 <!-- The grid between these markers is generated from the gallery manifest. --> 34 <!-- gallery:begin --> 35 <div class="gallery"> 36 <figure> 37 <a href="../assets/gallery/dc34-01.jpg" data-w="1600" data-h="1200"><img src="../assets/gallery/thumbs/dc34-01.jpg" alt="A green OnlyN00bs badge on the finder screen beside the official DEF CON 34 badge and a challenge coin, with a purple foot charm on the case" 38 width="720" height="540" loading="lazy" decoding="async"></a> 39 </figure> 40 <figure> 41 <a href="../assets/gallery/dc34-02.jpg" data-w="1234" data-h="929"><img src="../assets/gallery/thumbs/dc34-02.jpg" alt="A hand holding a purple OnlyN00bs badge on the finder screen, with a green badge on a lanyard behind it" 42 width="720" height="542" loading="lazy" decoding="async"></a> 43 </figure> 44 <figure> 45 <a href="../assets/gallery/dc34-03.jpg" data-w="929" data-h="1238"><img src="../assets/gallery/thumbs/dc34-03.jpg" alt="A lavender badge with foot and gummy-bear charms, its finder screen listing five nearby badges" 46 width="540" height="720" loading="lazy" decoding="async"></a> 47 </figure> 48 <figure> 49 <a href="../assets/gallery/dc34-04.jpg" data-w="1234" data-h="929"><img src="../assets/gallery/thumbs/dc34-04.jpg" alt="A purple and a blue OnlyN00bs badge side by side on a table, both on the home screen, decorated with charms" 50 width="720" height="542" loading="lazy" decoding="async"></a> 51 </figure> 52 <figure> 53 <a href="../assets/gallery/dc34-05.jpg" data-w="929" data-h="1238"><img src="../assets/gallery/thumbs/dc34-05.jpg" alt="A pastel badge on the home screen in front of a table covered in more badges and OnlyN00bs lanyards" 54 width="540" height="720" loading="lazy" decoding="async"></a> 55 </figure> 56 <figure> 57 <a href="../assets/gallery/dc34-06.jpg" data-w="929" data-h="1238"><img src="../assets/gallery/thumbs/dc34-06.jpg" alt="A small lavender badge on a table, its screen showing the avatar's shower-time reminder" 58 width="540" height="720" loading="lazy" decoding="async"></a> 59 </figure> 60 <figure> 61 <a href="../assets/gallery/dc34-08.jpg" data-w="1234" data-h="929"><img src="../assets/gallery/thumbs/dc34-08.jpg" alt="Two hands holding a purple badge on the finder screen, with green badges behind it" 62 width="720" height="542" loading="lazy" decoding="async"></a> 63 </figure> 64 <figure> 65 <a href="../assets/gallery/dc34-09.jpg" data-w="1600" data-h="1200"><img src="../assets/gallery/thumbs/dc34-09.jpg" alt="A mint-green badge on the home screen with pink foot, red dinosaur and cat charms, on its lanyard" 66 width="720" height="540" loading="lazy" decoding="async"></a> 67 </figure> 68 </div> 69 <!-- gallery:end --> 70 71 <p class="small muted">Photos CC BY-SA 4.0 · virtualtack. 72 <a href="../#source">The badge is open source too.</a></p> 73 74 <p style="text-align:center"><a class="totop" href="../">← back to the badge</a></p> 75 76 </main> 77 78 <dialog class="lightbox" id="lightbox" aria-label="photo viewer"> 79 <figure><img alt=""><figcaption></figcaption></figure> 80 <button class="close" type="button" aria-label="close">✕</button> 81 <button class="prev" type="button" aria-label="previous photo">←</button> 82 <button class="next" type="button" aria-label="next photo">→</button> 83 <span class="count"></span> 84 </dialog> 85 86 <script> 87 // Lightbox. Progressive enhancement: every tile is a real link to the full image, so with 88 // no JS (or no <dialog>) a click just opens the photo. Modifier-clicks are left alone so 89 // "open in new tab" still works. 90 (() => { 91 const links = [...document.querySelectorAll(".gallery a")]; 92 const dlg = document.getElementById("lightbox"); 93 if (!links.length || !dlg.showModal) return; 94 const img = dlg.querySelector("img"); 95 const cap = dlg.querySelector("figcaption"); 96 const count = dlg.querySelector(".count"); 97 let i = 0; 98 99 const show = (n) => { 100 i = (n + links.length) % links.length; 101 const a = links[i]; 102 const fc = a.closest("figure").querySelector("figcaption"); 103 img.width = a.dataset.w; // reserve the shape before the bytes arrive 104 img.height = a.dataset.h; 105 img.src = a.href; 106 img.alt = a.querySelector("img").alt; 107 cap.textContent = fc ? fc.textContent : ""; 108 count.textContent = `${i + 1} / ${links.length}`; 109 new Image().src = links[(i + 1) % links.length].href; // warm the next one 110 }; 111 112 links.forEach((a, n) => a.addEventListener("click", (e) => { 113 if (e.metaKey || e.ctrlKey || e.shiftKey || e.altKey || e.button) return; 114 e.preventDefault(); 115 show(n); 116 dlg.showModal(); 117 })); 118 dlg.querySelector(".prev").addEventListener("click", () => show(i - 1)); 119 dlg.querySelector(".next").addEventListener("click", () => show(i + 1)); 120 dlg.querySelector(".close").addEventListener("click", () => dlg.close()); 121 dlg.addEventListener("click", (e) => { if (e.target === dlg) dlg.close(); }); // empty space 122 dlg.addEventListener("keydown", (e) => { 123 if (e.key === "ArrowLeft") show(i - 1); 124 if (e.key === "ArrowRight") show(i + 1); 125 }); 126 let x0 = null; 127 dlg.addEventListener("touchstart", (e) => { x0 = e.touches[0].clientX; }, { passive: true }); 128 dlg.addEventListener("touchend", (e) => { 129 if (x0 === null) return; 130 const dx = e.changedTouches[0].clientX - x0; 131 x0 = null; 132 if (Math.abs(dx) > 50) show(i + (dx < 0 ? 1 : -1)); 133 }); 134 dlg.addEventListener("close", () => img.removeAttribute("src")); 135 })(); 136 137 if ("serviceWorker" in navigator) { 138 addEventListener("load", () => navigator.serviceWorker.register("../sw.js").catch(() => {})); 139 } 140 </script> 141 </body> 142 </html>