diff --git a/eleventy.config.js b/eleventy.config.js index 55405ee..64e7d60 100644 --- a/eleventy.config.js +++ b/eleventy.config.js @@ -12,7 +12,7 @@ import clean from "eleventy-plugin-clean"; import toc from "eleventy-plugin-toc"; import site from "./site/_data/site.js"; import { feedPlugin } from "@11ty/eleventy-plugin-rss"; -import { execSync } from 'child_process'; +import { execSync } from "child_process"; import eleventyAutoCacheBuster from "eleventy-auto-cache-buster"; import mdPrism from "markdown-it-prism"; @@ -36,7 +36,7 @@ export default (config) => { .use(mdFootnote) .use(mdSpoiler, { tag: "span", - attrs: [["class", "aside"]] + attrs: [["class", "aside"]], }) .use(mdLinkAttributes, { matcher(href) { @@ -48,11 +48,17 @@ export default (config) => { }, }) .use(mdPrism); - mdLib.renderer.rules.render_footnote_anchor = (tokens, idx, options, env, slf) => { - let id = slf.rules.footnote_anchor_name(tokens, idx, options, env, slf) - if (tokens[idx].meta.subId > 0) id += `:${tokens[idx].meta.subId}` + mdLib.renderer.rules.render_footnote_anchor = ( + tokens, + idx, + options, + env, + slf, + ) => { + let id = slf.rules.footnote_anchor_name(tokens, idx, options, env, slf); + if (tokens[idx].meta.subId > 0) id += `:${tokens[idx].meta.subId}`; /* ↩ with escape code to prevent display as Apple Emoji on iOS */ - return ` \u21a9\uFE0E` + return ` \u21a9\uFE0E`; }; config.setLibrary("md", mdLib); config.addPassthroughCopy({ @@ -83,6 +89,24 @@ export default (config) => { return musicFiles; }); + config.addCollection("categories", (collectionApi) => { + const posts = collectionApi + .getFilteredByTag("posts") + .filter( + (p) => !p.data.draft || process.env.ELEVENTY_RUN_MODE !== "build", + ); + + return posts.reduce((tags, post) => { + post.data.tags + .filter((tag) => tag !== "posts") + .forEach((tag) => { + const prev = tags[tag] ?? { id: Object.keys(tags).length, count: 0 }; + tags[tag] = { ...prev, count: prev.count + 1 }; + }); + return tags; + }, {}); + }); + config.addTransform("prettier", (content, outputPath) => { if (typeof outputPath !== "string") { return content; @@ -142,8 +166,10 @@ export default (config) => { config.addPlugin(toc); - config.on('eleventy.after', () => { - execSync(`npx pagefind --site dist --glob \"**/*.html\"`, { encoding: 'utf-8' }); + config.on("eleventy.after", () => { + execSync(`npx pagefind --site dist --glob \"**/*.html\"`, { + encoding: "utf-8", + }); }); return { diff --git a/site/_includes/-header.njk b/site/_includes/-header.njk index fde7dbe..266a380 100644 --- a/site/_includes/-header.njk +++ b/site/_includes/-header.njk @@ -1,3 +1,4 @@ +{%- from "components/tagList.njk" import tagList with context -%}
-

{{ tag | capitalize if tag else title }}

+

{{ title }}

{%- if author -%} {{ author }} @@ -33,5 +34,8 @@ {%- if date and not omitMetadata -%} {{ date | formatDate("MMMM D, YYYY") }} {%- endif -%} + {# {%- if tags -%} #} + {{ tagList(tags.slice(1)) }} + {# {%- endif -%} #}
\ No newline at end of file diff --git a/site/_includes/base.njk b/site/_includes/base.njk index 3f22dc3..d15bd8c 100644 --- a/site/_includes/base.njk +++ b/site/_includes/base.njk @@ -2,7 +2,7 @@ title: Joshua's Homepage --- -{%- set computedTitle = tag | capitalize if tag else title -%} +{%- set computedTitle = tag | capitalize if tag and not useTitle else title -%} @@ -19,6 +19,9 @@ title: Joshua's Homepage {{ computedTitle }} - {{ site.title }} + {%- if keywords -%} + + {%- endif %} diff --git a/site/_includes/collection.njk b/site/_includes/collection.njk index 03082a7..ec4dbac 100644 --- a/site/_includes/collection.njk +++ b/site/_includes/collection.njk @@ -3,23 +3,13 @@ layout: "base.njk" eleventyComputed: title: "{{ page.fileSlug | capitalize }}" --- - +{%- from "components/collectionList.njk" import collectionList with context -%}
{{ content | safe }}
{% set tag = page.fileSlug %} {% if collections[tag] %} -
- {% for item in collections[tag] | reverse %} -
- {{ item.data.title }} - {% if item.data.date %} - - {% endif %} -

{{ item.data.description }}

-
- {% endfor %} -
+ {{ collectionList(collections[tag]) }} {% endif %} -
\ No newline at end of file + diff --git a/site/_includes/components/collectionList.njk b/site/_includes/components/collectionList.njk new file mode 100644 index 0000000..98be34e --- /dev/null +++ b/site/_includes/components/collectionList.njk @@ -0,0 +1,23 @@ +{%- from "components/tagList.njk" import tagList with context -%} + +{% macro collectionList(collection, limit=0) %} + +{% endmacro %} \ No newline at end of file diff --git a/site/_includes/components/tagList.njk b/site/_includes/components/tagList.njk new file mode 100644 index 0000000..e75e475 --- /dev/null +++ b/site/_includes/components/tagList.njk @@ -0,0 +1,11 @@ +{% macro tagList(tags) %} + + {%- for tag in tags -%} + {{ tag }} + {% endfor -%} + +{% endmacro %} \ No newline at end of file diff --git a/site/_includes/css/site.css b/site/_includes/css/site.css index ba8d744..1e22cc5 100644 --- a/site/_includes/css/site.css +++ b/site/_includes/css/site.css @@ -1,6 +1,6 @@ :root { font-size: 16px; - font-size: clamp(14px, 8px + 0.65vw, 24px); + font-size: clamp(14px, 8px + 0.65vw, 30px); box-sizing: border-box; display: flex; flex-direction: column; @@ -22,7 +22,9 @@ body { --c-text-light: var(--c-highlight); --c-text-dark: var(--c-dark); - --c-text-dim: color-mix(in lch, var(--c-text-dark), transparent 30%); + --c-text-dim: color-mix(in lch, var(--c-text-dark), transparent 50%); + + --tag-luminance: 0.4; --ratio: 1.333; --s-5: calc(var(--s-4) / var(--ratio)); @@ -79,6 +81,7 @@ body[data-theme="dark"] { --c-text-background-light: oklch(45% 0.135 280 / 0.3); --c-text-light: oklch(94% 0.045 107.2); --c-text-dark: oklch(94% 0.045 107.2); + --tag-luminance: 0.85; } @media (prefers-color-scheme: dark) { @@ -95,6 +98,7 @@ body[data-theme="dark"] { --c-text-background-light: oklch(45% 0.135 280 / 0.3); --c-text-light: oklch(94% 0.045 107.2); --c-text-dark: oklch(94% 0.045 107.2); + --tag-luminance: 0.85; } } @@ -135,6 +139,7 @@ a[href] { nav label:has(input:focus-visible), nav label:has(input:checked) { outline: none; + color: var(--c-text-dark); background-color: var(--c-accent); box-shadow: 0 0 0.5rem var(--c-accent), @@ -153,6 +158,13 @@ nav label:has(input:focus-visible) { outline: 2px solid var(--c-text-dark); } +.tag { + &::before { + content: "#"; + } + color: oklch(var(--tag-luminance) 0.25 calc(25 + 360 * var(--tagIndex, 0) / var(--totalTags, 1)) / 0.8); +} + main p img { max-width: 100%; } @@ -280,6 +292,16 @@ ul.collection { padding-left: 0; > li { list-style-type: none; + display: grid; + align-items: baseline; + grid-template-columns: auto auto 1fr; + > p { + &::before { + content: none; + } + width: 100%; + grid-column: 1/-1; + } + li { margin-top: 1rem; } @@ -479,39 +501,38 @@ h4 { font-weight: 700; text-shadow: 0 0 0.5em var(--c-highlight); margin-top: 0; - main > &:nth-child(n + 2) { - margin-top: 1.5rem; + &:nth-child(n + 2) { + margin-top: 2rem; } margin-bottom: 0.5rem; &:hover .header-anchor { opacity: 1; } } -:is(h1, h2, h3)::after { - pointer-events: none; - opacity: 0.5; - content: ""; - position: absolute; - z-index: -1; - bottom: 0; - left: -4rem; - height: 4em; - width: 30rem; - max-width: 100%; - background: radial-gradient( - ellipse farthest-side at 50% 100%, - var(--c-highlight), - transparent - ); +@media screen { + :is(h1, h2, h3)::after { + pointer-events: none; + opacity: 0.5; + content: ""; + position: absolute; + z-index: -1; + bottom: 0; + left: -4rem; + height: 4em; + width: 30rem; + max-width: 100%; + background: radial-gradient( + ellipse farthest-side at 50% 100%, + var(--c-highlight), + transparent + ); + } } h1 { margin-bottom: 0; } .header-meta { - display: flex; - flex-direction: row; - flex-wrap: wrap; font-size: var(--s-1); > * + *::before { content: "-"; diff --git a/site/_includes/home.njk b/site/_includes/home.njk index 92dac4e..0520347 100644 --- a/site/_includes/home.njk +++ b/site/_includes/home.njk @@ -1,29 +1,13 @@ --- layout: "base.njk" --- +{%- from "components/collectionList.njk" import collectionList with context -%}
{{ content | safe }} {%- for name, collection in collections -%} {%- if name in ["posts"] -%}

{{name | capitalize }}

- + {{ collectionList(collection, limit=5) }} {%- endif -%} {%- endfor -%}
diff --git a/site/pages/books.md b/site/pages/books.md index 457bcb7..de096c6 100644 --- a/site/pages/books.md +++ b/site/pages/books.md @@ -2,6 +2,7 @@ title: Books layout: "page.njk" omitTOC: true +keywords: books --- These books had a big influence on my thinking and taste. diff --git a/site/pages/now.md b/site/pages/now.md index aaeab5b..3cff963 100644 --- a/site/pages/now.md +++ b/site/pages/now.md @@ -3,6 +3,7 @@ title: Now layout: "page.njk" date: 2025-06-07 omitTOC: true +keywords: now --- I live in Southbridge, Massachusetts since 2022, with my wife Sara and our three cats. We're starting to get more integrated here. I'm about to start a new role, my third or fourth startup depending on how you count it. I'm active in [a local church](https://www.stmichaelorthodox.com/). I recently changed my mind about olives (I like them now). diff --git a/site/pages/recipes.md b/site/pages/recipes.md index fe4f12e..1ac1214 100644 --- a/site/pages/recipes.md +++ b/site/pages/recipes.md @@ -1 +1,4 @@ +--- +keywords: recipes +--- My most-made or most-requested recipes: diff --git a/site/pages/tag.njk b/site/pages/tag.njk new file mode 100644 index 0000000..e876f84 --- /dev/null +++ b/site/pages/tag.njk @@ -0,0 +1,24 @@ +--- +layout: "base.njk" +eleventyComputed: + title: "Posts tagged #{{ tag }}" +pagination: + data: collections + size: 1 + alias: tag + filter: + - posts + - recipes + - timeline +permalink: /tags/{{ tag | slugify }}/ +useTitle: true +--- +{%- from "components/collectionList.njk" import collectionList with context -%} +
+
+ {{ content | safe }} +
+ {% if collections[tag] %} + {{ collectionList(collections[tag]) }} + {% endif %} +
diff --git a/site/pages/tags.njk b/site/pages/tags.njk new file mode 100644 index 0000000..97f56a8 --- /dev/null +++ b/site/pages/tags.njk @@ -0,0 +1,15 @@ +--- +layout: base.njk +title: Tags +permalink: /tags/ +--- +
+ +
diff --git a/site/pages/unoffice-hours.md b/site/pages/unoffice-hours.md index 27ea8bf..c568ec0 100644 --- a/site/pages/unoffice-hours.md +++ b/site/pages/unoffice-hours.md @@ -3,6 +3,7 @@ title: Unoffice Hours layout: "page.njk" omitDate: true omitTOS: true +keywords: Unoffice hours --- Working remotely has a lot of benefits, but we miss out on the serendipitous connections and socialization that happened just from being in the same room. Unoffice hours is part of an attempt to make new connections, but without applying the giant invisible filter of my own preferences. diff --git a/site/pages/uses.md b/site/pages/uses.md index d6208a1..c7c2724 100644 --- a/site/pages/uses.md +++ b/site/pages/uses.md @@ -1,13 +1,13 @@ --- title: What I Use layout: "page.njk" -date: 2025-06-10 +date: 2025-06-20 --- ## Hardware - Lenovo LOQ laptop (personal), Framework laptop (work). -- Google Pixel 8 phone. +- Google Pixel 7 phone. - Wide IKEA GALANT desk (discontinued). @@ -19,7 +19,7 @@ date: 2025-06-10 - [High-output LED light](https://amzn.to/4kUm2ku) in an [IKEA REGOLIT shade](https://www.ikea.com/us/en/p/regolit-pendant-lamp-shade-white-handmade-70103410/) for mood regulation and for the webcam. -- [USB microphone with boom](https://amzn.to/4lgbwob). +- [USB microphone with boom](https://amzn.to/4lgbwob) but I recently got [a better mic arm](https://amzn.to/4l2wE16). - [Sony WH1000XM2 headphones](https://www.sony.com/electronics/support/wireless-headphones-bluetooth-headphones/wh-1000xm2/specifications). diff --git a/site/posts/2020-07-02-authority-consents-blind-spot.md b/site/posts/2020-07-02-authority-consents-blind-spot.md index 4d5b331..f3dfb3c 100644 --- a/site/posts/2020-07-02-authority-consents-blind-spot.md +++ b/site/posts/2020-07-02-authority-consents-blind-spot.md @@ -1,6 +1,8 @@ --- title: Authority - Consent's Blind Spot description: Authority is the idea that certain people are justified in violating consent. This belief allows people to force others to do what they want with a clear conscience. +tags: + - ethos --- > 2025 disclaimer: I haven't thought through all these ideas in a while, but I probably would change some of this or soften/rephrase it. diff --git a/site/posts/2021-04-03-trivium.md b/site/posts/2021-04-03-trivium.md index 393234b..b04da39 100644 --- a/site/posts/2021-04-03-trivium.md +++ b/site/posts/2021-04-03-trivium.md @@ -1,6 +1,9 @@ --- title: 'The Trivium: A Tool for Learning Anything' description: An ancient methodology for learning, which formed the basis of classical education in the Middle Ages and Rennaisance. +tags: + - learning + - how to --- Information today has become siloed. It's a common belief that little, if any, expertise from one field of knowledge transfers over to other fields. But there is a forgotten tool that anyone can use to confidently approach new subjects and problems: the _Trivium_. diff --git a/site/posts/2021-05-26-needs-based-communication.md b/site/posts/2021-05-26-needs-based-communication.md index 3d1ddb5..b13f9a6 100644 --- a/site/posts/2021-05-26-needs-based-communication.md +++ b/site/posts/2021-05-26-needs-based-communication.md @@ -1,6 +1,9 @@ --- title: Needs-based communication description: How to connect with others through shared human needs. +tags: + - communication + - how to --- Needs-based communication (usually called non-violent communication or NVC™) is a way of understanding yourself and others with a unique insight: everyone has their own personal reactions to the world, but people have the same basic needs. We recognize those needs in other people, and that common connection can allow us to communicate clearly when there is conflict. diff --git a/site/posts/2021-10-16-embracing-mysticism.md b/site/posts/2021-10-16-embracing-mysticism.md index 0399f40..7261b0a 100644 --- a/site/posts/2021-10-16-embracing-mysticism.md +++ b/site/posts/2021-10-16-embracing-mysticism.md @@ -1,6 +1,9 @@ --- title: Embracing Mysticism description: Society is moving from a materialistic era into a mystical one. I describe my attempt to gain mystical proficiency. +tags: + - faith + - ethos --- Logical arguments no longer work. This has been especially visible in the pandemic response in 2020. If you dug even a little below the surface of any mainstream narrative in the past two years, you likely found points of disagreement. But showing people scientific papers, charts, statistical analysis, or other types of evidence accomplishes nothing, or worse it invites accusations of being part of the Other Team, one of those backwards, wrong, stupid people. diff --git a/site/posts/2023-07-14-finally-a-coherent-worldview.md b/site/posts/2023-07-14-finally-a-coherent-worldview.md index b455533..e2989d5 100644 --- a/site/posts/2023-07-14-finally-a-coherent-worldview.md +++ b/site/posts/2023-07-14-finally-a-coherent-worldview.md @@ -1,6 +1,9 @@ --- title: Finally, a Coherent Worldview description: Why did I think I could figure everything out on my own? +tags: + - faith + - ethos --- Around the time of my previous post, I had just been received into the Orthodox church. Since then I have found it to be everything I was looking for, and a great deal more. There is such a wealth of wisdom, going back thousands of years. And it was all there, un-seen, un-read, as far as I was concerned not existing at all! It seems impossible to me that I was so unaware of these treasures by chance. diff --git a/site/posts/2024-06-05-redesign.md b/site/posts/2024-06-05-redesign.md index c13265e..75cbb11 100644 --- a/site/posts/2024-06-05-redesign.md +++ b/site/posts/2024-06-05-redesign.md @@ -1,6 +1,8 @@ --- title: Site design updated description: New look, simpler tech. +tags: + - technical --- New design! The tools I used before have a lot of unmaintained or outdated dependencies and I wanted something a little simpler. The new site uses [pnpm](https://pnpm.io/), [11ty](https://www.11ty.dev/), and [Nunjucks](https://mozilla.github.io/nunjucks/). Content is still in [markdown](https://daringfireball.net/projects/markdown/). diff --git a/site/posts/2025-04-24-thinking-machines.md b/site/posts/2025-04-24-thinking-machines.md index e2ed0ce..2419e02 100644 --- a/site/posts/2025-04-24-thinking-machines.md +++ b/site/posts/2025-04-24-thinking-machines.md @@ -3,6 +3,9 @@ title: Thinking machines slug: thinking-machines description: "The computers will start thinking, and people will stop." cover: "/2025-04-24--computers-will-start-thinking.jpg" +tags: + - zeitgeist + - ai --- There's an exchange early in the classic '80s movie [TRON](https://www.themoviedb.org/movie/97-tron). Some scientists are talking shop: diff --git a/site/posts/2025-05-15-tools-of-the-trade.md b/site/posts/2025-05-15-tools-of-the-trade.md index 7a019a1..40ff74c 100644 --- a/site/posts/2025-05-15-tools-of-the-trade.md +++ b/site/posts/2025-05-15-tools-of-the-trade.md @@ -2,6 +2,9 @@ title: Tools of the trade slug: tools-of-the-trade description: Some dev tools I recommend. +tags: + - technical + - software --- Everyone has different tools that they find especially effective. Here are some I have found with a few words about why I like them. diff --git a/site/posts/2025-06-15-replacing-github-pages.md b/site/posts/2025-06-15-replacing-github-pages.md index 12525f7..9cd51fe 100644 --- a/site/posts/2025-06-15-replacing-github-pages.md +++ b/site/posts/2025-06-15-replacing-github-pages.md @@ -2,6 +2,10 @@ title: My Very Own GitHub Pages slug: my-very-own-github-pages description: How to self-host Forgejo and automatically serve your web build branches with SSL. +tags: + - how to + - technical + - selfhosting --- I recently started self-hosting [Forgejo](https://forgejo.org/), but I wanted something to replace GitHub pages, which has been very convenient for publishing little website projects. My server runs Debian, so I decided to use [webhook](https://github.com/adnanh/webhook) and [Caddy](https://caddyserver.com/). I'm very happy how it turned out. diff --git a/site/posts/posts.11tydata.js b/site/posts/posts.11tydata.js index 1dabe26..4f5c77d 100644 --- a/site/posts/posts.11tydata.js +++ b/site/posts/posts.11tydata.js @@ -6,6 +6,7 @@ export default { eleventyComputed: { date: "{{ page.date }}", slug: "{{ page.slug }}", + keywords: (data) => data.tags.join(", "), permalink: (data) => { if (process.env.ELEVENTY_RUN_MODE !== "build") return data.permalink; else return data.draft ? false : data.permalink;