chugging away, no i am not splitting this into fine-grained commits
This commit is contained in:
@ -8,5 +8,5 @@ charset = utf-8
|
|||||||
indent_style = tab
|
indent_style = tab
|
||||||
indent_size = 2
|
indent_size = 2
|
||||||
|
|
||||||
[.{yaml,yml,md,njk}]
|
[*.{yaml,yml,md,njk}]
|
||||||
indent_style = space
|
indent_style = space
|
||||||
|
|||||||
24
_includes/footer.njk
Normal file
24
_includes/footer.njk
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
<footer>
|
||||||
|
<ul>
|
||||||
|
<li>
|
||||||
|
<a href="/colophon" title="colophon" aria-label="colophon"
|
||||||
|
{% if page.url == "/colophon/" %}aria-current="page"{% endif %}>
|
||||||
|
colophon
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li>
|
||||||
|
<a href="/" title="go home"
|
||||||
|
aria-label="go home | {{ metadata.title }} from {{ metadata.author }} in 2026"
|
||||||
|
{% if page.url == "/" %}aria-current="page"{% endif %}>
|
||||||
|
{{ metadata.title }} from {{ metadata.author }} in 2026</a>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li>
|
||||||
|
<a href="https://heckin.technology/inherentlee/leecat.art" title="source code"
|
||||||
|
aria-label="source code" target="_blank" rel="external">
|
||||||
|
src
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</footer>
|
||||||
22
_includes/header.njk
Normal file
22
_includes/header.njk
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
<header>
|
||||||
|
|
||||||
|
<a href="#main" id="skip" title="skip to main content" aria-label="skip to main content">
|
||||||
|
<i class="fa-solid fa-forward" aria-hidden="true"></i> skip
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<nav aria-label="main navigation">
|
||||||
|
<ul>
|
||||||
|
{% for entry in collections.all | eleventyNavigation %}
|
||||||
|
<li>
|
||||||
|
<a href="{{ entry.url }}" title="{{ entry.data.label }}"
|
||||||
|
{% if entry.url == page.url %}aria-current="page"{% endif %}
|
||||||
|
aria-label="{{ entry.data.label }}">
|
||||||
|
<i class="{{ entry.data.icon }}" aria-hidden="true"></i>
|
||||||
|
<span class="menu-text">{{ entry.title }}</span>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
</header>
|
||||||
@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
{# Styles #}
|
{# Styles #}
|
||||||
<link rel="stylesheet" href="/assets/css/main.css">
|
<link rel="stylesheet" href="/assets/css/main.css">
|
||||||
|
<link rel="stylesheet" href="/assets/css/nav.css">
|
||||||
|
|
||||||
{# Fonts #}
|
{# Fonts #}
|
||||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||||
@ -26,5 +27,11 @@
|
|||||||
<script src="https://kit.fontawesome.com/884dded219.js" crossorigin="anonymous"></script>
|
<script src="https://kit.fontawesome.com/884dded219.js" crossorigin="anonymous"></script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
{% include "header.njk" %}
|
||||||
|
|
||||||
|
<main id="main">
|
||||||
{{ content | safe }}
|
{{ content | safe }}
|
||||||
|
</main>
|
||||||
|
|
||||||
|
{% include "footer.njk" %}
|
||||||
<body>
|
<body>
|
||||||
6
_includes/layouts/page.njk
Normal file
6
_includes/layouts/page.njk
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
---
|
||||||
|
layout: base.njk
|
||||||
|
---
|
||||||
|
<h1>{{ title }}</h1>
|
||||||
|
|
||||||
|
{{ content | safe }}
|
||||||
8
_includes/layouts/post.njk
Normal file
8
_includes/layouts/post.njk
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
---
|
||||||
|
layout: base.njk
|
||||||
|
---
|
||||||
|
<article>
|
||||||
|
<h1>{{ title }}</h1>
|
||||||
|
|
||||||
|
{{ content | safe }}
|
||||||
|
</article>
|
||||||
@ -7,7 +7,7 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
<h2>{{ post.data.title }}</h2>
|
<h2>{{ post.data.title }}</h2>
|
||||||
<ul>
|
<ul>
|
||||||
{% for tag in post.data.tags %}
|
{% for tag in post.data.tags | removeBasicTags %}
|
||||||
<li>{{ tag }}</li>
|
<li>{{ tag }}</li>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</ul>
|
</ul>
|
||||||
|
|||||||
199
css/main.css
199
css/main.css
@ -3,10 +3,12 @@
|
|||||||
|
|
||||||
--font-family: 'Atkinson Hyperlegible Next', sans-serif;
|
--font-family: 'Atkinson Hyperlegible Next', sans-serif;
|
||||||
--font-family-code: 'Atkinson Hyperlegible Mono', monospace;
|
--font-family-code: 'Atkinson Hyperlegible Mono', monospace;
|
||||||
|
|
||||||
--color-dark: #2e303e;
|
--color-dark: #2e303e;
|
||||||
--color-dark-alt: #3c3f52;
|
--color-dark-alt: #3c3f52;
|
||||||
--color-light: #ebeeef;
|
--color-light: #ebeeef;
|
||||||
--color-light-alt: #dbe1e3;
|
--color-light-alt: #dbe1e3;
|
||||||
|
|
||||||
--color-teal-dark: #18737b;
|
--color-teal-dark: #18737b;
|
||||||
--color-teal-light: #25b0bc;
|
--color-teal-light: #25b0bc;
|
||||||
--color-pink-dark: #94195d;
|
--color-pink-dark: #94195d;
|
||||||
@ -47,8 +49,12 @@
|
|||||||
--color-blue: light-dark(var(--color-blue-dark), var(--color-blue-light));
|
--color-blue: light-dark(var(--color-blue-dark), var(--color-blue-light));
|
||||||
--color-purple: light-dark(var(--color-purple-dark), var(--color-purple-light));
|
--color-purple: light-dark(var(--color-purple-dark), var(--color-purple-light));
|
||||||
--color-grey: light-dark(var(--color-grey-dark), var(--color-grey-light));
|
--color-grey: light-dark(var(--color-grey-dark), var(--color-grey-light));
|
||||||
|
|
||||||
|
--header-offset: 3.1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Base */
|
||||||
|
|
||||||
* {
|
* {
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
@ -59,6 +65,195 @@ body {
|
|||||||
font-family: var(--font-family);
|
font-family: var(--font-family);
|
||||||
color: var(--color-text);
|
color: var(--color-text);
|
||||||
background-color: var(--color-bg);
|
background-color: var(--color-bg);
|
||||||
max-width: 60vw;
|
|
||||||
margin: 0 auto;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
main {
|
||||||
|
width: 60vw;
|
||||||
|
margin: 0 auto;
|
||||||
|
scroll-margin-top: var(--header-offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 650px) {
|
||||||
|
main {
|
||||||
|
width: 92vw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Headers */
|
||||||
|
h1, h2, h3, h4, h5, h6 {
|
||||||
|
line-height: 1.25;
|
||||||
|
color: var(--color-teal);
|
||||||
|
scroll-margin-top: var(--header-offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
margin-top: 3rem;
|
||||||
|
font-size: 3.5rem;
|
||||||
|
}
|
||||||
|
h2 {
|
||||||
|
margin-top: 1rem;
|
||||||
|
font-size: 2.2rem;
|
||||||
|
}
|
||||||
|
h3 {
|
||||||
|
margin-top: 1.5rem;
|
||||||
|
font-size: 1.6rem;
|
||||||
|
}
|
||||||
|
h4, h5, h6 {
|
||||||
|
margin-top: 1rem;
|
||||||
|
font-size: 1.2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Images */
|
||||||
|
img {
|
||||||
|
display: block;
|
||||||
|
max-width: 100%;
|
||||||
|
height: auto;
|
||||||
|
border-radius: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Paragraphs */
|
||||||
|
p {
|
||||||
|
margin: 1.25rem 0;
|
||||||
|
line-height: 1.4;
|
||||||
|
}
|
||||||
|
|
||||||
|
strong,
|
||||||
|
b {
|
||||||
|
font-weight: 900;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Links */
|
||||||
|
a {
|
||||||
|
color: var(--color-font);
|
||||||
|
border-radius: 1rem;
|
||||||
|
text-decoration: underline;
|
||||||
|
text-decoration-style: solid;
|
||||||
|
text-decoration-thickness: .2em;
|
||||||
|
text-decoration-color: var(--color-teal);
|
||||||
|
transition: text-decoration-thickness .5s;
|
||||||
|
padding: 0 .1rem; /* These stop the focus outline from covering text */
|
||||||
|
margin: 0 .1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
a:focus-visible {
|
||||||
|
text-decoration: none;
|
||||||
|
outline: .15rem solid var(--color-teal);
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (any-hover: hover) {
|
||||||
|
a:hover {
|
||||||
|
text-decoration-thickness: .4em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
a:active {
|
||||||
|
text-decoration-thickness: .4em;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Lists */
|
||||||
|
::marker {
|
||||||
|
color: var(--color-pink);
|
||||||
|
}
|
||||||
|
|
||||||
|
ul, ol, li {
|
||||||
|
margin-left: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
li {
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
li ul, li ol {
|
||||||
|
margin: .5rem 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Blockquotes */
|
||||||
|
blockquote {
|
||||||
|
margin: .5rem 1rem;
|
||||||
|
padding: 0 1rem;
|
||||||
|
border-radius: .25rem 1rem 1rem .25rem;
|
||||||
|
line-height: 1.25;
|
||||||
|
border-left: .5rem solid var(--color-pink);
|
||||||
|
}
|
||||||
|
|
||||||
|
blockquote,
|
||||||
|
blockquote p,
|
||||||
|
blockquote ol,
|
||||||
|
blockquote ul {
|
||||||
|
background-color: var(--color-bg-alt);
|
||||||
|
padding: .5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
blockquote p {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Tables */
|
||||||
|
table {
|
||||||
|
width: 100%;
|
||||||
|
border-spacing: 0; /* border collapse doesn't play nice with radii */
|
||||||
|
border-radius: .3rem;
|
||||||
|
border: thin solid var(--color-pink);
|
||||||
|
}
|
||||||
|
|
||||||
|
th {
|
||||||
|
color: var(--color-bg);
|
||||||
|
background-color: var(--color-pink);
|
||||||
|
}
|
||||||
|
|
||||||
|
th, td {
|
||||||
|
padding: .5rem;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
tr:nth-child(even) { background-color: var(--color-bg-alt); }
|
||||||
|
th:not(:first-child) { border-left: thin solid var(--color-bg); }
|
||||||
|
th:first-child { border-top-left-radius: .25rem; }
|
||||||
|
th:last-child { border-top-right-radius: .25rem; }
|
||||||
|
td:not(:first-child) { border-left: thin solid var(--color-pink); }
|
||||||
|
|
||||||
|
/* Code */
|
||||||
|
/* Syntax highlighting in highlighting.css */
|
||||||
|
code,
|
||||||
|
pre {
|
||||||
|
font-family: var(--font-family-code);
|
||||||
|
background-color: var(--color-bg-alt);
|
||||||
|
font-size: .9rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
code {
|
||||||
|
padding: .2rem;
|
||||||
|
border-radius: .25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
pre {
|
||||||
|
display: block;
|
||||||
|
margin: 1rem 0;
|
||||||
|
padding: 1rem;
|
||||||
|
line-height: 1.5;
|
||||||
|
white-space: pre-wrap;
|
||||||
|
word-break: break-word;
|
||||||
|
overflow-wrap: break-word;
|
||||||
|
tab-size: 4;
|
||||||
|
border-radius: .5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
pre code {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Times */
|
||||||
|
time {
|
||||||
|
color: var(--color-grey);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Horizontal rules */
|
||||||
|
hr {
|
||||||
|
color: var(--color-teal);
|
||||||
|
border: .25rem solid var(--color-teal);
|
||||||
|
margin: 2rem 0;
|
||||||
|
}
|
||||||
|
hr:last-child {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
190
css/nav.css
Normal file
190
css/nav.css
Normal file
@ -0,0 +1,190 @@
|
|||||||
|
/* Header */
|
||||||
|
header {
|
||||||
|
position: sticky;
|
||||||
|
top: 0;
|
||||||
|
background-color: var(--color-bg);
|
||||||
|
box-shadow: 0 .25rem .15rem var(--color-shadow);
|
||||||
|
padding: .75rem 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Header links */
|
||||||
|
header a {
|
||||||
|
border-radius: 1rem;
|
||||||
|
border: .125rem solid var(--color-pink);
|
||||||
|
color: var(--color-pink);
|
||||||
|
text-decoration: none;
|
||||||
|
padding: 0 .25rem;
|
||||||
|
box-shadow: .15rem .15rem var(--color-shadow);
|
||||||
|
font-size: 1.2rem;
|
||||||
|
padding-right: .35rem;
|
||||||
|
/* click animation handling */
|
||||||
|
position: relative;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
transition: top .05s ease-in, left .05s ease-in;
|
||||||
|
}
|
||||||
|
|
||||||
|
header a:focus-visible {
|
||||||
|
color: var(--color-bg);
|
||||||
|
border-color: var(--color-pink);
|
||||||
|
background-color: var(--color-pink);
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (any-hover: hover) {
|
||||||
|
header a:hover {
|
||||||
|
color: var(--color-bg);
|
||||||
|
border-color: var(--color-pink);
|
||||||
|
background-color: var(--color-pink);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (forced-colors: active) {
|
||||||
|
.site-header a:focus-visible {
|
||||||
|
outline-offset: .125rem;
|
||||||
|
outline: .125rem solid;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (any-hover: hover) {
|
||||||
|
.site-header a:hover {
|
||||||
|
outline-offset: .125rem;
|
||||||
|
outline: .125rem solid;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Click animation */
|
||||||
|
header a:active {
|
||||||
|
top: .1rem;
|
||||||
|
left: .1rem;
|
||||||
|
box-shadow: .05rem .05rem var(--color-shadow);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Current page */
|
||||||
|
header a[aria-current="page"] {
|
||||||
|
border-color: var(--color-teal);
|
||||||
|
color: var(--color-teal);
|
||||||
|
}
|
||||||
|
|
||||||
|
header a[aria-current="page"]:focus-visible {
|
||||||
|
color: var(--color-bg);
|
||||||
|
border-color: var(--color-teal);
|
||||||
|
background-color: var(--color-teal);
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (any-hover: hover) {
|
||||||
|
header a[aria-current="page"]:hover {
|
||||||
|
color: var(--color-bg);
|
||||||
|
background-color: var(--color-teal);
|
||||||
|
border-color: var(--color-teal);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Header link icons */
|
||||||
|
header i {
|
||||||
|
color: var(--color-teal);
|
||||||
|
padding-left: .25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
header a[aria-current="page"] i {
|
||||||
|
color: var(--color-pink);
|
||||||
|
}
|
||||||
|
|
||||||
|
header a:focus-visible i,
|
||||||
|
a[aria-current="page"] a:focus-visible i {
|
||||||
|
color: var(--color-bg);
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (any-hover: hover) {
|
||||||
|
header a:hover i,
|
||||||
|
header a[aria-current="page"]:hover i {
|
||||||
|
color: var(--color-bg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Skip link */
|
||||||
|
#skip {
|
||||||
|
left: -999px;
|
||||||
|
position: absolute;
|
||||||
|
top: auto;
|
||||||
|
width: 1px;
|
||||||
|
height: 1px;
|
||||||
|
overflow: hidden;
|
||||||
|
z-index: -99;
|
||||||
|
}
|
||||||
|
|
||||||
|
#skip:focus-visible {
|
||||||
|
display: inline-block;
|
||||||
|
left: auto;
|
||||||
|
top: auto;
|
||||||
|
width: auto;
|
||||||
|
height: auto;
|
||||||
|
overflow: auto;
|
||||||
|
margin: 0 10%;
|
||||||
|
z-index: 999;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Nav */
|
||||||
|
header ul {
|
||||||
|
display: flex;
|
||||||
|
list-style: none;
|
||||||
|
gap: 1rem;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 650px) {
|
||||||
|
.menu-text {
|
||||||
|
display: none; /* Icons only on small screens */
|
||||||
|
}
|
||||||
|
|
||||||
|
header a {
|
||||||
|
padding: .15rem .5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
header i {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Footer */
|
||||||
|
footer {
|
||||||
|
padding: 1rem 0;
|
||||||
|
font-size: .9rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
footer ul {
|
||||||
|
display: flex;
|
||||||
|
list-style: none;
|
||||||
|
gap: .5rem;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
footer li {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
footer li:nth-child(2)::before,
|
||||||
|
footer li:nth-child(2)::after {
|
||||||
|
content: " ● " / "";
|
||||||
|
color: var(--color-teal);
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 650px) {
|
||||||
|
footer ul {
|
||||||
|
flex-flow: column;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
footer li:nth-child(2)::before,
|
||||||
|
footer li:nth-child(2)::after {
|
||||||
|
content: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
footer a {
|
||||||
|
text-decoration-color: var(--color-pink);
|
||||||
|
}
|
||||||
|
|
||||||
|
footer a:focus-visible {
|
||||||
|
outline-color: var(--color-pink);
|
||||||
|
}
|
||||||
@ -1,7 +1,14 @@
|
|||||||
import { feedPlugin } from "@11ty/eleventy-plugin-rss";
|
|
||||||
import { eleventyImageTransformPlugin } from "@11ty/eleventy-img";
|
import { eleventyImageTransformPlugin } from "@11ty/eleventy-img";
|
||||||
|
import eleventyNavigationPlugin from "@11ty/eleventy-navigation";
|
||||||
|
import { feedPlugin } from "@11ty/eleventy-plugin-rss";
|
||||||
|
import syntaxHighlight from "@11ty/eleventy-plugin-syntaxhighlight";
|
||||||
|
|
||||||
export default async function(eleventyConfig) {
|
export default async function(eleventyConfig) {
|
||||||
|
/* Filters */
|
||||||
|
eleventyConfig.addFilter("removeBasicTags", (tags) => {
|
||||||
|
return tags.filter(tag => ["all", "posts", "gallery", "reference"].indexOf(tag) === -1);
|
||||||
|
});
|
||||||
|
|
||||||
/* Passthroughs */
|
/* Passthroughs */
|
||||||
eleventyConfig.addPassthroughCopy({"css": "assets/css"});
|
eleventyConfig.addPassthroughCopy({"css": "assets/css"});
|
||||||
|
|
||||||
@ -43,6 +50,12 @@ export default async function(eleventyConfig) {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/* Navigation */
|
||||||
|
eleventyConfig.addPlugin(eleventyNavigationPlugin);
|
||||||
|
|
||||||
|
/* Syntax highlighting */
|
||||||
|
eleventyConfig.addPlugin(syntaxHighlight);
|
||||||
|
|
||||||
/* Watch when serving */
|
/* Watch when serving */
|
||||||
eleventyConfig.addWatchTarget("css");
|
eleventyConfig.addWatchTarget("css");
|
||||||
};
|
};
|
||||||
@ -51,6 +64,7 @@ export const config = {
|
|||||||
dir: {
|
dir: {
|
||||||
input: "src",
|
input: "src",
|
||||||
includes: "../_includes",
|
includes: "../_includes",
|
||||||
|
layouts: "../_includes/layouts",
|
||||||
data: "../_data"
|
data: "../_data"
|
||||||
},
|
},
|
||||||
markdownTemplateEngine: "njk",
|
markdownTemplateEngine: "njk",
|
||||||
|
|||||||
42
package-lock.json
generated
42
package-lock.json
generated
@ -11,7 +11,9 @@
|
|||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@11ty/eleventy": "^3.1.2",
|
"@11ty/eleventy": "^3.1.2",
|
||||||
"@11ty/eleventy-img": "^6.0.4",
|
"@11ty/eleventy-img": "^6.0.4",
|
||||||
"@11ty/eleventy-plugin-rss": "^2.0.4"
|
"@11ty/eleventy-navigation": "^1.0.5",
|
||||||
|
"@11ty/eleventy-plugin-rss": "^2.0.4",
|
||||||
|
"@11ty/eleventy-plugin-syntaxhighlight": "^5.0.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@11ty/dependency-tree": {
|
"node_modules/@11ty/dependency-tree": {
|
||||||
@ -165,6 +167,20 @@
|
|||||||
"url": "https://opencollective.com/11ty"
|
"url": "https://opencollective.com/11ty"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@11ty/eleventy-navigation": {
|
||||||
|
"version": "1.0.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/@11ty/eleventy-navigation/-/eleventy-navigation-1.0.5.tgz",
|
||||||
|
"integrity": "sha512-zb6xe29cM9viSdYtZywKIkJw2HIROyBINdBcFWC9uD0c/jYOTAex5nwy3HNEuh5t6/Ld/S9V4gEizfmeYuYpCQ==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"dependency-graph": "^1.0.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/11ty"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@11ty/eleventy-plugin-bundle": {
|
"node_modules/@11ty/eleventy-plugin-bundle": {
|
||||||
"version": "3.0.7",
|
"version": "3.0.7",
|
||||||
"resolved": "https://registry.npmjs.org/@11ty/eleventy-plugin-bundle/-/eleventy-plugin-bundle-3.0.7.tgz",
|
"resolved": "https://registry.npmjs.org/@11ty/eleventy-plugin-bundle/-/eleventy-plugin-bundle-3.0.7.tgz",
|
||||||
@ -201,6 +217,20 @@
|
|||||||
"url": "https://opencollective.com/11ty"
|
"url": "https://opencollective.com/11ty"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@11ty/eleventy-plugin-syntaxhighlight": {
|
||||||
|
"version": "5.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@11ty/eleventy-plugin-syntaxhighlight/-/eleventy-plugin-syntaxhighlight-5.0.2.tgz",
|
||||||
|
"integrity": "sha512-T6xVVRDJuHlrFMHbUiZkHjj5o1IlLzZW+1IL9eUsyXFU7rY2ztcYhZew/64vmceFFpQwzuSfxQOXxTJYmKkQ+A==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"prismjs": "^1.30.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/11ty"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@11ty/eleventy-utils": {
|
"node_modules/@11ty/eleventy-utils": {
|
||||||
"version": "2.0.7",
|
"version": "2.0.7",
|
||||||
"resolved": "https://registry.npmjs.org/@11ty/eleventy-utils/-/eleventy-utils-2.0.7.tgz",
|
"resolved": "https://registry.npmjs.org/@11ty/eleventy-utils/-/eleventy-utils-2.0.7.tgz",
|
||||||
@ -2059,6 +2089,16 @@
|
|||||||
"node": ">=12"
|
"node": ">=12"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/prismjs": {
|
||||||
|
"version": "1.30.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.30.0.tgz",
|
||||||
|
"integrity": "sha512-DEvV2ZF2r2/63V+tK8hQvrR2ZGn10srHbXviTlcv7Kpzw8jWiNTqbVgjO3IY8RxrrOUF8VPMQQFysYYYv0YZxw==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/prr": {
|
"node_modules/prr": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz",
|
||||||
|
|||||||
@ -4,7 +4,7 @@
|
|||||||
"description": "Lee's personal website, take 2, built with 11ty",
|
"description": "Lee's personal website, take 2, built with 11ty",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"serve": "rm -rf _site && npx @11ty/eleventy --serve"
|
"start": "rm -rf _site && npx @11ty/eleventy --serve --quiet"
|
||||||
},
|
},
|
||||||
"keywords": [],
|
"keywords": [],
|
||||||
"author": "Lee Cattarin",
|
"author": "Lee Cattarin",
|
||||||
@ -13,6 +13,8 @@
|
|||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@11ty/eleventy": "^3.1.2",
|
"@11ty/eleventy": "^3.1.2",
|
||||||
"@11ty/eleventy-img": "^6.0.4",
|
"@11ty/eleventy-img": "^6.0.4",
|
||||||
"@11ty/eleventy-plugin-rss": "^2.0.4"
|
"@11ty/eleventy-navigation": "^1.0.5",
|
||||||
|
"@11ty/eleventy-plugin-rss": "^2.0.4",
|
||||||
|
"@11ty/eleventy-plugin-syntaxhighlight": "^5.0.2"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
BIN
src/img/2026/cormorant.jpg
Normal file
BIN
src/img/2026/cormorant.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 82 KiB |
@ -1,11 +1,14 @@
|
|||||||
---
|
---
|
||||||
layout: base.njk
|
|
||||||
eleventyExcludeFromCollections: true
|
|
||||||
pagination:
|
pagination:
|
||||||
data: collections.posts
|
data: collections.posts
|
||||||
size: 13
|
size: 13
|
||||||
reverse: true
|
reverse: true
|
||||||
alias: postlist
|
alias: postlist
|
||||||
|
eleventyNavigation:
|
||||||
|
key: home
|
||||||
|
order: 3
|
||||||
|
icon: fa fa-solid fa-crow
|
||||||
|
title: home
|
||||||
---
|
---
|
||||||
<h1>Home</h1>
|
<h1>Home</h1>
|
||||||
|
|
||||||
|
|||||||
8
src/pages/about.md
Normal file
8
src/pages/about.md
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
---
|
||||||
|
eleventyNavigation:
|
||||||
|
key: about
|
||||||
|
order: 4
|
||||||
|
title: about
|
||||||
|
icon: fa-regular fa-user
|
||||||
|
label: about Lee
|
||||||
|
---
|
||||||
3
src/pages/colophon.md
Normal file
3
src/pages/colophon.md
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
---
|
||||||
|
title: colophon
|
||||||
|
---
|
||||||
8
src/pages/contact.md
Normal file
8
src/pages/contact.md
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
---
|
||||||
|
eleventyNavigation:
|
||||||
|
key: contact
|
||||||
|
order: 5
|
||||||
|
title: contact
|
||||||
|
icon: fa-solid fa-envelope-open-text
|
||||||
|
label: contact Lee
|
||||||
|
---
|
||||||
16
src/pages/gallery.njk
Normal file
16
src/pages/gallery.njk
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
---
|
||||||
|
pagination:
|
||||||
|
data: collections["gallery"]
|
||||||
|
size: 13
|
||||||
|
reverse: true
|
||||||
|
alias: postlist
|
||||||
|
eleventyNavigation:
|
||||||
|
key: gallery
|
||||||
|
order: 2
|
||||||
|
title: gallery
|
||||||
|
icon: fa-regular fa-images
|
||||||
|
label: view the gallery
|
||||||
|
---
|
||||||
|
<p>the gallery page is for finished art</p>
|
||||||
|
|
||||||
|
{% include "postlist.njk" %}
|
||||||
6
src/pages/pages.11tydata.js
Normal file
6
src/pages/pages.11tydata.js
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
export default {
|
||||||
|
permalink: function ({ title }) {
|
||||||
|
return `/${this.slugify(title)}/index.html`;
|
||||||
|
},
|
||||||
|
layout: "page.njk"
|
||||||
|
};
|
||||||
16
src/pages/reference.njk
Normal file
16
src/pages/reference.njk
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
---
|
||||||
|
pagination:
|
||||||
|
data: collections["reference"]
|
||||||
|
size: 13
|
||||||
|
reverse: true
|
||||||
|
alias: postlist
|
||||||
|
eleventyNavigation:
|
||||||
|
key: reference
|
||||||
|
order: 2
|
||||||
|
title: reference
|
||||||
|
icon: fa-regular fa-folder-open
|
||||||
|
label: read reference posts
|
||||||
|
---
|
||||||
|
<p>the reference page is for informational posts</p>
|
||||||
|
|
||||||
|
{% include "postlist.njk" %}
|
||||||
123
src/pages/style.md
Normal file
123
src/pages/style.md
Normal file
@ -0,0 +1,123 @@
|
|||||||
|
---
|
||||||
|
title: style
|
||||||
|
---
|
||||||
|
|
||||||
|
Adaped from an introduction to Markdown in order to test and display styling of basic components of the site.
|
||||||
|
|
||||||
|
## Heading level 2
|
||||||
|
|
||||||
|
Since your title (defined in the front matter) is your heading level 1, you should never use another heading level 1 in your body.
|
||||||
|
|
||||||
|
### Heading level 3
|
||||||
|
|
||||||
|
The number of pound signs determines the heading level.
|
||||||
|
|
||||||
|
#### Heading level 4
|
||||||
|
|
||||||
|
It's also important not to skip heading levels. Don't jump from a 2 to a 4 or similar.
|
||||||
|
|
||||||
|
## Paragraphs
|
||||||
|
|
||||||
|
You'll notice that I am putting blank lines between headings and plain text. This is necessary, or they won't render correctly.
|
||||||
|
|
||||||
|
It's also important to put a blank line in between each paragraph. See what happens without it:
|
||||||
|
This is supposed to be a new paragraph, but it isn't.
|
||||||
|
|
||||||
|
### Inline styles
|
||||||
|
|
||||||
|
We can, of course, create **bold** and *italicized* text, or `inline monospace text`.
|
||||||
|
|
||||||
|
We can also create links, like this [link to the home page](/).
|
||||||
|
|
||||||
|
## Horizontal lines
|
||||||
|
|
||||||
|
Sometimes you want to insert a visual break in your text that isn't just a new paragraph. You can use three dashes to create a horizontal line:
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
This text will be below the line.
|
||||||
|
|
||||||
|
## Lists
|
||||||
|
|
||||||
|
### Unordered lists
|
||||||
|
|
||||||
|
Unordered lists can be created with dashes or asterisks. With dashes:
|
||||||
|
|
||||||
|
- this is an item
|
||||||
|
- this is another item
|
||||||
|
|
||||||
|
With asterisks:
|
||||||
|
|
||||||
|
* this is an item
|
||||||
|
* this is another item
|
||||||
|
|
||||||
|
### Ordered lists
|
||||||
|
|
||||||
|
Ordered (numbered) lists can be created with (surprise!) numbers. You can write numbers as you would normally, *or* you can just write the number 1 over and over, like so:
|
||||||
|
|
||||||
|
1. this is item 1
|
||||||
|
1. despite being written with a 1, this is item 2
|
||||||
|
|
||||||
|
This allows you to insert more information into lists in the future without having to renumber every following item.
|
||||||
|
|
||||||
|
### Nested lists
|
||||||
|
|
||||||
|
Both unordered and ordered lists can be nested. Just tab the nested section inwards:
|
||||||
|
|
||||||
|
- this is an item
|
||||||
|
- this is nested below it
|
||||||
|
- this is also nested
|
||||||
|
- this is another item
|
||||||
|
|
||||||
|
You can mix unordered and ordered lists when you nest.
|
||||||
|
|
||||||
|
## Quotes
|
||||||
|
|
||||||
|
You can always just use quotation marks, of course, but if you are quoting a larger chunk of text it can be nice to use a blockquote.
|
||||||
|
|
||||||
|
You format a blockquote by starting the line with a caret:
|
||||||
|
|
||||||
|
> This is a quote, and it will render differently than a paragraph.
|
||||||
|
|
||||||
|
If you want a quote to have multiple separate paragraphs, and still contiguously display as one quote, make sure to put a caret on the empty line between the paragraphs.
|
||||||
|
|
||||||
|
> This is a multi-paragraph quote.
|
||||||
|
>
|
||||||
|
> Here's the second paragraph.
|
||||||
|
>
|
||||||
|
> - Blockquotes can also have lists
|
||||||
|
> - They still have the caret at the front
|
||||||
|
|
||||||
|
## Monospace
|
||||||
|
|
||||||
|
You can write single words `in monospace`, or create code blocks:
|
||||||
|
|
||||||
|
```
|
||||||
|
3 backticks surround code blocks
|
||||||
|
```
|
||||||
|
|
||||||
|
Code blocks can have syntax highlighting:
|
||||||
|
|
||||||
|
```html
|
||||||
|
<h1>Hello, world</h1>
|
||||||
|
```
|
||||||
|
|
||||||
|
## Tables
|
||||||
|
|
||||||
|
Tables in Markdown are kind of annoying to format. You use the pipe (`|`) character as well as dashes.
|
||||||
|
|
||||||
|
```
|
||||||
|
| Header 1 | Header 2 |
|
||||||
|
|---|---|
|
||||||
|
| data 1a | data 1b |
|
||||||
|
| data 2a | data 2b |
|
||||||
|
| data 3a | data 3b |
|
||||||
|
```
|
||||||
|
|
||||||
|
When I remove the monospace block, you can see how this formats:
|
||||||
|
|
||||||
|
| Header 1 | Header 2 |
|
||||||
|
|---|---|
|
||||||
|
| data 1a | data 1b |
|
||||||
|
| data 2a | data 2b |
|
||||||
|
| data 3a | data 3b |
|
||||||
137
src/posts/2026/2026-01-05-moving-images.md
Normal file
137
src/posts/2026/2026-01-05-moving-images.md
Normal file
@ -0,0 +1,137 @@
|
|||||||
|
---
|
||||||
|
title: moving images
|
||||||
|
image:
|
||||||
|
src: 2026/cormorant.jpg
|
||||||
|
alt: "Image unrelated to post. A cormorant, a type of black waterfowl, poses with wings spread on a buoy in Puget Sound. Off to the left, another bird floats."
|
||||||
|
tags:
|
||||||
|
- reference
|
||||||
|
- software
|
||||||
|
---
|
||||||
|
|
||||||
|
## problem statement
|
||||||
|
|
||||||
|
today I decided to finally clean up the `assets/img` directory for this site. Since 2022, when I started this project, I've just been adding images directly to that directory with no further segmentation - messy of me, I know! It's gotten unwieldy and I'm starting to get worried about generic names leading to duplicates at some point, particularly for the non-gallery images where I have a tendency to [use](/stationery-exchange) [lots](/favorite-git-flag) [of](/trans-networks) [mushroom](/no-politics) [images](/domain-and-site-setup).
|
||||||
|
|
||||||
|
so it's time to move them into year-based folders. Let's talk about how I did that. `bash` away!
|
||||||
|
|
||||||
|
(want to [skip right to the completed script?](#result))
|
||||||
|
|
||||||
|
## find
|
||||||
|
|
||||||
|
let's start with the basics: a list of posts. `find` gets us everything under a specific directory - in this case, the `_posts` directory. We can filter out the directories a few different ways, but I piped the `find` output through a basic `grep` looking for `.md` in the filename.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
for FILE in $(find _posts | grep .md)
|
||||||
|
do
|
||||||
|
# TBD
|
||||||
|
done
|
||||||
|
```
|
||||||
|
|
||||||
|
## grep
|
||||||
|
|
||||||
|
`grep` can also help us get image names with the regex `"name:.+jpg|png"`. I add `name:` to the regex because there are *very occasionally* images that aren't the featured image for the post, and those don't fit the pattern of `name: <img>`. Since there's so few of those, I ended up handling them manually.
|
||||||
|
|
||||||
|
to make `grep` work with regex, it needs the `-E` flag.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
# gives us
|
||||||
|
# name: <img>
|
||||||
|
# note the 4 spaces at the beginning of the line
|
||||||
|
IMAGE_LINE=$(cat $FILE | grep -E "name:.+jpg|png$")
|
||||||
|
```
|
||||||
|
|
||||||
|
## cut
|
||||||
|
|
||||||
|
that output gets us the full line of text that includes the image filename. Let's trim out what we actually want.
|
||||||
|
|
||||||
|
below, `-d` sets a delimiter, and `-f` chooses what field we want to return. Because there's 4 spaces before `name`, our field index is actually pretty high - `cut` is creating 4 empty strings.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
IMAGE=$(echo $IMAGE_LINE | cut -d ' ' -f 6 -)
|
||||||
|
```
|
||||||
|
|
||||||
|
or, for brevity:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
IMAGE=$(cat $FILE | grep -E "name:.+jpg|png$" | cut -d ' ' -f 6 -)
|
||||||
|
```
|
||||||
|
|
||||||
|
with `cut`, we can also get the year of the post:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
YEAR=$(echo $FILE | cut -d '/' -f 2 -)
|
||||||
|
```
|
||||||
|
|
||||||
|
## sed
|
||||||
|
|
||||||
|
there's two major things we need to do with the information we've gathered:
|
||||||
|
|
||||||
|
1. replace the image filename in-place in the post's markdown file
|
||||||
|
1. move the image file from its original location into a new directory
|
||||||
|
|
||||||
|
we can do replacement with `sed`, where our pattern should be something like this: `s/$IMAGE/$YEAR\/&\` (the `&` subs in the found string - in this case `$IMAGE`). We could also use comma separators if we don't want to escape the slash, like `s,$IMAGE,$YEAR/&,` - I did this for ease of reading.
|
||||||
|
|
||||||
|
by default, `sed` prints to standard output, so we'll tell it to edit in-place instead with `-i`. Here's our full `sed` command:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
sed "s,$IMAGE,$YEAR/&," -i $FILE
|
||||||
|
```
|
||||||
|
|
||||||
|
## mving and shaking
|
||||||
|
|
||||||
|
(my mom thinks I'm funny.)
|
||||||
|
|
||||||
|
now we'll handle moving the image file from its original location into a new directory. let's create our image paths, source and destination:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
IMG_DIR=assets/img
|
||||||
|
NEW_IMAGE=$IMG_DIR/$YEAR/$IMAGE
|
||||||
|
IMAGE=$IMG_DIR/$IMAGE
|
||||||
|
```
|
||||||
|
|
||||||
|
trying to `mv` the images will immediately cause problems, because the year directories don't exist yet. A simple check gets us past that:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
if [ ! -d $IMG_DIR/$YEAR ]
|
||||||
|
then
|
||||||
|
mkdir $IMG_DIR/$YEAR
|
||||||
|
fi
|
||||||
|
```
|
||||||
|
|
||||||
|
finally, we can `mv` the image:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
mv $IMAGE $NEW_IMAGE
|
||||||
|
```
|
||||||
|
|
||||||
|
## result
|
||||||
|
|
||||||
|
here's our final script:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
for FILE in $(find _posts | grep .md)
|
||||||
|
do
|
||||||
|
# parse image and year info
|
||||||
|
IMAGE=$(cat $FILE | grep -E "name:.+jpg|png$" | cut -d ' ' -f 6 -)
|
||||||
|
YEAR=$(echo $FILE | cut -d '/' -f 2 -)
|
||||||
|
|
||||||
|
# replace in-place in file
|
||||||
|
sed "s,$IMAGE,$YEAR/&," -i $FILE
|
||||||
|
|
||||||
|
# path creation
|
||||||
|
IMG_DIR=assets/img
|
||||||
|
NEW_IMAGE=$IMG_DIR/$YEAR/$IMAGE
|
||||||
|
IMAGE=$IMG_DIR/$IMAGE
|
||||||
|
|
||||||
|
# create dir for year if it doesn't exist
|
||||||
|
if [ ! -d $IMG_DIR/$YEAR ]
|
||||||
|
then
|
||||||
|
mkdir $IMG_DIR/$YEAR
|
||||||
|
fi
|
||||||
|
|
||||||
|
# move image
|
||||||
|
mv $IMAGE $NEW_IMAGE
|
||||||
|
done
|
||||||
|
```
|
||||||
|
|
||||||
|
questions? errors? [ping me!](/contact)
|
||||||
@ -2,7 +2,7 @@
|
|||||||
title: Sample
|
title: Sample
|
||||||
date: 2026-02-17
|
date: 2026-02-17
|
||||||
tags:
|
tags:
|
||||||
- test
|
- gallery
|
||||||
image:
|
image:
|
||||||
src: 2026/sample-0.jpg
|
src: 2026/sample-0.jpg
|
||||||
alt: filler
|
alt: filler
|
||||||
|
|||||||
@ -5,5 +5,5 @@ export default {
|
|||||||
tags: [
|
tags: [
|
||||||
"posts"
|
"posts"
|
||||||
],
|
],
|
||||||
layout: "base.njk"
|
layout: "post.njk"
|
||||||
};
|
};
|
||||||
|
|||||||
3
src/src.11tydata.js
Normal file
3
src/src.11tydata.js
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
export default {
|
||||||
|
layout: "base.njk"
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user