blog.grace.moe

Source for the blog blog.grace.moe
git clone https://git.grace.moe/blog.grace.moe
Log | Files | Refs

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>