Article title links now have explicit text color instead of inheriting
the global amber link color. Default article left-border changed from
amber to neutral surface tone. Card hover border toned down to neutral.
Interaction types (likes, bookmarks, reposts, replies, photos) keep
their semantic colors unchanged.
Confab-Link: http://localhost:8080/sessions/bd3f7012-c703-47e9-bfe2-2ad04ce1842d
OG images are now the primary navigation element — large, prominent,
and clickable. Directional labels overlay the image. Falls back to
text title + date when no OG image exists. Inspired by zachleat.com.
Confab-Link: http://localhost:8080/sessions/bd3f7012-c703-47e9-bfe2-2ad04ce1842d
Add `featuredPosts` collection filtering posts with `featured: true`
frontmatter. New `featured-posts` section template with type-aware
rendering (articles, notes, photos, bookmarks, etc.) and star icon
header. Registered in homepage-section.njk dispatcher.
To feature a post, add `featured: true` to its frontmatter. Then add
a `{ "type": "featured-posts" }` section to the homepage config.
Confab-Link: http://localhost:8080/sessions/bd3f7012-c703-47e9-bfe2-2ad04ce1842d
PagefindUI in the ~300px sidebar caused 218+ results to expand inline,
pushing content down with poor readability. Replace with a simple GET
form that redirects to /search/?q=query in all four sidebars.
Confab-Link: http://localhost:8080/sessions/4d40ef89-a713-48c1-b4ed-0ffafca25677
Pages (slash pages like /about, /now, /ai) were missing the AI usage
disclosure that already displays on posts. Adds the same aside block
to page.njk so AI metadata renders consistently across all content types.
Confab-Link: http://localhost:8080/sessions/3bf0740c-86e8-4ed6-b7ce-f83d8462c682
Add items-start to grid container so expanding one project card no longer
stretches the adjacent card to fill the row height. Add per-project color
cycling (border-l + tech tags) using the 8-color palette, replacing uniform
amber accent colors.
Confab-Link: http://localhost:8080/sessions/bd3f7012-c703-47e9-bfe2-2ad04ce1842d
Apply the collaboratively-written design system from .interface-design/system.md:
- Domain color map: Social pages (rose), Code pages (emerald), Reading pages
(orange), Music pages (purple) replace generic accent tokens
- Font-mono on all <time> elements via global CSS rule + wrap 7 non-<time> dates
- Shadow standardization: replace hover:shadow-lg with border-hover on news cards
- Active states: scale(0.97) press feedback on buttons and pagination links
- Gradient cleanup: remove to-white gradient on github featured project cards
- CV Skills & Interests: cycle through 8 distinct colors per family category
(amber, emerald, sky, rose, purple, orange, teal, indigo) on both CV page
and homepage
- Update system.md with refined palette documentation and domain color spec
Files: 19 changed across templates, CSS, and design documentation
Confab-Link: http://localhost:8080/sessions/bd3f7012-c703-47e9-bfe2-2ad04ce1842d
Replace the entire color system with a design-driven warm palette:
- Surface tokens: cold zinc grays → warm stone (#faf8f5 to #0f0e0d)
- Accent tokens: cold teal → warm amber (#fffbeb to #451a03)
- All bg-white → bg-surface-50 across templates (warm cream instead of pure white)
- Critical CSS: all hardcoded hex values updated to warm palette
- Prism code blocks: cold gray backgrounds → warm stone
- Pagefind search UI: blue buttons/links → amber interactive colors
- Dark mode: warm dark surfaces with amber accents throughout
Design system documented in .interface-design/system.md
Confab-Link: http://localhost:8080/sessions/bd3f7012-c703-47e9-bfe2-2ad04ce1842d
- Add teal accent color scale and activate Inter font via @font-face
- Neutralize nav/footer hovers from primary blue to surface neutrals
- Apply accent color to hero subtitle, FAB, CTA buttons, card hovers
- Fix reply post-type color from generic primary to distinctive sky blue
- Create centralized icon macro (icon.njk) with 24 reusable SVG icons
- Add per-widget-type icons and colored left-accent borders to all sidebars
- Update .p-category tags from blue to neutral surface with border
- Diversify color vocabulary: red (likes), amber (bookmarks/blogroll),
green (reposts), purple (funkwhale), sky (replies), orange (subscribe)
Confab-Link: http://localhost:8080/sessions/bd3f7012-c703-47e9-bfe2-2ad04ce1842d
Add usageInfo (link to /ai transparency page) and creativeWorkStatus
(human-readable AI disclosure) to the Article JSON-LD when AI metadata
properties are present. Valid Schema.org, no impact on ActivityPub.
Confab-Link: http://localhost:8080/sessions/83d68915-7921-4cc4-a838-c331fd1c4d6e
Show AI transparency metadata (text level, code level, tools) as a
subtle aside on individual posts and compact badges in blog listing.
Supports both camelCase and underscore property names.
Confab-Link: http://localhost:8080/sessions/83d68915-7921-4cc4-a838-c331fd1c4d6e
- Replace single localStorage string with versioned multi-domain store
(fediverse_domains_v1) with usage tracking, inspired by Mastodon's
share.joinmastodon.org project
- Add domain validation via URL constructor before redirecting
- Add mode param to fediverseInteract component: "interact" for
authorize_interaction, "share" for /share?text=...
- Migrate old fediverse_instance key automatically on first load
- Extract shared modal partial (fediverse-modal.njk) used by post
interaction, follow widget, and share widget
- Share widget now prompts visitors for their own instance instead of
hardcoding site owner's Mastodon instance
Confab-Link: http://localhost:8080/sessions/0ec83454-d346-4329-8aaf-6b12139bf596
Add shared save-later.js module and per-item save buttons to
blogroll, podroll, listening, and news pages. Buttons are hidden
by default and only visible when logged in. Posts to the readlater
plugin API at /readlater/save.
Alpine.js component that lets visitors click any image inside
article content to view it fullscreen with keyboard navigation
(arrow keys, Escape to close) and prev/next buttons.
When markdown images use full https:// URLs, the template was prepending
site.url again, causing double-domain in structured data
(e.g., https://rmendes.net/https://rmendes.net/media/...).
Now checks if postImage starts with 'http' and skips the prefix.
Add a reusable fullwidth layout (layouts/fullwidth.njk) for rich HTML
content that needs the full container width without sidebar or prose
constraints. Add the interactive architecture explorer as a static
asset served via passthrough copy at /interactive/architecture.html.
- layouts/fullwidth.njk: site header + footer only, no sidebar
- interactive/architecture.html: tabbed architecture guide
- eleventy.config.js: passthrough copy for interactive/ directory
Interests are now grouped by category (matching skills pattern).
Updated cv-interests.njk to iterate categories, cv.js fallback to {},
and cv.njk hasCvData check to include interests.
CV page hero now checks cvPageConfig.identity before falling back to
site.author, matching the homepage hero pattern. Social links use the
shared socialIcon macro.
Replace duplicated inline SVGs in h-card.njk and hero.njk with a shared
Nunjucks macro. Adds proper icons for ActivityPub, Forgejo, Codeberg,
PixelFed, and 25+ more platforms via Simple Icons (CC0).
Hero and h-card templates now resolve homepageConfig.identity.*
fields first, falling back to site.author.* env vars. Includes
design doc and implementation plan for the identity editor.
The extractFirstImage filter picks up <img> tags from the full rendered
page content, including sidebar widgets (like recent post thumbnails).
This caused og:image to reference sidebar OG images from OTHER posts
instead of falling through to the __OG_IMAGE_PLACEHOLDER__ that the
og-fix transform resolves from outputPath.
Only ogPhoto and image (from frontmatter) are now used as explicit
image sources. All other cases use the placeholder resolved by the
og-fix transform.
The Eleventy 3.x parallel rendering race condition (#3183) makes
page.url unreliable in templates — it changes between lines during
concurrent processing. All previous approaches (eleventyComputed,
capturing page.url early with {% set %}) failed because the page
object is shared and mutated by parallel renders.
The transform approach works because outputPath is passed as a
function parameter (not read from a shared object) and IS correct
since files are written to the right location. The transform:
- Derives the OG slug from outputPath pattern matching
- Replaces __OG_IMAGE_PLACEHOLDER__ with the correct OG image URL
- Replaces __TWITTER_CARD_PLACEHOLDER__ with the correct card type
- Fixes og:url and canonical URL from outputPath
Generate index.md alongside index.html for /articles/ at build time.
Agents can access clean Markdown via .md URL extension or Accept:
text/markdown content negotiation. Includes configurable content-signal
policy (ai-train, search, ai-input) and a master on/off toggle via
MARKDOWN_AGENTS_ENABLED env var.
Pages with permalink:false (like about.njk) have page.url as false,
which crashes inline string operations. Use the ogSlug filter with
(page.url or "") guard to handle falsy values safely. Also removes
debug comment from previous debugging session.
permalink is set by eleventyComputed which cross-contaminates return
values across pages during Eleventy 3.x parallel rendering. page.url
is set by Eleventy's internal pipeline and is correct in templates
(verified via og:url meta tag which always shows the right URL).
Both page.url AND page.inputPath are unreliable in eleventyComputed due to
Eleventy 3.x parallel rendering (issue #3183). They return values from OTHER
pages being processed concurrently, causing og:image meta tags to reference
wrong OG images.
Fix: compute ogSlug directly in base.njk from the permalink data value using
existing Nunjucks filters (ogSlug, hasOgImage). permalink comes from frontmatter
(per-file data) and is immune to cross-page contamination.
Add color-coded left borders to post cards on all blog listing and
category pages, and make sidebar widgets collapsible with localStorage
persistence on both listing and single-post sidebars.
The widget used bare 'recentPosts' variable which doesn't exist in
the homepage context (it's a collection). Add fallback to
collections.recentPosts so the widget works on all pages.
site.url had a trailing slash (added for Mastodon rel=me verification),
which caused double slashes in all URL constructions like
{{ site.url }}/auth → https://rmendes.net//auth
This broke IndieAuth login — indielogin.com read the authorization_endpoint
link tag with //auth and redirected users there, which 404'd in nginx.
Split into site.url (no slash, for URL construction) and site.me /
site.author.url (with slash, for Mastodon rel=me strict matching).
Also fixed twitter:image meta tags to use smart slash logic matching
the og:image pattern (check if path starts with / before prepending one).
The homepage uses cv-projects-personal.njk, not cv-projects.njk.
Apply the same collapsible accordion pattern to both personal
and work project section variants.
Sidebar widgets are now wrapped in a collapsible Alpine.js container
with a title + chevron toggle. First 3 widgets open by default, rest
collapsed. State persists in localStorage across page loads. Inner
widget titles hidden by CSS to avoid duplication with wrapper titles.
Project cards now show a compact summary row (name, status badge, date
range, chevron) that expands on click to reveal description and tech
tags. Uses Alpine.js with independent toggles and smooth transitions.
Each post card in the recent-posts section now has a 3px left border
colored by post type: red for likes, amber for bookmarks, green for
reposts, blue for replies, purple for photos, neutral for articles/notes.
Reuses the existing fediverseInteract Alpine.js component to let
visitors follow the site author from their own fediverse instance.
Registered in all three sidebar routers (homepage, blog listing,
blog post) as widget type "fediverse-follow".
- Add computed permalink in data cascade for existing posts without
frontmatter permalink (converts file path to Indiekit URL)
- Fix ogSlug filter and computed data for new 5-segment URL structure
- Add conversations API as build-time data source
- Merge conversations + webmentions in webmentionsForUrl filter with
deduplication and legacy /content/ URL alias computation
- Sidebar widget fetches from both webmention-io and conversations APIs
- Update webmention-debug page with conversationMentions parameter
Pass the self-hosted ActivityPub syndication URL to the modal instead
of the Eleventy HTML page URL. Mastodon's authorize_interaction needs
a resolvable AP URI, not the HTML page which lacks an activity+json
alternate link.
Replace @click.outside on modal panel with @click.stop — the backdrop
already handles closing. @click.outside fires from the same click event
that opens the modal via x-if, immediately setting showModal back to false.
nodes.fediverse.party doesn't send CORS headers, so the fetch fails
from the browser. Remove autocomplete entirely — users type their
instance once and localStorage remembers it.
Add a Fediverse button to the "Also on" footer for posts syndicated via
self-hosted ActivityPub. Clicking it redirects users to their own instance
via authorize_interaction so they can like/boost/reply natively. Instance
is stored in localStorage for repeat visits, with a modal for first-time
entry and Shift+click to change.
Also adds branded syndication buttons for LinkedIn and IndieNews, and
replaces the heuristic Mastodon URL detection with exact matching against
the configured MASTODON_INSTANCE.