all but posts omg

This commit is contained in:
2026-02-19 12:07:10 -08:00
parent 1913d9c46e
commit 96049f57ee
40 changed files with 1030 additions and 192 deletions

View File

@ -1,10 +1,9 @@
import { DateTime } from "luxon"; import { DateTime } from "luxon";
export default function(eleventyConfig) { export default function(eleventyConfig) {
/* Human-readable dates */ // Return the keys used in an object
eleventyConfig.addFilter("readableDate", (dateObj, format, zone) => { eleventyConfig.addFilter("getKeys", target => {
return DateTime.fromJSDate(dateObj, { zone: zone || "utc" }) return Object.keys(target);
.toLocaleString(DateTime.DATE_FULL);
}); });
/* For <time> elements */ /* For <time> elements */
@ -12,8 +11,20 @@ export default function(eleventyConfig) {
return DateTime.fromJSDate(dateObj, { zone: "utc" }).toFormat('yyyy-LL-dd'); return DateTime.fromJSDate(dateObj, { zone: "utc" }).toFormat('yyyy-LL-dd');
}); });
/* Human-readable dates */
eleventyConfig.addFilter("readableDate", (dateObj, format, zone) => {
return DateTime.fromJSDate(dateObj, { zone: zone || "utc" })
.toLocaleString(DateTime.DATE_FULL);
});
/* Filter out structural tags */ /* Filter out structural tags */
eleventyConfig.addFilter("removeBasicTags", (tags) => { eleventyConfig.addFilter("removeBasicTags", (tags) => {
return tags.filter(tag => ["all", "posts", "gallery", "reference", "tagPagination"].indexOf(tag) === -1); return tags.filter(tag => ["all", "posts", "gallery", "reference", "tagPagination"].indexOf(tag) === -1);
}); });
/* What it says */
eleventyConfig.addFilter("sortAlphabetically", strings =>
(strings || []).sort((b, a) => b.localeCompare(a))
);
}; };

View File

