template.shtml (21472B)
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head id="head"> 4 <meta charset="utf-8"> 5 <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> 6 <title :text="$site.title"></title> 7 <meta name="author" content="$page.author"> 8 <meta name="description" content="$page.description"> 9 <meta name="generator" content="Zine"> 10 <!-- Generate your favicons --> 11 <link rel="apple-touch-icon" sizes="180x180" href="$site.asset('icons/apple-touch-icon.png').link()"> 12 <link rel="icon" type="image/png" sizes="32x32" href="$site.asset('icons/favicon-32x32.png').link()"> 13 <link rel="icon" type="image/png" sizes="16x16" href="$site.asset('icons/favicon-16x16.png').link()"> 14 <link rel="manifest" href="$site.asset('icons/site.webmanifest').link()"> 15 <link rel="mask-icon" href="$site.asset('icons/safari-pinned-tab.svg').link()" color="#ffb6c1"> 16 <link rel="shortcut icon" href="$site.asset('icons/favicon.ico').link()"> 17 <meta name="msapplication-TileColor" content="#603cba"> 18 <meta name="msapplication-config" content="$site.asset('icons/browserconfig.xml').link()"> 19 <meta name="theme-color" content="#fdf9ee"> 20 <!-- preloading seems broken on firefox, sticking with the convoluted .font-loading solution --> 21 <!-- <link rel="preload" href="/fonts/AtkinsonHyperlegibleNextVF-Variable.woff2" as="font" type="font/woff2" crossorigin="anonymous"> --> 22 <!-- <link type="text/css" rel="stylesheet" href="$site.asset('style.css').link()"> --> 23 <!-- <link type="text/css" rel="stylesheet" href="$site.asset('highlight.css').link()"> --> 24 <!-- mathtex --> 25 <!-- <link type="text/css" rel="stylesheet" href="$site.asset('Temml-Local.css').link()"> --> 26 <!-- <script defer src="$site.asset('temml.min.js').link()"></script> --> 27 <!-- <script defer src="$site.asset('render-mathtex.js').link()"></script> --> 28 <!-- /mathtex --> 29 <style> 30 /* Modern CSS Reset https://www.joshwcomeau.com/css/custom-css-reset/ */ 31 *, *::before, *::after { box-sizing: border-box; } 32 * { margin: 0; } 33 @media (prefers-reduced-motion: no-preference) { html { interpolate-size: allow-keywords; } } 34 body { line-height: 1.5; -webkit-font-smoothing: antialiased; } 35 img, picture, video, canvas, svg { display: block; max-width: 100%; } 36 input, button, textarea, select { font: inherit; } 37 p, h1, h2, h3, h4, h5, h6 { overflow-wrap: break-word; hyphens: auto; } 38 p { text-wrap: pretty; } 39 h1, h2, h3, h4, h5, h6 { text-wrap: balance; } 40 #content { isolation: isolate; } 41 42 /* My own resets */ 43 h1, h2, h3, h4, h5, h6 { line-height: 1; } 44 textarea { display: block; max-width: 100%; } 45 pre { overflow-x: scroll; } 46 47 /* Remove pointer events from structural elements cause it messes up hitboxes */ 48 div, main, header, footer, section, article, [structural] { 49 pointer-events: none; 50 } 51 /* pointer-events is inherited, so we have to reset it to auto for children */ 52 :is(div, main, header, footer, section, article, [structural]) 53 > *:not(div, main, header, footer, section, article, [structural]) { 54 pointer-events: auto; 55 } 56 </style> 57 <style> 58 /* Sizes */ 59 :root { 60 --viewport-width: min(100vw, 600px); 61 --viewport-padding: 15px; 62 --content-width: calc(var(--viewport-width) - 2 * var(--viewport-padding)); 63 --wide-viewport-width: min(100vw, 900px); 64 --wide-content-width: calc(var(--wide-viewport-width) - 2 * var(--viewport-padding)); 65 --full-bleed-width: calc(100vw - 2 * var(--viewport-padding)); 66 --rem: 14pt; 67 } 68 </style> 69 <style> 70 /* Font face */ 71 @font-face { font-family: 'Atkinson Hyperlegible Next'; font-display: swap; font-style: normal; font-weight: normal; font-variant: normal; text-transform: none; -webkit-font-smoothing: antialiased; src: url(/fonts/AtkinsonHyperlegibleNextVF-Variable.woff2) format('woff2'); } 72 @font-face { font-family: 'Atkinson Hyperlegible Mono'; font-display: swap; font-style: normal; font-weight: normal; font-variant: normal; text-transform: none; -webkit-font-smoothing: antialiased; src: url(/fonts/AtkinsonHyperlegibleMonoVF-Variable.woff2) format('woff2'); } 73 </style> 74 <style> 75 /* Font loading magic to avoid Flash of Invisible Text */ 76 html { font-size: var(--rem); font-family: Arial, Helvetica, sans-serif; font-size-adjust: 0.52; } 77 html.font-loaded { font-family: 'Atkinson Hyperlegible Next', Arial, Helvetica, sans-serif; font-size-adjust: none; } 78 html.font-loaded :is(code, pre, tt, kbd, samp) { font-family: 'Atkinson Hyperlegible Mono', monospace; } 79 </style> 80 <style> 81 /* Horizontal spacing management */ 82 /* Philosophy: everything should be width = ~100vw by default, and limited at the last minute */ 83 div, header, footer, section, article, 84 h1, h2, h3, h4, h5, h6, 85 blockquote, aside, details, 86 p, ul, ol, menu, dl, pre { 87 width: 100%; 88 } 89 90 /* 2 ways to limit: limit-children and limit-self */ 91 .limit-self, 92 .limit-children > * { 93 margin-inline: calc(50vw - var(--content-width) / 2); 94 max-width: var(--content-width); 95 transition: all .3s cubic-bezier(0, 0, 0, 1); 96 } 97 .limit-children > .full-bleed-content { 98 margin-inline: calc(50vw - var(--full-bleed-width) / 2); 99 max-width: var(--full-bleed-width); 100 } 101 .limit-children > .wide-content, 102 #base-middle.wide > #content .limit-children > *:not(.full-bleed-content), 103 #base-middle.wide > #content .limit-self { 104 margin-inline: calc(50vw - var(--wide-content-width) / 2); 105 max-width: var(--wide-content-width); 106 } 107 108 #base-middle { 109 display: flex; 110 flex-direction: row; 111 align-items: stretch; 112 width: 100%; 113 } 114 #content { 115 pointer-events: none; 116 width: 100%; 117 } 118 .base-padding { 119 width: 0; 120 } 121 .base-padding.right { 122 direction: rtl; 123 } 124 .base-padding > button { 125 height: 100%; 126 border-radius: var(--viewport-padding); 127 border: none; 128 background: none; 129 padding: 0; 130 width: calc(max(var(--viewport-padding), 50vw - var(--content-width) / 2 - var(--viewport-padding))); 131 transition: all .3s cubic-bezier(0, 0, 0, 1); 132 } 133 .base-padding > button:hover { 134 background: rgba(128, 0, 0, 2%); 135 } 136 #base-middle.wide > .base-padding > button { 137 width: calc(max(var(--viewport-padding), 50vw - var(--wide-content-width) / 2 - var(--viewport-padding))); 138 } 139 </style> 140 <style> 141 /* Hierarchical vertical spacing management */ 142 /* Enable with .hierarchical, disable inside a .hierarchical with .non-hierarchical */ 143 /* Enable for a single element with .hierarchical-self */ 144 145 /* Blocky texts */ 146 /* Headings: h1, h2, h3, h4, h5, h6 */ 147 /* Biglets: blockquote, figure, figcaption, details, pre, dl, ul, ol, menu */ 148 /* Everything else: p, li, dt, dd, ... */ 149 150 /* Lists have special rules: They remove spacing from their children. */ 151 /* Lists: ul, ol, dl, menu */ 152 153 /* Everything gets some start and end margin. */ 154 /* Biglets additionally get some padding. */ 155 156 :root { 157 --headings-start: 1rlh; 158 --paras-start: .5lh; 159 --paras-end: .25lh; 160 --biglets-padding: .2lh; 161 --hr-start: 1em; 162 --hr-end: 1em; 163 } 164 165 :is(.hierarchical *:not(.non-hierarchical *), .hierarchical-self 166 ):not(:is(ol, ul, dl, menu).tight > li, :is(ol, ul, dl, menu).tight > li > *) a:hover { 167 text-decoration: 2.5px dotted underline; 168 text-underline-position: under; 169 } 170 171 :is(.hierarchical *:not(.non-hierarchical *), .hierarchical-self 172 ):not(:is(ol, ul, dl, menu).tight > li, :is(ol, ul, dl, menu).tight > li > *, :first-child 173 ):not(:is(h1, h2, h3, h4, h5, h6) + *) { 174 margin-block-start: var(--paras-start); 175 } 176 177 :is(.hierarchical *:not(.non-hierarchical *), .hierarchical-self 178 ):not(:is(ol, ul, dl, menu).tight > li, :is(ol, ul, dl, menu).tight > li > *, :first-child 179 ):not(:is(h1, h2, h3, h4, h5, h6) + *):is(h1, h2, h3, h4, h5, h6) { 180 margin-block-start: var(--headings-start); 181 } 182 183 :is(.hierarchical *:not(.non-hierarchical *), .hierarchical-self 184 ):not(:is(ol, ul, dl, menu).tight > li, :is(ol, ul, dl, menu).tight > li > *, :last-child) { 185 margin-block-end: var(--paras-end); 186 } 187 188 :is(.hierarchical *:not(.non-hierarchical *), .hierarchical-self 189 ):not(:is(ol, ul, dl, menu).tight > li, :is(ol, ul, dl, menu).tight > li > * 190 ):is(blockquote, figure, figcaption, details, pre, dl, ul, ol, menu) { 191 padding-block-end: var(--biglets-padding); 192 padding-block-start: var(--biglets-padding); 193 } 194 195 hr:is(.hierarchical *:not(.non-hierarchical *), .hierarchical-self 196 ):not(:is(ol, ul, dl, menu).tight > li, :is(ol, ul, dl, menu).tight > li > *) { 197 margin-block-start: var(--hr-start); 198 margin-block-end: var(--hr-end); 199 } 200 </style> 201 <style> 202 /* PERSONAL STYLE SECTION */ 203 /* Colours, sizes, lines, links, etc. */ 204 body { 205 background-color: #fdf9ee; 206 } 207 208 ::selection { 209 background-color: #ff002630; 210 } 211 212 h1 { 213 font-size: 1.9em; 214 } 215 216 hr { 217 border: none; 218 border-top: 1px solid; 219 } 220 221 a { 222 text-decoration: none; 223 } 224 a:hover { 225 text-decoration: 1px dotted underline; 226 } 227 a { color: #ac1052; } 228 a:hover { color: #7b0c3b; } 229 a:visited { color: #994016; } 230 a:visited:hover { color: #753111; } 231 232 blockquote { 233 padding-inline-start: 15px; 234 border-inline-start: 3px solid gray; 235 } 236 237 figure > img { 238 margin-inline: auto; 239 } 240 figure > figcaption { 241 text-align: center; 242 } 243 244 ul, ol, dl, menu { 245 padding-inline-start: 1.5em; 246 } 247 248 tags { 249 display: flex; 250 gap: 0.5ch; 251 font-size: 0.8em; 252 color: #711; 253 } 254 tags > * { 255 background: #ffe8c8; 256 border-radius: calc(0.7ch); 257 padding: 0.5ch 0.7ch; 258 pointer-events: auto; 259 line-height: 1; 260 } 261 tags > tag { 262 background: #cef; 263 } 264 265 code:not(pre *) { 266 line-height: 1.3; 267 background: #ffe8c8; 268 padding: 0.1lh; 269 border-radius: calc(0.1lh / 0.4142135623730951); 270 } 271 pre { 272 line-height: 1.3; 273 background-image: 274 linear-gradient(to right, #ffe8c8, #ffe8c8), 275 linear-gradient(to right, #ffe8c8, #ffe8c8), 276 linear-gradient(to right, rgba(0, 0, 0, 0.1), rgba(255, 255, 255, 0)), 277 linear-gradient(to left, rgba(0, 0, 0, 0.1), rgba(255, 255, 255, 0)); 278 background-position: left center, right center, left center, right center; 279 background-repeat: no-repeat; 280 background-color: #ffe8c8; 281 background-size: 20px 100%, 20px 100%, 15px 100%, 15px 100%; 282 background-attachment: local, local, scroll, scroll; 283 padding: 0.3lh; 284 border-radius: calc(0.3lh / 0.4142135623730951); 285 } 286 </style> 287 <style> 288 /* Template styling */ 289 #base-everything { 290 display: flex; 291 flex-direction: column; 292 align-items: center; 293 min-height: 100vh; 294 } 295 296 #base-nav { 297 margin-inline: var(--viewport-padding); 298 } 299 300 #base-nav a:not(:hover) { 301 color: #000; 302 text-decoration: none; 303 } 304 305 #base-site-title-text { 306 padding-block-start: 12px; 307 text-align: center; 308 } 309 310 #base-links { 311 display: flex; 312 justify-content: center; 313 flex-wrap: wrap; 314 gap: 20px; 315 row-gap: 0px; 316 } 317 318 #base-header { 319 overflow: hidden; 320 } 321 322 #base-header-rule { 323 margin-inline: calc(0px - mod(-100vw, 40px) / 2); 324 margin-block-end: 30px; 325 border-block-start: 10px dotted lightpink; 326 } 327 328 #base-footer { 329 margin-block-start: auto; 330 } 331 332 #base-footer-rule { 333 max-width: 25ch; 334 margin-block-start: 20px; 335 margin-inline: auto; 336 border-block-start: 1.5px dotted brown; 337 } 338 339 #base-no-cookie { 340 max-width: var(--content-width); 341 margin-block-end: 15px; 342 padding-block-start: .4lh; 343 text-align: center; 344 color: brown; 345 } 346 </style> 347 <super> 348 </head> 349 <body id="body"> 350 <span style="position: absolute; color: #fdf9ee; left: -10em; top: -10em; font-family: 'Atkinson Hyperlegible Next'">.</span> 351 <span style="position: absolute; color: #fdf9ee; left: -10em; top: -10em; font-family: 'Atkinson Hyperlegible Mono'">.</span> 352 <div id="base-everything"> 353 <header id="base-header"> 354 <nav id="base-nav"> 355 <a id="base-site-title" href="$site.page('').link()"> 356 <h2 id="base-site-title-text" :text="$site.title"></h2> 357 </a> 358 <div id="base-links"> 359 <super> 360 </div> 361 </nav> 362 <hr id="base-header-rule"> 363 </header> 364 <div id="base-middle"> 365 <div class="base-padding left"><button onclick="document.querySelector('#base-middle').classList.toggle('wide');"></button></div> 366 <div id="content"><super></div> 367 <div class="base-padding right"><button onclick="document.querySelector('#base-middle').classList.toggle('wide');"></button></div> 368 </div> 369 <footer id="base-footer" class="limit-children"> 370 <hr id="base-footer-rule"> 371 <super> 372 </footer> 373 </div> 374 <script> 375 /* Font Face Observer v2.3.0 - © Bram Stein. License: BSD-3-Clause */(function(){function p(a,c){document.addEventListener?a.addEventListener("scroll",c,!1):a.attachEvent("scroll",c)}function u(a){document.body?a():document.addEventListener?document.addEventListener("DOMContentLoaded",function b(){document.removeEventListener("DOMContentLoaded",b);a()}):document.attachEvent("onreadystatechange",function g(){if("interactive"==document.readyState||"complete"==document.readyState)document.detachEvent("onreadystatechange",g),a()})};function w(a){this.g=document.createElement("div");this.g.setAttribute("aria-hidden","true");this.g.appendChild(document.createTextNode(a));this.h=document.createElement("span");this.i=document.createElement("span");this.m=document.createElement("span");this.j=document.createElement("span");this.l=-1;this.h.style.cssText="max-width:none;display:inline-block;position:absolute;height:100%;width:100%;overflow:scroll;font-size:16px;";this.i.style.cssText="max-width:none;display:inline-block;position:absolute;height:100%;width:100%;overflow:scroll;font-size:16px;"; 376 this.j.style.cssText="max-width:none;display:inline-block;position:absolute;height:100%;width:100%;overflow:scroll;font-size:16px;";this.m.style.cssText="display:inline-block;width:200%;height:200%;font-size:16px;max-width:none;";this.h.appendChild(this.m);this.i.appendChild(this.j);this.g.appendChild(this.h);this.g.appendChild(this.i)} 377 function x(a,c){a.g.style.cssText="max-width:none;min-width:20px;min-height:20px;display:inline-block;overflow:hidden;position:absolute;width:auto;margin:0;padding:0;top:-999px;white-space:nowrap;font-synthesis:none;font:"+c+";"}function B(a){var c=a.g.offsetWidth,b=c+100;a.j.style.width=b+"px";a.i.scrollLeft=b;a.h.scrollLeft=a.h.scrollWidth+100;return a.l!==c?(a.l=c,!0):!1}function C(a,c){function b(){var e=g;B(e)&&null!==e.g.parentNode&&c(e.l)}var g=a;p(a.h,b);p(a.i,b);B(a)};function D(a,c,b){c=c||{};b=b||window;this.family=a;this.style=c.style||"normal";this.weight=c.weight||"normal";this.stretch=c.stretch||"normal";this.context=b}var E=null,F=null,G=null,H=null;function I(a){null===F&&(M(a)&&/Apple/.test(window.navigator.vendor)?(a=/AppleWebKit\/([0-9]+)(?:\.([0-9]+))(?:\.([0-9]+))/.exec(window.navigator.userAgent),F=!!a&&603>parseInt(a[1],10)):F=!1);return F}function M(a){null===H&&(H=!!a.document.fonts);return H} 378 function N(a,c){var b=a.style,g=a.weight;if(null===G){var e=document.createElement("div");try{e.style.font="condensed 100px sans-serif"}catch(q){}G=""!==e.style.font}return[b,g,G?a.stretch:"","100px",c].join(" ")} 379 D.prototype.load=function(a,c){var b=this,g=a||"BESbswy",e=0,q=c||3E3,J=(new Date).getTime();return new Promise(function(K,L){if(M(b.context)&&!I(b.context)){var O=new Promise(function(r,t){function h(){(new Date).getTime()-J>=q?t(Error(""+q+"ms timeout exceeded")):b.context.document.fonts.load(N(b,'"'+b.family+'"'),g).then(function(n){1<=n.length?r():setTimeout(h,25)},t)}h()}),P=new Promise(function(r,t){e=setTimeout(function(){t(Error(""+q+"ms timeout exceeded"))},q)});Promise.race([P,O]).then(function(){clearTimeout(e); 380 K(b)},L)}else u(function(){function r(){var d;if(d=-1!=k&&-1!=l||-1!=k&&-1!=m||-1!=l&&-1!=m)(d=k!=l&&k!=m&&l!=m)||(null===E&&(d=/AppleWebKit\/([0-9]+)(?:\.([0-9]+))/.exec(window.navigator.userAgent),E=!!d&&(536>parseInt(d[1],10)||536===parseInt(d[1],10)&&11>=parseInt(d[2],10))),d=E&&(k==y&&l==y&&m==y||k==z&&l==z&&m==z||k==A&&l==A&&m==A)),d=!d;d&&(null!==f.parentNode&&f.parentNode.removeChild(f),clearTimeout(e),K(b))}function t(){if((new Date).getTime()-J>=q)null!==f.parentNode&&f.parentNode.removeChild(f), 381 L(Error(""+q+"ms timeout exceeded"));else{var d=b.context.document.hidden;if(!0===d||void 0===d)k=h.g.offsetWidth,l=n.g.offsetWidth,m=v.g.offsetWidth,r();e=setTimeout(t,50)}}var h=new w(g),n=new w(g),v=new w(g),k=-1,l=-1,m=-1,y=-1,z=-1,A=-1,f=document.createElement("div");f.dir="ltr";x(h,N(b,"sans-serif"));x(n,N(b,"serif"));x(v,N(b,"monospace"));f.appendChild(h.g);f.appendChild(n.g);f.appendChild(v.g);b.context.document.body.appendChild(f);y=h.g.offsetWidth;z=n.g.offsetWidth;A=v.g.offsetWidth;t(); 382 C(h,function(d){k=d;r()});x(h,N(b,'"'+b.family+'",sans-serif'));C(n,function(d){l=d;r()});x(n,N(b,'"'+b.family+'",serif'));C(v,function(d){m=d;r()});x(v,N(b,'"'+b.family+'",monospace'))})})};"object"===typeof module?module.exports=D:(window.FontFaceObserver=D,window.FontFaceObserver.prototype.load=D.prototype.load);}()); 383 384 var font = new FontFaceObserver('Atkinson Hyperlegible Next'); 385 font.load().then(function () { 386 document.querySelector('html').classList.add('font-loaded'); 387 console.log("Loaded."); 388 }, function () { 389 document.querySelector('html').classList.add('font-loaded'); 390 console.log("Failed."); 391 }); 392 </script> 393 <script> 394 const promises = {}; 395 const results = {}; 396 397 function mouseEnterListener() { 398 const href = this.href; 399 console.log('enter', this, href, promises[href]); 400 if (promises[href] === undefined) { 401 promises[href] = new Promise((resolve) => { 402 const req = new XMLHttpRequest(); 403 req.addEventListener("load", function() { 404 results[href] = new DOMParser().parseFromString(this.response, "text/html").querySelector('#content'); 405 console.log('ho', results[href]); 406 resolve(results[href]); 407 }); 408 req.open("GET", href + '/index.raw.html'); 409 req.send(); 410 }); 411 } 412 } 413 414 function clickListener(event) { 415 const href = this.href; 416 console.log('click', this, href, promises[href]); 417 if (promises[href] !== undefined) { 418 event.preventDefault(); 419 promises[href].then((content) => { 420 document.querySelector('#content').innerHTML = content.innerHTML; 421 history.pushState(null, '', href + '#body'); 422 history.replaceState(null, '', href); 423 refreshLinks(); 424 }); 425 } 426 } 427 428 function refreshLinks() { 429 for (let a of document.querySelectorAll("a")) { 430 if ( 431 !a.href.endsWith("/") || 432 a.href.match("^.*//[^/]*/")[0] !== 433 location.href.match("^.*//[^/]*/")[0] || 434 a.href.match("^.*//[^/]*/git") !== null 435 ) { 436 continue; 437 } 438 console.log(a); 439 a.onmouseenter = mouseEnterListener; 440 a.onclick = clickListener; 441 } 442 } 443 refreshLinks(); 444 445 function popStateListener(event) { 446 console.log('popstate'); 447 const href = location.href; 448 if (results[location.href] === undefined) { 449 if (promises[href] === undefined) { 450 promises[href] = new Promise((resolve) => { 451 const req = new XMLHttpRequest(); 452 req.addEventListener("load", function() { 453 results[href] = new DOMParser().parseFromString(this.response, "text/html").querySelector('#content'); 454 console.log('ho', results[href]); 455 resolve(results[href]); 456 }); 457 req.open("GET", href + '/index.raw.html'); 458 req.send(); 459 }); 460 } 461 promises[href].then((content) => { 462 document.querySelector('#content').innerHTML = content.innerHTML; 463 refreshLinks(); 464 }); 465 } else { 466 document.querySelector('#content').innerHTML = results[location.href].innerHTML; 467 refreshLinks(); 468 } 469 } 470 471 window.onpopstate = popStateListener; 472 </script> 473 </body> 474 </html>