first commit

This commit is contained in:
yann 2025-06-25 14:09:10 +02:00
commit 04a0c6f6b9
66 changed files with 9233 additions and 0 deletions

166
.eleventy.js Normal file
View File

@ -0,0 +1,166 @@
const { format, formatISO, getYear } = require("date-fns");
const pluginRss = require("@11ty/eleventy-plugin-rss");
const pluginToc = require("eleventy-plugin-toc");
const { MD5 } = require("crypto-js");
const { URL } = require("url");
const { readFileSync } = require("fs");
const siteconfig = require("./content/_data/siteconfig.js");
const markdownIt = require("markdown-it");
const markdownItAnchor = require("markdown-it-anchor");
module.exports = function (eleventyConfig) {
// Set Markdown library
eleventyConfig.setLibrary(
"md",
markdownIt({
html: true,
xhtmlOut: true,
linkify: true,
typographer: true
}).use(markdownItAnchor)
);
// Define passthrough for assets
eleventyConfig.addPassthroughCopy("assets");
// Add watch target for JS files (needed for JS bundling in dev mode)
eleventyConfig.addWatchTarget("./assets/js/");
// And to make this work we've to disable the .gitignore usage of eleventy.
eleventyConfig.setUseGitIgnore(false);
// Add 3rd party plugins
eleventyConfig.addPlugin(pluginRss);
eleventyConfig.addPlugin(pluginToc);
// Define 11ty template formats
eleventyConfig.setTemplateFormats([
"njk",
"md",
"svg",
"jpg",
"css",
"png"
]);
// Generate excerpt from first paragraph
eleventyConfig.addShortcode("excerpt", (article) =>
extractExcerpt(article)
);
// Set absolute url
eleventyConfig.addNunjucksFilter("absoluteUrl", (path) => {
return new URL(path, siteconfig.url).toString();
});
// Extract reading time
eleventyConfig.addNunjucksFilter("readingTime", (wordcount) => {
let readingTime = Math.ceil(wordcount / 220);
if (readingTime === 1) {
return readingTime + " minute";
}
return readingTime + " minutes";
});
// Extract word count
eleventyConfig.addNunjucksFilter("formatWords", (wordcount) => {
return wordcount.toLocaleString("en");
});
// Returns CSS class for home page link
eleventyConfig.addNunjucksFilter("isHomeLink", function (url, pattern) {
return (pattern === "/" && url === "/") ||
(pattern === "/" && url.startsWith("/posts"))
? "active"
: "";
});
// Returns CSS class for active page link
eleventyConfig.addNunjucksFilter("isActiveLink", function (url, pattern) {
return url.length > 1 && url.startsWith(pattern) ? "active" : "";
});
// Format dates for sitemap
eleventyConfig.addNunjucksFilter("sitemapdate", function (date) {
return format(date, "yyyy-MM-dd");
});
// Format dates for JSON-LD
eleventyConfig.addNunjucksFilter("isodate", function (date) {
return formatISO(date);
});
// Extracts the year from a post
eleventyConfig.addNunjucksFilter("year", function (post) {
if (post && post.date) {
return getYear(post.date);
}
return "n/a";
});
// Extracts the day of a date
eleventyConfig.addNunjucksFilter("day", function (date) {
return format(date, "dd");
});
// Extracts the month of a date
eleventyConfig.addNunjucksFilter("month", function (date) {
return format(date, "MMM");
});
// Extracts readable date of a date
eleventyConfig.addNunjucksFilter("readableDate", function (date) {
return format(date, "MMM dd, yyyy");
});
// Add custom hash for cache busting
const hashes = new Map();
eleventyConfig.addNunjucksFilter("addHash", function (absolutePath) {
const cached = hashes.get(absolutePath);
if (cached) {
return `${absolutePath}?hash=${cached}`;
}
const fileContent = readFileSync(`${process.cwd()}${absolutePath}`, {
encoding: "utf-8"
}).toString();
const hash = MD5(fileContent.toString());
hashes.set(absolutePath, hash);
return `${absolutePath}?hash=${hash}`;
});
// Create custom collection for getting the newest 5 updates
eleventyConfig.addCollection("recents", function (collectionApi) {
return collectionApi.getAllSorted().reverse().slice(0, 5);
});
// Plugin for setting _blank and rel=noopener on external links in markdown content
eleventyConfig.addPlugin(require("./_11ty/external-links.js"));
// Plugin for transforming images
eleventyConfig.addPlugin(require("./_11ty/srcset.js"));
// Plugin for minifying HTML
eleventyConfig.addPlugin(require("./_11ty/html-minify.js"));
return {
dir: {
// Consolidating everything below the `content` folder
input: "content"
}
};
};
// Taken from here => https://keepinguptodate.com/pages/2019/06/creating-blog-with-eleventy/
function extractExcerpt(article) {
if (!Object.prototype.hasOwnProperty.call(article, "templateContent")) {
console.warn(
'Failed to extract excerpt: Document has no property "templateContent".'
);
return null;
}
const content = article.templateContent;
const excerpt = content.slice(0, content.indexOf("\n"));
return excerpt;
}

17
.eslintrc.js Executable file
View File

@ -0,0 +1,17 @@
module.exports = {
extends: ["eslint:recommended", "prettier"],
plugins: ["inclusive-language"],
rules: {
"inclusive-language/use-inclusive-words": "error"
},
parserOptions: {
ecmaVersion: 2020,
sourceType: "module"
},
env: {
browser: true,
node: true,
es6: true
},
ignorePatterns: ["!/.*.js", "_site", "/assets/js/", "node_modules"]
};

44
.gitignore vendored Normal file
View File

@ -0,0 +1,44 @@
# This file is used for Git repositories to specify intentionally untracked files that Git should ignore.
# If you are not using git, you can delete this file. For more information see: https://git-scm.com/docs/gitignore
# For useful gitignore templates see: https://github.com/github/gitignore
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# Dependency directories
node_modules/
# Eslint cache
.eslintcache
# MacOS system files
.DS_Store
# Windows system files
Thumbs.db
ehthumbs.db
[Dd]esktop.ini
$RECYCLE.BIN/
# Local .env file
.env
# Static content
_site/
# Uncomment the following line if you want to commit all images to source control. This will def speed up build time.
# !_site/assets/images
# Temporary file for changed pages
scripts/temp-output/*
# Locally built assets
assets/css/*-build.css
assets/js/min.*
# Local Netlify folder
.netlify

1
.husky/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
_

1
.husky/pre-commit Normal file
View File

@ -0,0 +1 @@
npm run precommit

5
.markdownlint.json Normal file
View File

@ -0,0 +1,5 @@
{
"line-length": false,
"list-marker-space": false,
"no-inline-html": false
}

9
.prettierignore Executable file
View File

@ -0,0 +1,9 @@
# List files or directories below to ignore them when running prettier
# More information: https://prettier.io/docs/en/ignore.html
#
_site/
.netlify/
.vscode/
assets/css/*-build.css
assets/js/*.js

4
.prettierrc Executable file
View File

@ -0,0 +1,4 @@
{
"trailingComma": "none",
"tabWidth": 4
}

45
.vscode/css_custom_data.json vendored Normal file
View File

@ -0,0 +1,45 @@
{
"version": 1.1,
"atDirectives": [
{
"name": "@tailwind",
"description": "Use the `@tailwind` directive to insert Tailwind's `base`, `components`, `utilities` and `screens` styles into your CSS.",
"references": [
{
"name": "Tailwind Documentation",
"url": "https://tailwindcss.com/docs/functions-and-directives#tailwind"
}
]
},
{
"name": "@responsive",
"description": "You can generate responsive variants of your own classes by wrapping their definitions in the `@responsive` directive:\n```css\n@responsive {\n .alert {\n background-color: #E53E3E;\n }\n}\n```\n",
"references": [
{
"name": "Tailwind Documentation",
"url": "https://tailwindcss.com/docs/functions-and-directives#responsive"
}
]
},
{
"name": "@screen",
"description": "The `@screen` directive allows you to create media queries that reference your breakpoints by **name** instead of duplicating their values in your own CSS:\n```css\n@screen sm {\n /* ... */\n}\n```\n…gets transformed into this:\n```css\n@media (min-width: 640px) {\n /* ... */\n}\n```\n",
"references": [
{
"name": "Tailwind Documentation",
"url": "https://tailwindcss.com/docs/functions-and-directives#screen"
}
]
},
{
"name": "@variants",
"description": "Generate `hover`, `focus`, `active` and other **variants** of your own utilities by wrapping their definitions in the `@variants` directive:\n```css\n@variants hover, focus {\n .btn-brand {\n background-color: #3182CE;\n }\n}\n```\n",
"references": [
{
"name": "Tailwind Documentation",
"url": "https://tailwindcss.com/docs/functions-and-directives#variants"
}
]
}
]
}

7
.vscode/extensions.json vendored Normal file
View File

@ -0,0 +1,7 @@
{
"recommendations": [
"DavidAnson.vscode-markdownlint",
"esbenp.prettier-vscode",
"bradlc.vscode-tailwindcss"
]
}

4
.vscode/settings.json vendored Normal file
View File

@ -0,0 +1,4 @@
{
"css.customData": [".vscode/css_custom_data.json"],
"css.validate": false,
}

20
LICENSE.md Normal file
View File

@ -0,0 +1,20 @@
Copyright (c) 2021 René Winkelmeyer and others
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

74
README.md Normal file
View File

