auto-colored tags for posts

This commit is contained in:
Joshua Seigler 2025-06-21 14:45:15 -04:00
parent 4cd8d22238
commit 04e4b0e51e
17 changed files with 152 additions and 64 deletions

View file

@ -12,7 +12,7 @@ import clean from "eleventy-plugin-clean";
import toc from "eleventy-plugin-toc"; import toc from "eleventy-plugin-toc";
import site from "./site/_data/site.js"; import site from "./site/_data/site.js";
import { feedPlugin } from "@11ty/eleventy-plugin-rss"; 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 eleventyAutoCacheBuster from "eleventy-auto-cache-buster";
import mdPrism from "markdown-it-prism"; import mdPrism from "markdown-it-prism";
@ -36,7 +36,7 @@ export default (config) => {
.use(mdFootnote) .use(mdFootnote)
.use(mdSpoiler, { .use(mdSpoiler, {
tag: "span", tag: "span",
attrs: [["class", "aside"]] attrs: [["class", "aside"]],
}) })
.use(mdLinkAttributes, { .use(mdLinkAttributes, {
matcher(href) { matcher(href) {
@ -48,11 +48,17 @@ export default (config) => {
}, },
}) })
.use(mdPrism); .use(mdPrism);
mdLib.renderer.rules.render_footnote_anchor = (tokens, idx, options, env, slf) => { mdLib.renderer.rules.render_footnote_anchor = (
let id = slf.rules.footnote_anchor_name(tokens, idx, options, env, slf) tokens,
if (tokens[idx].meta.subId > 0) id += `:${tokens[idx].meta.subId}` 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 */ /* ↩ with escape code to prevent display as Apple Emoji on iOS */
return ` <a href="#fnref${id}" class="footnote-backref">\u21a9\uFE0E</a>` return ` <a href="#fnref${id}" class="footnote-backref">\u21a9\uFE0E</a>`;
}; };
config.setLibrary("md", mdLib); config.setLibrary("md", mdLib);
config.addPassthroughCopy({ config.addPassthroughCopy({
@ -83,6 +89,24 @@ export default (config) => {
return musicFiles; 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) => { config.addTransform("prettier", (content, outputPath) => {
if (typeof outputPath !== "string") { if (typeof outputPath !== "string") {
return content; return content;
@ -142,8 +166,10 @@ export default (config) => {
config.addPlugin(toc); config.addPlugin(toc);
config.on('eleventy.after', () => { config.on("eleventy.after", () => {
execSync(`npx pagefind --site dist --glob \"**/*.html\"`, { encoding: 'utf-8' }); execSync(`npx pagefind --site dist --glob \"**/*.html\"`, {
encoding: "utf-8",
});
}); });
return { return {

View file

@ -1,3 +1,4 @@
{%- from "components/tagList.njk" import tagList with context -%}
<header> <header>
<nav> <nav>
<div class="nav-row"> <div class="nav-row">
@ -33,5 +34,8 @@
{%- if date and not omitMetadata -%} {%- if date and not omitMetadata -%}
<date>{{ date | formatDate("MMMM D, YYYY") }}</date> <date>{{ date | formatDate("MMMM D, YYYY") }}</date>
{%- endif -%} {%- endif -%}
{# {%- if tags -%} #}
{{ tagList(tags.slice(1)) }}
{# {%- endif -%} #}
</div> </div>
</header> </header>

View file

@ -3,23 +3,13 @@ layout: "base.njk"
eleventyComputed: eleventyComputed:
title: "{{ page.fileSlug | capitalize }}" title: "{{ page.fileSlug | capitalize }}"
--- ---
{%- from "components/collectionList.njk" import collectionList with context -%}
<main> <main>
<section data-pagefind-body> <section data-pagefind-body>
{{ content | safe }} {{ content | safe }}
</section> </section>
{% set tag = page.fileSlug %} {% set tag = page.fileSlug %}
{% if collections[tag] %} {% if collections[tag] %}
<section> {{ collectionList(collections[tag]) }}
{% for item in collections[tag] | reverse %}
<article class="item-summary">
<a href="{{ item.url }}">{{ item.data.title }}</a>
{% if item.data.date %}
<aside>{{ item.data.date | formatDate("MMMM DD, YYYY") }}</aside>
{% endif %}
<p class="item-summary-description">{{ item.data.description }}</p>
</article>
{% endfor %}
</section>
{% endif %} {% endif %}
</main> </main>

View file

@ -0,0 +1,23 @@
{%- from "components/tagList.njk" import tagList with context -%}
{% macro collectionList(collection, limit=0) %}
<ul class="collection">
{%- for item in collection | reverse -%}
{%- if (limit === 0 or loop.index <= limit) -%}
<li>
<a href="{{item.url}}">{{item.data.title}}</a>
{%- if item.data.date -%}
<aside>{{item.data.date | formatDate("MMMM DD, YYYY") }}</aside>
{%- endif -%}
<aside>{{ tagList(item.data.tags.slice(1)) }}</aside>
<p>{{item.data.description | safe}}</p>
</li>
{%- endif -%}
{%- if (limit !== 0 and loop.index == limit + 1) -%}
<li>
<a href="/{{name}}/">More {{name}}&hellip;</a>
</li>
{%- endif -%}
{%- endfor -%}
</ul>
{% endmacro %}

View file

@ -0,0 +1,11 @@
{% macro tagList(tags) %}
<span class="tags">
{%- for tag in tags -%}
<a
class="tag"
style="--tagIndex:{{ collections.categories[tag].id }}"
href="/posts/tag/{{ tag | slugify }}"
>{{ tag }}</a>
{% endfor -%}
</span>
{% endmacro %}

View file

@ -22,7 +22,9 @@ body {
--c-text-light: var(--c-highlight); --c-text-light: var(--c-highlight);
--c-text-dark: var(--c-dark); --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; --ratio: 1.333;
--s-5: calc(var(--s-4) / var(--ratio)); --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-background-light: oklch(45% 0.135 280 / 0.3);
--c-text-light: oklch(94% 0.045 107.2); --c-text-light: oklch(94% 0.045 107.2);
--c-text-dark: oklch(94% 0.045 107.2); --c-text-dark: oklch(94% 0.045 107.2);
--tag-luminance: 0.85;
} }
@media (prefers-color-scheme: dark) { @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-background-light: oklch(45% 0.135 280 / 0.3);
--c-text-light: oklch(94% 0.045 107.2); --c-text-light: oklch(94% 0.045 107.2);
--c-text-dark: oklch(94% 0.045 107.2); --c-text-dark: oklch(94% 0.045 107.2);
--tag-luminance: 0.85;
} }
} }
@ -153,6 +157,13 @@ nav label:has(input:focus-visible) {
outline: 2px solid var(--c-text-dark); outline: 2px solid var(--c-text-dark);
} }
.tag {
&::before {
content: "#";
}
color: oklch(var(--tag-luminance) 0.25 calc(222.5 * var(--tagIndex, 0)) / 0.8);
}
main p img { main p img {
max-width: 100%; max-width: 100%;
} }
@ -280,6 +291,16 @@ ul.collection {
padding-left: 0; padding-left: 0;
> li { > li {
list-style-type: none; 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 { + li {
margin-top: 1rem; margin-top: 1rem;
} }
@ -479,39 +500,38 @@ h4 {
font-weight: 700; font-weight: 700;
text-shadow: 0 0 0.5em var(--c-highlight); text-shadow: 0 0 0.5em var(--c-highlight);
margin-top: 0; margin-top: 0;
main > &:nth-child(n + 2) { &:nth-child(n + 2) {
margin-top: 1.5rem; margin-top: 2rem;
} }
margin-bottom: 0.5rem; margin-bottom: 0.5rem;
&:hover .header-anchor { &:hover .header-anchor {
opacity: 1; opacity: 1;
} }
} }
:is(h1, h2, h3)::after { @media screen {
pointer-events: none; :is(h1, h2, h3)::after {
opacity: 0.5; pointer-events: none;
content: ""; opacity: 0.5;
position: absolute; content: "";
z-index: -1; position: absolute;
bottom: 0; z-index: -1;
left: -4rem; bottom: 0;
height: 4em; left: -4rem;
width: 30rem; height: 4em;
max-width: 100%; width: 30rem;
background: radial-gradient( max-width: 100%;
ellipse farthest-side at 50% 100%, background: radial-gradient(
var(--c-highlight), ellipse farthest-side at 50% 100%,
transparent var(--c-highlight),
); transparent
);
}
} }
h1 { h1 {
margin-bottom: 0; margin-bottom: 0;
} }
.header-meta { .header-meta {
display: flex;
flex-direction: row;
flex-wrap: wrap;
font-size: var(--s-1); font-size: var(--s-1);
> * + *::before { > * + *::before {
content: "-"; content: "-";

View file

@ -1,29 +1,13 @@
--- ---
layout: "base.njk" layout: "base.njk"
--- ---
{%- from "components/collectionList.njk" import collectionList with context -%}
<main data-pagefind-body> <main data-pagefind-body>
{{ content | safe }} {{ content | safe }}
{%- for name, collection in collections -%} {%- for name, collection in collections -%}
{%- if name in ["posts"] -%} {%- if name in ["posts"] -%}
<h2>{{name | capitalize }}</h2> <h2>{{name | capitalize }}</h2>
<ul class="collection"> {{ collectionList(collection, limit=5) }}
{%- for item in collection | reverse -%}
{%- if (loop.index <= 5) -%}
<li>
<a href="{{item.url}}">{{item.data.title}}</a>
{%- if item.data.date -%}
<aside>{{item.data.date | formatDate("MMMM DD, YYYY") }}</aside>
{%- endif -%}
<p>{{item.data.description | safe}}</p>
</li>
{%- endif -%}
{%- if (loop.index == 6) -%}
<li>
<a href="/{{name}}/">More {{name}}&hellip;</a>
</li>
{%- endif -%}
{%- endfor -%}
</ul>
{%- endif -%} {%- endif -%}
{%- endfor -%} {%- endfor -%}
</main> </main>

View file

@ -1,13 +1,13 @@
--- ---
title: What I Use title: What I Use
layout: "page.njk" layout: "page.njk"
date: 2025-06-10 date: 2025-06-20
--- ---
## Hardware ## Hardware
- Lenovo LOQ laptop (personal), Framework laptop (work). - Lenovo LOQ laptop (personal), Framework laptop (work).
- Google Pixel 8 phone. - Google Pixel 7 phone.
- Wide IKEA GALANT desk (discontinued). - 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. - [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). - [Sony WH1000XM2 headphones](https://www.sony.com/electronics/support/wireless-headphones-bluetooth-headphones/wh-1000xm2/specifications).

View file

@ -1,6 +1,9 @@
--- ---
title: Authority - Consent's Blind Spot 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. 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:
- opinions
- worldview
--- ---
> 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. > 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.

View file

@ -1,6 +1,9 @@
--- ---
title: 'The Trivium: A Tool for Learning Anything' 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. 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_. 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_.

View file

@ -1,6 +1,9 @@
--- ---
title: Needs-based communication title: Needs-based communication
description: How to connect with others through shared human needs. 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. 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.

View file

@ -1,6 +1,10 @@
--- ---
title: Embracing Mysticism title: Embracing Mysticism
description: Society is moving from a materialistic era into a mystical one. I describe my attempt to gain mystical proficiency. description: Society is moving from a materialistic era into a mystical one. I describe my attempt to gain mystical proficiency.
tags:
- opinions
- faith
- worldview
--- ---
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. 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.

View file

@ -1,6 +1,9 @@
--- ---
title: Finally, a Coherent Worldview title: Finally, a Coherent Worldview
description: Why did I think I could figure everything out on my own? description: Why did I think I could figure everything out on my own?
tags:
- faith
- worldview
--- ---
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. 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.

View file

@ -1,6 +1,8 @@
--- ---
title: Site design updated title: Site design updated
description: New look, simpler tech. description: New look, simpler tech.
tags:
- this site
--- ---
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/). 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/).

View file

@ -3,6 +3,10 @@ title: Thinking machines
slug: thinking-machines slug: thinking-machines
description: "The computers will start thinking, and people will stop." description: "The computers will start thinking, and people will stop."
cover: "/2025-04-24--computers-will-start-thinking.jpg" cover: "/2025-04-24--computers-will-start-thinking.jpg"
tags:
- opinions
- trends
- ai
--- ---
There's an exchange early in the classic '80s movie [TRON](https://www.themoviedb.org/movie/97-tron). Some scientists are talking shop: There's an exchange early in the classic '80s movie [TRON](https://www.themoviedb.org/movie/97-tron). Some scientists are talking shop:

View file

@ -2,6 +2,10 @@
title: Tools of the trade title: Tools of the trade
slug: tools-of-the-trade slug: tools-of-the-trade
description: Some dev tools I recommend. description: Some dev tools I recommend.
tags:
- technical
- software
- opinions
--- ---
Everyone has different tools that they find especially effective. Here are some I have found with a few words about why I like them. Everyone has different tools that they find especially effective. Here are some I have found with a few words about why I like them.

View file

@ -2,6 +2,10 @@
title: My Very Own GitHub Pages title: My Very Own GitHub Pages
slug: 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. 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. 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.