a11y: comprehensive WCAG 2.1 Level AA accessibility audit

- Add skip-to-main-content link and main content ID target
- Add prefers-reduced-motion media queries for all animations
- Enhance visible focus indicators (2px offset, high-contrast ring)
- Replace ~160 text-surface-500 instances with text-surface-600/dark:text-surface-400
  for 4.5:1+ contrast ratio compliance
- Add aria-hidden="true" to ~30+ decorative SVG icons across sidebars/widgets
- Convert facepile containers from div to semantic ul/li with role="list"
- Add aria-label to icon-only buttons (share, sort controls)
- Add sr-only labels to form inputs (webmention, search)
- Add aria-live="polite" to dynamically loaded webmentions
- Add aria-label with relative+absolute date to time-difference component
- Add keyboard handlers (Enter/Space) to custom interactive elements
- Add aria-label to nav landmarks (table of contents)
- Fix modal focus trap and dialog accessibility
- Fix lightbox keyboard navigation and screen reader announcements

Confab-Link: http://localhost:8080/sessions/edb1b7b0-da66-4486-bd9c-d1cfa7553b88
This commit is contained in:
Ricardo
2026-03-07 18:58:08 +01:00
parent db75bd05ea
commit e236b4bf65
77 changed files with 638 additions and 505 deletions

View File

@@ -46,23 +46,23 @@
{% elif widget.type == "fediverse-follow" %}
{% set widgetIcon = "user-plus" %}{% set widgetIconClass = "w-5 h-5 text-[#a730b8]" %}{% set widgetBorder = "border-l-[3px] border-l-[#a730b8]" %}
{% elif widget.type == "author-card" or widget.type == "author-card-compact" %}
{% set widgetIcon = "user" %}{% set widgetIconClass = "w-5 h-5 text-surface-500" %}{% set widgetBorder = "" %}
{% set widgetIcon = "user" %}{% set widgetIconClass = "w-5 h-5 text-surface-600 dark:text-surface-400" %}{% set widgetBorder = "" %}
{% elif widget.type == "recent-posts" %}
{% set widgetIcon = "list" %}{% set widgetIconClass = "w-5 h-5 text-surface-500" %}{% set widgetBorder = "" %}
{% set widgetIcon = "list" %}{% set widgetIconClass = "w-5 h-5 text-surface-600 dark:text-surface-400" %}{% set widgetBorder = "" %}
{% elif widget.type == "categories" or widget.type == "post-categories" %}
{% set widgetIcon = "tag" %}{% set widgetIconClass = "w-5 h-5 text-surface-500" %}{% set widgetBorder = "" %}
{% set widgetIcon = "tag" %}{% set widgetIconClass = "w-5 h-5 text-surface-600 dark:text-surface-400" %}{% set widgetBorder = "" %}
{% elif widget.type == "recent-comments" %}
{% set widgetIcon = "chat" %}{% set widgetIconClass = "w-5 h-5 text-surface-500" %}{% set widgetBorder = "" %}
{% set widgetIcon = "chat" %}{% set widgetIconClass = "w-5 h-5 text-surface-600 dark:text-surface-400" %}{% set widgetBorder = "" %}
{% elif widget.type == "search" %}
{% set widgetIcon = "search" %}{% set widgetIconClass = "w-5 h-5 text-surface-500" %}{% set widgetBorder = "" %}
{% set widgetIcon = "search" %}{% set widgetIconClass = "w-5 h-5 text-surface-600 dark:text-surface-400" %}{% set widgetBorder = "" %}
{% elif widget.type == "webmentions" %}
{% set widgetIcon = "share" %}{% set widgetIconClass = "w-5 h-5 text-surface-500" %}{% set widgetBorder = "" %}
{% set widgetIcon = "share" %}{% set widgetIconClass = "w-5 h-5 text-surface-600 dark:text-surface-400" %}{% set widgetBorder = "" %}
{% elif widget.type == "ai-usage" %}
{% set widgetIcon = "zap" %}{% set widgetIconClass = "w-5 h-5 text-amber-500" %}{% set widgetBorder = "border-l-[3px] border-l-amber-400 dark:border-l-amber-500" %}
{% elif widget.type == "toc" %}
{% set widgetIcon = "list" %}{% set widgetIconClass = "w-5 h-5 text-surface-500" %}{% set widgetBorder = "" %}
{% set widgetIcon = "list" %}{% set widgetIconClass = "w-5 h-5 text-surface-600 dark:text-surface-400" %}{% set widgetBorder = "" %}
{% elif widget.type == "share" %}
{% set widgetIcon = "share" %}{% set widgetIconClass = "w-5 h-5 text-surface-500" %}{% set widgetBorder = "" %}
{% set widgetIcon = "share" %}{% set widgetIconClass = "w-5 h-5 text-surface-600 dark:text-surface-400" %}{% set widgetBorder = "" %}
{% else %}
{% set widgetIcon = "" %}{% set widgetIconClass = "" %}{% set widgetBorder = "" %}
{% endif %}
@@ -89,6 +89,7 @@
class="widget-chevron"
:class="open && 'rotate-180'"
fill="none" stroke="currentColor" viewBox="0 0 24 24"
aria-hidden="true"
>
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"/>
</svg>
@@ -169,8 +170,8 @@
<div class="widget-collapsible mb-4" x-data="{ open: localStorage.getItem('{{ widgetKey }}') !== null ? localStorage.getItem('{{ widgetKey }}') === 'true' : true }">
<div class="bg-surface-50 dark:bg-surface-800 rounded-lg border border-surface-200 dark:border-surface-700 shadow-sm overflow-hidden">
<button class="widget-header w-full p-4" @click="open = !open; localStorage.setItem('{{ widgetKey }}', open)" :aria-expanded="open ? 'true' : 'false'">
<h3 class="widget-title font-bold text-lg flex items-center gap-2">{{ icon("user", "w-5 h-5 text-surface-500") }} Author</h3>
<svg class="widget-chevron" :class="open && 'rotate-180'" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"/></svg>
<h3 class="widget-title font-bold text-lg flex items-center gap-2">{{ icon("user", "w-5 h-5 text-surface-600 dark:text-surface-400") }} Author</h3>
<svg class="widget-chevron" :class="open && 'rotate-180'" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"/></svg>
</button>
<div x-show="open" x-transition:enter="transition ease-out duration-150" x-transition:enter-start="opacity-0" x-transition:enter-end="opacity-100" x-transition:leave="transition ease-in duration-100" x-transition:leave-start="opacity-100" x-transition:leave-end="opacity-0" x-cloak>
{% include "components/widgets/author-card-compact.njk" %}
@@ -183,8 +184,8 @@
<div class="widget-collapsible mb-4" x-data="{ open: localStorage.getItem('{{ widgetKey }}') !== null ? localStorage.getItem('{{ widgetKey }}') === 'true' : true }">
<div class="bg-surface-50 dark:bg-surface-800 rounded-lg border border-surface-200 dark:border-surface-700 shadow-sm overflow-hidden">
<button class="widget-header w-full p-4" @click="open = !open; localStorage.setItem('{{ widgetKey }}', open)" :aria-expanded="open ? 'true' : 'false'">
<h3 class="widget-title font-bold text-lg flex items-center gap-2">{{ icon("list", "w-5 h-5 text-surface-500") }} Table of Contents</h3>
<svg class="widget-chevron" :class="open && 'rotate-180'" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"/></svg>
<h3 class="widget-title font-bold text-lg flex items-center gap-2">{{ icon("list", "w-5 h-5 text-surface-600 dark:text-surface-400") }} Table of Contents</h3>
<svg class="widget-chevron" :class="open && 'rotate-180'" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"/></svg>
</button>
<div x-show="open" x-transition:enter="transition ease-out duration-150" x-transition:enter-start="opacity-0" x-transition:enter-end="opacity-100" x-transition:leave="transition ease-in duration-100" x-transition:leave-start="opacity-100" x-transition:leave-end="opacity-0" x-cloak>
{% include "components/widgets/toc.njk" %}
@@ -197,8 +198,8 @@
<div class="widget-collapsible mb-4" x-data="{ open: localStorage.getItem('{{ widgetKey }}') !== null ? localStorage.getItem('{{ widgetKey }}') === 'true' : true }">
<div class="bg-surface-50 dark:bg-surface-800 rounded-lg border border-surface-200 dark:border-surface-700 shadow-sm overflow-hidden">
<button class="widget-header w-full p-4" @click="open = !open; localStorage.setItem('{{ widgetKey }}', open)" :aria-expanded="open ? 'true' : 'false'">
<h3 class="widget-title font-bold text-lg flex items-center gap-2">{{ icon("tag", "w-5 h-5 text-surface-500") }} Categories</h3>
<svg class="widget-chevron" :class="open && 'rotate-180'" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"/></svg>
<h3 class="widget-title font-bold text-lg flex items-center gap-2">{{ icon("tag", "w-5 h-5 text-surface-600 dark:text-surface-400") }} Categories</h3>
<svg class="widget-chevron" :class="open && 'rotate-180'" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"/></svg>
</button>
<div x-show="open" x-transition:enter="transition ease-out duration-150" x-transition:enter-start="opacity-0" x-transition:enter-end="opacity-100" x-transition:leave="transition ease-in duration-100" x-transition:leave-start="opacity-100" x-transition:leave-end="opacity-0" x-cloak>
{% include "components/widgets/post-categories.njk" %}
@@ -211,8 +212,8 @@
<div class="widget-collapsible mb-4" x-data="{ open: localStorage.getItem('{{ widgetKey }}') !== null ? localStorage.getItem('{{ widgetKey }}') === 'true' : false }">
<div class="bg-surface-50 dark:bg-surface-800 rounded-lg border border-surface-200 dark:border-surface-700 shadow-sm overflow-hidden">
<button class="widget-header w-full p-4" @click="open = !open; localStorage.setItem('{{ widgetKey }}', open)" :aria-expanded="open ? 'true' : 'false'">
<h3 class="widget-title font-bold text-lg flex items-center gap-2">{{ icon("list", "w-5 h-5 text-surface-500") }} Recent Posts</h3>
<svg class="widget-chevron" :class="open && 'rotate-180'" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"/></svg>
<h3 class="widget-title font-bold text-lg flex items-center gap-2">{{ icon("list", "w-5 h-5 text-surface-600 dark:text-surface-400") }} Recent Posts</h3>
<svg class="widget-chevron" :class="open && 'rotate-180'" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"/></svg>
</button>
<div x-show="open" x-transition:enter="transition ease-out duration-150" x-transition:enter-start="opacity-0" x-transition:enter-end="opacity-100" x-transition:leave="transition ease-in duration-100" x-transition:leave-start="opacity-100" x-transition:leave-end="opacity-0" x-cloak>
{% include "components/widgets/recent-posts-blog.njk" %}
@@ -225,8 +226,8 @@
<div class="widget-collapsible mb-4" x-data="{ open: localStorage.getItem('{{ widgetKey }}') !== null ? localStorage.getItem('{{ widgetKey }}') === 'true' : false }">
<div class="bg-surface-50 dark:bg-surface-800 rounded-lg border border-surface-200 dark:border-surface-700 shadow-sm overflow-hidden">
<button class="widget-header w-full p-4" @click="open = !open; localStorage.setItem('{{ widgetKey }}', open)" :aria-expanded="open ? 'true' : 'false'">
<h3 class="widget-title font-bold text-lg flex items-center gap-2">{{ icon("share", "w-5 h-5 text-surface-500") }} Webmentions</h3>
<svg class="widget-chevron" :class="open && 'rotate-180'" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"/></svg>
<h3 class="widget-title font-bold text-lg flex items-center gap-2">{{ icon("share", "w-5 h-5 text-surface-600 dark:text-surface-400") }} Webmentions</h3>
<svg class="widget-chevron" :class="open && 'rotate-180'" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"/></svg>
</button>
<div x-show="open" x-transition:enter="transition ease-out duration-150" x-transition:enter-start="opacity-0" x-transition:enter-end="opacity-100" x-transition:leave="transition ease-in duration-100" x-transition:leave-start="opacity-100" x-transition:leave-end="opacity-0" x-cloak>
{% include "components/widgets/webmentions.njk" %}
@@ -239,8 +240,8 @@
<div class="widget-collapsible mb-4" x-data="{ open: localStorage.getItem('{{ widgetKey }}') !== null ? localStorage.getItem('{{ widgetKey }}') === 'true' : false }">
<div class="bg-surface-50 dark:bg-surface-800 rounded-lg border border-surface-200 dark:border-surface-700 shadow-sm overflow-hidden">
<button class="widget-header w-full p-4" @click="open = !open; localStorage.setItem('{{ widgetKey }}', open)" :aria-expanded="open ? 'true' : 'false'">
<h3 class="widget-title font-bold text-lg flex items-center gap-2">{{ icon("share", "w-5 h-5 text-surface-500") }} Share</h3>
<svg class="widget-chevron" :class="open && 'rotate-180'" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"/></svg>
<h3 class="widget-title font-bold text-lg flex items-center gap-2">{{ icon("share", "w-5 h-5 text-surface-600 dark:text-surface-400") }} Share</h3>
<svg class="widget-chevron" :class="open && 'rotate-180'" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"/></svg>
</button>
<div x-show="open" x-transition:enter="transition ease-out duration-150" x-transition:enter-start="opacity-0" x-transition:enter-end="opacity-100" x-transition:leave="transition ease-in duration-100" x-transition:leave-start="opacity-100" x-transition:leave-end="opacity-0" x-cloak>
{% include "components/widgets/share.njk" %}
@@ -254,7 +255,7 @@
<div class="bg-surface-50 dark:bg-surface-800 rounded-lg border border-surface-200 dark:border-surface-700 shadow-sm overflow-hidden border-l-[3px] border-l-orange-400 dark:border-l-orange-500">
<button class="widget-header w-full p-4" @click="open = !open; localStorage.setItem('{{ widgetKey }}', open)" :aria-expanded="open ? 'true' : 'false'">
<h3 class="widget-title font-bold text-lg flex items-center gap-2">{{ icon("rss", "w-5 h-5 text-orange-500") }} Subscribe</h3>
<svg class="widget-chevron" :class="open && 'rotate-180'" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"/></svg>
<svg class="widget-chevron" :class="open && 'rotate-180'" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"/></svg>
</button>
<div x-show="open" x-transition:enter="transition ease-out duration-150" x-transition:enter-start="opacity-0" x-transition:enter-end="opacity-100" x-transition:leave="transition ease-in duration-100" x-transition:leave-start="opacity-100" x-transition:leave-end="opacity-0" x-cloak>
{% include "components/widgets/subscribe.njk" %}
@@ -267,8 +268,8 @@
<div class="widget-collapsible mb-4" x-data="{ open: localStorage.getItem('{{ widgetKey }}') !== null ? localStorage.getItem('{{ widgetKey }}') === 'true' : false }">
<div class="bg-surface-50 dark:bg-surface-800 rounded-lg border border-surface-200 dark:border-surface-700 shadow-sm overflow-hidden">
<button class="widget-header w-full p-4" @click="open = !open; localStorage.setItem('{{ widgetKey }}', open)" :aria-expanded="open ? 'true' : 'false'">
<h3 class="widget-title font-bold text-lg flex items-center gap-2">{{ icon("chat", "w-5 h-5 text-surface-500") }} Recent Comments</h3>
<svg class="widget-chevron" :class="open && 'rotate-180'" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"/></svg>
<h3 class="widget-title font-bold text-lg flex items-center gap-2">{{ icon("chat", "w-5 h-5 text-surface-600 dark:text-surface-400") }} Recent Comments</h3>
<svg class="widget-chevron" :class="open && 'rotate-180'" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"/></svg>
</button>
<div x-show="open" x-transition:enter="transition ease-out duration-150" x-transition:enter-start="opacity-0" x-transition:enter-end="opacity-100" x-transition:leave="transition ease-in duration-100" x-transition:leave-start="opacity-100" x-transition:leave-end="opacity-0" x-cloak>
{% include "components/widgets/recent-comments.njk" %}

View File