@ -0,0 +1,74 @@
# eleventy-chirpy-blog-template
[![Github Workflow](<https://github.com/muenzpraeger/eleventy-chirpy-blog-template/workflows/Blog%20build%20(main)/badge.svg?branch=main>)](https://github.com/muenzpraeger/eleventy-chirpy-blog-template/actions?query=workflow%3A%22Blog+build+%28main%29%22) [![Netlify Status](https://api.netlify.com/api/v1/badges/ceb123c7-d071-495e-b9a7-51d82b38c8a0/deploy-status)](https://app.netlify.com/sites/eleventy-chirpy-blog-template/deploys)
[11ty](https://www.11ty.dev/) version of the popular [Chirpy Jekyll](https://github.com/cotes2020/jekyll-theme-chirpy) blog theme. Also powers [my personal blog](https://blog.winkelmeyer.com). I liked the UX a lot, but not the tech stack, hence I re-built it for myself. Sharing here with everybody, in case you like the same.
You can check out the live version on <https://eleventy-chirpy-blog-template.netlify.app>.
If you want to deploy, there's a button for it: [![Deploy to Netlify](https://www.netlify.com/img/deploy/button.svg)](https://app.netlify.com/start/deploy?repository=https://github.com/muenzpraeger/eleventy-chirpy-blog-template)
## Features
- 💯 on Lighthouse
- 🔆 and 🌛 mode
- 🎯 SEO and OpenGraph optimized
- 🌄 Responsive images optimization
- 👀 Accessible
- 🛠 JavaScript and CSS build optimization
- 👨‍💻 Prism-based syntax highlighting
- 📚 RSS (yup, still a thing), sitemap.xml, and JSON-LD
- 🔍 [Algolia Search](https://github.com/algolia/algoliasearch-netlify) enabled
- and more
Opinionated setup with [Prettier](https://prettier.io/), [ESlint](https://eslint.org/), [markdownlint](https://github.com/DavidAnson/markdownlint) and others. UX build with [Nunjucks](https://mozilla.github.io/nunjucks/templating.html) and [TailwindCSS](https://tailwindcss.com/docs). JavaScript bundled with [Rollup](https://rollupjs.org/).
## Configuration
All blog configuration is handled via [`siteconfig.js`](./content/_data/siteconfig.js). Everything is inline documented.
## Deployment
All build processes rely on how `NODE_ENV` is set. For production builds, which then also means minified CSS and JS you've to set the value to `production`. I mention this explicitly as this is for some vendors not the default.
If you want to speed up your build times a bit you can add the generated images to your git repo. The `.gitignore` already contains a commented section for that.
## Local Development
### Before you install dependencies
This repo uses [Volta](https://volta.sh/). Get it, and it'll make your node life so much easier.
### Instructions
Clone this repository.
```zsh
git clone https://github.com/muenzpraeger/eleventy-chirpy-blog-template
```
Change into the cloned directory.
```zsh
cd eleventy-chirpy-blog-template
```
Install dependencies. Note, if you prefer `npm` over `yarn` make sure to first remove the `yarn.lock` file, and then run `npm install`.
```zsh
yarn install
```
Start the local development process.
```zsh
yarn dev
```
Open the page, usually on <http://localhost:8080>, and dig around!
## Credits
The UX of this template is based on the popular Chirpy template, just with a different tech stack. If you prefer to run Jekyll and Bootstrap, checkout [Chirpy](https://github.com/cotes2020/jekyll-theme-chirpy) here. It's great.
Also big thanks to the the authors of the [11ty High Performance Blog](https://github.com/google/eleventy-high-performance-blog).

39
_11ty/external-links.js Normal file
View File

@ -0,0 +1,39 @@
// Transformer to ensure that non-relative links open in a new window
// and have for SEO reasons `rel="noopener"` set.
const { JSDOM } = require("jsdom");
const siteconfig = require("../content/_data/siteconfig");
const processHrefs = async (el) => {
if (
!el.href.startsWith("/") &&
!el.href.startsWith(siteconfig.url) &&
!el.href.startsWith("about:blank#")
) {
el.target = "_blank";
el.rel = "noopener";
}
};
const convert = async (rawContent, outputPath) => {
let content = rawContent;
if (outputPath && outputPath.endsWith(".html")) {
const dom = new JSDOM(content);
const hrefs = [...dom.window.document.querySelectorAll("a")];
if (hrefs.length > 0) {
await Promise.all(hrefs.map((i) => processHrefs(i)));
content = dom.serialize();
}
}
return content;
};
module.exports = {
initArguments: {},
configFunction: async (eleventyConfig = {}) => {
eleventyConfig.addTransform("externalContentLinks", convert);
}
};

25
_11ty/html-minify.js Normal file
View File

@ -0,0 +1,25 @@
// Transformer to minify HTML output.
const htmlmin = require("html-minifier");
const convert = async (rawContent, outputPath) => {
const content = rawContent;
if (outputPath && outputPath.endsWith(".html")) {
const minified = htmlmin.minify(content, {
useShortDoctype: true,
removeComments: true,
collapseWhitespace: true
});
return minified;
}
return content;
};
module.exports = {
initArguments: {},
configFunction: async (eleventyConfig = {}) => {
eleventyConfig.addTransform("minifyHTML", convert);
}
};

162
_11ty/srcset.js Normal file
View File

@ -0,0 +1,162 @@
/**
* Copyright (c) 2020 Google Inc
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
// Transformer to set `srcset` for all images, and to create respective
// image versions in different formats and resolutions.
// Modification of original script from https://github.com/google/eleventy-high-performance-blog
const { JSDOM } = require("jsdom");
const sharp = require("sharp");
const { copyFileSync, existsSync, mkdirSync, readFileSync } = require("fs");
const { MD5 } = require("crypto-js");
const { extname, join } = require("path");
const widths = [1024, 820, 640, 320];
const extension = {
jpeg: "jpg",
webp: "webp"
};
// Map filenames to types and width, and then resize
async function srcset(filename, hash, format, metadataWidth) {
// Create a map of all file names
const names = await Promise.all(
widths.map((width) =>
resize(filename, width, hash, format, metadataWidth)
)
);
return names.map((n, i) => `${n} ${widths[i]}w`).join(", ");
}
async function resize(filename, width, hash, format, metadataWidth) {
const out = sizedName(filename, width, hash, format);
if (existsSync("_site/" + out)) {
return out;
}
const file = join(process.cwd(), filename);
const resizeWidth = metadataWidth < width ? metadataWidth : width;
await sharp(file)
.resize({ width: resizeWidth })
[format]({
quality: 80,
reductionEffort: 6
})
.toFile("_site/" + out);
return out;
}
function sizedName(filename, width, hash, format) {
const ext = extension[format];
if (!ext) {
throw new Error(`Unknown format ${format}`);
}
return filename.replace(
/\.\w+$/,
() => "-" + hash + "-" + width + "w." + ext
);
}
function hashedName(filename, hash) {
return filename.replace(extname(filename), "-" + hash + extname(filename));
}
async function setSrcset(img, src, hash, format, metadataWidth) {
img.setAttribute("srcset", await srcset(src, hash, format, metadataWidth));
}
const processImage = async (el) => {
const filename = el.getAttribute("src");
if (/^(https?:\/\/|\/\/)/i.test(filename)) {
return;
}
if (extname(filename.toLowerCase()) === ".svg") {
return;
}
const file = join(process.cwd(), filename);
// Generate file hash
const hash = MD5(readFileSync(file).toString());
// Get image metadata
const metadata = await sharp(file).metadata();
el.setAttribute("decoding", "async");
el.setAttribute("loading", "lazy");
el.setAttribute("height", metadata.height);
el.setAttribute("width", metadata.width);
const doc = el.ownerDocument;
const picture = doc.createElement("picture");
const webp = doc.createElement("source");
const jpeg = doc.createElement("source");
await setSrcset(webp, filename, hash, "webp", metadata.width);
webp.setAttribute("type", "image/webp");
await setSrcset(jpeg, filename, hash, "jpeg", metadata.width);
jpeg.setAttribute("type", "image/jpeg");
picture.appendChild(webp);
picture.appendChild(jpeg);
el.parentElement.replaceChild(picture, el);
picture.appendChild(el);
copyFileSync(
join(process.cwd(), filename),
join("_site", hashedName(filename, hash))
);
};
const convert = async (rawContent, outputPath) => {
let content = rawContent;
const targetDirectory = "./_site/assets/images";
if (!existsSync(targetDirectory)) {
mkdirSync(targetDirectory, { recursive: true });
}
if (outputPath && outputPath.endsWith(".html")) {
const dom = new JSDOM(content);
const images = [...dom.window.document.querySelectorAll("img")];
if (images.length > 0) {
await Promise.all(images.map((i) => processImage(i, outputPath)));
content = dom.serialize();
}
}
return content;
};
module.exports = {
initArguments: {},
configFunction: async (eleventyConfig = {}) => {
eleventyConfig.addTransform("imageConversion", convert);
}
};

494
assets/css/prism.css Normal file
View File

@ -0,0 +1,494 @@
/* PrismJS 1.22.0
https://prismjs.com/download.html#themes=prism-tomorrow&languages=markup+css+clike+javascript+apex+sql+typoscript&plugins=line-highlight+line-numbers+show-language+toolbar */
/**
* prism.js tomorrow night eighties for JavaScript, CoffeeScript, CSS and HTML
* Based on https://github.com/chriskempson/tomorrow-theme
* @author Rose Pritchard
*/
html[class="dark"] code[class*="language-"],
html[class="dark"] pre[class*="language-"] {
color: #ccc;
background: none;
font-family: Consolas, Monaco, "Andale Mono", "Ubuntu Mono", monospace;
font-size: 1em;
text-align: left;
white-space: pre;
word-spacing: normal;
word-break: normal;
word-wrap: normal;
line-height: 1.5;
-moz-tab-size: 4;
-o-tab-size: 4;
tab-size: 4;
-webkit-hyphens: none;
-moz-hyphens: none;
-ms-hyphens: none;
hyphens: none;
}
/* Code blocks */
html[class="dark"] pre[class*="language-"] {
padding: 1em;
margin: 0.5em 0;
overflow: auto;
}
html[class="dark"] :not(pre) > code[class*="language-"],
html[class="dark"] pre[class*="language-"] {
background: #2d2d2d;
}
/* Inline code */
html[class="dark"] :not(pre) > code {
padding: 0.3em;
border-radius: 0.3em;
white-space: normal;
background: #2d2d2d;
}
html[class="dark"] .token.comment,
html[class="dark"] .token.block-comment,
html[class="dark"] .token.prolog,
html[class="dark"] .token.doctype,
html[class="dark"] .token.cdata {
color: #999;
}
html[class="dark"] .token.punctuation {
color: #ccc;
}
html[class="dark"] .token.tag,
html[class="dark"] .token.attr-name,
html[class="dark"] .token.namespace,
html[class="dark"] .token.deleted {
color: #e2777a;
}
html[class="dark"] .token.function-name {
color: #6196cc;
}
html[class="dark"] .token.boolean,
html[class="dark"] .token.number,
html[class="dark"] .token.function {
color: #f08d49;
}
html[class="dark"] .token.property,
html[class="dark"] .token.class-name,
html[class="dark"] .token.constant,
html[class="dark"] .token.symbol {
color: #f8c555;
}
html[class="dark"] .token.selector,
html[class="dark"] .token.important,
html[class="dark"] .token.atrule,
html[class="dark"] .token.keyword,
html[class="dark"] .token.builtin {
color: #cc99cd;
}
html[class="dark"] .token.string,
html[class="dark"] .token.char,
html[class="dark"] .token.attr-value,
html[class="dark"] .token.regex,
html[class="dark"] .token.variable {
color: #7ec699;
}
html[class="dark"] .token.operator,
html[class="dark"] .token.entity,
html[class="dark"] .token.url {
color: #67cdcc;
}
html[class="dark"] .token.important,
html[class="dark"] .token.bold {
font-weight: bold;
}
html[class="dark"] .token.italic {
font-style: italic;
}
html[class="dark"] .token.entity {
cursor: help;
}
html[class="dark"] .token.inserted {
color: green;
}
html:not(.dark) code[class*="language-"],
html:not(.dark) pre[class*="language-"] {
color: black;
background: none;
font-family: Consolas, Monaco, "Andale Mono", "Ubuntu Mono", monospace;
font-size: 1em;
text-align: left;
white-space: pre;
word-spacing: normal;
word-break: normal;
word-wrap: normal;
line-height: 1.5;
-moz-tab-size: 4;
-o-tab-size: 4;
tab-size: 4;
-webkit-hyphens: none;
-moz-hyphens: none;
-ms-hyphens: none;
hyphens: none;
}
/* Code blocks */
html:not(.dark) pre[class*="language-"] {
position: relative;
margin: 0.5em 0;
overflow: scroll;
padding: 1em;
}
html:not(.dark) pre[class*="language-"] {
position: relative;
border: 1px solid transparent;
border-left: 4px solid;
border-color: rgba(49, 46, 129);
background-color: #fdfdfd;
background-image: linear-gradient(
transparent 50%,
rgba(69, 142, 209, 0.04) 50%
);
background-size: 3em 3em;
background-origin: content-box;
background-attachment: local;
}
html:not(.dark) code[class*="language-"] {
max-height: inherit;
height: inherit;
display: block;
overflow: auto;
}
/* Inline code */
html:not(.dark) :not(pre) > code[class*="language-"] {
position: relative;
padding: 0.2em;
border-radius: 0.3em;
color: #c92c2c;
border: 1px solid rgba(0, 0, 0, 0.1);
display: inline;
white-space: normal;
}
/* Inline code */
html:not(.dark) :not(pre) > code {
position: relative;
padding: 0.2em;
border-radius: 0.3em;
color: #c92c2c;
border: 1px solid rgba(0, 0, 0, 0.1);
display: inline;
white-space: normal;
background-color: #fefafafa;
}
html:not(.dark) .token.comment,
html:not(.dark) .token.block-comment,
html:not(.dark) .token.prolog,
html:not(.dark) .token.doctype,
html:not(.dark) .token.cdata {
color: #7d8b99;
}
html:not(.dark) .token.punctuation {
color: #5f6364;
}
html:not(.dark) .token.property,
html:not(.dark) .token.tag,
html:not(.dark) .token.boolean,
html:not(.dark) .token.number,
html:not(.dark) .token.function-name,
html:not(.dark) .token.constant,
html:not(.dark) .token.symbol,
html:not(.dark) .token.deleted {
color: #c92c2c;
}
html:not(.dark) .token.selector,
html:not(.dark) .token.attr-name,
html:not(.dark) .token.string,
html:not(.dark) .token.char,
html:not(.dark) .token.function,
html:not(.dark) .token.builtin,
html:not(.dark) .token.inserted {
color: #2f9c0a;
}
html:not(.dark) .token.operator,
html:not(.dark) .token.entity,
html:not(.dark) .token.url,
html:not(.dark) .token.variable {
color: #a67f59;
background: rgba(255, 255, 255, 0.5);
}
html:not(.dark) .token.atrule,
html:not(.dark) .token.attr-value,
html:not(.dark) .token.keyword,
html:not(.dark) .token.class-name {
color: #1990b8;
}
html:not(.dark) .token.regex,
html:not(.dark) .token.important {
color: #e90;
}
html:not(.dark) .language-css .token.string,
html:not(.dark) .style .token.string {
color: #a67f59;
background: rgba(255, 255, 255, 0.5);
}
html:not(.dark) .token.important {
font-weight: normal;
}
html:not(.dark) .token.bold {
font-weight: bold;
}
html:not(.dark) .token.italic {
font-style: italic;
}
html:not(.dark) .token.entity {
cursor: help;
}
html:not(.dark) .token.namespace {
opacity: 0.7;
}
@media screen and (max-width: 767px) {
html:not(.dark) pre[class*="language-"]:before,
html:not(.dark) pre[class*="language-"]:after {
bottom: 14px;
box-shadow: none;
}
}
/* Plugin styles: Line Numbers */
html:not(.dark) pre[class*="language-"].line-numbers.line-numbers {
padding-left: 0;
}
html:not(.dark) pre[class*="language-"].line-numbers.line-numbers code {
padding-left: 3.8em;
}
html:not(.dark)
pre[class*="language-"].line-numbers.line-numbers
.line-numbers-rows {
left: 0;
}
/* Plugin styles: Line Highlight */
html:not(.dark) pre[class*="language-"][data-line] {
padding-top: 0;
padding-bottom: 0;
padding-left: 0;
}
html:not(.dark) pre[data-line] code {
position: relative;
padding-left: 4em;
}
html:not(.dark) pre .line-highlight {
margin-top: 0;
}
pre[data-line] {
position: relative;
padding: 1em 0 1em 3em;
}
.line-highlight {
position: absolute;
left: 0;
right: 0;
padding: inherit 0;
margin-top: 1em; /* Same as .prisms padding-top */
background: hsla(24, 20%, 50%, 0.08);
background: linear-gradient(
to right,
hsla(24, 20%, 50%, 0.1) 70%,
hsla(24, 20%, 50%, 0)
);
pointer-events: none;
line-height: inherit;
white-space: pre;
}
@media print {
.line-highlight {
/*
* This will prevent browsers from replacing the background color with white.
* It's necessary because the element is layered on top of the displayed code.
*/
-webkit-print-color-adjust: exact;
color-adjust: exact;
}
}
.line-highlight:before,
.line-highlight[data-end]:after {
content: attr(data-start);
position: absolute;
top: 0.4em;
left: 0.6em;
min-width: 1em;
padding: 0 0.5em;
background-color: hsla(24, 20%, 50%, 0.4);
color: hsl(24, 20%, 95%);
font: bold 65%/1.5 sans-serif;
text-align: center;
vertical-align: 0.3em;
border-radius: 999px;
text-shadow: none;
box-shadow: 0 1px white;
}
.line-highlight[data-end]:after {
content: attr(data-end);
top: auto;
bottom: 0.4em;
}
.line-numbers .line-highlight:before,
.line-numbers .line-highlight:after {
content: none;
}
pre[id].linkable-line-numbers span.line-numbers-rows {
pointer-events: all;
}
pre[id].linkable-line-numbers span.line-numbers-rows > span:before {
cursor: pointer;
}
pre[id].linkable-line-numbers span.line-numbers-rows > span:hover:before {
background-color: rgba(128, 128, 128, 0.2);
}
pre[class*="language-"].line-numbers {
position: relative;
padding-left: 3.8em;
counter-reset: linenumber;
}
pre[class*="language-"].line-numbers > code {
position: relative;
white-space: inherit;
}
.line-numbers .line-numbers-rows {
position: absolute;
pointer-events: none;
top: 0;
font-size: 100%;
left: -3.8em;
width: 3em; /* works for line-numbers below 1000 lines */
letter-spacing: -1px;
border-right: 1px solid #999;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
.line-numbers-rows > span {
display: block;
counter-increment: linenumber;
}
.line-numbers-rows > span:before {
content: counter(linenumber);
color: #999;
display: block;
padding-right: 0.8em;
text-align: right;
}
div.code-toolbar {
position: relative;
}
div.code-toolbar > .toolbar {
position: absolute;
top: 0.3em;
right: 0.2em;
transition: opacity 0.3s ease-in-out;
opacity: 1;
}
div.code-toolbar > .toolbar .toolbar-item {
display: inline-block;
}
div.code-toolbar > .toolbar a {
cursor: pointer;
}
html div.code-toolbar > .toolbar button {
background: none;
border: 0;
color: inherit;
font: inherit;
line-height: normal;
overflow: visible;
padding: 0;
-webkit-user-select: none; /* for button */
-moz-user-select: none;
-ms-user-select: none;
}
html[class="dark"] div.code-toolbar > .toolbar a,
html[class="dark"] div.code-toolbar > .toolbar button,
html[class="dark"] div.code-toolbar > .toolbar span {
color: #bbb;
font-size: 0.8em;
padding: 0 0.5em;
}
html:not(.dark) div.code-toolbar > .toolbar a,
html:not(.dark) div.code-toolbar > .toolbar button,
html:not(.dark) div.code-toolbar > .toolbar span {
color: rgb(40, 40, 40);
font-size: 0.8em;
padding: 0 0.5em;
}
div.code-toolbar > .toolbar a:hover,
div.code-toolbar > .toolbar a:focus,
div.code-toolbar > .toolbar button:hover,
div.code-toolbar > .toolbar button:focus,
div.code-toolbar > .toolbar span:hover,
div.code-toolbar > .toolbar span:focus {
color: inherit;
text-decoration: none;
}

510
assets/css/site.css Normal file
View File

@ -0,0 +1,510 @@
@tailwind base;
@tailwind components;
@tailwind utilities;
img {
@apply rounded-lg shadow-lg block mx-auto h-auto;
}
/* Left sidebar */
.sidebar .navitem {
@apply text-gray-400 hover:text-white px-1;
}
.sidebar .navitem.active {
@apply text-white;
}
div.menu li {
@apply leading-12;
}
div.menu ul > li:last-child > a {
margin-right: -3px;
max-width: calc(100% - 3px);
}
div.menu ul > li:last-child::after {
visibility: hidden;
content: "";
position: relative;
right: -8.8rem;
width: 3px;
background-color: white;
pointer-events: none;
border-right: solid 2px white;
transition: top 0.5s ease;
}
@media screen and (max-width: 1280px) {
div.menu ul > li:last-child::after {
right: -6.8rem;
}
}
div.menu ul > li.active:nth-child(1) ~ li:last-child::after,
div.menu ul > li.navitem:nth-child(1):hover ~ li:last-child::after {
top: -12rem;
visibility: visible;
}
div.menu ul > li.active:nth-child(2) ~ li:last-child::after,
div.menu ul > li.navitem:nth-child(2):hover ~ li:last-child::after {
top: -9rem;
visibility: visible;
}
div.menu ul > li.active:nth-child(3) ~ li:last-child::after,
div.menu ul > li.navitem:nth-child(3):hover ~ li:last-child::after {
top: -6rem;
visibility: visible;
}
div.menu ul > li.active:nth-child(4):last-child::after,
div.menu ul > li.navitem:nth-child(4):last-child:hover::after {
top: -3rem;
visibility: visible;
}
div.menu > * {
transition: transform 0.4s ease;
}
/* SVGs */
html[class="dark"] svg {
fill: #9ca3af;
stroke: #9ca3af;
}
html[class="dark"] .social svg:hover {
fill: white;
stroke: white;
}
html:not(.dark) svg.twitter {
fill: #1da1f2;
stroke: #1da1f2;
}
html:not(.dark) svg.twitter:hover {
fill: #1b90d8;
stroke: #1b90d8;
}
html:not(.dark) svg.facebook {
fill: #4267b2;
stroke: #4267b2;
}
html:not(.dark) svg.facebook:hover {
fill: #375593;
stroke: #375593;
}
html:not(.dark) svg.linkedin {
fill: #0072b1;
stroke: #0072b1;
}
html:not(.dark) svg.linkedin:hover {
fill: #006398;
stroke: #006398;
}
html[class="dark"] svg.darktoggle {
@apply hidden;
}
html:not(.dark) svg.lighttoggle {
@apply hidden;
}
.sidebar svg {
fill: #9ca3af;
stroke: #9ca3af;
@apply transition duration-200 ease-in;
}
.sidebar svg:hover {
fill: #fff;
stroke: #fff;
}
/* Content area */
.content,
.postlist {
@apply mt-8 mx-8 lg:mr-32 lg:ml-32 leading-relaxed tracking-wider;
}
.content a {
@apply text-indigo-900 dark:text-indigo-300 underline;
}
.content p {
@apply pb-4 pt-2 font-light dark:font-extralight;
}
.content blockquote {
@apply border-l-2 border-gray-300 dark:border-gray-600 my-4;
}
.content blockquote > p {
@apply ml-4 py-2;
}
.content h1 {
@apply text-2xl dark:font-light py-6 mt-4;
}
.content h2 {
@apply text-xl mt-6;
}
.content h3 {
@apply text-lg mt-6;
}
.content ol {
@apply list-decimal pl-10 leading-8;
}
.content ul {
@apply list-disc pl-10 leading-8;
}
.content table {
@apply table-auto w-full;
}
.content table thead tr {
@apply leading-12;
}
.content table tbody tr:nth-child(even) {
@apply dark:bg-dark-heading;
}
.content table tbody tr:nth-child(odd) {
@apply bg-gray-100 dark:bg-sidebar-dark;
}
.content img {
max-width: 100%;
}
.social svg {
@apply h-5 ml-2 mr-3;
}
.social a {
position: relative;
}
.social a::before {
content: attr(aria-label);
position: absolute;
transform: translateY(-50%);
top: -42px;
left: -45px;
width: 150px;
padding: 10px;
background: #000;
border-radius: 0.3rem;
color: #fff;
text-align: center;
opacity: 0;
font-size: 12px;
}
.social a.link:active::before {
content: "Copied";
top: -33px;
}
.social a:hover::before {
opacity: 1;
display: block;
}
.social a:hover > span::after {
content: " ";
position: absolute;
top: -14px;
left: 7px;
margin-bottom: -5px;
border-width: 8px;
border-style: solid;
border-color: black transparent transparent transparent;
}
/* Utils */
.readingtime {
@apply cursor-default;
position: relative;
}
.readingtime::before {
content: attr(data-words);
position: absolute;
transform: translateY(-50%);
bottom: -49px;
right: 1px;
width: 100px;
padding: 4px;
background: #000;
border-radius: 0.3rem;
color: #fff;
text-align: center;
opacity: 0;
font-size: 12px;
}
div.readingtime:hover::before {
opacity: 1;
display: block;
}
.readingtime:hover > span::after {
content: " ";
position: absolute;
top: 11px;
right: 41px;
margin-bottom: -5px;
border-width: 8px;
border-style: solid;
border-color: transparent transparent black transparent;
}
.content.post > p > a:hover,
.content.page a:hover,
a.postlistheading:hover,
.postlist a:hover,
#recents a:hover,
#top-bar a:hover {
@apply text-red-500 underline;
}
input[type="search"]::-webkit-search-cancel-button {
display: none;
}
/* Right sidebar */
#toc {
transition: top 0.2s ease-in-out;
animation: fadeup-content 0.8s;
}
nav.toc {
margin-left: 8px;
}
nav.toc ol {
@apply list-none;
transition: top 0.4s ease;
}
nav.toc li {
@apply leading-8;
}
nav.toc a {
@apply no-underline block;
padding-left: 18px;
margin-left: -9px;
}
nav.toc a:active,
nav.toc a:hover {
@apply text-indigo-900 dark:text-indigo-300;
border-left: 1px solid;
padding-left: 17px;
}
nav.toc a.active {
@apply text-indigo-900 dark:text-indigo-300 font-bold;
border-left: 2px solid;
padding-left: 16px;
}
nav.toc ol ol a,
nav.toc ol ol a:hover,
nav.toc ol ol a:active,
nav.toc ol ol a.active {
padding-left: 26px;
}
nav.toc ol ol a:active,
nav.toc ol ol a:hover {
padding-left: 25px;
}
nav.toc ol ol a.active {
padding-left: 24px;
}
/* Collection pagination */
ul.pagination div {
@apply h-8 w-8 border-1 dark:text-gray-400 bg-white dark:bg-gray-700 hover:bg-indigo-800 dark:hover:bg-gray-600 border-gray-600 hover:text-white rounded-full text-center;
}
html:not(.dark) ul.pagination div.active {
@apply text-white bg-indigo-800;
box-shadow: 0 0 8px 0 rgba(55, 48, 163);
}
html[class="dark"] ul.pagination div.active {
@apply bg-indigo-900;
}
::placeholder {
@apply text-gray-800;
}
:-ms-input-placeholder {
@apply text-gray-800;
}
#top-bar.hide {
@apply transform -translate-y-12;
}
.additions a {
@apply no-underline;
}
.additions div {
@apply text-gray-700 dark:text-gray-400 bg-white dark:bg-dark-body border-gray-600 no-underline;
}
.additions div:hover {
@apply no-underline;
}
.scroll {
@apply bg-white dark:bg-gray-700 w-12 h-12 border-indigo-900 border-solid border-1;
position: fixed;
display: none;
justify-content: center;
align-content: center;
align-items: center;
bottom: 80px;
right: 60px;
opacity: 0.8;
border-radius: 50%;
z-index: 9;
cursor: pointer;
transition: opacity 0.2s ease-in-out;
animation: fadeup-scrolltop 0.7s;
}
@media all and (max-width: 400px) {
.scroll {
bottom: 40px;
right: 40px;
animation: fadeup-scrolltop-mobile 0.7s;
}
}
.scroll svg {
@apply stroke-current fill-current;
}
@keyframes fadeup-content {
0% {
opacity: 0;
position: relative;
top: 2rem;
}
100% {
opacity: 1;
position: relative;
top: 0;
}
}
@keyframes fadeup-scrolltop {
0% {
opacity: 0;
bottom: 20px;
}
100% {
opacity: 0.8;
bottom: 80px;
}
}
@keyframes fadeup-scrolltop-mobile {
0% {
opacity: 0;
bottom: 20px;
}
100% {
opacity: 0.8;
bottom: 40px;
}
}
/* Syntax highlighting */
pre {
@apply rounded-md;
}
div.code-toolbar {
@apply w-full;
}
div.code-toolbar > .toolbar {
margin-top: -5px;
opacity: 1;
}
div.code-toolbar > .toolbar a,
div.code-toolbar > .toolbar button,
div.code-toolbar > .toolbar span {
background: unset;
box-shadow: unset;
border-radius: unset;
}
@media print {
#sidebar #sidebar-contents {
display: none;
}
#right-area #top-bar {
display: none;
}
.grid {
display: block;
}
#scroll {
visibility: hidden;
}
aside {
display: none;
}
#search-container {
display: none;
}
main .content .social {
display: none;
}
main .content.border-b-1,
main .block.border-b-1 {
border-bottom-width: 0px;
}
main .content .additions {
display: none;
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 69 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 115 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 61 KiB

View File

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<browserconfig>
<msapplication>
<tile>
<square150x150logo src="/assets/favicons/mstile-150x150.png"/>
<TileColor>#ffc40d</TileColor>
</tile>
</msapplication>
</browserconfig>

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

BIN
assets/favicons/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

View File

@ -0,0 +1,254 @@
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
<svg version="1.0" xmlns="http://www.w3.org/2000/svg"
width="340.000000pt" height="340.000000pt" viewBox="0 0 340.000000 340.000000"
preserveAspectRatio="xMidYMid meet">
<metadata>
Created by potrace 1.11, written by Peter Selinger 2001-2013
</metadata>
<g transform="translate(0.000000,340.000000) scale(0.100000,-0.100000)"
fill="#000000" stroke="none">
<path d="M366 3349 c-27 -27 -46 -53 -42 -56 3 -3 7 -4 9 -2 1 2 24 28 52 57
27 28 45 52 40 52 -6 0 -32 -23 -59 -51z"/>
<path d="M586 3335 c31 -35 60 -66 63 -67 19 -8 62 -79 55 -90 -6 -10 -3 -9 9
1 9 7 17 25 16 40 0 14 -3 20 -6 13 -3 -8 -38 21 -92 78 -101 104 -129 120
-45 25z"/>
<path d="M857 3357 c-39 -44 -36 -65 5 -24 51 53 62 67 47 66 -8 0 -31 -19
-52 -42z"/>
<path d="M1019 3383 c5 -10 19 -28 30 -40 12 -12 21 -26 21 -30 0 -5 9 -17 21
-26 14 -12 17 -22 11 -30 -5 -7 -5 -23 -1 -37 4 -16 7 -19 8 -7 1 9 5 17 11
17 5 0 7 -7 3 -16 -3 -8 4 -25 17 -39 19 -20 21 -27 11 -39 -11 -13 -9 -13 13
-3 13 6 21 14 18 17 -4 3 -8 13 -10 22 -2 14 2 13 24 -7 23 -21 28 -23 41 -10
15 15 33 12 33 -6 0 -6 -4 -8 -9 -5 -13 9 -30 -17 -26 -39 2 -11 4 -23 4 -27
1 -11 41 -10 41 0 0 4 -5 13 -12 20 -15 15 -4 15 25 1 21 -10 21 -9 -2 10 -13
12 -18 21 -13 21 6 0 14 -4 17 -10 9 -14 45 -13 45 2 0 6 3 9 6 5 8 -8 64 13
64 24 0 5 4 9 9 9 4 0 7 -11 6 -25 -2 -14 1 -25 6 -25 5 0 9 4 9 8 0 12 40 9
41 -3 3 -23 6 -51 7 -68 1 -10 6 -16 11 -12 5 3 16 -2 24 -10 19 -18 56 -20
61 -2 2 6 7 21 12 32 6 18 8 18 11 3 3 -12 14 -18 31 -18 15 0 41 -7 57 -16
17 -9 42 -23 58 -31 15 -8 27 -20 28 -26 0 -7 2 -22 3 -35 2 -15 -2 -21 -10
-18 -11 4 -11 2 -1 -14 6 -11 15 -20 20 -20 4 0 5 -4 2 -10 -3 -5 1 -10 11
-10 12 0 14 -3 6 -13 -8 -10 -5 -20 13 -41 13 -15 29 -25 35 -21 6 3 10 3 9
-2 -5 -22 14 -43 35 -38 11 2 15 2 9 -1 -21 -10 -14 -33 11 -40 14 -3 32 -14
41 -24 16 -18 16 -19 -9 -26 -19 -5 -26 -13 -26 -31 0 -19 3 -23 13 -16 6 6
22 12 33 15 17 4 20 2 17 -13 -3 -11 -1 -19 5 -19 7 0 4 -13 -6 -31 -13 -24
-14 -33 -4 -36 6 -3 10 -9 7 -14 -4 -5 2 -6 11 -2 15 5 16 1 10 -30 -5 -29 -2
-40 17 -61 l24 -26 21 23 c12 13 22 34 22 48 1 41 27 134 45 163 10 15 37 38
61 52 23 14 48 31 54 39 14 17 95 21 105 5 10 -16 25 -12 25 6 0 9 16 50 35
90 29 59 46 80 94 117 33 25 58 46 56 48 -2 2 6 5 18 6 12 1 60 8 106 15 72
10 87 10 110 -3 22 -13 31 -13 56 -3 17 7 41 14 55 14 23 1 23 2 5 10 -22 10
-14 12 48 14 23 1 36 -3 33 -9 -2 -7 19 -15 50 -21 59 -11 62 -5 6 12 -32 10
-34 12 -11 13 14 1 40 -6 57 -15 35 -17 53 -37 25 -28 -15 6 -15 4 2 -9 23
-18 39 -22 30 -7 -9 15 21 12 32 -2 6 -7 13 -27 16 -44 5 -26 2 -34 -16 -43
-19 -10 -20 -11 -2 -7 45 10 175 17 185 11 8 -5 2 -17 -15 -35 -15 -16 -26
-30 -25 -32 7 -9 -13 -48 -21 -42 -6 3 -5 12 3 22 11 15 11 15 -3 5 -14 -11
-12 -20 18 -80 18 -38 39 -93 47 -123 8 -30 15 -45 15 -34 1 12 -6 41 -14 64
-19 53 -19 62 0 30 13 -22 15 3 14 227 0 207 -3 251 -14 247 -8 -3 -16 4 -20
16 -15 47 -12 72 12 101 27 32 25 62 -4 53 -11 -4 -14 -3 -7 3 6 4 13 19 16
33 3 14 9 33 13 43 4 11 3 17 -5 17 -6 0 -14 -9 -17 -20 -4 -16 -8 -18 -19 -9
-11 9 -14 7 -15 -7 -1 -14 -3 -13 -8 5 -4 14 -12 26 -19 29 -13 4 -12 -24 3
-94 6 -25 2 -36 -20 -60 -17 -18 -25 -22 -21 -11 3 9 2 17 -2 17 -4 0 -8 9 -9
20 -2 22 13 28 19 8 3 -8 6 -9 11 -1 9 14 -13 40 -43 48 -21 7 -23 6 -13 -7
10 -11 8 -18 -8 -32 -20 -17 -21 -17 -14 3 3 10 2 22 -3 25 -5 3 -8 24 -7 46
2 39 1 40 -30 40 -23 0 -39 -9 -59 -30 -15 -17 -31 -28 -37 -25 -5 4 -8 2 -7
-3 2 -6 -14 -31 -36 -57 -35 -43 -40 -45 -55 -31 -12 13 -13 20 -4 34 9 15 7
21 -8 30 -20 13 -24 4 -20 -38 1 -11 -1 -11 -9 3 -6 9 -20 17 -30 17 -11 0
-20 7 -20 15 0 8 5 15 10 15 15 0 49 37 42 45 -4 3 0 10 8 15 9 6 -317 10
-867 10 l-883 -1 0 -21 c0 -16 -5 -19 -23 -16 -13 2 -29 -1 -35 -9 -15 -15
-42 -17 -42 -3 0 6 -5 10 -11 10 -5 0 -7 -6 -4 -12 4 -7 -7 2 -24 20 -34 35
-58 44 -42 15z m2148 -75 c-3 -7 -5 -2 -5 12 0 14 2 19 5 13 2 -7 2 -19 0 -25z
m-1727 -98 c0 -5 -2 -10 -4 -10 -3 0 -8 5 -11 10 -3 6 -1 10 4 10 6 0 11 -4
11 -10z m-190 -29 c0 -5 -7 -11 -15 -15 -9 -3 -15 0 -15 9 0 8 7 15 15 15 8 0
15 -4 15 -9z m2077 -13 c-3 -8 -6 -5 -6 6 -1 11 2 17 5 13 3 -3 4 -12 1 -19z
m3 -22 c0 -2 -7 -7 -16 -10 -8 -3 -12 -2 -9 4 6 10 25 14 25 6z m-1720 -52 c0
-8 -4 -14 -10 -14 -5 0 -10 9 -10 21 0 11 5 17 10 14 6 -3 10 -13 10 -21z
m1627 -36 c-3 -8 -6 -5 -6 6 -1 11 2 17 5 13 3 -3 4 -12 1 -19z m140 0 c-3 -8
-6 -5 -6 6 -1 11 2 17 5 13 3 -3 4 -12 1 -19z m-642 -39 c-38 -4 -83 -10 -100
-13 -22 -4 -25 -3 -10 4 33 14 118 25 150 20 21 -3 9 -6 -40 -11z m155 -3 c0
-2 -11 -6 -25 -8 -13 -3 -22 -1 -19 3 5 9 44 13 44 5z m460 -106 c0 -5 -5 -10
-11 -10 -5 0 -7 5 -4 10 3 6 8 10 11 10 2 0 4 -4 4 -10z m18 -196 c-3 -16 -7
-13 -21 13 -23 43 -21 52 4 26 12 -11 19 -29 17 -39z"/>
<path d="M80 3291 c47 -45 99 -91 115 -102 28 -20 -30 39 -120 120 -81 73 -78
61 5 -18z"/>
<path d="M890 3271 c0 -6 4 -13 10 -16 6 -3 7 1 4 9 -7 18 -14 21 -14 7z"/>
<path d="M1070 3199 c0 -11 4 -18 10 -14 5 3 7 12 3 20 -7 21 -13 19 -13 -6z"/>
<path d="M1113 3178 c4 -11 0 -18 -11 -21 -9 -3 -5 -4 11 -4 31 2 34 9 10 28
-15 12 -16 12 -10 -3z"/>
<path d="M660 3150 c-8 -14 -8 -26 -2 -32 6 -6 12 0 16 17 9 35 1 43 -14 15z"/>
<path d="M1460 3050 c0 -5 5 -10 10 -10 6 0 10 5 10 10 0 6 -4 10 -10 10 -5 0
-10 -4 -10 -10z"/>
<path d="M3296 2555 c-9 -26 -7 -32 5 -12 6 10 9 21 6 23 -2 3 -7 -2 -11 -11z"/>
<path d="M3266 2465 c-9 -26 -7 -32 5 -12 6 10 9 21 6 23 -2 3 -7 -2 -11 -11z"/>
<path d="M3165 2360 c-3 -5 -1 -10 4 -10 6 0 11 5 11 10 0 6 -2 10 -4 10 -3 0
-8 -4 -11 -10z"/>
<path d="M824 2288 c3 -15 26 -23 26 -8 0 6 -4 9 -9 6 -5 -4 -12 -1 -14 4 -3
6 -4 5 -3 -2z"/>
<path d="M1091 2266 c7 -15 17 -22 26 -19 11 5 11 9 -3 24 -24 26 -36 24 -23
-5z"/>
<path d="M2710 2255 c-7 -8 -16 -13 -20 -10 -11 7 -86 -62 -77 -71 4 -4 19 3
33 17 14 13 28 22 31 20 7 -5 53 37 53 49 0 14 -5 13 -20 -5z"/>
<path d="M1075 2230 c21 -24 26 -25 19 -6 -3 8 -13 17 -22 21 -13 5 -13 2 3
-15z"/>
<path d="M1373 2216 c0 -14 3 -26 8 -26 11 0 11 21 0 39 -6 9 -8 5 -8 -13z"/>
<path d="M590 2191 c0 -5 5 -13 10 -16 6 -3 10 -2 10 4 0 5 -4 13 -10 16 -5 3
-10 2 -10 -4z"/>
<path d="M2668 2179 c-38 -22 -11 -89 36 -89 47 0 63 57 24 84 -26 18 -36 19
-60 5z"/>
<path d="M1395 2160 c-3 -5 -1 -10 4 -10 6 0 11 5 11 10 0 6 -2 10 -4 10 -3 0
-8 -4 -11 -10z"/>
<path d="M3320 2145 c-32 -8 -62 -22 -72 -36 -16 -18 -22 -20 -41 -11 -24 11
-57 13 -57 2 0 -4 -25 -13 -56 -20 -50 -12 -58 -12 -85 4 -16 9 -34 14 -39 11
-6 -3 -10 2 -11 12 0 12 -2 13 -6 5 -3 -8 4 -22 16 -33 12 -11 21 -23 21 -26
0 -4 -5 -2 -12 5 -7 7 -15 12 -20 12 -4 0 4 -11 17 -25 32 -31 32 -55 0 -55
-13 0 -35 -5 -49 -11 -21 -10 -23 -14 -13 -30 17 -28 -6 -48 -34 -31 -19 12
-20 11 -14 -10 4 -13 14 -27 21 -31 24 -14 15 -27 -17 -27 -28 0 -30 -2 -18
-16 11 -14 11 -18 -4 -29 -17 -12 -17 -13 0 -19 17 -7 17 -8 0 -17 -14 -8 -15
-14 -7 -24 17 -21 -29 -48 -61 -37 -18 7 -28 4 -43 -11 -16 -17 -17 -19 -3
-14 9 3 17 3 17 -1 0 -4 14 -13 31 -19 21 -8 28 -16 23 -25 -6 -10 -5 -10 7
-2 8 6 39 12 69 13 65 1 110 10 127 23 7 5 18 8 25 6 7 -3 1 -12 -17 -23 -17
-11 -48 -34 -69 -52 -22 -18 -55 -36 -75 -39 -20 -4 -45 -13 -56 -20 -11 -8
-28 -14 -39 -14 -11 0 -28 -7 -38 -17 -14 -13 -18 -13 -18 -2 -1 9 -4 8 -11
-5 -10 -16 -19 -17 -68 -11 -69 9 -85 16 -138 68 -35 33 -50 41 -82 42 -21 0
-37 4 -34 9 3 5 2 7 -3 5 -5 -2 -60 -24 -121 -48 -80 -31 -110 -47 -106 -57 3
-8 0 -14 -7 -14 -6 0 -8 5 -5 10 7 11 -6 13 -29 4 -9 -3 -16 -11 -16 -18 0
-14 -51 -15 -67 0 -6 6 -43 14 -82 18 -77 8 -83 13 -95 79 -4 20 -11 37 -15
37 -4 0 -21 30 -36 68 -31 75 -80 168 -103 194 -8 10 -12 25 -8 35 7 16 6 16
-12 0 -18 -17 -20 -16 -36 8 -22 34 -41 32 -41 -4 -1 -21 3 -27 10 -21 7 6 11
2 12 -11 1 -10 -5 -19 -13 -19 -8 0 -14 -5 -14 -11 0 -5 -4 -8 -9 -5 -5 4 -11
1 -13 -5 -3 -10 -16 -5 -90 41 -15 9 -17 7 -12 -7 5 -16 4 -16 -10 1 -9 11
-16 23 -16 28 0 4 -11 8 -25 8 -14 0 -37 12 -51 26 -24 23 -25 26 -10 43 9 10
16 27 16 39 0 11 7 25 16 30 10 6 12 12 6 16 -5 3 -17 -6 -26 -19 -9 -14 -21
-23 -26 -20 -6 4 -10 0 -10 -7 0 -7 -7 0 -15 15 -10 20 -12 34 -5 46 7 13 5
18 -6 18 -8 0 -13 -6 -10 -14 3 -8 -2 -13 -14 -13 -11 0 -20 7 -20 16 0 24
-104 16 -108 -9 -4 -22 26 -59 41 -50 6 3 7 1 3 -6 -6 -9 -11 -9 -22 0 -11 9
-17 9 -28 0 -8 -7 -16 -8 -18 -4 -3 4 -4 3 -2 -3 1 -5 -7 -21 -17 -35 -18 -22
-20 -22 -18 -4 2 35 -1 46 -11 40 -5 -3 -7 0 -4 8 5 15 -21 31 -36 22 -5 -3
-11 -1 -15 5 -3 5 -13 10 -22 10 -9 0 -30 11 -47 24 -29 21 -121 42 -110 24 3
-5 11 -8 19 -8 7 0 15 -12 17 -27 2 -18 10 -29 23 -31 11 -3 28 -13 38 -23 17
-18 17 -19 -8 -19 -16 0 -25 6 -25 15 0 8 -4 15 -9 15 -5 0 -22 5 -38 11 -25
9 -26 11 -10 21 18 9 18 10 0 24 -10 7 -21 14 -25 14 -15 0 -8 -59 8 -74 11
-12 14 -25 9 -47 -5 -22 -3 -34 6 -40 10 -6 11 -9 2 -9 -8 0 -13 -15 -13 -40
0 -52 -23 -53 -76 -4 -23 22 -40 32 -42 25 -3 -8 -9 -5 -18 7 -7 11 -34 29
-59 41 -29 13 -49 31 -56 47 -7 19 -15 25 -29 21 -11 -3 -29 3 -41 14 -22 19
-39 17 -39 -4 0 -7 -8 -24 -17 -37 -10 -14 -16 -33 -15 -42 2 -10 -2 -27 -8
-38 -9 -17 -8 -25 4 -39 18 -20 21 -57 5 -66 -7 -5 -4 -14 7 -26 16 -17 16
-24 5 -55 -12 -33 -11 -37 8 -56 23 -21 21 -49 -4 -75 -11 -10 -15 -10 -24 1
-6 7 -7 17 -3 22 4 4 0 3 -9 -4 -14 -11 -18 -9 -23 13 -8 31 -19 32 -35 2 -14
-25 0 -109 18 -115 6 -2 11 -13 11 -25 0 -20 0 -20 15 -1 12 16 14 17 15 2 0
-9 22 -49 49 -90 27 -40 51 -82 53 -93 3 -22 122 -149 159 -170 13 -8 36 -19
51 -25 15 -7 34 -23 43 -37 12 -18 23 -23 38 -19 26 6 40 -4 90 -66 22 -28 50
-51 65 -54 15 -4 47 -19 72 -34 25 -15 76 -40 113 -56 86 -37 152 -99 196
-186 35 -70 53 -158 32 -158 -6 0 -16 -10 -22 -22 -9 -22 -9 -22 -4 2 15 63
16 99 5 120 -11 20 -14 21 -27 7 -13 -13 -19 -13 -41 -1 -25 13 -28 12 -40
-11 -7 -14 -17 -25 -23 -25 -6 0 -8 -9 -4 -22 6 -20 3 -19 -19 7 -14 17 -25
38 -26 48 0 9 -3 17 -8 17 -4 0 -15 18 -25 40 -12 28 -23 40 -37 40 -11 0 -20
4 -20 10 0 5 -6 7 -14 4 -9 -3 -12 -12 -9 -21 4 -11 2 -14 -5 -9 -7 4 -12 13
-12 19 0 18 -34 44 -49 39 -8 -3 -11 0 -8 5 3 5 -4 10 -15 10 -19 2 -19 1 -3
-17 10 -11 17 -31 17 -45 0 -15 7 -29 17 -33 19 -7 36 -31 36 -52 0 -8 11 -20
24 -26 13 -7 28 -22 35 -35 8 -16 23 -26 46 -29 49 -7 49 -32 1 -79 -23 -22
-41 -43 -41 -46 0 -10 40 -85 76 -140 l34 -55 -49 -102 c-27 -56 -47 -107 -44
-115 4 -11 203 -13 1129 -13 l1124 0 0 1080 c0 858 -3 1080 -12 1079 -7 0 -38
-7 -68 -14z m-154 -232 c-11 -11 -19 6 -11 24 8 17 8 17 12 0 3 -10 2 -21 -1
-24z m-2092 -5 c27 -11 68 -19 90 -20 44 0 86 -15 99 -36 5 -7 14 -9 20 -6 7
4 9 4 5 0 -4 -5 -3 -18 2 -31 7 -18 5 -24 -8 -27 -10 -3 -23 1 -30 9 -21 22
-41 15 -59 -22 -18 -39 -12 -55 13 -34 14 12 16 11 13 -6 -7 -32 -43 -26 -47
8 -2 15 -9 27 -16 27 -22 0 -84 63 -91 91 -5 20 -12 25 -25 22 -13 -3 -20 2
-25 19 -4 12 -13 27 -19 31 -12 10 1 5 78 -25z m2136 -48 c10 -18 8 -20 -20
-20 -28 0 -30 2 -20 20 6 11 15 20 20 20 5 0 14 -9 20 -20z m-67 -151 c4 -14
0 -16 -26 -8 -33 9 -60 29 -40 29 7 0 15 10 19 22 l6 21 19 -24 c10 -13 20
-31 22 -40z m-1603 -102 c0 -10 11 -28 25 -41 16 -15 20 -23 10 -23 -8 0 -15
4 -15 9 0 4 -5 8 -11 8 -6 0 -8 -12 -6 -29 4 -24 2 -28 -15 -23 -11 3 -24 -3
-33 -14 -8 -11 -14 -14 -15 -6 0 6 -5 12 -11 12 -5 0 -8 -4 -5 -8 3 -5 -1 -9
-9 -9 -8 0 -15 5 -15 11 0 7 -9 19 -20 29 -22 18 -26 37 -9 37 6 0 20 12 31
26 12 14 31 30 44 34 13 5 24 17 24 27 0 15 2 15 15 -3 8 -10 15 -27 15 -37z
m-124 29 c19 -20 18 -26 -8 -26 -20 0 -20 -1 -5 -12 15 -12 14 -15 -8 -41 -13
-15 -34 -28 -47 -30 -20 -2 -23 3 -26 35 -3 30 0 37 11 32 10 -3 17 4 21 21 5
18 12 25 21 21 8 -3 15 -1 15 4 0 14 9 12 26 -4z m967 -71 c-3 -9 -8 -14 -10
-11 -3 3 -2 9 2 15 9 16 15 13 8 -4z m27 -15 c0 -5 -2 -10 -4 -10 -3 0 -8 5
-11 10 -3 6 -1 10 4 10 6 0 11 -4 11 -10z m-1120 -30 c0 -5 -2 -10 -4 -10 -3
0 -8 5 -11 10 -3 6 -1 10 4 10 6 0 11 -4 11 -10z m960 -48 c0 -10 -81 -44 -88
-37 -4 3 63 38 86 44 1 1 2 -3 2 -7z m-290 -17 c7 -8 18 -15 25 -15 22 0 60
-39 48 -51 -7 -7 -24 -4 -56 10 -25 12 -49 21 -53 21 -8 0 -33 32 -34 43 0 13
57 7 70 -8z m-130 -35 c0 -5 -2 -10 -4 -10 -3 0 -8 5 -11 10 -3 6 -1 10 4 10
6 0 11 -4 11 -10z m185 -154 c18 -19 61 -49 93 -65 l60 -30 -8 -36 c-11 -51
-52 -109 -129 -185 -66 -65 -141 -118 -141 -100 0 6 -6 10 -13 10 -8 0 -19 11
-27 24 -7 14 -18 22 -26 19 -14 -5 -15 -1 -18 61 0 19 -4 32 -8 29 -15 -8 -8
58 8 82 9 13 13 28 10 33 -3 5 6 19 19 32 13 13 22 27 19 31 -3 5 -1 9 4 9 6
0 12 15 15 33 8 48 18 90 25 107 7 17 9 28 11 49 1 8 16 -3 36 -27 20 -22 51
-57 70 -76z m1151 -251 c-10 -8 -24 -16 -30 -18 -6 -2 -8 -7 -5 -11 4 -4 25 0
47 9 53 22 107 17 107 -9 0 -24 -71 -64 -86 -49 -7 7 0 14 20 23 17 7 31 16
31 21 0 13 -32 10 -60 -6 -35 -20 -89 -19 -97 2 -8 21 31 53 66 53 25 -1 25
-1 7 -15z m-128 -59 c15 -7 53 -19 85 -27 58 -12 75 -23 58 -33 -22 -14 -220
52 -204 68 10 10 32 7 61 -8z m27 -83 c30 -10 54 -22 52 -28 -2 -5 -11 -9 -21
-7 -9 2 -16 -4 -16 -12 0 -19 -61 -66 -87 -66 -10 0 -28 6 -38 14 -18 13 -18
14 4 45 12 17 27 31 32 31 5 0 9 5 9 10 0 19 -30 10 -65 -21 -36 -32 -54 -34
-38 -4 14 24 64 55 90 55 12 0 47 -8 78 -17z m-143 -130 c22 -20 23 -52 0 -78
-49 -58 -112 -71 -157 -32 -24 20 -25 25 -14 55 6 19 25 42 43 53 38 23 103
25 128 2z m-336 -155 c-25 -22 -44 -51 -56 -88 -16 -46 -19 -50 -20 -25 0 46
70 144 104 145 5 0 -8 -15 -28 -32z m194 -58 c0 -4 -13 -25 -29 -46 -59 -81
-112 -113 -146 -87 -19 14 -18 14 13 8 23 -5 40 -2 58 9 27 18 84 93 84 110 0
6 -14 17 -32 25 -29 13 -33 13 -49 -7 -9 -12 -21 -22 -25 -22 -4 0 5 13 20 29
l27 28 39 -20 c22 -10 40 -22 40 -27z m-213 -126 c-7 -7 -37 7 -37 17 0 6 9 5
21 -1 11 -7 19 -14 16 -16z m-67 -51 c0 -5 -13 -8 -29 -8 -16 0 -27 3 -24 8 2
4 15 7 29 7 13 0 24 -3 24 -7z m30 -23 c0 -5 -4 -10 -10 -10 -5 0 -10 5 -10
10 0 6 5 10 10 10 6 0 10 -4 10 -10z m-128 -54 c-2 -18 1 -37 7 -40 6 -4 11
-12 11 -17 0 -5 -9 -1 -20 9 -18 17 -21 47 -5 72 10 16 11 12 7 -24z m163 -13
c-22 -35 -56 -73 -65 -73 -4 0 8 16 26 36 19 19 34 42 34 50 0 8 5 14 11 14 7
0 4 -11 -6 -27z m-85 13 c0 -2 -8 -10 -17 -17 -16 -13 -17 -12 -4 4 13 16 21
21 21 13z m0 -82 c0 -2 -11 -3 -24 -2 -14 1 -23 5 -20 9 4 7 44 0 44 -7z
m-119 -177 c-10 -9 -11 -8 -5 6 3 10 9 15 12 12 3 -3 0 -11 -7 -18z"/>
<path d="M3002 830 c-26 -24 -28 -40 -6 -40 27 0 54 20 54 40 0 26 -20 25 -48
0z"/>
<path d="M2815 731 c-36 -15 -61 -50 -49 -69 18 -28 91 -28 120 -1 29 27 31
55 5 69 -23 12 -48 12 -76 1z"/>
<path d="M2867 2127 c3 -4 11 -6 19 -3 8 3 14 -1 14 -9 0 -8 9 -15 20 -15 25
0 26 15 3 26 -25 11 -63 12 -56 1z"/>
<path d="M2409 2073 c-13 -16 -12 -17 4 -4 9 7 17 15 17 17 0 8 -8 3 -21 -13z"/>
<path d="M2883 2083 c9 -2 23 -2 30 0 6 3 -1 5 -18 5 -16 0 -22 -2 -12 -5z"/>
<path d="M1481 2064 c0 -11 3 -14 6 -6 3 7 2 16 -1 19 -3 4 -6 -2 -5 -13z"/>
<path d="M2086 2057 c3 -10 9 -15 12 -12 3 3 0 11 -7 18 -10 9 -11 8 -5 -6z"/>
<path d="M760 1979 c-14 -13 -18 -20 -9 -16 11 4 20 -3 28 -19 7 -16 16 -22
24 -18 6 4 9 4 5 -1 -8 -9 9 -35 22 -35 13 0 -1 43 -20 65 -10 11 -19 26 -21
33 -3 10 -11 7 -29 -9z"/>
<path d="M2045 1990 c3 -5 13 -10 21 -10 8 0 14 5 14 10 0 6 -9 10 -21 10 -11
0 -17 -4 -14 -10z"/>
<path d="M2772 1978 c-1 -11 -8 -15 -17 -12 -9 4 -16 -2 -19 -18 -20 -99 -12
-121 24 -62 14 25 20 49 17 73 -2 20 -4 28 -5 19z"/>
<path d="M2511 1919 c-1 -24 6 -45 20 -60 19 -23 20 -23 10 -2 -6 12 -15 39
-20 60 l-9 38 -1 -36z"/>
<path d="M2563 1903 c-7 -2 -13 -10 -13 -17 0 -20 47 -58 67 -54 23 5 19 37
-9 61 -21 18 -25 19 -45 10z"/>
<path d="M2094 1870 c-10 -4 -18 -14 -20 -23 -1 -10 2 -15 7 -12 5 4 17 1 26
-4 16 -9 15 -11 -7 -12 l-25 -1 25 -8 c14 -4 21 -9 15 -9 -13 -2 36 -30 53
-31 15 0 61 -50 62 -67 0 -6 -9 -14 -20 -18 -11 -3 -28 -17 -38 -30 -14 -18
-22 -21 -31 -12 -7 7 -11 8 -11 0 0 -15 25 -24 36 -13 5 5 15 6 22 1 9 -5 5
-11 -13 -19 -31 -15 -65 -28 -88 -36 -16 -5 -16 -7 4 -30 17 -19 26 -23 43
-16 11 5 66 28 122 52 l101 43 -33 17 c-30 16 -37 16 -70 2 -20 -8 -39 -13
-42 -10 -3 3 11 13 32 21 l36 15 -47 60 c-32 40 -54 58 -66 56 -9 -2 -14 0
-11 5 3 5 3 27 -1 47 -6 39 -20 46 -61 32z m16 -310 c0 -5 -2 -10 -4 -10 -3 0
-8 5 -11 10 -3 6 -1 10 4 10 6 0 11 -4 11 -10z"/>
<path d="M377 1783 c-9 -8 22 -43 37 -43 6 0 17 9 24 20 7 12 8 20 2 20 -5 0
-10 -4 -10 -9 0 -5 -7 -12 -16 -15 -14 -5 -15 -4 -5 8 9 11 8 15 -5 20 -19 7
-19 7 -27 -1z"/>
<path d="M380 1726 c0 -2 8 -10 18 -17 15 -13 16 -12 3 4 -13 16 -21 21 -21
13z"/>
<path d="M2513 1713 c9 -2 23 -2 30 0 6 3 -1 5 -18 5 -16 0 -22 -2 -12 -5z"/>
<path d="M2458 1663 c7 -3 16 -2 19 1 4 3 -2 6 -13 5 -11 0 -14 -3 -6 -6z"/>
<path d="M2533 1663 c9 -2 25 -2 35 0 9 3 1 5 -18 5 -19 0 -27 -2 -17 -5z"/>
<path d="M2785 1609 c-4 -6 -5 -12 -2 -15 2 -3 7 2 10 11 7 17 1 20 -8 4z"/>
<path d="M2030 1590 c0 -5 7 -10 15 -10 8 0 15 5 15 10 0 6 -7 10 -15 10 -8 0
-15 -4 -15 -10z"/>
<path d="M211 811 c-12 -8 -12 -14 -3 -33 10 -20 12 -20 9 -3 -1 11 4 25 13
32 17 14 2 17 -19 4z"/>
<path d="M255 790 c-3 -5 -1 -10 4 -10 6 0 11 5 11 10 0 6 -2 10 -4 10 -3 0
-8 -4 -11 -10z"/>
<path d="M900 766 c0 -9 -8 -20 -17 -23 -15 -6 -15 -7 3 -14 18 -7 18 -8 0 -8
-15 -1 -17 -8 -14 -46 2 -33 0 -44 -9 -40 -7 2 -13 -2 -13 -11 0 -14 14 -23
42 -28 7 -1 18 -5 25 -9 6 -4 25 -6 42 -5 37 3 48 39 20 64 -11 10 -19 31 -19
48 0 22 -9 40 -30 59 -23 22 -30 25 -30 13z"/>
<path d="M685 650 c3 -5 11 -10 16 -10 6 0 7 5 4 10 -3 6 -11 10 -16 10 -6 0
-7 -4 -4 -10z"/>
<path d="M810 646 c0 -9 5 -16 10 -16 6 0 10 4 10 9 0 6 -4 13 -10 16 -5 3
-10 -1 -10 -9z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 16 KiB

View File

@ -0,0 +1,19 @@
{
"name": "{ rene.winkelmeyer }",
"short_name": "muenzpraeger",
"icons": [
{
"src": "/assets/favicons/android-chrome-192x192.png",
"sizes": "192x192",
"type": "image/png"
},
{
"src": "/assets/favicons/android-chrome-256x256.png",
"sizes": "256x256",
"type": "image/png"
}
],
"theme_color": "#ffffff",
"background_color": "#ffffff",
"display": "standalone"
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 189 KiB

BIN
assets/images/404.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

BIN
assets/images/algolia.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

BIN
assets/images/opengraph.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

18
assets/js/prism.js Normal file

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,51 @@
require("dotenv").config();
module.exports = {
// Website title, shown in left sidebar and in page title
title: "{ rené.winkelmeyer }",
// Site URL to generate absolute URLs. Used across the board.
url: process.env.URL || "http://localhost:8080",
// Profile image for left sidebar
image: "/assets/images/sidebar_profile.jpg",
// Image alt text for left sidebar
imageAlt: "René and Einstein",
// Author name, shown in left sidebar, and used in JSON-LD
author: "René Winkelmeyer",
// Site description, shown below site image (optional)
description: "My kitchen sink about stuff",
// OpenGraph default image, in case you don't have an `image`
// set in your Markdown frontmatter; relevant for social
// sharing.
openGraphDefaultImage: "/assets/images/opengraph.jpg",
// GitHub ID (optional, remove it not needed), used for link in the left sidebar
socialGitHub: "muenzpraeger",
// LinkedIn ID (optional, remove it not needed), used for link in the left sidebar
socialLinkedIn: "muenzpraeger",
// Twitter ID (optional, remove it not needed), used for link in the left sidebar, and for OpenGraph sharing information
socialTwitter: "muenzpraeger",
// YouTube ID/Channel (optional, remove it not needed), used for link in the left sidebar
socialYouTube: "UCH60RRaY2GI9m62z1loLjcA",
// Google Analytics ID (optional, remove it not needed), used for... well, Google Analytics
googleAnalytics: "YOUR_GA-ID",
// Algolia-powered search (optional, remove it not needed),
// See: https://github.com/algolia/algoliasearch-netlify
algoliaSearch: {
// When enabled shows the search bar in the UI
enabled: true,
// You'll have to set this manually in your build settings.
// The value comes from Algolia, and is either visible in the
// UI for the Crawler Plugin or the Algolia Dashboard.
appId: process.env.ALGOLIA_APP_ID,
// You'll have to set this manually in your build settings.
// The value comes from Algolia, and is either visible in the
// UI for the Crawler Plugin or the Algolia Dashboard.
searchApiKey: process.env.ALGOLIA_SEARCH_API_KEY,
// You'll have to set this manually in your build settings.
// The value comes from Algolia, and is either visible in the
// UI for the Crawler Plugin or the Algolia Dashboard.
siteId: process.env.ALGOLIA_SITE_ID,
// Assuming that you deploy your "main" branch. Otherwise you
// can either override or configure this (using process.env.HEAD)
branch: "main"
}
};

View File

@ -0,0 +1,80 @@
<!doctype html>
<html lang="en" class="dark">
<head>
{% include "head.njk" %}
{% include "json-ld.njk" %}
</head>
<body class="dark:text-gray-300 dark:bg-dark-body bg-white text-gray-900 tracking-wider font-light overflow-x-hidden sm:overflow-auto">
<div class="grid grid-flow-col grid-cols-small sm:grid-cols-regular h-screen">
<div id="sidebar" class="sidebar transition-transform transform -translate-x-56 sm:translate-x-0 sm:transition-none">
{% include "sidebar.njk" %}
</div>
<div id="right-area" class="overflow-x-hidden sm:overflow-auto transition-transform transform sm:translate-x-0 sm:transition-none">
<div id="top-bar" class="h-12 z-10 bg-white dark:bg-dark-heading transition-transform duration-200 ease-in sm:sticky top-0 border-gray-600 border-b-1 dark:border-transparent border-opacity-30 shadow-lg">
<div class="grid grid-flow-col grid-cols-auto md:grid-cols-topbar max-w-content">
<div id="mobile-top-bar" class="flex leading-12 sm:hidden col-span-2">
<svg viewBox="0 0 100 80" class="block align-middle h-12 leading-12 ml-3 py-3 pt-4" onclick="slideLeftSidebar()">
<rect width="60" height="7"></rect>
<rect y="30" width="60" height="7"></rect>
<rect y="60" width="60" height="7"></rect>
</svg>
<p class="flex-grow inline align-middle leading-12 text-xl text-center font-medium">{{ siteconfig.title }}</p>
{% if siteconfig.algoliaSearch and siteconfig.algoliaSearch.enabled %}
<svg viewBox="0 0 32 32" class="block align-middle h-12 leading-12 mr-3 py-3 pt-3" onclick="toggleSearch()">
<path d="M27.414,24.586l-5.077-5.077C23.386,17.928,24,16.035,24,14c0-5.514-4.486-10-10-10S4,8.486,4,14 s4.486,10,10,10c2.035,0,3.928-0.614,5.509-1.663l5.077,5.077c0.78,0.781,2.048,0.781,2.828,0 C28.195,26.633,28.195,25.367,27.414,24.586z M7,14c0-3.86,3.14-7,7-7s7,3.14,7,7s-3.14,7-7,7S7,17.86,7,14z" id="XMLID_223_" />
</svg>
{% else %}
<div class="w-12"></div>
{% endif %}
</div>
<div id="topbar-desktop-title" class="hidden sm:inline-block leading-12 align-middle ml-8 lg:ml-32 text-base text-gray-500 dark:text-gray-400 pr-8 truncate">
{% if date %}<a href="/" class="text-indigo-900 dark:text-indigo-300 font-normal dark:font-light">Posts</a> {% endif %}{{ title }}</div>
<div id="search-desktop-title" class="hidden sm:hidden leading-12 align-middle ml-8 lg:ml-32 text-base text-gray-500 dark:text-gray-400">
</div>
{% if siteconfig.algoliaSearch and siteconfig.algoliaSearch.enabled %}
<div id="search-box-bar" class="hidden sm:inline-block mr-6 ml-6 sm:mx-0 pt-3 col-span-2 sm:col-auto">
<input id="search-box" type="search" autocomplete="off" spellcheck="false" autocorrect="off" onkeyup="searchContent(event)" class="bg-white dark:bg-dark-nav text-gray-800 dark:text-gray-400 font-light shadow rounded-full border-0 ring-1 ring-gray-300 focus:ring-indigo-400 dark:ring-gray-700 dark:focus:ring-gray-600 pl-2 h-6 w-2/3 sm:float-right sm:mr-6 md:float-none md:w-56 focus:outline-none" placeholder="Search..." aria-label="Search box">
<span id="mobile-search-cancel" class="hidden sm:hidden ml-4" onclick="toggleSearch()">Cancel</span>
</div>
{% endif %}
</div>
<div></div>
</div>
<div class="dark:bg-dark-body grid grid-flow-col grid-cols-auto xl:grid-cols-topbar max-w-content mb-12">
<main class="min-w-0">
{{ content | safe }}
</main>
<div id="search-container" class="hidden truncate">
<div class="postlist pb-8 mt-8 font-light dark:font-extralight pt-4 min-w-0">
<div id="search-mobile-title" class="hidden leading-12 align-middle text-gray-500 dark:text-gray-400">
</div>
<ul id="search-results" class="mt-3">
</ul>
<span id="search-no-results" class="mt-3 hidden">
No data found for the given query.
</span>
<img src="/assets/images/algolia.png" alt="Search powered by Algolia" />
</div>
</div>
<aside class="hidden xl:block w-72 float-right">
{% include "recents.njk" %}
{% include "toc.njk" %}
</aside>
</div>
</div>
</div>
<div id="scroll" class="scroll" onclick="scrollToTop()">
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 1000 1000"
class="h-6 w-6"
>
<g>
<path
d="M990,699.9c0,8.1,0,16.3-8.1,24.4c-16.3,16.3-40.7,16.3-56.9,0L502,350.2L79.1,732.4c-16.3,16.3-40.7,16.3-56.9,0c-16.3-16.3-16.3-40.7,0-56.9l447.3-406.6c24.4-16.3,40.7-16.3,56.9-8.1l447.3,406.6C981.9,675.5,990,691.8,990,699.9z"
></path>
</g>
</svg>
</div>
</body>
</html>

View File

@ -0,0 +1,77 @@
<meta charset="utf-8">
<title>{% if title %}{{ title }} | {% endif %}{{ siteconfig.title }}</title>
{% if siteconfig.googleAnalytics %}
<!-- Global site tag (gtag.js) - Google Analytics -->
<script async src="https://www.googletagmanager.com/gtag/js?id={{ siteconfig.googleAnalytics }}"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', '{{ siteconfig.googleAnalytics }}');
</script>
{% endif %}
<script>
// Toggle dark/light mode, inline JS to avoid any theme flicker
function toggleDarkLightMode (isInit) {
// First time visitors
if (!("theme" in localStorage)) {
if (window.matchMedia("(prefers-color-scheme: light)").matches) {
document.querySelector("html").classList.remove("dark");
document.querySelector("html").classList.add("light");
localStorage.theme = "light";
} else {
localStorage.theme = "dark";
}
} else {
if (!isInit) {
if (localStorage.theme === "light") {
document.querySelector("html").classList.add("dark");
document.querySelector("html").classList.remove("light");
localStorage.theme = "dark";
} else if (localStorage.theme === "dark") {
document.querySelector("html").classList.add("light");
document.querySelector("html").classList.remove("dark");
localStorage.theme = "light";
}
} else {
if (localStorage.theme === "light") {
document.querySelector("html").classList.remove("dark");
document.querySelector("html").classList.add("light");
} else if (localStorage.theme === "dark") {
document.querySelector("html").classList.remove("light");
document.querySelector("html").classList.add("dark");
}
}
}
};
toggleDarkLightMode(true);
</script>
<link rel="apple-touch-icon" sizes="180x180" href="{{ '/assets/favicons/apple-touch-icon.png' | addHash }}">
<link rel="icon" type="image/png" sizes="32x32" href="{{ '/assets/favicons/favicon-32x32.png' | addHash }}">
<link rel="icon" type="image/png" sizes="16x16" href="{{ '/assets/favicons/favicon-16x16.png' | addHash }}">
<link rel="manifest" href="{{ '/assets/favicons/site.webmanifest' | addHash }}" crossorigin="use-credentials">
<link rel="mask-icon" href="{{ '/assets/favicons/safari-pinned-tab.svg' | addHash }}" color="#5bbad5">
<link rel="shortcut icon" href="{{ '/assets/favicons/favicon.ico' | addHash }}">
<link rel="canonical" href="{{ page.url | absoluteUrl }}" />
{% for name, item in meta %}
<meta name="{{ name }}" content="{{ item.value }}">
{% endfor %}
<meta name="msapplication-TileColor" content="#da532c">
<meta name="msapplication-config" content="{{ '/assets/favicons/browserconfig.xml' | addHash }}">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
{% set description = description | truncate(55) %}
<meta name="description" content="{% if description %}{{ description }}{% else %}{{ siteconfig.description }}{% endif %}">
<meta property="og:url" content="{{ page.url | absoluteUrl }}">
<meta property="og:type" content="website">
<meta property="og:title" content="{% if title %}{{ title }} | {% endif %}{{ siteconfig.title }}">
<meta property="og:description" content="{% if description %}{{ description }}{% else %}{{ siteconfig.description }}{% endif %}">
<meta property="og:image" content="{% if image %}{{ image | addHash | absoluteUrl }}{% else %}{{ siteconfig.openGraphDefaultImage | addHash | absoluteUrl }}{% endif %}">
<meta property="og:image:url" content="{% if image %}{{ image | addHash | absoluteUrl }}{% else %}{{ siteconfig.openGraphDefaultImage | addHash | absoluteUrl }}{% endif %}">
<meta name="twitter:image" content="{% if image %}{{ image | addHash | absoluteUrl }}{% else %}{{ siteconfig.openGraphDefaultImage | addHash | absoluteUrl }} {% endif %}">
<meta property="twitter:card" content="summary_large_image">
<meta property="twitter:site" content="{{ siteconfig.socialTwitter }}">
<script src="{{ '/assets/js/min.js' | addHash }}" type="module"></script>
<link rel="stylesheet" href="{{ '/assets/css/site-build.css' | addHash }}">
<script src="{{ '/assets/js/prism.js' | addHash }}"></script>
<link rel="stylesheet" href="{{ '/assets/css/prism-build.css' | addHash }}">

View File

@ -0,0 +1,15 @@
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "Article",
"headline": "{{ title }}",
"image": ["{% if image %}{{ image | absoluteUrl }}{% else %}{{ siteconfig.openGraphDefaultImage | absoluteUrl }}{% endif %}"],
"author": {
"@type": "Person",
"name": "{{ siteconfig.author }}"
},
"mainEntityOfPage": "{{ page.url | absoluteUrl }}",
"datePublished": "{{ page.date | isodate }}",
"description": "{{ content | striptags | truncate(140) }}"
}
</script>

View File

@ -0,0 +1,7 @@
---
layout: base.njk
---
<div class="content page pt-4">
{{ content | safe }}
</div>

View File

@ -0,0 +1,87 @@
---
layout: base.njk
---
<div class="content post border-solid border-b-1 border-gray-700 pb-6">
<h1>{{ title }}</h1>
<div class="mb-12 flex text-sm font-light dark:font-extralight">
<div class="flex">
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 600.112 600.111" class="h-5 w-4 mr-2">
<path d="M537.423,52.563h-59.836V21.92c0-11.83-9.591-21.42-21.42-21.42c-11.83,0-21.42,9.59-21.42,21.42v30.642H165.364V21.92
c0-11.83-9.59-21.42-21.42-21.42c-11.83,0-21.42,9.59-21.42,21.42v30.642H62.687c-32.058,0-58.14,26.082-58.14,58.14v430.77
c0,32.059,26.082,58.139,58.14,58.139h474.736c32.059,0,58.141-26.08,58.141-58.139v-430.77
C595.564,78.645,569.482,52.563,537.423,52.563z M47.387,110.703c0-8.451,6.85-15.3,15.3-15.3h59.837v24.443
c0,11.83,9.59,21.42,21.42,21.42s21.42-9.59,21.42-21.42V95.403h269.383v24.443c0,11.83,9.59,21.42,21.42,21.42
c11.829,0,21.42-9.59,21.42-21.42V95.403h59.836c8.45,0,15.3,6.85,15.3,15.3v53.856H47.387V110.703z M552.723,541.473
c0,8.449-6.85,15.301-15.3,15.301H62.687c-8.45,0-15.3-6.852-15.3-15.301V207.399h505.336V541.473z" />
<path d="M537.423,600.111H62.687c-32.334,0-58.64-26.306-58.64-58.639v-430.77c0-32.334,26.306-58.64,58.64-58.64h59.336V21.92
c0-12.087,9.833-21.92,21.92-21.92c12.086,0,21.92,9.833,21.92,21.92v30.142h268.384V21.92c0-12.087,9.833-21.92,21.92-21.92
s21.92,9.833,21.92,21.92v30.143h59.336c32.335,0,58.641,26.306,58.641,58.64v430.77
C596.064,573.806,569.758,600.111,537.423,600.111z M62.687,53.062c-31.783,0-57.64,25.857-57.64,57.64v430.77
c0,31.782,25.857,57.639,57.64,57.639h474.736c31.783,0,57.641-25.856,57.641-57.639v-430.77c0-31.783-25.857-57.64-57.641-57.64
h-60.336V21.92c0-11.536-9.385-20.92-20.92-20.92s-20.92,9.385-20.92,20.92v31.142H164.864V21.92
c0-11.536-9.385-20.92-20.92-20.92c-11.536,0-20.92,9.385-20.92,20.92v31.142H62.687z M537.423,557.273H62.687
c-8.712,0-15.8-7.088-15.8-15.801V206.899h506.336v334.574C553.223,550.186,546.135,557.273,537.423,557.273z M47.887,207.899
v333.574c0,8.161,6.639,14.801,14.8,14.801h474.736c8.16,0,14.8-6.64,14.8-14.801V207.899H47.887z M553.223,165.059H46.887
v-54.356c0-8.712,7.088-15.8,15.8-15.8h60.337v24.943c0,11.535,9.385,20.92,20.92,20.92s20.92-9.385,20.92-20.92V94.903h270.383
v24.943c0,11.535,9.385,20.92,20.92,20.92s20.92-9.385,20.92-20.92V94.903h60.336c8.712,0,15.8,7.088,15.8,15.8V165.059z
M47.887,164.059h504.336v-53.356c0-8.161-6.64-14.8-14.8-14.8h-59.336v23.943c0,12.087-9.833,21.92-21.92,21.92
s-21.92-9.833-21.92-21.92V95.903H165.864v23.943c0,12.087-9.833,21.92-21.92,21.92s-21.92-9.833-21.92-21.92V95.903H62.687
c-8.161,0-14.8,6.639-14.8,14.8V164.059z" />
</svg>
{{ date | readableDate }}
</div>
<div class="flex readingtime" data-words="{{ content | striptags | wordcount | formatWords }} words">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 472.615 472.615" class="h-5 w-8 mr-2 ml-12">
<path d="M472.615,205.774h-25.344c-12.797-37.99-48.686-65.462-90.935-65.462c-43.224,0-79.837,28.73-91.823,68.087
c-7.582-5.011-17.305-8.115-28.211-8.115c-10.903,0-20.624,3.103-28.207,8.114c-11.989-39.357-48.608-68.085-91.831-68.085
c-42.249,0-78.135,27.472-90.93,65.462H0v19.692h20.925c-0.403,3.565-0.656,7.175-0.656,10.846
c0,52.933,43.063,95.99,95.995,95.99s96-43.058,96-95.99c0-0.552-0.073-1.085-0.083-1.635h0.069
c0-6.952,9.88-14.702,24.053-14.702c14.178,0,24.053,7.75,24.053,14.702h0.073c-0.01,0.549-0.083,1.083-0.083,1.635
c0,52.933,43.058,95.99,95.99,95.99s96-43.058,96-95.99c0-3.671-0.253-7.281-0.656-10.846h20.935V205.774z M116.264,312.611
c-42.072,0-76.303-34.231-76.303-76.298c0-42.077,34.231-76.308,76.303-76.308c42.077,0,76.308,34.231,76.308,76.308
C192.572,278.38,158.341,312.611,116.264,312.611z M356.337,312.611c-42.067,0-76.298-34.231-76.298-76.298
c0-42.077,34.231-76.308,76.298-76.308c42.077,0,76.308,34.231,76.308,76.308C432.644,278.38,398.413,312.611,356.337,312.611z" />
</svg>
{{ content | striptags | wordcount | readingTime }}
<span></span>
</div>
</div>
{% if image %}
<img src="{{ image }}" alt="{{ imageAlt }}" />
{% endif %}
{{ content | safe }}
{% include "social.njk" %}
<div class="block w-full h-1 border-solid border-b-1 border-gray-700"></div>
<div class="additions flex flex-col sm:flex-row gap-3 mt-6 px-4 w-full">
{% set previousPost = collections.posts | getPreviousCollectionItem(page) %}
{% set nextPost = collections.posts | getNextCollectionItem(page) %}
<a href="{{ previousPost.url }}" class="flex-1">
<div class="h-24 border-1 border-gray-700 leading-pagination text-center{% if previousPost.url %} hover:bg-indigo-900 dark:hover:bg-indigo-900 hover:text-gray-200 dark:hover:text-gray-200 cursor-pointer{% else %} cursor-not-allowed{% endif %}">
<span class="text-xs block mt-1 mb-3">Older</span>
<span class="text-xl">{% if not previousPost.url %} - {% else %}{{ previousPost.data.title | truncate(40) }}{% endif %}</span>
</div>
</a>
<a href="{{ nextPost.url }}" class="flex-1">
<div class="h-24 border-1 border-gray-700 leading-pagination text-center{% if nextPost.url %} hover:bg-indigo-900 dark:hover:bg-indigo-900 hover:text-gray-200 dark:hover:text-gray-200 cursor-pointer{% else %} cursor-not-allowed{% endif %} overflow-auto">
<span class="text-xs block mt-1 mb-3">Newer</span>
<span class="text-xl">{% if not nextPost.url %} - {% else %}{{ nextPost.data.title | truncate(40) }}{% endif %}</span>
</div>
</a>
</div>
{% set relatedPosts = related %}
{% if relatedPosts %}
<div class="block w-full h-1 border-solid border-b-1 border-gray-700"></div>
<span class="content text-gray-800 dark:text-gray-400">Related</span>
<div class="content additions flex mt-6 border-solid border-b-1 border-gray-700 mr-8 lg:mr-24">
{% for post in relatedPosts %}
<a href="{{ post.url }}" class="flex-1">
<div class="h-24 m-1 border-1 border-gray-700 leading-pagination text-center hover:bg-indigo-900 hover:text-gray-200 rounded">
<span class="text-xs block mt-1 mb-3">Recommended</span>
<span class="text-xl">{{ post.title }}</span>
</div>
</a>
{% endfor %}
</div>
{% endif %}
</div>

View File

@ -0,0 +1,12 @@
{% if collections.recents %}
<div id="recents" class="ml-1 mt-12 p-2 border-solid border-l-1 border-gray-300 dark:border-gray-700 mb-16 text-gray-800 dark:text-gray-400">
<span class="ml-1">Recent updates</span>
<ul class="list-none mt-2 pt-2 pl-2 pr-4 text-xs">
{% for recent in collections.recents %}
<li class="truncate{% if not loop.last %} pb-4{% endif %}">
<a href="{{ recent.url }}">{{ recent.data.title }}</a>
</li>
{% endfor %}
</ul>
</div>
{% endif %}

View File

@ -0,0 +1,115 @@
<div id="sidebar-contents" class="dark:bg-sidebar-dark bg-sidebar-light grid-cols-1 gap-4 pt-8 h-screen grid w-56 xl:w-72">
<div>
<a href="/">
<img alt="{{ siteconfig.imageAlt }}" src="{{ siteconfig.image }}" class="rounded-full h-36 border dark:border-gray-100 border-indigo-600" height="144px" width="144px" />
</a>
<div class="text-center font-semibold pt-6 text-gray-400">
<a href="/" class="mx-auto">{{ siteconfig.title }}</a>
</div>
<div class="text-center text-sm pt-3 text-gray-400">
{{ siteconfig.description }}
</div>
</div>
<div class="menu">
<ul class="list-none pl-0">
<li class="navitem text-center uppercase {{ page.url | isHomeLink('/') }}">
<a href="/" class="block no-underline">Posts</a>
</li>
<li class="navitem text-center uppercase {{ page.url | isActiveLink('/who') }}">
<a href="/who/" class="block no-underline">Who</a>
</li>
<li class="navitem text-center uppercase {{ page.url | isActiveLink('/why') }}">
<a href="/why/" class="block no-underline">Why</a>
</li>
<li class="navitem text-center uppercase {{ page.url | isActiveLink('/archive') }}">
<a href="/archive/" class="block no-underline">Archive</a>
</li>
</ul>
</div>
<div class="row-end-5 grid grid-cols-3 xl:grid-cols-6 gap-5 mx-auto place-content-end mb-12">
<a onclick="toggleDarkLightMode(false)" href="#" class="w-5 h-5">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 612.001 612.001" class="lighttoggle">
<title>Light mode icon</title>
<g>
<path d="M306,149.341c-86.382,0-156.661,70.278-156.661,156.661c0,86.382,70.278,156.66,156.661,156.66
s156.66-70.278,156.66-156.66C462.66,219.618,392.382,149.341,306,149.341z" />
<path d="M274.194,117.278h63.612c5.032,0,9.356-2.101,11.863-5.763c2.508-3.662,2.9-8.453,1.079-13.146L315.749,8.257
c-2.789-7.184-7.305-8.256-9.749-8.256s-6.96,1.073-9.749,8.255l-35,90.114c-1.821,4.692-1.427,9.482,1.079,13.145
C264.837,115.178,269.162,117.278,274.194,117.278z" />
<path d="M337.806,494.723h-63.612c-5.032,0-9.357,2.102-11.863,5.764c-2.506,3.663-2.9,8.453-1.079,13.145l34.999,90.114
c2.789,7.182,7.305,8.254,9.749,8.254c2.444,0,6.96-1.072,9.749-8.254l34.999-90.115c1.821-4.69,1.429-9.48-1.079-13.144
C347.162,496.825,342.838,494.723,337.806,494.723z" />
<path d="M127.54,190.824c2.412,5.477,7.028,8.746,12.348,8.746c3.644,0,7.257-1.608,10.174-4.526l44.981-44.98
c3.558-3.558,5.13-8.102,4.312-12.466c-0.819-4.362-3.928-8.028-8.532-10.056l-88.467-38.973c-2.233-0.983-4.336-1.482-6.25-1.482
c-3.201,0-5.959,1.415-7.568,3.882c-1.357,2.081-2.454,5.747,0.031,11.389L127.54,190.824z" />
<path d="M484.46,421.178c-2.412-5.477-7.027-8.746-12.346-8.746c-3.645,0-7.259,1.609-10.177,4.527l-44.981,44.98
c-3.558,3.559-5.13,8.104-4.312,12.466c0.818,4.362,3.929,8.028,8.532,10.055l88.466,38.974c2.233,0.983,4.336,1.482,6.25,1.482
c3.201,0,5.959-1.417,7.568-3.882c1.358-2.083,2.455-5.748-0.03-11.389L484.46,421.178z" />
<path d="M461.937,195.044c2.918,2.918,6.532,4.526,10.176,4.526c5.319,0,9.934-3.269,12.348-8.746l38.972-88.465
c2.486-5.643,1.389-9.308,0.031-11.389c-1.609-2.467-4.367-3.882-7.568-3.882c-1.914,0-4.017,0.499-6.251,1.483l-88.466,38.97
c-4.604,2.029-7.715,5.694-8.532,10.057c-0.818,4.363,0.754,8.908,4.312,12.466L461.937,195.044z" />
<path d="M150.063,416.959c-2.918-2.918-6.532-4.527-10.177-4.527c-5.319,0-9.934,3.269-12.346,8.746l-38.972,88.465
c-2.486,5.643-1.389,9.308-0.031,11.39c1.609,2.466,4.368,3.882,7.568,3.882c1.914,0,4.017-0.499,6.251-1.484l88.466-38.972
c4.604-2.028,7.715-5.694,8.532-10.056c0.818-4.362-0.753-8.907-4.312-12.466L150.063,416.959z" />
<path d="M603.745,296.251l-90.111-34.996c-1.942-0.755-3.896-1.137-5.806-1.137c-7.593,0-13.104,5.921-13.104,14.078l0.001,63.613
c0,8.157,5.511,14.078,13.104,14.078c1.912,0,3.866-0.382,5.806-1.136l90.112-34.999c7.182-2.79,8.254-7.306,8.254-9.751
C612.001,303.558,610.926,299.04,603.745,296.251z" />
<path d="M104.173,351.886c7.594,0,13.106-5.921,13.106-14.078v-63.613c0-8.157-5.511-14.078-13.106-14.078
c-1.912,0-3.864,0.382-5.805,1.136L8.255,296.251C1.073,299.04,0,303.556,0,306.001c0,2.444,1.072,6.96,8.255,9.752l90.111,34.996
C100.308,351.503,102.261,351.886,104.173,351.886z" />
</g>
</svg>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 455 455" class="darktoggle">
<title>Dark mode icon</title>
<g>
<polygon points="320.18,162.705 280.63,171.052 307.72,201.052 303.437,241.245 340.34,224.751 377.243,241.245 372.96,201.052
400.05,171.052 360.5,162.705 340.34,127.67 " />
<polygon points="440,325.677 414.091,320.208 400.883,297.253 387.675,320.208 361.766,325.677 379.513,345.33 376.708,371.661
400.884,360.855 425.063,371.661 422.254,345.329 " />
<path d="M218,227.5c0-89.167,51.306-166.338,126-203.64C313.443,8.6,278.978,0,242.5,0C116.855,0,15,101.855,15,227.5
S116.855,455,242.5,455c36.478,0,70.943-8.6,101.5-23.86C269.306,393.838,218,316.667,218,227.5z" />
</g>
</svg>
</a>
{% if siteconfig.socialLinkedIn %}
<a href="https://linkedin.com/in/{{ siteconfig.socialLinkedIn }}" aria-label="Open LinkedIn profile" target="_blank" class="w-5 h-5" rel="noopener">
<svg role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<title>LinkedIn icon</title>
<path d="M20.447 20.452h-3.554v-5.569c0-1.328-.027-3.037-1.852-3.037-1.853 0-2.136 1.445-2.136 2.939v5.667H9.351V9h3.414v1.561h.046c.477-.9 1.637-1.85 3.37-1.85 3.601 0 4.267 2.37 4.267 5.455v6.286zM5.337 7.433c-1.144 0-2.063-.926-2.063-2.065 0-1.138.92-2.063 2.063-2.063 1.14 0 2.064.925 2.064 2.063 0 1.139-.925 2.065-2.064 2.065zm1.782 13.019H3.555V9h3.564v11.452zM22.225 0H1.771C.792 0 0 .774 0 1.729v20.542C0 23.227.792 24 1.771 24h20.451C23.2 24 24 23.227 24 22.271V1.729C24 .774 23.2 0 22.222 0h.003z" />
</svg>
</a>
{% endif %}
{% if siteconfig.socialGitHub %}
<a href="https://github.com/{{ siteconfig.socialGitHub }}" aria-label="Open GitHub profile" target="_blank" class="w-5 h-5" rel="noopener">
<svg role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<title>GitHub icon</title>
<path d="M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12" />
</svg>
</a>
{% endif %}
{% if siteconfig.socialTwitter %}
<a href="https://twitter.com/{{ siteconfig.socialTwitter }}" aria-label="Open Twitter profile" target="_blank" class="w-5 h-5" rel="noopener">
<svg role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<title>Twitter icon</title>
<path d="M23.953 4.57a10 10 0 01-2.825.775 4.958 4.958 0 002.163-2.723c-.951.555-2.005.959-3.127 1.184a4.92 4.92 0 00-8.384 4.482C7.69 8.095 4.067 6.13 1.64 3.162a4.822 4.822 0 00-.666 2.475c0 1.71.87 3.213 2.188 4.096a4.904 4.904 0 01-2.228-.616v.06a4.923 4.923 0 003.946 4.827 4.996 4.996 0 01-2.212.085 4.936 4.936 0 004.604 3.417 9.867 9.867 0 01-6.102 2.105c-.39 0-.779-.023-1.17-.067a13.995 13.995 0 007.557 2.209c9.053 0 13.998-7.496 13.998-13.985 0-.21 0-.42-.015-.63A9.935 9.935 0 0024 4.59z"/>
</svg>
</a>
{% endif %}
{% if siteconfig.socialYouTube %}
<a href="https://www.youtube.com/channel/{{ siteconfig.socialYouTube }}" aria-label="Open YouTube channel" target="_blank" class="w-5 h-5" rel="noopener">
<svg role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" class="p-0.5">
<title>YouTube icon</title>
<path d="M23.498,3.186 C23.2220635,2.14660344 22.4135596,1.33276534 21.376,1.05 C19.505,0.545 12,0.545 12,0.545 C12,0.545 4.495,0.545 2.623,1.05 C1.5859324,1.33325676 0.777941373,2.14696201 0.502,3.186 C0,5.07 0,9 0,9 C0,9 0,12.93 0.502,14.814 C0.777936456,15.8533966 1.58644041,16.6672347 2.624,16.95 C4.495,17.455 12,17.455 12,17.455 C12,17.455 19.505,17.455 21.377,16.95 C22.4146666,16.6674185 23.2232481,15.8535024 23.499,14.814 C24,12.93 24,9 24,9 C24,9 24,5.07 23.498,3.186 Z M8.545,13.432 L8.545,3.432 L18.545,8.432 L8.545,13.432 Z" id="Shape"></path>
</svg>
</a>
{% endif %}
<a href="{{ '/blog.rss' | absoluteUrl }}" aria-label="Open RSS feed" target="_blank" class="w-5 h-5" rel="noopener">
<svg role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 -256 1792 1792">
<title>RSS icon</title>
<g transform="matrix(1,0,0,-1,212.61017,1346.1695)" id="g2991">
<path d="M 384,192 Q 384,112 328,56 272,0 192,0 112,0 56,56 0,112 0,192 q 0,80 56,136 56,56 136,56 80,0 136,-56 56,-56 56,-136 z M 896,69 Q 898,41 879,21 861,0 832,0 H 697 Q 672,0 654,16.5 636,33 634,58 612,287 449.5,449.5 287,612 58,634 33,636 16.5,654 0,672 0,697 v 135 q 0,29 21,47 17,17 43,17 h 5 Q 229,883 375,815.5 521,748 634,634 748,521 815.5,375 883,229 896,69 z m 512,-2 Q 1410,40 1390,20 1372,0 1344,0 H 1201 Q 1175,0 1156.5,17.5 1138,35 1137,60 1125,275 1036,468.5 947,662 804.5,804.5 662,947 468.5,1036 275,1125 60,1138 35,1139 17.5,1157.5 0,1176 0,1201 v 143 q 0,28 20,46 18,18 44,18 h 3 Q 329,1395 568.5,1288 808,1181 994,994 1181,808 1288,568.5 1395,329 1408,67 z" />
</g>
</svg>
</a>
</div>
</div>

View File

@ -0,0 +1,54 @@
<div class="social flex flex-row-reverse mt-8 mb-4">
<a
href="#"
aria-label="Copy page link to clipboard"
onclick="copyUrlToClipboard()"
class="link"
>
<span></span>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 50 50">
<path d="M24 30.2c0 .2.1.5.1.8 0 1.4-.5 2.6-1.5 3.6l-2 2c-1 1-2.2 1.5-3.6 1.5-2.8 0-5.1-2.3-5.1-5.1 0-1.4.5-2.6 1.5-3.6l2-2c1-1 2.2-1.5 3.6-1.5.3 0 .5 0 .8.1l1.5-1.5c-.7-.3-1.5-.4-2.3-.4-1.9 0-3.6.7-4.9 2l-2 2c-1.3 1.3-2 3-2 4.9 0 3.8 3.1 6.9 6.9 6.9 1.9 0 3.6-.7 4.9-2l2-2c1.3-1.3 2-3 2-4.9 0-.8-.1-1.6-.4-2.3L24 30.2z" />
<path d="M33 10.1c-1.9 0-3.6.7-4.9 2l-2 2c-1.3 1.3-2 3-2 4.9 0 .8.1 1.6.4 2.3l1.5-1.5c0-.2-.1-.5-.1-.8 0-1.4.5-2.6 1.5-3.6l2-2c1-1 2.2-1.5 3.6-1.5 2.8 0 5.1 2.3 5.1 5.1 0 1.4-.5 2.6-1.5 3.6l-2 2c-1 1-2.2 1.5-3.6 1.5-.3 0-.5 0-.8-.1l-1.5 1.5c.7.3 1.5.4 2.3.4 1.9 0 3.6-.7 4.9-2l2-2c1.3-1.3 2-3 2-4.9 0-3.8-3.1-6.9-6.9-6.9z" />
<path d="M20 31c-.3 0-.5-.1-.7-.3-.4-.4-.4-1 0-1.4l10-10c.4-.4 1-.4 1.4 0s.4 1 0 1.4l-10 10c-.2.2-.4.3-.7.3z" />
</svg>
</a>
<a
href="https://www.facebook.com/sharer/sharer.php?u={{ page.url | absoluteUrl }}"
target="_blank"
rel="noopener"
aria-label="Share on Facebook (opens new tab)"
>
<span></span>
<svg
viewBox="0 0 9 18"
xmlns="http://www.w3.org/2000/svg"
class="facebook"
>
<path
d="M6.115 0c-2.173 0-3.66 1.5-3.66 4.23v2.363H0v3.195h2.455V18h2.926V9.788h2.45l.37-3.195H5.38V4.545c0-.922.236-1.545 1.42-1.545h1.507V.128A18.2 18.2 0 006.115 0z"
></path>
</svg>
</a>
<a
href="https://www.linkedin.com/sharing/share-offsite/?url={{ page.url | absoluteUrl }}"
target="_blank"
rel="noopener"
aria-label="Share on LinkedIn (opens new tab)">
<span></span>
<svg role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" class="linkedin">
<path d="M20.447 20.452h-3.554v-5.569c0-1.328-.027-3.037-1.852-3.037-1.853 0-2.136 1.445-2.136 2.939v5.667H9.351V9h3.414v1.561h.046c.477-.9 1.637-1.85 3.37-1.85 3.601 0 4.267 2.37 4.267 5.455v6.286zM5.337 7.433c-1.144 0-2.063-.926-2.063-2.065 0-1.138.92-2.063 2.063-2.063 1.14 0 2.064.925 2.064 2.063 0 1.139-.925 2.065-2.064 2.065zm1.782 13.019H3.555V9h3.564v11.452zM22.225 0H1.771C.792 0 0 .774 0 1.729v20.542C0 23.227.792 24 1.771 24h20.451C23.2 24 24 23.227 24 22.271V1.729C24 .774 23.2 0 22.222 0h.003z" />
</svg>
</a>
<a
href="https://twitter.com/intent/tweet/?text={{ title }}&url={{ page.url | absoluteUrl }}&via={{ siteconfig.twitterHandle }}"
target="_blank"
rel="noopener"
aria-label="Share on Twitter (opens new tab)"
class="fill">
<span></span>
<svg role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" class="twitter">
<path d="M23.953 4.57a10 10 0 01-2.825.775 4.958 4.958 0 002.163-2.723c-.951.555-2.005.959-3.127 1.184a4.92 4.92 0 00-8.384 4.482C7.69 8.095 4.067 6.13 1.64 3.162a4.822 4.822 0 00-.666 2.475c0 1.71.87 3.213 2.188 4.096a4.904 4.904 0 01-2.228-.616v.06a4.923 4.923 0 003.946 4.827 4.996 4.996 0 01-2.212.085 4.936 4.936 0 004.604 3.417 9.867 9.867 0 01-6.102 2.105c-.39 0-.779-.023-1.17-.067a13.995 13.995 0 007.557 2.209c9.053 0 13.998-7.496 13.998-13.985 0-.21 0-.42-.015-.63A9.935 9.935 0 0024 4.59z"/>
</svg>
</a>
<span class="text-sm mr-1">Share: </span>
</div>

View File

@ -0,0 +1,9 @@
{% set outline = content | toc %}
{% if outline %}
<div id="toc" class="list-none ml-1 pt-2 pr-2 border-solid border-l-1 border-gray-300 dark:border-gray-700 text-gray-800 dark:text-gray-400 sticky top-8">
<span class="pl-2">Content</span>
<div class="text-xs pt-2 pr-4">
{{ outline | safe }}
</div>
</div>
{% endif %}

31
content/archive.njk Normal file
View File

@ -0,0 +1,31 @@
---
eleventyExcludeFromCollections: true
layout: base.njk
title: Archive
meta:
robots:
value: noindex
---
<div class="postlist pb-8 mt-8 font-light dark:font-extralight pt-4">
{% for post in collections.posts | reverse %}
{% set currentYear = post | year %}
{% set previousYear = collections.posts | getPreviousCollectionItem(post) | year %}
{% if loop.first %}
<span class="text-2xl">{{ currentYear }}</span>
<ul class="mt-3">
{% endif %}
<li class="bg-gradient-to-r from-white via-gray-100 to-white dark:via-dark-middle dark:from-dark-outer dark:to-dark-outer mb-4 px-6 py-2 truncate">
<span class="text-lg tabular-nums">{{ post.date | day }}</span>
<span class="text-sm font-extralight">{{ post.date | month }}</span>
<a href="{{ post.url }}" class="ml-6">{{ post.data.title }}</a>
</li>
{% if loop.last %}
</ul>
{% elif currentYear != previousYear %}
</ul>
<span class="text-2xl">{{ previousYear }}</span>
<ul class="mt-3">
{% endif %}
{% endfor %}
</div>

112
content/index.njk Normal file
View File

@ -0,0 +1,112 @@
---
eleventyExcludeFromCollections: true
layout: base.njk
title: Posts
pagination:
data: collections.posts
size: 10
alias: posts
reverse: true
meta:
robots:
value: noindex
---
<div class="postlist pb-8 mt-8 font-light dark:font-extralight">
{% for post in posts %}
<div>
<div class="pb-4 pt-4">
<a class="text-indigo-900 dark:text-indigo-300 text-xl dark:font-light font-normal postlistheading" href="{{ post.url | url }}">{{ post.data.title }}</a>
</div>
<div class="pb-4 text-gray-900 dark:text-gray-400">{% excerpt post %}</div>
<div class="pb-6 text-sm text-gray-500 dark:text-gray-400 border-b-1 border-gray-800 dark:border-gray-700 border-opacity-20 flex">
<div class="flex">
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 600.112 600.111" class="h-5 w-4 mr-2">
<path d="M537.423,52.563h-59.836V21.92c0-11.83-9.591-21.42-21.42-21.42c-11.83,0-21.42,9.59-21.42,21.42v30.642H165.364V21.92
c0-11.83-9.59-21.42-21.42-21.42c-11.83,0-21.42,9.59-21.42,21.42v30.642H62.687c-32.058,0-58.14,26.082-58.14,58.14v430.77
c0,32.059,26.082,58.139,58.14,58.139h474.736c32.059,0,58.141-26.08,58.141-58.139v-430.77
C595.564,78.645,569.482,52.563,537.423,52.563z M47.387,110.703c0-8.451,6.85-15.3,15.3-15.3h59.837v24.443
c0,11.83,9.59,21.42,21.42,21.42s21.42-9.59,21.42-21.42V95.403h269.383v24.443c0,11.83,9.59,21.42,21.42,21.42
c11.829,0,21.42-9.59,21.42-21.42V95.403h59.836c8.45,0,15.3,6.85,15.3,15.3v53.856H47.387V110.703z M552.723,541.473
c0,8.449-6.85,15.301-15.3,15.301H62.687c-8.45,0-15.3-6.852-15.3-15.301V207.399h505.336V541.473z" />
<path d="M537.423,600.111H62.687c-32.334,0-58.64-26.306-58.64-58.639v-430.77c0-32.334,26.306-58.64,58.64-58.64h59.336V21.92
c0-12.087,9.833-21.92,21.92-21.92c12.086,0,21.92,9.833,21.92,21.92v30.142h268.384V21.92c0-12.087,9.833-21.92,21.92-21.92
s21.92,9.833,21.92,21.92v30.143h59.336c32.335,0,58.641,26.306,58.641,58.64v430.77
C596.064,573.806,569.758,600.111,537.423,600.111z M62.687,53.062c-31.783,0-57.64,25.857-57.64,57.64v430.77
c0,31.782,25.857,57.639,57.64,57.639h474.736c31.783,0,57.641-25.856,57.641-57.639v-430.77c0-31.783-25.857-57.64-57.641-57.64
h-60.336V21.92c0-11.536-9.385-20.92-20.92-20.92s-20.92,9.385-20.92,20.92v31.142H164.864V21.92
c0-11.536-9.385-20.92-20.92-20.92c-11.536,0-20.92,9.385-20.92,20.92v31.142H62.687z M537.423,557.273H62.687
c-8.712,0-15.8-7.088-15.8-15.801V206.899h506.336v334.574C553.223,550.186,546.135,557.273,537.423,557.273z M47.887,207.899
v333.574c0,8.161,6.639,14.801,14.8,14.801h474.736c8.16,0,14.8-6.64,14.8-14.801V207.899H47.887z M553.223,165.059H46.887
v-54.356c0-8.712,7.088-15.8,15.8-15.8h60.337v24.943c0,11.535,9.385,20.92,20.92,20.92s20.92-9.385,20.92-20.92V94.903h270.383
v24.943c0,11.535,9.385,20.92,20.92,20.92s20.92-9.385,20.92-20.92V94.903h60.336c8.712,0,15.8,7.088,15.8,15.8V165.059z
M47.887,164.059h504.336v-53.356c0-8.161-6.64-14.8-14.8-14.8h-59.336v23.943c0,12.087-9.833,21.92-21.92,21.92
s-21.92-9.833-21.92-21.92V95.903H165.864v23.943c0,12.087-9.833,21.92-21.92,21.92s-21.92-9.833-21.92-21.92V95.903H62.687
c-8.161,0-14.8,6.639-14.8,14.8V164.059z" />
</svg>
{{ post.date | readableDate }}
</div>
<div class="readingtime flex" data-words="{{ post.templateContent | striptags | wordcount | formatWords }} words">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 472.615 472.615" class="h-5 w-8 mr-2 ml-12">
<path d="M472.615,205.774h-25.344c-12.797-37.99-48.686-65.462-90.935-65.462c-43.224,0-79.837,28.73-91.823,68.087
c-7.582-5.011-17.305-8.115-28.211-8.115c-10.903,0-20.624,3.103-28.207,8.114c-11.989-39.357-48.608-68.085-91.831-68.085
c-42.249,0-78.135,27.472-90.93,65.462H0v19.692h20.925c-0.403,3.565-0.656,7.175-0.656,10.846
c0,52.933,43.063,95.99,95.995,95.99s96-43.058,96-95.99c0-0.552-0.073-1.085-0.083-1.635h0.069
c0-6.952,9.88-14.702,24.053-14.702c14.178,0,24.053,7.75,24.053,14.702h0.073c-0.01,0.549-0.083,1.083-0.083,1.635
c0,52.933,43.058,95.99,95.99,95.99s96-43.058,96-95.99c0-3.671-0.253-7.281-0.656-10.846h20.935V205.774z M116.264,312.611
c-42.072,0-76.303-34.231-76.303-76.298c0-42.077,34.231-76.308,76.303-76.308c42.077,0,76.308,34.231,76.308,76.308
C192.572,278.38,158.341,312.611,116.264,312.611z M356.337,312.611c-42.067,0-76.298-34.231-76.298-76.298
c0-42.077,34.231-76.308,76.298-76.308c42.077,0,76.308,34.231,76.308,76.308C432.644,278.38,398.413,312.611,356.337,312.611z" />
</svg>
{{ post.templateContent | striptags | wordcount | readingTime }}
<span></span>
</div>
</div>
</div>
{% endfor %}
</div>
<div class="mt-2 mx-8 lg:ml-32">
<ul class="pagination flex list-none center pb-8 font-light dark:font-extralight">
{% if pagination.pageNumber > 0 %}
<li class="mr-3">
<div class="leading-relaxed">
{% if not pagination.href.first %}
<span class="block cursor-not-allowed">&lt;&lt;</a>
{% else %}
<a href="{{ pagination.href.first }}" class="block hover:no-underline">&lt;&lt;</a>
{% endif %}
</div>
</li>
<li class="mr-3">
<div class="leading-relaxed">
{% if not pagination.href.previous %}
<span class="block cursor-not-allowed">&lt;</a>
{% else %}
<a href="{{ pagination.href.previous }}" class="block hover:no-underline">&lt;</a>
{% endif %}
</div>
</li>
{% endif %}
{% for pageEntry in pagination.pages %}
{% if loop.index0 < 6 and pagination.hrefs[loop.index0 + pagination.pageNumber] %}
<li class="mr-3">
<div class="leading-pagination{% if pagination.hrefs[loop.index0 + pagination.pageNumber] === page.url %} active{% endif %}">
<a href="{{ pagination.hrefs[loop.index0 + pagination.pageNumber] }}" class="block hover:no-underline">{{ loop.index + pagination.pageNumber }}</a>
</div>
</li>
{% endif %}
{% endfor %}
{% if (pagination.pageNumber + 5) < pagination.pages.length %}
<li class="mr-3">
<div class="leading-relaxed">
<a href="{{ pagination.href.next }}" class="block hover:no-underline">&gt;</a>
</div>
</li>
<li class="mr-3">
<div class="leading-relaxed">
<a href="{{ pagination.href.last }}" class="block hover:no-underline">&gt;&gt;</a>
</div>
</li>
{% endif %}
</ul>
</div>

View File

@ -0,0 +1,7 @@
---
title: Random post
date: 2021-01-26
image: /assets/images/2021/coolimage.jpg
---
This is a super random post.

View File

@ -0,0 +1,50 @@
---
title: Your first amazing blog post
date: 2021-01-27
image: /assets/images/2021/coolimage.jpg
---
This is your first blog post. And it can contain a lot of stuff. So let's go through a few things.
## Code for fun or profit
Having good looking (totally opiniated here, no) code snippets is mandatory. At least for myself. The included Prism config is based on [this selection](https://prismjs.com/download.html#themes=prism-coy&languages=markup+css+clike+javascript+bash+docker+java+regex+ruby+rust+scala+shell-session+typescript&plugins=show-language+toolbar). No standard theme has been chosen, there are some modifications to fit the Chirpy UX.
Find here an excerpt of the visuals.
### Bash
```bash
if [ -z "$HEROKU_PWA_APP_NAME" ]; then
echo "Please provide HEROKU_PWA_APP_NAME environment variable"
exit 1
fi
```
### JavaScript
```javascript
const jestLwcConfig = require("@lwc/jest-preset");
import { resolve } from "path";
export const jestConfig = {
...jestLwcConfig,
resolver: resolve(__dirname, "../utils/resolver.js")
};
```
## Images, images, images
First, you'll notice this stunning picture. I got it royalty-free from Pixabay (great site). Now, that's not the point that I want to make here. The image is not added via Markdown, but instead via the `image` value of the Markdown front matter. As it's always good IMHO to start with a visual you'll get a standardized way of addding an image asset.
Second, the image is optimized for your browser size _and_ browser. Depending on what you currently use as browser you'll get i. e. a JPG or a WebP file. All in the right size for the screen. Obviously, all images have standard settings for lazy loading etc.
## Headings all over the place
It's all standard markdown to render the headings, and as well to display the table of contents (TOC) on the right side.
Note: never ever add a first level heading (aka: `h1`) to your page. This will break accessibility, as the title is already an h1, and will be represented as such in the rendered HTML.
## Other stuff
As to be expected you can do all the things that are _standard_ Markdown. So tables, blockquotes etc. And if you prefer to add custom Markdown functionality, just extend the configuration with custom [markdown-it](https://github.com/markdown-it/markdown-it) plugins.

View File

@ -0,0 +1,7 @@
---
title: Your first amazing blog post
date: 2021-01-24
image: /assets/images/2021/coolimage.jpg
---
Call me "Random Post" if you like.

4
content/posts/posts.json Normal file
View File

@ -0,0 +1,4 @@
{
"layout": "post.njk",
"tags": ["posts"]
}

11
content/utils/404.njk Normal file
View File

@ -0,0 +1,11 @@
---
eleventyExcludeFromCollections: true
layout: base.njk
permalink: /404.html
title: Ooooops
---
<div class="content page pt-4">
Sorry, this site doesn't exist.<br /><br />
<img src="/assets/images/404.png" alt="Sad panda can't find this site" />
</div>

6
content/utils/robots.njk Normal file
View File

@ -0,0 +1,6 @@
---
eleventyExcludeFromCollections: true
permalink: /robots.txt
---
Sitemap: {{ siteconfig.url }}/sitemap.xml

27
content/utils/rss.njk Normal file
View File

@ -0,0 +1,27 @@
---
eleventyExcludeFromCollections: true
permalink: /blog.rss
---
<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
<title>{{ siteconfig.title }}</title>
<subtitle>{{ siteconfig.description }}</subtitle>
<link href="{{ '/blog.rss' | absoluteUrl }}" rel="self"/>
<link href="{{ siteconfig.url }}"/>
<updated>{{ collections.posts | rssLastUpdatedDate }}</updated>
<id>{{ siteconfig.url }}</id>
<author>
<name>{{ siteconfig.author }}</name>
</author>
{%- for post in collections.posts | reverse %}
{% set absolutePostUrl %}{{ post.url | url | absoluteUrl(metadata.url) }}{% endset %}
<entry>
<title>{{ post.data.title }}</title>
<link href="{{ absolutePostUrl }}"/>
<updated>{{ post.date | rssDate }}</updated>
<id>{{ absolutePostUrl }}</id>
<content type="html">{{ post.templateContent | htmlToAbsoluteUrls(absolutePostUrl) | truncate(400) }}</content>
</entry>
{%- endfor %}
</feed>

15
content/utils/sitemap.njk Normal file
View File

@ -0,0 +1,15 @@
---
eleventyExcludeFromCollections: true
permalink: /sitemap.xml
---
<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
{% for item in collections.posts %}
<url>
<loc>{{ siteconfig.url }}{{ item.url }}</loc>
<lastmod>{{ item.date | sitemapdate }}</lastmod>
<changefreq>{{ item.data.sitemapChangefreq | default("daily") }}</changefreq>
<priority>{{ item.data.sitemapPriority | default(0.8) }}</priority>
</url>
{% endfor %}
</urlset>

7
content/who/index.md Normal file
View File

@ -0,0 +1,7 @@
---
eleventyExcludeFromCollections: true
layout: page.njk
title: Who
---
And here you can write about yourself. Or just change in the `sidebar.njk` the label and write something different.

7
content/why/index.md Normal file
View File

@ -0,0 +1,7 @@
---
eleventyExcludeFromCollections: true
layout: page.njk
title: Why
---
You can put your own page here.

18
netlify.toml Normal file
View File

@ -0,0 +1,18 @@
[build]
publish = "_site/"
command = "yarn build"
[[headers]]
for = "/*"
[headers.values]
cache-control = '''
max-age=3600,
public'''
[[headers]]
for = "/assets/*"
[headers.values]
cache-control = '''
max-age=31536000,
public,
immutable'''

61
package.json Normal file
View File

@ -0,0 +1,61 @@
{
"name": "eleventy-chirpy-blog-template",
"version": "1.0.0",
"private": true,
"license": "MIT",
"scripts": {
"build": "npm run build:css && npm run build:js && npm run build:eleventy ",
"build:css": "postcss assets/css/site.css -o assets/css/site-build.css && postcss assets/css/prism.css -o assets/css/prism-build.css",
"build:eleventy": "eleventy",
"build:js": "rollup -c scripts/rollup.config.js",
"dev": "concurrently \"npm:dev:*\"",
"dev:css:prism": "postcss assets/css/prism.css -o assets/css/prism-build.css --watch",
"dev:css:site": "postcss assets/css/site.css -o assets/css/site-build.css --watch",
"dev:eleventy": "eleventy --serve",
"dev:js": "rollup -c scripts/rollup.config.js -w",
"lint": "npm run lint:markdown && npm run lint:js",
"lint:markdown": "npx markdownlint-cli2 \"./content/**/*.md\"",
"lint:js": "eslint '**/*.js'",
"postinstall:": "husky install",
"precommit": "npm run prettier && npm run lint",
"prettier": "prettier --write \"**/*.{css,html,js,json,md,yaml,yml}\"",
"prettier:verify": "prettier --list-different \"**/*.{css,html,js,json,md,yaml,yml}\""
},
"dependencies": {
"@11ty/eleventy": "^0.12.1",
"@11ty/eleventy-plugin-rss": "^1.1.1",
"@rollup/plugin-replace": "^2.4.2",
"autoprefixer": "^10.2.6",
"crypto-js": "^4.0.0",
"cssnano": "^5.0.6",
"date-fns": "^2.22.1",
"dotenv": "^10.0.0",
"eleventy-plugin-toc": "^1.1.0",
"html-minifier": "^4.0.0",
"jsdom": "^16.6.0",
"markdown-it": "^12.0.6",
"markdown-it-anchor": "^7.1.0",
"postcss": "^8.3.2",
"postcss-cli": "^8.3.1",
"rollup": "^2.51.2",
"rollup-plugin-postcss": "^4.0.0",
"rollup-plugin-terser": "^7.0.2",
"sharp": "^0.28.3",
"shelljs": "^0.8.4",
"tailwindcss": "^2.1.4"
},
"devDependencies": {
"concurrently": "^6.2.0",
"eslint": "^7.28.0",
"eslint-config-prettier": "^8.3.0",
"eslint-plugin-inclusive-language": "^2.1.1",
"husky": "^6.0.0",
"markdownlint": "^0.23.1",
"markdownlint-cli2": "^0.1.3",
"prettier": "^2.3.1"
},
"volta": {
"node": "14.15.4",
"yarn": "1.22.10"
}
}

22
postcss.config.js Normal file
View File

@ -0,0 +1,22 @@
const isProduction = process.env.NODE_ENV === "production";
let cssnano = undefined;
// We want optimization only in production
if (isProduction) {
cssnano = {
cssnano: {
preset: "default"
}
};
}
const plugins = {
tailwindcss: {},
autoprefixer: {},
...(isProduction && cssnano)
};
module.exports = {
plugins
};

31
scripts/rollup.config.js Normal file
View File

@ -0,0 +1,31 @@
require("dotenv").config();
import { terser } from "rollup-plugin-terser";
const replace = require("@rollup/plugin-replace");
const siteconfig = require("../content/_data/siteconfig");
const isProduction = process.env.NODE_ENV === "production";
export default {
input: "src/index.js",
treeshake: false,
output: [
{
file: "assets/js/min.js",
sourcemap: !isProduction,
format: "esm"
}
],
plugins: [
// Minify JS in production mode
isProduction && terser(),
// Replace env variables for Algolia, if enabled
siteconfig.algoliaSearch &&
siteconfig.algoliaSearch.enabled &&
replace({
"process.env.ALGOLIA_INDEX": `netlify_${siteconfig.algoliaSearch.siteId}_${siteconfig.algoliaSearch.branch}_all`,
"process.env.ALGOLIA_APP_ID": siteconfig.algoliaSearch.appId,
"process.env.ALGOLIA_SEARCH_API_KEY":
siteconfig.algoliaSearch.searchApiKey
})
]
};

92
src/index.js Normal file
View File

@ -0,0 +1,92 @@
import { scrollToTop, slideLeftSidebar } from "./ui";
import { searchContent, toggleSearch } from "./search";
import { copyUrlToClipboard } from "./utils";
window.scrollToTop = function () {
scrollToTop();
};
window.slideLeftSidebar = function () {
slideLeftSidebar();
};
window.searchContent = function (e) {
window.clearTimeout(window.searchDelay);
window.searchDelay = setTimeout(() => {
searchContent(e);
}, 300);
};
window.toggleSearch = function () {
toggleSearch();
};
window.copyUrlToClipboard = function () {
copyUrlToClipboard();
};
document.addEventListener("DOMContentLoaded", function () {
const sections = document.querySelectorAll(".content h2,.content h3");
const menu = document.querySelectorAll("nav.toc a");
const hash = window.location.hash;
if (hash) {
for (const item of menu) {
if (menu.href === hash) {
item.classList.add("active");
}
}
}
const makeActive = (link) => {
if (menu[link]) {
menu[link].classList.add("active");
}
};
const removeActive = (link) => menu[link].classList.remove("active");
const removeAllActive = () =>
[...Array(sections.length).keys()].forEach((link) =>
removeActive(link)
);
let currentActive = 0;
document
.getElementById("right-area")
.addEventListener("scroll", function () {
{
const areaEl = document.getElementById("right-area");
const barEl = document.getElementById("top-bar");
const scrollEl = document.getElementById("scroll");
const scrollTop = areaEl.scrollTop;
const currentHeading =
sections.length -
[...sections].reverse().findIndex((section) => {
return section.offsetTop - 45 <= scrollTop;
}) -
1;
if (currentHeading !== currentActive) {
removeAllActive();
currentActive = currentHeading;
makeActive(currentHeading);
}
if (scrollTop >= 45) {
barEl.classList.add("hide");
} else {
barEl.classList.remove("hide");
}
if (scrollEl) {
if (scrollTop > window.innerHeight) {
scrollEl.style.display = "flex";
} else {
scrollEl.style.display = "none";
}
}
}
});
});

131
src/search.js Normal file
View File

@ -0,0 +1,131 @@
const toggleSearch = () => {
const elMobileTopBar = document.getElementById("mobile-top-bar");
const elSearchBox = document.getElementById("search-box");
const elSearchBoxBar = document.getElementById("search-box-bar");
const elMobileSearchCancel = document.getElementById(
"mobile-search-cancel"
);
elMobileTopBar.classList.toggle("hidden");
elSearchBox.classList.toggle("w-60");
elSearchBoxBar.classList.toggle("hidden");
elSearchBoxBar.classList.toggle("w-full");
elMobileSearchCancel.classList.toggle("hidden");
elSearchBox.value = "";
hideSearchContents();
};
const showSearchContents = () => {
const mainEl = document.querySelector("main");
const tocEl = document.getElementById("toc");
const searchContainerEl = document.getElementById("search-container");
const searchDesktopTitleEl = document.getElementById(
"search-desktop-title"
);
const searchMobileTitleEl = document.getElementById("search-mobile-title");
const searchTopbarTitleEl = document.getElementById("topbar-desktop-title");
mainEl.classList.add("hidden");
if (tocEl) {
tocEl.classList.add("hidden");
}
searchContainerEl.classList.remove("hidden");
if (window.innerWidth >= 640) {
searchDesktopTitleEl.classList.remove("hidden");
} else {
searchMobileTitleEl.classList.remove("hidden");
}
searchDesktopTitleEl.textContent = "Search";
searchDesktopTitleEl.classList.remove("sm:hidden");
searchTopbarTitleEl.classList.add("sm:hidden");
};
const hideSearchContents = () => {
const mainEl = document.querySelector("main");
const tocEl = document.getElementById("toc");
const noResultsEl = document.getElementById("search-no-results");
const searchContainerEl = document.getElementById("search-container");
const searchDesktopTitleEl = document.getElementById(
"search-desktop-title"
);
const searchMobileTitleEl = document.getElementById("search-mobile-title");
const searchTopbarTitleEl = document.getElementById("topbar-desktop-title");
mainEl.classList.remove("hidden");
if (tocEl) {
tocEl.classList.remove("hidden");
}
searchContainerEl.classList.add("hidden");
searchDesktopTitleEl.classList.add("hidden");
searchDesktopTitleEl.classList.add("sm:hidden");
searchMobileTitleEl.classList.add("hidden");
searchTopbarTitleEl.classList.remove("sm:hidden");
noResultsEl.classList.add("hidden");
};
const searchContent = (e) => {
const INDEX = "process.env.ALGOLIA_INDEX";
const APP_ID = "process.env.ALGOLIA_APP_ID";
const API_ID = "process.env.ALGOLIA_SEARCH_API_KEY";
const keywords = e.target.value;
const noResultsEl = document.getElementById("search-no-results");
const searchDesktopTitleEl = document.getElementById(
"search-desktop-title"
);
const searchMobileTitleEl = document.getElementById("search-mobile-title");
if (keywords !== "" && keywords !== undefined) {
showSearchContents();
} else {
hideSearchContents();
return;
}
fetch(
`https://${APP_ID}-dsn.algolia.net/1/indexes/${INDEX}?query=${keywords}&hitsPerPage=200`,
{
headers: {
"X-Algolia-Application-Id": APP_ID,
"X-Algolia-API-Key": API_ID
}
}
)
.then((result) => {
return result.json();
})
.then((json) => {
const ulEl = document.getElementById("search-results");
while (ulEl.lastChild) {
ulEl.lastChild.remove();
}
if (json.hits.length === 0) {
noResultsEl.classList.remove("hidden");
} else {
noResultsEl.classList.add("hidden");
}
let hits = 0;
for (const item of json.hits) {
const liEl = document.createElement("li");
liEl.setAttribute(
"class",
"bg-gradient-to-r from-white via-gray-100 to-white dark:via-dark-middle dark:from-dark-outer dark:to-dark-outer mb-4 py-2 truncate"
);
const aEl = document.createElement("a");
aEl.setAttribute("href", item.url);
aEl.textContent = item.title.split("|")[0];
liEl.appendChild(aEl);
ulEl.appendChild(liEl);
hits = hits + 1;
}
searchDesktopTitleEl.textContent = `Search (${hits} hit/s)`;
searchMobileTitleEl.textContent = `${hits} hit/s`;
})
.catch((error) => {
console.log(error);
});
};
export { searchContent, toggleSearch };

17
src/ui.js Normal file
View File

@ -0,0 +1,17 @@
const scrollToTop = () => {
document.getElementById("right-area").scroll({
top: 0,
left: 0,
behavior: "smooth"
});
};
const slideLeftSidebar = () => {
const elSidebar = document.getElementById("sidebar");
const elRightArea = document.getElementById("right-area");
elSidebar.classList.toggle("-translate-x-56");
elSidebar.classList.toggle("translate-x-0");
elRightArea.classList.toggle("translate-x-56");
};
export { scrollToTop, slideLeftSidebar };

9
src/utils.js Normal file
View File

@ -0,0 +1,9 @@
const copyUrlToClipboard = async () => {
try {
await navigator.clipboard.writeText(location.href);
} catch (err) {
console.error("Failed to copy: ", err);
}
};
export { copyUrlToClipboard };

77
tailwind.config.js Normal file
View File

@ -0,0 +1,77 @@
const isProduction = process.env.NODE_ENV === "production";
let purge = false;
// We want optimization only in production
if (isProduction) {
purge = ["./content/**/*.njk", "./src/*.js"];
}
module.exports = {
purge,
darkMode: "class",
plugins: [
function ({ addUtilities }) {
const extendUnderline = {
".underline": {
textDecoration: "underline",
"text-decoration-color": "text-indigo-300",
"text-underline-position": "under"
}
};
addUtilities(extendUnderline);
}
],
variants: {
extend: {
backgroundImage: ["dark"],
fill: ["dark"],
fontWeight: ["dark"],
gradientColorStops: ["dark"],
stroke: ["dark"]
}
},
theme: {
extend: {
backgroundColor: (theme) => ({
...theme("colors"),
"dark-nav": "#242424",
"dark-body": "#1B1B1E",
"dark-heading": "#27282B"
}),
backgroundImage: () => ({
"sidebar-dark":
"radial-gradient(circle, #242424 0%, #1d1f27 100%)",
"sidebar-light":
"radial-gradient(circle,rgba(42, 30, 107, 1) 0%,rgba(35, 37, 46, 1) 100%)"
}),
borderWidth: (theme) => ({
...theme("width"),
1: "1px"
}),
gradientColorStops: (theme) => ({
...theme("colors"),
"dark-outer": "#1B1B1E",
"dark-middle": "#242424"
}),
gridTemplateColumns: {
small: "0 auto",
regular: "minmax(auto, 0fr) auto;",
topbar: "auto 18rem"
},
lineHeight: {
pagination: "1.8rem",
12: "3rem"
},
margin: {
15: "3.75rem"
},
maxWidth: {
content: "95rem"
},
textColor: {
"orange-hover": "#d2603a"
}
}
}
};

6057
yarn.lock Normal file

File diff suppressed because it is too large Load Diff