@ -23,26 +23,24 @@
<script src="https://kit.fontawesome.com/884dded219.js" crossorigin="anonymous"></script> <script src="https://kit.fontawesome.com/884dded219.js" crossorigin="anonymous"></script>
{# Styles #} {# Styles #}
<link rel="stylesheet" href="/assets/css/main.css"> <style>{% include "css/main.css" %}</style>
<link rel="stylesheet" href="/assets/css/nav.css"> <style>{% include "css/nav.css" %}</style>
<link rel="stylesheet" href="/assets/css/highlighting.css"> <style>{% include "css/print.css" %}</style>
<link rel="stylesheet" href="/assets/css/print.css">
<link rel="stylesheet" href="/assets/css/postlist.css">
<link rel="stylesheet" href="/assets/css/post.css">
{# Heading anchors #} <style>{% getBundle "css" %}</style>
<script type="module">{% include "node_modules/@zachleat/heading-anchors/heading-anchors.js" %}</script>
<script type="module">{% getBundle "js" %}</script>
</head> </head>
<body> <body>
{% include "header.njk" %} {% include "header.njk" %}
<main id="main"> <main id="main">
<heading-anchors>
{{ content | safe }} {{ content | safe }}
<hr> <hr>
</heading-anchors>
</main> </main>
{% include "footer.njk" %} {% include "footer.njk" %}
<!-- This page `{{ page.url }}` was built on {% currentBuildDate %} -->
<body> <body>

View File

@ -0,0 +1,8 @@
---
layout: base.njk
---
{% css %}{% include "css/lists.css" %}{% endcss %}
<h1>{{ title }}</h1>
{{ content | safe }}

View File

@ -1,6 +1,10 @@
--- ---
layout: base.njk layout: base.njk
--- ---
{% js %}{% include "node_modules/@zachleat/heading-anchors/heading-anchors.js" %}{% endjs %}
<heading-anchors content="<i class='fa-solid fa-anchor'></i>">
<h1>{{ title }}</h1> <h1>{{ title }}</h1>
{{ content | safe }} {{ content | safe }}
</heading-anchors>

View File

@ -1,6 +1,11 @@
--- ---
layout: base.njk layout: base.njk
--- ---
{% css %}{% include "css/post.css" %}{% endcss %}
{% css %}{% include "css/highlighting.css" %}{% endcss %}
{% js %}{% include "node_modules/@zachleat/heading-anchors/heading-anchors.js" %}{% endjs %}
<heading-anchors content="<i class='fa-solid fa-anchor'></i>">
<article> <article>
<h1>{{ title }}</h1> <h1>{{ title }}</h1>
@ -15,7 +20,7 @@ layout: base.njk
<ul class="post-tags"> <ul class="post-tags">
{% for tag in relevantTags %} {% for tag in relevantTags %}
<li> <li>
{% set tagUrl %}/tag/{{ tag | slugify }}/{% endset %} {% set tagUrl %}/tags/{{ tag | slugify }}/{% endset %}
<a href="{{ tagUrl }}">{{ tag }}</a> <a href="{{ tagUrl }}">{{ tag }}</a>
</li> </li>
{% endfor %} {% endfor %}
@ -29,3 +34,4 @@ layout: base.njk
{{ content | safe }} {{ content | safe }}
</article> </article>
</heading-anchors>

View File

@ -0,0 +1,6 @@
---
layout: base.njk
---
{% css %}{% include "css/resume.css" %}{% endcss %}
{{ content | safe }}

View File

@ -4,14 +4,14 @@
{% if olderHref %} {% if olderHref %}
<li class="older"> <li class="older">
<a href="{{ olderHref }}"> <a href="{{ olderHref }}">
<i class="fa-solid fa-hand-point-left"></i> older <i class="fa-solid fa-hand-point-left" aria-hidden="true"></i> older
</a> </a>
</li> </li>
{% endif %} {% endif %}
{% if newerHref %} {% if newerHref %}
<li class="newer"> <li class="newer">
<a href="{{ newerHref }}"> <a href="{{ newerHref }}">
newer <i class="fa-solid fa-hand-point-right"></i> newer <i class="fa-solid fa-hand-point-right" aria-hidden="true"></i>
</a> </a>
</li> </li>
{% endif %} {% endif %}

25
_includes/webring.njk Normal file
View File

@ -0,0 +1,25 @@
<nav class="webring" aria-labelledby="{{ id }}">
<h3 id="{{ id }}">{{ name }}</h3>
<p>This site is a member of the <a rel="external" target="_blank" href="{{ url }}">{{ name }}</a>. Navigate through the webring:</p>
<ul>
<li class="prev">
<a rel="external" referrerpolicy="strict-origin" href="{{ prev }}" target="_blank" title="{{ name }} - previous website">
<i class="fa-solid fa-hand-point-left" aria-hidden="true"></i>
prev
</a>
</li>
<li class="rand">
<a rel="external" referrerpolicy="strict-origin" href="{{ rand }}" target="_blank" title="{{ name }} - random website">
<i class="fa-solid fa-dice" aria-hidden="true"></i>
rand
<i class="fa-solid fa-dice" aria-hidden="true"></i>
</a>
</li>
<li class="next">
<a rel="external" referrerpolicy="strict-origin" href="{{ next }}" target="_blank" title="{{ name }} - next website">
next
<i class="fa-solid fa-hand-point-right" aria-hidden="true"></i>
</a>
</li>
</ul>
</nav>

View File

@ -1,28 +1,31 @@
#postlist { #postlist,
#taglist {
list-style: none; list-style: none;
}
#postlist, .post,
#taglist, .tag {
margin: 0; margin: 0;
} }
.post { /* Odd-numbered posts & tag layout/coloration */
margin: 0; .post:nth-child(odd) .postlink,
} .tag:nth-child(odd) .taglink {
/* Odd-numbered post layout/coloration */
.post:nth-child(odd) .postlink {
grid-template-areas: grid-template-areas:
'img h2' 'img h2'
'img tags' 'img info'
'img .'; 'img .';
grid-template-columns: 45% auto; grid-template-columns: 45% auto;;
--color-primary: var(--color-teal); --color-primary: var(--color-teal);
--color-accent: var(--color-pink); --color-accent: var(--color-pink);
} }
/* Even-numbered post layout/coloration */ /* Even-numbered posts & tags layout/coloration */
.post:nth-child(even) .postlink { .post:nth-child(even) .postlink,
.tag:nth-child(even) .taglink {
grid-template-areas: grid-template-areas:
'h2 img' 'h2 img'
'tags img' 'info img'
'. img'; '. img';
grid-template-columns: auto 45%; grid-template-columns: auto 45%;
--color-primary: var(--color-pink); --color-primary: var(--color-pink);
@ -31,24 +34,25 @@
/* Layout for all posts on mobile */ /* Layout for all posts on mobile */
@media (max-width: 650px) { @media (max-width: 650px) {
.post:nth-child(n) .postlink { .post:nth-child(n) .postlink,
.tag:nth-child(n) .taglink {
grid-template-areas: grid-template-areas:
'img' 'img'
'h2' 'h2'
'tags'; 'info';
grid-template-columns: auto; grid-template-columns: auto;
} }
} }
/* Link */ /* Link */
.postlink { .postlink,
.taglink {
display: grid; display: grid;
border: .25rem solid; border: .25rem solid var(--color-primary);
border-radius: 1.25rem; border-radius: 1.25rem;
box-shadow: .35rem .35rem var(--color-shadow); box-shadow: .35rem .35rem var(--color-shadow);
margin: 2rem 0; margin: 2rem 0;
text-decoration: none; text-decoration: none;
border-color: var(--color-primary);
/* Click animation handling */ /* Click animation handling */
position: relative; position: relative;
top: 0; top: 0;
@ -56,26 +60,30 @@
transition: top .05s ease-in, left .05s ease-in; transition: top .05s ease-in, left .05s ease-in;
} }
.postlink:focus-visible { .postlink:focus-visible,
.taglink:focus-visible {
background-color: var(--color-primary); background-color: var(--color-primary);
outline: none; outline: none;
} }
@media (any-hover: hover) { @media (any-hover: hover) {
.postlink:hover { .postlink:hover,
.taglink:hover {
background-color: var(--color-primary); background-color: var(--color-primary);
} }
} }
/* Forced colors */ /* Forced colors */
@media (forced-colors: active) { @media (forced-colors: active) {
.postlink:focus-visible { .postlink:focus-visible,
.taglink:focus-visible {
outline-offset: .25rem; outline-offset: .25rem;
outline: .25rem solid; outline: .25rem solid;
} }
@media (any-hover: hover) { @media (any-hover: hover) {
.postlink:hover { .postlink:hover,
.taglink:hover {
outline-offset: .25rem; outline-offset: .25rem;
outline: .25rem solid; outline: .25rem solid;
} }
@ -83,23 +91,24 @@
} }
/* Click animation */ /* Click animation */
.postlink:active { .postlink:active,
.taglink:active {
box-shadow: none; box-shadow: none;
top: .2rem; top: .2rem;
left: .2rem; left: .2rem;
box-shadow: .15rem .15rem var(--color-shadow); box-shadow: .15rem .15rem var(--color-shadow);
} }
/* Post elements */ /* Post & tag elements */
.post h2, .post h2, .post img,
.post img, .post ul, .post li,
.post ul, .tag h2, .tag p,
.post li { .tag img {
margin: 0; margin: 0;
} }
/* Headers */ .post h2,
.post h2 { .tag h2 {
grid-area: h2; grid-area: h2;
padding: .25rem .5rem; padding: .25rem .5rem;
text-transform: uppercase; text-transform: uppercase;
@ -109,30 +118,62 @@
border-bottom: .25rem solid var(--color-accent); border-bottom: .25rem solid var(--color-accent);
} }
.post:nth-child(even) h2 { .post:nth-child(even) h2,
.tag:nth-child(even) h2 {
text-align: right; text-align: right;
} }
.postlink:focus-visible h2 { .postlink:focus-visible h2,
.taglink:focus-visible h2 {
color: var(--color-bg); color: var(--color-bg);
border-color: var(--color-bg); border-color: var(--color-bg);
} }
@media (any-hover: hover) { @media (any-hover: hover) {
.postlink:hover h2 { .postlink:hover h2,
.taglink:hover h2 {
color: var(--color-bg); color: var(--color-bg);
border-color: var(--color-bg); border-color: var(--color-bg);
} }
} }
/* Image */ /* Images */
.post img { .post img,
.tag-imgs {
grid-area: img; grid-area: img;
} }
/* Tags */ .tag-imgs {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: .15rem;
}
.tag-imgs img {
aspect-ratio: 3 / 2;
object-fit: cover;
}
.missing-image {
width: 100%;
aspect-ratio: 3 / 2;
background-color: var(--color-bg-alt);
border-radius: calc(1rem);
}
.taglink:focus-visible .missing-image {
opacity: .7;
}
@media (any-hover: hover) {
.taglink:hover .missing-image {
opacity: .7;
}
}
/* Post tags */
.postlist-tags { .postlist-tags {
grid-area: tags; grid-area: info;
list-style: none; list-style: none;
display: flex; display: flex;
flex-flow: row wrap; flex-flow: row wrap;
@ -144,25 +185,35 @@
justify-content: flex-end; justify-content: flex-end;
} }
.post:nth-child(odd) .postlist-tags { .postlist-tags li,
justify-content: flex-start; .tagcount {
}
.postlist-tags li {
background-color: var(--color-primary); background-color: var(--color-primary);
color: var(--color-bg); color: var(--color-bg);
padding: 0 .5rem; padding: 0 .5rem;
border-radius: 1rem; border-radius: 1rem;
} }
.postlink:focus-visible .postlist-tags li { .postlink:focus-visible .postlist-tags li,
.taglink:focus-visible .tagcount {
background-color: var(--color-bg); background-color: var(--color-bg);
color: var(--color-primary); color: var(--color-primary);
} }
@media (any-hover: hover) { @media (any-hover: hover) {
.postlink:hover .postlist-tags li { .postlink:hover .postlist-tags li,
.taglink:hover .tagcount {
background-color: var(--color-bg); background-color: var(--color-bg);
color: var(--color-primary); color: var(--color-primary);
} }
} }
/* Tag count */
.tag p {
grid-area: info;
padding: .5rem;
}
.tag:nth-child(odd) p {
text-align: right;
}

View File

@ -96,15 +96,19 @@ h1, h2, h3, h4, h5, h6 {
h1 { h1 {
margin-top: 3rem; margin-top: 3rem;
font-size: 3.5rem; font-size: 3.5rem;
text-align: center;
} }
h2 { h2 {
margin-top: 1rem; margin-top: 2rem;
font-size: 2.2rem; font-size: 2.2rem;
} }
h3 { h3 {
margin-top: 1.5rem; margin-top: 1.5rem;
font-size: 1.6rem; font-size: 1.6rem;
} }
h4, h5, h6 { h4, h5, h6 {
margin-top: 1rem; margin-top: 1rem;
font-size: 1.2rem; font-size: 1.2rem;
@ -131,7 +135,7 @@ b {
/* Links */ /* Links */
a { a {
color: var(--color-font); color: var(--color-text);
border-radius: .25rem; border-radius: .25rem;
text-decoration: underline; text-decoration: underline;
text-decoration-style: solid; text-decoration-style: solid;
@ -155,23 +159,45 @@ a:active {
text-decoration-thickness: .4em; text-decoration-thickness: .4em;
} }
/* Heading anchors */
a.ha,
span.ha-placeholder {
color: var(--color-pink);
}
span.ha-placeholder {
opacity: .55;
}
/* Lists */ /* Lists */
::marker { ::marker {
color: var(--color-pink); color: var(--color-pink);
} }
ul, ol, li { ul, ol, dl, li {
margin-left: 1rem; margin-left: 1rem;
} }
li { li {
line-height: 1.5; line-height: 1.2 5;
margin-top: .65rem;
margin-bottom: .65rem;
} }
li ul, li ol { li ul, li ol {
margin: .5rem 0; margin: .5rem 0;
} }
dt {
font-weight: 900;
margin-top: .5rem;
}
dd {
margin-left: 2rem;
margin-bottom: .75rem;
}
/* Blockquotes */ /* Blockquotes */
blockquote { blockquote {
margin: .5rem 1rem; margin: .5rem 1rem;
@ -232,3 +258,12 @@ hr:last-child {
margin-bottom: 0; margin-bottom: 0;
} }
/* Used on home, reference, gallery pages */
.centered {
text-align: center;
}
/* Currently only used for resume, but it's generalizable */
.upper {
text-transform: uppercase;
}

View File

@ -10,7 +10,8 @@ header {
/* Header links, pagination links */ /* Header links, pagination links */
header a, header a,
.pagination a { .pagination a,
.webring ul a {
border-radius: 1rem; border-radius: 1rem;
border: .125rem solid var(--color-pink); border: .125rem solid var(--color-pink);
color: var(--color-pink); color: var(--color-pink);
@ -26,16 +27,21 @@ header a,
} }
header a, header a,
.pagination .older a { .pagination .older a,
.webring .prev a,
.webring .rand a {
padding-right: .35rem; padding-right: .35rem;
} }
.pagination .newer a { .pagination .newer a,
.webring .next a,
.webring .rand a {
padding-left: .35rem; padding-left: .35rem;
} }
header a:focus-visible, header a:focus-visible,
.pagination a:focus-visible { .pagination a:focus-visible,
.webring ul a:focus-visible {
color: var(--color-bg); color: var(--color-bg);
border-color: var(--color-pink); border-color: var(--color-pink);
background-color: var(--color-pink); background-color: var(--color-pink);
@ -44,7 +50,8 @@ header a:focus-visible,
@media (any-hover: hover) { @media (any-hover: hover) {
header a:hover, header a:hover,
.pagination a:hover { .pagination a:hover,
.webring ul a:hover {
color: var(--color-bg); color: var(--color-bg);
border-color: var(--color-pink); border-color: var(--color-pink);
background-color: var(--color-pink); background-color: var(--color-pink);
@ -53,14 +60,16 @@ header a:focus-visible,
@media (forced-colors: active) { @media (forced-colors: active) {
header a:focus-visible, header a:focus-visible,
.pagination a:focus-visible { .pagination a:focus-visible,
.webring ul a:focus-visible {
outline-offset: .125rem; outline-offset: .125rem;
outline: .125rem solid; outline: .125rem solid;
} }
@media (any-hover: hover) { @media (any-hover: hover) {
header a:hover, header a:hover,
.pagination a:hover { .pagination a:hover,
.webring ul a:hover {
outline-offset: .125rem; outline-offset: .125rem;
outline: .125rem solid; outline: .125rem solid;
} }
@ -69,7 +78,8 @@ header a:focus-visible,
/* Click animation */ /* Click animation */
header a:active, header a:active,
.pagination a:active { .pagination a:active,
.webring ul a:active {
top: .1rem; top: .1rem;
left: .1rem; left: .1rem;
box-shadow: .05rem .05rem var(--color-shadow); box-shadow: .05rem .05rem var(--color-shadow);
@ -97,28 +107,40 @@ header a[aria-current="page"]:focus-visible {
/* Header link icons, pagination icons */ /* Header link icons, pagination icons */
header i, header i,
.pagination i { .pagination i,
.webring ul i {
color: var(--color-teal); color: var(--color-teal);
} }
header i { header i,
.pagination .older i,
.webring .prev i,
.webring .rand i:nth-child(1) {
padding-left: .25rem; padding-left: .25rem;
} }
.pagination .newer i,
.webring .next i,
.webring .rand i:nth-child(2) {
padding-right: .25rem;
}
header a[aria-current="page"] i { header a[aria-current="page"] i {
color: var(--color-pink); color: var(--color-pink);
} }
header a:focus-visible i, header a:focus-visible i,
a[aria-current="page"] a:focus-visible i, a[aria-current="page"] a:focus-visible i,
.pagination a:focus-visible :in-range { .pagination a:focus-visible i,
.webring ul a:focus-visible i {
color: var(--color-bg); color: var(--color-bg);
} }
@media (any-hover: hover) { @media (any-hover: hover) {
header a:hover i, header a:hover i,
header a[aria-current="page"]:hover i, header a[aria-current="page"]:hover i,
.pagination a:hover i { .pagination a:hover i,
.webring ul a:hover i {
color: var(--color-bg); color: var(--color-bg);
} }
} }
@ -236,3 +258,21 @@ footer a:focus-visible {
grid-area: newer; grid-area: newer;
text-align: right; text-align: right;
} }
/* webring navigation */
.webring {
margin-bottom: 3rem;
}
.webring ul {
display: flex;
flex-flow: row wrap;
list-style: none;
justify-content: space-around;
gap: .35rem;
}
.webring ul,
.webring li {
margin-left: 0;
}

32
css/palette.css Normal file
View File

@ -0,0 +1,32 @@
.color {
border: .125rem solid var(--color-text);
border-radius: 2rem;
padding: .5rem;
text-align: center;
width: 65%;
margin: 1rem auto;
font-weight: bold;
}
#dark,
#dark-alt,
#teal-dark,
#pink-dark {
color: var(--color-light);
}
#light,
#light-alt,
#teal-light,
#pink-light {
color: var(--color-dark);
}
#dark { background-color: var(--color-dark); }
#dark-alt { background-color: var(--color-dark-alt); }
#light { background-color: var(--color-light); }
#light-alt { background-color: var(--color-light-alt); }
#teal-dark { background-color: var(--color-teal-dark); }
#teal-light { background-color: var(--color-teal-light); }
#pink-dark { background-color: var(--color-pink-dark); }
#pink-light { background-color: var(--color-pink-light); }

41
css/resume.css Normal file
View File

@ -0,0 +1,41 @@
.resume h1 {
margin-top: 2rem;
font-size: 2.8rem;
}
.resume h2 {
margin: 1.7rem 0 1rem;
font-size: 1.9rem;
}
.resume h3 {
margin: 0;
font-size: 1.2rem;
}
.two-col {
display: flex;
flex-flow: row wrap;
justify-content: space-between;
}
.col2 {
text-align: right;
}
.spacer {
margin: 1rem 0;
border-top: solid var(--color-teal);
}
.job-details p {
margin: 0;
}
.job-details a {
font-size: 1rem;
}
.job p {
margin-top: 0;
}

View File

@ -1,3 +1,4 @@
import { IdAttributePlugin } from "@11ty/eleventy";
import { eleventyImageTransformPlugin } from "@11ty/eleventy-img"; import { eleventyImageTransformPlugin } from "@11ty/eleventy-img";
import eleventyNavigationPlugin from "@11ty/eleventy-navigation"; import eleventyNavigationPlugin from "@11ty/eleventy-navigation";
import { feedPlugin } from "@11ty/eleventy-plugin-rss"; import { feedPlugin } from "@11ty/eleventy-plugin-rss";
@ -11,6 +12,19 @@ export default async function(eleventyConfig) {
/* Markdown HTML attribute parsing */ /* Markdown HTML attribute parsing */
eleventyConfig.amendLibrary("md", (mdLib) => mdLib.use(attrs)); eleventyConfig.amendLibrary("md", (mdLib) => mdLib.use(attrs));
/* Bundles */
/* CSS */
eleventyConfig.addBundle("css", {
toFileDirectory: "dist",
bundleHtmlContentFromSelector: "style",
});
/* Javascript */
eleventyConfig.addBundle("js", {
toFileDirectory: "dist",
bundleHtmlContentFromSelector: "script",
});
/* Collections */ /* Collections */
/* Tag pagination */ /* Tag pagination */
eleventyConfig.addCollection("tagPagination", function(collection) { eleventyConfig.addCollection("tagPagination", function(collection) {
@ -38,9 +52,6 @@ export default async function(eleventyConfig) {
return tagMap; return tagMap;
}); });
/* Passthroughs */
eleventyConfig.addPassthroughCopy({"css": "assets/css"});
/* Plugins */ /* Plugins */
/* All filters from _config/filters.js */ /* All filters from _config/filters.js */
eleventyConfig.addPlugin(pluginFilters); eleventyConfig.addPlugin(pluginFilters);
@ -85,9 +96,17 @@ export default async function(eleventyConfig) {
/* Navigation */ /* Navigation */
eleventyConfig.addPlugin(eleventyNavigationPlugin); eleventyConfig.addPlugin(eleventyNavigationPlugin);
/* `id` attributes */
eleventyConfig.addPlugin(IdAttributePlugin);
/* Syntax highlighting */ /* Syntax highlighting */
eleventyConfig.addPlugin(syntaxHighlight); eleventyConfig.addPlugin(syntaxHighlight);
/* Shortcodes */
eleventyConfig.addShortcode("currentBuildDate", () => {
return (new Date()).toISOString();
});
/* Watch when serving */ /* Watch when serving */
eleventyConfig.addWatchTarget("css"); eleventyConfig.addWatchTarget("css");
}; };

7
package-lock.json generated
View File

@ -12,10 +12,11 @@
"@11ty/eleventy": "^3.1.2", "@11ty/eleventy": "^3.1.2",
"@11ty/eleventy-img": "^6.0.4", "@11ty/eleventy-img": "^6.0.4",
"@11ty/eleventy-navigation": "^1.0.5", "@11ty/eleventy-navigation": "^1.0.5",
"@11ty/eleventy-plugin-bundle": "^3.0.7",
"@11ty/eleventy-plugin-rss": "^2.0.4", "@11ty/eleventy-plugin-rss": "^2.0.4",
"@11ty/eleventy-plugin-syntaxhighlight": "^5.0.2", "@11ty/eleventy-plugin-syntaxhighlight": "^5.0.2",
"@mdit/plugin-attrs": "^0.24.2", "@mdit/plugin-attrs": "^0.24.2",
"@zachleat/heading-anchors": "^1.0.5", "@zachleat/heading-anchors": "https://github.com/lee0c/heading-anchors/tarball/main",
"lodash-es": "^4.17.23", "lodash-es": "^4.17.23",
"luxon": "^3.7.2" "luxon": "^3.7.2"
} }
@ -799,8 +800,8 @@
}, },
"node_modules/@zachleat/heading-anchors": { "node_modules/@zachleat/heading-anchors": {
"version": "1.0.5", "version": "1.0.5",
"resolved": "https://registry.npmjs.org/@zachleat/heading-anchors/-/heading-anchors-1.0.5.tgz", "resolved": "https://github.com/lee0c/heading-anchors/tarball/main",
"integrity": "sha512-hsAljmm6py9VEf6ToKGyQJweemQJM4bI75TTTbwRYIrasCm66ajJDhWYpgZJM1B+8KSop371RDhDASYl3Q4y9g==", "integrity": "sha512-DQz11XHUNEXzwq8GBedqBVBYGjtdSjWDgcXQrg4bcbS9N3qwJZFhi6Sj4UnzQD7eQOEejCvxTzldS9iRi+rQzg==",
"dev": true, "dev": true,
"license": "MIT" "license": "MIT"
}, },

View File

@ -4,6 +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": {
"build": "rm -rf _site && npx @11ty/eleventy",
"start": "rm -rf _site && npx @11ty/eleventy --serve --quiet" "start": "rm -rf _site && npx @11ty/eleventy --serve --quiet"
}, },
"keywords": [], "keywords": [],
@ -14,10 +15,11 @@
"@11ty/eleventy": "^3.1.2", "@11ty/eleventy": "^3.1.2",
"@11ty/eleventy-img": "^6.0.4", "@11ty/eleventy-img": "^6.0.4",
"@11ty/eleventy-navigation": "^1.0.5", "@11ty/eleventy-navigation": "^1.0.5",
"@11ty/eleventy-plugin-bundle": "^3.0.7",
"@11ty/eleventy-plugin-rss": "^2.0.4", "@11ty/eleventy-plugin-rss": "^2.0.4",
"@11ty/eleventy-plugin-syntaxhighlight": "^5.0.2", "@11ty/eleventy-plugin-syntaxhighlight": "^5.0.2",
"@mdit/plugin-attrs": "^0.24.2", "@mdit/plugin-attrs": "^0.24.2",
"@zachleat/heading-anchors": "^1.0.5", "@zachleat/heading-anchors": "https://github.com/lee0c/heading-anchors/tarball/main",
"lodash-es": "^4.17.23", "lodash-es": "^4.17.23",
"luxon": "^3.7.2" "luxon": "^3.7.2"
} }

Binary file not shown.

Before

Width:  |  Height:  |  Size: 578 KiB

BIN
src/img/about/duckies.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 MiB

BIN
src/img/about/kestrel.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

BIN
src/img/about/koi-pond.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 283 KiB

View File

@ -1,4 +1,5 @@
--- ---
layout: page-lists.njk
pagination: pagination:
data: collections.posts data: collections.posts
size: 13 size: 13
@ -9,8 +10,10 @@ eleventyNavigation:
order: 3 order: 3
icon: fa fa-solid fa-crow icon: fa fa-solid fa-crow
title: home title: home
layout: page.njk permalink: index.html
--- ---
<p class="centered">(or <a href="/tags/">browse by tags</a>)</p>
{% include "postlist.njk" %} {% include "postlist.njk" %}
{# idk why these are backwards either #} {# idk why these are backwards either #}

View File

@ -1,4 +1,6 @@
--- ---
layout: page-lists.njk
permalink: gallery/index.html
pagination: pagination:
data: collections["gallery"] data: collections["gallery"]
size: 13 size: 13
@ -11,7 +13,9 @@ title: gallery
icon: fa-regular fa-images icon: fa-regular fa-images
label: view the gallery label: view the gallery
--- ---
<p>the gallery page is for finished art</p>
<p class="centered">the gallery page is for finished art</p>
<p class="centered">(or <a href="/tags/">browse by tags</a>)</p>
{% include "postlist.njk" %} {% include "postlist.njk" %}

View File

@ -1,4 +1,6 @@
--- ---
layout: page-lists.njk
permalink: reference/index.html
pagination: pagination:
data: collections["reference"] data: collections["reference"]
size: 13 size: 13
@ -6,12 +8,14 @@ pagination:
alias: postlist alias: postlist
eleventyNavigation: eleventyNavigation:
key: reference key: reference
order: 2 order: 1
title: reference title: reference
icon: fa-regular fa-folder-open icon: fa-regular fa-folder-open
label: read reference posts label: read reference posts
--- ---
<p>the reference page is for informational posts</p>
<p class="centered">the reference page is for informational posts</p>
<p class="centered">(or <a href="/tags/">browse by tags</a>)</p>
{% include "postlist.njk" %} {% include "postlist.njk" %}

View File

@ -1,11 +1,11 @@
--- ---
layout: page.njk layout: page-lists.njk
pagination: pagination:
data: collections.tagPagination data: collections.tagPagination
size: 1 size: 1
alias: tag alias: tag
eleventyComputed: eleventyComputed:
permalink: /tag/{{ tag.tagName | slugify }}/{% if tag.pageNumber %}{{ tag.pageNumber + 1 }}/{% endif %} permalink: /tags/{{ tag.tagName | slugify }}/{% if tag.pageNumber %}{{ tag.pageNumber + 1 }}/{% endif %}
title: "tag: {{ tag.tagName }}" title: "tag: {{ tag.tagName }}"
--- ---

27
src/meta/tags.njk Normal file
View File

@ -0,0 +1,27 @@
---
layout: page-lists.njk
permalink: tags/index.html
title: all tags
---
<ul id="taglist">
{% for tag in collections | getKeys | removeBasicTags | sortAlphabetically %}
{% set tagUrl %}/tags/{{ tag | slugify }}/{% endset %}
<li class="tag">
<a href="{{ tagUrl }}" class="taglink">
<div class="tag-imgs">
{% for i in range(0, 4) %}
{% if collections[tag][i].data.image %}
<img src="/img/{{ collections[tag][i].data.image.src }}" alt="{{ collections[tag][i].data.image.alt }}">
{% else %}
<div class="missing-image"></div>
{% endif %}
{% endfor %}
</div>
<h2>{{ tag }}</h2>
{% set numPosts = collections[tag].length %}
<p><span class="tagcount">({{ numPosts }} post{% if numPosts > 1 %}s{% endif %})</span></p>
</a>
</li>
{% endfor %}
</ul>

View File

@ -1,8 +0,0 @@
---
eleventyNavigation:
key: about
order: 4
title: about
icon: fa-regular fa-user
label: about Lee
---

52
src/pages/about.njk Normal file
View File

@ -0,0 +1,52 @@
---
eleventyNavigation:
key: about
order: 4
title: about
icon: fa-regular fa-user
label: about Lee
---
<p>My name is Lee Cattarin. I use he or ze pronouns.</p>
<p>I'm a programmer (<a href="/resume/">are you looking for a resume?</a>), artist&crafter - knitting, spinning yarn, stamp carving/printmaking, cardmaking & papercrafts, bookbinding, leatherworking - bread baker, guitar player, and probably a lot of other things.</p>
<img src="/img/about/koi-pond.jpg" alt="A koi pond in fall afternoon light. A slender white person in a knitted dark teal sweater crouches in front of the pond and extends hir hand towards the surface of the water. Several curious koi are arriving to see what the matter is, and one white koi has stuck its face a bit out of the water to reach toward hir hand.">
<p>As of November 2023, my wife Brooke Osment and I have a art store: Riverside Refuge Studio. You can find various store links, or other ways to connect, on my <a href="/contact">contact page</a>. We're based out of Vashon, WA, USA, and ship internationally.</p>
<h2>pets</h2>
<p>We have a dog...</p>
<img src="/img/about/kestrel.png" alt="4 collaged pictures of Kestrel the Malinois mutt, a big tan dog with a thick ruff and half floppy, half pointy ears. In the pictures, he rolls on his back in the grass, looks snobbily at the camera with one ear flopping, looks off to one side, and sleeps in a big leather chair.">
<p>...and six ducks.</p>
<img src="/img/about/duckies.png" alt="2 pictures that show all six ducks, each duck labeled with their name. In the first image, Sparrow (Golden 300, a light brown breed that looks similar to female mallards) and Puffin (Swedish Black, black with a white bib and beautiful iridescence) come up to eat from my hand. In the second image, Chickadee (Magpie, black and white splotched) is up front looking to one side, and behind her are Dodo (Silver Runner, white and grey), Emu (Fawn & White Runner, white and pale tan), and Kiwi (Chocolate Runner, dark brown all over), with Sparrow and Puffin partially visible behind.">
<h2>webrings</h2>
{% set id = "css-joy-webring" %}
{% set name = "CSS Joy webring" %}
{% set url = "https://cs.sjoy.lol/" %}
{% set prev = "https://webri.ng/webring/cssjoy/previous?via=https://leecat.art" %}
{% set next = "https://webri.ng/webring/cssjoy/next?via=https://leecat.art" %}
{% set rand = "https://webri.ng/webring/cssjoy/random?via=https://leecat.art" %}
{% include "webring.njk" %}
{% set id = "a11y-webring-club" %}
{% set name = "a11y-webring.club" %}
{% set url = "https://a11y-webring.club/" %}
{% set prev = "https://a11y-webring.club/prev" %}
{% set next = "https://a11y-webring.club/next" %}
{% set rand = "https://a11y-webring.club/random" %}
{% include "webring.njk" %}
{% set id = "no-ai-webring" %}
{% set name = "No AI webring" %}
{% set url = "https://baccyflap.com/noai/" %}
{% set prev = "https://baccyflap.com/noai/?prv&s=lee" %}
{% set next = "https://baccyflap.com/noai/?nxt&s=lee" %}
{% set rand = "https://baccyflap.com/noai/?rnd" %}
{% include "webring.njk" %}

View File

@ -2,8 +2,9 @@
title: colophon title: colophon
--- ---
This is v2 of my personal website, build with [{{ eleventy.generator }}](https://www.11ty.dev/){target="_blank" rel="external"}. It's been hand-coded from the ground up.
This site is based on, though long diverged from, [Millennial](https://lenpaul.github.io/Millennial/){target="_blank" rel="external"}, a minimalist Jekyll theme for running a blog or publication by Paul Le. It is built using [Jekyll](https://jekyllrb.com/){target="_blank" rel="external"}, a static site generator. It runs on GitHub Pages. v1 of this site began in 2022 and was based on [Millennial](https://lenpaul.github.io/Millennial/){target="_blank" rel="external"}, a minimalist Jekyll theme for running a blog or publication by Paul Le.
The fonts are [Atkinson Hyperlegible Next and Atkinson Hyperlegible Mono](https://brailleinstitute.org/freefont){target="_blank" rel="external"} for standard text and monospace respectively, specifically designed for low-vision readers to improve character recognition. Also they look neat :) The fonts are [Atkinson Hyperlegible Next and Atkinson Hyperlegible Mono](https://brailleinstitute.org/freefont){target="_blank" rel="external"} for standard text and monospace respectively, specifically designed for low-vision readers to improve character recognition. Also they look neat :)
@ -12,10 +13,6 @@ Thank you to some lovely friends for their feedback and help with the site! You
- [Lenny](http://www.wondra.codes/){target="_blank" rel="external"}, especially for their HTML/CSS and accessibility expertise - [Lenny](http://www.wondra.codes/){target="_blank" rel="external"}, especially for their HTML/CSS and accessibility expertise
- [Shir](https://shirgoldberg.com/){target="_blank" rel="external"} - [Shir](https://shirgoldberg.com/){target="_blank" rel="external"}
You can [find the accessibility statement here](/accessibility). You can also [explore the sitemap](/sitemap.xml). If you'd like, you can view the [site's palette](/palette) or the [style overview](/style).
This site is created [without the use of generative AI](https://declare-ai.org/1.0.0/none.html){target="_blank" rel="external"}. This site is created [without the use of generative AI](https://declare-ai.org/1.0.0/none.html){target="_blank" rel="external"}.
You can [find the accessibility statement for leecat.art here](/accessibility).
If you'd like, you can view the [site palette](/palette) or the [style overview](/style).
[The sitemap can be found here](/sitemap.xml).

View File

@ -6,3 +6,114 @@ title: contact
icon: fa-solid fa-envelope-open-text icon: fa-solid fa-envelope-open-text
label: contact Lee label: contact Lee
--- ---
## contact me
<dl>
<dt><i aria-hidden="true" class="fa-brands fa-signal-messenger"></i> signal</dt>
<dd><a href="https://signal.me/#eu/PcJ86iNQeAbC-SFbxqI00gFQb6zL4Y6QDCsFDU2mokM4K40djsdNYa3hJAee79ll">inherentlee.13</a></dd>
<dt><i aria-hidden="true" class="fa-solid fa-envelope"></i> email</dt>
<dd><a href="mailto:lee.cattarin@gmail.com" target="_blank">lee dot cattarin at gmail dot com</a></dd>
<dt><i aria-hidden="true" class="fa-solid fa-mobile-retro"></i> text (no phone calls!)</dt>
<dd>seven seven four, two four nine, zero five eight six</dd>
<dt><i aria-hidden="true" class="fa-brands fa-discord"></i> discord</dt>
<dd>inherentlee</dd>
<dt><i aria-hidden="true" class="fa-solid fa-envelopes-bulk"></i> snail mail</dt>
<dd>message me for address!</dd>
<dt><i aria-hidden="true" class="fa-solid fa-signature"></i> guestbook?</dt>
<dd><a href="/guestbook">guestbook.</a></dd>
</dl>
## socials
<dl>
<dt><i aria-hidden="true" class="fa-brands fa-mastodon"></i> the fediverse/mastodon</dt>
<dd><a rel="me" href="https://flipping.rocks/@inherentlee" target="_blank">@inherentlee@flipping.rocks</a> and <a rel="me" href="https://weirder.earth/@inherentlee" target="_blank">@inherentlee@weirder.earth</a> (weirder.earth largely unused)</dd>
<dt><i aria-hidden="true" class="fa-solid fa-rss"></i> rss</dt>
<dd><a href="/feed.xml">feed</a></dd>
<dt><i aria-hidden="true" class="fa-solid fa-icicles"></i> codeberg</dt>
<dd><a href="https://codeberg.org/inherentlee" target="_blank">inherentlee</a></dd>
<dt><i aria-hidden="true" class="fa-brands fa-github"></i> github</dt>
<dd><a href="https://github.com/lee0c" target="_blank">lee0c</a></dd>
<dt><i aria-hidden="true" class="fa-brands fa-ravelry"></i> ravelry</dt>
<dd><a href="https://ravelry.com/people/inherentlee" target="_blank">inherentlee</a></dd>
<dt><i aria-hidden="true" class="fa-brands fa-twitch"></i> twitch</dt>
<dd><a href="https://twitch.tv/inherentlee" target="_blank">inherentlee</a></dd>
</dl>
---
## shops
if pricing is an issue for you, reach out and we can work out sliding scale options - or an art trade!
<dl>
<dt><i aria-hidden="true" class="fa-regular fa-square"></i> square</dt>
<dd><a href="https://riverside-refuge.square.site/" target="_blank">Riverside Refuge Studio</a></dd>
<dt><i aria-hidden="true" class="fa-solid fa-store"></i> faire (wholesale)</dt>
<dd><a href="https://faire.com/direct/riversiderefugestudio" target="_blank">Riverside Refuge Studio</a></dd>
<dt><i aria-hidden="true" class="fa-solid fa-mug-saucer"></i> kofi (now the home of <a href="https://fedizinefest.fyi">FediZineFest 2025</a> and <a href="https://rescue-trans-rescue.quest" target="_blank">Rescue Trans Rescue</a>)</dt>
<dd><a href="https://ko-fi.com/inherentlee" target="_blank">inherentlee</a></dd>
</dl>
### payment methods
<dl>
<dt><i aria-hidden="true" class="fa-solid fa-comment-dollar"></i> venmo</dt>
<dd><a href="https://www.venmo.com/u/lee-cattarin" target="_blank">lee-cattarin</a></dd>
<dt><i aria-hidden="true" class="fa-brands fa-paypal"></i> paypal</dt>
<dd><a href="https://paypal.me/leecattarin?country.x=US&locale.x=en_US" target="_blank">leecattarin</a></dd>
</dl>
---
## in physical stores!
### Colorado
#### Denver
- [(dis)obedience](https://www.disobediencedenver.com){:target="_blank"}: stickers, pins, cards
### Oregon
#### Beaverton
- [Forager Vintage](https://foragervintage.com/){:target="_blank"}: stickers, pins, cards
### Washington
#### Port Orchard
- [Aphrodisia Boutique](https://www.aphrodisia.boutique/){:target="_blank"}: stickers, pins, cards, prints, leather goods (impact toys, cuffs, potentially harnesses)
#### Seattle
- [Push/Pull](https://www.pushpullseattle.com/){:target="_blank"}: stickers, pins, cards
- [Standard Goods Ballard](https://thestandardgoods.com/pages/ballard){:target="_blank"}: stickers
- [Standard Goods Capitol Hill](https://thestandardgoods.com/pages/capitol-hill){:target="_blank"}: stickers
#### Tacoma
- Anna's Anomalies (Sanford and Sons Antiques, 743 Broadway, Tacoma, WA, 98402): stickers, pins, cards, prints
#### Vashon
- [Vashon Made (formerly Starving Artist Works) (facebook)](https://www.facebook.com/SawStarvingArtistWorks/){:target="_blank"}: stickers, cards, prints, wallets, handbound books, yarn
- [Standard Goods Vashon](https://thestandardgoods.com/pages/vashon){:target="_blank"}: stickers
- [Vashon Pharmacy](https://vashonpharmacy.com/){:target="_blank"}: cards
---
## not online or online unreliably
<dl>
<dt><i aria-hidden="true" class="fa-brands fa-linkedin"></i> linkedin</dt>
<dd><a href="https://www.linkedin.com/in/lee-cattarin/" target="_blank">Lee Cattarin</a></dd>
<dt><i aria-hidden="true" class="fa-brands fa-instagram"></i> instagram</dt>
<dd><a href="https://instagram.com/inherentlee" target="_blank">inherentlee</a></dd>
<dt><i aria-hidden="true" class="fa-brands fa-facebook"></i> facebook</dt>
<dd><a href="https://www.facebook.com/lee.cattarin.50/" target="_blank">Lee Cattarin</a></dd>
<dt><i aria-hidden="true" class="fa-brands fa-twitter"></i> twitter</dt>
<dd><a href="https://twitter.com/inherentlee" target="_blank">inherentlee</a></dd>
</dl>

67
src/pages/guestbook.md Normal file
View File

@ -0,0 +1,67 @@
---
title: guestbook
---
guestbook entries are manually added and moderated :)
yes, you may sign again even if you've been here before - just no spamming please!
## write
if the embed below is not working for you, you can [open the guestbook form in a new tab](https://airtable.com/app1YuM4uE4sRTYgh/pag4JzOylrLoLy4AS/form){:target="_blank"} or just [contact me any way you want](/contact) with your desired message
<iframe class="airtable-embed" src="https://airtable.com/embed/app1YuM4uE4sRTYgh/pag4JzOylrLoLy4AS/form" frameborder="0" onmousewheel="" width="100%" height="533" style="background: transparent;"></iframe>
## read
J'myle Koretz on <time datetime="2025-05-21">21 May 2025</time>:
> It was delightful to meet you at the Vashon art walk a couple weekends back. Kestrel was awesome, and your [Brooke's] jackets were so cool!
Jack on <time datetime="2024-12-01">1 December 2024</time>:
> All the things you make and do are so cool and I feel so lucky every time I get to see them (including the ones that now live in my home!)
hello on <time datetime="2024-12-01">1 December 2024</time>:
> thanks for being gay and weird pal
Fén (Spirits) on <time datetime="2024-09-06">6 September 2024</time>:
> Hi you make really good art and crafts and have been awesome to get to know ^^ Keep on keeping on~
Olive on <time datetime="2024-09-06">6 September 2024</time>:
> Hi! You're cool :))
Fern (Rainbow) on <time datetime="2024-09-06">6 September 2024</time>:
> I think you're awesome and this is really impressive 🥺
handmade ghost on <time datetime="2024-09-06">6 September 2024</time>:
> Everything Lee makes is a gift to the world--just knowing ze made something new brings me joy and sends me scrambling to this site!
[jay](jaygrant.us){:target="_blank"} on <time datetime="2024-09-06">6 September 2024</time>:
> EGG
pqqq on <time datetime="2024-09-06">6 September 2024</time>:
> Hello from Fedi! Your site is absolutely wonderful, and I love that you're adding a guestbook, too!!
Morgan on <time datetime="2024-09-06">6 September 2024</time>:
> You make good art and good takes on gender 💜
[Lisa](@mycrowgirl@mastodon.social){:target="_blank"} on <time datetime="2024-09-06">6 September 2024</time>:
> Ask not for whom the snoot is booped. It is booped for weevils.
nathanlovestrees on <time datetime="2024-09-06">6 September 2024</time>:
> honk!
[✨pencilears✨](https://pencilears.eternalaugust.net/comic/){:target="_blank"} on <time datetime="2024-09-06">6 September 2024</time>:
> Hello! I'm glad we're still doing guest books on websites.
[brhfl](https://brhfl.com){:target="_blank"} on <time datetime="2024-09-06">6 September 2024</time>:
> hey hi! just giving you some test data and also basking in the nostalgia of a guestbook :)
Eedlipherus C. Bigribs on <time datetime="2024-09-06">6 September 2024</time>:
> Signing the guestbook so you have data to format on the page lyao.
>
> And this is a second paragraph, because, honestly, this is more interesting than the QA I get paid to do, so I'll just make this bit long enough to wrap for a few lines, yep, maybe a little bit more, uh huh, that's right, eat your heart out, Lorem Ipsum.
jade on <time datetime="2024-09-06">6 September 2024</time>:
> Hi Lee!
alex tax1a on <time datetime="2024-09-06">6 September 2024</time>:
> Hi!! signing the guestbook, so you have at least one datum.

13
src/pages/palette.njk Normal file
View File

@ -0,0 +1,13 @@
---
title: palette
---
{% css %}{% include "css/palette.css" %}{% endcss %}
<p class="color" id="dark">#2e303e</p>
<p class="color" id="dark-alt">#3c3f52</p>
<p class="color" id="light">#ebeeef</p>
<p class="color" id="light-alt">#dbe1e3</p>
<p class="color" id="teal-dark">#18737b</p>
<p class="color" id="teal-light">#25b0bc</p>
<p class="color" id="pink-dark">#94195d</p>
<p class="color" id="pink-light">#ee9fcb</p>

201
src/pages/resume.html Normal file
View File

@ -0,0 +1,201 @@
---
title: resume
layout: resume.njk
---
<div class="resume">
<h1 class="centered upper">Lee Cattarin</h1>
<p class="centered">
he/him or ze/hir • Vashon, WA 98070
<br/>
<a href="mailto:lee.cattarin@gmail.com?subject=Resume%20inquiry">lee.cattarin@gmail.com</a><a href="/">this very website</a><a href="https://linkedin.com/in/lee-cattarin" target="_blank">linkedin.com/in/lee-cattarin</a>
</p>
<h2 class="centered upper">Platforms Engineer</h2>
<p>
Design-oriented platforms/infrastructure engineer with a well-rounded background in algorithms, UI/UX, observabilty, databases, and accessibility. Consistent customer focus with strong communication skills and a passion for sharing knowledge.
</p>
<hr>
<h2 class="centered upper" id="toolkit">Toolkit</h2>
<ul>
<li>Git, bash, and the terminal</li>
<li>Containers and Kubernetes</li>
<li>Terraform, Bicep, and infrastructure deployment</li>
<li>GitHub Actions and other pipelines</li>
<li>HTML/CSS, Javascript, and UI/UX design</li>
<li>Languages like Go, C, and Python</li>
<li>Accessiblity knowledge and thoughtful design</li>
</ul>
<hr>
<h2 class="centered upper" id="experience">Experience</h2>
<div class="job">
<div class="two-col job-details">
<p><b>Microsoft,</b> Redmond, WA</p>
<p class="col2"><time datetime="2018-08">August 2018</time> <time datetime="2025-07">July 2025</time></p>
</div>
<h3>Software Development Engineer I & II</h3>
<p>
Worked on green- and brown-field projects with customers, solving emerging problems in infrastructure, devOps, and LLM/human interaction. Wrote reports on product feedback to relay to product teams and improved documentation.
</p>
<ul>
<li>Contributed UI/UX, algorithm, and database design to a new human-in-the-loop approach to LLM form automation, ensuring both that content was effectively reviewed and that auditing was smooth.</li>
<li>Deployed and configured Kubernetes for many scenarios, handling monitoring and observability, scaling, traffic routing, security and policy, Windows container support, and more.</li>
<li>Maintained team working agreements to ensure team unity and consistency. Shaped team processes and documentation to improve clarity and speed up onboarding.</li>
<li>Improved Azure and open source documentation with new, updated, and corrected information.</li>
<li>Coached multiple learning-oriented hackathons to ensure broader community understanding and adoption of Azure services.</li>
<li>Presented to groups of 10100 on backend accessibility and queer/trans education.</li>
</ul>
</div>
<div class="spacer"></div>
<div class="job">
<div class="two-col job-details">
<p><b>Riverside Refuge Studio,</b> Vashon, WA</p>
<p class="col2"><time datetime="2023-10">October 2023</time> Present</p>
</div>
<h3>Co-owner and artist</h3>
<p>
Maintains website, storefront, and inventory for a diverse set of artistic goods. Communicates with a range of customers, both digitally and in-person.
</p>
<ul>
<li>Designs and creates art/crafts in a variety of mediums ranging from visual to functional.</li>
<li>Presents work in art shows in and around Seattle.</li>
<li>Runs booths or studio space at art fairs and markets.</li>
</ul>
</div>
<div class="spacer"></div>
<div class="job">
<div class="two-col job-details">
<p><b>Rensselaer Polytechnic Institute,</b> Troy, NY</p>
<p class="col2"><time datetime="2015-09">September 2015</time> <time datetime="2017-12">December 2017</time></p>
</div>
<h3>Undergraduate Programming Mentor</h3>
<p>
Helped students debug and develop in Python and learn core programming concepts. Created daily quiz material and graded exams.
</p>
</div>
<div class="spacer"></div>
<div class="job">
<div class="two-col job-details">
<p><b>Microsoft,</b> Redmond, WA</p>
<p class="col2"><time datetime="2017-06">June 2017</time> <time datetime="2017-08">August 2017</time></p>
</div>
<h3>Software Development Engineering Intern</h3>
<p>
Developed a chatbot add-on for the Azure Android application.
</p>
</div>
<hr>
<h2 class="centered upper" id="projects">Projects</h2>
<div class="job">
<div class="two-col job-details">
<h3>11ty Lessons (<a href="https://inherentlee.codeberg.page/lessons" target="_blank">inherentlee.codeberg.page/lessons</a>)</h3>
<p class="col2"><time datetime="2026-02">February 2026</time> Present</p>
</div>
<p>Designs, develops, and maintains Eleventy-based website that details lessons learned while building with Eleventy.</p>
</div>
<div class="spacer"></div>
<div class="job">
<div class="two-col job-details">
<h3>Spoonfairies (<a href="https://inherentlee.codeberg.page/spoonfairies" target="_blank">inherentlee.codeberg.page/spoonfairies</a>)</h3>
<p class="col2"><time datetime="2026-02">February 2026</time> Present</p>
</div>
<p>Designs, develops, and maintains Eleventy-based website for a just-launching project to build community and help people find support.</p>
</div>
<div class="spacer"></div>
<div class="job">
<div class="two-col job-details">
<h3>Siblinghood of the Traveling Greeting Card (<a href="https://siblinghood.quest" target="_blank">siblinghood.quest</a>)</h3>
<p class="col2"><time datetime="2025-11">November 2025</time> Present</p>
</div>
<p>Designs, develops, and maintains website. Manages communication and logistics for a ~40 person global community building project.</p>
</div>
<div class="spacer"></div>
<div class="job">
<div class="two-col job-details">
<h3>Beall Greenhouses (<a href="https://beall-greenhouses-market.pages.dev" target="_blank">beall-greenhouses-market.pages.dev</a>)</h3>
<p class="col2"><time datetime="2025-10">October 2025</time> Present</p>
</div>
<p>Created logo. Designs, develops, and maintains website. Manages artist information for the Beall Greenhouses artist studios.</p>
</div>
<div class="spacer"></div>
<div class="job">
<div class="two-col job-details">
<h3>leecat.art (you are here!) (<a href="/">leecat.art</a>)</h3>
<p class="col2"><time datetime="2022-10">October 2022</time> Present</p>
</div>
<p>Designs, maintains, and creates all content.</p>
</div>
<div class="spacer"></div>
<div class="job">
<div class="two-col job-details">
<h3>FediZineFest (<a href="https://fedizinefest.fyi" target="_blank">fedizinefest.fyi</a>)</h3>
<p class="col2"><time datetime="2023-12">December 2023</time> <time datetime="2025-07">July 2025</time></p>
</div>
<p>Created and ran a global event for zine artists on the fediverse (Mastodon) for two years. Coordinated website, physical material shipping and logistics, payment, and marketing for a 4050 person project.</p>
</div>
<div class="spacer"></div>
<div class="job">
<div class="two-col job-details">
<h3>Rescue Trans Rescue (<a href="https://rescue-trans-rescue.quest" target="_blank">rescue-trans-rescue.quest</a>)</h3>
<p class="col2"><time datetime="2024">2024</time></p>
</div>
<p>Created a digital art exhibit and sale to raise money for charity. Coordinated ~30 artists to contribute physical and digital work as well as a collaborative sticker sheet. Raised ~2.3k for Trans Rescue.</p>
</div>
<hr>
<h2 class="centered upper">Education</h2>
<p class="centered">
<b>Bachelor of Science — Computer Science</b>
<br/>
Rensselaer Polytechnic Institute, Troy, NY
</p>
</div>

View File

@ -16,6 +16,14 @@ The number of pound signs determines the heading level.
It's also important not to skip heading levels. Don't jump from a 2 to a 4 or similar. It's also important not to skip heading levels. Don't jump from a 2 to a 4 or similar.
##### Heading level 5
You can use up to level 6!
###### Here's level 6
It's just unnecessary.
## Paragraphs ## Paragraphs
You'll notice that I am putting blank lines between headings and plain text. This is necessary, or they won't render correctly. You'll notice that I am putting blank lines between headings and plain text. This is necessary, or they won't render correctly.

View File

View File

@ -0,0 +1,140 @@
---
title: moving images two
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:
- gallery
- software
- some
- more
- tags
---
## 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)

View File

@ -1,12 +0,0 @@
---
title: Sample 0
date: 2026-02-17
tags:
- gallery
- test
image:
src: 2026/sample-0.jpg
alt: filler
---

View File

@ -1,12 +0,0 @@
---
title: Sample 1
date: 2026-02-18
tags:
- gallery
- test
image:
src: 2026/sample-0.jpg
alt: filler
---

View File

@ -1,12 +0,0 @@
---
title: Sample 2
date: 2026-02-19
tags:
- gallery
- test
image:
src: 2026/sample-0.jpg
alt: filler
---

View File

@ -1,12 +0,0 @@
---
title: Sample 3
date: 2026-02-20
tags:
- gallery
- test
image:
src: 2026/sample-0.jpg
alt: filler
---

View File

@ -1,12 +0,0 @@
---
title: Sample 4
date: 2026-02-21
tags:
- gallery
- test
image:
src: 2026/sample-0.jpg
alt: filler
---