Items without a type field (existing data before type feature was added)
were only appearing in "personal" filtered views. Since the /cv/ page uses
work-only variants, all existing untyped data was hidden. Changed filtering
so untyped items appear in both work and personal views.
Also guard cv.skills dictsort in cv.njk against undefined.
Add thin-wrapper templates for work/personal filtering of CV sections:
- 8 new templates: cv-{experience,education,skills,interests}-{personal,work}.njk
- cv-languages.njk: standalone languages section (split from education)
- homepage-section.njk: 9 new routes for filtered variants
- cv.njk: uses work-only variants for the /cv/ page
- Base templates: filterType support in experience, education, skills, interests
- _data/cv.js: skillTypes and interestTypes fallback fields
Show rich link preview cards in bookmarks, likes, replies, reposts
collection pages and the homepage recent posts section. URLs are
fetched once and cached — the same cache serves all templates.
The target URL in likes, bookmarks, replies, and reposts now renders
as a rich OpenGraph card via the unfurl shortcode instead of a bare
link. The raw URL remains below the card for h-cite microformat
compatibility. Results are cached in .cache/unfurl/ for 1 week.
- Add <noscript><style> in base.njk that unhides x-cloak/x-show content,
hides FAB and tab buttons when JS is disabled (content stacks instead)
- Add noscript message on search page with links to blog/categories
- Add noscript banner on interactions page explaining inbound tab needs JS
- Add ActivityPub/Fediverse platform badge (purple, network icon) to
interactions page alongside existing Mastodon and Bluesky badges
- Detect platform from Bridgy source URLs and author URLs for
webmention.io items that lack a platform field
- Filter self-referencing syndication URLs from "Also on" footer so
self-hosted AP posts don't show a redundant link back to the site
AI crawlers (GPTBot) were following edit/create links in the static
HTML, flooding the server with /posts/edit and /session/login requests.
Adding nofollow tells well-behaved crawlers to skip these admin-only links.
Eleventy 3.x renders Nunjucks templates in parallel, causing page.url
to return wrong values in {% set %} tags. This caused OG images to be
mismatched between pages (e.g., bookmark showed note's OG image).
Move ogSlug and hasOgImage computation to eleventyComputed, which runs
during the sequential data cascade phase before parallel rendering.
The computed values are then available as plain template variables.
Refs: https://github.com/11ty/eleventy/issues/3183
page.fileSlug suffers from a race condition in Eleventy 3.x parallel
rendering where Nunjucks shares state across template compilations,
causing slugs to get mixed up between pages. page.url is always
correct, so derive the OG slug from it instead.
The webmentions sidebar widget uses Alpine.js + fetch API but was
missing the is-land on:visible wrapper that all other JS-dependent
sidebar widgets have. This defers its initialization until scrolled
into view, consistent with social-activity, github-repos, funkwhale,
blogroll, and feedland widgets.
- Add time-difference web component for relative dates
- Add @zachleat/table-saw for responsive tables
- Add webmention facepile styling with bookmarks support
- Add OG image thumbnails to post navigation
- Add @11ty/is-land for lazy widget hydration
- Wrap sidebar widgets in is-land for deferred loading
- Lazy-load webmention avatars with is-land
- Add @zachleat/filter-container for blog archive filtering
- Add posting frequency sparkline to blog header
- Inline critical CSS and defer full stylesheet loading
Uses Satori + @resvg/resvg-js to create branded 1200x630 social
preview cards at build time. Cards show post title, type badge,
date, and site name on a dark background with blue accent.
Generated images are cached in .cache/og/ (persistent on Cloudron)
and passthrough-copied to the output. Posts with photos continue
using their own images. Untitled posts (notes) use body text.
Add row selection and expand/collapse behavior matching Dave Winer's
blogroll.js: first click selects, second click (or caret click) expands
to show up to 5 recent items fetched from blogroll API. Items cached
after first fetch. Added dark mode styles for expanded items.
Replicates Dave Winer's blogroll.js visual rendering (240px bordered
container, Ubuntu/Rancho fonts, Title/When sort, caret wedges, truncated
titles, relative timestamps, "Powered by FeedLand" footer) using Alpine.js
and the blogroll API instead of jQuery + external scripts.
Registered in all three sidebar types (homepage, blog listing, blog post)
and in the fallback sidebar.
Sidebar widget now fetches blogs sorted by recently updated and groups
them into tabs by source type (Microsub/FeedLand). Tabs only appear
when multiple source types exist. Each tab shows up to 8 blogs.
When viewing a post that has mpUrl in its frontmatter, the FAB menu
shows an "Edit this post" link at the top that redirects to the
Indiekit admin edit form via /posts/edit?url=<mpUrl>.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- cv-projects-personal.njk: filters projects with projectType=personal (or unset)
- cv-projects-work.njk: filters projects with projectType=work
- CV page now only shows work projects
- Homepage section dispatcher routes both new types
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The defer + DOMContentLoaded queue approach failed silently when
pagefind-ui.js couldn't load. Moving the script to end of body
ensures all DOM elements exist and processes the queue immediately
after the script loads.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Pagefind CSS/JS is now loaded once in base.njk <head> with defer.
A tiny initPagefind() helper queues widget inits until DOMContentLoaded
when PagefindUI is available. Removes duplicate <link>/<script> tags
from all sidebar widgets, search page, and 404 page.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replaces the per-post webmentions sidebar widget (which was redundant
with the main content webmentions section) with a site-wide widget
showing recent inbound webmentions (via API) and outbound interactions
(from Eleventy collections). Available in all sidebars.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Post navigation (prev/next) now renders below the syndication block
in the main content flow, matching how listing pages work. Removed
from sidebar widget system.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Post navigation now uses previousInCollection/nextInCollection filters
to find adjacent posts (avoids Nunjucks loop scoping bug). Webmentions
sidebar widget now uses webmentionsForUrl and webmentionsByType filters
instead of non-existent filter function.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Fix recentPosts collection glob (content/posts/ → content/) so the
widget actually finds posts. Extract blog-sidebar widgets into reusable
partials. Make both sidebar.njk (listing pages) and blog-sidebar.njk
(post pages) configurable via homepageConfig.blogListingSidebar and
homepageConfig.blogPostSidebar, with fallback to current hardcoded
widgets for backward compatibility.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- New /changelog/ page with Alpine.js tabbed interface showing commits
across all indiekit repos, categorized into 7 groups with load-more
- Footer refactored from minimal feed links to responsive 4-zone grid:
Navigate, Content, Connect (social links), Meta (feeds + changelog)
- Footer uses grid-cols-2 on mobile, grid-cols-4 on desktop
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Prevents all layout shift by locking the content area to h-[340px]
with overflow-y-auto for scrolling if content exceeds the area.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add min-h-[320px] to prevent layout shift when switching tabs
- Show 5 items per tab for consistent content height
- Simplify tab buttons to text-only at text-xs for compact layout
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Tab order: Commits (default), Repos, Featured, PRs.
Repos tab fetches full repo list from GitHub public API.
Featured tab shows curated projects from /githubapi.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Uses Alpine.js client-side fetching from /githubapi/api/* for live data.
Three tabs: Projects (featured repos), Commits (recent activity), PRs.
Follows the blogroll widget pattern for dynamic data loading.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Integrates @11ty/eleventy-plugin-syntaxhighlight (PrismJS) for
build-time syntax highlighting of fenced code blocks. Includes
a custom GitHub-inspired theme with dark mode support (.dark class).
All existing articles with triple-backtick code blocks will
automatically get highlighting on next Eleventy rebuild.
Also fixes overflow-x: hidden on .prose/.e-content that was
clipping scrollable code blocks — changed to overflow-x: clip.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Recent Posts widget and Previous/Next navigation now show colored
icons and descriptive labels for likes, bookmarks, reposts, and
replies instead of "Untitled".
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Port the blog page's rich post-type rendering into both the homepage
recent-posts section and the sidebar widget. Likes, bookmarks, reposts,
replies, and photos now show colored icons, target URLs, and content
previews instead of generic "Untitled" cards.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
/now is already in the /slashes submenu dropdown, so having it
top-level was redundant. /cv gets promoted to main navbar.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Move CV content to a standalone /cv page that reuses the section partials
from the homepage builder. Simplify home.njk from a 3-tier to 2-tier
fallback (plugin config OR recent posts + explore links). Add /cv to
the slash pages navigation dropdown.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Wraps footer items in a CSS grid that's 1 column on mobile, 2 on tablet,
3 on desktop. Each custom-html block fills one column.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Projects now show startDate/endDate range. Education supports both
startDate/endDate (new) and year (backward compat) display.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The homepage builder's sidebar bypass was using homepageConfig (a global
Eleventy data file) to suppress the sidebar on ALL pages, not just the
homepage. Now only bypasses when page.url == "/" so blog, post type, and
plugin pages keep their normal sidebar.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Homepage builder now renders footer section below main content
- Sidebar supports custom-html widget type with title + content
- New homepage-footer.njk component for footer items
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>