├── favicon.ico
├── img
├── og-img.png
├── intro-screenshot.png
└── favicon
│ ├── favicon-16x16.png
│ ├── favicon-32x32.png
│ ├── mstile-70x70.png
│ ├── mstile-144x144.png
│ ├── mstile-150x150.png
│ ├── mstile-310x150.png
│ ├── mstile-310x310.png
│ ├── apple-touch-icon.png
│ ├── android-chrome-192x192.png
│ ├── android-chrome-512x512.png
│ ├── android-chrome-192x192-maskable.png
│ ├── android-chrome-512x512-maskable.png
│ ├── favicon.svg
│ └── safari-pinned-tab.svg
├── .vscode
└── settings.json
├── browserconfig.xml
├── css
├── _fonts.scss
├── _themes.scss
├── index.min.css
├── index.css
└── index.scss
├── README.md
├── todo.txt
├── manifest.webmanifest
├── sw.js
└── js
└── index.js
/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/theothertored/kode-guide/HEAD/favicon.ico
--------------------------------------------------------------------------------
/img/og-img.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/theothertored/kode-guide/HEAD/img/og-img.png
--------------------------------------------------------------------------------
/img/intro-screenshot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/theothertored/kode-guide/HEAD/img/intro-screenshot.png
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "easysass.excludeRegex": "^_",
3 | "easysass.compileAfterSave": true
4 | }
--------------------------------------------------------------------------------
/img/favicon/favicon-16x16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/theothertored/kode-guide/HEAD/img/favicon/favicon-16x16.png
--------------------------------------------------------------------------------
/img/favicon/favicon-32x32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/theothertored/kode-guide/HEAD/img/favicon/favicon-32x32.png
--------------------------------------------------------------------------------
/img/favicon/mstile-70x70.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/theothertored/kode-guide/HEAD/img/favicon/mstile-70x70.png
--------------------------------------------------------------------------------
/img/favicon/mstile-144x144.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/theothertored/kode-guide/HEAD/img/favicon/mstile-144x144.png
--------------------------------------------------------------------------------
/img/favicon/mstile-150x150.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/theothertored/kode-guide/HEAD/img/favicon/mstile-150x150.png
--------------------------------------------------------------------------------
/img/favicon/mstile-310x150.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/theothertored/kode-guide/HEAD/img/favicon/mstile-310x150.png
--------------------------------------------------------------------------------
/img/favicon/mstile-310x310.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/theothertored/kode-guide/HEAD/img/favicon/mstile-310x310.png
--------------------------------------------------------------------------------
/img/favicon/apple-touch-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/theothertored/kode-guide/HEAD/img/favicon/apple-touch-icon.png
--------------------------------------------------------------------------------
/img/favicon/android-chrome-192x192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/theothertored/kode-guide/HEAD/img/favicon/android-chrome-192x192.png
--------------------------------------------------------------------------------
/img/favicon/android-chrome-512x512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/theothertored/kode-guide/HEAD/img/favicon/android-chrome-512x512.png
--------------------------------------------------------------------------------
/img/favicon/android-chrome-192x192-maskable.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/theothertored/kode-guide/HEAD/img/favicon/android-chrome-192x192-maskable.png
--------------------------------------------------------------------------------
/img/favicon/android-chrome-512x512-maskable.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/theothertored/kode-guide/HEAD/img/favicon/android-chrome-512x512-maskable.png
--------------------------------------------------------------------------------
/browserconfig.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | #101010
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/css/_fonts.scss:
--------------------------------------------------------------------------------
1 | @import url('https://fonts.googleapis.com/css2?family=Inria+Sans:ital@0;1&family=Roboto+Mono&family=Source+Serif+Pro:wght@700&display=swap');
2 |
3 | $font-family-heading: 'Source Serif Pro', serif;
4 | $font-family-body: 'Inria Sans', sans-serif;
5 | $font-family-mono: 'Roboto Mono', monospace;
6 | $font-size-base: 1rem;
7 | $font-size-code: 0.85em;
8 | $font-size-small: 0.75rem;
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # A guide to Kode basics
2 |
3 | (by Tored)
4 |
5 | ### [\[ Click here to read the guide \]](https://theothertored.github.io/kode-guide)
6 |
7 | This is an expanded and improved version of the old Kode guide I wrote in Google Docs.
8 | It comes with major rewrites, more sections and examples, a light & dark mode, and an interactive table of contents.
9 |
10 | It also works offline and can be installed like an app on your device.
11 |
12 | ---
13 |
14 | Shoutouts to Bunn-E, Grabster and especially robin for motivating me to work on this overhaul.
--------------------------------------------------------------------------------
/todo.txt:
--------------------------------------------------------------------------------
1 | DONE:
2 | - write some kind of readme
3 | - section about basic lv() usage
4 | - add a service worker + PWA capability
5 | - favicon
6 | - "pick up where you left off" prompt
7 | - open/close left drawer with a swipe (I already tried opening it like that without thinking and it went back to my new tab screen)
8 | - expand the regex section a bit to maybe convince people to learn it
9 | - hamburger to arrow animation for fab
10 | - add some text explaining section links and copying examples
11 | - double tap to copy example
12 | - refactor the absolute bowl of spaghetti I have created (wasn't that bad lol)
13 | - smaller/larger list entries for mouse/touchscreen
14 | - mode switcher in left drawer so the mode can be changed from any position on the page
15 | - fix light mode heavy border being the same color as the light border
--------------------------------------------------------------------------------
/manifest.webmanifest:
--------------------------------------------------------------------------------
1 | {
2 | "id": "kode-guide",
3 | "name": "Kode Guide",
4 | "short_name": "Kode Guide",
5 | "description": "An in-depth guide to Kustom formula basics (mainly KLWP and KWGT).",
6 | "icons": [
7 | {
8 | "src": "img/favicon/android-chrome-192x192.png",
9 | "sizes": "192x192",
10 | "type": "image/png"
11 | },
12 | {
13 | "src": "img/favicon/android-chrome-512x512.png",
14 | "sizes": "512x512",
15 | "type": "image/png"
16 | },
17 | {
18 | "src": "img/favicon/android-chrome-192x192-maskable.png",
19 | "sizes": "192x192",
20 | "type": "image/png",
21 | "purpose": "maskable"
22 | },
23 | {
24 | "src": "img/favicon/android-chrome-512x512-maskable.png",
25 | "sizes": "512x512",
26 | "type": "image/png",
27 | "purpose": "maskable"
28 | }
29 | ],
30 | "start_url": "/kode-guide/",
31 | "theme_color": "#101010",
32 | "background_color": "#101010",
33 | "display": "standalone"
34 | }
35 |
--------------------------------------------------------------------------------
/sw.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | const cacheName = 'kode-guide';
4 |
5 | self.addEventListener('install', ev => {
6 |
7 | ev.waitUntil(
8 | caches.open(cacheName).then(cache => {
9 | return cache.addAll([
10 | "",
11 | "js/index.js",
12 | "css/index.css",
13 | ]);
14 | })
15 | );
16 |
17 | });
18 |
19 | self.addEventListener('fetch', ev => {
20 |
21 | if (ev.request.method === 'GET') {
22 |
23 | ev.respondWith(
24 | caches.open(cacheName).then(cache => {
25 | return fetch(ev.request)
26 | .then(resp => {
27 |
28 | let reqUrl = ev.request.url.toString();
29 |
30 | if (reqUrl.includes('v=')) {
31 | cache.put(getReqUrlWithoutVersion(reqUrl), resp.clone());
32 | } else {
33 | cache.put(ev.request, resp.clone());
34 | }
35 |
36 | return resp;
37 |
38 | })
39 | .catch(err => {
40 |
41 | let reqUrl = ev.request.url.toString();
42 |
43 | if (reqUrl.includes('v=')) {
44 | return cache.match(getReqUrlWithoutVersion(reqUrl));
45 | } else {
46 | return cache.match(ev.request);
47 | }
48 |
49 | });
50 | })
51 | );
52 |
53 | }
54 |
55 | });
56 |
57 | function getReqUrlWithoutVersion(reqUrl){
58 | return reqUrl.replace(/[?&]v=.*?([]|$)/g, '');
59 | }
--------------------------------------------------------------------------------
/img/favicon/favicon.svg:
--------------------------------------------------------------------------------
1 |
2 |
45 |
--------------------------------------------------------------------------------
/img/favicon/safari-pinned-tab.svg:
--------------------------------------------------------------------------------
1 |
2 |
4 |
48 |
--------------------------------------------------------------------------------
/css/_themes.scss:
--------------------------------------------------------------------------------
1 | // variables shared between themes
2 | $c-dark-bg: #101010;
3 | $c-dark-text: rgba(#fff, 0.9);
4 | $c-light-bg: #ffffff;
5 | $c-light-text: rgba(#000, 0.9);
6 | $c-accent: #ff00ff;
7 |
8 | // dark theme
9 | @media not print {
10 | :root.dark {
11 | --c-bg: $c-dark-bg;
12 | --c-bg-hover: rgba(#fff, 0.1);
13 | --c-bg-copied: $c-light-bg;
14 | --c-text-on-bg-pri: $c-dark-text;
15 | --c-text-on-bg-sec: rgba(#fff, 0.8);
16 | --c-text-on-bg-tet: rgba(#fff, 0.6);
17 | --c-text-on-bg-irr: rgba(#fff, 0.4);
18 | --c-text-link: #74c9ea;
19 | --c-text-error: #ed9090;
20 | --c-text-success: #84c666;
21 | --c-border-light: rgba(#fff, 0.2);
22 | --c-border-heavy: rgba(#fff, 0.5);
23 | --c-code-bg: rgba(#000, 0.01);
24 | --c-hl-1: #eacf7f;
25 | --c-hl-2: #c88afe;
26 | --c-hl-3: #a6ddfd;
27 | --c-hl-4: #c2e4a1;
28 | --c-hl-5: #f794d1;
29 | --c-hl-6: #ffb872;
30 | --c-hl-1-strong: #ffcc35;
31 | --c-hl-2-strong: #d19dff;
32 | --c-hl-3-strong: #74ccff;
33 | --c-hl-4-strong: #c4ff8a;
34 | --c-hl-5-strong: #f794d1;
35 | --c-hl-6-strong: #ffb872;
36 | --c-hl-gray: #f5f5f5;
37 | --c-hl-gray-strong: #d2d2d2;
38 | --c-hl-before: #ffac58;
39 | --c-hl-after: #ffac58;
40 | --c-hl-error: #ea9999;
41 | --c-hl-true: #9ecd88;
42 | --c-hl-false: #d98989;
43 | --c-hl-true-strong: #9dff6f;
44 | --c-hl-false-strong: #ff6f6f;
45 | color-scheme: dark;
46 | }
47 | }
48 |
49 | // light theme
50 | :root {
51 | --c-bg: $c-light-bg;
52 | --c-bg-hover: rgba(#000, 0.05);
53 | --c-bg-copied: $c-dark-bg;
54 | --c-text-on-bg-pri: $c-light-text;
55 | --c-text-on-bg-sec: rgba(#000, 0.75);
56 | --c-text-on-bg-tet: rgba(#000, 0.5);
57 | --c-text-on-bg-irr: rgba(#000, 0.35);
58 | --c-text-link: #0e7398;
59 | --c-text-error: #e41717;
60 | --c-text-success: #52a92a;
61 | --c-border-light: rgba(#000, 0.1);
62 | --c-border-heavy: rgba(#000, 0.25);
63 | --c-code-bg: rgba(#000, 0.01);
64 | --c-hl-1: #ffe599;
65 | --c-hl-2: #f0dfff;
66 | --c-hl-3: #d1eeff;
67 | --c-hl-4: #d9ead3;
68 | --c-hl-5: #f4cccc;
69 | --c-hl-6: #f9cb9c;
70 | --c-hl-1-strong: #f7ca42;
71 | --c-hl-2-strong: #d8bcfd;
72 | --c-hl-3-strong: #9fdcff;
73 | --c-hl-4-strong: #a2f484;
74 | --c-hl-5-strong: #ff7979;
75 | --c-hl-6-strong: #fba44e;
76 | --c-hl-gray: #f5f5f5;
77 | --c-hl-gray-strong: #d2d2d2;
78 | --c-hl-before: #ffd9b3;
79 | --c-hl-after: #ffd9b3;
80 | --c-hl-error: #ea9999;
81 | --c-hl-true: #ecfccf;
82 | --c-hl-false: #ffc7c7;
83 | --c-hl-true-strong: #ccff6d;
84 | --c-hl-false-strong: #ff9797;
85 | color-scheme: light;
86 | }
87 |
88 | // variables so it's easier to change things later just in case
89 | $c-bg: var(--c-bg);
90 | $c-bg-hover: var(--c-bg-hover);
91 | $c-bg-copied: var(--c-bg-copied);
92 | $c-text-on-bg-pri: var(--c-text-on-bg-pri);
93 | $c-text-on-bg-sec: var(--c-text-on-bg-sec);
94 | $c-text-on-bg-tet: var(--c-text-on-bg-tet);
95 | $c-text-on-bg-irr: var(--c-text-on-bg-irr);
96 | $c-text-link: var(--c-text-link);
97 | $c-text-error: var(--c-text-error);
98 | $c-text-success: var(--c-text-success);
99 | $c-border-light: var(--c-border-light);
100 | $c-border-heavy: var(--c-border-heavy);
101 | $c-code-bg: var(--c-code-bg);
102 |
--------------------------------------------------------------------------------
/css/index.min.css:
--------------------------------------------------------------------------------
1 | @import url("https://fonts.googleapis.com/css2?family=Inria+Sans:ital@0;1&family=Roboto+Mono&family=Source+Serif+Pro:wght@700&display=swap");@media not print{:root.dark{--c-bg:#101010;--c-bg-hover:rgba(255,255,255,0.1);--c-bg-copied:#fff;--c-text-on-bg-pri:rgba(255,255,255,0.9);--c-text-on-bg-sec:rgba(255,255,255,0.8);--c-text-on-bg-tet:rgba(255,255,255,0.6);--c-text-on-bg-irr:rgba(255,255,255,0.4);--c-text-link:#74c9ea;--c-text-error:#ed9090;--c-text-success:#84c666;--c-border-light:rgba(255,255,255,0.2);--c-border-heavy:rgba(255,255,255,0.5);--c-code-bg:rgba(0,0,0,0.01);--c-hl-1:#eacf7f;--c-hl-2:#c88afe;--c-hl-3:#a6ddfd;--c-hl-4:#c2e4a1;--c-hl-5:#f794d1;--c-hl-6:#ffb872;--c-hl-1-strong:#ffcc35;--c-hl-2-strong:#d19dff;--c-hl-3-strong:#74ccff;--c-hl-4-strong:#c4ff8a;--c-hl-5-strong:#f794d1;--c-hl-6-strong:#ffb872;--c-hl-gray:#f5f5f5;--c-hl-gray-strong:#d2d2d2;--c-hl-before:#ffac58;--c-hl-after:#ffac58;--c-hl-error:#ea9999;--c-hl-true:#9ecd88;--c-hl-false:#d98989;--c-hl-true-strong:#9dff6f;--c-hl-false-strong:#ff6f6f;color-scheme:dark}}:root{--c-bg:#fff;--c-bg-hover:rgba(0,0,0,0.05);--c-bg-copied:#101010;--c-text-on-bg-pri:rgba(0,0,0,0.9);--c-text-on-bg-sec:rgba(0,0,0,0.75);--c-text-on-bg-tet:rgba(0,0,0,0.5);--c-text-on-bg-irr:rgba(0,0,0,0.35);--c-text-link:#0e7398;--c-text-error:#e41717;--c-text-success:#52a92a;--c-border-light:rgba(0,0,0,0.1);--c-border-heavy:rgba(0,0,0,0.25);--c-code-bg:rgba(0,0,0,0.01);--c-hl-1:#ffe599;--c-hl-2:#f0dfff;--c-hl-3:#d1eeff;--c-hl-4:#d9ead3;--c-hl-5:#f4cccc;--c-hl-6:#f9cb9c;--c-hl-1-strong:#f7ca42;--c-hl-2-strong:#d8bcfd;--c-hl-3-strong:#9fdcff;--c-hl-4-strong:#a2f484;--c-hl-5-strong:#ff7979;--c-hl-6-strong:#fba44e;--c-hl-gray:#f5f5f5;--c-hl-gray-strong:#d2d2d2;--c-hl-before:#ffd9b3;--c-hl-after:#ffd9b3;--c-hl-error:#ea9999;--c-hl-true:#ecfccf;--c-hl-false:#ffc7c7;--c-hl-true-strong:#ccff6d;--c-hl-false-strong:#ff9797;color-scheme:light}@media (hover: hover){.toc>main>.toc-entry,.toc-fab:before,.scroll-restore-message>.btn-dismiss,.btn{transition:background-color cubic-bezier(0.4, 0, 0.2, 1) 0.15s}.toc>main>.toc-entry:hover,.toc-fab:hover:before,.scroll-restore-message>.btn-dismiss:hover,.btn:hover{background-color:var(--c-bg-hover)}}*{box-sizing:border-box}html,body{width:100%;height:100%;padding:0;margin:0}.guide{width:100%;max-width:50rem;padding:0 1rem;padding-bottom:15rem}h1{font-size:2.5rem;margin:2.5rem 0;margin-top:6rem}h1>.small{margin-top:1rem}h2{font-size:2rem;margin:0;padding-top:2rem}h3{font-size:1.5rem;margin:0;padding-top:2.5rem}h4{font-size:1.25rem;margin:0;padding-top:2rem}h2+h3{padding-top:1.5rem}h1+p,h1+.snippet,h2+p,h2+.snippet,h3+p,h3+.snippet,h4+p,h4+.snippet{margin-top:2rem}p,blockquote{margin-top:1.5rem;line-height:140%}.snippet{margin-top:0.5rem}ul,ol{line-height:140%}ul>li+li,ol>li+li{margin-top:0.5rem}.toc{position:fixed;top:0;left:0;bottom:0;z-index:2;width:24rem;max-width:calc(100vw - 3rem)}.toc.in{transform:translateX(0)}.toc:not(.in){transform:translateX(-100%)}.toc-fab{position:fixed;top:1rem;right:-4rem;z-index:2}.toc{display:flex;flex-direction:column;background-color:var(--c-bg);vertical-align:top;border-right:1px solid var(--c-border-light);text-align:left}.toc.transition{transition:transform cubic-bezier(0.4, 0, 0.2, 1) 0.33s}.toc>header{display:flex;align-items:center;width:100%;border-bottom:1px solid var(--c-border-light);padding:1rem;font-family:"Source Serif Pro",serif;font-size:1.5rem}.toc>main{padding:0.5rem;flex:1;overflow-y:auto;overscroll-behavior-y:contain}.toc>main>.toc-entry{display:block;text-decoration:none;color:var(--c-text-on-bg-sec);padding:0.5rem;-webkit-tap-highlight-color:transparent;border-radius:0.25rem}.toc>main>.toc-entry>.number{color:var(--c-text-on-bg-irr)}.toc>main>.toc-1{padding-left:1rem}.toc>main>.toc-2{padding-left:2rem}.toc>footer{padding:1rem;padding-top:0.5rem;border-top:1px solid var(--c-border-light)}.toc>footer>.theme-switcher{margin-top:0}.toc-fab{width:3rem;height:3rem;font-size:1rem;border-radius:1.5rem;border:1px solid var(--c-border-light);color:var(--c-text-on-bg-pri);background-color:var(--c-bg);-webkit-tap-highlight-color:transparent;outline:none;cursor:pointer;overflow:hidden;transition:transform cubic-bezier(0.4, 0, 0.2, 1) 0.33s}.toc-fab:before{content:"";position:absolute;top:0;left:0;right:0;bottom:0}.toc-fab>.top,.toc-fab>.mid,.toc-fab>.bot{position:absolute;top:50%;left:50%;width:1rem;height:0.1rem;background-color:var(--c-text-on-bg-pri)}.toc-fab>.top{transform:translate(-50%, -50%) translateY(-.33333rem)}.toc-fab>.mid{transform:translate(-50%, -50%)}.toc-fab>.bot{transform:translate(-50%, -50%) translateY(.33333rem)}.toc.transition .toc-fab>.top,.toc.transition .toc-fab>.mid,.toc.transition .toc-fab>.bot{transition:transform cubic-bezier(0.4, 0, 0.2, 1) 0.15s}.toc.in .toc-fab>.top{transform:translate(-25%, -50%) translateY(-.33333rem) translate(-33%, .18182rem) rotate(-45deg) scaleX(0.5)}.toc.in .toc-fab>.mid{transform:translate(-50%, -50%) scaleX(0)}.toc.in .toc-fab>.bot{transform:translate(-25%, -50%) translateY(.33333rem) translate(-33%, -.18182rem) rotate(45deg) scaleX(0.5)}.toc-scrim{position:fixed;top:0;left:0;bottom:0;right:0;z-index:1;background-color:var(--c-bg);opacity:0;pointer-events:none;display:none;-webkit-tap-highlight-color:transparent}.toc.in ~ .toc-scrim{opacity:0.9;pointer-events:auto}body{font-family:"Inria Sans",sans-serif;font-size:1rem;color:var(--c-text-on-bg-sec);background-color:var(--c-bg);line-height:1.25}.guide{text-align:justify}h1,h2,h3,h4,h5,h6{font-family:"Source Serif Pro",serif;font-weight:700;color:var(--c-text-on-bg-pri);text-align:left}h1>a,h2>a,h3>a,h4>a,h5>a,h6>a{color:inherit;text-decoration:none}h1>a:hover,h2>a:hover,h3>a:hover,h4>a:hover,h5>a:hover,h6>a:hover{text-decoration:underline}.small{font-size:.75rem;font-weight:normal;color:var(--c-text-on-bg-tet);font-family:"Inria Sans",sans-serif}.guide>footer{margin-top:4rem;border-top:1px dashed var(--c-border-light)}.image-container{width:100%}.image-container img{display:block;max-width:100%;padding:1rem}.image-container label{margin-top:-0.5rem;display:block;text-align:center;font-size:.75rem;color:var(--c-text-on-bg-tet);font-style:italic}a{color:var(--c-text-link)}blockquote{font-style:italic;border-left:2px solid var(--c-border-light);margin-left:0;padding-left:1rem;padding-top:0.5rem;padding-bottom:0.5rem}.guide{counter-reset:h2 h3 h4}h2{counter-increment:h2;counter-reset:h3}h2:before{content:counter(h2) ". "}h3{counter-increment:h3;counter-reset:h4}h3:before{content:counter(h2) "." counter(h3) ". "}h4{counter-increment:h4}h4:before{content:counter(h2) "." counter(h3) "." counter(h4) ". "}code{display:inline-block;font-family:"Roboto Mono",monospace;font-size:.85em;border:1px solid var(--c-border-light);border-radius:0.25em;padding:0 0.35em;margin:0 0.2em;word-break:break-all;text-align:left;font-style:normal}code>pre{margin:0;white-space:pre-wrap}:root:not(.dark) .hl{position:relative;z-index:-1;line-height:1;white-space:pre-wrap;padding:0.1rem;margin:-0.1rem}:root:not(.dark) .hl+.hl{margin-left:0.1rem}:root:not(.dark) .hl-1{background-color:var(--c-hl-1)}:root:not(.dark) .hl-1.strong{background-color:var(--c-hl-1-strong)}:root:not(.dark) .hl-2{background-color:var(--c-hl-2)}:root:not(.dark) .hl-2.strong{background-color:var(--c-hl-2-strong)}:root:not(.dark) .hl-3{background-color:var(--c-hl-3)}:root:not(.dark) .hl-3.strong{background-color:var(--c-hl-3-strong)}:root:not(.dark) .hl-4{background-color:var(--c-hl-4)}:root:not(.dark) .hl-4.strong{background-color:var(--c-hl-4-strong)}:root:not(.dark) .hl-5{background-color:var(--c-hl-5)}:root:not(.dark) .hl-5.strong{background-color:var(--c-hl-5-strong)}:root:not(.dark) .hl-6{background-color:var(--c-hl-6)}:root:not(.dark) .hl-6.strong{background-color:var(--c-hl-6-strong)}:root:not(.dark) .hl-before{background-color:var(--c-hl-before)}:root:not(.dark) .hl-after{background-color:var(--c-hl-after)}:root:not(.dark) .hl-error{background-color:var(--c-hl-error)}:root:not(.dark) .hl-true{background-color:var(--c-hl-true)}:root:not(.dark) .hl-true.strong{background-color:var(--c-hl-true-strong)}:root:not(.dark) .hl-false{background-color:var(--c-hl-false)}:root:not(.dark) .hl-false.strong{background-color:var(--c-hl-false-strong)}:root:not(.dark) .hl-gray{background-color:var(--c-hl-gray)}:root:not(.dark) .hl-gray.strong{background-color:var(--c-hl-gray-strong)}:root.dark .hl-1{color:var(--c-hl-1)}:root.dark .hl-1.strong{color:var(--c-hl-1-strong)}:root.dark .hl-2{color:var(--c-hl-2)}:root.dark .hl-2.strong{color:var(--c-hl-2-strong)}:root.dark .hl-3{color:var(--c-hl-3)}:root.dark .hl-3.strong{color:var(--c-hl-3-strong)}:root.dark .hl-4{color:var(--c-hl-4)}:root.dark .hl-4.strong{color:var(--c-hl-4-strong)}:root.dark .hl-5{color:var(--c-hl-5)}:root.dark .hl-5.strong{color:var(--c-hl-5-strong)}:root.dark .hl-6{color:var(--c-hl-6)}:root.dark .hl-6.strong{color:var(--c-hl-6-strong)}:root.dark .hl-before{color:var(--c-hl-before)}:root.dark .hl-after{color:var(--c-hl-after)}:root.dark .hl-error{color:var(--c-hl-error)}:root.dark .hl-true{color:var(--c-hl-true)}:root.dark .hl-true.strong{color:var(--c-hl-true-strong)}:root.dark .hl-false{color:var(--c-hl-false)}:root.dark .hl-false.strong{color:var(--c-hl-false-strong)}:root.dark .hl-gray{color:var(--c-hl-gray)}:root.dark .hl-gray.strong{color:var(--c-hl-gray-strong)}:root.dark .hl.strong{font-weight:bold}.snippet{margin-top:1rem}.snippet label{display:block;position:sticky;left:0;font-size:.75rem;color:var(--c-text-on-bg-tet);line-height:1;padding:0.4rem 0.5rem}.snippet label>.error,.snippet label>.success{font-style:italic}.snippet label>.error:before,.snippet label>.success:before{content:"-";margin:0 0.25rem;color:var(--c-text-on-bg-tet)}.snippet label>.error{color:var(--c-text-error)}.snippet label>.success{color:var(--c-text-success)}.snippet label.output:before{content:"Output"}.snippet>code,.snippet .step>code{display:block;border:1px solid var(--c-border-light);padding:0.5rem 0.75rem;line-height:1.5;margin:0;overflow:hidden;position:relative}.snippet>code:empty,.snippet .step>code:empty{user-select:none}.snippet>code:empty:before,.snippet .step>code:empty:before{content:" ";white-space:pre}.snippet>code+code,.snippet .step>code+code{margin-top:0.2rem}.snippet>code+label,.snippet .step>code+label{margin-top:0.5rem}.snippet>code.copied:after,.snippet .step>code.copied:after{content:"";background-color:var(--c-bg-copied);position:absolute;top:50%;left:50%;z-index:1;width:50%;height:1000%;transform-origin:center;animation:0.5s 1 forwards copied;animation-timing-function:cubic-bezier(0, 0, 0.2, 1)}@keyframes copied{0%{transform:translateX(-50%) translateX(Min(25rem, 50vw)) translateX(80%) translateY(-50%) translateX(-50%) rotate(20deg)}100%{transform:translateX(-50%) translateX(Max(-25rem, -50vw)) translateX(-80%) translateY(-50%) translateX(-50%) rotate(20deg)}}.snippet>.step .arrow{display:flex;align-items:center;justify-content:start;padding:0 0.5rem;font-size:1.4rem;margin-top:-0.25em;margin-bottom:-0.25em;line-height:1}.snippet>.step .arrow::before{content:"↓";color:var(--c-text-on-bg-tet)}.snippet>.step+label{margin-top:0.5rem}.snippet>.note{font-size:.75rem;color:var(--c-text-on-bg-tet);margin-top:0.5rem;font-style:italic;padding:0 0.5rem}.snippet>.note+label{margin-top:0.5rem}.snippet+.snippet{margin-top:1.5rem}.table-container{width:calc(100% + 16px);padding:8px;margin:0 -8px;overflow-x:auto}table{border-spacing:0;border-collapse:separate;border-radius:0.25rem;border:1px solid var(--c-border-light);text-align:left}table thead>tr:last-child>th{border-bottom:1px solid var(--c-border-heavy)}table tr:first-child th,table tr:first-child td{border-top:none}table th,table td{padding:0.5rem 0.75rem;border-top:1px solid var(--c-border-light);border-left:1px solid var(--c-border-light)}table th:first-child,table td:first-child{border-left:none}table code{white-space:nowrap}#math-symbol-table td:first-child,#math-symbol-table th:first-child{text-align:center}#math-symbol-table td:nth-child(2),#math-symbol-table th:nth-child(2){width:100%}#string-mode-table td:nth-child(1),#string-mode-table th:nth-child(1){text-align:center}#string-mode-table td:nth-child(2),#string-mode-table th:nth-child(2){width:100%}#comparison-operator-table td:nth-child(1),#comparison-operator-table th:nth-child(1){text-align:center}#comparison-operator-table td:nth-child(2),#comparison-operator-table th:nth-child(2){width:100%}:root.light #comparison-operator-table td:nth-child(3),:root.light #comparison-operator-table th:nth-child(3){background-color:var(--c-hl-true)}:root.light #comparison-operator-table td:nth-child(4),:root.light #comparison-operator-table th:nth-child(4){background-color:var(--c-hl-false)}:root:not(.light) #comparison-operator-table td:nth-child(3),:root:not(.light) #comparison-operator-table th:nth-child(3){color:var(--c-hl-true)}:root:not(.light) #comparison-operator-table td:nth-child(4),:root:not(.light) #comparison-operator-table th:nth-child(4){color:var(--c-hl-false)}#logical-operator-table td,#logical-operator-table th{width:80px;text-align:center}#logical-operator-table td:nth-child(3),#logical-operator-table td:nth-child(4),#logical-operator-table th:nth-child(3),#logical-operator-table th:nth-child(4){border-left:1px solid var(--c-border-heavy)}#tf-token-table tr:nth-child(4) td{border-top:1px solid var(--c-border-heavy)}#tf-token-table td:nth-child(1),#tf-token-table th:nth-child(1){text-align:center}#tf-token-table td:nth-child(2),#tf-token-table th:nth-child(2){width:100%}.theme-switcher>label{display:block;font-size:.75rem;color:var(--c-text-on-bg-tet)}.theme-switcher>label+.buttons{margin-top:0.5rem}.theme-switcher>.buttons{display:flex;flex-direction:row;gap:0.5rem;margin-top:1rem;-webkit-tap-highlight-color:transparent}.theme-switcher>.buttons>label{flex:1;padding:0.5rem;text-align:center;position:relative;transition:background-color cubic-bezier(0.4, 0, 0.2, 1) 0.1s,transform cubic-bezier(0.4, 0, 0.2, 1) 0.33s;cursor:pointer}.theme-switcher>.buttons>label>input{display:none}.theme-switcher>.buttons>label>span:before{content:"";position:absolute;top:0;left:0;bottom:0;right:0;border-radius:0.25rem;border:1px solid var(--c-border-light);transition:border cubic-bezier(0.87, 1.92, 0.48, 0.75) 0.15s}@media (hover: hover){.theme-switcher>.buttons>label:hover{background-color:var(--c-bg-hover)}}.theme-switcher>.buttons>label:active{transition-duration:0s;transform:scale(0.96)}.theme-switcher>.buttons>label>input:checked+span:before{border:0.15rem solid #f0f}.theme-switch-overlay{position:fixed;top:0;left:0;width:100vw;bottom:0;z-index:10;transform:translateY(-100%);opacity:1;transition:transform cubic-bezier(0.4, 0, 1, 1) 0.25s}.theme-switch-overlay.dark{background-color:#101010}.theme-switch-overlay.light{background-color:#fff}.theme-switch-overlay.in{transform:translateY(0)}.theme-switch-overlay.out{transform:translateY(100%);transition-timing-function:cubic-bezier(0, 0, 0.2, 1);pointer-events:none}.scroll-restore-message{position:fixed;bottom:1rem;left:1rem;right:1rem;max-width:50rem;margin:auto;display:flex;flex-direction:row;justify-content:space-between;flex-wrap:wrap;align-items:center;border:1px solid var(--c-border-heavy);background-color:var(--c-bg);border-radius:0.25rem;transform:translateY(100%) translateY(1rem);transition:transform cubic-bezier(0.4, 0, 0.2, 1) 0.33s;-webkit-tap-highlight-color:transparent;cursor:pointer}.scroll-restore-message>span{display:inline-block;padding:1rem}.scroll-restore-message>.btn-dismiss{background:none;border:none;outline:none;margin-left:auto;font-family:"Inria Sans",sans-serif;font-size:1rem;color:var(--c-text-on-bg-tet);-webkit-tap-highlight-color:transparent;padding:1rem;cursor:pointer}.scroll-restore-message.in{transform:translateY(0)}.btn{background:none;border:1px solid var(--c-border-heavy);border-radius:0.25rem;outline:none;font-family:"Inria Sans",sans-serif;font-size:1rem;color:var(--c-text-on-bg-sec);-webkit-tap-highlight-color:transparent;padding:0.5rem 1rem;cursor:pointer}.nowrap{white-space:nowrap}.no-display{display:none}.text-right{text-align:right}.text-center{text-align:center}@media screen and (min-width: 100rem){.guide{margin:auto}}@media screen and (min-width: 79rem) and (max-width: 100rem){.toc.transition ~ .guide{transition:transform cubic-bezier(0.4, 0, 0.2, 1) 0.33s}.guide{transform:translateX(50vw) translateX(-50%)}.scroll-restore-message{margin:0;left:0;transform:translateX(50vw) translateX(-50%) translateY(100%) translateY(1rem)}.scroll-restore-message.in{transform:translateX(50vw) translateX(-50%)}.toc.in ~ .guide,.toc.in ~ .scroll-restore-message.in{transform:translateX(29rem)}.toc.in ~ .scroll-restore-message{transform:translateX(29rem) translateY(100%) translateY(1rem)}}@media screen and (max-width: 79rem){.guide{margin:auto}.toc-scrim{display:block !important;transition:opacity cubic-bezier(0.4, 0, 1, 1) 0.2s}}@media screen and (max-width: 60rem){.toc-fab.scroll-out{transform:translateY(-5rem)}.toc.in .toc-fab.scroll-out{transform:translateX(0)}}@media screen and (max-width: 29rem){.toc-fab{right:-1.5rem;top:0.5rem}.toc:not(.in) .toc-fab.scroll-out{transform:translate(2.5rem, -5rem)}.toc:not(.in) .toc-fab:not(.scroll-out){transform:translateX(2.5rem)}}@media (pointer: coarse){.toc main>.toc-entry{padding-top:.75rem;padding-bottom:.75rem}.theme-switcher>.buttons>label{padding:.75rem !important}.btn{padding:.75rem 1.5rem}}@media print{.toc,.toc-fab,.scroll-restore-message,.toc-scrim,.theme-switch-overlay{display:none}h1,h2,h3,h4,h5,h6{page-break-after:avoid}}
2 |
--------------------------------------------------------------------------------
/js/index.js:
--------------------------------------------------------------------------------
1 |
2 | let tocEl, guideEl;
3 | let isTOCOpen;
4 | let matchMedia, currentTheme;
5 |
6 | const mobileThreshold = rem2px(24 + 5 + 56);
7 |
8 | function rem2px(rem)
9 | {
10 | return rem * parseFloat(getComputedStyle(document.documentElement).fontSize);
11 | }
12 |
13 | // initialize before the page loads
14 | initThemeSwitching();
15 |
16 | document.addEventListener('DOMContentLoaded', ev =>
17 | {
18 |
19 | // make sure elements used by more than one function are initialized first
20 | guideEl = document.querySelector('.guide');
21 | tocEl = document.querySelector('.toc');
22 |
23 | initPWA();
24 | initExampleNumbers();
25 | initTOCAndHeaders();
26 | initReactingToResize();
27 | initDoubleTapToCopy();
28 | initSwipeInteractionsForToc();
29 | initScrollRestore();
30 |
31 | });
32 |
33 | function initPWA()
34 | {
35 |
36 | let prompt;
37 |
38 | if ('serviceWorker' in navigator)
39 | {
40 | navigator.serviceWorker.register('/kode-guide/sw.js', { scope: '/kode-guide/' }).then(reg =>
41 | {
42 |
43 | let swCachingP = document.getElementById('sw-caching-available');
44 | swCachingP.classList.remove('no-display');
45 |
46 | });
47 |
48 | }
49 |
50 | window.addEventListener('beforeinstallprompt', ev =>
51 | {
52 |
53 | prompt = ev;
54 | let promptP = document.getElementById('pwa-install-prompt');
55 | let promptBtnP = document.getElementById('pwa-install-button');
56 | let promptBtn = promptBtnP.querySelector('button');
57 |
58 | promptP.classList.remove('no-display');
59 | promptBtnP.classList.remove('no-display');
60 |
61 | promptBtn.addEventListener('click', ev =>
62 | {
63 | prompt.prompt();
64 | });
65 |
66 | });
67 |
68 | }
69 |
70 | function initReactingToResize()
71 | {
72 |
73 | let prevWindowWidth = window.innerWidth;
74 |
75 | // init auto-hiding TOC on window resize
76 | window.addEventListener('resize', ev =>
77 | {
78 |
79 | // expand/collapse toc if we went from mobile to desktop or vice versa
80 | if (window.innerWidth < mobileThreshold != prevWindowWidth < mobileThreshold)
81 | {
82 | setTOCOpen(window.innerWidth >= mobileThreshold);
83 | }
84 |
85 | // save window width for next resize event
86 | prevWindowWidth = window.innerWidth;
87 |
88 | });
89 |
90 | }
91 |
92 | function initExampleNumbers()
93 | {
94 |
95 | // ex. 1 and ex.1.1 counters
96 | let ex = 0, subex = 0;
97 |
98 | // go through each example number element
99 | document.querySelectorAll('.snippet .ex-no').forEach(el =>
100 | {
101 |
102 | if (el.classList.contains('subex'))
103 | {
104 |
105 | // subexample
106 |
107 | if (el.classList.contains('first'))
108 | {
109 | // fisrt subexample should increment the main example counter and reset the subexample counter
110 | ex++;
111 | subex = 0;
112 | }
113 |
114 | // increment subexample counter and set the text
115 | subex++;
116 | el.prepend(`ex. ${ex}.${subex} `);
117 |
118 | } else
119 | {
120 |
121 | // example
122 | // increment example counter, set the text and reset the subexample counter
123 | ex++;
124 | el.prepend(`ex. ${ex} `);
125 | subex = 0;
126 |
127 | }
128 |
129 | });
130 |
131 | }
132 |
133 | function initTOCAndHeaders()
134 | {
135 |
136 | // the element to append created list items to
137 | let tocListEl = tocEl.querySelector('main');
138 | // section counters (1., 1.1., 1.1.1)
139 | let counters = [0, 0, 0];
140 |
141 | // function to scroll to section linked to by the clicked link (ev.currentTarget)
142 | function sectionLinkEl_click(ev)
143 | {
144 | ev.preventDefault();
145 |
146 | // show fragment in the address bar without triggering the default instant scroll
147 | window.history.replaceState(null, null, ev.currentTarget.href);
148 |
149 | // find element given by the href of the clicked link
150 | let scrollToEl = document.querySelector(ev.currentTarget.getAttribute('href'));
151 |
152 | // make sure the TOC fab won't overlap the header we're scrolling to
153 | let scrollTop;
154 |
155 | if (scrollToEl.offsetTop < window.scrollY && window.innerWidth < mobileThreshold)
156 | {
157 | // scrolling up and on mobile, fab will be in the way, offset by its height
158 | scrollTop = scrollToEl.offsetTop - tocFab.clientHeight;
159 | } else
160 | {
161 | // scrolling down or not on mobile, fab is not in the way
162 | scrollTop = scrollToEl.offsetTop;
163 | }
164 |
165 | // use native smooth scroll to scroll to the position
166 | window.scrollTo({
167 | top: scrollTop,
168 | behavior: 'smooth'
169 | });
170 | }
171 |
172 | // go through all headers in the guide
173 | guideEl.querySelectorAll('h2, h3, h4').forEach(el =>
174 | {
175 |
176 | // store the text of the header for later
177 | let html = el.innerHTML;
178 | let text = el.innerText;
179 |
180 | // create the href that will link to this section
181 | let href = `#${el.id}`;
182 |
183 |
184 | // header link
185 |
186 | // create a link to the section for placing in the header
187 | let sectionLinkEl = document.createElement('a');
188 | sectionLinkEl.href = href;
189 | sectionLinkEl.title = 'Link to this section';
190 | sectionLinkEl.innerHTML = html;
191 |
192 | // listen to header clicks
193 | sectionLinkEl.addEventListener('click', sectionLinkEl_click);
194 |
195 | // replace header text with the link to the section
196 | el.innerText = '';
197 | el.appendChild(sectionLinkEl);
198 |
199 |
200 | // toc link
201 |
202 | // create a link to the section for putting in the TOC
203 | let tocLinkEl = document.createElement('a');
204 |
205 | // get which header level this is
206 | let entryIndex = parseInt(el.tagName[1]) - 2;
207 |
208 | // increment the counter for this header level
209 | counters[entryIndex]++;
210 |
211 | // reset counters for lower header levels
212 | for (let i = entryIndex + 1; i < counters.length; i++)
213 | {
214 | counters[i] = 0;
215 | }
216 |
217 | // give the entry a class for indenting
218 | tocLinkEl.classList.add('toc-entry', `toc-${entryIndex}`);
219 |
220 | // make a text for the entry from the counters and the header text
221 | tocLinkEl.innerHTML = `${counters.slice(0, entryIndex + 1).join('.')}. ${html}`;
222 |
223 | tocLinkEl.href = href;
224 |
225 | // listen to TOC entry clicks
226 | tocLinkEl.addEventListener('click', ev =>
227 | {
228 | sectionLinkEl_click(ev)
229 |
230 | // if mobile, close TOC so the user doesn't have to do it manually
231 | if (window.innerWidth < mobileThreshold)
232 | setTOCOpen(false)
233 | });
234 |
235 | // add created entry to the TOC
236 | tocListEl.append(tocLinkEl);
237 |
238 | });
239 |
240 |
241 | let tocFab = document.querySelector('.toc-fab');
242 |
243 | // toggle TOC on fab click
244 | tocFab.addEventListener('click', ev => setTOCOpen(!isTOCOpen));
245 |
246 |
247 | // hide TOC fab on scroll down on mobile
248 |
249 | // init scroll Y to current scroll Y
250 | let prevScrollY = window.scrollY;
251 | let showingFab = true;
252 |
253 | // react to window scrolling
254 | window.addEventListener('scroll', ev =>
255 | {
256 |
257 | // this code could maybe be changed to use requestAnimationFrame, but I don't think that's necessary
258 |
259 | if (window.scrollY > prevScrollY)
260 | {
261 |
262 | // scrolling down, hide fab if it's visible
263 | if (showingFab)
264 | {
265 | tocFab.classList.add('scroll-out');
266 | showingFab = false;
267 | }
268 |
269 | } else
270 | {
271 |
272 | // scrolling up, show fab if it's hidden
273 | if (!showingFab)
274 | {
275 | tocFab.classList.remove('scroll-out');
276 | showingFab = true;
277 | }
278 |
279 | }
280 |
281 | // store current scroll Y for next scroll event
282 | prevScrollY = window.scrollY;
283 |
284 | });
285 |
286 |
287 | // hide TOC on scrim click
288 | let tocScrimEl = document.querySelector('.toc-scrim');
289 | tocScrimEl.addEventListener('click', ev => setTOCOpen(false));
290 |
291 | // show TOC initially on desktop, hide on mobile
292 | setTOCOpen(window.innerWidth >= mobileThreshold);
293 |
294 | // add transition class after setting TOC initial state (so further changes will be animated)
295 | setTimeout(() => tocEl.classList.add('transition'), 0);
296 |
297 | }
298 |
299 | function initThemeSwitching()
300 | {
301 |
302 | // reacts to the user's theme selection changing between 'light', 'dark' and 'system'
303 | // 1. synchronizes both theme switchers
304 | // 2. switches to the appropriate theme (resolves 'system' to 'light' or 'dark')
305 | // 3. saves the selection to localStorage
306 | function reactToSelectedThemeChange(theme, dontAnimate)
307 | {
308 |
309 | if (document.readyState === 'interactive')
310 | {
311 | setCheckedValue('theme', theme);
312 | setCheckedValue('theme2', theme);
313 | }
314 |
315 | if (theme === 'dark' || theme === 'light')
316 | {
317 |
318 | // switch to theme directly
319 | switchTo(theme, dontAnimate);
320 |
321 | } else
322 | {
323 |
324 | // theme is system
325 | if (window.matchMedia)
326 | {
327 |
328 | if (!matchMedia)
329 | {
330 | // this is the first time system was selected, check system theme and listen for further changes
331 | matchMedia = window.matchMedia('(prefers-color-scheme: dark)');
332 | matchMedia.addEventListener('change', systemDarkThemeListener);
333 | }
334 |
335 | if (matchMedia.matches)
336 | {
337 | // system set to dark theme
338 | switchTo('dark', dontAnimate);
339 | } else
340 | {
341 | // system set to light theme
342 | switchTo('light', dontAnimate);
343 | }
344 |
345 | } else
346 | {
347 | // switch to light as fallback
348 | switchTo('light', dontAnimate);
349 | }
350 | }
351 |
352 | // store the selection as the last selected theme that will be loaded on next visit
353 | localStorage.setItem('theme', theme);
354 | }
355 |
356 | // listens to system dark theme changes and switches to the appropriate theme if 'system' is selected
357 | function systemDarkThemeListener()
358 | {
359 | if (document.querySelector('[name="theme"]:checked').value === 'system')
360 | {
361 | switchTo(matchMedia.matches ? 'dark' : 'light');
362 | }
363 | }
364 |
365 | // visually switches the document to light or dark theme (don't pass in 'system')
366 | // if dontAnimate is true:
367 | // 1. toggles the "light" theme class on
368 | // else
369 | // 1. creates a theme-switch-overlay
370 | // 2. shows the overlay and waits for animation finish
371 | // 3. toggles the "light" theme class on
372 | // 4. hides the overlay and waits for animation finish
373 | // 5. removes the overlay from the DOM
374 | function switchTo(toTheme, dontAnimate)
375 | {
376 |
377 | if (currentTheme === toTheme)
378 | return;
379 |
380 | document.documentElement.style.backgroundColor = null;
381 |
382 | if (dontAnimate)
383 | {
384 |
385 | document.documentElement.classList.toggle('dark', toTheme === 'dark');
386 |
387 | } else
388 | {
389 |
390 | let overlay = document.createElement('div');
391 | overlay.classList.add('theme-switch-overlay', toTheme);
392 | document.body.appendChild(overlay);
393 |
394 | setTimeout(() =>
395 | {
396 | overlay.classList.add('in');
397 | setTimeout(() =>
398 | {
399 | document.documentElement.classList.toggle('dark', toTheme === 'dark');
400 | overlay.classList.remove('in');
401 | overlay.classList.add('out');
402 | setTimeout(() =>
403 | {
404 | overlay.remove();
405 | }, 330);
406 | }, 330);
407 | }, 0);
408 |
409 | }
410 |
411 | currentTheme = toTheme;
412 |
413 | }
414 |
415 | // get initial theme from local storage, default to system
416 | let initialTheme = localStorage.getItem('theme') || 'system';
417 |
418 | document.addEventListener('DOMContentLoaded', ev =>
419 | {
420 | document.querySelectorAll('[name="theme"], [name="theme2"]').forEach(el =>
421 | {
422 |
423 | // check appropriate radios
424 | el.checked = el.value === initialTheme;
425 |
426 | // react to radios of both theme switchers changing
427 | el.addEventListener('change', ev => reactToSelectedThemeChange(ev.currentTarget.value));
428 |
429 | });
430 | })
431 |
432 | // set initial theme
433 | reactToSelectedThemeChange(initialTheme, true);
434 |
435 | }
436 |
437 | // helper function to set the checked value of a radio group given by name
438 | function setCheckedValue(name, value)
439 | {
440 | document.querySelectorAll(`[name="${name}"]`).forEach(el =>
441 | {
442 | el.checked = el.value === value;
443 | });
444 | }
445 |
446 | // initializes double-tapping on example boxes to copy to clipboard, with a fancy animation
447 | function initDoubleTapToCopy()
448 | {
449 |
450 | const doubleTapDelay = 500;
451 |
452 | document.querySelectorAll('.snippet > code, .snippet > .step > code').forEach(el =>
453 | {
454 |
455 | let lastTapForThisEl = 0;
456 |
457 | el.addEventListener('click', ev =>
458 | {
459 |
460 | let now = Date.now();
461 |
462 | if (now - lastTapForThisEl < doubleTapDelay)
463 | {
464 |
465 | let noDollars = el.classList.contains('no-dollars') || el.previousElementSibling?.classList.contains('output');
466 |
467 | // double tap detected, copy snippet text to clipboard
468 | navigator.clipboard.writeText(noDollars ? el.innerText : `$${el.innerText}$`).then(function ()
469 | {
470 |
471 | // success, animate
472 |
473 | // after animation ends, remove the animation class and the listener
474 | const listener = ev =>
475 | {
476 | el.classList.remove('copied');
477 | el.removeEventListener('transitionend', listener);
478 | };
479 |
480 | el.classList.add('copied');
481 | el.addEventListener('animationend', listener);
482 |
483 | }, function (err)
484 | {
485 | alert('Error copying:' + err);
486 | });
487 |
488 |
489 | } else
490 | {
491 | lastTapForThisEl = now;
492 | }
493 |
494 | });
495 |
496 | });
497 |
498 | }
499 |
500 | let swipeInProgress = false;
501 | let swipeStartX, swipeStartY;
502 | const swipeThreshold = 24;
503 |
504 | function initSwipeInteractionsForToc()
505 | {
506 |
507 | window.addEventListener('touchstart', ev =>
508 | {
509 |
510 | if (ev.touches.length !== 1)
511 | return;
512 |
513 | let touch = ev.touches[0];
514 | swipeStartX = touch.clientX;
515 | swipeStartY = touch.clientY;
516 |
517 | swipeInProgress = true;
518 |
519 | }, false);
520 |
521 | window.addEventListener('touchmove', ev =>
522 | {
523 |
524 | if (!swipeInProgress)
525 | return;
526 |
527 | let touch = ev.touches[0];
528 |
529 | let dx = touch.clientX - swipeStartX;
530 | let dy = touch.clientY - swipeStartY;
531 |
532 | if (Math.abs(dx) > Math.abs(dy))
533 | {
534 |
535 | if (Math.abs(dx) >= swipeThreshold)
536 | {
537 | // horizontal swipe
538 | ev.preventDefault();
539 | setTOCOpen(dx > 0);
540 | swipeInProgress = false;
541 | }
542 |
543 | } else
544 | {
545 | // vertical swipe
546 | swipeInProgress = false;
547 | }
548 |
549 |
550 | }, { passive: false });
551 |
552 | window.addEventListener('touchend', ev =>
553 | {
554 | swipeInProgress = false;
555 | }, false);
556 |
557 | }
558 |
559 | // opens/closes the TOC and remembers the state in isTOCOpen
560 | function setTOCOpen(open)
561 | {
562 | tocEl.classList.toggle('in', open);
563 | isTOCOpen = open;
564 | }
565 |
566 | function initScrollRestore()
567 | {
568 |
569 | const lastVisitScrollYKey = 'lastVisitScrollY';
570 | const lastVisitFragmentKey = 'lastVisitFragment';
571 |
572 | // restore scroll position on back/forward
573 | window.addEventListener('pageshow', ev =>
574 | {
575 |
576 | let lastVisitScrollY = parseInt(localStorage.getItem(lastVisitScrollYKey));
577 | let lastVisitFragment = localStorage.getItem(lastVisitFragmentKey);
578 |
579 | // ev.persisted is true when loading from bfcache, which preserves scroll pos on its own
580 | if (!ev.persisted)
581 | {
582 |
583 | // if (Math.abs(window.scrollY - lastVisitScrollY) >= 24 && (!window.location.hash || window.location.hash === lastVisitFragment))
584 | if ((window.scrollY === 0 && lastVisitScrollY >= 24) || (window.location.fragment === lastVisitFragment && Math.abs(document.querySelector(lastVisitFragment).offsetTop - window.scrollY) >= 24))
585 | {
586 |
587 | // offer to restore after hard reload/fresh visit
588 |
589 | console.log('adding notif el');
590 |
591 | let notifEl = document.createElement('div');
592 | notifEl.classList.add('scroll-restore-message');
593 |
594 | let spanEl = document.createElement('span');
595 | spanEl.innerText = 'Click here to pick up where you left off';
596 |
597 |
598 | let dismissBtnEl = document.createElement('button');
599 | dismissBtnEl.classList.add('btn-dismiss');
600 | dismissBtnEl.innerText = 'DISMISS';
601 | dismissBtnEl.addEventListener('click', ev =>
602 | {
603 | hideNotifEl()
604 | // make sure the click doesn't reach the parent
605 | ev.stopPropagation();
606 | });
607 |
608 |
609 | notifEl.appendChild(spanEl);
610 | notifEl.appendChild(dismissBtnEl);
611 |
612 | notifEl.addEventListener('click', ev =>
613 | {
614 | window.scrollTo({
615 | top: lastVisitScrollY,
616 | behavior: 'smooth'
617 | });
618 | hideNotifEl();
619 | });
620 |
621 | document.body.appendChild(notifEl);
622 | setTimeout(() => notifEl.classList.add('in'), 0);
623 |
624 | function hideNotifEl()
625 | {
626 |
627 | notifEl.addEventListener('transitionend', ev => notifEl.remove());
628 | notifEl.classList.remove('in');
629 |
630 | localStorage.removeItem(lastVisitScrollYKey);
631 | localStorage.removeItem(lastVisitFragmentKey);
632 |
633 | }
634 |
635 | }
636 | }
637 |
638 | });
639 |
640 | let saveScrollTimeoutDuration = 100;
641 | let saveScrollTimeoutId = null;
642 |
643 | window.addEventListener('scroll', ev =>
644 | {
645 |
646 | clearTimeout(saveScrollTimeoutId);
647 |
648 | saveScrollTimeoutId = setTimeout(() =>
649 | {
650 |
651 | localStorage.setItem(lastVisitScrollYKey, window.scrollY);
652 | localStorage.setItem(lastVisitFragmentKey, window.location.hash);
653 |
654 | }, saveScrollTimeoutDuration);
655 |
656 | });
657 |
658 | }
--------------------------------------------------------------------------------
/css/index.css:
--------------------------------------------------------------------------------
1 | @charset "UTF-8";
2 | @import url("https://fonts.googleapis.com/css2?family=Inria+Sans:ital@0;1&family=Roboto+Mono&family=Source+Serif+Pro:wght@700&display=swap");
3 | @media not print {
4 | :root.dark {
5 | --c-bg: #101010;
6 | --c-bg-hover: rgba(255, 255, 255, 0.1);
7 | --c-bg-copied: #ffffff;
8 | --c-text-on-bg-pri: rgba(255, 255, 255, 0.9);
9 | --c-text-on-bg-sec: rgba(255, 255, 255, 0.8);
10 | --c-text-on-bg-tet: rgba(255, 255, 255, 0.6);
11 | --c-text-on-bg-irr: rgba(255, 255, 255, 0.4);
12 | --c-text-link: #74c9ea;
13 | --c-text-error: #ed9090;
14 | --c-text-success: #84c666;
15 | --c-border-light: rgba(255, 255, 255, 0.2);
16 | --c-border-heavy: rgba(255, 255, 255, 0.5);
17 | --c-code-bg: rgba(0, 0, 0, 0.01);
18 | --c-hl-1: #eacf7f;
19 | --c-hl-2: #c88afe;
20 | --c-hl-3: #a6ddfd;
21 | --c-hl-4: #c2e4a1;
22 | --c-hl-5: #f794d1;
23 | --c-hl-6: #ffb872;
24 | --c-hl-1-strong: #ffcc35;
25 | --c-hl-2-strong: #d19dff;
26 | --c-hl-3-strong: #74ccff;
27 | --c-hl-4-strong: #c4ff8a;
28 | --c-hl-5-strong: #f794d1;
29 | --c-hl-6-strong: #ffb872;
30 | --c-hl-gray: #f5f5f5;
31 | --c-hl-gray-strong: #d2d2d2;
32 | --c-hl-before: #ffac58;
33 | --c-hl-after: #ffac58;
34 | --c-hl-error: #ea9999;
35 | --c-hl-true: #9ecd88;
36 | --c-hl-false: #d98989;
37 | --c-hl-true-strong: #9dff6f;
38 | --c-hl-false-strong: #ff6f6f;
39 | color-scheme: dark;
40 | }
41 | }
42 |
43 | :root {
44 | --c-bg: #ffffff;
45 | --c-bg-hover: rgba(0, 0, 0, 0.05);
46 | --c-bg-copied: #101010;
47 | --c-text-on-bg-pri: rgba(0, 0, 0, 0.9);
48 | --c-text-on-bg-sec: rgba(0, 0, 0, 0.75);
49 | --c-text-on-bg-tet: rgba(0, 0, 0, 0.5);
50 | --c-text-on-bg-irr: rgba(0, 0, 0, 0.35);
51 | --c-text-link: #0e7398;
52 | --c-text-error: #e41717;
53 | --c-text-success: #52a92a;
54 | --c-border-light: rgba(0, 0, 0, 0.1);
55 | --c-border-heavy: rgba(0, 0, 0, 0.25);
56 | --c-code-bg: rgba(0, 0, 0, 0.01);
57 | --c-hl-1: #ffe599;
58 | --c-hl-2: #f0dfff;
59 | --c-hl-3: #d1eeff;
60 | --c-hl-4: #d9ead3;
61 | --c-hl-5: #f4cccc;
62 | --c-hl-6: #f9cb9c;
63 | --c-hl-1-strong: #f7ca42;
64 | --c-hl-2-strong: #d8bcfd;
65 | --c-hl-3-strong: #9fdcff;
66 | --c-hl-4-strong: #a2f484;
67 | --c-hl-5-strong: #ff7979;
68 | --c-hl-6-strong: #fba44e;
69 | --c-hl-gray: #f5f5f5;
70 | --c-hl-gray-strong: #d2d2d2;
71 | --c-hl-before: #ffd9b3;
72 | --c-hl-after: #ffd9b3;
73 | --c-hl-error: #ea9999;
74 | --c-hl-true: #ecfccf;
75 | --c-hl-false: #ffc7c7;
76 | --c-hl-true-strong: #ccff6d;
77 | --c-hl-false-strong: #ff9797;
78 | color-scheme: light;
79 | }
80 |
81 | @media (hover: hover) {
82 | .toc > main > .toc-entry, .toc-fab:before, .scroll-restore-message > .btn-dismiss, .btn {
83 | transition: background-color cubic-bezier(0.4, 0, 0.2, 1) 0.15s;
84 | }
85 | .toc > main > .toc-entry:hover, .toc-fab:hover:before, .scroll-restore-message > .btn-dismiss:hover, .btn:hover {
86 | background-color: var(--c-bg-hover);
87 | }
88 | }
89 |
90 | * {
91 | box-sizing: border-box;
92 | }
93 |
94 | html,
95 | body {
96 | width: 100%;
97 | height: 100%;
98 | padding: 0;
99 | margin: 0;
100 | }
101 |
102 | .guide {
103 | width: 100%;
104 | max-width: 50rem;
105 | padding: 0 1rem;
106 | padding-bottom: 15rem;
107 | }
108 |
109 | h1 {
110 | font-size: 2.5rem;
111 | margin: 2.5rem 0;
112 | margin-top: 6rem;
113 | }
114 |
115 | h1 > .small {
116 | margin-top: 1rem;
117 | }
118 |
119 | h2 {
120 | font-size: 2rem;
121 | margin: 0;
122 | padding-top: 2rem;
123 | }
124 |
125 | h3 {
126 | font-size: 1.5rem;
127 | margin: 0;
128 | padding-top: 2.5rem;
129 | }
130 |
131 | h4 {
132 | font-size: 1.25rem;
133 | margin: 0;
134 | padding-top: 2rem;
135 | }
136 |
137 | h2 + h3 {
138 | padding-top: 1.5rem;
139 | }
140 |
141 | h1 + p, h1 + .snippet, h2 + p, h2 + .snippet, h3 + p, h3 + .snippet, h4 + p, h4 + .snippet {
142 | margin-top: 2rem;
143 | }
144 |
145 | p, blockquote {
146 | margin-top: 1.5rem;
147 | line-height: 140%;
148 | }
149 |
150 | .snippet {
151 | margin-top: 0.5rem;
152 | }
153 |
154 | ul, ol {
155 | line-height: 140%;
156 | }
157 |
158 | ul > li + li, ol > li + li {
159 | margin-top: 0.5rem;
160 | }
161 |
162 | .toc {
163 | position: fixed;
164 | top: 0;
165 | left: 0;
166 | bottom: 0;
167 | z-index: 2;
168 | width: 24rem;
169 | max-width: calc(100vw - 3rem);
170 | }
171 |
172 | .toc.in {
173 | transform: translateX(0);
174 | }
175 |
176 | .toc:not(.in) {
177 | transform: translateX(-100%);
178 | }
179 |
180 | .toc-fab {
181 | position: fixed;
182 | top: 1rem;
183 | right: -4rem;
184 | z-index: 2;
185 | }
186 |
187 | .toc {
188 | display: flex;
189 | flex-direction: column;
190 | background-color: var(--c-bg);
191 | vertical-align: top;
192 | border-right: 1px solid var(--c-border-light);
193 | text-align: left;
194 | }
195 |
196 | .toc.transition {
197 | transition: transform cubic-bezier(0.4, 0, 0.2, 1) 0.33s;
198 | }
199 |
200 | .toc > header {
201 | display: flex;
202 | align-items: center;
203 | width: 100%;
204 | border-bottom: 1px solid var(--c-border-light);
205 | padding: 1rem;
206 | font-family: "Source Serif Pro", serif;
207 | font-size: 1.5rem;
208 | }
209 |
210 | .toc > main {
211 | padding: 0.5rem;
212 | flex: 1;
213 | overflow-y: auto;
214 | overscroll-behavior-y: contain;
215 | }
216 |
217 | .toc > main > .toc-entry {
218 | display: block;
219 | text-decoration: none;
220 | color: var(--c-text-on-bg-sec);
221 | padding: 0.5rem;
222 | -webkit-tap-highlight-color: transparent;
223 | border-radius: 0.25rem;
224 | }
225 |
226 | .toc > main > .toc-entry > .number {
227 | color: var(--c-text-on-bg-irr);
228 | }
229 |
230 | .toc > main > .toc-1 {
231 | padding-left: 1rem;
232 | }
233 |
234 | .toc > main > .toc-2 {
235 | padding-left: 2rem;
236 | }
237 |
238 | .toc > footer {
239 | padding: 1rem;
240 | padding-top: 0.5rem;
241 | border-top: 1px solid var(--c-border-light);
242 | }
243 |
244 | .toc > footer > .theme-switcher {
245 | margin-top: 0;
246 | }
247 |
248 | .toc-fab {
249 | width: 3rem;
250 | height: 3rem;
251 | font-size: 1rem;
252 | border-radius: 1.5rem;
253 | border: 1px solid var(--c-border-light);
254 | color: var(--c-text-on-bg-pri);
255 | background-color: var(--c-bg);
256 | -webkit-tap-highlight-color: transparent;
257 | outline: none;
258 | cursor: pointer;
259 | overflow: hidden;
260 | transition: transform cubic-bezier(0.4, 0, 0.2, 1) 0.33s;
261 | }
262 |
263 | .toc-fab:before {
264 | content: "";
265 | position: absolute;
266 | top: 0;
267 | left: 0;
268 | right: 0;
269 | bottom: 0;
270 | }
271 |
272 | .toc-fab > .top, .toc-fab > .mid, .toc-fab > .bot {
273 | position: absolute;
274 | top: 50%;
275 | left: 50%;
276 | width: 1rem;
277 | height: 0.1rem;
278 | background-color: var(--c-text-on-bg-pri);
279 | }
280 |
281 | .toc-fab > .top {
282 | transform: translate(-50%, -50%) translateY(-0.33333rem);
283 | }
284 |
285 | .toc-fab > .mid {
286 | transform: translate(-50%, -50%);
287 | }
288 |
289 | .toc-fab > .bot {
290 | transform: translate(-50%, -50%) translateY(0.33333rem);
291 | }
292 |
293 | .toc.transition .toc-fab > .top, .toc.transition .toc-fab > .mid, .toc.transition .toc-fab > .bot {
294 | transition: transform cubic-bezier(0.4, 0, 0.2, 1) 0.15s;
295 | }
296 |
297 | .toc.in .toc-fab > .top {
298 | transform: translate(-25%, -50%) translateY(-0.33333rem) translate(-33%, 0.18182rem) rotate(-45deg) scaleX(0.5);
299 | }
300 |
301 | .toc.in .toc-fab > .mid {
302 | transform: translate(-50%, -50%) scaleX(0);
303 | }
304 |
305 | .toc.in .toc-fab > .bot {
306 | transform: translate(-25%, -50%) translateY(0.33333rem) translate(-33%, -0.18182rem) rotate(45deg) scaleX(0.5);
307 | }
308 |
309 | .toc-scrim {
310 | position: fixed;
311 | top: 0;
312 | left: 0;
313 | bottom: 0;
314 | right: 0;
315 | z-index: 1;
316 | background-color: var(--c-bg);
317 | opacity: 0;
318 | pointer-events: none;
319 | display: none;
320 | -webkit-tap-highlight-color: transparent;
321 | }
322 |
323 | .toc.in ~ .toc-scrim {
324 | opacity: 0.9;
325 | pointer-events: auto;
326 | }
327 |
328 | body {
329 | font-family: "Inria Sans", sans-serif;
330 | font-size: 1rem;
331 | color: var(--c-text-on-bg-sec);
332 | background-color: var(--c-bg);
333 | line-height: 1.25;
334 | }
335 |
336 | .guide {
337 | text-align: justify;
338 | }
339 |
340 | h1, h2, h3, h4, h5, h6 {
341 | font-family: "Source Serif Pro", serif;
342 | font-weight: 700;
343 | color: var(--c-text-on-bg-pri);
344 | text-align: left;
345 | }
346 |
347 | h1 > a, h2 > a, h3 > a, h4 > a, h5 > a, h6 > a {
348 | color: inherit;
349 | text-decoration: none;
350 | }
351 |
352 | h1 > a:hover, h2 > a:hover, h3 > a:hover, h4 > a:hover, h5 > a:hover, h6 > a:hover {
353 | text-decoration: underline;
354 | }
355 |
356 | .small {
357 | font-size: 0.75rem;
358 | font-weight: normal;
359 | color: var(--c-text-on-bg-tet);
360 | font-family: "Inria Sans", sans-serif;
361 | }
362 |
363 | .guide > footer {
364 | margin-top: 4rem;
365 | border-top: 1px dashed var(--c-border-light);
366 | }
367 |
368 | .image-container {
369 | width: 100%;
370 | }
371 |
372 | .image-container img {
373 | display: block;
374 | max-width: 100%;
375 | padding: 1rem;
376 | }
377 |
378 | .image-container label {
379 | margin-top: -0.5rem;
380 | display: block;
381 | text-align: center;
382 | font-size: 0.75rem;
383 | color: var(--c-text-on-bg-tet);
384 | font-style: italic;
385 | }
386 |
387 | a {
388 | color: var(--c-text-link);
389 | }
390 |
391 | blockquote {
392 | font-style: italic;
393 | border-left: 2px solid var(--c-border-light);
394 | margin-left: 0;
395 | padding-left: 1rem;
396 | padding-top: 0.5rem;
397 | padding-bottom: 0.5rem;
398 | }
399 |
400 | .guide {
401 | counter-reset: h2 h3 h4;
402 | }
403 |
404 | h2 {
405 | counter-increment: h2;
406 | counter-reset: h3;
407 | }
408 |
409 | h2:before {
410 | content: counter(h2) ". ";
411 | }
412 |
413 | h3 {
414 | counter-increment: h3;
415 | counter-reset: h4;
416 | }
417 |
418 | h3:before {
419 | content: counter(h2) "." counter(h3) ". ";
420 | }
421 |
422 | h4 {
423 | counter-increment: h4;
424 | }
425 |
426 | h4:before {
427 | content: counter(h2) "." counter(h3) "." counter(h4) ". ";
428 | }
429 |
430 | code {
431 | display: inline-block;
432 | font-family: "Roboto Mono", monospace;
433 | font-size: 0.85em;
434 | border: 1px solid var(--c-border-light);
435 | border-radius: 0.25em;
436 | padding: 0 0.35em;
437 | margin: 0 0.2em;
438 | word-break: break-all;
439 | text-align: left;
440 | font-style: normal;
441 | }
442 |
443 | code > pre {
444 | margin: 0;
445 | white-space: pre-wrap;
446 | }
447 |
448 | :root:not(.dark) .hl {
449 | position: relative;
450 | z-index: -1;
451 | line-height: 1;
452 | white-space: pre-wrap;
453 | padding: 0.1rem;
454 | margin: -0.1rem;
455 | }
456 |
457 | :root:not(.dark) .hl + .hl {
458 | margin-left: 0.1rem;
459 | }
460 |
461 | :root:not(.dark) .hl-1 {
462 | background-color: var(--c-hl-1);
463 | }
464 |
465 | :root:not(.dark) .hl-1.strong {
466 | background-color: var(--c-hl-1-strong);
467 | }
468 |
469 | :root:not(.dark) .hl-2 {
470 | background-color: var(--c-hl-2);
471 | }
472 |
473 | :root:not(.dark) .hl-2.strong {
474 | background-color: var(--c-hl-2-strong);
475 | }
476 |
477 | :root:not(.dark) .hl-3 {
478 | background-color: var(--c-hl-3);
479 | }
480 |
481 | :root:not(.dark) .hl-3.strong {
482 | background-color: var(--c-hl-3-strong);
483 | }
484 |
485 | :root:not(.dark) .hl-4 {
486 | background-color: var(--c-hl-4);
487 | }
488 |
489 | :root:not(.dark) .hl-4.strong {
490 | background-color: var(--c-hl-4-strong);
491 | }
492 |
493 | :root:not(.dark) .hl-5 {
494 | background-color: var(--c-hl-5);
495 | }
496 |
497 | :root:not(.dark) .hl-5.strong {
498 | background-color: var(--c-hl-5-strong);
499 | }
500 |
501 | :root:not(.dark) .hl-6 {
502 | background-color: var(--c-hl-6);
503 | }
504 |
505 | :root:not(.dark) .hl-6.strong {
506 | background-color: var(--c-hl-6-strong);
507 | }
508 |
509 | :root:not(.dark) .hl-before {
510 | background-color: var(--c-hl-before);
511 | }
512 |
513 | :root:not(.dark) .hl-after {
514 | background-color: var(--c-hl-after);
515 | }
516 |
517 | :root:not(.dark) .hl-error {
518 | background-color: var(--c-hl-error);
519 | }
520 |
521 | :root:not(.dark) .hl-true {
522 | background-color: var(--c-hl-true);
523 | }
524 |
525 | :root:not(.dark) .hl-true.strong {
526 | background-color: var(--c-hl-true-strong);
527 | }
528 |
529 | :root:not(.dark) .hl-false {
530 | background-color: var(--c-hl-false);
531 | }
532 |
533 | :root:not(.dark) .hl-false.strong {
534 | background-color: var(--c-hl-false-strong);
535 | }
536 |
537 | :root:not(.dark) .hl-gray {
538 | background-color: var(--c-hl-gray);
539 | }
540 |
541 | :root:not(.dark) .hl-gray.strong {
542 | background-color: var(--c-hl-gray-strong);
543 | }
544 |
545 | :root.dark .hl-1 {
546 | color: var(--c-hl-1);
547 | }
548 |
549 | :root.dark .hl-1.strong {
550 | color: var(--c-hl-1-strong);
551 | }
552 |
553 | :root.dark .hl-2 {
554 | color: var(--c-hl-2);
555 | }
556 |
557 | :root.dark .hl-2.strong {
558 | color: var(--c-hl-2-strong);
559 | }
560 |
561 | :root.dark .hl-3 {
562 | color: var(--c-hl-3);
563 | }
564 |
565 | :root.dark .hl-3.strong {
566 | color: var(--c-hl-3-strong);
567 | }
568 |
569 | :root.dark .hl-4 {
570 | color: var(--c-hl-4);
571 | }
572 |
573 | :root.dark .hl-4.strong {
574 | color: var(--c-hl-4-strong);
575 | }
576 |
577 | :root.dark .hl-5 {
578 | color: var(--c-hl-5);
579 | }
580 |
581 | :root.dark .hl-5.strong {
582 | color: var(--c-hl-5-strong);
583 | }
584 |
585 | :root.dark .hl-6 {
586 | color: var(--c-hl-6);
587 | }
588 |
589 | :root.dark .hl-6.strong {
590 | color: var(--c-hl-6-strong);
591 | }
592 |
593 | :root.dark .hl-before {
594 | color: var(--c-hl-before);
595 | }
596 |
597 | :root.dark .hl-after {
598 | color: var(--c-hl-after);
599 | }
600 |
601 | :root.dark .hl-error {
602 | color: var(--c-hl-error);
603 | }
604 |
605 | :root.dark .hl-true {
606 | color: var(--c-hl-true);
607 | }
608 |
609 | :root.dark .hl-true.strong {
610 | color: var(--c-hl-true-strong);
611 | }
612 |
613 | :root.dark .hl-false {
614 | color: var(--c-hl-false);
615 | }
616 |
617 | :root.dark .hl-false.strong {
618 | color: var(--c-hl-false-strong);
619 | }
620 |
621 | :root.dark .hl-gray {
622 | color: var(--c-hl-gray);
623 | }
624 |
625 | :root.dark .hl-gray.strong {
626 | color: var(--c-hl-gray-strong);
627 | }
628 |
629 | :root.dark .hl.strong {
630 | font-weight: bold;
631 | }
632 |
633 | .snippet {
634 | margin-top: 1rem;
635 | }
636 |
637 | .snippet label {
638 | display: block;
639 | position: sticky;
640 | left: 0;
641 | font-size: 0.75rem;
642 | color: var(--c-text-on-bg-tet);
643 | line-height: 1;
644 | padding: 0.4rem 0.5rem;
645 | }
646 |
647 | .snippet label > .error,
648 | .snippet label > .success {
649 | font-style: italic;
650 | }
651 |
652 | .snippet label > .error:before,
653 | .snippet label > .success:before {
654 | content: "-";
655 | margin: 0 0.25rem;
656 | color: var(--c-text-on-bg-tet);
657 | }
658 |
659 | .snippet label > .error {
660 | color: var(--c-text-error);
661 | }
662 |
663 | .snippet label > .success {
664 | color: var(--c-text-success);
665 | }
666 |
667 | .snippet label.output:before {
668 | content: "Output";
669 | }
670 |
671 | .snippet > code, .snippet .step > code {
672 | display: block;
673 | border: 1px solid var(--c-border-light);
674 | padding: 0.5rem 0.75rem;
675 | line-height: 1.5;
676 | margin: 0;
677 | overflow: hidden;
678 | position: relative;
679 | }
680 |
681 | .snippet > code:empty, .snippet .step > code:empty {
682 | user-select: none;
683 | }
684 |
685 | .snippet > code:empty:before, .snippet .step > code:empty:before {
686 | content: " ";
687 | white-space: pre;
688 | }
689 |
690 | .snippet > code + code, .snippet .step > code + code {
691 | margin-top: 0.2rem;
692 | }
693 |
694 | .snippet > code + label, .snippet .step > code + label {
695 | margin-top: 0.5rem;
696 | }
697 |
698 | .snippet > code.copied:after, .snippet .step > code.copied:after {
699 | content: "";
700 | background-color: var(--c-bg-copied);
701 | position: absolute;
702 | top: 50%;
703 | left: 50%;
704 | z-index: 1;
705 | width: 50%;
706 | height: 1000%;
707 | transform-origin: center;
708 | animation: 0.5s 1 forwards copied;
709 | animation-timing-function: cubic-bezier(0, 0, 0.2, 1);
710 | }
711 |
712 | @keyframes copied {
713 | 0% {
714 | transform: translateX(-50%) translateX(Min(25rem, 50vw)) translateX(80%) translateY(-50%) translateX(-50%) rotate(20deg);
715 | }
716 | 100% {
717 | transform: translateX(-50%) translateX(Max(-25rem, -50vw)) translateX(-80%) translateY(-50%) translateX(-50%) rotate(20deg);
718 | }
719 | }
720 |
721 | .snippet > .step .arrow {
722 | display: flex;
723 | align-items: center;
724 | justify-content: start;
725 | padding: 0 0.5rem;
726 | font-size: 1.4rem;
727 | margin-top: -0.25em;
728 | margin-bottom: -0.25em;
729 | line-height: 1;
730 | }
731 |
732 | .snippet > .step .arrow::before {
733 | content: "↓";
734 | color: var(--c-text-on-bg-tet);
735 | }
736 |
737 | .snippet > .step + label {
738 | margin-top: 0.5rem;
739 | }
740 |
741 | .snippet > .note {
742 | font-size: 0.75rem;
743 | color: var(--c-text-on-bg-tet);
744 | margin-top: 0.5rem;
745 | font-style: italic;
746 | padding: 0 0.5rem;
747 | }
748 |
749 | .snippet > .note + label {
750 | margin-top: 0.5rem;
751 | }
752 |
753 | .snippet + .snippet {
754 | margin-top: 1.5rem;
755 | }
756 |
757 | .table-container {
758 | width: calc(100% + 16px);
759 | padding: 8px;
760 | margin: 0 -8px;
761 | overflow-x: auto;
762 | }
763 |
764 | table {
765 | border-spacing: 0;
766 | border-collapse: separate;
767 | border-radius: 0.25rem;
768 | border: 1px solid var(--c-border-light);
769 | text-align: left;
770 | }
771 |
772 | table thead > tr:last-child > th {
773 | border-bottom: 1px solid var(--c-border-heavy);
774 | }
775 |
776 | table tr:first-child th,
777 | table tr:first-child td {
778 | border-top: none;
779 | }
780 |
781 | table th,
782 | table td {
783 | padding: 0.5rem 0.75rem;
784 | border-top: 1px solid var(--c-border-light);
785 | border-left: 1px solid var(--c-border-light);
786 | }
787 |
788 | table th:first-child,
789 | table td:first-child {
790 | border-left: none;
791 | }
792 |
793 | table code {
794 | white-space: nowrap;
795 | }
796 |
797 | #math-symbol-table td:first-child,
798 | #math-symbol-table th:first-child {
799 | text-align: center;
800 | }
801 |
802 | #math-symbol-table td:nth-child(2),
803 | #math-symbol-table th:nth-child(2) {
804 | width: 100%;
805 | }
806 |
807 | #string-mode-table td:nth-child(1),
808 | #string-mode-table th:nth-child(1) {
809 | text-align: center;
810 | }
811 |
812 | #string-mode-table td:nth-child(2),
813 | #string-mode-table th:nth-child(2) {
814 | width: 100%;
815 | }
816 |
817 | #comparison-operator-table td:nth-child(1),
818 | #comparison-operator-table th:nth-child(1) {
819 | text-align: center;
820 | }
821 |
822 | #comparison-operator-table td:nth-child(2),
823 | #comparison-operator-table th:nth-child(2) {
824 | width: 100%;
825 | }
826 |
827 | :root.light #comparison-operator-table td:nth-child(3),
828 | :root.light #comparison-operator-table th:nth-child(3) {
829 | background-color: var(--c-hl-true);
830 | }
831 |
832 | :root.light #comparison-operator-table td:nth-child(4),
833 | :root.light #comparison-operator-table th:nth-child(4) {
834 | background-color: var(--c-hl-false);
835 | }
836 |
837 | :root:not(.light) #comparison-operator-table td:nth-child(3),
838 | :root:not(.light) #comparison-operator-table th:nth-child(3) {
839 | color: var(--c-hl-true);
840 | }
841 |
842 | :root:not(.light) #comparison-operator-table td:nth-child(4),
843 | :root:not(.light) #comparison-operator-table th:nth-child(4) {
844 | color: var(--c-hl-false);
845 | }
846 |
847 | #logical-operator-table td,
848 | #logical-operator-table th {
849 | width: 80px;
850 | text-align: center;
851 | }
852 |
853 | #logical-operator-table td:nth-child(3), #logical-operator-table td:nth-child(4),
854 | #logical-operator-table th:nth-child(3),
855 | #logical-operator-table th:nth-child(4) {
856 | border-left: 1px solid var(--c-border-heavy);
857 | }
858 |
859 | #tf-token-table tr:nth-child(4) td {
860 | border-top: 1px solid var(--c-border-heavy);
861 | }
862 |
863 | #tf-token-table td:nth-child(1),
864 | #tf-token-table th:nth-child(1) {
865 | text-align: center;
866 | }
867 |
868 | #tf-token-table td:nth-child(2),
869 | #tf-token-table th:nth-child(2) {
870 | width: 100%;
871 | }
872 |
873 | .theme-switcher > label {
874 | display: block;
875 | font-size: 0.75rem;
876 | color: var(--c-text-on-bg-tet);
877 | }
878 |
879 | .theme-switcher > label + .buttons {
880 | margin-top: 0.5rem;
881 | }
882 |
883 | .theme-switcher > .buttons {
884 | display: flex;
885 | flex-direction: row;
886 | gap: 0.5rem;
887 | margin-top: 1rem;
888 | -webkit-tap-highlight-color: transparent;
889 | }
890 |
891 | .theme-switcher > .buttons > label {
892 | flex: 1;
893 | padding: 0.5rem;
894 | text-align: center;
895 | position: relative;
896 | transition: background-color cubic-bezier(0.4, 0, 0.2, 1) 0.1s, transform cubic-bezier(0.4, 0, 0.2, 1) 0.33s;
897 | cursor: pointer;
898 | }
899 |
900 | .theme-switcher > .buttons > label > input {
901 | display: none;
902 | }
903 |
904 | .theme-switcher > .buttons > label > span:before {
905 | content: "";
906 | position: absolute;
907 | top: 0;
908 | left: 0;
909 | bottom: 0;
910 | right: 0;
911 | border-radius: 0.25rem;
912 | border: 1px solid var(--c-border-light);
913 | transition: border cubic-bezier(0.87, 1.92, 0.48, 0.75) 0.15s;
914 | }
915 |
916 | @media (hover: hover) {
917 | .theme-switcher > .buttons > label:hover {
918 | background-color: var(--c-bg-hover);
919 | }
920 | }
921 |
922 | .theme-switcher > .buttons > label:active {
923 | transition-duration: 0s;
924 | transform: scale(0.96);
925 | }
926 |
927 | .theme-switcher > .buttons > label > input:checked + span:before {
928 | border: 0.15rem solid #ff00ff;
929 | }
930 |
931 | .theme-switch-overlay {
932 | position: fixed;
933 | top: 0;
934 | left: 0;
935 | width: 100vw;
936 | bottom: 0;
937 | z-index: 10;
938 | transform: translateY(-100%);
939 | opacity: 1;
940 | transition: transform cubic-bezier(0.4, 0, 1, 1) 0.25s;
941 | }
942 |
943 | .theme-switch-overlay.dark {
944 | background-color: #101010;
945 | }
946 |
947 | .theme-switch-overlay.light {
948 | background-color: #ffffff;
949 | }
950 |
951 | .theme-switch-overlay.in {
952 | transform: translateY(0);
953 | }
954 |
955 | .theme-switch-overlay.out {
956 | transform: translateY(100%);
957 | transition-timing-function: cubic-bezier(0, 0, 0.2, 1);
958 | pointer-events: none;
959 | }
960 |
961 | .scroll-restore-message {
962 | position: fixed;
963 | bottom: 1rem;
964 | left: 1rem;
965 | right: 1rem;
966 | max-width: 50rem;
967 | margin: auto;
968 | display: flex;
969 | flex-direction: row;
970 | justify-content: space-between;
971 | flex-wrap: wrap;
972 | align-items: center;
973 | border: 1px solid var(--c-border-heavy);
974 | background-color: var(--c-bg);
975 | border-radius: 0.25rem;
976 | transform: translateY(100%) translateY(1rem);
977 | transition: transform cubic-bezier(0.4, 0, 0.2, 1) 0.33s;
978 | -webkit-tap-highlight-color: transparent;
979 | cursor: pointer;
980 | }
981 |
982 | .scroll-restore-message > span {
983 | display: inline-block;
984 | padding: 1rem;
985 | }
986 |
987 | .scroll-restore-message > .btn-dismiss {
988 | background: none;
989 | border: none;
990 | outline: none;
991 | margin-left: auto;
992 | font-family: "Inria Sans", sans-serif;
993 | font-size: 1rem;
994 | color: var(--c-text-on-bg-tet);
995 | -webkit-tap-highlight-color: transparent;
996 | padding: 1rem;
997 | cursor: pointer;
998 | }
999 |
1000 | .scroll-restore-message.in {
1001 | transform: translateY(0);
1002 | }
1003 |
1004 | .btn {
1005 | background: none;
1006 | border: 1px solid var(--c-border-heavy);
1007 | border-radius: 0.25rem;
1008 | outline: none;
1009 | font-family: "Inria Sans", sans-serif;
1010 | font-size: 1rem;
1011 | color: var(--c-text-on-bg-sec);
1012 | -webkit-tap-highlight-color: transparent;
1013 | padding: 0.5rem 1rem;
1014 | cursor: pointer;
1015 | }
1016 |
1017 | .nowrap {
1018 | white-space: nowrap;
1019 | }
1020 |
1021 | .no-display {
1022 | display: none;
1023 | }
1024 |
1025 | .text-right {
1026 | text-align: right;
1027 | }
1028 |
1029 | .text-center {
1030 | text-align: center;
1031 | }
1032 |
1033 | @media screen and (min-width: 100rem) {
1034 | .guide {
1035 | margin: auto;
1036 | }
1037 | }
1038 |
1039 | @media screen and (min-width: 79rem) and (max-width: 100rem) {
1040 | .toc.transition ~ .guide {
1041 | transition: transform cubic-bezier(0.4, 0, 0.2, 1) 0.33s;
1042 | }
1043 | .guide {
1044 | transform: translateX(50vw) translateX(-50%);
1045 | }
1046 | .scroll-restore-message {
1047 | margin: 0;
1048 | left: 0;
1049 | transform: translateX(50vw) translateX(-50%) translateY(100%) translateY(1rem);
1050 | }
1051 | .scroll-restore-message.in {
1052 | transform: translateX(50vw) translateX(-50%);
1053 | }
1054 | .toc.in ~ .guide, .toc.in ~ .scroll-restore-message.in {
1055 | transform: translateX(29rem);
1056 | }
1057 | .toc.in ~ .scroll-restore-message {
1058 | transform: translateX(29rem) translateY(100%) translateY(1rem);
1059 | }
1060 | }
1061 |
1062 | @media screen and (max-width: 79rem) {
1063 | .guide {
1064 | margin: auto;
1065 | }
1066 | .toc-scrim {
1067 | display: block !important;
1068 | transition: opacity cubic-bezier(0.4, 0, 1, 1) 0.2s;
1069 | }
1070 | }
1071 |
1072 | @media screen and (max-width: 60rem) {
1073 | .toc-fab.scroll-out {
1074 | transform: translateY(-5rem);
1075 | }
1076 | .toc.in .toc-fab.scroll-out {
1077 | transform: translateX(0);
1078 | }
1079 | }
1080 |
1081 | @media screen and (max-width: 29rem) {
1082 | .toc-fab {
1083 | right: -1.5rem;
1084 | top: 0.5rem;
1085 | }
1086 | .toc:not(.in) .toc-fab.scroll-out {
1087 | transform: translate(2.5rem, -5rem);
1088 | }
1089 | .toc:not(.in) .toc-fab:not(.scroll-out) {
1090 | transform: translateX(2.5rem);
1091 | }
1092 | }
1093 |
1094 | @media (pointer: coarse) {
1095 | .toc main > .toc-entry {
1096 | padding-top: 0.75rem;
1097 | padding-bottom: 0.75rem;
1098 | }
1099 | .theme-switcher > .buttons > label {
1100 | padding: 0.75rem !important;
1101 | }
1102 | .btn {
1103 | padding: 0.75rem 1.5rem;
1104 | }
1105 | }
1106 |
1107 | @media print {
1108 | .toc, .toc-fab, .scroll-restore-message, .toc-scrim, .theme-switch-overlay {
1109 | display: none;
1110 | }
1111 | h1, h2, h3, h4, h5, h6 {
1112 | page-break-after: avoid;
1113 | }
1114 | }
1115 |
--------------------------------------------------------------------------------
/css/index.scss:
--------------------------------------------------------------------------------
1 | @import "./_fonts.scss";
2 | @import "./_themes.scss";
3 |
4 |
5 | $ease-material-in-out: cubic-bezier(0.4, 0.0, 0.2, 1);
6 | $ease-material-in: cubic-bezier(0.4, 0.0, 1, 1);
7 | $ease-material-out: cubic-bezier(0.0, 0.0, 0.2, 1);
8 |
9 |
10 | // #region base
11 |
12 | // base class for things simply highlighting on hover, without sticking after touch
13 | @media (hover: hover) {
14 | %standard-hover {
15 | transition: background-color $ease-material-in-out 0.15s;
16 |
17 | &:hover {
18 | background-color: $c-bg-hover;
19 | }
20 | }
21 | }
22 |
23 | // #endregion
24 |
25 |
26 |
27 | // #region layout
28 |
29 |
30 | // layout variables
31 | $guide-max-w: 50rem;
32 | $toc-w: 24rem;
33 | $mobile-threshold: $toc-w + 5rem + $guide-max-w;
34 | $toc-fab-to-border-threshold: $toc-w + 5rem;
35 | $toc-fab-size: 3rem;
36 | $toc-fab-icon-size: 1rem;
37 | $toc-fab-scroll-out-y-offset: 5rem;
38 |
39 | * {
40 | box-sizing: border-box;
41 | }
42 |
43 | html,
44 | body {
45 | width: 100%;
46 | height: 100%;
47 | padding: 0;
48 | margin: 0;
49 | }
50 |
51 | .guide {
52 | width: 100%;
53 | max-width: $guide-max-w;
54 | padding: 0 1rem;
55 | padding-bottom: 15rem;
56 | }
57 |
58 |
59 | // #region header layout
60 |
61 | // headers use padding-top for spacing so scrolling to a section via offsetTop leaves a nice gap
62 | h1 {
63 | font-size: 2.5rem;
64 | margin: 2.5rem 0;
65 | margin-top: 6rem;
66 |
67 | > .small {
68 | margin-top: 1rem;
69 | }
70 | }
71 |
72 | h2 {
73 | font-size: 2rem;
74 | margin: 0;
75 | padding-top: 2rem;
76 | }
77 |
78 | h3 {
79 | font-size: 1.5rem;
80 | margin: 0;
81 | padding-top: 2.5rem;
82 | }
83 |
84 | h4 {
85 | font-size: 1.25rem;
86 | margin: 0;
87 | padding-top: 2rem;
88 | }
89 |
90 | // #endregion
91 |
92 |
93 | // #region spacing
94 |
95 | // spacing between 1. and 1.1. headings
96 | h2 + h3 {
97 | padding-top: 1.5rem;
98 | }
99 |
100 | // spacing between header and first section item
101 | h1, h2, h3, h4 {
102 | + p, + .snippet {
103 | margin-top: 2rem;
104 | }
105 | }
106 |
107 | // spacing between paragraphs
108 | p, blockquote {
109 | margin-top: 1.5rem;
110 | line-height: 140%;
111 | }
112 |
113 | // spacing before snippets
114 | .snippet {
115 | margin-top: 0.5rem;
116 | }
117 |
118 | // spacing between list items
119 | ul, ol {
120 | line-height: 140%;
121 |
122 | > li + li {
123 | margin-top: 0.5rem;
124 | }
125 | }
126 |
127 | // #endregion
128 |
129 |
130 | // #region toc
131 |
132 | .toc {
133 | position: fixed;
134 | top: 0;
135 | left: 0;
136 | bottom: 0;
137 | z-index: 2;
138 | width: $toc-w;
139 | max-width: calc(100vw - 3rem);
140 |
141 | }
142 |
143 | // toc position when expanded
144 | .toc.in {
145 | transform: translateX(0);
146 | }
147 |
148 | // toc position when collapsed
149 | .toc:not(.in) {
150 | transform: translateX(-100%);
151 | }
152 |
153 | // position fab 1rem from the top of the page and 1rem to the right of the guide
154 | .toc-fab {
155 | position: fixed;
156 | top: 1rem;
157 | right: -1rem - $toc-fab-size;
158 | z-index: 2;
159 | }
160 |
161 | // #endregion
162 |
163 |
164 | // #endregion
165 |
166 |
167 |
168 | // #region styling
169 |
170 |
171 | // #region toc
172 |
173 | .toc {
174 | display: flex;
175 | flex-direction: column;
176 | background-color: $c-bg;
177 | vertical-align: top;
178 | border-right: 1px solid $c-border-light;
179 | text-align: left;
180 |
181 | // class added by js to only animate after initial state was set
182 | &.transition {
183 | transition: transform $ease-material-in-out 0.33s;
184 | }
185 |
186 | > header {
187 | display: flex;
188 | align-items: center;
189 | width: 100%;
190 | border-bottom: 1px solid $c-border-light;
191 | padding: 1rem;
192 | font-family: $font-family-heading;
193 | font-size: 1.5rem;
194 | }
195 |
196 | > main {
197 | padding: 0.5rem;
198 | flex: 1;
199 | overflow-y: auto;
200 | overscroll-behavior-y: contain;
201 |
202 | > .toc-entry {
203 | @extend %standard-hover;
204 | display: block;
205 | text-decoration: none;
206 | color: $c-text-on-bg-sec;
207 | padding: 0.5rem;
208 | -webkit-tap-highlight-color: transparent;
209 | border-radius: 0.25rem;
210 |
211 | > .number {
212 | color: $c-text-on-bg-irr;
213 | }
214 | }
215 |
216 | > .toc-1 {
217 | padding-left: 1rem;
218 | }
219 |
220 | > .toc-2 {
221 | padding-left: 2rem;
222 | }
223 | }
224 |
225 | > footer {
226 | padding: 1rem;
227 | padding-top: 0.5rem;
228 | border-top: 1px solid $c-border-light;
229 |
230 | > .theme-switcher {
231 | margin-top: 0;
232 | }
233 | }
234 | }
235 |
236 | .toc-fab {
237 | width: $toc-fab-size;
238 | height: $toc-fab-size;
239 | font-size: $toc-fab-icon-size;
240 | border-radius: 1.5rem;
241 | border: 1px solid $c-border-light;
242 | color: $c-text-on-bg-pri;
243 | background-color: $c-bg;
244 | -webkit-tap-highlight-color: transparent;
245 | outline: none;
246 | cursor: pointer;
247 | overflow: hidden;
248 | transition: transform $ease-material-in-out 0.33s;
249 |
250 | // add a separate element responsible for just animating on hover
251 | // otherwise, switching the theme causes the fab bg colour to animate
252 | // and it looks very bad
253 | &:before {
254 | @extend %standard-hover;
255 | content: "";
256 | position: absolute;
257 | top: 0;
258 | left: 0;
259 | right: 0;
260 | bottom: 0;
261 | }
262 |
263 | > .top, > .mid, > .bot {
264 | position: absolute;
265 | top: 50%;
266 | left: 50%;
267 | width: $toc-fab-icon-size;
268 | height: 0.1rem;
269 | background-color: $c-text-on-bg-pri;
270 | }
271 |
272 | > .top {
273 | transform: translate(-50%, -50%) translateY(-$toc-fab-icon-size / 3);
274 | }
275 |
276 | > .mid {
277 | transform: translate(-50%, -50%);
278 | }
279 |
280 | > .bot {
281 | transform: translate(-50%, -50%) translateY($toc-fab-icon-size / 3);
282 | }
283 | }
284 |
285 | .toc.transition .toc-fab {
286 | > .top, > .mid, > .bot {
287 | transition: transform $ease-material-in-out 0.15s;
288 | }
289 | }
290 |
291 | .toc.in .toc-fab {
292 |
293 | > .top {
294 | transform: translate(-25%, -50%) translateY(-$toc-fab-icon-size / 3) translate(-33%, $toc-fab-icon-size / 5.5) rotate(-45deg) scaleX(0.5);
295 | }
296 |
297 | > .mid {
298 | transform: translate(-50%, -50%) scaleX(0);
299 | }
300 |
301 | > .bot {
302 | transform: translate(-25%, -50%) translateY($toc-fab-icon-size / 3) translate(-33%, -$toc-fab-icon-size / 5.5) rotate(45deg) scaleX(0.5);
303 | }
304 |
305 | }
306 |
307 | .toc-scrim {
308 | position: fixed;
309 | top: 0;
310 | left: 0;
311 | bottom: 0;
312 | right: 0;
313 | z-index: 1;
314 | background-color: $c-bg;
315 | opacity: 0;
316 | pointer-events: none;
317 | display: none;
318 | -webkit-tap-highlight-color: transparent;
319 | }
320 |
321 | .toc.in ~ .toc-scrim {
322 | opacity: 0.9;
323 | pointer-events: auto;
324 | }
325 |
326 | // #endregion
327 |
328 |
329 | // #region basic component styling
330 |
331 | body {
332 | font-family: $font-family-body;
333 | font-size: $font-size-base;
334 | color: $c-text-on-bg-sec;
335 | background-color: $c-bg;
336 | line-height: 1.25;
337 | }
338 |
339 | .guide {
340 | text-align: justify;
341 | }
342 |
343 |
344 | // header styles
345 | h1, h2, h3, h4, h5, h6 {
346 | font-family: $font-family-heading;
347 | font-weight: 700;
348 | color: $c-text-on-bg-pri;
349 | text-align: left;
350 |
351 | > a {
352 | color: inherit;
353 | text-decoration: none;
354 |
355 | &:hover {
356 | text-decoration: underline;
357 | }
358 | }
359 | }
360 |
361 | // styling for that tiny text below the main header
362 | .small {
363 | font-size: $font-size-small;
364 | font-weight: normal;
365 | color: $c-text-on-bg-tet;
366 | font-family: $font-family-body;
367 | }
368 |
369 | .guide > footer {
370 | margin-top: 4rem;
371 | border-top: 1px dashed $c-border-light;
372 | }
373 |
374 | // there is only one image at the moment, at the very start
375 | .image-container {
376 | width: 100%;
377 |
378 | img {
379 | display: block;
380 | max-width: 100%;
381 | padding: 1rem;
382 | }
383 |
384 | label {
385 | margin-top: -0.5rem;
386 | display: block;
387 | text-align: center;
388 | font-size: $font-size-small;
389 | color: $c-text-on-bg-tet;
390 | font-style: italic;
391 | }
392 | }
393 |
394 | // recolour links
395 | a {
396 | color: $c-text-link;
397 | }
398 |
399 | blockquote {
400 | font-style: italic;
401 | border-left: 2px solid $c-border-light;
402 | margin-left: 0;
403 | padding-left: 1rem;
404 | padding-top: 0.5rem;
405 | padding-bottom: 0.5rem;
406 | }
407 |
408 | // #endregion
409 |
410 |
411 | // #region section counters
412 |
413 | .guide {
414 | counter-reset: h2 h3 h4;
415 | }
416 |
417 | h2 {
418 | counter-increment: h2;
419 | counter-reset: h3;
420 |
421 | &:before {
422 | content: counter(h2) ". ";
423 | }
424 | }
425 |
426 | h3 {
427 | counter-increment: h3;
428 | counter-reset: h4;
429 |
430 | &:before {
431 | content: counter(h2) "." counter(h3) ". ";
432 | }
433 | }
434 |
435 | h4 {
436 | counter-increment: h4;
437 |
438 | &:before {
439 | content: counter(h2) "." counter(h3) "." counter(h4) ". ";
440 | }
441 | }
442 |
443 | // #endregion
444 |
445 |
446 | // #region code & highlighting
447 |
448 | // inline code bits
449 | code {
450 | display: inline-block;
451 | font-family: $font-family-mono;
452 | font-size: $font-size-code;
453 | border: 1px solid $c-border-light;
454 | // background-color: $c-code-bg;
455 | border-radius: 0.25em;
456 | padding: 0 0.35em;
457 | margin: 0 0.2em;
458 | word-break: break-all;
459 | text-align: left;
460 | font-style: normal;
461 |
462 | > pre {
463 | margin: 0;
464 | white-space: pre-wrap;
465 | }
466 | }
467 |
468 | // light mode highlights using background color
469 | :root:not(.dark) {
470 |
471 | .hl {
472 | position: relative;
473 | z-index: -1;
474 | // display: inline-block;
475 | line-height: 1;
476 | white-space: pre-wrap;
477 | padding: 0.1rem;
478 | margin: -0.1rem;
479 |
480 | + .hl {
481 | margin-left: 0.1rem;
482 | }
483 | }
484 |
485 | @for $i from 1 to 7 {
486 | .hl-#{$i} {
487 | background-color: var(--c-hl-#{$i});
488 |
489 | &.strong {
490 | background-color: var(--c-hl-#{$i}-strong);
491 | }
492 | }
493 | }
494 |
495 | .hl-before {
496 | background-color: var(--c-hl-before);
497 | }
498 |
499 | .hl-after {
500 | background-color: var(--c-hl-after);
501 | }
502 |
503 | .hl-error {
504 | background-color: var(--c-hl-error);
505 | }
506 |
507 | .hl-true {
508 | background-color: var(--c-hl-true);
509 |
510 | &.strong {
511 | background-color: var(--c-hl-true-strong);
512 | }
513 | }
514 |
515 | .hl-false {
516 | background-color: var(--c-hl-false);
517 |
518 | &.strong {
519 | background-color: var(--c-hl-false-strong);
520 | }
521 | }
522 |
523 | .hl-gray {
524 | background-color: var(--c-hl-gray);
525 |
526 | &.strong {
527 | background-color: var(--c-hl-gray-strong);
528 | }
529 | }
530 | }
531 |
532 | // dark mode highlights using text color
533 | :root.dark {
534 |
535 | @for $i from 1 to 7 {
536 | .hl-#{$i} {
537 | color: var(--c-hl-#{$i});
538 |
539 | &.strong {
540 | color: var(--c-hl-#{$i}-strong);
541 | }
542 | }
543 | }
544 |
545 | .hl-before {
546 | color: var(--c-hl-before);
547 | }
548 |
549 | .hl-after {
550 | color: var(--c-hl-after);
551 | }
552 |
553 | .hl-error {
554 | color: var(--c-hl-error);
555 | }
556 |
557 | .hl-true {
558 | color: var(--c-hl-true);
559 |
560 | &.strong {
561 | color: var(--c-hl-true-strong);
562 | }
563 | }
564 |
565 | .hl-false {
566 | color: var(--c-hl-false);
567 |
568 | &.strong {
569 | color: var(--c-hl-false-strong);
570 | }
571 | }
572 |
573 | .hl-gray {
574 | color: var(--c-hl-gray);
575 |
576 | &.strong {
577 | color: var(--c-hl-gray-strong);
578 | }
579 | }
580 |
581 | .hl.strong {
582 | font-weight: bold;
583 | }
584 |
585 | }
586 |
587 | // #endregion
588 |
589 |
590 | // #region snippets
591 |
592 | .snippet {
593 | margin-top: 1rem;
594 |
595 | label {
596 | display: block;
597 | position: sticky;
598 | left: 0;
599 | font-size: $font-size-small;
600 | color: $c-text-on-bg-tet;
601 | line-height: 1;
602 | padding: 0.4rem 0.5rem;
603 |
604 | > .error,
605 | > .success {
606 |
607 | &:before {
608 | content: "-";
609 | margin: 0 0.25rem;
610 | color: $c-text-on-bg-tet;
611 | }
612 |
613 | font-style: italic;
614 | }
615 |
616 | > .error {
617 | color: $c-text-error;
618 | }
619 |
620 | > .success {
621 | color: $c-text-success;
622 | }
623 |
624 | &.output {
625 | &:before {
626 | content: "Output";
627 | }
628 | }
629 | }
630 |
631 | > code, .step > code {
632 | display: block;
633 | border: 1px solid $c-border-light;
634 | padding: 0.5rem 0.75rem;
635 | line-height: 1.5;
636 | margin: 0;
637 | overflow: hidden;
638 | position: relative;
639 |
640 | &:empty {
641 | user-select: none;
642 |
643 | &:before {
644 | content: " ";
645 | white-space: pre;
646 | }
647 | }
648 |
649 | + code {
650 | margin-top: 0.2rem;
651 | }
652 |
653 | + label {
654 | margin-top: 0.5rem;
655 | }
656 |
657 | &.copied {
658 | &:after {
659 | content: "";
660 | background-color: $c-bg-copied;
661 | position: absolute;
662 | top: 50%;
663 | left: 50%;
664 | z-index: 1;
665 | width: 50%;
666 | height: 1000%;
667 | transform-origin: center;
668 | animation: 0.5s 1 forwards copied;
669 | animation-timing-function: $ease-material-out;
670 |
671 | @keyframes copied {
672 | $rot: 20deg;
673 | $tr: 80%;
674 |
675 | 0% {
676 | transform: translateX(-50%) translateX(Min($guide-max-w / 2, 50vw)) translateX($tr) translateY(-50%) translateX(-50%) rotate($rot);
677 | }
678 |
679 | 100% {
680 | transform: translateX(-50%) translateX(Max(-$guide-max-w / 2, -50vw)) translateX(-$tr) translateY(-50%) translateX(-50%) rotate($rot);
681 | }
682 | }
683 | }
684 | }
685 | }
686 |
687 | > .step {
688 |
689 | .arrow {
690 | display: flex;
691 | align-items: center;
692 | justify-content: start;
693 | padding: 0 0.5rem;
694 | font-size: 1.4rem;
695 | margin-top: -0.25em;
696 | margin-bottom: -0.25em;
697 | line-height: 1;
698 |
699 | &::before {
700 | content: "↓";
701 | color: $c-text-on-bg-tet;
702 | }
703 | }
704 |
705 | + label {
706 | margin-top: 0.5rem;
707 | }
708 | }
709 |
710 | > .note {
711 | font-size: $font-size-small;
712 | color: $c-text-on-bg-tet;
713 | margin-top: 0.5rem;
714 | font-style: italic;
715 | padding: 0 0.5rem;
716 |
717 | + label {
718 | margin-top: 0.5rem;
719 | }
720 | }
721 |
722 | + .snippet {
723 | margin-top: 1.5rem;
724 | }
725 | }
726 |
727 | // #endregion
728 |
729 |
730 | // #region tables
731 |
732 | .table-container {
733 | width: calc(100% + 16px);
734 | padding: 8px;
735 | margin: 0 -8px;
736 | overflow-x: auto;
737 | }
738 |
739 | table {
740 | border-spacing: 0;
741 | border-collapse: separate;
742 | border-radius: 0.25rem;
743 | border: 1px solid $c-border-light;
744 | text-align: left;
745 |
746 | thead > tr:last-child > th {
747 | border-bottom: 1px solid $c-border-heavy;
748 | }
749 |
750 | tr:first-child {
751 |
752 | th,
753 | td {
754 | border-top: none;
755 | }
756 | }
757 |
758 | th,
759 | td {
760 | padding: 0.5rem 0.75rem;
761 | border-top: 1px solid $c-border-light;
762 | border-left: 1px solid $c-border-light;
763 |
764 | &:first-child {
765 | border-left: none;
766 | }
767 | }
768 |
769 | code {
770 | white-space: nowrap;
771 | }
772 | }
773 |
774 | #math-symbol-table {
775 |
776 | td,
777 | th {
778 | &:first-child {
779 | text-align: center;
780 | }
781 |
782 | &:nth-child(2) {
783 | width: 100%;
784 | }
785 | }
786 | }
787 |
788 | #string-mode-table {
789 |
790 | td,
791 | th {
792 | &:nth-child(1) {
793 | text-align: center;
794 | }
795 |
796 | &:nth-child(2) {
797 | width: 100%;
798 | }
799 | }
800 | }
801 |
802 | #comparison-operator-table {
803 |
804 | td,
805 | th {
806 | &:nth-child(1) {
807 | text-align: center;
808 | }
809 |
810 | &:nth-child(2) {
811 | width: 100%;
812 | }
813 | }
814 | }
815 |
816 | :root.light {
817 |
818 | #comparison-operator-table {
819 |
820 | td,
821 | th {
822 | &:nth-child(3) {
823 | background-color: var(--c-hl-true);
824 | }
825 |
826 | &:nth-child(4) {
827 | background-color: var(--c-hl-false);
828 | }
829 | }
830 | }
831 |
832 | }
833 |
834 | :root:not(.light) {
835 | #comparison-operator-table {
836 |
837 | td,
838 | th {
839 | &:nth-child(3) {
840 | color: var(--c-hl-true);
841 | }
842 |
843 | &:nth-child(4) {
844 | color: var(--c-hl-false);
845 | }
846 | }
847 | }
848 | }
849 |
850 | #logical-operator-table {
851 |
852 | td,
853 | th {
854 | width: 80px;
855 | text-align: center;
856 |
857 | &:nth-child(3),
858 | &:nth-child(4) {
859 | border-left: 1px solid $c-border-heavy;
860 | }
861 | }
862 | }
863 |
864 | #tf-token-table {
865 | tr {
866 | &:nth-child(4) {
867 | td {
868 | border-top: 1px solid $c-border-heavy;
869 | }
870 | }
871 | }
872 |
873 | td,
874 | th {
875 | &:nth-child(1) {
876 | text-align: center;
877 | }
878 |
879 | &:nth-child(2) {
880 | width: 100%;
881 | }
882 | }
883 | }
884 |
885 | // #endregion
886 |
887 |
888 | // #region theme switcher
889 |
890 | .theme-switcher {
891 |
892 | > label {
893 | display: block;
894 | font-size: $font-size-small;
895 | color: $c-text-on-bg-tet;
896 |
897 | + .buttons {
898 | margin-top: 0.5rem;
899 | }
900 | }
901 |
902 | > .buttons {
903 | display: flex;
904 | flex-direction: row;
905 | gap: 0.5rem;
906 | margin-top: 1rem;
907 | -webkit-tap-highlight-color: transparent;
908 |
909 | > label {
910 | flex: 1;
911 | padding: 0.5rem;
912 | text-align: center;
913 | position: relative;
914 | transition: background-color $ease-material-in-out 0.1s, transform $ease-material-in-out 0.33s;
915 | cursor: pointer;
916 |
917 | > input {
918 | display: none;
919 | }
920 |
921 | > span {
922 | &:before {
923 | content: "";
924 | position: absolute;
925 | top: 0;
926 | left: 0;
927 | bottom: 0;
928 | right: 0;
929 | border-radius: 0.25rem;
930 | border: 1px solid $c-border-light;
931 | // overshoot
932 | transition: border cubic-bezier(0.87, 1.92, 0.48, 0.75) 0.15s;
933 | }
934 | }
935 |
936 | @media (hover: hover) {
937 | &:hover {
938 | background-color: $c-bg-hover;
939 | }
940 | }
941 |
942 | &:active {
943 | transition-duration: 0s;
944 | transform: scale(0.96);
945 | }
946 |
947 | > input:checked + span {
948 | &:before {
949 | border: 0.15rem solid $c-accent;
950 | }
951 | }
952 | }
953 | }
954 | }
955 |
956 | .theme-switch-overlay {
957 | position: fixed;
958 | top: 0;
959 | left: 0;
960 | width: 100vw;
961 | bottom: 0;
962 | z-index: 10;
963 | transform: translateY(-100%);
964 | opacity: 1;
965 | transition: transform $ease-material-in 0.25s;
966 |
967 | &.dark {
968 | background-color: $c-dark-bg;
969 | }
970 |
971 | &.light {
972 | background-color: $c-light-bg;
973 | }
974 |
975 | &.in {
976 | transform: translateY(0);
977 | }
978 |
979 | &.out {
980 | transform: translateY(100%);
981 | transition-timing-function: $ease-material-out;
982 | pointer-events: none;
983 | }
984 | }
985 |
986 | // #endregion
987 |
988 |
989 | // #region scroll restore
990 |
991 | .scroll-restore-message {
992 | position: fixed;
993 | bottom: 1rem;
994 | left: 1rem;
995 | right: 1rem;
996 | max-width: $guide-max-w;
997 | margin: auto;
998 | display: flex;
999 | flex-direction: row;
1000 | justify-content: space-between;
1001 | flex-wrap: wrap;
1002 | align-items: center;
1003 | border: 1px solid $c-border-heavy;
1004 | background-color: $c-bg;
1005 | border-radius: 0.25rem;
1006 | transform: translateY(100%) translateY(1rem);
1007 | transition: transform $ease-material-in-out 0.33s;
1008 | -webkit-tap-highlight-color: transparent;
1009 | cursor: pointer;
1010 |
1011 | > span {
1012 | display: inline-block;
1013 | padding: 1rem;
1014 | }
1015 |
1016 | > .btn-dismiss {
1017 | @extend %standard-hover;
1018 | background: none;
1019 | border: none;
1020 | outline: none;
1021 | margin-left: auto;
1022 | font-family: $font-family-body;
1023 | font-size: $font-size-base;
1024 | color: $c-text-on-bg-tet;
1025 | -webkit-tap-highlight-color: transparent;
1026 | padding: 1rem;
1027 | cursor: pointer;
1028 | }
1029 |
1030 | &.in {
1031 | transform: translateY(0);
1032 | }
1033 | }
1034 |
1035 | // #endregion
1036 |
1037 |
1038 | // #region buttons
1039 |
1040 | .btn {
1041 | @extend %standard-hover;
1042 | background: none;
1043 | border: 1px solid $c-border-heavy;
1044 | border-radius: 0.25rem;
1045 | outline: none;
1046 | font-family: $font-family-body;
1047 | font-size: $font-size-base;
1048 | color: $c-text-on-bg-sec;
1049 | -webkit-tap-highlight-color: transparent;
1050 | padding: 0.5rem 1rem;
1051 | cursor: pointer;
1052 | }
1053 |
1054 | // #endregion
1055 |
1056 |
1057 | // #endregion
1058 |
1059 |
1060 |
1061 | // #region misc
1062 |
1063 | .nowrap {
1064 | white-space: nowrap;
1065 | }
1066 |
1067 | .no-display {
1068 | display: none;
1069 | }
1070 |
1071 | .text-right {
1072 | text-align: right;
1073 | }
1074 |
1075 | .text-center {
1076 | text-align: center;
1077 | }
1078 |
1079 | // #endregion
1080 |
1081 |
1082 | // #region responsiveness
1083 |
1084 | // larger than 1600px at 16px font size (max desktop)
1085 | @media screen and (min-width: 100rem) {
1086 |
1087 | // center the guide on the screen
1088 | .guide {
1089 | margin: auto;
1090 | }
1091 |
1092 | }
1093 |
1094 | // less than 1600px at 16px, but can fit both toc and the guide side by side
1095 | @media screen and (min-width: $mobile-threshold) and (max-width: 100rem) {
1096 |
1097 | .toc.transition ~ .guide {
1098 | transition: transform $ease-material-in-out 0.33s;
1099 | }
1100 |
1101 | // center the guide on the screen by default
1102 | .guide {
1103 | transform: translateX(50vw) translateX(-50%);
1104 | }
1105 |
1106 | .scroll-restore-message {
1107 | margin: 0;
1108 | left: 0;
1109 | transform: translateX(50vw) translateX(-50%) translateY(100%) translateY(1rem);
1110 |
1111 | &.in {
1112 | transform: translateX(50vw) translateX(-50%);
1113 | }
1114 | }
1115 |
1116 | // position the guide to the right of the toc when it is expanded
1117 | .toc.in {
1118 |
1119 | ~ .guide, ~ .scroll-restore-message.in {
1120 | transform: translateX($toc-w + 5rem);
1121 | }
1122 |
1123 | ~ .scroll-restore-message {
1124 | transform: translateX($toc-w + 5rem) translateY(100%) translateY(1rem);
1125 | }
1126 | }
1127 |
1128 | }
1129 |
1130 | // mobile (toc is overlaid as a left drawer)
1131 | @media screen and (max-width: $mobile-threshold) {
1132 |
1133 | // center the guide on the screen
1134 | .guide {
1135 | margin: auto;
1136 | }
1137 |
1138 | // stop no-displaying the toc scrim
1139 | .toc-scrim {
1140 | display: block !important;
1141 | transition: opacity $ease-material-in 0.2s;
1142 | }
1143 | }
1144 |
1145 | // the fab will start to overlap the content at this point
1146 | @media screen and (max-width: ($guide-max-w + 10rem)) {
1147 |
1148 | // js applies this class to hide the fab when scrolling down
1149 | .toc-fab {
1150 | &.scroll-out {
1151 | transform: translateY(-5rem);
1152 | }
1153 | }
1154 |
1155 | // if the toc is expanded, override the fab being hidden due to scrolling down
1156 | .toc.in .toc-fab.scroll-out {
1157 | transform: translateX(0);
1158 | }
1159 |
1160 | }
1161 |
1162 | // the drawer will start to decrease in width at this point
1163 | @media screen and (max-width: $toc-fab-to-border-threshold) {
1164 |
1165 | // move fab so it overlaps the right border of the toc
1166 | .toc-fab {
1167 | right: -1.5rem;
1168 | top: 0.5rem;
1169 | }
1170 |
1171 | // if the toc is collapsed,
1172 | .toc:not(.in) .toc-fab {
1173 |
1174 | // hide the fab when scrolling up and also move it to the right so it no longer overlaps the border of the toc
1175 | &.scroll-out {
1176 | transform: translate(2.5rem, -$toc-fab-scroll-out-y-offset);
1177 | }
1178 |
1179 | // otherwise only move it to the right so it no longer overlaps the border of the toc
1180 | &:not(.scroll-out) {
1181 | transform: translateX(2.5rem);
1182 | }
1183 | }
1184 |
1185 | }
1186 |
1187 | // expand touch targets for touchscreens
1188 | @media (pointer: coarse) {
1189 | $p: 0.75rem;
1190 |
1191 | .toc main > .toc-entry {
1192 | padding-top: $p;
1193 | padding-bottom: $p;
1194 | }
1195 |
1196 | .theme-switcher {
1197 | > .buttons {
1198 | > label {
1199 | padding: $p !important;
1200 | }
1201 | }
1202 | }
1203 |
1204 | .btn {
1205 | padding: $p 1.5rem;
1206 | }
1207 | }
1208 |
1209 | @media print {
1210 | .toc, .toc-fab, .scroll-restore-message, .toc-scrim, .theme-switch-overlay {
1211 | display: none;
1212 | }
1213 |
1214 | h1, h2, h3, h4, h5, h6 {
1215 | page-break-after: avoid;
1216 | }
1217 | }
1218 |
1219 | // #endregion
1220 |
--------------------------------------------------------------------------------