@@ -21,6 +21,7 @@
<div class="mt-4">
{# Status messages #}
<div x-show="statusMessage" x-cloak
role="alert"
x-bind:class="statusType === 'error' ? 'bg-red-50 text-red-700 dark:bg-red-900/20 dark:text-red-400' :
statusType === 'success' ? 'bg-green-50 text-green-700 dark:bg-green-900/20 dark:text-green-400' :
'bg-blue-50 text-blue-700 dark:bg-blue-900/20 dark:text-blue-400'"
@@ -54,12 +55,13 @@
</div>
<form x-on:submit.prevent="submitComment()">
<textarea x-model="commentText" rows="4" required
<label for="comment-text" class="sr-only">Your comment</label>
<textarea id="comment-text" x-model="commentText" rows="4" required
placeholder="Share your thoughts... (supports **bold**, *italic*, and [links](url))"
class="w-full px-3 py-2 border rounded-lg mb-2 dark:bg-surface-800 dark:border-surface-700 dark:text-surface-100"
x-bind:maxlength="maxLength"></textarea>
<div class="flex items-center justify-between">
<span class="text-xs text-surface-500" x-text="commentText.length + '/' + maxLength"></span>
<span class="text-xs text-surface-600 dark:text-surface-400" x-text="commentText.length + '/' + maxLength"></span>
<button type="submit" class="button" x-bind:disabled="submitting">
<span x-show="!submitting">Post Comment</span>
<span x-show="submitting" x-cloak>Posting...</span>
@@ -71,7 +73,7 @@
{# Comment list #}
<div class="mt-6 space-y-4">
<template x-if="loading">
<p class="text-sm text-surface-500">Loading comments...</p>
<p class="text-sm text-surface-600 dark:text-surface-400">Loading comments...</p>
</template>
<template x-for="comment in comments" x-bind:key="comment.published">
@@ -90,7 +92,7 @@
<div class="flex items-center gap-2">
<a x-bind:href="comment.author?.url" class="font-medium text-sm hover:underline" target="_blank" rel="noopener"
x-text="comment.author?.name || comment.author?.url"></a>
<time class="text-xs text-surface-500 font-mono" x-bind:datetime="comment.published"
<time class="text-xs text-surface-600 dark:text-surface-400 font-mono" x-bind:datetime="comment.published"
x-text="new Date(comment.published).toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' })"></time>
</div>
<div class="mt-1 text-sm prose dark:prose-invert" x-html="comment.content?.html || comment.content?.text"></div>
@@ -100,7 +102,7 @@
</template>
<template x-if="!loading && comments.length === 0">
<p class="text-sm text-surface-500">No comments yet. Be the first to share your thoughts!</p>
<p class="text-sm text-surface-600 dark:text-surface-400">No comments yet. Be the first to share your thoughts!</p>
</template>
</div>
</div>

View File

@@ -74,7 +74,7 @@
{% endif %}
{# Contact details — location, organization, website, email, PGP #}
{% if cvLocality or cvCountry or cvOrg or cvUrl or cvEmail or cvKeyUrl %}
<div class="flex flex-wrap gap-x-4 gap-y-1 mt-4 text-sm text-surface-500 dark:text-surface-400">
<div class="flex flex-wrap gap-x-4 gap-y-1 mt-4 text-sm text-surface-600 dark:text-surface-400">
{% if cvLocality or cvCountry %}
<span>{% if cvLocality %}{{ cvLocality }}{% endif %}{% if cvLocality and cvCountry %}, {% endif %}{% if cvCountry %}{{ cvCountry }}{% endif %}</span>
{% endif %}
@@ -160,7 +160,7 @@
{# Last Updated #}
{% if cv.lastUpdated %}
<p class="text-sm text-surface-500 text-center mt-8">
<p class="text-sm text-surface-600 dark:text-surface-400 text-center mt-8">
Last updated: <time class="font-mono text-sm" datetime="{{ cv.lastUpdated }}">{{ cv.lastUpdated | date("PPP") }}</time>
</p>
{% endif %}

View File

@@ -12,7 +12,7 @@
</svg>
</div>
<h2 class="text-lg font-semibold text-surface-700 dark:text-surface-300 mb-2">No {{ title | lower }} yet</h2>
<p class="text-surface-500 dark:text-surface-400 mb-6 max-w-md mx-auto">
<p class="text-surface-600 dark:text-surface-400 mb-6 max-w-md mx-auto">
This is where your {{ title | lower }} will appear once you start creating content.
</p>
{% if typeInfo %}

View File

@@ -14,6 +14,7 @@
@click="showModal = false"></div>
{# Panel #}
<div class="relative bg-surface-50 dark:bg-surface-800 rounded-xl shadow-xl w-full max-w-sm p-6"
role="dialog" aria-modal="true" aria-labelledby="fediverse-modal-title"
x-transition:enter="transition ease-out duration-200"
x-transition:enter-start="opacity-0 scale-95"
x-transition:enter-end="opacity-100 scale-100"
@@ -21,8 +22,8 @@
x-transition:leave-start="opacity-100 scale-100"
x-transition:leave-end="opacity-0 scale-95"
@click.stop>
<h3 class="text-lg font-semibold text-surface-900 dark:text-surface-100 mb-1">{{ modalTitle }}</h3>
<p class="text-sm text-surface-500 dark:text-surface-400 mb-4">{{ modalDescription }}</p>
<h3 id="fediverse-modal-title" class="text-lg font-semibold text-surface-900 dark:text-surface-100 mb-1">{{ modalTitle }}</h3>
<p class="text-sm text-surface-600 dark:text-surface-400 mb-4">{{ modalDescription }}</p>
{# Saved domains list #}
<template x-if="savedDomains.length > 0 && !showInput">
@@ -35,8 +36,8 @@
x-text="item.domain"></button>
<button class="px-2 py-2.5 text-surface-400 hover:text-red-500 transition-colors cursor-pointer"
@click="deleteSaved(item.domain)"
title="Remove">
<svg class="w-4 h-4" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/></svg>
:aria-label="'Remove ' + item.domain">
<svg class="w-4 h-4" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/></svg>
</button>
</div>
</template>
@@ -55,14 +56,16 @@
{# New domain input #}
<template x-if="savedDomains.length === 0 || showInput">
<div>
<input x-ref="instanceInput"
<label for="fediverse-instance-input" class="sr-only">Fediverse instance domain</label>
<input id="fediverse-instance-input"
x-ref="instanceInput"
x-model="instance"
@keydown.enter.prevent="confirm()"
type="text"
placeholder="mastodon.social"
class="w-full px-3 py-2 border border-surface-300 dark:border-surface-600 rounded-lg bg-surface-50 dark:bg-surface-800 text-surface-900 dark:text-surface-100 placeholder-surface-400 text-sm">
<template x-if="error">
<p class="text-xs text-red-500 mt-1" x-text="error"></p>
<p class="text-xs text-red-500 mt-1" x-text="error" role="alert"></p>
</template>
<div class="flex gap-3 mt-4">
<button @click="showInput ? (showInput = false) : (showModal = false)"

View File

@@ -3,19 +3,19 @@
<div class="grid grid-cols-2 sm:grid-cols-4 gap-3 sm:gap-4 mb-6 sm:mb-8">
<div class="p-4 bg-surface-50 dark:bg-surface-800 rounded-lg border border-surface-200 dark:border-surface-700 shadow-sm text-center">
<span class="text-2xl font-bold font-mono text-purple-600 dark:text-purple-400 block">{{ summary.totalPlays or 0 }}</span>
<span class="text-xs text-surface-500 uppercase tracking-wide">Plays</span>
<span class="text-xs text-surface-600 dark:text-surface-400 uppercase tracking-wide">Plays</span>
</div>
<div class="p-4 bg-surface-50 dark:bg-surface-800 rounded-lg border border-surface-200 dark:border-surface-700 shadow-sm text-center">
<span class="text-2xl font-bold font-mono text-purple-600 dark:text-purple-400 block">{{ summary.uniqueTracks or 0 }}</span>
<span class="text-xs text-surface-500 uppercase tracking-wide">Tracks</span>
<span class="text-xs text-surface-600 dark:text-surface-400 uppercase tracking-wide">Tracks</span>
</div>
<div class="p-4 bg-surface-50 dark:bg-surface-800 rounded-lg border border-surface-200 dark:border-surface-700 shadow-sm text-center">
<span class="text-2xl font-bold font-mono text-purple-600 dark:text-purple-400 block">{{ summary.uniqueArtists or 0 }}</span>
<span class="text-xs text-surface-500 uppercase tracking-wide">Artists</span>
<span class="text-xs text-surface-600 dark:text-surface-400 uppercase tracking-wide">Artists</span>
</div>
<div class="p-4 bg-surface-50 dark:bg-surface-800 rounded-lg border border-surface-200 dark:border-surface-700 shadow-sm text-center">
<span class="text-2xl font-bold font-mono text-purple-600 dark:text-purple-400 block">{{ summary.totalDurationFormatted or '0m' }}</span>
<span class="text-xs text-surface-500 uppercase tracking-wide">Listened</span>
<span class="text-xs text-surface-600 dark:text-surface-400 uppercase tracking-wide">Listened</span>
</div>
</div>
{% endif %}
@@ -29,7 +29,7 @@
<div class="flex items-center gap-3 p-3 bg-surface-50 dark:bg-surface-800 rounded-lg border border-surface-200 dark:border-surface-700 shadow-sm">
<span class="w-6 h-6 flex items-center justify-center text-sm font-bold text-surface-400 bg-surface-100 dark:bg-surface-700 rounded-full">{{ loop.index }}</span>
<span class="flex-1 font-medium text-surface-900 dark:text-surface-100">{{ artist.name }}</span>
<span class="text-sm text-surface-500">{{ artist.playCount }} plays</span>
<span class="text-sm text-surface-600 dark:text-surface-400">{{ artist.playCount }} plays</span>
</div>
{% endfor %}
</div>
@@ -53,7 +53,7 @@
</div>
{% endif %}
<p class="text-sm font-medium text-surface-900 dark:text-surface-100 truncate">{{ album.title }}</p>
<p class="text-xs text-surface-500 truncate">{{ album.artist }}</p>
<p class="text-xs text-surface-600 dark:text-surface-400 truncate">{{ album.artist }}</p>
<p class="text-xs text-surface-400">{{ album.playCount }} plays</p>
</div>
{% endfor %}

View File

@@ -41,11 +41,11 @@
{{ authorName }}
</a>
{% if authorPronoun %}
<span class="p-pronoun text-xs text-surface-500">({{ authorPronoun }})</span>
<span class="p-pronoun text-xs text-surface-600 dark:text-surface-400">({{ authorPronoun }})</span>
{% endif %}
<p class="p-job-title text-sm text-surface-600 dark:text-surface-400" itemprop="jobTitle">{{ authorTitle }}</p>
{# Structured address #}
<p class="p-adr h-adr text-sm text-surface-500 dark:text-surface-500" itemprop="address" itemscope itemtype="http://schema.org/PostalAddress">
<p class="p-adr h-adr text-sm text-surface-600 dark:text-surface-400" itemprop="address" itemscope itemtype="http://schema.org/PostalAddress">
{% if authorLocality %}
<span class="p-locality" itemprop="addressLocality">{{ authorLocality }}</span>{% if authorCountry %}, {% endif %}
{% endif %}
@@ -79,7 +79,7 @@
</a>
{% endif %}
{% if authorKeyUrl %}
<a href="{{ authorKeyUrl }}" class="u-key text-surface-500 dark:text-surface-400 hover:underline" rel="pgpkey">
<a href="{{ authorKeyUrl }}" class="u-key text-surface-600 dark:text-surface-400 hover:underline" rel="pgpkey">
🔐 PGP Key
</a>
{% endif %}
@@ -87,11 +87,11 @@
{# Categories / Skills #}
{% if authorCategories and authorCategories.length %}
<div class="mt-3 flex flex-wrap gap-1">
<ul class="mt-3 flex flex-wrap gap-1 list-none p-0 m-0" role="list" aria-label="Skills and interests">
{% for category in authorCategories %}
<span class="p-category text-xs px-2 py-0.5 bg-surface-100 dark:bg-surface-800 rounded-full">{{ category }}</span>
<li class="p-category text-xs px-2 py-0.5 bg-surface-100 dark:bg-surface-800 rounded-full">{{ category }}</li>
{% endfor %}
</div>
</ul>
{% endif %}
{# Social links with rel="me" - critical for IndieWeb identity verification #}
@@ -102,8 +102,8 @@
<a
href="{{ link.url }}"
rel="{{ link.rel }} noopener"
class="u-url text-surface-500 hover:text-accent-600 dark:hover:text-accent-400 transition-colors"
aria-label="{{ link.name }}"
class="u-url text-surface-600 dark:text-surface-400 hover:text-accent-600 dark:hover:text-accent-400 transition-colors"
aria-label="{{ link.name }} (opens in new tab)"
target="_blank">
{{ socialIcon(link.icon, "w-5 h-5") }}
</a>

View File

@@ -39,17 +39,17 @@
{% elif widget.type == "fediverse-follow" %}
{% set widgetIcon = "user-plus" %}{% set widgetIconClass = "w-5 h-5 text-[#a730b8]" %}{% set widgetBorder = "border-l-[3px] border-l-[#a730b8]" %}
{% elif widget.type == "author-card" %}
{% set widgetIcon = "user" %}{% set widgetIconClass = "w-5 h-5 text-surface-500" %}{% set widgetBorder = "" %}
{% set widgetIcon = "user" %}{% set widgetIconClass = "w-5 h-5 text-surface-600 dark:text-surface-400" %}{% set widgetBorder = "" %}
{% elif widget.type == "recent-posts" %}
{% set widgetIcon = "list" %}{% set widgetIconClass = "w-5 h-5 text-surface-500" %}{% set widgetBorder = "" %}
{% set widgetIcon = "list" %}{% set widgetIconClass = "w-5 h-5 text-surface-600 dark:text-surface-400" %}{% set widgetBorder = "" %}
{% elif widget.type == "categories" %}
{% set widgetIcon = "tag" %}{% set widgetIconClass = "w-5 h-5 text-surface-500" %}{% set widgetBorder = "" %}
{% set widgetIcon = "tag" %}{% set widgetIconClass = "w-5 h-5 text-surface-600 dark:text-surface-400" %}{% set widgetBorder = "" %}
{% elif widget.type == "recent-comments" %}
{% set widgetIcon = "chat" %}{% set widgetIconClass = "w-5 h-5 text-surface-500" %}{% set widgetBorder = "" %}
{% set widgetIcon = "chat" %}{% set widgetIconClass = "w-5 h-5 text-surface-600 dark:text-surface-400" %}{% set widgetBorder = "" %}
{% elif widget.type == "search" %}
{% set widgetIcon = "search" %}{% set widgetIconClass = "w-5 h-5 text-surface-500" %}{% set widgetBorder = "" %}
{% set widgetIcon = "search" %}{% set widgetIconClass = "w-5 h-5 text-surface-600 dark:text-surface-400" %}{% set widgetBorder = "" %}
{% elif widget.type == "webmentions" %}
{% set widgetIcon = "share" %}{% set widgetIconClass = "w-5 h-5 text-surface-500" %}{% set widgetBorder = "" %}
{% set widgetIcon = "share" %}{% set widgetIconClass = "w-5 h-5 text-surface-600 dark:text-surface-400" %}{% set widgetBorder = "" %}
{% elif widget.type == "ai-usage" %}
{% set widgetIcon = "zap" %}{% set widgetIconClass = "w-5 h-5 text-amber-500" %}{% set widgetBorder = "border-l-[3px] border-l-amber-400 dark:border-l-amber-500" %}
{% else %}
@@ -78,6 +78,7 @@
class="widget-chevron"
:class="open && 'rotate-180'"
fill="none" stroke="currentColor" viewBox="0 0 24 24"
aria-hidden="true"
>
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"/>
</svg>

View File

@@ -39,17 +39,17 @@
</span>
{% else %}
<div class="p-4 sm:p-5">
<span class="text-[10px] sm:text-xs font-semibold uppercase tracking-wide text-surface-500 block mb-2">&larr; Previous</span>
<span class="text-[10px] sm:text-xs font-semibold uppercase tracking-wide text-surface-600 dark:text-surface-400 block mb-2">&larr; Previous</span>
<span class="text-sm sm:text-base font-medium text-surface-900 dark:text-surface-100 group-hover:text-accent-600 dark:group-hover:text-accent-400 line-clamp-2 transition-colors">
{{ _prevTitle }}
</span>
<time class="text-xs text-surface-500 mt-1 block font-mono" datetime="{{ _prevPost.date | isoDate }}">{{ _prevPost.date | dateDisplay }}</time>
<time class="text-xs text-surface-600 dark:text-surface-400 mt-1 block font-mono" datetime="{{ _prevPost.date | isoDate }}">{{ _prevPost.date | dateDisplay }}</time>
</div>
{% endif %}
</a>
{% else %}
<div class="rounded-lg bg-surface-50 dark:bg-surface-800/50 border border-surface-200/50 dark:border-surface-700/50"></div>
<div class="rounded-lg bg-surface-50 dark:bg-surface-800/50 border border-surface-200/50 dark:border-surface-700/50" aria-hidden="true"></div>
{% endif %}
{# ── Next Post ── #}
@@ -85,17 +85,17 @@
</span>
{% else %}
<div class="p-4 sm:p-5 text-right">
<span class="text-[10px] sm:text-xs font-semibold uppercase tracking-wide text-surface-500 block mb-2">Next &rarr;</span>
<span class="text-[10px] sm:text-xs font-semibold uppercase tracking-wide text-surface-600 dark:text-surface-400 block mb-2">Next &rarr;</span>
<span class="text-sm sm:text-base font-medium text-surface-900 dark:text-surface-100 group-hover:text-accent-600 dark:group-hover:text-accent-400 line-clamp-2 transition-colors">
{{ _nextTitle }}
</span>
<time class="text-xs text-surface-500 mt-1 block font-mono" datetime="{{ _nextPost.date | isoDate }}">{{ _nextPost.date | dateDisplay }}</time>
<time class="text-xs text-surface-600 dark:text-surface-400 mt-1 block font-mono" datetime="{{ _nextPost.date | isoDate }}">{{ _nextPost.date | dateDisplay }}</time>
</div>
{% endif %}
</a>
{% else %}
<div class="rounded-lg bg-surface-50 dark:bg-surface-800/50 border border-surface-200/50 dark:border-surface-700/50"></div>
<div class="rounded-lg bg-surface-50 dark:bg-surface-800/50 border border-surface-200/50 dark:border-surface-700/50" aria-hidden="true"></div>
{% endif %}
</div>

View File

@@ -13,14 +13,14 @@
<aside class="reply-context mb-6">
{% if replyTo %}
<div class="u-in-reply-to h-cite">
<p class="text-sm text-surface-500 dark:text-surface-400 mb-2 flex items-center gap-2">
<p class="text-sm text-surface-600 dark:text-surface-400 mb-2 flex items-center gap-2">
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 10h10a8 8 0 018 8v2M3 10l6 6m-6-6l6-6"/>
</svg>
<span>In reply to:</span>
</p>
{% unfurl replyTo %}
<a class="u-url text-xs text-surface-400 dark:text-surface-500 hover:underline break-all" href="{{ replyTo }}">
<a class="u-url text-xs text-surface-600 dark:text-surface-400 hover:underline break-all" href="{{ replyTo }}">
{{ replyTo }}
</a>
</div>
@@ -28,14 +28,14 @@
{% if likedUrl %}
<div class="u-like-of h-cite">
<p class="text-sm text-surface-500 dark:text-surface-400 mb-2 flex items-center gap-2">
<p class="text-sm text-surface-600 dark:text-surface-400 mb-2 flex items-center gap-2">
<svg class="w-4 h-4 text-red-500" fill="currentColor" viewBox="0 0 24 24" aria-hidden="true">
<path d="M12 21.35l-1.45-1.32C5.4 15.36 2 12.28 2 8.5 2 5.42 4.42 3 7.5 3c1.74 0 3.41.81 4.5 2.09C13.09 3.81 14.76 3 16.5 3 19.58 3 22 5.42 22 8.5c0 3.78-3.4 6.86-8.55 11.54L12 21.35z"/>
</svg>
<span>Liked:</span>
</p>
{% unfurl likedUrl %}
<a class="u-url text-xs text-surface-400 dark:text-surface-500 hover:underline break-all" href="{{ likedUrl }}">
<a class="u-url text-xs text-surface-600 dark:text-surface-400 hover:underline break-all" href="{{ likedUrl }}">
{{ likedUrl }}
</a>
</div>
@@ -43,14 +43,14 @@
{% if repostedUrl %}
<div class="u-repost-of h-cite">
<p class="text-sm text-surface-500 dark:text-surface-400 mb-2 flex items-center gap-2">
<p class="text-sm text-surface-600 dark:text-surface-400 mb-2 flex items-center gap-2">
<svg class="w-4 h-4 text-green-500" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15"/>
</svg>
<span>Reposted:</span>
</p>
{% unfurl repostedUrl %}
<a class="u-url text-xs text-surface-400 dark:text-surface-500 hover:underline break-all" href="{{ repostedUrl }}">
<a class="u-url text-xs text-surface-600 dark:text-surface-400 hover:underline break-all" href="{{ repostedUrl }}">
{{ repostedUrl }}
</a>
</div>
@@ -58,14 +58,14 @@
{% if bookmarkedUrl %}
<div class="u-bookmark-of h-cite">
<p class="text-sm text-surface-500 dark:text-surface-400 mb-2 flex items-center gap-2">
<p class="text-sm text-surface-600 dark:text-surface-400 mb-2 flex items-center gap-2">
<svg class="w-4 h-4 text-yellow-500" fill="currentColor" viewBox="0 0 24 24" aria-hidden="true">
<path d="M17 3H7c-1.1 0-2 .9-2 2v16l7-3 7 3V5c0-1.1-.9-2-2-2z"/>
</svg>
<span>Bookmarked:</span>
</p>
{% unfurl bookmarkedUrl %}
<a class="u-url text-xs text-surface-400 dark:text-surface-500 hover:underline break-all" href="{{ bookmarkedUrl }}">
<a class="u-url text-xs text-surface-600 dark:text-surface-400 hover:underline break-all" href="{{ bookmarkedUrl }}">
{{ bookmarkedUrl }}
</a>
</div>

View File

@@ -15,19 +15,19 @@
<div class="grid gap-4 sm:grid-cols-4 mb-6">
<div class="text-center p-3 rounded-lg bg-surface-50 dark:bg-surface-800 border border-surface-200 dark:border-surface-700 shadow-sm">
<div class="text-2xl font-bold font-mono text-surface-900 dark:text-surface-100">{{ stats.total }}</div>
<div class="text-xs text-surface-500 dark:text-surface-400">Total posts</div>
<div class="text-xs text-surface-600 dark:text-surface-400">Total posts</div>
</div>
<div class="text-center p-3 rounded-lg bg-surface-50 dark:bg-surface-800 border border-surface-200 dark:border-surface-700 shadow-sm">
<div class="text-2xl font-bold font-mono text-amber-600 dark:text-amber-400">{{ stats.aiCount }}</div>
<div class="text-xs text-surface-500 dark:text-surface-400">AI-involved</div>
<div class="text-xs text-surface-600 dark:text-surface-400">AI-involved</div>
</div>
<div class="text-center p-3 rounded-lg bg-surface-50 dark:bg-surface-800 border border-surface-200 dark:border-surface-700 shadow-sm">
<div class="text-2xl font-bold font-mono text-emerald-600 dark:text-emerald-400">{{ stats.total - stats.aiCount }}</div>
<div class="text-xs text-surface-500 dark:text-surface-400">Human-only</div>
<div class="text-xs text-surface-600 dark:text-surface-400">Human-only</div>
</div>
<div class="text-center p-3 rounded-lg bg-surface-50 dark:bg-surface-800 border border-surface-200 dark:border-surface-700 shadow-sm">
<div class="text-2xl font-bold font-mono text-surface-900 dark:text-surface-100">{{ stats.percentage }}%</div>
<div class="text-xs text-surface-500 dark:text-surface-400">AI ratio</div>
<div class="text-xs text-surface-600 dark:text-surface-400">AI ratio</div>
</div>
</div>
@@ -50,7 +50,7 @@
{# Post graph — AI-involved posts highlighted #}
{% if aiPostsList and aiPostsList.length %}
<h3 class="text-lg font-semibold text-surface-900 dark:text-surface-100 mb-3">AI-Involved Posts Over Time</h3>
<p class="text-sm text-surface-500 dark:text-surface-400 mb-4">Highlighted days had posts with AI involvement (level 1+). Empty boxes represent days with no AI-involved posts.</p>
<p class="text-sm text-surface-600 dark:text-surface-400 mb-4">Highlighted days had posts with AI involvement (level 1+). Empty boxes represent days with no AI-involved posts.</p>
{% set graphLimit = sectionConfig.limit or 1 %}
{% postGraph aiPostsList, { prefix: "ai-section", limit: graphLimit, boxColorDark: "#44403c", highlightColorLight: "#d97706", highlightColorDark: "#fbbf24" } %}
{% endif %}

View File

@@ -40,11 +40,11 @@
</div>
<div class="flex items-center gap-2 shrink-0">
{% if item.startDate %}
<span class="text-xs text-surface-500 hidden sm:inline font-mono">
<span class="text-xs text-surface-600 dark:text-surface-400 hidden sm:inline font-mono">
{{ item.startDate }}{% if item.endDate %} {{ item.endDate }}{% else %} Present{% endif %}
</span>
{% elif item.year %}
<span class="text-xs text-surface-500 hidden sm:inline font-mono">{{ item.year }}</span>
<span class="text-xs text-surface-600 dark:text-surface-400 hidden sm:inline font-mono">{{ item.year }}</span>
{% endif %}
<svg
class="w-4 h-4 text-surface-400 transition-transform duration-200"
@@ -69,11 +69,11 @@
class="px-4 pb-4"
>
{% if item.startDate %}
<p class="text-xs text-surface-500 mb-1 sm:hidden font-mono">
<p class="text-xs text-surface-600 dark:text-surface-400 mb-1 sm:hidden font-mono">
{{ item.startDate }}{% if item.endDate %} {{ item.endDate }}{% else %} Present{% endif %}
</p>
{% elif item.year %}
<p class="text-xs text-surface-500 mb-1 sm:hidden font-mono">{{ item.year }}</p>
<p class="text-xs text-surface-600 dark:text-surface-400 mb-1 sm:hidden font-mono">{{ item.year }}</p>
{% endif %}
{% if item.description %}

View File

@@ -24,7 +24,7 @@
{% if item.type %} &middot; <span class="capitalize">{{ item.type }}</span>{% endif %}
</p>
{% if item.startDate %}
<p class="text-xs text-surface-500 mt-0.5 font-mono">
<p class="text-xs text-surface-600 dark:text-surface-400 mt-0.5 font-mono">
{{ item.startDate }}{% if item.endDate %} {{ item.endDate }}{% else %} Present{% endif %}
</p>
{% endif %}

View File

@@ -13,7 +13,7 @@
{% for lang in cv.languages %}
<div class="flex items-center gap-2 px-3 py-1.5 bg-surface-50 dark:bg-surface-800 rounded-full border border-surface-200 dark:border-surface-700">
<span class="font-medium text-sm text-surface-900 dark:text-surface-100">{{ lang.name }}</span>
<span class="text-xs text-surface-500 capitalize">{{ lang.level }}</span>
<span class="text-xs text-surface-600 dark:text-surface-400 capitalize">{{ lang.level }}</span>
</div>
{% endfor %}
</div>

View File

@@ -62,7 +62,7 @@
</div>
<div class="flex items-center gap-2 shrink-0">
{% if item.startDate %}
<span class="text-xs text-surface-500 hidden sm:inline font-mono">
<span class="text-xs text-surface-600 dark:text-surface-400 hidden sm:inline font-mono">
{{ item.startDate }}{% if item.endDate %} {{ item.endDate }}{% else %} Present{% endif %}
</span>
{% endif %}
@@ -89,7 +89,7 @@
class="px-4 pb-4"
>
{% if item.startDate %}
<p class="text-xs text-surface-500 mb-1 sm:hidden font-mono">
<p class="text-xs text-surface-600 dark:text-surface-400 mb-1 sm:hidden font-mono">
{{ item.startDate }}{% if item.endDate %} {{ item.endDate }}{% else %} Present{% endif %}
</p>
{% endif %}

View File

@@ -62,7 +62,7 @@
</div>
<div class="flex items-center gap-2 shrink-0">
{% if item.startDate %}
<span class="text-xs text-surface-500 hidden sm:inline font-mono">
<span class="text-xs text-surface-600 dark:text-surface-400 hidden sm:inline font-mono">
{{ item.startDate }}{% if item.endDate %} {{ item.endDate }}{% else %} Present{% endif %}
</span>
{% endif %}
@@ -89,7 +89,7 @@
class="px-4 pb-4"
>
{% if item.startDate %}
<p class="text-xs text-surface-500 mb-1 sm:hidden font-mono">
<p class="text-xs text-surface-600 dark:text-surface-400 mb-1 sm:hidden font-mono">
{{ item.startDate }}{% if item.endDate %} {{ item.endDate }}{% else %} Present{% endif %}
</p>
{% endif %}

View File

@@ -52,7 +52,7 @@
</div>
<div class="flex items-center gap-2 shrink-0">
{% if item.startDate %}
<span class="text-xs text-surface-500 hidden sm:inline font-mono">
<span class="text-xs text-surface-600 dark:text-surface-400 hidden sm:inline font-mono">
{{ item.startDate }}{% if item.endDate %} {{ item.endDate }}{% else %} Present{% endif %}
</span>
{% endif %}
@@ -79,7 +79,7 @@
class="px-4 pb-4"
>
{% if item.startDate %}
<p class="text-xs text-surface-500 mb-1 sm:hidden font-mono">
<p class="text-xs text-surface-600 dark:text-surface-400 mb-1 sm:hidden font-mono">
{{ item.startDate }}{% if item.endDate %} {{ item.endDate }}{% else %} Present{% endif %}
</p>
{% endif %}

View File

@@ -53,14 +53,14 @@
</svg>
</div>
<div class="flex-1 min-w-0">
<div class="flex items-center gap-3 text-xs text-surface-500">
<div class="flex items-center gap-3 text-xs text-surface-600 dark:text-surface-400">
<span class="font-medium text-red-600 dark:text-red-400">Liked</span>
<time class="dt-published font-mono" datetime="{{ post.date | isoDate }}">
{{ post.date | dateDisplay }}
</time>
</div>
{{ likedUrl | unfurlCard | safe }}
<a class="u-like-of text-xs text-surface-400 dark:text-surface-500 hover:underline break-all mt-1 inline-block" href="{{ likedUrl }}">
<a class="u-like-of text-xs text-surface-400 dark:text-surface-400 hover:underline break-all mt-1 inline-block" href="{{ likedUrl }}">
{{ likedUrl }}
</a>
{% if post.templateContent %}
@@ -81,7 +81,7 @@
</svg>
</div>
<div class="flex-1 min-w-0">
<div class="flex items-center gap-3 text-xs text-surface-500">
<div class="flex items-center gap-3 text-xs text-surface-600 dark:text-surface-400">
<span class="font-medium text-amber-600 dark:text-amber-400">Bookmarked</span>
<time class="dt-published font-mono" datetime="{{ post.date | isoDate }}">
{{ post.date | dateDisplay }}
@@ -93,7 +93,7 @@
</h3>
{% endif %}
{{ bookmarkedUrl | unfurlCard | safe }}
<a class="u-bookmark-of text-xs text-surface-400 dark:text-surface-500 hover:underline break-all mt-1 inline-block" href="{{ bookmarkedUrl }}">
<a class="u-bookmark-of text-xs text-surface-400 dark:text-surface-400 hover:underline break-all mt-1 inline-block" href="{{ bookmarkedUrl }}">
{{ bookmarkedUrl }}
</a>
{% if post.templateContent %}
@@ -114,14 +114,14 @@
</svg>
</div>
<div class="flex-1 min-w-0">
<div class="flex items-center gap-3 text-xs text-surface-500">
<div class="flex items-center gap-3 text-xs text-surface-600 dark:text-surface-400">
<span class="font-medium text-green-600 dark:text-green-400">Reposted</span>
<time class="dt-published font-mono" datetime="{{ post.date | isoDate }}">
{{ post.date | dateDisplay }}
</time>
</div>
{{ repostedUrl | unfurlCard | safe }}
<a class="u-repost-of text-xs text-surface-400 dark:text-surface-500 hover:underline break-all mt-1 inline-block" href="{{ repostedUrl }}">
<a class="u-repost-of text-xs text-surface-400 dark:text-surface-400 hover:underline break-all mt-1 inline-block" href="{{ repostedUrl }}">
{{ repostedUrl }}
</a>
{% if post.templateContent %}
@@ -142,14 +142,14 @@
</svg>
</div>
<div class="flex-1 min-w-0">
<div class="flex items-center gap-3 text-xs text-surface-500">
<div class="flex items-center gap-3 text-xs text-surface-600 dark:text-surface-400">
<span class="font-medium text-sky-600 dark:text-sky-400">In reply to</span>
<time class="dt-published font-mono" datetime="{{ post.date | isoDate }}">
{{ post.date | dateDisplay }}
</time>
</div>
{{ replyToUrl | unfurlCard | safe }}
<a class="u-in-reply-to text-xs text-surface-400 dark:text-surface-500 hover:underline break-all mt-1 inline-block" href="{{ replyToUrl }}">
<a class="u-in-reply-to text-xs text-surface-400 dark:text-surface-400 hover:underline break-all mt-1 inline-block" href="{{ replyToUrl }}">
{{ replyToUrl }}
</a>
{% if post.templateContent %}
@@ -171,7 +171,7 @@
</svg>
</div>
<div class="flex-1 min-w-0">
<div class="flex items-center gap-3 text-xs text-surface-500">
<div class="flex items-center gap-3 text-xs text-surface-600 dark:text-surface-400">
<span class="font-medium text-purple-600 dark:text-purple-400">Photo</span>
<time class="dt-published font-mono" datetime="{{ post.date | isoDate }}">
{{ post.date | dateDisplay }}
@@ -209,7 +209,7 @@
{{ post.templateContent | striptags | truncate(250) }}
</p>
{% endif %}
<div class="flex items-center gap-3 text-xs text-surface-500">
<div class="flex items-center gap-3 text-xs text-surface-600 dark:text-surface-400">
<time class="dt-published font-mono" datetime="{{ post.date | isoDate }}">
{{ post.date | dateDisplay }}
</time>
@@ -222,9 +222,9 @@
{% else %}
{# ── Note card ── #}
<div class="flex items-center gap-3 text-xs text-surface-500 mb-2">
<div class="flex items-center gap-3 text-xs text-surface-600 dark:text-surface-400 mb-2">
<a class="u-url" href="{{ post.url }}">
<time class="dt-published font-medium font-mono text-surface-500 dark:text-surface-400" datetime="{{ post.date | isoDate }}">
<time class="dt-published font-medium font-mono text-surface-600 dark:text-surface-400" datetime="{{ post.date | isoDate }}">
{{ post.date | dateDisplay }}
</time>
</a>

View File

@@ -50,14 +50,14 @@
</svg>
</div>
<div class="flex-1 min-w-0">
<div class="flex items-center gap-3 text-xs text-surface-500">
<div class="flex items-center gap-3 text-xs text-surface-600 dark:text-surface-400">
<span class="font-medium text-red-600 dark:text-red-400">Liked</span>
<time class="dt-published font-mono" datetime="{{ post.date | isoDate }}">
{{ post.date | dateDisplay }}
</time>
</div>
{{ likedUrl | unfurlCard | safe }}
<a class="u-like-of text-xs text-surface-400 dark:text-surface-500 hover:underline break-all mt-1 inline-block" href="{{ likedUrl }}">
<a class="u-like-of text-xs text-surface-400 dark:text-surface-400 hover:underline break-all mt-1 inline-block" href="{{ likedUrl }}">
{{ likedUrl }}
</a>
{% if post.templateContent %}
@@ -78,7 +78,7 @@
</svg>
</div>
<div class="flex-1 min-w-0">
<div class="flex items-center gap-3 text-xs text-surface-500">
<div class="flex items-center gap-3 text-xs text-surface-600 dark:text-surface-400">
<span class="font-medium text-amber-600 dark:text-amber-400">Bookmarked</span>
<time class="dt-published font-mono" datetime="{{ post.date | isoDate }}">
{{ post.date | dateDisplay }}
@@ -90,7 +90,7 @@
</h3>
{% endif %}
{{ bookmarkedUrl | unfurlCard | safe }}
<a class="u-bookmark-of text-xs text-surface-400 dark:text-surface-500 hover:underline break-all mt-1 inline-block" href="{{ bookmarkedUrl }}">
<a class="u-bookmark-of text-xs text-surface-400 dark:text-surface-400 hover:underline break-all mt-1 inline-block" href="{{ bookmarkedUrl }}">
{{ bookmarkedUrl }}
</a>
{% if post.templateContent %}
@@ -111,14 +111,14 @@
</svg>
</div>
<div class="flex-1 min-w-0">
<div class="flex items-center gap-3 text-xs text-surface-500">
<div class="flex items-center gap-3 text-xs text-surface-600 dark:text-surface-400">
<span class="font-medium text-green-600 dark:text-green-400">Reposted</span>
<time class="dt-published font-mono" datetime="{{ post.date | isoDate }}">
{{ post.date | dateDisplay }}
</time>
</div>
{{ repostedUrl | unfurlCard | safe }}
<a class="u-repost-of text-xs text-surface-400 dark:text-surface-500 hover:underline break-all mt-1 inline-block" href="{{ repostedUrl }}">
<a class="u-repost-of text-xs text-surface-400 dark:text-surface-400 hover:underline break-all mt-1 inline-block" href="{{ repostedUrl }}">
{{ repostedUrl }}
</a>
{% if post.templateContent %}
@@ -139,14 +139,14 @@
</svg>
</div>
<div class="flex-1 min-w-0">
<div class="flex items-center gap-3 text-xs text-surface-500">
<div class="flex items-center gap-3 text-xs text-surface-600 dark:text-surface-400">
<span class="font-medium text-sky-600 dark:text-sky-400">In reply to</span>
<time class="dt-published font-mono" datetime="{{ post.date | isoDate }}">
{{ post.date | dateDisplay }}
</time>
</div>
{{ replyToUrl | unfurlCard | safe }}
<a class="u-in-reply-to text-xs text-surface-400 dark:text-surface-500 hover:underline break-all mt-1 inline-block" href="{{ replyToUrl }}">
<a class="u-in-reply-to text-xs text-surface-400 dark:text-surface-400 hover:underline break-all mt-1 inline-block" href="{{ replyToUrl }}">
{{ replyToUrl }}
</a>
{% if post.templateContent %}
@@ -168,7 +168,7 @@
</svg>
</div>
<div class="flex-1 min-w-0">
<div class="flex items-center gap-3 text-xs text-surface-500">
<div class="flex items-center gap-3 text-xs text-surface-600 dark:text-surface-400">
<span class="font-medium text-purple-600 dark:text-purple-400">Photo</span>
<time class="dt-published font-mono" datetime="{{ post.date | isoDate }}">
{{ post.date | dateDisplay }}
@@ -206,7 +206,7 @@
{{ post.templateContent | striptags | truncate(250) }}
</p>
{% endif %}
<div class="flex items-center gap-3 text-xs text-surface-500">
<div class="flex items-center gap-3 text-xs text-surface-600 dark:text-surface-400">
<time class="dt-published font-mono" datetime="{{ post.date | isoDate }}">
{{ post.date | dateDisplay }}
</time>
@@ -219,9 +219,9 @@
{% else %}
{# ── Note card ── #}
<div class="flex items-center gap-3 text-xs text-surface-500 mb-2">
<div class="flex items-center gap-3 text-xs text-surface-600 dark:text-surface-400 mb-2">
<a class="u-url" href="{{ post.url }}">
<time class="dt-published font-medium font-mono text-surface-500 dark:text-surface-400" datetime="{{ post.date | isoDate }}">
<time class="dt-published font-medium font-mono text-surface-600 dark:text-surface-400" datetime="{{ post.date | isoDate }}">
{{ post.date | dateDisplay }}
</time>
</a>

View File

@@ -43,17 +43,17 @@
{% elif widget.type == "fediverse-follow" %}
{% set widgetIcon = "user-plus" %}{% set widgetIconClass = "w-5 h-5 text-[#a730b8]" %}{% set widgetBorder = "border-l-[3px] border-l-[#a730b8]" %}
{% elif widget.type == "author-card" or widget.type == "author-card-compact" %}
{% set widgetIcon = "user" %}{% set widgetIconClass = "w-5 h-5 text-surface-500" %}{% set widgetBorder = "" %}
{% set widgetIcon = "user" %}{% set widgetIconClass = "w-5 h-5 text-surface-600 dark:text-surface-400" %}{% set widgetBorder = "" %}
{% elif widget.type == "recent-posts" %}
{% set widgetIcon = "list" %}{% set widgetIconClass = "w-5 h-5 text-surface-500" %}{% set widgetBorder = "" %}
{% set widgetIcon = "list" %}{% set widgetIconClass = "w-5 h-5 text-surface-600 dark:text-surface-400" %}{% set widgetBorder = "" %}
{% elif widget.type == "categories" %}
{% set widgetIcon = "tag" %}{% set widgetIconClass = "w-5 h-5 text-surface-500" %}{% set widgetBorder = "" %}
{% set widgetIcon = "tag" %}{% set widgetIconClass = "w-5 h-5 text-surface-600 dark:text-surface-400" %}{% set widgetBorder = "" %}
{% elif widget.type == "recent-comments" %}
{% set widgetIcon = "chat" %}{% set widgetIconClass = "w-5 h-5 text-surface-500" %}{% set widgetBorder = "" %}
{% set widgetIcon = "chat" %}{% set widgetIconClass = "w-5 h-5 text-surface-600 dark:text-surface-400" %}{% set widgetBorder = "" %}
{% elif widget.type == "search" %}
{% set widgetIcon = "search" %}{% set widgetIconClass = "w-5 h-5 text-surface-500" %}{% set widgetBorder = "" %}
{% set widgetIcon = "search" %}{% set widgetIconClass = "w-5 h-5 text-surface-600 dark:text-surface-400" %}{% set widgetBorder = "" %}
{% elif widget.type == "webmentions" %}
{% set widgetIcon = "share" %}{% set widgetIconClass = "w-5 h-5 text-surface-500" %}{% set widgetBorder = "" %}
{% set widgetIcon = "share" %}{% set widgetIconClass = "w-5 h-5 text-surface-600 dark:text-surface-400" %}{% set widgetBorder = "" %}
{% elif widget.type == "ai-usage" %}
{% set widgetIcon = "zap" %}{% set widgetIconClass = "w-5 h-5 text-amber-500" %}{% set widgetBorder = "border-l-[3px] border-l-amber-400 dark:border-l-amber-500" %}
{% else %}
@@ -82,6 +82,7 @@
class="widget-chevron"
:class="open && 'rotate-180'"
fill="none" stroke="currentColor" viewBox="0 0 24 24"
aria-hidden="true"
>
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"/>
</svg>
@@ -158,8 +159,8 @@
<div class="widget-collapsible mb-4" x-data="{ open: localStorage.getItem('{{ widgetKey }}') !== null ? localStorage.getItem('{{ widgetKey }}') === 'true' : true }">
<div class="bg-surface-50 dark:bg-surface-800 rounded-lg border border-surface-200 dark:border-surface-700 shadow-sm overflow-hidden">
<button class="widget-header w-full p-4" @click="open = !open; localStorage.setItem('{{ widgetKey }}', open)" :aria-expanded="open ? 'true' : 'false'">
<h3 class="widget-title font-bold text-lg flex items-center gap-2">{{ icon("user", "w-5 h-5 text-surface-500") }} Author</h3>
<svg class="widget-chevron" :class="open && 'rotate-180'" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"/></svg>
<h3 class="widget-title font-bold text-lg flex items-center gap-2">{{ icon("user", "w-5 h-5 text-surface-600 dark:text-surface-400") }} Author</h3>
<svg class="widget-chevron" :class="open && 'rotate-180'" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"/></svg>
</button>
<div x-show="open" x-transition:enter="transition ease-out duration-150" x-transition:enter-start="opacity-0" x-transition:enter-end="opacity-100" x-transition:leave="transition ease-in duration-100" x-transition:leave-start="opacity-100" x-transition:leave-end="opacity-0" x-cloak>
{% include "components/widgets/author-card.njk" %}
@@ -173,7 +174,7 @@
<div class="bg-surface-50 dark:bg-surface-800 rounded-lg border border-surface-200 dark:border-surface-700 shadow-sm overflow-hidden border-l-[3px] border-l-[#0085ff]">
<button class="widget-header w-full p-4" @click="open = !open; localStorage.setItem('{{ widgetKey }}', open)" :aria-expanded="open ? 'true' : 'false'">
<h3 class="widget-title font-bold text-lg flex items-center gap-2">{{ icon("globe", "w-5 h-5 text-[#0085ff]") }} Social Activity</h3>
<svg class="widget-chevron" :class="open && 'rotate-180'" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"/></svg>
<svg class="widget-chevron" :class="open && 'rotate-180'" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"/></svg>
</button>
<div x-show="open" x-transition:enter="transition ease-out duration-150" x-transition:enter-start="opacity-0" x-transition:enter-end="opacity-100" x-transition:leave="transition ease-in duration-100" x-transition:leave-start="opacity-100" x-transition:leave-end="opacity-0" x-cloak>
{% include "components/widgets/social-activity.njk" %}
@@ -187,7 +188,7 @@
<div class="bg-surface-50 dark:bg-surface-800 rounded-lg border border-surface-200 dark:border-surface-700 shadow-sm overflow-hidden border-l-[3px] border-l-surface-400 dark:border-l-surface-500">
<button class="widget-header w-full p-4" @click="open = !open; localStorage.setItem('{{ widgetKey }}', open)" :aria-expanded="open ? 'true' : 'false'">
<h3 class="widget-title font-bold text-lg flex items-center gap-2">{{ icon("github", "w-5 h-5 text-surface-800 dark:text-surface-200") }} GitHub</h3>
<svg class="widget-chevron" :class="open && 'rotate-180'" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"/></svg>
<svg class="widget-chevron" :class="open && 'rotate-180'" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"/></svg>
</button>
<div x-show="open" x-transition:enter="transition ease-out duration-150" x-transition:enter-start="opacity-0" x-transition:enter-end="opacity-100" x-transition:leave="transition ease-in duration-100" x-transition:leave-start="opacity-100" x-transition:leave-end="opacity-0" x-cloak>
{% include "components/widgets/github-repos.njk" %}
@@ -201,7 +202,7 @@
<div class="bg-surface-50 dark:bg-surface-800 rounded-lg border border-surface-200 dark:border-surface-700 shadow-sm overflow-hidden border-l-[3px] border-l-purple-400 dark:border-l-purple-500">
<button class="widget-header w-full p-4" @click="open = !open; localStorage.setItem('{{ widgetKey }}', open)" :aria-expanded="open ? 'true' : 'false'">
<h3 class="widget-title font-bold text-lg flex items-center gap-2">{{ icon("headphones", "w-5 h-5 text-purple-500") }} Listening</h3>
<svg class="widget-chevron" :class="open && 'rotate-180'" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"/></svg>
<svg class="widget-chevron" :class="open && 'rotate-180'" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"/></svg>
</button>
<div x-show="open" x-transition:enter="transition ease-out duration-150" x-transition:enter-start="opacity-0" x-transition:enter-end="opacity-100" x-transition:leave="transition ease-in duration-100" x-transition:leave-start="opacity-100" x-transition:leave-end="opacity-0" x-cloak>
{% include "components/widgets/funkwhale.njk" %}
@@ -214,8 +215,8 @@
<div class="widget-collapsible mb-4" x-data="{ open: localStorage.getItem('{{ widgetKey }}') !== null ? localStorage.getItem('{{ widgetKey }}') === 'true' : false }">
<div class="bg-surface-50 dark:bg-surface-800 rounded-lg border border-surface-200 dark:border-surface-700 shadow-sm overflow-hidden">
<button class="widget-header w-full p-4" @click="open = !open; localStorage.setItem('{{ widgetKey }}', open)" :aria-expanded="open ? 'true' : 'false'">
<h3 class="widget-title font-bold text-lg flex items-center gap-2">{{ icon("list", "w-5 h-5 text-surface-500") }} Recent Posts</h3>
<svg class="widget-chevron" :class="open && 'rotate-180'" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"/></svg>
<h3 class="widget-title font-bold text-lg flex items-center gap-2">{{ icon("list", "w-5 h-5 text-surface-600 dark:text-surface-400") }} Recent Posts</h3>
<svg class="widget-chevron" :class="open && 'rotate-180'" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"/></svg>
</button>
<div x-show="open" x-transition:enter="transition ease-out duration-150" x-transition:enter-start="opacity-0" x-transition:enter-end="opacity-100" x-transition:leave="transition ease-in duration-100" x-transition:leave-start="opacity-100" x-transition:leave-end="opacity-0" x-cloak>
{% include "components/widgets/recent-posts.njk" %}
@@ -230,7 +231,7 @@
<div class="bg-surface-50 dark:bg-surface-800 rounded-lg border border-surface-200 dark:border-surface-700 shadow-sm overflow-hidden border-l-[3px] border-l-amber-400 dark:border-l-amber-500">
<button class="widget-header w-full p-4" @click="open = !open; localStorage.setItem('{{ widgetKey }}', open)" :aria-expanded="open ? 'true' : 'false'">
<h3 class="widget-title font-bold text-lg flex items-center gap-2">{{ icon("book-open", "w-5 h-5 text-amber-500") }} Blogroll</h3>
<svg class="widget-chevron" :class="open && 'rotate-180'" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"/></svg>
<svg class="widget-chevron" :class="open && 'rotate-180'" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"/></svg>
</button>
<div x-show="open" x-transition:enter="transition ease-out duration-150" x-transition:enter-start="opacity-0" x-transition:enter-end="opacity-100" x-transition:leave="transition ease-in duration-100" x-transition:leave-start="opacity-100" x-transition:leave-end="opacity-0" x-cloak>
{% include "components/widgets/blogroll.njk" %}
@@ -246,7 +247,7 @@
<div class="bg-surface-50 dark:bg-surface-800 rounded-lg border border-surface-200 dark:border-surface-700 shadow-sm overflow-hidden border-l-[3px] border-l-amber-400 dark:border-l-amber-500">
<button class="widget-header w-full p-4" @click="open = !open; localStorage.setItem('{{ widgetKey }}', open)" :aria-expanded="open ? 'true' : 'false'">
<h3 class="widget-title font-bold text-lg flex items-center gap-2">{{ icon("rss", "w-5 h-5 text-amber-500") }} FeedLand</h3>
<svg class="widget-chevron" :class="open && 'rotate-180'" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"/></svg>
<svg class="widget-chevron" :class="open && 'rotate-180'" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"/></svg>
</button>
<div x-show="open" x-transition:enter="transition ease-out duration-150" x-transition:enter-start="opacity-0" x-transition:enter-end="opacity-100" x-transition:leave="transition ease-in duration-100" x-transition:leave-start="opacity-100" x-transition:leave-end="opacity-0" x-cloak>
{% include "components/widgets/feedland.njk" %}
@@ -260,8 +261,8 @@
<div class="widget-collapsible mb-4" x-data="{ open: localStorage.getItem('{{ widgetKey }}') !== null ? localStorage.getItem('{{ widgetKey }}') === 'true' : false }">
<div class="bg-surface-50 dark:bg-surface-800 rounded-lg border border-surface-200 dark:border-surface-700 shadow-sm overflow-hidden">
<button class="widget-header w-full p-4" @click="open = !open; localStorage.setItem('{{ widgetKey }}', open)" :aria-expanded="open ? 'true' : 'false'">
<h3 class="widget-title font-bold text-lg flex items-center gap-2">{{ icon("chat", "w-5 h-5 text-surface-500") }} Recent Comments</h3>
<svg class="widget-chevron" :class="open && 'rotate-180'" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"/></svg>
<h3 class="widget-title font-bold text-lg flex items-center gap-2">{{ icon("chat", "w-5 h-5 text-surface-600 dark:text-surface-400") }} Recent Comments</h3>
<svg class="widget-chevron" :class="open && 'rotate-180'" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"/></svg>
</button>
<div x-show="open" x-transition:enter="transition ease-out duration-150" x-transition:enter-start="opacity-0" x-transition:enter-end="opacity-100" x-transition:leave="transition ease-in duration-100" x-transition:leave-start="opacity-100" x-transition:leave-end="opacity-0" x-cloak>
{% include "components/widgets/recent-comments.njk" %}
@@ -274,8 +275,8 @@
<div class="widget-collapsible mb-4" x-data="{ open: localStorage.getItem('{{ widgetKey }}') !== null ? localStorage.getItem('{{ widgetKey }}') === 'true' : false }">
<div class="bg-surface-50 dark:bg-surface-800 rounded-lg border border-surface-200 dark:border-surface-700 shadow-sm overflow-hidden">
<button class="widget-header w-full p-4" @click="open = !open; localStorage.setItem('{{ widgetKey }}', open)" :aria-expanded="open ? 'true' : 'false'">
<h3 class="widget-title font-bold text-lg flex items-center gap-2">{{ icon("tag", "w-5 h-5 text-surface-500") }} Categories</h3>
<svg class="widget-chevron" :class="open && 'rotate-180'" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"/></svg>
<h3 class="widget-title font-bold text-lg flex items-center gap-2">{{ icon("tag", "w-5 h-5 text-surface-600 dark:text-surface-400") }} Categories</h3>
<svg class="widget-chevron" :class="open && 'rotate-180'" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"/></svg>
</button>
<div x-show="open" x-transition:enter="transition ease-out duration-150" x-transition:enter-start="opacity-0" x-transition:enter-end="opacity-100" x-transition:leave="transition ease-in duration-100" x-transition:leave-start="opacity-100" x-transition:leave-end="opacity-0" x-cloak>
{% include "components/widgets/categories.njk" %}

View File

@@ -28,22 +28,24 @@
{{ likes.length }} Like{% if likes.length != 1 %}s{% endif %}
</h3>
<is-land on:visible>
<div class="facepile">
<ul class="facepile" role="list">
{% for like in likes %}
<a href="{{ like.author.url }}"
class="facepile-avatar"
title="{{ like.author.name }}"
target="_blank"
rel="noopener">
<img
src="{{ like.author.photo or '/images/default-avatar.svg' }}"
alt="{{ like.author.name }}"
class="w-8 h-8 rounded-full ring-2 ring-white dark:ring-surface-900"
loading="lazy"
>
</a>
<li class="inline">
<a href="{{ like.author.url }}"
class="facepile-avatar"
aria-label="{{ like.author.name }} (opens in new tab)"
target="_blank"
rel="noopener">
<img
src="{{ like.author.photo or '/images/default-avatar.svg' }}"
alt=""
class="w-8 h-8 rounded-full ring-2 ring-white dark:ring-surface-900"
loading="lazy"
>
</a>
</li>
{% endfor %}
</div>
</ul>
</is-land>
</div>
{% endif %}
@@ -56,22 +58,24 @@
{{ reposts.length }} Repost{% if reposts.length != 1 %}s{% endif %}
</h3>
<is-land on:visible>
<div class="facepile">
<ul class="facepile" role="list">
{% for repost in reposts %}
<a href="{{ repost.author.url }}"
class="facepile-avatar"
title="{{ repost.author.name }}"
target="_blank"
rel="noopener">
<img
src="{{ repost.author.photo or '/images/default-avatar.svg' }}"
alt="{{ repost.author.name }}"
class="w-8 h-8 rounded-full ring-2 ring-white dark:ring-surface-900"
loading="lazy"
>
</a>
<li class="inline">
<a href="{{ repost.author.url }}"
class="facepile-avatar"
aria-label="{{ repost.author.name }} (opens in new tab)"
target="_blank"
rel="noopener">
<img
src="{{ repost.author.photo or '/images/default-avatar.svg' }}"
alt=""
class="w-8 h-8 rounded-full ring-2 ring-white dark:ring-surface-900"
loading="lazy"
>
</a>
</li>
{% endfor %}
</div>
</ul>
</is-land>
</div>
{% endif %}
@@ -84,22 +88,24 @@
{{ bookmarks.length }} Bookmark{% if bookmarks.length != 1 %}s{% endif %}
</h3>
<is-land on:visible>
<div class="facepile">
<ul class="facepile" role="list">
{% for bookmark in bookmarks %}
<a href="{{ bookmark.author.url }}"
class="facepile-avatar"
title="{{ bookmark.author.name }}"
target="_blank"
rel="noopener">
<img
src="{{ bookmark.author.photo or '/images/default-avatar.svg' }}"
alt="{{ bookmark.author.name }}"
class="w-8 h-8 rounded-full ring-2 ring-white dark:ring-surface-900"
loading="lazy"
>
</a>
<li class="inline">
<a href="{{ bookmark.author.url }}"
class="facepile-avatar"
aria-label="{{ bookmark.author.name }} (opens in new tab)"
target="_blank"
rel="noopener">
<img
src="{{ bookmark.author.photo or '/images/default-avatar.svg' }}"
alt=""
class="w-8 h-8 rounded-full ring-2 ring-white dark:ring-surface-900"
loading="lazy"
>
</a>
</li>
{% endfor %}
</div>
</ul>
</is-land>
</div>
{% endif %}
@@ -132,7 +138,7 @@
{{ reply.author.name }}
</a>
<a href="{{ reply.url }}"
class="text-xs text-surface-500 hover:underline"
class="text-xs text-surface-600 dark:text-surface-400 hover:underline"
target="_blank"
rel="noopener">
<time class="font-mono" datetime="{{ reply.published }}">
@@ -189,7 +195,9 @@
</p>
<form action="https://webmention.io/{{ site.webmentions.domain }}/webmention" method="post" class="flex gap-2">
<input type="hidden" name="target" value="{{ site.url }}{{ page.url }}">
<label for="webmention-source" class="sr-only">Your post URL</label>
<input
id="webmention-source"
type="url"
name="source"
placeholder="https://your-site.com/response"

View File

@@ -5,7 +5,7 @@
<is-land on:visible>
<div class="widget">
<h3 class="widget-title flex items-center gap-2">
<svg class="w-5 h-5 text-amber-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<svg class="w-5 h-5 text-amber-500" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 10V3L4 14h7v7l9-11h-7z"/>
</svg>
AI Transparency
@@ -15,19 +15,19 @@
<div class="grid grid-cols-2 gap-2 mb-3">
<div class="text-center p-2 rounded-lg bg-surface-50 dark:bg-surface-800 border border-surface-200 dark:border-surface-700">
<div class="text-lg font-bold text-surface-900 dark:text-surface-100">{{ stats.total }}</div>
<div class="text-[10px] text-surface-500 dark:text-surface-400">Total</div>
<div class="text-[10px] text-surface-600 dark:text-surface-400">Total</div>
</div>
<div class="text-center p-2 rounded-lg bg-surface-50 dark:bg-surface-800 border border-surface-200 dark:border-surface-700">
<div class="text-lg font-bold text-amber-600 dark:text-amber-400">{{ stats.aiCount }}</div>
<div class="text-[10px] text-surface-500 dark:text-surface-400">AI-involved</div>
<div class="text-[10px] text-surface-600 dark:text-surface-400">AI-involved</div>
</div>
<div class="text-center p-2 rounded-lg bg-surface-50 dark:bg-surface-800 border border-surface-200 dark:border-surface-700">
<div class="text-lg font-bold text-emerald-600 dark:text-emerald-400">{{ stats.total - stats.aiCount }}</div>
<div class="text-[10px] text-surface-500 dark:text-surface-400">Human-only</div>
<div class="text-[10px] text-surface-600 dark:text-surface-400">Human-only</div>
</div>
<div class="text-center p-2 rounded-lg bg-surface-50 dark:bg-surface-800 border border-surface-200 dark:border-surface-700">
<div class="text-lg font-bold text-surface-900 dark:text-surface-100">{{ stats.percentage }}%</div>
<div class="text-[10px] text-surface-500 dark:text-surface-400">AI ratio</div>
<div class="text-[10px] text-surface-600 dark:text-surface-400">AI ratio</div>
</div>
</div>
@@ -49,13 +49,13 @@
{# Compact post-graph — current year only, AI posts highlighted #}
{% if aiPostsList and aiPostsList.length %}
<div class="text-[10px] text-surface-500 dark:text-surface-400 mb-2">AI-involved posts this year</div>
<div class="text-[10px] text-surface-600 dark:text-surface-400 mb-2">AI-involved posts this year</div>
{% postGraph aiPostsList, { prefix: "ai-widget", limit: 1, noLabels: true, boxColorDark: "#44403c", highlightColorLight: "#d97706", highlightColorDark: "#fbbf24" } %}
{% endif %}
<a href="/ai/" class="text-sm text-amber-600 dark:text-amber-400 hover:underline flex items-center gap-1 mt-3">
View full AI report
<svg class="w-3 h-3" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7"/></svg>
<svg class="w-3 h-3" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7"/></svg>
</a>
</div>
</is-land>

View File

@@ -4,10 +4,10 @@
<div class="h-card p-author flex items-center gap-3">
{# Hidden u-photo for reliable microformat parsing #}
<data class="u-photo hidden" value="{{ site.author.avatar }}"></data>
<a href="{{ site.author.url }}" class="u-url u-uid" rel="me" itemprop="url">
<a href="{{ site.author.url }}" class="u-url u-uid" rel="me" itemprop="url" aria-label="{{ site.author.name }}">
<img
src="{{ site.author.avatar }}"
alt="{{ site.author.name }}"
alt=""
class="w-12 h-12 rounded-full object-cover shadow-lg"
loading="lazy"
>
@@ -16,9 +16,9 @@
<a href="{{ site.author.url }}" class="u-url p-name font-medium text-surface-900 dark:text-surface-100 hover:text-accent-600 dark:hover:text-accent-400 transition-colors">
{{ site.author.name }}
</a>
<p class="p-job-title text-xs text-surface-500">{{ site.author.title }}</p>
<p class="p-job-title text-xs text-surface-600 dark:text-surface-400">{{ site.author.title }}</p>
{% if site.author.locality %}
<p class="p-locality text-xs text-surface-500">{{ site.author.locality }}{% if site.author.country %}, <span class="p-country-name">{{ site.author.country }}</span>{% endif %}</p>
<p class="p-locality text-xs text-surface-600 dark:text-surface-400">{{ site.author.locality }}{% if site.author.country %}, <span class="p-country-name">{{ site.author.country }}</span>{% endif %}</p>
{% endif %}
</div>
</div>

View File

@@ -15,7 +15,7 @@
@click="activeTab = tab.key"
:class="activeTab === tab.key
? 'border-b-2 border-accent-600 text-accent-600 dark:text-accent-400 dark:border-accent-400'
: 'text-surface-500 hover:text-surface-700 dark:hover:text-surface-300'"
: 'text-surface-600 dark:text-surface-400 hover:text-surface-700 dark:hover:text-surface-300'"
class="px-2 py-1 text-xs font-medium transition-colors -mb-px"
x-text="tab.label + ' (' + tab.count + ')'"
></button>
@@ -40,7 +40,7 @@
</template>
</ul>
<div x-show="filteredBlogs.length === 0 && !loading" class="text-sm text-surface-500 py-2">
<div x-show="filteredBlogs.length === 0 && !loading" class="text-sm text-surface-600 dark:text-surface-400 py-2">
No blogs loaded yet.
</div>

View File

@@ -20,7 +20,7 @@
<is-land on:visible>
<div class="widget" x-data="fediverseInteract('{{ actorUrl }}', 'interact')">
<h3 class="widget-title">Follow Me</h3>
<p class="text-sm text-surface-500 dark:text-surface-400 mb-3">Follow me from your fediverse instance.</p>
<p class="text-sm text-surface-600 dark:text-surface-400 mb-3">Follow me from your fediverse instance.</p>
<a href="{{ actorUrl }}"
@click="handleClick($event)"
class="inline-flex items-center gap-2 px-4 py-2 rounded-lg bg-[#a730b8]/10 text-[#a730b8] hover:bg-[#a730b8]/20 transition-colors text-sm font-medium cursor-pointer"

View File

@@ -240,8 +240,8 @@
{# Sort links #}
<div class="fl-sort">
<span :class="sortBy === 'title' ? 'selected' : ''" @click="sortBy = 'title'">Title</span>
<span :class="sortBy === 'when' ? 'selected' : ''" @click="sortBy = 'when'">When</span>
<span :class="sortBy === 'title' ? 'selected' : ''" @click="sortBy = 'title'" @keydown.enter="sortBy = 'title'" @keydown.space.prevent="sortBy = 'title'" tabindex="0" role="button" :aria-pressed="sortBy === 'title'">Title</span>
<span :class="sortBy === 'when' ? 'selected' : ''" @click="sortBy = 'when'" @keydown.enter="sortBy = 'when'" @keydown.space.prevent="sortBy = 'when'" tabindex="0" role="button" :aria-pressed="sortBy === 'when'">When</span>
</div>
{# Feed list — pure divs, no table #}
@@ -253,7 +253,13 @@
<span class="fl-caret"
:class="expandedId === blog.id ? 'fl-caret-dark' : (selectedId === blog.id ? 'fl-caret-dark' : 'fl-caret-light')"
x-text="expandedId === blog.id ? '\u25BC' : '\u25B6'"
@click.stop="toggleExpand(blog)"></span>
@click.stop="toggleExpand(blog)"
@keydown.enter.stop="toggleExpand(blog)"
@keydown.space.prevent.stop="toggleExpand(blog)"
tabindex="0"
role="button"
:aria-label="expandedId === blog.id ? 'Collapse ' + blog.title : 'Expand ' + blog.title"
:aria-expanded="expandedId === blog.id"></span>
<span class="fl-name">
<a :href="blog.siteUrl || blog.feedUrl" target="_blank" rel="noopener"
x-text="blog.title" @click.stop></a>

View File

@@ -68,7 +68,7 @@
{{ listening.track }}
{% endif %}
</p>
<p class="text-xs text-surface-500 truncate">{{ listening.artist }}
<p class="text-xs text-surface-600 dark:text-surface-400 truncate">{{ listening.artist }}
<span class="text-purple-500 ml-1">Funkwhale</span>
</p>
</div>
@@ -97,7 +97,7 @@
{% endif %}
{% if scrobble.loved %}<span class="text-red-500 ml-0.5">&#9829;</span>{% endif %}
</p>
<p class="text-xs text-surface-500 truncate">{{ scrobble.artist }}
<p class="text-xs text-surface-600 dark:text-surface-400 truncate">{{ scrobble.artist }}
<span class="text-red-500 ml-1">Last.fm</span>
</p>
</div>

View File

@@ -9,31 +9,39 @@
</h3>
{# Tab buttons — order: Commits, Repos, Featured, PRs #}
<div class="flex gap-1 mb-4 border-b border-surface-200 dark:border-surface-700">
<div class="flex gap-1 mb-4 border-b border-surface-200 dark:border-surface-700" role="tablist" aria-label="GitHub activity">
<button
@click="activeTab = 'commits'"
:class="activeTab === 'commits' ? 'border-b-2 border-accent-500 text-accent-600 dark:text-accent-400' : 'text-surface-500 hover:text-surface-700 dark:hover:text-surface-300'"
:class="activeTab === 'commits' ? 'border-b-2 border-accent-500 text-accent-600 dark:text-accent-400' : 'text-surface-600 dark:text-surface-400 hover:text-surface-700 dark:hover:text-surface-300'"
:aria-selected="(activeTab === 'commits').toString()"
role="tab" id="gh-tab-commits" aria-controls="gh-panel-commits"
class="flex items-center gap-1.5 px-2 py-2 text-xs font-medium transition-colors -mb-px"
>
Commits
</button>
<button
@click="activeTab = 'repos'"
:class="activeTab === 'repos' ? 'border-b-2 border-accent-500 text-accent-600 dark:text-accent-400' : 'text-surface-500 hover:text-surface-700 dark:hover:text-surface-300'"
:class="activeTab === 'repos' ? 'border-b-2 border-accent-500 text-accent-600 dark:text-accent-400' : 'text-surface-600 dark:text-surface-400 hover:text-surface-700 dark:hover:text-surface-300'"
:aria-selected="(activeTab === 'repos').toString()"
role="tab" id="gh-tab-repos" aria-controls="gh-panel-repos"
class="flex items-center gap-1.5 px-2 py-2 text-xs font-medium transition-colors -mb-px"
>
Repos
</button>
<button
@click="activeTab = 'featured'"
:class="activeTab === 'featured' ? 'border-b-2 border-accent-500 text-accent-600 dark:text-accent-400' : 'text-surface-500 hover:text-surface-700 dark:hover:text-surface-300'"
:class="activeTab === 'featured' ? 'border-b-2 border-accent-500 text-accent-600 dark:text-accent-400' : 'text-surface-600 dark:text-surface-400 hover:text-surface-700 dark:hover:text-surface-300'"
:aria-selected="(activeTab === 'featured').toString()"
role="tab" id="gh-tab-featured" aria-controls="gh-panel-featured"
class="flex items-center gap-1.5 px-2 py-2 text-xs font-medium transition-colors -mb-px"
>
Featured
</button>
<button
@click="activeTab = 'prs'"
:class="activeTab === 'prs' ? 'border-b-2 border-accent-500 text-accent-600 dark:text-accent-400' : 'text-surface-500 hover:text-surface-700 dark:hover:text-surface-300'"
:class="activeTab === 'prs' ? 'border-b-2 border-accent-500 text-accent-600 dark:text-accent-400' : 'text-surface-600 dark:text-surface-400 hover:text-surface-700 dark:hover:text-surface-300'"
:aria-selected="(activeTab === 'prs').toString()"
role="tab" id="gh-tab-prs" aria-controls="gh-panel-prs"
class="flex items-center gap-1.5 px-2 py-2 text-xs font-medium transition-colors -mb-px"
>
PRs
@@ -44,18 +52,18 @@
<div class="h-[420px] overflow-y-auto">
{# Loading state #}
<div x-show="loading" class="text-sm text-surface-500 py-4 text-center">
<div x-show="loading" class="text-sm text-surface-600 dark:text-surface-400 py-4 text-center">
Loading...
</div>
{# Commits Tab #}
<div x-show="activeTab === 'commits' && !loading" x-cloak>
<div x-show="activeTab === 'commits' && !loading" x-cloak role="tabpanel" id="gh-panel-commits" aria-labelledby="gh-tab-commits">
<ul x-show="commits.length > 0" class="space-y-3">
<template x-for="commit in commits.slice(0, 5)" :key="commit.sha">
<li class="border-b border-surface-200 dark:border-surface-700 pb-3 last:border-0">
<a :href="commit.url" target="_blank" rel="noopener" class="block group">
<p class="text-sm text-surface-700 dark:text-surface-300 group-hover:text-surface-900 dark:group-hover:text-surface-100 transition-colors line-clamp-2" x-text="commit.message"></p>
<div class="flex items-center gap-2 mt-1.5 text-xs text-surface-500">
<div class="flex items-center gap-2 mt-1.5 text-xs text-surface-600 dark:text-surface-400">
<code class="text-xs font-mono bg-surface-100 dark:bg-surface-800 px-1 py-0.5 rounded" x-text="commit.sha"></code>
<span class="truncate" x-text="commit.repo?.split('/')[1] || commit.repo"></span>
<span class="font-mono" x-text="formatDate(commit.date)"></span>
@@ -64,11 +72,11 @@
</li>
</template>
</ul>
<div x-show="commits.length === 0" class="text-sm text-surface-500 py-2">No recent commits.</div>
<div x-show="commits.length === 0" class="text-sm text-surface-600 dark:text-surface-400 py-2">No recent commits.</div>
</div>
{# Repos Tab #}
<div x-show="activeTab === 'repos' && !loading" x-cloak>
<div x-show="activeTab === 'repos' && !loading" x-cloak role="tabpanel" id="gh-panel-repos" aria-labelledby="gh-tab-repos">
<ul x-show="repos.length > 0" class="space-y-3">
<template x-for="repo in repos.slice(0, 5)" :key="repo.name">
<li class="border-b border-surface-200 dark:border-surface-700 pb-3 last:border-0">
@@ -78,7 +86,7 @@
<span x-show="repo.language" class="text-xs px-1.5 py-0.5 rounded bg-surface-100 dark:bg-surface-800 text-surface-600 dark:text-surface-400 flex-shrink-0" x-text="repo.language"></span>
</div>
<p x-show="repo.description" class="text-xs text-surface-600 dark:text-surface-400 mt-1 line-clamp-2" x-text="repo.description"></p>
<div class="flex items-center gap-3 mt-1.5 text-xs text-surface-500">
<div class="flex items-center gap-3 mt-1.5 text-xs text-surface-600 dark:text-surface-400">
<span x-show="repo.stargazers_count > 0" class="flex items-center gap-1">
<svg class="w-3 h-3" fill="currentColor" viewBox="0 0 24 24"><path d="M12 17.27L18.18 21l-1.64-7.03L22 9.24l-7.19-.61L12 2 9.19 8.63 2 9.24l5.46 4.73L5.82 21z"/></svg>
<span x-text="repo.stargazers_count"></span>
@@ -89,11 +97,11 @@
</li>
</template>
</ul>
<div x-show="repos.length === 0" class="text-sm text-surface-500 py-2">No repositories found.</div>
<div x-show="repos.length === 0" class="text-sm text-surface-600 dark:text-surface-400 py-2">No repositories found.</div>
</div>
{# Featured Tab #}
<div x-show="activeTab === 'featured' && !loading" x-cloak>
<div x-show="activeTab === 'featured' && !loading" x-cloak role="tabpanel" id="gh-panel-featured" aria-labelledby="gh-tab-featured">
<ul x-show="featured.length > 0" class="space-y-3">
<template x-for="repo in featured.slice(0, 5)" :key="repo.fullName || repo.name">
<li class="border-b border-surface-200 dark:border-surface-700 pb-3 last:border-0">
@@ -103,7 +111,7 @@
<span x-show="repo.language" class="text-xs px-1.5 py-0.5 rounded bg-surface-100 dark:bg-surface-800 text-surface-600 dark:text-surface-400 flex-shrink-0" x-text="repo.language"></span>
</div>
<p x-show="repo.description" class="text-xs text-surface-600 dark:text-surface-400 mt-1 line-clamp-2" x-text="repo.description"></p>
<div class="flex items-center gap-3 mt-1.5 text-xs text-surface-500">
<div class="flex items-center gap-3 mt-1.5 text-xs text-surface-600 dark:text-surface-400">
<span x-show="repo.stars > 0" class="flex items-center gap-1">
<svg class="w-3 h-3" fill="currentColor" viewBox="0 0 24 24"><path d="M12 17.27L18.18 21l-1.64-7.03L22 9.24l-7.19-.61L12 2 9.19 8.63 2 9.24l5.46 4.73L5.82 21z"/></svg>
<span x-text="repo.stars"></span>
@@ -117,11 +125,11 @@
</li>
</template>
</ul>
<div x-show="featured.length === 0" class="text-sm text-surface-500 py-2">No featured projects.</div>
<div x-show="featured.length === 0" class="text-sm text-surface-600 dark:text-surface-400 py-2">No featured projects.</div>
</div>
{# PRs Tab #}
<div x-show="activeTab === 'prs' && !loading" x-cloak>
<div x-show="activeTab === 'prs' && !loading" x-cloak role="tabpanel" id="gh-panel-prs" aria-labelledby="gh-tab-prs">
<ul x-show="contributions.length > 0" class="space-y-3">
<template x-for="item in contributions.slice(0, 5)" :key="item.url">
<li class="border-b border-surface-200 dark:border-surface-700 pb-3 last:border-0">
@@ -136,7 +144,7 @@
</span>
<span class="text-sm text-surface-700 dark:text-surface-300 group-hover:text-surface-900 dark:group-hover:text-surface-100 transition-colors truncate" x-text="item.title"></span>
</div>
<div class="flex items-center gap-2 mt-1.5 text-xs text-surface-500 pl-6">
<div class="flex items-center gap-2 mt-1.5 text-xs text-surface-600 dark:text-surface-400 pl-6">
<span x-text="item.repo?.split('/')[1] || item.repo"></span>
<span x-show="item.number" x-text="'#' + item.number"></span>
<span class="font-mono" x-text="formatDate(item.date)"></span>
@@ -145,7 +153,7 @@
</li>
</template>
</ul>
<div x-show="contributions.length === 0" class="text-sm text-surface-500 py-2">No recent PRs or issues.</div>
<div x-show="contributions.length === 0" class="text-sm text-surface-600 dark:text-surface-400 py-2">No recent PRs or issues.</div>
</div>
</div>

View File

@@ -10,7 +10,7 @@
<div class="space-y-3">
{% if _prevPost %}
<div class="border-b border-surface-200 dark:border-surface-700 pb-3">
<span class="text-xs text-surface-500 uppercase tracking-wide block mb-1">Previous</span>
<span class="text-xs text-surface-600 dark:text-surface-400 uppercase tracking-wide block mb-1">Previous</span>
{% set _likedUrl = _prevPost.data.likeOf or _prevPost.data.like_of %}
{% set _bookmarkedUrl = _prevPost.data.bookmarkOf or _prevPost.data.bookmark_of %}
{% set _repostedUrl = _prevPost.data.repostOf or _prevPost.data.repost_of %}
@@ -36,7 +36,7 @@
{% endif %}
{% if _nextPost %}
<div>
<span class="text-xs text-surface-500 uppercase tracking-wide block mb-1">Next</span>
<span class="text-xs text-surface-600 dark:text-surface-400 uppercase tracking-wide block mb-1">Next</span>
{% set _likedUrl = _nextPost.data.likeOf or _nextPost.data.like_of %}
{% set _bookmarkedUrl = _nextPost.data.bookmarkOf or _nextPost.data.bookmark_of %}
{% set _repostedUrl = _nextPost.data.repostOf or _nextPost.data.repost_of %}

View File

@@ -20,7 +20,7 @@
<a href="{{ post.url }}" class="text-sm text-accent-600 dark:text-accent-400 hover:underline line-clamp-1">
Liked {{ _likedUrl | replace("https://", "") | truncate(40) }}
</a>
<time class="text-xs text-surface-500 block font-mono" datetime="{{ post.data.published or post.date }}">
<time class="text-xs text-surface-600 dark:text-surface-400 block font-mono" datetime="{{ post.data.published or post.date }}">
{{ (post.data.published or post.date) | dateDisplay }}
</time>
</div>
@@ -33,7 +33,7 @@
<a href="{{ post.url }}" class="text-sm text-accent-600 dark:text-accent-400 hover:underline line-clamp-1">
{{ post.data.title or ("Bookmarked " + (_bookmarkedUrl | replace("https://", "") | truncate(35))) }}
</a>
<time class="text-xs text-surface-500 block font-mono" datetime="{{ post.data.published or post.date }}">
<time class="text-xs text-surface-600 dark:text-surface-400 block font-mono" datetime="{{ post.data.published or post.date }}">
{{ (post.data.published or post.date) | dateDisplay }}
</time>
</div>
@@ -46,7 +46,7 @@
<a href="{{ post.url }}" class="text-sm text-accent-600 dark:text-accent-400 hover:underline line-clamp-1">
Reposted {{ _repostedUrl | replace("https://", "") | truncate(40) }}
</a>
<time class="text-xs text-surface-500 block font-mono" datetime="{{ post.data.published or post.date }}">
<time class="text-xs text-surface-600 dark:text-surface-400 block font-mono" datetime="{{ post.data.published or post.date }}">
{{ (post.data.published or post.date) | dateDisplay }}
</time>
</div>
@@ -59,7 +59,7 @@
<a href="{{ post.url }}" class="text-sm text-sky-600 dark:text-sky-400 hover:underline line-clamp-1">
Reply to {{ _replyToUrl | replace("https://", "") | truncate(40) }}
</a>
<time class="text-xs text-surface-500 block font-mono" datetime="{{ post.data.published or post.date }}">
<time class="text-xs text-surface-600 dark:text-surface-400 block font-mono" datetime="{{ post.data.published or post.date }}">
{{ (post.data.published or post.date) | dateDisplay }}
</time>
</div>
@@ -69,7 +69,7 @@
<a href="{{ post.url }}" class="text-sm text-accent-600 dark:text-accent-400 hover:underline line-clamp-2">
{{ post.data.title or post.data.name or (post.templateContent | striptags | truncate(50)) or "Note" }}
</a>
<time class="text-xs text-surface-500 block font-mono" datetime="{{ post.data.published or post.date }}">
<time class="text-xs text-surface-600 dark:text-surface-400 block font-mono" datetime="{{ post.data.published or post.date }}">
{{ (post.data.published or post.date) | dateDisplay }}
</time>
{% endif %}

View File

@@ -22,7 +22,7 @@
<a href="{{ post.url }}" class="text-sm text-red-600 dark:text-red-400 hover:underline break-all line-clamp-1">
Liked {{ likedUrl | replace("https://", "") | truncate(40) }}
</a>
<time class="text-xs text-surface-500 block font-mono" datetime="{{ post.data.published }}">
<time class="text-xs text-surface-600 dark:text-surface-400 block font-mono" datetime="{{ post.data.published }}">
{{ (post.data.published or post.date) | dateDisplay }}
</time>
</div>
@@ -37,7 +37,7 @@
<a href="{{ post.url }}" class="text-sm text-amber-600 dark:text-amber-400 hover:underline line-clamp-1">
{{ post.data.title or ("Bookmarked " + (bookmarkedUrl | replace("https://", "") | truncate(35))) }}
</a>
<time class="text-xs text-surface-500 block font-mono" datetime="{{ post.data.published }}">
<time class="text-xs text-surface-600 dark:text-surface-400 block font-mono" datetime="{{ post.data.published }}">
{{ (post.data.published or post.date) | dateDisplay }}
</time>
</div>
@@ -52,7 +52,7 @@
<a href="{{ post.url }}" class="text-sm text-green-600 dark:text-green-400 hover:underline break-all line-clamp-1">
Reposted {{ repostedUrl | replace("https://", "") | truncate(40) }}
</a>
<time class="text-xs text-surface-500 block font-mono" datetime="{{ post.data.published }}">
<time class="text-xs text-surface-600 dark:text-surface-400 block font-mono" datetime="{{ post.data.published }}">
{{ (post.data.published or post.date) | dateDisplay }}
</time>
</div>
@@ -67,7 +67,7 @@
<a href="{{ post.url }}" class="text-sm text-sky-600 dark:text-sky-400 hover:underline break-all line-clamp-1">
Reply to {{ replyToUrl | replace("https://", "") | truncate(40) }}
</a>
<time class="text-xs text-surface-500 block font-mono" datetime="{{ post.data.published }}">
<time class="text-xs text-surface-600 dark:text-surface-400 block font-mono" datetime="{{ post.data.published }}">
{{ (post.data.published or post.date) | dateDisplay }}
</time>
</div>
@@ -83,7 +83,7 @@
<a href="{{ post.url }}" class="text-sm text-indigo-600 dark:text-indigo-400 hover:underline line-clamp-1">
{{ post.data.title }}
</a>
<time class="text-xs text-surface-500 block font-mono" datetime="{{ post.data.published }}">
<time class="text-xs text-surface-600 dark:text-surface-400 block font-mono" datetime="{{ post.data.published }}">
{{ (post.data.published or post.date) | dateDisplay }}
</time>
</div>
@@ -99,7 +99,7 @@
<a href="{{ post.url }}" class="text-sm text-teal-600 dark:text-teal-400 hover:underline line-clamp-1">
{{ post.templateContent | striptags | truncate(50) or "Note" }}
</a>
<time class="text-xs text-surface-500 block font-mono" datetime="{{ post.data.published }}">
<time class="text-xs text-surface-600 dark:text-surface-400 block font-mono" datetime="{{ post.data.published }}">
{{ (post.data.published or post.date) | dateDisplay }}
</time>
</div>

View File

@@ -8,7 +8,8 @@
target="_blank"
rel="noopener"
class="flex-1 inline-flex items-center justify-center gap-2 px-3 py-2 rounded-lg bg-[#0085ff]/10 text-[#0085ff] hover:bg-[#0085ff]/20 transition-colors text-sm font-medium"
title="Share on Bluesky">
title="Share on Bluesky"
aria-label="Share on Bluesky">
<svg class="w-4 h-4" viewBox="0 0 568 501" fill="currentColor" aria-hidden="true">
<path d="M123.121 33.664C188.241 82.553 258.281 181.68 284 234.873c25.719-53.192 95.759-152.32 160.879-201.21C491.866-1.611 568-28.906 568 57.947c0 17.346-9.945 145.713-15.778 166.555-20.275 72.453-94.155 90.933-159.875 79.748C507.222 323.8 536.444 388.56 473.333 453.32c-119.86 122.992-172.272-30.859-185.702-70.281-2.462-7.227-3.614-10.608-3.631-7.733-.017-2.875-1.169.506-3.631 7.733-13.43 39.422-65.842 193.273-185.702 70.281-63.111-64.76-33.89-129.52 80.986-149.071-65.72 11.185-139.6-7.295-159.875-79.748C9.945 203.659 0 75.291 0 57.946 0-28.906 76.135-1.612 123.121 33.664Z"/>
</svg>
@@ -17,7 +18,8 @@
<a href="https://share.joinmastodon.org/#text={{ shareText | urlencode }}"
@click="handleClick($event)"
class="w-full inline-flex items-center justify-center gap-2 px-3 py-2 rounded-lg bg-[#a730b8]/10 text-[#a730b8] hover:bg-[#a730b8]/20 transition-colors text-sm font-medium cursor-pointer"
title="Share on the Fediverse (Shift+click to change instance)">
title="Share on the Fediverse (Shift+click to change instance)"
aria-label="Share on the Fediverse">
<svg class="w-4 h-4" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true">
<path d="M13.09 4.43L24 10.73v2.51L13.09 19.58v-2.51L21.83 12 13.09 6.98v-2.55zM13.09 9.49L17.44 12l-4.35 2.51V9.49z"/><path d="M10.91 4.43L0 10.73v2.51l8.74-5.03v10.09l2.18 1.28V4.43zM6.56 12L2.18 14.51l4.35 2.51V12z"/>
</svg>

View File

@@ -5,11 +5,13 @@
<h3 class="widget-title">Social Activity</h3>
{# Tab buttons #}
<div class="flex gap-1 mb-4 border-b border-surface-200 dark:border-surface-700">
<div class="flex gap-1 mb-4 border-b border-surface-200 dark:border-surface-700" role="tablist" aria-label="Social feeds">
{% if blueskyFeed and blueskyFeed.length %}
<button
@click="activeTab = 'bluesky'"
:class="activeTab === 'bluesky' ? 'border-b-2 border-[#0085ff] text-[#0085ff]' : 'text-surface-500 hover:text-surface-700 dark:hover:text-surface-300'"
:class="activeTab === 'bluesky' ? 'border-b-2 border-[#0085ff] text-[#0085ff]' : 'text-surface-600 dark:text-surface-400 hover:text-surface-700 dark:hover:text-surface-300'"
:aria-selected="(activeTab === 'bluesky').toString()"
role="tab" id="social-tab-bluesky" aria-controls="social-panel-bluesky"
class="flex items-center gap-1.5 px-3 py-2 text-sm font-medium transition-colors -mb-px"
>
<svg class="w-4 h-4 text-[#0085ff]" fill="currentColor" viewBox="0 0 24 24" aria-hidden="true">
@@ -21,7 +23,9 @@
{% if mastodonFeed and mastodonFeed.length %}
<button
@click="activeTab = 'mastodon'"
:class="activeTab === 'mastodon' ? 'border-b-2 border-[#a730b8] text-[#a730b8]' : 'text-surface-500 hover:text-surface-700 dark:hover:text-surface-300'"
:class="activeTab === 'mastodon' ? 'border-b-2 border-[#a730b8] text-[#a730b8]' : 'text-surface-600 dark:text-surface-400 hover:text-surface-700 dark:hover:text-surface-300'"
:aria-selected="(activeTab === 'mastodon').toString()"
role="tab" id="social-tab-mastodon" aria-controls="social-panel-mastodon"
class="flex items-center gap-1.5 px-3 py-2 text-sm font-medium transition-colors -mb-px"
>
<svg class="w-4 h-4 text-[#6364ff]" fill="currentColor" viewBox="0 0 24 24" aria-hidden="true">
@@ -34,7 +38,7 @@
{# Bluesky Tab Content #}
{% if blueskyFeed and blueskyFeed.length %}
<div x-show="activeTab === 'bluesky'" x-cloak>
<div x-show="activeTab === 'bluesky'" x-cloak role="tabpanel" id="social-panel-bluesky" aria-labelledby="social-tab-bluesky">
<ul class="space-y-3">
{% for post in blueskyFeed | head(5) %}
<li class="border-b border-surface-200 dark:border-surface-700 pb-3 last:border-0">
@@ -42,7 +46,7 @@
<p class="text-sm text-surface-700 dark:text-surface-300 group-hover:text-[#0085ff] transition-colors">
{{ post.text | truncate(140) }}
</p>
<div class="flex items-center gap-3 mt-2 text-xs text-surface-500">
<div class="flex items-center gap-3 mt-2 text-xs text-surface-600 dark:text-surface-400">
<time class="font-mono" datetime="{{ post.createdAt }}">{{ post.createdAt | date("MMM d, yyyy") }}</time>
{% if post.likeCount > 0 %}
<span class="flex items-center gap-1">
@@ -64,7 +68,7 @@
{# Mastodon Tab Content #}
{% if mastodonFeed and mastodonFeed.length %}
<div x-show="activeTab === 'mastodon'" x-cloak>
<div x-show="activeTab === 'mastodon'" x-cloak role="tabpanel" id="social-panel-mastodon" aria-labelledby="social-tab-mastodon">
<ul class="space-y-3">
{% for post in mastodonFeed | head(5) %}
<li class="border-b border-surface-200 dark:border-surface-700 pb-3 last:border-0">
@@ -72,7 +76,7 @@
<p class="text-sm text-surface-700 dark:text-surface-300 group-hover:text-[#a730b8] transition-colors">
{{ post.text | truncate(140) }}
</p>
<div class="flex items-center gap-3 mt-2 text-xs text-surface-500">
<div class="flex items-center gap-3 mt-2 text-xs text-surface-600 dark:text-surface-400">
<time class="font-mono" datetime="{{ post.createdAt }}">{{ post.createdAt | date("MMM d, yyyy") }}</time>
{% if post.favouritesCount > 0 %}
<span class="flex items-center gap-1">

View File

@@ -4,13 +4,13 @@
<h3 class="widget-title">Subscribe</h3>
<div class="space-y-2">
<a href="/feed.xml" class="flex items-center gap-2 text-sm text-surface-600 dark:text-surface-400 hover:text-orange-600 dark:hover:text-orange-400 hover:underline transition-colors">
<svg class="w-4 h-4 text-orange-500" fill="currentColor" viewBox="0 0 24 24">
<svg class="w-4 h-4 text-orange-500" fill="currentColor" viewBox="0 0 24 24" aria-hidden="true">
<path d="M6.18 15.64a2.18 2.18 0 0 1 2.18 2.18C8.36 19 7.38 20 6.18 20C5 20 4 19 4 17.82a2.18 2.18 0 0 1 2.18-2.18M4 4.44A15.56 15.56 0 0 1 19.56 20h-2.83A12.73 12.73 0 0 0 4 7.27V4.44m0 5.66a9.9 9.9 0 0 1 9.9 9.9h-2.83A7.07 7.07 0 0 0 4 12.93V10.1Z"/>
</svg>
RSS Feed
</a>
<a href="/feed.json" class="flex items-center gap-2 text-sm text-surface-600 dark:text-surface-400 hover:text-orange-600 dark:hover:text-orange-400 hover:underline transition-colors">
<svg class="w-4 h-4 text-yellow-500" fill="currentColor" viewBox="0 0 24 24">
<svg class="w-4 h-4 text-yellow-500" fill="currentColor" viewBox="0 0 24 24" aria-hidden="true">
<path d="M5 3h14a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2m3 12h2v2H8v-2m4-8h2v10h-2V7m4 4h2v6h-2v-6Z"/>
</svg>
JSON Feed

View File

@@ -3,7 +3,7 @@
<is-land on:visible>
<div class="widget">
<h3 class="widget-title">Contents</h3>
<nav class="toc">
<nav class="toc" aria-label="Table of contents">
<ul class="space-y-1 text-sm">
{% for item in toc %}
<li class="{% if item.level > 2 %}ml-{{ (item.level - 2) * 3 }}{% endif %}">

View File

@@ -14,14 +14,14 @@
<div class="flex border-b border-surface-200 dark:border-surface-700 mb-3">
<button
@click="tab = 'inbound'"
:class="tab === 'inbound' ? 'border-accent-500 text-accent-600 dark:text-accent-400' : 'border-transparent text-surface-500 hover:text-surface-700 dark:hover:text-surface-300'"
:class="tab === 'inbound' ? 'border-accent-500 text-accent-600 dark:text-accent-400' : 'border-transparent text-surface-600 dark:text-surface-400 hover:text-surface-700 dark:hover:text-surface-300'"
class="px-2 py-1.5 text-xs font-medium border-b-2 -mb-px transition-colors">
Received
<span x-show="mentions.length" x-text="mentions.length" class="ml-0.5 text-xs opacity-75"></span>
</button>
<button
@click="tab = 'outbound'"
:class="tab === 'outbound' ? 'border-accent-500 text-accent-600 dark:text-accent-400' : 'border-transparent text-surface-500 hover:text-surface-700 dark:hover:text-surface-300'"
:class="tab === 'outbound' ? 'border-accent-500 text-accent-600 dark:text-accent-400' : 'border-transparent text-surface-600 dark:text-surface-400 hover:text-surface-700 dark:hover:text-surface-300'"
class="px-2 py-1.5 text-xs font-medium border-b-2 -mb-px transition-colors">
Sent
</button>
@@ -52,14 +52,14 @@
<span x-show="wm['wm-property'] === 'in-reply-to'" class="text-sky-500"> replied to</span>
<span x-show="wm['wm-property'] === 'mention-of'" class="text-amber-500"> mentioned</span>
<span x-show="wm['wm-property'] === 'bookmark-of'" class="text-purple-500"> bookmarked</span>
<a :href="wm['wm-target']" class="text-surface-500 hover:underline block truncate" x-text="formatPath(wm['wm-target'])"></a>
<a :href="wm['wm-target']" class="text-surface-600 dark:text-surface-400 hover:underline block truncate" x-text="formatPath(wm['wm-target'])"></a>
</div>
</div>
</template>
</div>
{# Empty #}
<p x-show="!loading && !mentions.length && !error" class="text-xs text-surface-500 py-2">No webmentions received yet.</p>
<p x-show="!loading && !mentions.length && !error" class="text-xs text-surface-600 dark:text-surface-400 py-2">No webmentions received yet.</p>
{# Error #}
<p x-show="error" class="text-xs text-red-500 py-2" x-text="error"></p>

View File

@@ -135,18 +135,19 @@
}
})();
</script>
<a href="#main-content" class="skip-link">Skip to main content</a>
<header class="site-header">
<div class="container header-container">
<a href="/" class="site-title">{{ site.name }}</a>
{# Mobile menu button #}
<button id="menu-toggle" type="button" class="menu-toggle" aria-label="Toggle menu" aria-expanded="false">
<svg class="menu-icon" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round">
<svg class="menu-icon" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" aria-hidden="true">
<line x1="3" y1="6" x2="21" y2="6"/>
<line x1="3" y1="12" x2="21" y2="12"/>
<line x1="3" y1="18" x2="21" y2="18"/>
</svg>
<svg class="close-icon hidden" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round">
<svg class="close-icon hidden" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" aria-hidden="true">
<line x1="18" y1="6" x2="6" y2="18"/>
<line x1="6" y1="6" x2="18" y2="18"/>
</svg>
@@ -159,33 +160,33 @@
<a href="/about/">About</a>
<a href="/now/">Now</a>
{# Blog dropdown #}
<div class="nav-dropdown" x-data="{ open: false }" @mouseenter="open = true" @mouseleave="open = false">
<a href="/blog/" class="nav-dropdown-trigger">
<div class="nav-dropdown" x-data="{ open: false }" @mouseenter="open = true" @mouseleave="open = false" @focusin="open = true" @focusout="!$el.contains($event.relatedTarget) && (open = false)" @keydown.escape="open = false">
<a href="/blog/" class="nav-dropdown-trigger" :aria-expanded="open.toString()" aria-haspopup="true">
Blog
<svg class="w-3 h-3 ml-1 inline" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<svg class="w-3 h-3 ml-1 inline" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"/>
</svg>
</a>
<div class="nav-dropdown-menu" x-show="open" x-transition x-cloak>
<a href="/blog/">All Posts</a>
<div class="nav-dropdown-menu" x-show="open" x-transition x-cloak role="menu">
<a href="/blog/" role="menuitem">All Posts</a>
{% for pt in enabledPostTypes %}
<a href="{{ pt.path }}">{{ pt.label }}</a>
<a href="{{ pt.path }}" role="menuitem">{{ pt.label }}</a>
{% endfor %}
</div>
</div>
{# Pages dropdown #}
<div class="nav-dropdown" x-data="{ open: false }" @mouseenter="open = true" @mouseleave="open = false">
<a href="/slashes/" class="nav-dropdown-trigger">
<div class="nav-dropdown" x-data="{ open: false }" @mouseenter="open = true" @mouseleave="open = false" @focusin="open = true" @focusout="!$el.contains($event.relatedTarget) && (open = false)" @keydown.escape="open = false">
<a href="/slashes/" class="nav-dropdown-trigger" :aria-expanded="open.toString()" aria-haspopup="true">
Pages
<svg class="w-3 h-3 ml-1 inline" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<svg class="w-3 h-3 ml-1 inline" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"/>
</svg>
</a>
<div class="nav-dropdown-menu" x-show="open" x-transition x-cloak>
{% if blogrollStatus and blogrollStatus.source == "indiekit" %}<a href="/blogroll/">Blogroll</a>{% endif %}
{% if podrollStatus and podrollStatus.source == "indiekit" %}<a href="/podroll/">Podroll</a>{% endif %}
{% if newsActivity and newsActivity.source == "indiekit" %}<a href="/news/">News</a>{% endif %}
<a href="/slashes/">All Pages</a>
<div class="nav-dropdown-menu" x-show="open" x-transition x-cloak role="menu">
{% if blogrollStatus and blogrollStatus.source == "indiekit" %}<a href="/blogroll/" role="menuitem">Blogroll</a>{% endif %}
{% if podrollStatus and podrollStatus.source == "indiekit" %}<a href="/podroll/" role="menuitem">Podroll</a>{% endif %}
{% if newsActivity and newsActivity.source == "indiekit" %}<a href="/news/" role="menuitem">News</a>{% endif %}
<a href="/slashes/" role="menuitem">All Pages</a>
</div>
</div>
<a href="/interactions/">Interactions</a>
@@ -196,23 +197,23 @@
x-transition
@indiekit:auth.window="show = $event.detail.loggedIn"
class="admin-nav-link">
<svg class="w-4 h-4 inline -mt-0.5" fill="none" stroke="currentColor" viewBox="0 0 24 24" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<svg class="w-4 h-4 inline -mt-0.5" fill="none" stroke="currentColor" viewBox="0 0 24 24" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
<rect x="3" y="3" width="7" height="7"/><rect x="14" y="3" width="7" height="7"/><rect x="3" y="14" width="7" height="7"/><rect x="14" y="14" width="7" height="7"/>
</svg>
Dashboard
</a>
</nav>
<a href="/search/" aria-label="Search" title="Search" class="p-2 rounded-lg text-surface-600 dark:text-surface-400 hover:bg-surface-100 dark:hover:bg-surface-800 transition-colors">
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
<circle cx="11" cy="11" r="8"/><path d="M21 21l-4.35-4.35"/>
</svg>
</a>
<button id="theme-toggle" type="button" class="theme-toggle" aria-label="Toggle dark mode" title="Toggle dark mode">
<svg class="sun-icon" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<svg class="sun-icon" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true">
<circle cx="12" cy="12" r="5"/>
<path d="M12 1v2M12 21v2M4.22 4.22l1.42 1.42M18.36 18.36l1.42 1.42M1 12h2M21 12h2M4.22 19.78l1.42-1.42M18.36 5.64l1.42-1.42"/>
</svg>
<svg class="moon-icon" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<svg class="moon-icon" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true">
<path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"/>
</svg>
</button>
@@ -226,13 +227,13 @@
<a href="/now/">Now</a>
{# Blog section #}
<div class="mobile-nav-section">
<button type="button" class="mobile-nav-toggle" @click="blogOpen = !blogOpen">
<button type="button" class="mobile-nav-toggle" @click="blogOpen = !blogOpen" :aria-expanded="blogOpen.toString()" aria-controls="mobile-blog-submenu">
Blog
<svg class="w-4 h-4 transition-transform" :class="{ 'rotate-180': blogOpen }" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<svg class="w-4 h-4 transition-transform" :class="{ 'rotate-180': blogOpen }" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"/>
</svg>
</button>
<div class="mobile-nav-submenu" x-show="blogOpen" x-collapse>
<div id="mobile-blog-submenu" class="mobile-nav-submenu" x-show="blogOpen" x-collapse>
<a href="/blog/">All Posts</a>
{% for pt in enabledPostTypes %}
<a href="{{ pt.path }}">{{ pt.label }}</a>
@@ -241,13 +242,13 @@
</div>
{# Pages section #}
<div class="mobile-nav-section">
<button type="button" class="mobile-nav-toggle" @click="pagesOpen = !pagesOpen">
<button type="button" class="mobile-nav-toggle" @click="pagesOpen = !pagesOpen" :aria-expanded="pagesOpen.toString()" aria-controls="mobile-pages-submenu">
Pages
<svg class="w-4 h-4 transition-transform" :class="{ 'rotate-180': pagesOpen }" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<svg class="w-4 h-4 transition-transform" :class="{ 'rotate-180': pagesOpen }" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"/>
</svg>
</button>
<div class="mobile-nav-submenu" x-show="pagesOpen" x-collapse>
<div id="mobile-pages-submenu" class="mobile-nav-submenu" x-show="pagesOpen" x-collapse>
{% if blogrollStatus and blogrollStatus.source == "indiekit" %}<a href="/blogroll/">Blogroll</a>{% endif %}
{% if podrollStatus and podrollStatus.source == "indiekit" %}<a href="/podroll/">Podroll</a>{% endif %}
{% if newsActivity and newsActivity.source == "indiekit" %}<a href="/news/">News</a>{% endif %}
@@ -267,11 +268,11 @@
<button type="button" class="mobile-theme-toggle" aria-label="Toggle dark mode">
<span class="theme-label">Theme</span>
<span class="theme-icons">
<svg class="sun-icon" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<svg class="sun-icon" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true">
<circle cx="12" cy="12" r="5"/>
<path d="M12 1v2M12 21v2M4.22 4.22l1.42 1.42M18.36 18.36l1.42 1.42M1 12h2M21 12h2M4.22 19.78l1.42-1.42M18.36 5.64l1.42-1.42"/>
</svg>
<svg class="moon-icon" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<svg class="moon-icon" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true">
<path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"/>
</svg>
</span>
@@ -279,7 +280,7 @@
</nav>
</header>
<main class="container py-8" data-pagefind-body>
<main class="container py-8" id="main-content" data-pagefind-body>
{# Skeleton loader — shown until Tailwind stylesheet loads #}
<div class="page-skeleton" aria-hidden="true">
<div style="display:flex;gap:1.5rem;align-items:flex-start;margin-bottom:2rem">
@@ -329,7 +330,7 @@
<div class="grid grid-cols-2 md:grid-cols-4 gap-8 mb-8">
{# Navigate #}
<div>
<h4 class="text-sm font-semibold uppercase tracking-wider text-surface-500 dark:text-surface-400 mb-3">Navigate</h4>
<h4 class="text-sm font-semibold uppercase tracking-wider text-surface-600 dark:text-surface-400 mb-3">Navigate</h4>
<ul class="space-y-2">
<li><a href="/" class="text-sm text-surface-600 dark:text-surface-400 hover:text-surface-900 dark:hover:text-surface-100 hover:underline">Home</a></li>
<li><a href="/about/" class="text-sm text-surface-600 dark:text-surface-400 hover:text-surface-900 dark:hover:text-surface-100 hover:underline">About</a></li>
@@ -339,7 +340,7 @@
</div>
{# Content #}
<div>
<h4 class="text-sm font-semibold uppercase tracking-wider text-surface-500 dark:text-surface-400 mb-3">Content</h4>
<h4 class="text-sm font-semibold uppercase tracking-wider text-surface-600 dark:text-surface-400 mb-3">Content</h4>
<ul class="space-y-2">
<li><a href="/blog/" class="text-sm text-surface-600 dark:text-surface-400 hover:text-surface-900 dark:hover:text-surface-100 hover:underline">Blog</a></li>
{% for pt in enabledPostTypes %}
@@ -350,7 +351,7 @@
</div>
{# Connect #}
<div>
<h4 class="text-sm font-semibold uppercase tracking-wider text-surface-500 dark:text-surface-400 mb-3">Connect</h4>
<h4 class="text-sm font-semibold uppercase tracking-wider text-surface-600 dark:text-surface-400 mb-3">Connect</h4>
<ul class="space-y-2">
{% for social in site.social %}
<li><a href="{{ social.url }}" rel="{{ social.rel }}" target="_blank" class="text-sm text-surface-600 dark:text-surface-400 hover:text-surface-900 dark:hover:text-surface-100 hover:underline">{{ social.name }}</a></li>
@@ -359,7 +360,7 @@
</div>
{# Meta #}
<div>
<h4 class="text-sm font-semibold uppercase tracking-wider text-surface-500 dark:text-surface-400 mb-3">Meta</h4>
<h4 class="text-sm font-semibold uppercase tracking-wider text-surface-600 dark:text-surface-400 mb-3">Meta</h4>
<ul class="space-y-2">
<li><a href="/feed.xml" class="text-sm text-surface-600 dark:text-surface-400 hover:text-surface-900 dark:hover:text-surface-100 hover:underline">RSS Feed</a></li>
<li><a href="/feed.json" class="text-sm text-surface-600 dark:text-surface-400 hover:text-surface-900 dark:hover:text-surface-100 hover:underline">JSON Feed</a></li>
@@ -367,7 +368,7 @@
</ul>
</div>
</div>
<p class="text-center text-sm text-surface-500 dark:text-surface-400">Powered by <a href="https://getindiekit.com" class="hover:text-surface-900 dark:hover:text-surface-100 hover:underline">Indiekit</a> + <a href="https://11ty.dev" class="hover:text-surface-900 dark:hover:text-surface-100 hover:underline">Eleventy</a></p>
<p class="text-center text-sm text-surface-600 dark:text-surface-400">Powered by <a href="https://getindiekit.com" class="hover:text-surface-900 dark:hover:text-surface-100 hover:underline">Indiekit</a> + <a href="https://11ty.dev" class="hover:text-surface-900 dark:hover:text-surface-100 hover:underline">Eleventy</a></p>
</div>
</footer>
<script>
@@ -469,43 +470,43 @@
x-transition:leave="transition ease-in duration-150"
x-transition:leave-start="opacity-100 translate-y-0"
x-transition:leave-end="opacity-0 translate-y-4"
class="fab-menu">
class="fab-menu" role="menu" aria-label="Create new post">
{% if mpUrl %}
<a href="/posts/edit?url={{ mpUrl | urlencode }}" @click="open = false" class="fab-menu-item" rel="nofollow">
<svg class="w-5 h-5 text-accent-500" fill="none" stroke="currentColor" viewBox="0 0 24 24" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<a href="/posts/edit?url={{ mpUrl | urlencode }}" @click="open = false" class="fab-menu-item" rel="nofollow" role="menuitem">
<svg class="w-5 h-5 text-accent-500" fill="none" stroke="currentColor" viewBox="0 0 24 24" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
<path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"/>
<path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z"/>
</svg>
<span>Edit this post</span>
</a>
<div class="fab-menu-divider"></div>
<div class="fab-menu-divider" role="separator"></div>
{% endif %}
<a href="/posts/create?type=page" @click="open = false" class="fab-menu-item" rel="nofollow">
<svg class="w-5 h-5 text-surface-500" fill="none" stroke="currentColor" viewBox="0 0 24 24" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<a href="/posts/create?type=page" @click="open = false" class="fab-menu-item" rel="nofollow" role="menuitem">
<svg class="w-5 h-5 text-surface-600 dark:text-surface-400" fill="none" stroke="currentColor" viewBox="0 0 24 24" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
<path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><polyline points="14 2 14 8 20 8"/>
</svg>
<span>Page</span>
</a>
<a href="/posts/create?type=bookmark" @click="open = false" class="fab-menu-item" rel="nofollow">
<svg class="w-5 h-5 text-surface-500" fill="none" stroke="currentColor" viewBox="0 0 24 24" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<a href="/posts/create?type=bookmark" @click="open = false" class="fab-menu-item" rel="nofollow" role="menuitem">
<svg class="w-5 h-5 text-surface-600 dark:text-surface-400" fill="none" stroke="currentColor" viewBox="0 0 24 24" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
<path d="M19 21l-7-5-7 5V5a2 2 0 0 1 2-2h10a2 2 0 0 1 2 2z"/>
</svg>
<span>Bookmark</span>
</a>
<a href="/posts/create?type=photo" @click="open = false" class="fab-menu-item" rel="nofollow">
<svg class="w-5 h-5 text-surface-500" fill="none" stroke="currentColor" viewBox="0 0 24 24" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<a href="/posts/create?type=photo" @click="open = false" class="fab-menu-item" rel="nofollow" role="menuitem">
<svg class="w-5 h-5 text-surface-600 dark:text-surface-400" fill="none" stroke="currentColor" viewBox="0 0 24 24" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
<rect x="3" y="3" width="18" height="18" rx="2"/><circle cx="8.5" cy="8.5" r="1.5"/><polyline points="21 15 16 10 5 21"/>
</svg>
<span>Photo</span>
</a>
<a href="/posts/create?type=article" @click="open = false" class="fab-menu-item" rel="nofollow">
<svg class="w-5 h-5 text-surface-500" fill="none" stroke="currentColor" viewBox="0 0 24 24" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<a href="/posts/create?type=article" @click="open = false" class="fab-menu-item" rel="nofollow" role="menuitem">
<svg class="w-5 h-5 text-surface-600 dark:text-surface-400" fill="none" stroke="currentColor" viewBox="0 0 24 24" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
<path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><polyline points="14 2 14 8 20 8"/><line x1="16" y1="13" x2="8" y2="13"/><line x1="16" y1="17" x2="8" y2="17"/><polyline points="10 9 9 9 8 9"/>
</svg>
<span>Article</span>
</a>
<a href="/posts/create?type=note" @click="open = false" class="fab-menu-item" rel="nofollow">
<svg class="w-5 h-5 text-surface-500" fill="none" stroke="currentColor" viewBox="0 0 24 24" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<a href="/posts/create?type=note" @click="open = false" class="fab-menu-item" rel="nofollow" role="menuitem">
<svg class="w-5 h-5 text-surface-600 dark:text-surface-400" fill="none" stroke="currentColor" viewBox="0 0 24 24" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
<path d="M12 20h9"/><path d="M16.5 3.5a2.121 2.121 0 0 1 3 3L7 19l-4 1 1-4L16.5 3.5z"/>
</svg>
<span>Note</span>
@@ -515,8 +516,9 @@
<button @click="open = !open"
class="fab-button"
:aria-expanded="open"
aria-label="Create new post">
<svg class="w-7 h-7 transition-transform duration-200" :class="{ 'rotate-45': open }" fill="none" stroke="currentColor" viewBox="0 0 24 24" stroke-width="2.5" stroke-linecap="round">
aria-label="Create new post"
aria-haspopup="menu">
<svg class="w-7 h-7 transition-transform duration-200" :class="{ 'rotate-45': open }" fill="none" stroke="currentColor" viewBox="0 0 24 24" stroke-width="2.5" stroke-linecap="round" aria-hidden="true">
<line x1="12" y1="5" x2="12" y2="19"/><line x1="5" y1="12" x2="19" y2="12"/>
</svg>
</button>

View File

@@ -90,7 +90,7 @@ withSidebar: true
{% if post.data.summary %}
<p class="text-sm text-surface-600 dark:text-surface-400 mb-2 line-clamp-2">{{ post.data.summary }}</p>
{% endif %}
<div class="flex items-center gap-3 text-xs text-surface-500">
<div class="flex items-center gap-3 text-xs text-surface-600 dark:text-surface-400">
<time class="font-mono" datetime="{{ post.data.published or post.date }}">
{{ (post.data.published or post.date) | date("MMM d, yyyy") }}
</time>

View File

@@ -16,7 +16,7 @@ withSidebar: true
</p>
{% endif %}
{% if updated %}
<p class="text-sm text-surface-500 dark:text-surface-400 mt-2">
<p class="text-sm text-surface-600 dark:text-surface-400 mt-2">
Last updated: <time class="dt-updated font-mono text-sm" datetime="{{ updated | isoDate }}">{{ updated | dateDisplay }}</time>
</p>
{% endif %}
@@ -35,19 +35,19 @@ withSidebar: true
<div class="grid gap-4 sm:grid-cols-4 mb-6">
<div class="text-center p-3 rounded-lg bg-surface-50 dark:bg-surface-800 border border-surface-200 dark:border-surface-700 shadow-sm">
<div class="text-2xl font-bold text-surface-900 dark:text-surface-100">{{ stats.total }}</div>
<div class="text-xs text-surface-500 dark:text-surface-400">Total posts</div>
<div class="text-xs text-surface-600 dark:text-surface-400">Total posts</div>
</div>
<div class="text-center p-3 rounded-lg bg-surface-50 dark:bg-surface-800 border border-surface-200 dark:border-surface-700 shadow-sm">
<div class="text-2xl font-bold text-amber-600 dark:text-amber-400">{{ stats.aiCount }}</div>
<div class="text-xs text-surface-500 dark:text-surface-400">AI-involved</div>
<div class="text-xs text-surface-600 dark:text-surface-400">AI-involved</div>
</div>
<div class="text-center p-3 rounded-lg bg-surface-50 dark:bg-surface-800 border border-surface-200 dark:border-surface-700 shadow-sm">
<div class="text-2xl font-bold text-emerald-600 dark:text-emerald-400">{{ stats.total - stats.aiCount }}</div>
<div class="text-xs text-surface-500 dark:text-surface-400">Human-only</div>
<div class="text-xs text-surface-600 dark:text-surface-400">Human-only</div>
</div>
<div class="text-center p-3 rounded-lg bg-surface-50 dark:bg-surface-800 border border-surface-200 dark:border-surface-700 shadow-sm">
<div class="text-2xl font-bold text-surface-900 dark:text-surface-100">{{ stats.percentage }}%</div>
<div class="text-xs text-surface-500 dark:text-surface-400">AI ratio</div>
<div class="text-xs text-surface-600 dark:text-surface-400">AI ratio</div>
</div>
</div>
@@ -69,7 +69,7 @@ withSidebar: true
{# Post graph showing AI posts (highlighted) on the full year grid #}
<h3 class="text-lg font-semibold text-surface-900 dark:text-surface-100 mb-3">AI-Involved Posts Over Time</h3>
<p class="text-sm text-surface-500 dark:text-surface-400 mb-4">Highlighted days had posts with AI involvement (level 1+). Empty boxes represent days with no AI-involved posts.</p>
<p class="text-sm text-surface-600 dark:text-surface-400 mb-4">Highlighted days had posts with AI involvement (level 1+). Empty boxes represent days with no AI-involved posts.</p>
{% postGraph aiPostsList, { prefix: "ai", highlightColorLight: "#d97706", highlightColorDark: "#fbbf24" } %}
</section>
{% endif %}
@@ -82,7 +82,7 @@ withSidebar: true
{% if aiTextLevel or aiCodeLevel or aiTools %}
<aside class="mt-6 p-4 rounded-lg bg-surface-50 dark:bg-surface-800/50 border border-surface-200 dark:border-surface-700 shadow-sm">
<div class="flex items-center gap-2 mb-2">
<svg class="w-4 h-4 text-surface-500 dark:text-surface-400 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true">
<svg class="w-4 h-4 text-surface-600 dark:text-surface-400 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9.75 3.104v5.714a2.25 2.25 0 01-.659 1.591L5 14.5M9.75 3.104c-.251.023-.501.05-.75.082m.75-.082a24.301 24.301 0 014.5 0m0 0v5.714a2.25 2.25 0 00.659 1.591L19 14.5M14.25 3.104c.251.023.501.05.75.082M19 14.5l-2.47 2.47a2.25 2.25 0 01-1.59.659H9.06a2.25 2.25 0 01-1.591-.659L5 14.5m14 0V17a2 2 0 01-2 2H7a2 2 0 01-2-2v-2.5"/>
</svg>
<strong class="text-sm font-semibold text-surface-700 dark:text-surface-300">AI Usage</strong>
@@ -105,7 +105,7 @@ withSidebar: true
{% endif %}
</div>
{% if aiDescription %}
<p class="mt-2 text-xs text-surface-500 dark:text-surface-400">{{ aiDescription }}</p>
<p class="mt-2 text-xs text-surface-600 dark:text-surface-400">{{ aiDescription }}</p>
{% endif %}
<p class="mt-2 text-xs"><a href="/ai/" class="text-accent-600 dark:text-accent-400 hover:underline">Learn more about AI usage on this site &rarr;</a></p>
</aside>

View File

@@ -13,7 +13,7 @@ withBlogSidebar: true
<h1 class="p-name text-2xl sm:text-3xl font-bold text-surface-900 dark:text-surface-100 mb-3 sm:mb-4">{{ title }}</h1>
{% else %}
<div class="flex items-center gap-2 mb-1">
<span class="text-sm font-medium text-surface-500 dark:text-surface-400">
<span class="text-sm font-medium text-surface-600 dark:text-surface-400">
{% if replyTo %}&#8617; Reply{% elif likedUrl %}&#9829; Like{% elif repostedUrl %}&#9851; Repost{% elif bookmarkedUrl %}&#128278; Bookmark{% else %}&#9998; Note{% endif %}
</span>
</div>
@@ -73,7 +73,7 @@ withBlogSidebar: true
{% set aiCodeLevel = aiCodeLevel or ai_code_level %}
{% set aiTools = aiTools or ai_tools %}
{% set aiDescription = aiDescription or ai_description %}
<details class="mt-4 text-xs text-surface-500 dark:text-surface-400">
<details class="mt-4 text-xs text-surface-600 dark:text-surface-400">
<summary class="cursor-pointer hover:text-surface-600 dark:hover:text-surface-300 list-none flex items-center gap-1.5 [&::-webkit-details-marker]:hidden">
<svg class="w-3.5 h-3.5 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9.75 3.104v5.714a2.25 2.25 0 01-.659 1.591L5 14.5M9.75 3.104c-.251.023-.501.05-.75.082m.75-.082a24.301 24.301 0 014.5 0m0 0v5.714a2.25 2.25 0 00.659 1.591L19 14.5M14.25 3.104c.251.023.501.05.75.082M19 14.5l-2.47 2.47a2.25 2.25 0 01-1.59.659H9.06a2.25 2.25 0 01-1.591-.659L5 14.5m14 0V17a2 2 0 01-2 2H7a2 2 0 01-2-2v-2.5"/>
@@ -115,7 +115,7 @@ withBlogSidebar: true
{% if externalSyndication.length or selfHostedApUrl %}
<footer class="post-footer mt-8 pt-6 border-t border-surface-200 dark:border-surface-700">
<div class="flex flex-wrap items-center gap-4">
<span class="text-sm text-surface-500 dark:text-surface-400">Also on:</span>
<span class="text-sm text-surface-600 dark:text-surface-400">Also on:</span>
<div class="flex flex-wrap gap-3">
{# Fediverse remote interaction button (self-hosted ActivityPub) #}
{% if selfHostedApUrl %}
@@ -269,9 +269,10 @@ withBlogSidebar: true
{# Lightbox overlay for article images #}
<template x-teleport="body">
<div x-show="open" x-transition.opacity.duration.200ms
role="dialog" aria-modal="true" aria-label="Image viewer"
class="fixed inset-0 z-[9999] flex items-center justify-center bg-black/90 backdrop-blur-sm"
@click.self="close()">
<button @click="close()" class="absolute top-4 right-4 text-white/70 hover:text-white text-3xl leading-none p-2 z-10" aria-label="Close">&times;</button>
<button x-ref="closeBtn" @click="close()" class="absolute top-4 right-4 text-white/70 hover:text-white text-3xl leading-none p-2 z-10" aria-label="Close lightbox">&times;</button>
<template x-if="images.length > 1">
<button @click="prev()" class="absolute left-4 top-1/2 -translate-y-1/2 text-white/70 hover:text-white text-4xl leading-none p-2 z-10" aria-label="Previous">&lsaquo;</button>
</template>