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" %} {% 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]" %} {% 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" %} {% 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" %} {% 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" %} {% 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" %} {% 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" %} {% 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" %} {% 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" %} {% 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" %} {% 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" %} {% 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" %} {% 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 %} {% else %}
{% set widgetIcon = "" %}{% set widgetIconClass = "" %}{% set widgetBorder = "" %} {% set widgetIcon = "" %}{% set widgetIconClass = "" %}{% set widgetBorder = "" %}
{% endif %} {% endif %}
@@ -89,6 +89,7 @@
class="widget-chevron" class="widget-chevron"
:class="open && 'rotate-180'" :class="open && 'rotate-180'"
fill="none" stroke="currentColor" viewBox="0 0 24 24" 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"/> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"/>
</svg> </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="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"> <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'"> <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> <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"><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> </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> <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" %} {% 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="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"> <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'"> <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> <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"><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> </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> <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" %} {% 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="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"> <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'"> <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> <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"><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> </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> <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" %} {% 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="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"> <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'"> <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> <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"><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> </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> <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" %} {% 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="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"> <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'"> <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> <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"><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> </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> <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" %} {% 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="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"> <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'"> <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> <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"><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> </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> <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" %} {% 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"> <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'"> <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> <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> </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> <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" %} {% 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="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"> <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'"> <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> <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"><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> </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> <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" %} {% include "components/widgets/recent-comments.njk" %}

View File

@@ -21,6 +21,7 @@
<div class="mt-4"> <div class="mt-4">
{# Status messages #} {# Status messages #}
<div x-show="statusMessage" x-cloak <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' : 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' : 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'" 'bg-blue-50 text-blue-700 dark:bg-blue-900/20 dark:text-blue-400'"
@@ -54,12 +55,13 @@
</div> </div>
<form x-on:submit.prevent="submitComment()"> <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))" 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" 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> x-bind:maxlength="maxLength"></textarea>
<div class="flex items-center justify-between"> <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"> <button type="submit" class="button" x-bind:disabled="submitting">
<span x-show="!submitting">Post Comment</span> <span x-show="!submitting">Post Comment</span>
<span x-show="submitting" x-cloak>Posting...</span> <span x-show="submitting" x-cloak>Posting...</span>
@@ -71,7 +73,7 @@
{# Comment list #} {# Comment list #}
<div class="mt-6 space-y-4"> <div class="mt-6 space-y-4">
<template x-if="loading"> <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>
<template x-for="comment in comments" x-bind:key="comment.published"> <template x-for="comment in comments" x-bind:key="comment.published">
@@ -90,7 +92,7 @@
<div class="flex items-center gap-2"> <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" <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> 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> x-text="new Date(comment.published).toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' })"></time>
</div> </div>
<div class="mt-1 text-sm prose dark:prose-invert" x-html="comment.content?.html || comment.content?.text"></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>
<template x-if="!loading && comments.length === 0"> <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> </template>
</div> </div>
</div> </div>

View File

@@ -74,7 +74,7 @@
{% endif %} {% endif %}
{# Contact details — location, organization, website, email, PGP #} {# Contact details — location, organization, website, email, PGP #}
{% if cvLocality or cvCountry or cvOrg or cvUrl or cvEmail or cvKeyUrl %} {% 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 %} {% if cvLocality or cvCountry %}
<span>{% if cvLocality %}{{ cvLocality }}{% endif %}{% if cvLocality and cvCountry %}, {% endif %}{% if cvCountry %}{{ cvCountry }}{% endif %}</span> <span>{% if cvLocality %}{{ cvLocality }}{% endif %}{% if cvLocality and cvCountry %}, {% endif %}{% if cvCountry %}{{ cvCountry }}{% endif %}</span>
{% endif %} {% endif %}
@@ -160,7 +160,7 @@
{# Last Updated #} {# Last Updated #}
{% if cv.lastUpdated %} {% 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> Last updated: <time class="font-mono text-sm" datetime="{{ cv.lastUpdated }}">{{ cv.lastUpdated | date("PPP") }}</time>
</p> </p>
{% endif %} {% endif %}

View File

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

View File

@@ -14,6 +14,7 @@
@click="showModal = false"></div> @click="showModal = false"></div>
{# Panel #} {# Panel #}
<div class="relative bg-surface-50 dark:bg-surface-800 rounded-xl shadow-xl w-full max-w-sm p-6" <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="transition ease-out duration-200"
x-transition:enter-start="opacity-0 scale-95" x-transition:enter-start="opacity-0 scale-95"
x-transition:enter-end="opacity-100 scale-100" x-transition:enter-end="opacity-100 scale-100"
@@ -21,8 +22,8 @@
x-transition:leave-start="opacity-100 scale-100" x-transition:leave-start="opacity-100 scale-100"
x-transition:leave-end="opacity-0 scale-95" x-transition:leave-end="opacity-0 scale-95"
@click.stop> @click.stop>
<h3 class="text-lg font-semibold text-surface-900 dark:text-surface-100 mb-1">{{ modalTitle }}</h3> <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-500 dark:text-surface-400 mb-4">{{ modalDescription }}</p> <p class="text-sm text-surface-600 dark:text-surface-400 mb-4">{{ modalDescription }}</p>
{# Saved domains list #} {# Saved domains list #}
<template x-if="savedDomains.length > 0 && !showInput"> <template x-if="savedDomains.length > 0 && !showInput">
@@ -35,8 +36,8 @@
x-text="item.domain"></button> x-text="item.domain"></button>
<button class="px-2 py-2.5 text-surface-400 hover:text-red-500 transition-colors cursor-pointer" <button class="px-2 py-2.5 text-surface-400 hover:text-red-500 transition-colors cursor-pointer"
@click="deleteSaved(item.domain)" @click="deleteSaved(item.domain)"
title="Remove"> :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"><line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/></svg> <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> </button>
</div> </div>
</template> </template>
@@ -55,14 +56,16 @@
{# New domain input #} {# New domain input #}
<template x-if="savedDomains.length === 0 || showInput"> <template x-if="savedDomains.length === 0 || showInput">
<div> <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" x-model="instance"
@keydown.enter.prevent="confirm()" @keydown.enter.prevent="confirm()"
type="text" type="text"
placeholder="mastodon.social" 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"> 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"> <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> </template>
<div class="flex gap-3 mt-4"> <div class="flex gap-3 mt-4">
<button @click="showInput ? (showInput = false) : (showModal = false)" <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="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"> <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-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>
<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"> <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-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>
<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"> <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-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>
<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"> <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-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>
</div> </div>
{% endif %} {% 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"> <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="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="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> </div>
{% endfor %} {% endfor %}
</div> </div>
@@ -53,7 +53,7 @@
</div> </div>
{% endif %} {% endif %}
<p class="text-sm font-medium text-surface-900 dark:text-surface-100 truncate">{{ album.title }}</p> <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> <p class="text-xs text-surface-400">{{ album.playCount }} plays</p>
</div> </div>
{% endfor %} {% endfor %}

View File

@@ -41,11 +41,11 @@
{{ authorName }} {{ authorName }}
</a> </a>
{% if authorPronoun %} {% 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 %} {% endif %}
<p class="p-job-title text-sm text-surface-600 dark:text-surface-400" itemprop="jobTitle">{{ authorTitle }}</p> <p class="p-job-title text-sm text-surface-600 dark:text-surface-400" itemprop="jobTitle">{{ authorTitle }}</p>
{# Structured address #} {# 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 %} {% if authorLocality %}
<span class="p-locality" itemprop="addressLocality">{{ authorLocality }}</span>{% if authorCountry %}, {% endif %} <span class="p-locality" itemprop="addressLocality">{{ authorLocality }}</span>{% if authorCountry %}, {% endif %}
{% endif %} {% endif %}
@@ -79,7 +79,7 @@
</a> </a>
{% endif %} {% endif %}
{% if authorKeyUrl %} {% 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 🔐 PGP Key
</a> </a>
{% endif %} {% endif %}
@@ -87,11 +87,11 @@
{# Categories / Skills #} {# Categories / Skills #}
{% if authorCategories and authorCategories.length %} {% 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 %} {% 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 %} {% endfor %}
</div> </ul>
{% endif %} {% endif %}
{# Social links with rel="me" - critical for IndieWeb identity verification #} {# Social links with rel="me" - critical for IndieWeb identity verification #}
@@ -102,8 +102,8 @@
<a <a
href="{{ link.url }}" href="{{ link.url }}"
rel="{{ link.rel }} noopener" rel="{{ link.rel }} noopener"
class="u-url text-surface-500 hover:text-accent-600 dark:hover:text-accent-400 transition-colors" 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 }}" aria-label="{{ link.name }} (opens in new tab)"
target="_blank"> target="_blank">
{{ socialIcon(link.icon, "w-5 h-5") }} {{ socialIcon(link.icon, "w-5 h-5") }}
</a> </a>

View File

@@ -39,17 +39,17 @@
{% elif widget.type == "fediverse-follow" %} {% 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]" %} {% 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" %} {% 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" %} {% 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" %} {% 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" %} {% 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" %} {% 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" %} {% 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" %} {% 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" %} {% 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 %} {% else %}
@@ -78,6 +78,7 @@
class="widget-chevron" class="widget-chevron"
:class="open && 'rotate-180'" :class="open && 'rotate-180'"
fill="none" stroke="currentColor" viewBox="0 0 24 24" 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"/> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"/>
</svg> </svg>

View File

@@ -39,17 +39,17 @@
</span> </span>
{% else %} {% else %}
<div class="p-4 sm:p-5"> <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"> <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 }} {{ _prevTitle }}
</span> </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> </div>
{% endif %} {% endif %}
</a> </a>
{% else %} {% 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 %} {% endif %}
{# ── Next Post ── #} {# ── Next Post ── #}
@@ -85,17 +85,17 @@
</span> </span>
{% else %} {% else %}
<div class="p-4 sm:p-5 text-right"> <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"> <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 }} {{ _nextTitle }}
</span> </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> </div>
{% endif %} {% endif %}
</a> </a>
{% else %} {% 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 %} {% endif %}
</div> </div>

View File

@@ -13,14 +13,14 @@
<aside class="reply-context mb-6"> <aside class="reply-context mb-6">
{% if replyTo %} {% if replyTo %}
<div class="u-in-reply-to h-cite"> <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"> <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"/> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 10h10a8 8 0 018 8v2M3 10l6 6m-6-6l6-6"/>
</svg> </svg>
<span>In reply to:</span> <span>In reply to:</span>
</p> </p>
{% unfurl replyTo %} {% 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 }} {{ replyTo }}
</a> </a>
</div> </div>
@@ -28,14 +28,14 @@
{% if likedUrl %} {% if likedUrl %}
<div class="u-like-of h-cite"> <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"> <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"/> <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> </svg>
<span>Liked:</span> <span>Liked:</span>
</p> </p>
{% unfurl likedUrl %} {% 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 }} {{ likedUrl }}
</a> </a>
</div> </div>
@@ -43,14 +43,14 @@
{% if repostedUrl %} {% if repostedUrl %}
<div class="u-repost-of h-cite"> <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"> <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"/> <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> </svg>
<span>Reposted:</span> <span>Reposted:</span>
</p> </p>
{% unfurl repostedUrl %} {% 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 }} {{ repostedUrl }}
</a> </a>
</div> </div>
@@ -58,14 +58,14 @@
{% if bookmarkedUrl %} {% if bookmarkedUrl %}
<div class="u-bookmark-of h-cite"> <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"> <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"/> <path d="M17 3H7c-1.1 0-2 .9-2 2v16l7-3 7 3V5c0-1.1-.9-2-2-2z"/>
</svg> </svg>
<span>Bookmarked:</span> <span>Bookmarked:</span>
</p> </p>
{% unfurl bookmarkedUrl %} {% 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 }} {{ bookmarkedUrl }}
</a> </a>
</div> </div>

View File

@@ -15,19 +15,19 @@
<div class="grid gap-4 sm:grid-cols-4 mb-6"> <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-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-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>
<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-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-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>
<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-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-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>
<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-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-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>
</div> </div>
@@ -50,7 +50,7 @@
{# Post graph — AI-involved posts highlighted #} {# Post graph — AI-involved posts highlighted #}
{% if aiPostsList and aiPostsList.length %} {% 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> <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 %} {% set graphLimit = sectionConfig.limit or 1 %}
{% postGraph aiPostsList, { prefix: "ai-section", limit: graphLimit, boxColorDark: "#44403c", highlightColorLight: "#d97706", highlightColorDark: "#fbbf24" } %} {% postGraph aiPostsList, { prefix: "ai-section", limit: graphLimit, boxColorDark: "#44403c", highlightColorLight: "#d97706", highlightColorDark: "#fbbf24" } %}
{% endif %} {% endif %}

View File

@@ -40,11 +40,11 @@
</div> </div>
<div class="flex items-center gap-2 shrink-0"> <div class="flex items-center gap-2 shrink-0">
{% if item.startDate %} {% 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 %} {{ item.startDate }}{% if item.endDate %} {{ item.endDate }}{% else %} Present{% endif %}
</span> </span>
{% elif item.year %} {% 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 %} {% endif %}
<svg <svg
class="w-4 h-4 text-surface-400 transition-transform duration-200" class="w-4 h-4 text-surface-400 transition-transform duration-200"
@@ -69,11 +69,11 @@
class="px-4 pb-4" class="px-4 pb-4"
> >
{% if item.startDate %} {% 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 %} {{ item.startDate }}{% if item.endDate %} {{ item.endDate }}{% else %} Present{% endif %}
</p> </p>
{% elif item.year %} {% 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 %} {% endif %}
{% if item.description %} {% if item.description %}

View File

@@ -24,7 +24,7 @@
{% if item.type %} &middot; <span class="capitalize">{{ item.type }}</span>{% endif %} {% if item.type %} &middot; <span class="capitalize">{{ item.type }}</span>{% endif %}
</p> </p>
{% if item.startDate %} {% 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 %} {{ item.startDate }}{% if item.endDate %} {{ item.endDate }}{% else %} Present{% endif %}
</p> </p>
{% endif %} {% endif %}

View File

@@ -13,7 +13,7 @@
{% for lang in cv.languages %} {% 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"> <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="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> </div>
{% endfor %} {% endfor %}
</div> </div>

View File

@@ -62,7 +62,7 @@
</div> </div>
<div class="flex items-center gap-2 shrink-0"> <div class="flex items-center gap-2 shrink-0">
{% if item.startDate %} {% 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 %} {{ item.startDate }}{% if item.endDate %} {{ item.endDate }}{% else %} Present{% endif %}
</span> </span>
{% endif %} {% endif %}
@@ -89,7 +89,7 @@
class="px-4 pb-4" class="px-4 pb-4"
> >
{% if item.startDate %} {% 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 %} {{ item.startDate }}{% if item.endDate %} {{ item.endDate }}{% else %} Present{% endif %}
</p> </p>
{% endif %} {% endif %}

View File

@@ -62,7 +62,7 @@
</div> </div>
<div class="flex items-center gap-2 shrink-0"> <div class="flex items-center gap-2 shrink-0">
{% if item.startDate %} {% 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 %} {{ item.startDate }}{% if item.endDate %} {{ item.endDate }}{% else %} Present{% endif %}
</span> </span>
{% endif %} {% endif %}
@@ -89,7 +89,7 @@
class="px-4 pb-4" class="px-4 pb-4"
> >
{% if item.startDate %} {% 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 %} {{ item.startDate }}{% if item.endDate %} {{ item.endDate }}{% else %} Present{% endif %}
</p> </p>
{% endif %} {% endif %}

View File

@@ -52,7 +52,7 @@
</div> </div>
<div class="flex items-center gap-2 shrink-0"> <div class="flex items-center gap-2 shrink-0">
{% if item.startDate %} {% 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 %} {{ item.startDate }}{% if item.endDate %} {{ item.endDate }}{% else %} Present{% endif %}
</span> </span>
{% endif %} {% endif %}
@@ -79,7 +79,7 @@
class="px-4 pb-4" class="px-4 pb-4"
> >
{% if item.startDate %} {% 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 %} {{ item.startDate }}{% if item.endDate %} {{ item.endDate }}{% else %} Present{% endif %}
</p> </p>
{% endif %} {% endif %}

View File

@@ -53,14 +53,14 @@
</svg> </svg>
</div> </div>
<div class="flex-1 min-w-0"> <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> <span class="font-medium text-red-600 dark:text-red-400">Liked</span>
<time class="dt-published font-mono" datetime="{{ post.date | isoDate }}"> <time class="dt-published font-mono" datetime="{{ post.date | isoDate }}">
{{ post.date | dateDisplay }} {{ post.date | dateDisplay }}
</time> </time>
</div> </div>
{{ likedUrl | unfurlCard | safe }} {{ 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 }} {{ likedUrl }}
</a> </a>
{% if post.templateContent %} {% if post.templateContent %}
@@ -81,7 +81,7 @@
</svg> </svg>
</div> </div>
<div class="flex-1 min-w-0"> <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> <span class="font-medium text-amber-600 dark:text-amber-400">Bookmarked</span>
<time class="dt-published font-mono" datetime="{{ post.date | isoDate }}"> <time class="dt-published font-mono" datetime="{{ post.date | isoDate }}">
{{ post.date | dateDisplay }} {{ post.date | dateDisplay }}
@@ -93,7 +93,7 @@
</h3> </h3>
{% endif %} {% endif %}
{{ bookmarkedUrl | unfurlCard | safe }} {{ 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 }} {{ bookmarkedUrl }}
</a> </a>
{% if post.templateContent %} {% if post.templateContent %}
@@ -114,14 +114,14 @@
</svg> </svg>
</div> </div>
<div class="flex-1 min-w-0"> <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> <span class="font-medium text-green-600 dark:text-green-400">Reposted</span>
<time class="dt-published font-mono" datetime="{{ post.date | isoDate }}"> <time class="dt-published font-mono" datetime="{{ post.date | isoDate }}">
{{ post.date | dateDisplay }} {{ post.date | dateDisplay }}
</time> </time>
</div> </div>
{{ repostedUrl | unfurlCard | safe }} {{ 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 }} {{ repostedUrl }}
</a> </a>
{% if post.templateContent %} {% if post.templateContent %}
@@ -142,14 +142,14 @@
</svg> </svg>
</div> </div>
<div class="flex-1 min-w-0"> <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> <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 }}"> <time class="dt-published font-mono" datetime="{{ post.date | isoDate }}">
{{ post.date | dateDisplay }} {{ post.date | dateDisplay }}
</time> </time>
</div> </div>
{{ replyToUrl | unfurlCard | safe }} {{ 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 }} {{ replyToUrl }}
</a> </a>
{% if post.templateContent %} {% if post.templateContent %}
@@ -171,7 +171,7 @@
</svg> </svg>
</div> </div>
<div class="flex-1 min-w-0"> <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> <span class="font-medium text-purple-600 dark:text-purple-400">Photo</span>
<time class="dt-published font-mono" datetime="{{ post.date | isoDate }}"> <time class="dt-published font-mono" datetime="{{ post.date | isoDate }}">
{{ post.date | dateDisplay }} {{ post.date | dateDisplay }}
@@ -209,7 +209,7 @@
{{ post.templateContent | striptags | truncate(250) }} {{ post.templateContent | striptags | truncate(250) }}
</p> </p>
{% endif %} {% 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 }}"> <time class="dt-published font-mono" datetime="{{ post.date | isoDate }}">
{{ post.date | dateDisplay }} {{ post.date | dateDisplay }}
</time> </time>
@@ -222,9 +222,9 @@
{% else %} {% else %}
{# ── Note card ── #} {# ── 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 }}"> <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 }} {{ post.date | dateDisplay }}
</time> </time>
</a> </a>

View File

@@ -50,14 +50,14 @@
</svg> </svg>
</div> </div>
<div class="flex-1 min-w-0"> <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> <span class="font-medium text-red-600 dark:text-red-400">Liked</span>
<time class="dt-published font-mono" datetime="{{ post.date | isoDate }}"> <time class="dt-published font-mono" datetime="{{ post.date | isoDate }}">
{{ post.date | dateDisplay }} {{ post.date | dateDisplay }}
</time> </time>
</div> </div>
{{ likedUrl | unfurlCard | safe }} {{ 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 }} {{ likedUrl }}
</a> </a>
{% if post.templateContent %} {% if post.templateContent %}
@@ -78,7 +78,7 @@
</svg> </svg>
</div> </div>
<div class="flex-1 min-w-0"> <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> <span class="font-medium text-amber-600 dark:text-amber-400">Bookmarked</span>
<time class="dt-published font-mono" datetime="{{ post.date | isoDate }}"> <time class="dt-published font-mono" datetime="{{ post.date | isoDate }}">
{{ post.date | dateDisplay }} {{ post.date | dateDisplay }}
@@ -90,7 +90,7 @@
</h3> </h3>
{% endif %} {% endif %}
{{ bookmarkedUrl | unfurlCard | safe }} {{ 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 }} {{ bookmarkedUrl }}
</a> </a>
{% if post.templateContent %} {% if post.templateContent %}
@@ -111,14 +111,14 @@
</svg> </svg>
</div> </div>
<div class="flex-1 min-w-0"> <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> <span class="font-medium text-green-600 dark:text-green-400">Reposted</span>
<time class="dt-published font-mono" datetime="{{ post.date | isoDate }}"> <time class="dt-published font-mono" datetime="{{ post.date | isoDate }}">
{{ post.date | dateDisplay }} {{ post.date | dateDisplay }}
</time> </time>
</div> </div>
{{ repostedUrl | unfurlCard | safe }} {{ 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 }} {{ repostedUrl }}
</a> </a>
{% if post.templateContent %} {% if post.templateContent %}
@@ -139,14 +139,14 @@
</svg> </svg>
</div> </div>
<div class="flex-1 min-w-0"> <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> <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 }}"> <time class="dt-published font-mono" datetime="{{ post.date | isoDate }}">
{{ post.date | dateDisplay }} {{ post.date | dateDisplay }}
</time> </time>
</div> </div>
{{ replyToUrl | unfurlCard | safe }} {{ 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 }} {{ replyToUrl }}
</a> </a>
{% if post.templateContent %} {% if post.templateContent %}
@@ -168,7 +168,7 @@
</svg> </svg>
</div> </div>
<div class="flex-1 min-w-0"> <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> <span class="font-medium text-purple-600 dark:text-purple-400">Photo</span>
<time class="dt-published font-mono" datetime="{{ post.date | isoDate }}"> <time class="dt-published font-mono" datetime="{{ post.date | isoDate }}">
{{ post.date | dateDisplay }} {{ post.date | dateDisplay }}
@@ -206,7 +206,7 @@
{{ post.templateContent | striptags | truncate(250) }} {{ post.templateContent | striptags | truncate(250) }}
</p> </p>
{% endif %} {% 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 }}"> <time class="dt-published font-mono" datetime="{{ post.date | isoDate }}">
{{ post.date | dateDisplay }} {{ post.date | dateDisplay }}
</time> </time>
@@ -219,9 +219,9 @@
{% else %} {% else %}
{# ── Note card ── #} {# ── 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 }}"> <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 }} {{ post.date | dateDisplay }}
</time> </time>
</a> </a>

View File

@@ -43,17 +43,17 @@
{% elif widget.type == "fediverse-follow" %} {% 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]" %} {% 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" %} {% 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" %} {% 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" %} {% 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" %} {% 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" %} {% 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" %} {% 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" %} {% 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" %} {% 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 %} {% else %}
@@ -82,6 +82,7 @@
class="widget-chevron" class="widget-chevron"
:class="open && 'rotate-180'" :class="open && 'rotate-180'"
fill="none" stroke="currentColor" viewBox="0 0 24 24" 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"/> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"/>
</svg> </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="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"> <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'"> <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> <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"><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> </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> <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" %} {% 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]"> <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'"> <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> <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> </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> <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" %} {% 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"> <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'"> <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> <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> </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> <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" %} {% 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"> <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'"> <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> <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> </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> <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" %} {% 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="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"> <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'"> <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> <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"><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> </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> <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" %} {% 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"> <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'"> <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> <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> </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> <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" %} {% 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"> <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'"> <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> <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> </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> <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" %} {% 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="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"> <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'"> <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> <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"><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> </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> <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" %} {% 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="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"> <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'"> <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> <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"><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> </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> <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" %} {% include "components/widgets/categories.njk" %}

View File

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

View File

@@ -5,7 +5,7 @@
<is-land on:visible> <is-land on:visible>
<div class="widget"> <div class="widget">
<h3 class="widget-title flex items-center gap-2"> <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"/> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 10V3L4 14h7v7l9-11h-7z"/>
</svg> </svg>
AI Transparency AI Transparency
@@ -15,19 +15,19 @@
<div class="grid grid-cols-2 gap-2 mb-3"> <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-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-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>
<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-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-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>
<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-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-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>
<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-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-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>
</div> </div>
@@ -49,13 +49,13 @@
{# Compact post-graph — current year only, AI posts highlighted #} {# Compact post-graph — current year only, AI posts highlighted #}
{% if aiPostsList and aiPostsList.length %} {% 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" } %} {% postGraph aiPostsList, { prefix: "ai-widget", limit: 1, noLabels: true, boxColorDark: "#44403c", highlightColorLight: "#d97706", highlightColorDark: "#fbbf24" } %}
{% endif %} {% endif %}
<a href="/ai/" class="text-sm text-amber-600 dark:text-amber-400 hover:underline flex items-center gap-1 mt-3"> <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 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> </a>
</div> </div>
</is-land> </is-land>

View File

@@ -4,10 +4,10 @@
<div class="h-card p-author flex items-center gap-3"> <div class="h-card p-author flex items-center gap-3">
{# Hidden u-photo for reliable microformat parsing #} {# Hidden u-photo for reliable microformat parsing #}
<data class="u-photo hidden" value="{{ site.author.avatar }}"></data> <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 <img
src="{{ site.author.avatar }}" src="{{ site.author.avatar }}"
alt="{{ site.author.name }}" alt=""
class="w-12 h-12 rounded-full object-cover shadow-lg" class="w-12 h-12 rounded-full object-cover shadow-lg"
loading="lazy" 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"> <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 }} {{ site.author.name }}
</a> </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 %} {% 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 %} {% endif %}
</div> </div>
</div> </div>

View File

@@ -15,7 +15,7 @@
@click="activeTab = tab.key" @click="activeTab = tab.key"
:class="activeTab === tab.key :class="activeTab === tab.key
? 'border-b-2 border-accent-600 text-accent-600 dark:text-accent-400 dark:border-accent-400' ? '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" class="px-2 py-1 text-xs font-medium transition-colors -mb-px"
x-text="tab.label + ' (' + tab.count + ')'" x-text="tab.label + ' (' + tab.count + ')'"
></button> ></button>
@@ -40,7 +40,7 @@
</template> </template>
</ul> </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. No blogs loaded yet.
</div> </div>

View File

@@ -20,7 +20,7 @@
<is-land on:visible> <is-land on:visible>
<div class="widget" x-data="fediverseInteract('{{ actorUrl }}', 'interact')"> <div class="widget" x-data="fediverseInteract('{{ actorUrl }}', 'interact')">
<h3 class="widget-title">Follow Me</h3> <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 }}" <a href="{{ actorUrl }}"
@click="handleClick($event)" @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" 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 #} {# Sort links #}
<div class="fl-sort"> <div class="fl-sort">
<span :class="sortBy === 'title' ? 'selected' : ''" @click="sortBy = 'title'">Title</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'">When</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> </div>
{# Feed list — pure divs, no table #} {# Feed list — pure divs, no table #}
@@ -253,7 +253,13 @@
<span class="fl-caret" <span class="fl-caret"
:class="expandedId === blog.id ? 'fl-caret-dark' : (selectedId === blog.id ? 'fl-caret-dark' : 'fl-caret-light')" :class="expandedId === blog.id ? 'fl-caret-dark' : (selectedId === blog.id ? 'fl-caret-dark' : 'fl-caret-light')"
x-text="expandedId === blog.id ? '\u25BC' : '\u25B6'" 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"> <span class="fl-name">
<a :href="blog.siteUrl || blog.feedUrl" target="_blank" rel="noopener" <a :href="blog.siteUrl || blog.feedUrl" target="_blank" rel="noopener"
x-text="blog.title" @click.stop></a> x-text="blog.title" @click.stop></a>

View File

@@ -68,7 +68,7 @@
{{ listening.track }} {{ listening.track }}
{% endif %} {% endif %}
</p> </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> <span class="text-purple-500 ml-1">Funkwhale</span>
</p> </p>
</div> </div>
@@ -97,7 +97,7 @@
{% endif %} {% endif %}
{% if scrobble.loved %}<span class="text-red-500 ml-0.5">&#9829;</span>{% endif %} {% if scrobble.loved %}<span class="text-red-500 ml-0.5">&#9829;</span>{% endif %}
</p> </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> <span class="text-red-500 ml-1">Last.fm</span>
</p> </p>
</div> </div>

View File

@@ -9,31 +9,39 @@
</h3> </h3>
{# Tab buttons — order: Commits, Repos, Featured, PRs #} {# 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 <button
@click="activeTab = 'commits'" @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" class="flex items-center gap-1.5 px-2 py-2 text-xs font-medium transition-colors -mb-px"
> >
Commits Commits
</button> </button>
<button <button
@click="activeTab = 'repos'" @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" class="flex items-center gap-1.5 px-2 py-2 text-xs font-medium transition-colors -mb-px"
> >
Repos Repos
</button> </button>
<button <button
@click="activeTab = 'featured'" @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" class="flex items-center gap-1.5 px-2 py-2 text-xs font-medium transition-colors -mb-px"
> >
Featured Featured
</button> </button>
<button <button
@click="activeTab = 'prs'" @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" class="flex items-center gap-1.5 px-2 py-2 text-xs font-medium transition-colors -mb-px"
> >
PRs PRs
@@ -44,18 +52,18 @@
<div class="h-[420px] overflow-y-auto"> <div class="h-[420px] overflow-y-auto">
{# Loading state #} {# 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... Loading...
</div> </div>
{# Commits Tab #} {# 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"> <ul x-show="commits.length > 0" class="space-y-3">
<template x-for="commit in commits.slice(0, 5)" :key="commit.sha"> <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"> <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"> <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> <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> <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="truncate" x-text="commit.repo?.split('/')[1] || commit.repo"></span>
<span class="font-mono" x-text="formatDate(commit.date)"></span> <span class="font-mono" x-text="formatDate(commit.date)"></span>
@@ -64,11 +72,11 @@
</li> </li>
</template> </template>
</ul> </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> </div>
{# Repos Tab #} {# 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"> <ul x-show="repos.length > 0" class="space-y-3">
<template x-for="repo in repos.slice(0, 5)" :key="repo.name"> <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"> <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> <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> </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> <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"> <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> <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> <span x-text="repo.stargazers_count"></span>
@@ -89,11 +97,11 @@
</li> </li>
</template> </template>
</ul> </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> </div>
{# Featured Tab #} {# 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"> <ul x-show="featured.length > 0" class="space-y-3">
<template x-for="repo in featured.slice(0, 5)" :key="repo.fullName || repo.name"> <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"> <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> <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> </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> <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"> <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> <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> <span x-text="repo.stars"></span>
@@ -117,11 +125,11 @@
</li> </li>
</template> </template>
</ul> </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> </div>
{# PRs Tab #} {# 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"> <ul x-show="contributions.length > 0" class="space-y-3">
<template x-for="item in contributions.slice(0, 5)" :key="item.url"> <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"> <li class="border-b border-surface-200 dark:border-surface-700 pb-3 last:border-0">
@@ -136,7 +144,7 @@
</span> </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> <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>
<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-text="item.repo?.split('/')[1] || item.repo"></span>
<span x-show="item.number" x-text="'#' + item.number"></span> <span x-show="item.number" x-text="'#' + item.number"></span>
<span class="font-mono" x-text="formatDate(item.date)"></span> <span class="font-mono" x-text="formatDate(item.date)"></span>
@@ -145,7 +153,7 @@
</li> </li>
</template> </template>
</ul> </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>
</div> </div>

View File

@@ -10,7 +10,7 @@
<div class="space-y-3"> <div class="space-y-3">
{% if _prevPost %} {% if _prevPost %}
<div class="border-b border-surface-200 dark:border-surface-700 pb-3"> <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 _likedUrl = _prevPost.data.likeOf or _prevPost.data.like_of %}
{% set _bookmarkedUrl = _prevPost.data.bookmarkOf or _prevPost.data.bookmark_of %} {% set _bookmarkedUrl = _prevPost.data.bookmarkOf or _prevPost.data.bookmark_of %}
{% set _repostedUrl = _prevPost.data.repostOf or _prevPost.data.repost_of %} {% set _repostedUrl = _prevPost.data.repostOf or _prevPost.data.repost_of %}
@@ -36,7 +36,7 @@
{% endif %} {% endif %}
{% if _nextPost %} {% if _nextPost %}
<div> <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 _likedUrl = _nextPost.data.likeOf or _nextPost.data.like_of %}
{% set _bookmarkedUrl = _nextPost.data.bookmarkOf or _nextPost.data.bookmark_of %} {% set _bookmarkedUrl = _nextPost.data.bookmarkOf or _nextPost.data.bookmark_of %}
{% set _repostedUrl = _nextPost.data.repostOf or _nextPost.data.repost_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"> <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) }} Liked {{ _likedUrl | replace("https://", "") | truncate(40) }}
</a> </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 }} {{ (post.data.published or post.date) | dateDisplay }}
</time> </time>
</div> </div>
@@ -33,7 +33,7 @@
<a href="{{ post.url }}" class="text-sm text-accent-600 dark:text-accent-400 hover:underline line-clamp-1"> <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))) }} {{ post.data.title or ("Bookmarked " + (_bookmarkedUrl | replace("https://", "") | truncate(35))) }}
</a> </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 }} {{ (post.data.published or post.date) | dateDisplay }}
</time> </time>
</div> </div>
@@ -46,7 +46,7 @@
<a href="{{ post.url }}" class="text-sm text-accent-600 dark:text-accent-400 hover:underline line-clamp-1"> <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) }} Reposted {{ _repostedUrl | replace("https://", "") | truncate(40) }}
</a> </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 }} {{ (post.data.published or post.date) | dateDisplay }}
</time> </time>
</div> </div>
@@ -59,7 +59,7 @@
<a href="{{ post.url }}" class="text-sm text-sky-600 dark:text-sky-400 hover:underline line-clamp-1"> <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) }} Reply to {{ _replyToUrl | replace("https://", "") | truncate(40) }}
</a> </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 }} {{ (post.data.published or post.date) | dateDisplay }}
</time> </time>
</div> </div>
@@ -69,7 +69,7 @@
<a href="{{ post.url }}" class="text-sm text-accent-600 dark:text-accent-400 hover:underline line-clamp-2"> <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" }} {{ post.data.title or post.data.name or (post.templateContent | striptags | truncate(50)) or "Note" }}
</a> </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 }} {{ (post.data.published or post.date) | dateDisplay }}
</time> </time>
{% endif %} {% 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"> <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) }} Liked {{ likedUrl | replace("https://", "") | truncate(40) }}
</a> </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 }} {{ (post.data.published or post.date) | dateDisplay }}
</time> </time>
</div> </div>
@@ -37,7 +37,7 @@
<a href="{{ post.url }}" class="text-sm text-amber-600 dark:text-amber-400 hover:underline line-clamp-1"> <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))) }} {{ post.data.title or ("Bookmarked " + (bookmarkedUrl | replace("https://", "") | truncate(35))) }}
</a> </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 }} {{ (post.data.published or post.date) | dateDisplay }}
</time> </time>
</div> </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"> <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) }} Reposted {{ repostedUrl | replace("https://", "") | truncate(40) }}
</a> </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 }} {{ (post.data.published or post.date) | dateDisplay }}
</time> </time>
</div> </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"> <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) }} Reply to {{ replyToUrl | replace("https://", "") | truncate(40) }}
</a> </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 }} {{ (post.data.published or post.date) | dateDisplay }}
</time> </time>
</div> </div>
@@ -83,7 +83,7 @@
<a href="{{ post.url }}" class="text-sm text-indigo-600 dark:text-indigo-400 hover:underline line-clamp-1"> <a href="{{ post.url }}" class="text-sm text-indigo-600 dark:text-indigo-400 hover:underline line-clamp-1">
{{ post.data.title }} {{ post.data.title }}
</a> </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 }} {{ (post.data.published or post.date) | dateDisplay }}
</time> </time>
</div> </div>
@@ -99,7 +99,7 @@
<a href="{{ post.url }}" class="text-sm text-teal-600 dark:text-teal-400 hover:underline line-clamp-1"> <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" }} {{ post.templateContent | striptags | truncate(50) or "Note" }}
</a> </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 }} {{ (post.data.published or post.date) | dateDisplay }}
</time> </time>
</div> </div>

View File

@@ -8,7 +8,8 @@
target="_blank" target="_blank"
rel="noopener" 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" 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"> <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"/> <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> </svg>
@@ -17,7 +18,8 @@
<a href="https://share.joinmastodon.org/#text={{ shareText | urlencode }}" <a href="https://share.joinmastodon.org/#text={{ shareText | urlencode }}"
@click="handleClick($event)" @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" 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"> <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"/> <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> </svg>

View File

@@ -5,11 +5,13 @@
<h3 class="widget-title">Social Activity</h3> <h3 class="widget-title">Social Activity</h3>
{# Tab buttons #} {# 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 %} {% if blueskyFeed and blueskyFeed.length %}
<button <button
@click="activeTab = 'bluesky'" @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" 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"> <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 %} {% if mastodonFeed and mastodonFeed.length %}
<button <button
@click="activeTab = 'mastodon'" @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" 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"> <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 #} {# Bluesky Tab Content #}
{% if blueskyFeed and blueskyFeed.length %} {% 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"> <ul class="space-y-3">
{% for post in blueskyFeed | head(5) %} {% for post in blueskyFeed | head(5) %}
<li class="border-b border-surface-200 dark:border-surface-700 pb-3 last:border-0"> <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"> <p class="text-sm text-surface-700 dark:text-surface-300 group-hover:text-[#0085ff] transition-colors">
{{ post.text | truncate(140) }} {{ post.text | truncate(140) }}
</p> </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> <time class="font-mono" datetime="{{ post.createdAt }}">{{ post.createdAt | date("MMM d, yyyy") }}</time>
{% if post.likeCount > 0 %} {% if post.likeCount > 0 %}
<span class="flex items-center gap-1"> <span class="flex items-center gap-1">
@@ -64,7 +68,7 @@
{# Mastodon Tab Content #} {# Mastodon Tab Content #}
{% if mastodonFeed and mastodonFeed.length %} {% 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"> <ul class="space-y-3">
{% for post in mastodonFeed | head(5) %} {% for post in mastodonFeed | head(5) %}
<li class="border-b border-surface-200 dark:border-surface-700 pb-3 last:border-0"> <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"> <p class="text-sm text-surface-700 dark:text-surface-300 group-hover:text-[#a730b8] transition-colors">
{{ post.text | truncate(140) }} {{ post.text | truncate(140) }}
</p> </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> <time class="font-mono" datetime="{{ post.createdAt }}">{{ post.createdAt | date("MMM d, yyyy") }}</time>
{% if post.favouritesCount > 0 %} {% if post.favouritesCount > 0 %}
<span class="flex items-center gap-1"> <span class="flex items-center gap-1">

View File

@@ -4,13 +4,13 @@
<h3 class="widget-title">Subscribe</h3> <h3 class="widget-title">Subscribe</h3>
<div class="space-y-2"> <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"> <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"/> <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> </svg>
RSS Feed RSS Feed
</a> </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"> <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"/> <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> </svg>
JSON Feed JSON Feed

View File

@@ -3,7 +3,7 @@
<is-land on:visible> <is-land on:visible>
<div class="widget"> <div class="widget">
<h3 class="widget-title">Contents</h3> <h3 class="widget-title">Contents</h3>
<nav class="toc"> <nav class="toc" aria-label="Table of contents">
<ul class="space-y-1 text-sm"> <ul class="space-y-1 text-sm">
{% for item in toc %} {% for item in toc %}
<li class="{% if item.level > 2 %}ml-{{ (item.level - 2) * 3 }}{% endif %}"> <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"> <div class="flex border-b border-surface-200 dark:border-surface-700 mb-3">
<button <button
@click="tab = 'inbound'" @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"> class="px-2 py-1.5 text-xs font-medium border-b-2 -mb-px transition-colors">
Received Received
<span x-show="mentions.length" x-text="mentions.length" class="ml-0.5 text-xs opacity-75"></span> <span x-show="mentions.length" x-text="mentions.length" class="ml-0.5 text-xs opacity-75"></span>
</button> </button>
<button <button
@click="tab = 'outbound'" @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"> class="px-2 py-1.5 text-xs font-medium border-b-2 -mb-px transition-colors">
Sent Sent
</button> </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'] === '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'] === 'mention-of'" class="text-amber-500"> mentioned</span>
<span x-show="wm['wm-property'] === 'bookmark-of'" class="text-purple-500"> bookmarked</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>
</div> </div>
</template> </template>
</div> </div>
{# Empty #} {# 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 #} {# Error #}
<p x-show="error" class="text-xs text-red-500 py-2" x-text="error"></p> <p x-show="error" class="text-xs text-red-500 py-2" x-text="error"></p>

View File

@@ -135,18 +135,19 @@
} }
})(); })();
</script> </script>
<a href="#main-content" class="skip-link">Skip to main content</a>
<header class="site-header"> <header class="site-header">
<div class="container header-container"> <div class="container header-container">
<a href="/" class="site-title">{{ site.name }}</a> <a href="/" class="site-title">{{ site.name }}</a>
{# Mobile menu button #} {# Mobile menu button #}
<button id="menu-toggle" type="button" class="menu-toggle" aria-label="Toggle menu" aria-expanded="false"> <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="6" x2="21" y2="6"/>
<line x1="3" y1="12" x2="21" y2="12"/> <line x1="3" y1="12" x2="21" y2="12"/>
<line x1="3" y1="18" x2="21" y2="18"/> <line x1="3" y1="18" x2="21" y2="18"/>
</svg> </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="18" y1="6" x2="6" y2="18"/>
<line x1="6" y1="6" x2="18" y2="18"/> <line x1="6" y1="6" x2="18" y2="18"/>
</svg> </svg>
@@ -159,33 +160,33 @@
<a href="/about/">About</a> <a href="/about/">About</a>
<a href="/now/">Now</a> <a href="/now/">Now</a>
{# Blog dropdown #} {# Blog dropdown #}
<div class="nav-dropdown" x-data="{ open: false }" @mouseenter="open = true" @mouseleave="open = false"> <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"> <a href="/blog/" class="nav-dropdown-trigger" :aria-expanded="open.toString()" aria-haspopup="true">
Blog 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"/> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"/>
</svg> </svg>
</a> </a>
<div class="nav-dropdown-menu" x-show="open" x-transition x-cloak> <div class="nav-dropdown-menu" x-show="open" x-transition x-cloak role="menu">
<a href="/blog/">All Posts</a> <a href="/blog/" role="menuitem">All Posts</a>
{% for pt in enabledPostTypes %} {% for pt in enabledPostTypes %}
<a href="{{ pt.path }}">{{ pt.label }}</a> <a href="{{ pt.path }}" role="menuitem">{{ pt.label }}</a>
{% endfor %} {% endfor %}
</div> </div>
</div> </div>
{# Pages dropdown #} {# Pages dropdown #}
<div class="nav-dropdown" x-data="{ open: false }" @mouseenter="open = true" @mouseleave="open = false"> <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"> <a href="/slashes/" class="nav-dropdown-trigger" :aria-expanded="open.toString()" aria-haspopup="true">
Pages 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"/> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"/>
</svg> </svg>
</a> </a>
<div class="nav-dropdown-menu" x-show="open" x-transition x-cloak> <div class="nav-dropdown-menu" x-show="open" x-transition x-cloak role="menu">
{% if blogrollStatus and blogrollStatus.source == "indiekit" %}<a href="/blogroll/">Blogroll</a>{% endif %} {% if blogrollStatus and blogrollStatus.source == "indiekit" %}<a href="/blogroll/" role="menuitem">Blogroll</a>{% endif %}
{% if podrollStatus and podrollStatus.source == "indiekit" %}<a href="/podroll/">Podroll</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/">News</a>{% endif %} {% if newsActivity and newsActivity.source == "indiekit" %}<a href="/news/" role="menuitem">News</a>{% endif %}
<a href="/slashes/">All Pages</a> <a href="/slashes/" role="menuitem">All Pages</a>
</div> </div>
</div> </div>
<a href="/interactions/">Interactions</a> <a href="/interactions/">Interactions</a>
@@ -196,23 +197,23 @@
x-transition x-transition
@indiekit:auth.window="show = $event.detail.loggedIn" @indiekit:auth.window="show = $event.detail.loggedIn"
class="admin-nav-link"> 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"/> <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> </svg>
Dashboard Dashboard
</a> </a>
</nav> </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"> <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"/> <circle cx="11" cy="11" r="8"/><path d="M21 21l-4.35-4.35"/>
</svg> </svg>
</a> </a>
<button id="theme-toggle" type="button" class="theme-toggle" aria-label="Toggle dark mode" title="Toggle dark mode"> <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"/> <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"/> <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>
<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"/> <path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"/>
</svg> </svg>
</button> </button>
@@ -226,13 +227,13 @@
<a href="/now/">Now</a> <a href="/now/">Now</a>
{# Blog section #} {# Blog section #}
<div class="mobile-nav-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 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"/> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"/>
</svg> </svg>
</button> </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> <a href="/blog/">All Posts</a>
{% for pt in enabledPostTypes %} {% for pt in enabledPostTypes %}
<a href="{{ pt.path }}">{{ pt.label }}</a> <a href="{{ pt.path }}">{{ pt.label }}</a>
@@ -241,13 +242,13 @@
</div> </div>
{# Pages section #} {# Pages section #}
<div class="mobile-nav-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 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"/> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"/>
</svg> </svg>
</button> </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 blogrollStatus and blogrollStatus.source == "indiekit" %}<a href="/blogroll/">Blogroll</a>{% endif %}
{% if podrollStatus and podrollStatus.source == "indiekit" %}<a href="/podroll/">Podroll</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 %} {% 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"> <button type="button" class="mobile-theme-toggle" aria-label="Toggle dark mode">
<span class="theme-label">Theme</span> <span class="theme-label">Theme</span>
<span class="theme-icons"> <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"/> <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"/> <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>
<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"/> <path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"/>
</svg> </svg>
</span> </span>
@@ -279,7 +280,7 @@
</nav> </nav>
</header> </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 #} {# Skeleton loader — shown until Tailwind stylesheet loads #}
<div class="page-skeleton" aria-hidden="true"> <div class="page-skeleton" aria-hidden="true">
<div style="display:flex;gap:1.5rem;align-items:flex-start;margin-bottom:2rem"> <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"> <div class="grid grid-cols-2 md:grid-cols-4 gap-8 mb-8">
{# Navigate #} {# Navigate #}
<div> <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"> <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="/" 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> <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> </div>
{# Content #} {# Content #}
<div> <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"> <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> <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 %} {% for pt in enabledPostTypes %}
@@ -350,7 +351,7 @@
</div> </div>
{# Connect #} {# Connect #}
<div> <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"> <ul class="space-y-2">
{% for social in site.social %} {% 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> <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> </div>
{# Meta #} {# Meta #}
<div> <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"> <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.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> <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> </ul>
</div> </div>
</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> </div>
</footer> </footer>
<script> <script>
@@ -469,43 +470,43 @@
x-transition:leave="transition ease-in duration-150" x-transition:leave="transition ease-in duration-150"
x-transition:leave-start="opacity-100 translate-y-0" x-transition:leave-start="opacity-100 translate-y-0"
x-transition:leave-end="opacity-0 translate-y-4" x-transition:leave-end="opacity-0 translate-y-4"
class="fab-menu"> class="fab-menu" role="menu" aria-label="Create new post">
{% if mpUrl %} {% if mpUrl %}
<a href="/posts/edit?url={{ mpUrl | urlencode }}" @click="open = false" class="fab-menu-item" rel="nofollow"> <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"> <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="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"/> <path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z"/>
</svg> </svg>
<span>Edit this post</span> <span>Edit this post</span>
</a> </a>
<div class="fab-menu-divider"></div> <div class="fab-menu-divider" role="separator"></div>
{% endif %} {% endif %}
<a href="/posts/create?type=page" @click="open = false" class="fab-menu-item" rel="nofollow"> <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-500" fill="none" stroke="currentColor" viewBox="0 0 24 24" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> <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"/> <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> </svg>
<span>Page</span> <span>Page</span>
</a> </a>
<a href="/posts/create?type=bookmark" @click="open = false" class="fab-menu-item" rel="nofollow"> <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-500" fill="none" stroke="currentColor" viewBox="0 0 24 24" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> <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"/> <path d="M19 21l-7-5-7 5V5a2 2 0 0 1 2-2h10a2 2 0 0 1 2 2z"/>
</svg> </svg>
<span>Bookmark</span> <span>Bookmark</span>
</a> </a>
<a href="/posts/create?type=photo" @click="open = false" class="fab-menu-item" rel="nofollow"> <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-500" fill="none" stroke="currentColor" viewBox="0 0 24 24" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> <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"/> <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> </svg>
<span>Photo</span> <span>Photo</span>
</a> </a>
<a href="/posts/create?type=article" @click="open = false" class="fab-menu-item" rel="nofollow"> <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-500" fill="none" stroke="currentColor" viewBox="0 0 24 24" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> <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"/> <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> </svg>
<span>Article</span> <span>Article</span>
</a> </a>
<a href="/posts/create?type=note" @click="open = false" class="fab-menu-item" rel="nofollow"> <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-500" fill="none" stroke="currentColor" viewBox="0 0 24 24" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> <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"/> <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> </svg>
<span>Note</span> <span>Note</span>
@@ -515,8 +516,9 @@
<button @click="open = !open" <button @click="open = !open"
class="fab-button" class="fab-button"
:aria-expanded="open" :aria-expanded="open"
aria-label="Create new post"> 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-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"/> <line x1="12" y1="5" x2="12" y2="19"/><line x1="5" y1="12" x2="19" y2="12"/>
</svg> </svg>
</button> </button>

View File

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

View File

@@ -16,7 +16,7 @@ withSidebar: true
</p> </p>
{% endif %} {% endif %}
{% if updated %} {% 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> Last updated: <time class="dt-updated font-mono text-sm" datetime="{{ updated | isoDate }}">{{ updated | dateDisplay }}</time>
</p> </p>
{% endif %} {% endif %}
@@ -35,19 +35,19 @@ withSidebar: true
<div class="grid gap-4 sm:grid-cols-4 mb-6"> <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-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-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>
<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-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-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>
<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-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-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>
<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-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-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>
</div> </div>
@@ -69,7 +69,7 @@ withSidebar: true
{# Post graph showing AI posts (highlighted) on the full year grid #} {# 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> <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" } %} {% postGraph aiPostsList, { prefix: "ai", highlightColorLight: "#d97706", highlightColorDark: "#fbbf24" } %}
</section> </section>
{% endif %} {% endif %}
@@ -82,7 +82,7 @@ withSidebar: true
{% if aiTextLevel or aiCodeLevel or aiTools %} {% 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"> <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"> <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"/> <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> </svg>
<strong class="text-sm font-semibold text-surface-700 dark:text-surface-300">AI Usage</strong> <strong class="text-sm font-semibold text-surface-700 dark:text-surface-300">AI Usage</strong>
@@ -105,7 +105,7 @@ withSidebar: true
{% endif %} {% endif %}
</div> </div>
{% if aiDescription %} {% 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 %} {% 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> <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> </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> <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 %} {% else %}
<div class="flex items-center gap-2 mb-1"> <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 %} {% if replyTo %}&#8617; Reply{% elif likedUrl %}&#9829; Like{% elif repostedUrl %}&#9851; Repost{% elif bookmarkedUrl %}&#128278; Bookmark{% else %}&#9998; Note{% endif %}
</span> </span>
</div> </div>
@@ -73,7 +73,7 @@ withBlogSidebar: true
{% set aiCodeLevel = aiCodeLevel or ai_code_level %} {% set aiCodeLevel = aiCodeLevel or ai_code_level %}
{% set aiTools = aiTools or ai_tools %} {% set aiTools = aiTools or ai_tools %}
{% set aiDescription = aiDescription or ai_description %} {% 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"> <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"> <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"/> <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 %} {% if externalSyndication.length or selfHostedApUrl %}
<footer class="post-footer mt-8 pt-6 border-t border-surface-200 dark:border-surface-700"> <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"> <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"> <div class="flex flex-wrap gap-3">
{# Fediverse remote interaction button (self-hosted ActivityPub) #} {# Fediverse remote interaction button (self-hosted ActivityPub) #}
{% if selfHostedApUrl %} {% if selfHostedApUrl %}
@@ -269,9 +269,10 @@ withBlogSidebar: true
{# Lightbox overlay for article images #} {# Lightbox overlay for article images #}
<template x-teleport="body"> <template x-teleport="body">
<div x-show="open" x-transition.opacity.duration.200ms <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" class="fixed inset-0 z-[9999] flex items-center justify-center bg-black/90 backdrop-blur-sm"
@click.self="close()"> @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"> <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> <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> </template>

View File

@@ -72,7 +72,7 @@ permalink: "articles/{% if pagination.pageNumber > 0 %}page/{{ pagination.pageNu
Previous Previous
</a> </a>
{% else %} {% else %}
<span class="pagination-link disabled"> <span class="pagination-link disabled" aria-disabled="true">
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 19l-7-7 7-7"></path></svg> <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 19l-7-7 7-7"></path></svg>
Previous Previous
</span> </span>
@@ -84,7 +84,7 @@ permalink: "articles/{% if pagination.pageNumber > 0 %}page/{{ pagination.pageNu
<svg class="w-4 h-4" 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"></path></svg> <svg class="w-4 h-4" 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"></path></svg>
</a> </a>
{% else %} {% else %}
<span class="pagination-link disabled"> <span class="pagination-link disabled" aria-disabled="true">
Next Next
<svg class="w-4 h-4" 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"></path></svg> <svg class="w-4 h-4" 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"></path></svg>
</span> </span>

View File

@@ -26,7 +26,7 @@ permalink: "blog/{% if pagination.pageNumber > 0 %}page/{{ pagination.pageNumber
<a href="/blog/" class="px-3 py-1.5 text-sm font-medium rounded-full bg-accent-600 text-white dark:bg-accent-700">All Posts <span class="opacity-75">({{ collections.posts.length }})</span></a> <a href="/blog/" class="px-3 py-1.5 text-sm font-medium rounded-full bg-accent-600 text-white dark:bg-accent-700">All Posts <span class="opacity-75">({{ collections.posts.length }})</span></a>
{% for pt in enabledPostTypes %} {% for pt in enabledPostTypes %}
{% set collName = pt.label | lower %} {% set collName = pt.label | lower %}
<a href="{{ pt.path }}" class="px-3 py-1.5 text-sm font-medium rounded-full bg-surface-100 dark:bg-surface-800 text-surface-600 dark:text-surface-300 hover:bg-surface-200 dark:hover:bg-surface-700 border border-surface-200 dark:border-surface-700 transition-colors">{{ pt.label }}{% if collections[collName] %} <span class="text-surface-400 dark:text-surface-500">({{ collections[collName].length }})</span>{% endif %}</a> <a href="{{ pt.path }}" class="px-3 py-1.5 text-sm font-medium rounded-full bg-surface-100 dark:bg-surface-800 text-surface-600 dark:text-surface-300 hover:bg-surface-200 dark:hover:bg-surface-700 border border-surface-200 dark:border-surface-700 transition-colors">{{ pt.label }}{% if collections[collName] %} <span class="text-surface-600 dark:text-surface-400">({{ collections[collName].length }})</span>{% endif %}</a>
{% endfor %} {% endfor %}
</nav> </nav>
<ul class="post-list"> <ul class="post-list">
@@ -82,7 +82,7 @@ permalink: "blog/{% if pagination.pageNumber > 0 %}page/{{ pagination.pageNumber
{% endif %} {% endif %}
</div> </div>
{% unfurl likedUrl %} {% unfurl likedUrl %}
<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-600 dark:text-surface-400 hover:underline break-all mt-1 inline-block" href="{{ likedUrl }}">
{{ likedUrl }} {{ likedUrl }}
</a> </a>
{% if post.templateContent %} {% if post.templateContent %}
@@ -126,7 +126,7 @@ permalink: "blog/{% if pagination.pageNumber > 0 %}page/{{ pagination.pageNumber
</h2> </h2>
{% endif %} {% endif %}
{% unfurl bookmarkedUrl %} {% unfurl bookmarkedUrl %}
<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-600 dark:text-surface-400 hover:underline break-all mt-1 inline-block" href="{{ bookmarkedUrl }}">
{{ bookmarkedUrl }} {{ bookmarkedUrl }}
</a> </a>
{% if post.templateContent %} {% if post.templateContent %}
@@ -165,7 +165,7 @@ permalink: "blog/{% if pagination.pageNumber > 0 %}page/{{ pagination.pageNumber
{% endif %} {% endif %}
</div> </div>
{% unfurl repostedUrl %} {% unfurl repostedUrl %}
<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-600 dark:text-surface-400 hover:underline break-all mt-1 inline-block" href="{{ repostedUrl }}">
{{ repostedUrl }} {{ repostedUrl }}
</a> </a>
{% if post.templateContent %} {% if post.templateContent %}
@@ -204,7 +204,7 @@ permalink: "blog/{% if pagination.pageNumber > 0 %}page/{{ pagination.pageNumber
{% endif %} {% endif %}
</div> </div>
{% unfurl replyToUrl %} {% unfurl replyToUrl %}
<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-600 dark:text-surface-400 hover:underline break-all mt-1 inline-block" href="{{ replyToUrl }}">
{{ replyToUrl }} {{ replyToUrl }}
</a> </a>
<div class="e-content prose dark:prose-invert prose-sm mt-3 max-w-none"> <div class="e-content prose dark:prose-invert prose-sm mt-3 max-w-none">
@@ -297,7 +297,7 @@ permalink: "blog/{% if pagination.pageNumber > 0 %}page/{{ pagination.pageNumber
{# ── Note card (unchanged) ── #} {# ── Note card (unchanged) ── #}
<div class="post-header"> <div class="post-header">
<a class="u-url" href="{{ post.url }}"> <a class="u-url" href="{{ post.url }}">
<time-difference><time class="dt-published text-sm text-surface-500 dark:text-surface-400 font-medium font-mono" datetime="{{ post.date | isoDate }}"> <time-difference><time class="dt-published text-sm text-surface-600 dark:text-surface-400 font-medium font-mono" datetime="{{ post.date | isoDate }}">
{{ post.date | dateDisplay }} {{ post.date | dateDisplay }}
</time></time-difference> </time></time-difference>
</a> </a>
@@ -327,7 +327,7 @@ permalink: "blog/{% if pagination.pageNumber > 0 %}page/{{ pagination.pageNumber
{% set postAiText = post.data.aiTextLevel or post.data.ai_text_level %} {% set postAiText = post.data.aiTextLevel or post.data.ai_text_level %}
{% set postAiCode = post.data.aiCodeLevel or post.data.ai_code_level %} {% set postAiCode = post.data.aiCodeLevel or post.data.ai_code_level %}
{% if (postAiText and postAiText !== "0") or (postAiCode and postAiCode !== "0") %} {% if (postAiText and postAiText !== "0") or (postAiCode and postAiCode !== "0") %}
<span class="inline-flex items-center gap-1 mt-2 px-1.5 py-0.5 rounded text-[10px] font-medium bg-surface-100 dark:bg-surface-700 text-surface-500 dark:text-surface-400" title="AI usage: Text level {{ postAiText or '' }}, Code level {{ postAiCode or '' }}"> <span class="inline-flex items-center gap-1 mt-2 px-1.5 py-0.5 rounded text-[10px] font-medium bg-surface-100 dark:bg-surface-700 text-surface-600 dark:text-surface-400" title="AI usage: Text level {{ postAiText or '' }}, Code level {{ postAiCode or '' }}">
<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.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> <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.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>
AI{% if postAiText %}: T{{ postAiText }}{% endif %}{% if postAiCode %}/C{{ postAiCode }}{% endif %} AI{% if postAiText %}: T{{ postAiText }}{% endif %}{% if postAiCode %}/C{{ postAiCode }}{% endif %}
</span> </span>
@@ -349,7 +349,7 @@ permalink: "blog/{% if pagination.pageNumber > 0 %}page/{{ pagination.pageNumber
Previous Previous
</a> </a>
{% else %} {% else %}
<span class="pagination-link disabled"> <span class="pagination-link disabled" aria-disabled="true">
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 19l-7-7 7-7"></path></svg> <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 19l-7-7 7-7"></path></svg>
Previous Previous
</span> </span>
@@ -361,7 +361,7 @@ permalink: "blog/{% if pagination.pageNumber > 0 %}page/{{ pagination.pageNumber
<svg class="w-4 h-4" 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"></path></svg> <svg class="w-4 h-4" 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"></path></svg>
</a> </a>
{% else %} {% else %}
<span class="pagination-link disabled"> <span class="pagination-link disabled" aria-disabled="true">
Next Next
<svg class="w-4 h-4" 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"></path></svg> <svg class="w-4 h-4" 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"></path></svg>
</span> </span>

View File

@@ -14,17 +14,19 @@ permalink: /blogroll/
<p class="text-surface-600 dark:text-surface-400"> <p class="text-surface-600 dark:text-surface-400">
Blogs I follow - <span x-text="blogs.length" class="font-medium"></span> feeds Blogs I follow - <span x-text="blogs.length" class="font-medium"></span> feeds
</p> </p>
<p class="text-xs text-surface-500 mt-2" x-show="status?.lastSync"> <p class="text-xs text-surface-600 dark:text-surface-400 mt-2" x-show="status?.lastSync">
Last synced: <span class="font-mono" x-text="formatDate(status?.lastSync, 'full')"></span> Last synced: <span class="font-mono" x-text="formatDate(status?.lastSync, 'full')"></span>
</p> </p>
</header> </header>
{# Tab Navigation - All Blogs first, then one tab per category #} {# Tab Navigation - All Blogs first, then one tab per category #}
<div class="mb-6 border-b border-surface-200 dark:border-surface-700"> <div class="mb-6 border-b border-surface-200 dark:border-surface-700">
<nav class="flex gap-1 overflow-x-auto -mb-px" aria-label="Tabs"> <nav class="flex gap-1 overflow-x-auto -mb-px" role="tablist" aria-label="Blogroll categories">
<button <button
@click="switchTab('blogs')" @click="switchTab('blogs')"
:class="activeTab === 'blogs' ? 'border-orange-500 text-orange-600 dark:text-orange-400' : 'border-transparent text-surface-500 hover:text-surface-700 dark:hover:text-surface-300'" :class="activeTab === 'blogs' ? 'border-orange-500 text-orange-600 dark:text-orange-400' : 'border-transparent text-surface-600 dark:text-surface-400 hover:text-surface-700 dark:hover:text-surface-300'"
:aria-selected="(activeTab === 'blogs').toString()"
role="tab" id="blogroll-tab-blogs" aria-controls="blogroll-panel-blogs"
class="pb-3 px-3 border-b-2 font-medium text-sm transition-colors whitespace-nowrap flex-shrink-0" class="pb-3 px-3 border-b-2 font-medium text-sm transition-colors whitespace-nowrap flex-shrink-0"
> >
All Blogs All Blogs
@@ -32,7 +34,9 @@ permalink: /blogroll/
<template x-for="cat in categories" :key="cat.name"> <template x-for="cat in categories" :key="cat.name">
<button <button
@click="switchTab('category:' + cat.name)" @click="switchTab('category:' + cat.name)"
:class="activeTab === 'category:' + cat.name ? 'border-orange-500 text-orange-600 dark:text-orange-400' : 'border-transparent text-surface-500 hover:text-surface-700 dark:hover:text-surface-300'" :class="activeTab === 'category:' + cat.name ? 'border-orange-500 text-orange-600 dark:text-orange-400' : 'border-transparent text-surface-600 dark:text-surface-400 hover:text-surface-700 dark:hover:text-surface-300'"
:aria-selected="(activeTab === 'category:' + cat.name).toString()"
role="tab" :id="'blogroll-tab-' + cat.name.toLowerCase().replace(/\s+/g, '-')" :aria-controls="'blogroll-panel-' + cat.name.toLowerCase().replace(/\s+/g, '-')"
class="pb-3 px-3 border-b-2 font-medium text-sm transition-colors whitespace-nowrap flex-shrink-0" class="pb-3 px-3 border-b-2 font-medium text-sm transition-colors whitespace-nowrap flex-shrink-0"
> >
<span x-text="cat.name"></span> <span x-text="cat.name"></span>
@@ -46,8 +50,8 @@ permalink: /blogroll/
{# Main Content #} {# Main Content #}
<div class="main-content"> <div class="main-content">
{# Loading State #} {# Loading State #}
<div x-show="loading" class="text-center py-12"> <div x-show="loading" class="text-center py-12" role="status">
<svg class="w-8 h-8 mx-auto text-orange-600 animate-spin mb-4" fill="none" viewBox="0 0 24 24"> <svg class="w-8 h-8 mx-auto text-orange-600 animate-spin mb-4" fill="none" viewBox="0 0 24 24" aria-hidden="true">
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle> <circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
<path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path> <path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
</svg> </svg>
@@ -55,13 +59,13 @@ permalink: /blogroll/
</div> </div>
{# Error State #} {# Error State #}
<div x-show="error" class="bg-red-50 dark:bg-red-900/20 border border-red-200 dark:border-red-800 rounded-lg p-4 mb-6"> <div x-show="error" role="alert" class="bg-red-50 dark:bg-red-900/20 border border-red-200 dark:border-red-800 rounded-lg p-4 mb-6">
<p class="text-red-700 dark:text-red-400" x-text="error"></p> <p class="text-red-700 dark:text-red-400" x-text="error"></p>
<button @click="fetchData()" class="mt-2 text-sm text-red-600 hover:text-red-700 underline">Try again</button> <button @click="fetchData()" class="mt-2 text-sm text-red-600 hover:text-red-700 underline">Try again</button>
</div> </div>
{# All Blogs Tab #} {# All Blogs Tab #}
<div x-show="activeTab === 'blogs' && !loading" class="grid gap-4 sm:grid-cols-2 lg:grid-cols-3"> <div x-show="activeTab === 'blogs' && !loading" role="tabpanel" id="blogroll-panel-blogs" aria-labelledby="blogroll-tab-blogs" class="grid gap-4 sm:grid-cols-2 lg:grid-cols-3">
<template x-for="blog in blogs" :key="blog.id"> <template x-for="blog in blogs" :key="blog.id">
<a <a
:href="blog.siteUrl || blog.feedUrl" :href="blog.siteUrl || blog.feedUrl"
@@ -71,16 +75,16 @@ permalink: /blogroll/
> >
<div class="flex items-center gap-3 mb-2"> <div class="flex items-center gap-3 mb-2">
<div class="w-10 h-10 rounded-lg bg-gradient-to-br from-orange-400 to-orange-600 flex items-center justify-center flex-shrink-0 overflow-hidden"> <div class="w-10 h-10 rounded-lg bg-gradient-to-br from-orange-400 to-orange-600 flex items-center justify-center flex-shrink-0 overflow-hidden">
<img x-show="blog.photo" :src="blog.photo" class="w-10 h-10 object-cover" loading="lazy" /> <img x-show="blog.photo" :src="blog.photo" :alt="blog.title" class="w-10 h-10 object-cover" loading="lazy" />
<span x-show="!blog.photo" class="text-white text-sm font-bold" x-text="blog.title?.charAt(0)?.toUpperCase()"></span> <span x-show="!blog.photo" class="text-white text-sm font-bold" x-text="blog.title?.charAt(0)?.toUpperCase()"></span>
</div> </div>
<div class="flex-1 min-w-0"> <div class="flex-1 min-w-0">
<h3 class="font-medium text-surface-900 dark:text-surface-100 truncate group-hover:text-orange-600 dark:group-hover:text-orange-400 transition-colors" x-text="blog.title"></h3> <h3 class="font-medium text-surface-900 dark:text-surface-100 truncate group-hover:text-orange-600 dark:group-hover:text-orange-400 transition-colors" x-text="blog.title"></h3>
<p x-show="blog.category" class="text-xs text-surface-500 truncate" x-text="blog.category"></p> <p x-show="blog.category" class="text-xs text-surface-600 dark:text-surface-400 truncate" x-text="blog.category"></p>
</div> </div>
</div> </div>
<p x-show="blog.description" class="text-sm text-surface-600 dark:text-surface-400 line-clamp-2 mb-3" x-text="blog.description"></p> <p x-show="blog.description" class="text-sm text-surface-600 dark:text-surface-400 line-clamp-2 mb-3" x-text="blog.description"></p>
<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 x-text="`${blog.itemCount || 0} posts`"></span> <span x-text="`${blog.itemCount || 0} posts`"></span>
<span :class="blog.status === 'active' ? 'text-green-500' : 'text-red-500'"> <span :class="blog.status === 'active' ? 'text-green-500' : 'text-red-500'">
<span x-show="blog.status === 'active'" class="flex items-center gap-1"> <span x-show="blog.status === 'active'" class="flex items-center gap-1">
@@ -99,15 +103,15 @@ permalink: /blogroll/
{# Empty State for Blogs #} {# Empty State for Blogs #}
<div x-show="!loading && blogs.length === 0 && activeTab === 'blogs' && !error" class="text-center py-12"> <div x-show="!loading && blogs.length === 0 && activeTab === 'blogs' && !error" class="text-center py-12">
<svg class="w-16 h-16 mx-auto text-surface-300 dark:text-surface-600 mb-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"> <svg class="w-16 h-16 mx-auto text-surface-300 dark:text-surface-400 mb-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M5 5a2 2 0 012-2h10a2 2 0 012 2v16l-7-3.5L5 21V5z"/> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M5 5a2 2 0 012-2h10a2 2 0 012 2v16l-7-3.5L5 21V5z"/>
</svg> </svg>
<p class="text-surface-600 dark:text-surface-400 text-lg">No blogs yet.</p> <p class="text-surface-600 dark:text-surface-400 text-lg">No blogs yet.</p>
<p class="text-surface-500 text-sm mt-2">Add blogs via the admin dashboard.</p> <p class="text-surface-600 dark:text-surface-400 text-sm mt-2">Add blogs via the admin dashboard.</p>
</div> </div>
{# Category Items Tab (one for each category) #} {# Category Items Tab (one for each category) #}
<div x-show="activeTab.startsWith('category:') && !loading" class="space-y-4"> <div x-show="activeTab.startsWith('category:') && !loading" role="tabpanel" :id="'blogroll-panel-' + activeTab.replace('category:', '').toLowerCase().replace(/\s+/g, '-')" :aria-labelledby="'blogroll-tab-' + activeTab.replace('category:', '').toLowerCase().replace(/\s+/g, '-')" class="space-y-4">
<template x-for="item in categoryItems" :key="item.id"> <template x-for="item in categoryItems" :key="item.id">
<article class="bg-surface-50 dark:bg-surface-800 rounded-lg border border-surface-200 dark:border-surface-700 p-4 sm:p-6 hover:border-orange-400 dark:hover:border-orange-600 transition-colors shadow-sm"> <article class="bg-surface-50 dark:bg-surface-800 rounded-lg border border-surface-200 dark:border-surface-700 p-4 sm:p-6 hover:border-orange-400 dark:hover:border-orange-600 transition-colors shadow-sm">
<div class="flex items-start gap-4"> <div class="flex items-start gap-4">
@@ -126,7 +130,7 @@ permalink: /blogroll/
Upcoming Upcoming
</span> </span>
</div> </div>
<div class="flex flex-wrap items-center gap-2 text-sm text-surface-500 mb-3"> <div class="flex flex-wrap items-center gap-2 text-sm text-surface-600 dark:text-surface-400 mb-3">
<a <a
:href="item.blog?.siteUrl || '#'" :href="item.blog?.siteUrl || '#'"
class="inline-flex items-center gap-1 px-2 py-0.5 bg-orange-100 dark:bg-orange-900/30 text-orange-700 dark:text-orange-400 rounded-full hover:bg-orange-200 dark:hover:bg-orange-900/50 transition-colors" class="inline-flex items-center gap-1 px-2 py-0.5 bg-orange-100 dark:bg-orange-900/30 text-orange-700 dark:text-orange-400 rounded-full hover:bg-orange-200 dark:hover:bg-orange-900/50 transition-colors"
@@ -167,7 +171,7 @@ permalink: /blogroll/
<a <a
x-show="item.blog?.siteUrl" x-show="item.blog?.siteUrl"
:href="item.blog?.siteUrl" :href="item.blog?.siteUrl"
class="inline-flex items-center gap-2 text-sm text-surface-500 hover:text-surface-700 dark:hover:text-surface-300" class="inline-flex items-center gap-2 text-sm text-surface-600 dark:text-surface-400 hover:text-surface-700 dark:hover:text-surface-300"
target="_blank" target="_blank"
rel="noopener" rel="noopener"
> >
@@ -221,11 +225,11 @@ permalink: /blogroll/
{# Empty State for Category Items #} {# Empty State for Category Items #}
<div x-show="categoryItems.length === 0 && !error" class="text-center py-12"> <div x-show="categoryItems.length === 0 && !error" class="text-center py-12">
<svg class="w-16 h-16 mx-auto text-surface-300 dark:text-surface-600 mb-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"> <svg class="w-16 h-16 mx-auto text-surface-300 dark:text-surface-400 mb-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M19 20H5a2 2 0 01-2-2V6a2 2 0 012-2h10a2 2 0 012 2v1m2 13a2 2 0 01-2-2V7m2 13a2 2 0 002-2V9a2 2 0 00-2-2h-2m-4-3H9M7 16h6M7 8h6v4H7V8z"/> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M19 20H5a2 2 0 01-2-2V6a2 2 0 012-2h10a2 2 0 012 2v1m2 13a2 2 0 01-2-2V7m2 13a2 2 0 002-2V9a2 2 0 00-2-2h-2m-4-3H9M7 16h6M7 8h6v4H7V8z"/>
</svg> </svg>
<p class="text-surface-600 dark:text-surface-400 text-lg">No posts in this category yet.</p> <p class="text-surface-600 dark:text-surface-400 text-lg">No posts in this category yet.</p>
<p class="text-surface-500 text-sm mt-2">Posts will appear once blogs are synced.</p> <p class="text-surface-600 dark:text-surface-400 text-sm mt-2">Posts will appear once blogs are synced.</p>
</div> </div>
</div> </div>
</div> </div>
@@ -256,11 +260,11 @@ permalink: /blogroll/
<div class="grid grid-cols-2 gap-3 text-center"> <div class="grid grid-cols-2 gap-3 text-center">
<div class="p-3 bg-surface-50 dark:bg-surface-800 rounded-lg"> <div class="p-3 bg-surface-50 dark:bg-surface-800 rounded-lg">
<span class="text-2xl font-bold text-orange-600 dark:text-orange-400 block" x-text="status?.blogs?.count || 0"></span> <span class="text-2xl font-bold text-orange-600 dark:text-orange-400 block" x-text="status?.blogs?.count || 0"></span>
<span class="text-xs text-surface-500 uppercase">Blogs</span> <span class="text-xs text-surface-600 dark:text-surface-400 uppercase">Blogs</span>
</div> </div>
<div class="p-3 bg-surface-50 dark:bg-surface-800 rounded-lg"> <div class="p-3 bg-surface-50 dark:bg-surface-800 rounded-lg">
<span class="text-2xl font-bold text-orange-600 dark:text-orange-400 block" x-text="status?.items?.count || 0"></span> <span class="text-2xl font-bold text-orange-600 dark:text-orange-400 block" x-text="status?.items?.count || 0"></span>
<span class="text-xs text-surface-500 uppercase">Posts</span> <span class="text-xs text-surface-600 dark:text-surface-400 uppercase">Posts</span>
</div> </div>
</div> </div>
</div> </div>
@@ -283,7 +287,7 @@ permalink: /blogroll/
class="w-full text-left px-3 py-2 rounded-lg text-sm transition-colors" class="w-full text-left px-3 py-2 rounded-lg text-sm transition-colors"
> >
<span x-text="cat.name"></span> <span x-text="cat.name"></span>
<span class="text-surface-500" x-text="`(${cat.count})`"></span> <span class="text-surface-600 dark:text-surface-400" x-text="`(${cat.count})`"></span>
</button> </button>
</li> </li>
</template> </template>

View File

@@ -55,7 +55,7 @@ permalink: "bookmarks/{% if pagination.pageNumber > 0 %}page/{{ pagination.pageN
{% set bookmarkedUrl = post.data.bookmarkOf or post.data.bookmark_of %} {% set bookmarkedUrl = post.data.bookmarkOf or post.data.bookmark_of %}
{% if bookmarkedUrl %} {% if bookmarkedUrl %}
{% unfurl bookmarkedUrl %} {% unfurl bookmarkedUrl %}
<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-600 dark:text-surface-400 hover:underline break-all mt-1 inline-block" href="{{ bookmarkedUrl }}">
{{ bookmarkedUrl }} {{ bookmarkedUrl }}
</a> </a>
{% endif %} {% endif %}
@@ -81,7 +81,7 @@ permalink: "bookmarks/{% if pagination.pageNumber > 0 %}page/{{ pagination.pageN
Previous Previous
</a> </a>
{% else %} {% else %}
<span class="pagination-link disabled"> <span class="pagination-link disabled" aria-disabled="true">
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 19l-7-7 7-7"></path></svg> <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 19l-7-7 7-7"></path></svg>
Previous Previous
</span> </span>
@@ -93,7 +93,7 @@ permalink: "bookmarks/{% if pagination.pageNumber > 0 %}page/{{ pagination.pageN
<svg class="w-4 h-4" 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"></path></svg> <svg class="w-4 h-4" 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"></path></svg>
</a> </a>
{% else %} {% else %}
<span class="pagination-link disabled"> <span class="pagination-link disabled" aria-disabled="true">
Next Next
<svg class="w-4 h-4" 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"></path></svg> <svg class="w-4 h-4" 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"></path></svg>
</span> </span>

View File

@@ -32,7 +32,7 @@ eleventyComputed:
{% endfor %} {% endfor %}
{% if categoryPosts.length > 0 %} {% if categoryPosts.length > 0 %}
<p class="text-sm text-surface-500 dark:text-surface-400 mb-4">{{ categoryPosts.length }} post{% if categoryPosts.length != 1 %}s{% endif %}</p> <p class="text-sm text-surface-600 dark:text-surface-400 mb-4">{{ categoryPosts.length }} post{% if categoryPosts.length != 1 %}s{% endif %}</p>
<ul class="post-list"> <ul class="post-list">
{% for post in categoryPosts %} {% for post in categoryPosts %}
{% set postType = post.inputPath | replace("./content/", "") %} {% set postType = post.inputPath | replace("./content/", "") %}

View File

@@ -14,11 +14,13 @@ withSidebar: false
<div x-data="changelogApp()" x-init="init()"> <div x-data="changelogApp()" x-init="init()">
{# Tab navigation #} {# Tab navigation #}
<div class="flex gap-1 mb-6 border-b border-surface-200 dark:border-surface-700 overflow-x-auto"> <div class="flex gap-1 mb-6 border-b border-surface-200 dark:border-surface-700 overflow-x-auto" role="tablist" aria-label="Changelog categories">
<template x-for="tab in tabs" :key="tab.key"> <template x-for="tab in tabs" :key="tab.key">
<button <button
@click="activeTab = tab.key" @click="activeTab = tab.key"
:class="activeTab === tab.key ? '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 === tab.key ? '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 === tab.key).toString()"
role="tab"
class="flex items-center gap-1.5 px-3 py-2 text-sm font-medium transition-colors -mb-px whitespace-nowrap flex-shrink-0" class="flex items-center gap-1.5 px-3 py-2 text-sm font-medium transition-colors -mb-px whitespace-nowrap flex-shrink-0"
> >
<span x-text="tab.label"></span> <span x-text="tab.label"></span>
@@ -37,13 +39,13 @@ withSidebar: false
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle> <circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
<path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4z"></path> <path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4z"></path>
</svg> </svg>
<span class="ml-3 text-surface-500">Loading changelog...</span> <span class="ml-3 text-surface-600 dark:text-surface-400">Loading changelog...</span>
</div> </div>
{# Commit list #} {# Commit list #}
<div x-show="!loading" x-cloak> <div x-show="!loading" x-cloak>
<template x-if="filteredCommits().length === 0"> <template x-if="filteredCommits().length === 0">
<p class="text-surface-500 py-8 text-center">No recent activity in this category.</p> <p class="text-surface-600 dark:text-surface-400 py-8 text-center">No recent activity in this category.</p>
</template> </template>
<ul class="space-y-4"> <ul class="space-y-4">
@@ -64,12 +66,12 @@ withSidebar: false
<a :href="commit.repoUrl" target="_blank" rel="noopener" <a :href="commit.repoUrl" target="_blank" rel="noopener"
class="text-xs px-2 py-0.5 rounded-full bg-surface-100 dark:bg-surface-800 text-surface-600 dark:text-surface-400 hover:text-accent-600 dark:hover:text-accent-400" class="text-xs px-2 py-0.5 rounded-full bg-surface-100 dark:bg-surface-800 text-surface-600 dark:text-surface-400 hover:text-accent-600 dark:hover:text-accent-400"
x-text="commit.repoName"></a> x-text="commit.repoName"></a>
<span class="text-xs text-surface-500 font-mono" x-text="formatDate(commit.date)"></span> <span class="text-xs text-surface-600 dark:text-surface-400 font-mono" x-text="formatDate(commit.date)"></span>
<span class="text-xs text-surface-400" x-text="'by ' + commit.author"></span> <span class="text-xs text-surface-400" x-text="'by ' + commit.author"></span>
</div> </div>
<template x-if="commit.body"> <template x-if="commit.body">
<details class="mt-2"> <details class="mt-2">
<summary class="text-xs text-surface-500 cursor-pointer hover:text-surface-700 dark:hover:text-surface-300">Show details</summary> <summary class="text-xs text-surface-600 dark:text-surface-400 cursor-pointer hover:text-surface-700 dark:hover:text-surface-300">Show details</summary>
<pre class="mt-1 text-xs text-surface-600 dark:text-surface-400 whitespace-pre-wrap break-words bg-surface-50 dark:bg-surface-800 rounded p-2" x-text="commit.body"></pre> <pre class="mt-1 text-xs text-surface-600 dark:text-surface-400 whitespace-pre-wrap break-words bg-surface-50 dark:bg-surface-800 rounded p-2" x-text="commit.body"></pre>
</details> </details>
</template> </template>

View File

@@ -60,6 +60,16 @@ a{color:#b45309}
img{max-width:100%;height:auto} img{max-width:100%;height:auto}
svg:not(:root):not([width]){width:1.25rem;height:1.25rem} svg:not(:root):not([width]){width:1.25rem;height:1.25rem}
/* Focus indicators — visible in critical CSS before Tailwind loads */
a:focus-visible{outline:2px solid #b45309;outline-offset:2px;border-radius:2px}
.dark a:focus-visible{outline-color:#fbbf24}
button:focus-visible,[type="button"]:focus-visible{outline:2px solid #b45309;outline-offset:2px;border-radius:4px}
.dark button:focus-visible,.dark [type="button"]:focus-visible{outline-color:#fbbf24}
/* Skip link */
.skip-link{position:absolute;top:-100%;left:0;z-index:100;background:#b45309;color:#fff;padding:0.5rem 1rem;font-weight:600;text-decoration:none}
.skip-link:focus{top:0;outline:none}
/* Skeleton loader — visible until Tailwind stylesheet loads */ /* Skeleton loader — visible until Tailwind stylesheet loads */
html.loading main.container>.page-content{display:none} html.loading main.container>.page-content{display:none}
html:not(.loading) .page-skeleton{display:none} html:not(.loading) .page-skeleton{display:none}
@@ -67,3 +77,6 @@ html:not(.loading) .page-skeleton{display:none}
.skel-bone{background:#e8e5df;border-radius:.5rem;animation:skel-pulse 1.5s ease-in-out infinite} .skel-bone{background:#e8e5df;border-radius:.5rem;animation:skel-pulse 1.5s ease-in-out infinite}
.dark .skel-bone{background:#3f3b35} .dark .skel-bone{background:#3f3b35}
.skel-circle{border-radius:50%} .skel-circle{border-radius:50%}
/* Reduced motion — disable animations for users who prefer it */
@media(prefers-reduced-motion:reduce){.skel-bone{animation:none}*{transition-duration:0.01ms!important;animation-duration:0.01ms!important}}

View File

@@ -70,8 +70,11 @@ lite-youtube > .lty-playbtn {
} }
lite-youtube:hover > .lty-playbtn, lite-youtube:hover > .lty-playbtn,
lite-youtube .lty-playbtn:focus { lite-youtube .lty-playbtn:focus-visible {
filter: none; filter: none;
outline: 2px solid #fbbf24;
outline-offset: -2px;
border-radius: 4px;
} }
/* Post-click styles */ /* Post-click styles */

View File

@@ -49,7 +49,7 @@ pre[class*="language-"] {
.token.prolog, .token.prolog,
.token.doctype, .token.doctype,
.token.cdata { .token.cdata {
color: #6a737d; color: #586069;
} }
.token.punctuation { .token.punctuation {
@@ -57,7 +57,7 @@ pre[class*="language-"] {
} }
.token.namespace { .token.namespace {
opacity: 0.7; opacity: 0.85;
} }
.token.property, .token.property,

View File

@@ -288,7 +288,7 @@
/* Site footer */ /* Site footer */
.site-footer { .site-footer {
@apply mt-12 py-8 border-t border-surface-200 dark:border-surface-700 text-center text-sm text-surface-500; @apply mt-12 py-8 border-t border-surface-200 dark:border-surface-700 text-center text-sm text-surface-600 dark:text-surface-400;
} }
.site-footer a { .site-footer a {
@@ -501,6 +501,13 @@
.pagination-link:active:not(.disabled) { .pagination-link:active:not(.disabled) {
transform: scale(0.97); transform: scale(0.97);
} }
@media (prefers-reduced-motion: reduce) {
button:active:not(:disabled),
.pagination-link:active:not(.disabled) {
transform: none;
}
}
} }
/* Video embeds */ /* Video embeds */
@@ -630,7 +637,7 @@
transition: opacity 0.15s; transition: opacity 0.15s;
} }
.prose :is(h2, h3, h4):hover > a.header-anchor::after { .prose :is(h2, h3, h4):hover > a.header-anchor::after {
opacity: 0.4; opacity: 0.6;
} }
.post-list li { .post-list li {
@@ -677,7 +684,7 @@
--pagefind-ui-primary: #fbbf24; --pagefind-ui-primary: #fbbf24;
--pagefind-ui-text: #faf8f5; --pagefind-ui-text: #faf8f5;
--pagefind-ui-background: #0f0e0d; --pagefind-ui-background: #0f0e0d;
--pagefind-ui-border: #3f3b35; --pagefind-ui-border: #5c5750;
--pagefind-ui-tag: #2a2722; --pagefind-ui-tag: #2a2722;
} }
@@ -692,7 +699,7 @@
.dark #search .pagefind-ui__search-input { .dark #search .pagefind-ui__search-input {
background-color: #1c1b19; background-color: #1c1b19;
color: #faf8f5; color: #faf8f5;
border-color: #3f3b35; border-color: #5c5750;
} }
#search .pagefind-ui__search-input:focus { #search .pagefind-ui__search-input:focus {

4
cv.njk
View File

@@ -78,7 +78,7 @@ pagefindIgnore: true
{% endif %} {% endif %}
{# Contact details #} {# Contact details #}
{% if cvLocality or cvCountry or cvOrg or cvUrl or cvEmail or cvKeyUrl %} {% 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 %} {% if cvLocality or cvCountry %}
<span>{% if cvLocality %}{{ cvLocality }}{% endif %}{% if cvLocality and cvCountry %}, {% endif %}{% if cvCountry %}{{ cvCountry }}{% endif %}</span> <span>{% if cvLocality %}{{ cvLocality }}{% endif %}{% if cvLocality and cvCountry %}, {% endif %}{% if cvCountry %}{{ cvCountry }}{% endif %}</span>
{% endif %} {% endif %}
@@ -126,7 +126,7 @@ pagefindIgnore: true
{# Last Updated #} {# Last Updated #}
{% if cv.lastUpdated %} {% 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> Last updated: <time class="font-mono text-sm" datetime="{{ cv.lastUpdated }}">{{ cv.lastUpdated | date("PPP") }}</time>
</p> </p>
{% endif %} {% endif %}

View File

@@ -27,7 +27,7 @@ permalink: "digest/{% if pagination.pageNumber > 0 %}page/{{ pagination.pageNumb
<h2 class="font-semibold text-surface-900 dark:text-surface-100 hover:text-accent-600 dark:hover:text-accent-400"> <h2 class="font-semibold text-surface-900 dark:text-surface-100 hover:text-accent-600 dark:hover:text-accent-400">
{{ d.label }} {{ d.label }}
</h2> </h2>
<p class="text-sm text-surface-500 dark:text-surface-400 mt-1"> <p class="text-sm text-surface-600 dark:text-surface-400 mt-1">
<time class="font-mono" datetime="{{ d.startDate | isoDate }}">{{ d.startDate | dateDisplay }}</time> &ndash; <time class="font-mono" datetime="{{ d.endDate | isoDate }}">{{ d.endDate | dateDisplay }}</time> <time class="font-mono" datetime="{{ d.startDate | isoDate }}">{{ d.startDate | dateDisplay }}</time> &ndash; <time class="font-mono" datetime="{{ d.endDate | isoDate }}">{{ d.endDate | dateDisplay }}</time>
&middot; {{ d.posts.length }} post{% if d.posts.length != 1 %}s{% endif %} &middot; {{ d.posts.length }} post{% if d.posts.length != 1 %}s{% endif %}
</p> </p>
@@ -36,7 +36,7 @@ permalink: "digest/{% if pagination.pageNumber > 0 %}page/{{ pagination.pageNumb
{% set typeLabels = (typeLabels.push(key + " (" + posts.length + ")"), typeLabels) %} {% set typeLabels = (typeLabels.push(key + " (" + posts.length + ")"), typeLabels) %}
{% endfor %} {% endfor %}
{% if typeLabels.length %} {% if typeLabels.length %}
<p class="text-xs text-surface-400 dark:text-surface-500 mt-1"> <p class="text-xs text-surface-600 dark:text-surface-400 mt-1">
{{ typeLabels | join(", ") }} {{ typeLabels | join(", ") }}
</p> </p>
{% endif %} {% endif %}
@@ -57,7 +57,7 @@ permalink: "digest/{% if pagination.pageNumber > 0 %}page/{{ pagination.pageNumb
Previous Previous
</a> </a>
{% else %} {% else %}
<span class="pagination-link disabled"> <span class="pagination-link disabled" aria-disabled="true">
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 19l-7-7 7-7"></path></svg> <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 19l-7-7 7-7"></path></svg>
Previous Previous
</span> </span>
@@ -69,7 +69,7 @@ permalink: "digest/{% if pagination.pageNumber > 0 %}page/{{ pagination.pageNumb
<svg class="w-4 h-4" 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"></path></svg> <svg class="w-4 h-4" 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"></path></svg>
</a> </a>
{% else %} {% else %}
<span class="pagination-link disabled"> <span class="pagination-link disabled" aria-disabled="true">
Next Next
<svg class="w-4 h-4" 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"></path></svg> <svg class="w-4 h-4" 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"></path></svg>
</span> </span>

View File

@@ -39,7 +39,7 @@ permalink: "digest/{{ digest.slug }}/"
<section class="mb-8"> <section class="mb-8">
<h2 class="text-lg sm:text-xl font-semibold text-surface-800 dark:text-surface-200 mb-4 border-b border-surface-200 dark:border-surface-700 pb-2"> <h2 class="text-lg sm:text-xl font-semibold text-surface-800 dark:text-surface-200 mb-4 border-b border-surface-200 dark:border-surface-700 pb-2">
{{ typeInfo.label }} {{ typeInfo.label }}
<span class="text-sm font-normal text-surface-500 dark:text-surface-400">({{ typePosts.length }})</span> <span class="text-sm font-normal text-surface-600 dark:text-surface-400">({{ typePosts.length }})</span>
</h2> </h2>
<ul class="space-y-4"> <ul class="space-y-4">
{% for post in typePosts %} {% for post in typePosts %}
@@ -50,7 +50,7 @@ permalink: "digest/{{ digest.slug }}/"
<span class="text-red-500 flex-shrink-0">&#x2764;</span> <span class="text-red-500 flex-shrink-0">&#x2764;</span>
<div> <div>
<a href="{{ targetUrl }}" class="text-accent-600 dark:text-accent-400 hover:underline break-all">{{ targetUrl }}</a> <a href="{{ targetUrl }}" class="text-accent-600 dark:text-accent-400 hover:underline break-all">{{ targetUrl }}</a>
<div class="text-sm text-surface-500 dark:text-surface-400 mt-1"> <div class="text-sm text-surface-600 dark:text-surface-400 mt-1">
<time class="dt-published font-mono text-sm" datetime="{{ post.date | isoDate }}">{{ post.date | dateDisplay }}</time> <time class="dt-published font-mono text-sm" datetime="{{ post.date | isoDate }}">{{ post.date | dateDisplay }}</time>
&middot; <a href="{{ post.url }}" class="hover:underline">Permalink</a> &middot; <a href="{{ post.url }}" class="hover:underline">Permalink</a>
</div> </div>
@@ -67,7 +67,7 @@ permalink: "digest/{{ digest.slug }}/"
{% else %} {% else %}
<a href="{{ targetUrl }}" class="text-accent-600 dark:text-accent-400 hover:underline break-all">{{ targetUrl }}</a> <a href="{{ targetUrl }}" class="text-accent-600 dark:text-accent-400 hover:underline break-all">{{ targetUrl }}</a>
{% endif %} {% endif %}
<div class="text-sm text-surface-500 dark:text-surface-400 mt-1"> <div class="text-sm text-surface-600 dark:text-surface-400 mt-1">
<time class="dt-published font-mono text-sm" datetime="{{ post.date | isoDate }}">{{ post.date | dateDisplay }}</time> <time class="dt-published font-mono text-sm" datetime="{{ post.date | isoDate }}">{{ post.date | dateDisplay }}</time>
&middot; <a href="{{ post.url }}" class="hover:underline">Permalink</a> &middot; <a href="{{ post.url }}" class="hover:underline">Permalink</a>
</div> </div>
@@ -80,7 +80,7 @@ permalink: "digest/{{ digest.slug }}/"
<span class="text-green-500 flex-shrink-0">&#x1F501;</span> <span class="text-green-500 flex-shrink-0">&#x1F501;</span>
<div> <div>
<a href="{{ targetUrl }}" class="text-accent-600 dark:text-accent-400 hover:underline break-all">{{ targetUrl }}</a> <a href="{{ targetUrl }}" class="text-accent-600 dark:text-accent-400 hover:underline break-all">{{ targetUrl }}</a>
<div class="text-sm text-surface-500 dark:text-surface-400 mt-1"> <div class="text-sm text-surface-600 dark:text-surface-400 mt-1">
<time class="dt-published font-mono text-sm" datetime="{{ post.date | isoDate }}">{{ post.date | dateDisplay }}</time> <time class="dt-published font-mono text-sm" datetime="{{ post.date | isoDate }}">{{ post.date | dateDisplay }}</time>
&middot; <a href="{{ post.url }}" class="hover:underline">Permalink</a> &middot; <a href="{{ post.url }}" class="hover:underline">Permalink</a>
</div> </div>
@@ -103,7 +103,7 @@ permalink: "digest/{{ digest.slug }}/"
{% elif post.templateContent %} {% elif post.templateContent %}
<p class="text-surface-700 dark:text-surface-300 text-sm">{{ post.templateContent | striptags | truncate(120) }}</p> <p class="text-surface-700 dark:text-surface-300 text-sm">{{ post.templateContent | striptags | truncate(120) }}</p>
{% endif %} {% endif %}
<div class="text-sm text-surface-500 dark:text-surface-400 mt-1"> <div class="text-sm text-surface-600 dark:text-surface-400 mt-1">
<time class="dt-published font-mono text-sm" datetime="{{ post.date | isoDate }}">{{ post.date | dateDisplay }}</time> <time class="dt-published font-mono text-sm" datetime="{{ post.date | isoDate }}">{{ post.date | dateDisplay }}</time>
&middot; <a href="{{ post.url }}" class="hover:underline">Permalink</a> &middot; <a href="{{ post.url }}" class="hover:underline">Permalink</a>
</div> </div>
@@ -117,7 +117,7 @@ permalink: "digest/{{ digest.slug }}/"
{% if post.templateContent %} {% if post.templateContent %}
<p class="text-surface-700 dark:text-surface-300 text-sm mt-1">{{ post.templateContent | striptags | truncate(200) }}</p> <p class="text-surface-700 dark:text-surface-300 text-sm mt-1">{{ post.templateContent | striptags | truncate(200) }}</p>
{% endif %} {% endif %}
<div class="text-sm text-surface-500 dark:text-surface-400 mt-1"> <div class="text-sm text-surface-600 dark:text-surface-400 mt-1">
<time class="dt-published font-mono text-sm" datetime="{{ post.date | isoDate }}">{{ post.date | dateDisplay }}</time> <time class="dt-published font-mono text-sm" datetime="{{ post.date | isoDate }}">{{ post.date | dateDisplay }}</time>
&middot; <a href="{{ post.url }}" class="hover:underline">Permalink</a> &middot; <a href="{{ post.url }}" class="hover:underline">Permalink</a>
</div> </div>
@@ -126,7 +126,7 @@ permalink: "digest/{{ digest.slug }}/"
{% else %} {% else %}
<div> <div>
<p class="text-surface-700 dark:text-surface-300">{{ post.templateContent | striptags | truncate(200) }}</p> <p class="text-surface-700 dark:text-surface-300">{{ post.templateContent | striptags | truncate(200) }}</p>
<div class="text-sm text-surface-500 dark:text-surface-400 mt-1"> <div class="text-sm text-surface-600 dark:text-surface-400 mt-1">
<time class="dt-published font-mono text-sm" datetime="{{ post.date | isoDate }}">{{ post.date | dateDisplay }}</time> <time class="dt-published font-mono text-sm" datetime="{{ post.date | isoDate }}">{{ post.date | dateDisplay }}</time>
&middot; <a href="{{ post.url }}" class="hover:underline">Permalink</a> &middot; <a href="{{ post.url }}" class="hover:underline">Permalink</a>
</div> </div>

View File

@@ -321,7 +321,7 @@ export default function (eleventyConfig) {
cacheOptions: { cacheOptions: {
duration: process.env.ELEVENTY_RUN_MODE === "build" ? "1d" : "30d", duration: process.env.ELEVENTY_RUN_MODE === "build" ? "1d" : "30d",
}, },
concurrency: 4, concurrency: 1,
defaultAttributes: { defaultAttributes: {
loading: "lazy", loading: "lazy",
decoding: "async", decoding: "async",

View File

@@ -55,7 +55,7 @@ permalink: "featured/{% if pagination.pageNumber > 0 %}page/{{ pagination.pageNu
<div class="post-header"> <div class="post-header">
<span class="text-xs font-medium text-rose-600 dark:text-rose-400">Liked</span> <span class="text-xs font-medium text-rose-600 dark:text-rose-400">Liked</span>
<a class="u-url ml-2" href="{{ post.url }}"> <a class="u-url ml-2" href="{{ post.url }}">
<time class="dt-published text-sm text-surface-500 dark:text-surface-400 font-mono" datetime="{{ post.date | isoDate }}">{{ post.date | dateDisplay }}</time> <time class="dt-published text-sm text-surface-600 dark:text-surface-400 font-mono" datetime="{{ post.date | isoDate }}">{{ post.date | dateDisplay }}</time>
</a> </a>
</div> </div>
<a class="u-like-of text-sm text-surface-600 dark:text-surface-400 hover:underline break-all mt-2 inline-block" href="{{ likedUrl }}">{{ likedUrl }}</a> <a class="u-like-of text-sm text-surface-600 dark:text-surface-400 hover:underline break-all mt-2 inline-block" href="{{ likedUrl }}">{{ likedUrl }}</a>
@@ -67,7 +67,7 @@ permalink: "featured/{% if pagination.pageNumber > 0 %}page/{{ pagination.pageNu
{# ── Bookmark ── #} {# ── Bookmark ── #}
<div class="post-header"> <div class="post-header">
<span class="text-xs font-medium text-amber-600 dark:text-amber-400">Bookmarked</span> <span class="text-xs font-medium text-amber-600 dark:text-amber-400">Bookmarked</span>
<time class="dt-published text-sm text-surface-500 dark:text-surface-400 ml-2 font-mono" datetime="{{ post.date | isoDate }}">{{ post.date | dateDisplay }}</time> <time class="dt-published text-sm text-surface-600 dark:text-surface-400 ml-2 font-mono" datetime="{{ post.date | isoDate }}">{{ post.date | dateDisplay }}</time>
</div> </div>
{% if post.data.title %} {% if post.data.title %}
<h2 class="p-name text-lg font-semibold mt-2"> <h2 class="p-name text-lg font-semibold mt-2">
@@ -84,7 +84,7 @@ permalink: "featured/{% if pagination.pageNumber > 0 %}page/{{ pagination.pageNu
<div class="post-header"> <div class="post-header">
<span class="text-xs font-medium text-rose-600 dark:text-rose-400">Reposted</span> <span class="text-xs font-medium text-rose-600 dark:text-rose-400">Reposted</span>
<a class="u-url ml-2" href="{{ post.url }}"> <a class="u-url ml-2" href="{{ post.url }}">
<time class="dt-published text-sm text-surface-500 dark:text-surface-400 font-mono" datetime="{{ post.date | isoDate }}">{{ post.date | dateDisplay }}</time> <time class="dt-published text-sm text-surface-600 dark:text-surface-400 font-mono" datetime="{{ post.date | isoDate }}">{{ post.date | dateDisplay }}</time>
</a> </a>
</div> </div>
<a class="u-repost-of text-sm text-surface-600 dark:text-surface-400 hover:underline break-all mt-2 inline-block" href="{{ repostedUrl }}">{{ repostedUrl }}</a> <a class="u-repost-of text-sm text-surface-600 dark:text-surface-400 hover:underline break-all mt-2 inline-block" href="{{ repostedUrl }}">{{ repostedUrl }}</a>
@@ -97,7 +97,7 @@ permalink: "featured/{% if pagination.pageNumber > 0 %}page/{{ pagination.pageNu
<div class="post-header"> <div class="post-header">
<span class="text-xs font-medium text-rose-600 dark:text-rose-400">In reply to</span> <span class="text-xs font-medium text-rose-600 dark:text-rose-400">In reply to</span>
<a class="u-url ml-2" href="{{ post.url }}"> <a class="u-url ml-2" href="{{ post.url }}">
<time class="dt-published text-sm text-surface-500 dark:text-surface-400 font-mono" datetime="{{ post.date | isoDate }}">{{ post.date | dateDisplay }}</time> <time class="dt-published text-sm text-surface-600 dark:text-surface-400 font-mono" datetime="{{ post.date | isoDate }}">{{ post.date | dateDisplay }}</time>
</a> </a>
</div> </div>
<a class="u-in-reply-to text-sm text-surface-600 dark:text-surface-400 hover:underline break-all mt-2 inline-block" href="{{ replyToUrl }}">{{ replyToUrl }}</a> <a class="u-in-reply-to text-sm text-surface-600 dark:text-surface-400 hover:underline break-all mt-2 inline-block" href="{{ replyToUrl }}">{{ replyToUrl }}</a>
@@ -127,7 +127,7 @@ permalink: "featured/{% if pagination.pageNumber > 0 %}page/{{ pagination.pageNu
{# ── Note ── #} {# ── Note ── #}
<div class="post-header"> <div class="post-header">
<a class="u-url" href="{{ post.url }}"> <a class="u-url" href="{{ post.url }}">
<time class="dt-published text-sm text-surface-500 dark:text-surface-400 font-medium font-mono" datetime="{{ post.date | isoDate }}">{{ post.date | dateDisplay }}</time> <time class="dt-published text-sm text-surface-600 dark:text-surface-400 font-medium font-mono" datetime="{{ post.date | isoDate }}">{{ post.date | dateDisplay }}</time>
</a> </a>
{% if post.data.postType %} {% if post.data.postType %}
<span class="px-2 py-0.5 bg-surface-100 dark:bg-surface-700 rounded text-xs ml-2">{{ post.data.postType }}</span> <span class="px-2 py-0.5 bg-surface-100 dark:bg-surface-700 rounded text-xs ml-2">{{ post.data.postType }}</span>
@@ -158,7 +158,7 @@ permalink: "featured/{% if pagination.pageNumber > 0 %}page/{{ pagination.pageNu
Previous Previous
</a> </a>
{% else %} {% else %}
<span class="pagination-link disabled"> <span class="pagination-link disabled" aria-disabled="true">
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 19l-7-7 7-7"></path></svg> <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 19l-7-7 7-7"></path></svg>
Previous Previous
</span> </span>
@@ -170,7 +170,7 @@ permalink: "featured/{% if pagination.pageNumber > 0 %}page/{{ pagination.pageNu
<svg class="w-4 h-4" 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"></path></svg> <svg class="w-4 h-4" 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"></path></svg>
</a> </a>
{% else %} {% else %}
<span class="pagination-link disabled"> <span class="pagination-link disabled" aria-disabled="true">
Next Next
<svg class="w-4 h-4" 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"></path></svg> <svg class="w-4 h-4" 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"></path></svg>
</span> </span>

View File

@@ -66,9 +66,9 @@ withSidebar: true
</h2> </h2>
<p class="text-surface-600 dark:text-surface-400">{{ funkwhaleActivity.nowPlaying.artist }}</p> <p class="text-surface-600 dark:text-surface-400">{{ funkwhaleActivity.nowPlaying.artist }}</p>
{% if funkwhaleActivity.nowPlaying.album %} {% if funkwhaleActivity.nowPlaying.album %}
<p class="text-sm text-surface-500 mt-1">{{ funkwhaleActivity.nowPlaying.album }}</p> <p class="text-sm text-surface-600 dark:text-surface-400 mt-1">{{ funkwhaleActivity.nowPlaying.album }}</p>
{% endif %} {% endif %}
<p class="text-xs text-surface-500 mt-2">{{ funkwhaleActivity.nowPlaying.relativeTime }}</p> <p class="text-xs text-surface-600 dark:text-surface-400 mt-2">{{ funkwhaleActivity.nowPlaying.relativeTime }}</p>
</div> </div>
</div> </div>
</div> </div>
@@ -86,31 +86,39 @@ withSidebar: true
</h2> </h2>
{# Tab buttons #} {# Tab buttons #}
<div class="flex gap-1 mb-6 border-b border-surface-200 dark:border-surface-700 overflow-x-auto"> <div class="flex gap-1 mb-6 border-b border-surface-200 dark:border-surface-700 overflow-x-auto" role="tablist" aria-label="Listening statistics period">
<button <button
@click="activeTab = 'all'" @click="activeTab = 'all'"
:class="activeTab === 'all' ? 'border-b-2 border-purple-500 text-purple-600 dark:text-purple-400' : 'text-surface-500 hover:text-surface-700 dark:hover:text-surface-300'" :class="activeTab === 'all' ? 'border-b-2 border-purple-500 text-purple-600 dark:text-purple-400' : 'text-surface-600 dark:text-surface-400 hover:text-surface-700 dark:hover:text-surface-300'"
:aria-selected="(activeTab === 'all').toString()"
role="tab" id="fw-tab-all" aria-controls="fw-panel-all"
class="px-4 py-2 text-sm font-medium transition-colors -mb-px whitespace-nowrap" class="px-4 py-2 text-sm font-medium transition-colors -mb-px whitespace-nowrap"
> >
All Time All Time
</button> </button>
<button <button
@click="activeTab = 'month'" @click="activeTab = 'month'"
:class="activeTab === 'month' ? 'border-b-2 border-purple-500 text-purple-600 dark:text-purple-400' : 'text-surface-500 hover:text-surface-700 dark:hover:text-surface-300'" :class="activeTab === 'month' ? 'border-b-2 border-purple-500 text-purple-600 dark:text-purple-400' : 'text-surface-600 dark:text-surface-400 hover:text-surface-700 dark:hover:text-surface-300'"
:aria-selected="(activeTab === 'month').toString()"
role="tab" id="fw-tab-month" aria-controls="fw-panel-month"
class="px-4 py-2 text-sm font-medium transition-colors -mb-px whitespace-nowrap" class="px-4 py-2 text-sm font-medium transition-colors -mb-px whitespace-nowrap"
> >
This Month This Month
</button> </button>
<button <button
@click="activeTab = 'week'" @click="activeTab = 'week'"
:class="activeTab === 'week' ? 'border-b-2 border-purple-500 text-purple-600 dark:text-purple-400' : 'text-surface-500 hover:text-surface-700 dark:hover:text-surface-300'" :class="activeTab === 'week' ? 'border-b-2 border-purple-500 text-purple-600 dark:text-purple-400' : 'text-surface-600 dark:text-surface-400 hover:text-surface-700 dark:hover:text-surface-300'"
:aria-selected="(activeTab === 'week').toString()"
role="tab" id="fw-tab-week" aria-controls="fw-panel-week"
class="px-4 py-2 text-sm font-medium transition-colors -mb-px whitespace-nowrap" class="px-4 py-2 text-sm font-medium transition-colors -mb-px whitespace-nowrap"
> >
This Week This Week
</button> </button>
<button <button
@click="activeTab = 'trends'" @click="activeTab = 'trends'"
:class="activeTab === 'trends' ? 'border-b-2 border-purple-500 text-purple-600 dark:text-purple-400' : 'text-surface-500 hover:text-surface-700 dark:hover:text-surface-300'" :class="activeTab === 'trends' ? 'border-b-2 border-purple-500 text-purple-600 dark:text-purple-400' : 'text-surface-600 dark:text-surface-400 hover:text-surface-700 dark:hover:text-surface-300'"
:aria-selected="(activeTab === 'trends').toString()"
role="tab" id="fw-tab-trends" aria-controls="fw-panel-trends"
class="px-4 py-2 text-sm font-medium transition-colors -mb-px whitespace-nowrap" class="px-4 py-2 text-sm font-medium transition-colors -mb-px whitespace-nowrap"
> >
Trends Trends
@@ -118,7 +126,7 @@ withSidebar: true
</div> </div>
{# All Time Tab #} {# All Time Tab #}
<div x-show="activeTab === 'all'" x-cloak> <div x-show="activeTab === 'all'" x-cloak role="tabpanel" id="fw-panel-all" aria-labelledby="fw-tab-all">
{% set summary = funkwhaleActivity.stats.summary.all %} {% set summary = funkwhaleActivity.stats.summary.all %}
{% set topArtists = funkwhaleActivity.stats.topArtists.all %} {% set topArtists = funkwhaleActivity.stats.topArtists.all %}
{% set topAlbums = funkwhaleActivity.stats.topAlbums.all %} {% set topAlbums = funkwhaleActivity.stats.topAlbums.all %}
@@ -126,7 +134,7 @@ withSidebar: true
</div> </div>
{# This Month Tab #} {# This Month Tab #}
<div x-show="activeTab === 'month'" x-cloak> <div x-show="activeTab === 'month'" x-cloak role="tabpanel" id="fw-panel-month" aria-labelledby="fw-tab-month">
{% set summary = funkwhaleActivity.stats.summary.month %} {% set summary = funkwhaleActivity.stats.summary.month %}
{% set topArtists = funkwhaleActivity.stats.topArtists.month %} {% set topArtists = funkwhaleActivity.stats.topArtists.month %}
{% set topAlbums = funkwhaleActivity.stats.topAlbums.month %} {% set topAlbums = funkwhaleActivity.stats.topAlbums.month %}
@@ -134,7 +142,7 @@ withSidebar: true
</div> </div>
{# This Week Tab #} {# This Week Tab #}
<div x-show="activeTab === 'week'" x-cloak> <div x-show="activeTab === 'week'" x-cloak role="tabpanel" id="fw-panel-week" aria-labelledby="fw-tab-week">
{% set summary = funkwhaleActivity.stats.summary.week %} {% set summary = funkwhaleActivity.stats.summary.week %}
{% set topArtists = funkwhaleActivity.stats.topArtists.week %} {% set topArtists = funkwhaleActivity.stats.topArtists.week %}
{% set topAlbums = funkwhaleActivity.stats.topAlbums.week %} {% set topAlbums = funkwhaleActivity.stats.topAlbums.week %}
@@ -142,7 +150,7 @@ withSidebar: true
</div> </div>
{# Trends Tab #} {# Trends Tab #}
<div x-show="activeTab === 'trends'" x-cloak> <div x-show="activeTab === 'trends'" x-cloak role="tabpanel" id="fw-panel-trends" aria-labelledby="fw-tab-trends">
{% if funkwhaleActivity.stats.trends and funkwhaleActivity.stats.trends.length %} {% if funkwhaleActivity.stats.trends and funkwhaleActivity.stats.trends.length %}
<div class="bg-surface-50 dark:bg-surface-800 rounded-lg p-6 border border-surface-200 dark:border-surface-700 shadow-sm"> <div class="bg-surface-50 dark:bg-surface-800 rounded-lg p-6 border border-surface-200 dark:border-surface-700 shadow-sm">
<h3 class="text-lg font-semibold text-surface-900 dark:text-surface-100 mb-4">Daily Listening (Last 30 Days)</h3> <h3 class="text-lg font-semibold text-surface-900 dark:text-surface-100 mb-4">Daily Listening (Last 30 Days)</h3>
@@ -161,7 +169,7 @@ withSidebar: true
></div> ></div>
{% endfor %} {% endfor %}
</div> </div>
<div class="flex justify-between text-xs text-surface-500 mt-2"> <div class="flex justify-between text-xs text-surface-600 dark:text-surface-400 mt-2">
<span>{{ funkwhaleActivity.stats.trends[0].date }}</span> <span>{{ funkwhaleActivity.stats.trends[0].date }}</span>
<span>{{ funkwhaleActivity.stats.trends[funkwhaleActivity.stats.trends.length - 1].date }}</span> <span>{{ funkwhaleActivity.stats.trends[funkwhaleActivity.stats.trends.length - 1].date }}</span>
</div> </div>
@@ -210,7 +218,7 @@ withSidebar: true
</div> </div>
<div class="text-right flex-shrink-0"> <div class="text-right flex-shrink-0">
<span class="text-xs text-surface-500">{{ listening.relativeTime }}</span> <span class="text-xs text-surface-600 dark:text-surface-400">{{ listening.relativeTime }}</span>
{% if listening.duration %} {% if listening.duration %}
<span class="text-xs text-surface-400 block">{{ listening.duration }}</span> <span class="text-xs text-surface-400 block">{{ listening.duration }}</span>
{% endif %} {% endif %}
@@ -258,7 +266,7 @@ withSidebar: true
</h3> </h3>
<p class="text-sm text-surface-600 dark:text-surface-400 truncate">{{ favorite.artist }}</p> <p class="text-sm text-surface-600 dark:text-surface-400 truncate">{{ favorite.artist }}</p>
{% if favorite.album %} {% if favorite.album %}
<p class="text-xs text-surface-500 truncate">{{ favorite.album }}</p> <p class="text-xs text-surface-600 dark:text-surface-400 truncate">{{ favorite.album }}</p>
{% endif %} {% endif %}
</div> </div>
</div> </div>

View File

@@ -43,7 +43,7 @@ withSidebar: true
<p class="text-sm text-surface-600 dark:text-surface-400 mb-4">{{ repo.description }}</p> <p class="text-sm text-surface-600 dark:text-surface-400 mb-4">{{ repo.description }}</p>
{% endif %} {% endif %}
<div class="flex flex-wrap items-center gap-3 text-sm text-surface-500 mb-4"> <div class="flex flex-wrap items-center gap-3 text-sm text-surface-600 dark:text-surface-400 mb-4">
{% if repo.language %} {% if repo.language %}
<span class="flex items-center gap-1"> <span class="flex items-center gap-1">
<span class="w-3 h-3 rounded-full bg-surface-500"></span> <span class="w-3 h-3 rounded-full bg-surface-500"></span>
@@ -109,7 +109,7 @@ withSidebar: true
<a href="{{ commit.url }}" class="text-surface-900 dark:text-surface-100 hover:text-emerald-600 dark:hover:text-emerald-400" target="_blank" rel="noopener"> <a href="{{ commit.url }}" class="text-surface-900 dark:text-surface-100 hover:text-emerald-600 dark:hover:text-emerald-400" target="_blank" rel="noopener">
{{ commit.message }} {{ commit.message }}
</a> </a>
<p class="text-xs text-surface-500 mt-1"> <p class="text-xs text-surface-600 dark:text-surface-400 mt-1">
<a href="{{ commit.repoUrl }}" class="hover:underline" target="_blank" rel="noopener">{{ commit.repo }}</a> <a href="{{ commit.repoUrl }}" class="hover:underline" target="_blank" rel="noopener">{{ commit.repo }}</a>
· <time class="font-mono" datetime="{{ commit.date }}">{{ commit.date | date("MMM d, yyyy") }}</time> · <time class="font-mono" datetime="{{ commit.date }}">{{ commit.date | date("MMM d, yyyy") }}</time>
</p> </p>
@@ -144,7 +144,7 @@ withSidebar: true
<a href="{{ item.url }}" class="text-surface-900 dark:text-surface-100 hover:text-emerald-600 dark:hover:text-emerald-400" target="_blank" rel="noopener"> <a href="{{ item.url }}" class="text-surface-900 dark:text-surface-100 hover:text-emerald-600 dark:hover:text-emerald-400" target="_blank" rel="noopener">
{{ item.title }} {{ item.title }}
</a> </a>
<p class="text-xs text-surface-500 mt-1"> <p class="text-xs text-surface-600 dark:text-surface-400 mt-1">
<a href="{{ item.repoUrl }}" class="hover:underline" target="_blank" rel="noopener">{{ item.repo }}</a> <a href="{{ item.repoUrl }}" class="hover:underline" target="_blank" rel="noopener">{{ item.repo }}</a>
#{{ item.number }} #{{ item.number }}
· <time class="font-mono" datetime="{{ item.date }}">{{ item.date | date("MMM d, yyyy") }}</time> · <time class="font-mono" datetime="{{ item.date }}">{{ item.date | date("MMM d, yyyy") }}</time>
@@ -179,7 +179,7 @@ withSidebar: true
<p class="text-sm text-surface-600 dark:text-surface-400 mb-3">{{ repo.description | truncate(100) }}</p> <p class="text-sm text-surface-600 dark:text-surface-400 mb-3">{{ repo.description | truncate(100) }}</p>
{% endif %} {% endif %}
<div class="flex flex-wrap items-center gap-3 text-sm text-surface-500"> <div class="flex flex-wrap items-center gap-3 text-sm text-surface-600 dark:text-surface-400">
{% if repo.language %} {% if repo.language %}
<span class="flex items-center gap-1"> <span class="flex items-center gap-1">
<span class="w-3 h-3 rounded-full bg-surface-500"></span> <span class="w-3 h-3 rounded-full bg-surface-500"></span>
@@ -242,7 +242,7 @@ withSidebar: true
{% endfor %} {% endfor %}
</div> </div>
<div class="flex flex-wrap items-center gap-3 text-sm text-surface-500"> <div class="flex flex-wrap items-center gap-3 text-sm text-surface-600 dark:text-surface-400">
{% if repo.language %} {% if repo.language %}
<span class="flex items-center gap-1"> <span class="flex items-center gap-1">
<span class="w-3 h-3 rounded-full bg-surface-500"></span> <span class="w-3 h-3 rounded-full bg-surface-500"></span>

View File

@@ -14,5 +14,5 @@ withSidebar: true
{% if collections.posts and collections.posts.length %} {% if collections.posts and collections.posts.length %}
{% postGraph collections.posts, { limit: 0 } %} {% postGraph collections.posts, { limit: 0 } %}
{% else %} {% else %}
<p class="text-surface-500 dark:text-surface-400">No posts found.</p> <p class="text-surface-600 dark:text-surface-400">No posts found.</p>
{% endif %} {% endif %}

View File

@@ -17,17 +17,21 @@ permalink: /interactions/
{# Tab navigation for Outbound/Inbound #} {# Tab navigation for Outbound/Inbound #}
<div x-data="interactionsApp()" x-init="init()"> <div x-data="interactionsApp()" x-init="init()">
{# Tab buttons #} {# Tab buttons #}
<div class="flex border-b border-surface-200 dark:border-surface-700 mb-6"> <div class="flex border-b border-surface-200 dark:border-surface-700 mb-6" role="tablist" aria-label="Interaction direction">
<button <button
@click="activeTab = 'outbound'" @click="activeTab = 'outbound'"
:class="activeTab === 'outbound' ? 'border-rose-500 text-rose-600 dark:text-rose-400' : 'border-transparent text-surface-500 hover:text-surface-700 dark:hover:text-surface-300'" :class="activeTab === 'outbound' ? 'border-rose-500 text-rose-600 dark:text-rose-400' : 'border-transparent text-surface-600 dark:text-surface-400 hover:text-surface-700 dark:hover:text-surface-300'"
:aria-selected="(activeTab === 'outbound').toString()"
role="tab" id="interactions-tab-outbound" aria-controls="interactions-panel-outbound"
class="px-4 py-3 text-sm font-medium border-b-2 -mb-px transition-colors"> class="px-4 py-3 text-sm font-medium border-b-2 -mb-px transition-colors">
My Activity My Activity
<span class="ml-1 text-xs text-surface-400">(outbound)</span> <span class="ml-1 text-xs text-surface-400">(outbound)</span>
</button> </button>
<button <button
@click="activeTab = 'inbound'" @click="activeTab = 'inbound'"
:class="activeTab === 'inbound' ? 'border-rose-500 text-rose-600 dark:text-rose-400' : 'border-transparent text-surface-500 hover:text-surface-700 dark:hover:text-surface-300'" :class="activeTab === 'inbound' ? 'border-rose-500 text-rose-600 dark:text-rose-400' : 'border-transparent text-surface-600 dark:text-surface-400 hover:text-surface-700 dark:hover:text-surface-300'"
:aria-selected="(activeTab === 'inbound').toString()"
role="tab" id="interactions-tab-inbound" aria-controls="interactions-panel-inbound"
class="px-4 py-3 text-sm font-medium border-b-2 -mb-px transition-colors"> class="px-4 py-3 text-sm font-medium border-b-2 -mb-px transition-colors">
Received Received
<span class="ml-1 text-xs text-surface-400">(inbound)</span> <span class="ml-1 text-xs text-surface-400">(inbound)</span>
@@ -36,7 +40,7 @@ permalink: /interactions/
</div> </div>
{# ===== OUTBOUND TAB - My Activity ===== #} {# ===== OUTBOUND TAB - My Activity ===== #}
<div x-show="activeTab === 'outbound'" x-transition> <div x-show="activeTab === 'outbound'" x-transition role="tabpanel" id="interactions-panel-outbound" aria-labelledby="interactions-tab-outbound">
<p class="text-surface-600 dark:text-surface-400 text-sm mb-6">Content I've interacted with across the web.</p> <p class="text-surface-600 dark:text-surface-400 text-sm mb-6">Content I've interacted with across the web.</p>
<div class="grid gap-4 sm:gap-6 md:grid-cols-2 lg:grid-cols-3"> <div class="grid gap-4 sm:gap-6 md:grid-cols-2 lg:grid-cols-3">
@@ -50,7 +54,7 @@ permalink: /interactions/
</div> </div>
<div> <div>
<h2 class="text-lg font-semibold text-surface-900 dark:text-surface-100 group-hover:text-rose-600 dark:group-hover:text-rose-400">Likes</h2> <h2 class="text-lg font-semibold text-surface-900 dark:text-surface-100 group-hover:text-rose-600 dark:group-hover:text-rose-400">Likes</h2>
<p class="text-sm text-surface-500">{{ collections.likes.length }} item{% if collections.likes.length != 1 %}s{% endif %}</p> <p class="text-sm text-surface-600 dark:text-surface-400">{{ collections.likes.length }} item{% if collections.likes.length != 1 %}s{% endif %}</p>
</div> </div>
</div> </div>
<p class="text-surface-600 dark:text-surface-400 text-sm">Content I've appreciated across the web.</p> <p class="text-surface-600 dark:text-surface-400 text-sm">Content I've appreciated across the web.</p>
@@ -66,7 +70,7 @@ permalink: /interactions/
</div> </div>
<div> <div>
<h2 class="text-lg font-semibold text-surface-900 dark:text-surface-100 group-hover:text-rose-600 dark:group-hover:text-rose-400">Replies</h2> <h2 class="text-lg font-semibold text-surface-900 dark:text-surface-100 group-hover:text-rose-600 dark:group-hover:text-rose-400">Replies</h2>
<p class="text-sm text-surface-500">{{ collections.replies.length }} item{% if collections.replies.length != 1 %}s{% endif %}</p> <p class="text-sm text-surface-600 dark:text-surface-400">{{ collections.replies.length }} item{% if collections.replies.length != 1 %}s{% endif %}</p>
</div> </div>
</div> </div>
<p class="text-surface-600 dark:text-surface-400 text-sm">My responses to posts across the web.</p> <p class="text-surface-600 dark:text-surface-400 text-sm">My responses to posts across the web.</p>
@@ -82,7 +86,7 @@ permalink: /interactions/
</div> </div>
<div> <div>
<h2 class="text-lg font-semibold text-surface-900 dark:text-surface-100 group-hover:text-amber-600 dark:group-hover:text-amber-400">Bookmarks</h2> <h2 class="text-lg font-semibold text-surface-900 dark:text-surface-100 group-hover:text-amber-600 dark:group-hover:text-amber-400">Bookmarks</h2>
<p class="text-sm text-surface-500">{{ collections.bookmarks.length }} item{% if collections.bookmarks.length != 1 %}s{% endif %}</p> <p class="text-sm text-surface-600 dark:text-surface-400">{{ collections.bookmarks.length }} item{% if collections.bookmarks.length != 1 %}s{% endif %}</p>
</div> </div>
</div> </div>
<p class="text-surface-600 dark:text-surface-400 text-sm">Links I've saved for later.</p> <p class="text-surface-600 dark:text-surface-400 text-sm">Links I've saved for later.</p>
@@ -98,7 +102,7 @@ permalink: /interactions/
</div> </div>
<div> <div>
<h2 class="text-lg font-semibold text-surface-900 dark:text-surface-100 group-hover:text-rose-600 dark:group-hover:text-rose-400">Reposts</h2> <h2 class="text-lg font-semibold text-surface-900 dark:text-surface-100 group-hover:text-rose-600 dark:group-hover:text-rose-400">Reposts</h2>
<p class="text-sm text-surface-500">{{ collections.reposts.length }} item{% if collections.reposts.length != 1 %}s{% endif %}</p> <p class="text-sm text-surface-600 dark:text-surface-400">{{ collections.reposts.length }} item{% if collections.reposts.length != 1 %}s{% endif %}</p>
</div> </div>
</div> </div>
<p class="text-surface-600 dark:text-surface-400 text-sm">Content I've shared from others.</p> <p class="text-surface-600 dark:text-surface-400 text-sm">Content I've shared from others.</p>
@@ -114,7 +118,7 @@ permalink: /interactions/
</div> </div>
<div> <div>
<h2 class="text-lg font-semibold text-surface-900 dark:text-surface-100 group-hover:text-purple-600 dark:group-hover:text-purple-400">Photos</h2> <h2 class="text-lg font-semibold text-surface-900 dark:text-surface-100 group-hover:text-purple-600 dark:group-hover:text-purple-400">Photos</h2>
<p class="text-sm text-surface-500">{{ collections.photos.length }} item{% if collections.photos.length != 1 %}s{% endif %}</p> <p class="text-sm text-surface-600 dark:text-surface-400">{{ collections.photos.length }} item{% if collections.photos.length != 1 %}s{% endif %}</p>
</div> </div>
</div> </div>
<p class="text-surface-600 dark:text-surface-400 text-sm">Photo posts and images.</p> <p class="text-surface-600 dark:text-surface-400 text-sm">Photo posts and images.</p>
@@ -137,7 +141,7 @@ permalink: /interactions/
</div> </div>
{# ===== INBOUND TAB - Received Webmentions ===== #} {# ===== INBOUND TAB - Received Webmentions ===== #}
<div x-show="activeTab === 'inbound'" x-transition> <div x-show="activeTab === 'inbound'" x-transition role="tabpanel" id="interactions-panel-inbound" aria-labelledby="interactions-tab-inbound">
<div class="flex items-center justify-between mb-6"> <div class="flex items-center justify-between mb-6">
<p class="text-surface-600 dark:text-surface-400 text-sm">Webmentions and interactions others have made with my content.</p> <p class="text-surface-600 dark:text-surface-400 text-sm">Webmentions and interactions others have made with my content.</p>
<button <button
@@ -154,7 +158,7 @@ permalink: /interactions/
{# Loading state #} {# Loading state #}
<div x-show="loading && !webmentions.length" class="text-center py-12"> <div x-show="loading && !webmentions.length" class="text-center py-12">
<div class="inline-block animate-spin rounded-full h-8 w-8 border-b-2 border-rose-500"></div> <div class="inline-block animate-spin rounded-full h-8 w-8 border-b-2 border-rose-500"></div>
<p class="mt-4 text-surface-500">Loading webmentions...</p> <p class="mt-4 text-surface-600 dark:text-surface-400">Loading webmentions...</p>
</div> </div>
{# Setup required state — shown when webmentions proxy is not configured #} {# Setup required state — shown when webmentions proxy is not configured #}
@@ -263,7 +267,7 @@ permalink: /interactions/
<svg class="w-3 h-3" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="12" cy="12" r="10"/><line x1="2" y1="12" x2="22" y2="12"/><path d="M12 2a15.3 15.3 0 014 10 15.3 15.3 0 01-4 10 15.3 15.3 0 01-4-10 15.3 15.3 0 014-10z"/></svg> <svg class="w-3 h-3" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="12" cy="12" r="10"/><line x1="2" y1="12" x2="22" y2="12"/><path d="M12 2a15.3 15.3 0 014 10 15.3 15.3 0 01-4 10 15.3 15.3 0 01-4-10 15.3 15.3 0 014-10z"/></svg>
</span> </span>
<a :href="wm.url || '#'" target="_blank" rel="noopener" class="text-xs text-surface-500 hover:underline"> <a :href="wm.url || '#'" target="_blank" rel="noopener" class="text-xs text-surface-600 dark:text-surface-400 hover:underline">
<time class="font-mono text-sm" :datetime="wm.published || wm['wm-received']" x-text="formatDate(wm.published || wm['wm-received'])"></time> <time class="font-mono text-sm" :datetime="wm.published || wm['wm-received']" x-text="formatDate(wm.published || wm['wm-received'])"></time>
</a> </a>
</div> </div>
@@ -272,7 +276,7 @@ permalink: /interactions/
<div x-show="wm.content?.text" class="text-surface-700 dark:text-surface-300 text-sm mt-2" x-text="truncateText(wm.content?.text, 280)"></div> <div x-show="wm.content?.text" class="text-surface-700 dark:text-surface-300 text-sm mt-2" x-text="truncateText(wm.content?.text, 280)"></div>
{# Target URL - which of my posts this is about #} {# Target URL - which of my posts this is about #}
<div class="mt-2 text-xs text-surface-500"> <div class="mt-2 text-xs text-surface-600 dark:text-surface-400">
<span>on </span> <span>on </span>
<a :href="wm['wm-target']" class="text-rose-600 dark:text-rose-400 hover:underline" x-text="formatTargetUrl(wm['wm-target'])"></a> <a :href="wm['wm-target']" class="text-rose-600 dark:text-rose-400 hover:underline" x-text="formatTargetUrl(wm['wm-target'])"></a>
</div> </div>
@@ -282,7 +286,7 @@ permalink: /interactions/
</template> </template>
{# Empty state #} {# Empty state #}
<div x-show="!loading && filteredWebmentions.length === 0" class="text-center py-12 text-surface-500"> <div x-show="!loading && filteredWebmentions.length === 0" class="text-center py-12 text-surface-600 dark:text-surface-400">
<p>No webmentions found for this filter.</p> <p>No webmentions found for this filter.</p>
</div> </div>
@@ -291,7 +295,7 @@ permalink: /interactions/
<nav class="pagination" aria-label="Webmentions pagination"> <nav class="pagination" aria-label="Webmentions pagination">
<div class="pagination-info"> <div class="pagination-info">
Page <span x-text="currentPage"></span> of <span x-text="totalPages"></span> Page <span x-text="currentPage"></span> of <span x-text="totalPages"></span>
<span class="text-surface-400 dark:text-surface-500 ml-1">(<span x-text="filteredWebmentions.length"></span> total)</span> <span class="text-surface-600 dark:text-surface-400 ml-1">(<span x-text="filteredWebmentions.length"></span> total)</span>
</div> </div>
<div class="pagination-links"> <div class="pagination-links">
<button <button

View File

@@ -11,6 +11,7 @@ document.addEventListener("alpine:init", () => {
alt: "", alt: "",
images: [], images: [],
currentIndex: 0, currentIndex: 0,
triggerElement: null,
init() { init() {
const container = this.$root; const container = this.$root;
@@ -21,14 +22,24 @@ document.addEventListener("alpine:init", () => {
this.images.forEach((img, i) => { this.images.forEach((img, i) => {
img.style.cursor = "zoom-in"; img.style.cursor = "zoom-in";
img.setAttribute("tabindex", "0");
img.setAttribute("role", "button");
img.setAttribute("aria-label", (img.alt || "Image") + " — click to enlarge");
img.addEventListener("click", (e) => { img.addEventListener("click", (e) => {
e.preventDefault(); e.preventDefault();
this.show(i); this.show(i);
}); });
img.addEventListener("keydown", (e) => {
if (e.key === "Enter" || e.key === " ") {
e.preventDefault();
this.show(i);
}
});
}); });
}, },
show(index) { show(index) {
this.triggerElement = this.images[index];
this.currentIndex = index; this.currentIndex = index;
const img = this.images[index]; const img = this.images[index];
// Use the largest source available // Use the largest source available
@@ -48,12 +59,22 @@ document.addEventListener("alpine:init", () => {
this.alt = img.alt || ""; this.alt = img.alt || "";
this.open = true; this.open = true;
document.body.style.overflow = "hidden"; document.body.style.overflow = "hidden";
// Move focus to close button for keyboard users
this.$nextTick(() => {
const closeBtn = document.querySelector('[x-ref="closeBtn"]');
if (closeBtn) closeBtn.focus();
});
}, },
close() { close() {
this.open = false; this.open = false;
this.src = ""; this.src = "";
document.body.style.overflow = ""; document.body.style.overflow = "";
// Return focus to the image that triggered the lightbox
if (this.triggerElement) {
this.triggerElement.focus();
this.triggerElement = null;
}
}, },
next() { next() {

View File

@@ -78,9 +78,12 @@ class TimeDifference extends HTMLElement {
const relative = rtf.format(value, unit); const relative = rtf.format(value, unit);
// Store original text as title for hover tooltip // Store original text as title for hover tooltip
const originalText = time.textContent.trim();
if (!time.hasAttribute("title")) { if (!time.hasAttribute("title")) {
time.setAttribute("title", time.textContent.trim()); time.setAttribute("title", originalText);
} }
// aria-label provides the full context: "2 days ago (March 5, 2026)"
time.setAttribute("aria-label", relative + " (" + originalText + ")");
time.textContent = relative; time.textContent = relative;
} catch { } catch {
// Intl.RelativeTimeFormat not supported, keep static text // Intl.RelativeTimeFormat not supported, keep static text

View File

@@ -197,22 +197,26 @@
items.forEach((item) => { items.forEach((item) => {
const author = item.author || {}; const author = item.author || {};
const li = document.createElement('li');
li.className = 'inline';
const link = document.createElement('a'); const link = document.createElement('a');
link.href = author.url || '#'; link.href = author.url || '#';
link.className = 'facepile-avatar'; link.className = 'facepile-avatar';
link.title = author.name || 'Anonymous'; link.setAttribute('aria-label', (author.name || 'Anonymous') + ' (opens in new tab)');
link.target = '_blank'; link.target = '_blank';
link.rel = 'noopener'; link.rel = 'noopener';
link.dataset.new = 'true'; link.dataset.new = 'true';
const img = document.createElement('img'); const img = document.createElement('img');
img.src = author.photo || '/images/default-avatar.svg'; img.src = author.photo || '/images/default-avatar.svg';
img.alt = author.name || 'Anonymous'; img.alt = '';
img.className = 'w-8 h-8 rounded-full ring-2 ring-white dark:ring-surface-900'; img.className = 'w-8 h-8 rounded-full ring-2 ring-white dark:ring-surface-900';
img.loading = 'lazy'; img.loading = 'lazy';
link.appendChild(img); link.appendChild(img);
row.appendChild(link); li.appendChild(link);
row.appendChild(li);
}); });
} }
@@ -278,7 +282,7 @@
const dateLink = document.createElement('a'); const dateLink = document.createElement('a');
dateLink.href = item.url || '#'; dateLink.href = item.url || '#';
dateLink.className = 'text-xs text-surface-500 hover:underline'; dateLink.className = 'text-xs text-surface-600 dark:text-surface-400 hover:underline';
dateLink.target = '_blank'; dateLink.target = '_blank';
dateLink.rel = 'noopener'; dateLink.rel = 'noopener';
@@ -396,8 +400,9 @@
header.className = 'text-sm font-semibold text-surface-600 dark:text-surface-400 uppercase tracking-wide mb-3'; header.className = 'text-sm font-semibold text-surface-600 dark:text-surface-400 uppercase tracking-wide mb-3';
header.textContent = `0 ${type === 'likes' ? 'Likes' : 'Reposts'}`; header.textContent = `0 ${type === 'likes' ? 'Likes' : 'Reposts'}`;
const row = document.createElement('div'); const row = document.createElement('ul');
row.className = 'facepile'; row.className = 'facepile';
row.setAttribute('role', 'list');
section.appendChild(header); section.appendChild(header);
section.appendChild(row); section.appendChild(row);
@@ -446,6 +451,8 @@
const section = document.createElement('section'); const section = document.createElement('section');
section.className = 'webmentions mt-8 pt-8 border-t border-surface-200 dark:border-surface-700'; section.className = 'webmentions mt-8 pt-8 border-t border-surface-200 dark:border-surface-700';
section.id = 'webmentions'; section.id = 'webmentions';
section.setAttribute('aria-live', 'polite');
section.setAttribute('aria-label', 'Webmentions');
const header = document.createElement('h2'); const header = document.createElement('h2');
header.className = 'text-xl font-bold text-surface-900 dark:text-surface-100 mb-6'; header.className = 'text-xl font-bold text-surface-900 dark:text-surface-100 mb-6';

View File

@@ -109,8 +109,8 @@ export function renderCard(url, metadata) {
<a href="${escapeHtml(url)}" rel="noopener" target="_blank" class="flex no-underline text-inherit hover:text-inherit"> <a href="${escapeHtml(url)}" rel="noopener" target="_blank" class="flex no-underline text-inherit hover:text-inherit">
<div class="flex-1 p-3 sm:p-4 min-w-0"> <div class="flex-1 p-3 sm:p-4 min-w-0">
<p class="font-semibold text-sm sm:text-base text-surface-900 dark:text-surface-100 truncate m-0">${escapeHtml(title)}</p> <p class="font-semibold text-sm sm:text-base text-surface-900 dark:text-surface-100 truncate m-0">${escapeHtml(title)}</p>
${desc ? `<p class="text-xs sm:text-sm text-surface-500 dark:text-surface-400 mt-1 m-0 line-clamp-2">${escapeHtml(desc)}</p>` : ""} ${desc ? `<p class="text-xs sm:text-sm text-surface-600 dark:text-surface-400 mt-1 m-0 line-clamp-2">${escapeHtml(desc)}</p>` : ""}
<p class="text-xs text-surface-400 dark:text-surface-500 mt-2 m-0">${faviconHtml}${escapeHtml(domain)}</p> <p class="text-xs text-surface-400 dark:text-surface-400 mt-2 m-0">${faviconHtml}${escapeHtml(domain)}</p>
</div> </div>
${imgHtml} ${imgHtml}
</a> </a>

View File

@@ -53,7 +53,7 @@ permalink: "likes/{% if pagination.pageNumber > 0 %}page/{{ pagination.pageNumbe
{% set likedUrl = post.data.likeOf or post.data.like_of %} {% set likedUrl = post.data.likeOf or post.data.like_of %}
{% if likedUrl %} {% if likedUrl %}
{% unfurl likedUrl %} {% unfurl likedUrl %}
<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-600 dark:text-surface-400 hover:underline break-all mt-1 inline-block" href="{{ likedUrl }}">
{{ likedUrl }} {{ likedUrl }}
</a> </a>
{% endif %} {% endif %}
@@ -82,7 +82,7 @@ permalink: "likes/{% if pagination.pageNumber > 0 %}page/{{ pagination.pageNumbe
Previous Previous
</a> </a>
{% else %} {% else %}
<span class="pagination-link disabled"> <span class="pagination-link disabled" aria-disabled="true">
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 19l-7-7 7-7"></path></svg> <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 19l-7-7 7-7"></path></svg>
Previous Previous
</span> </span>
@@ -94,7 +94,7 @@ permalink: "likes/{% if pagination.pageNumber > 0 %}page/{{ pagination.pageNumbe
<svg class="w-4 h-4" 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"></path></svg> <svg class="w-4 h-4" 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"></path></svg>
</a> </a>
{% else %} {% else %}
<span class="pagination-link disabled"> <span class="pagination-link disabled" aria-disabled="true">
Next Next
<svg class="w-4 h-4" 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"></path></svg> <svg class="w-4 h-4" 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"></path></svg>
</span> </span>

View File

@@ -99,9 +99,9 @@ withSidebar: true
</h2> </h2>
<p class="text-surface-600 dark:text-surface-400">{{ fwNowPlaying.artist }}</p> <p class="text-surface-600 dark:text-surface-400">{{ fwNowPlaying.artist }}</p>
{% if fwNowPlaying.album %} {% if fwNowPlaying.album %}
<p class="text-sm text-surface-500 mt-1">{{ fwNowPlaying.album }}</p> <p class="text-sm text-surface-600 dark:text-surface-400 mt-1">{{ fwNowPlaying.album }}</p>
{% endif %} {% endif %}
<p class="text-xs text-surface-500 mt-2">{{ fwNowPlaying.relativeTime }}</p> <p class="text-xs text-surface-600 dark:text-surface-400 mt-2">{{ fwNowPlaying.relativeTime }}</p>
</div> </div>
</div> </div>
</div> </div>
@@ -154,9 +154,9 @@ withSidebar: true
</h2> </h2>
<p class="text-surface-600 dark:text-surface-400">{{ lfmNowPlaying.artist }}</p> <p class="text-surface-600 dark:text-surface-400">{{ lfmNowPlaying.artist }}</p>
{% if lfmNowPlaying.album %} {% if lfmNowPlaying.album %}
<p class="text-sm text-surface-500 mt-1">{{ lfmNowPlaying.album }}</p> <p class="text-sm text-surface-600 dark:text-surface-400 mt-1">{{ lfmNowPlaying.album }}</p>
{% endif %} {% endif %}
<p class="text-xs text-surface-500 mt-2">{{ lfmNowPlaying.relativeTime }}</p> <p class="text-xs text-surface-600 dark:text-surface-400 mt-2">{{ lfmNowPlaying.relativeTime }}</p>
</div> </div>
</div> </div>
</div> </div>
@@ -187,15 +187,15 @@ withSidebar: true
<div class="grid grid-cols-3 gap-4 text-center"> <div class="grid grid-cols-3 gap-4 text-center">
<div> <div>
<div class="text-2xl font-bold text-surface-900 dark:text-surface-100">{{ funkwhaleActivity.stats.summary.all.totalPlays | default(0) }}</div> <div class="text-2xl font-bold text-surface-900 dark:text-surface-100">{{ funkwhaleActivity.stats.summary.all.totalPlays | default(0) }}</div>
<div class="text-xs text-surface-500 uppercase">Plays</div> <div class="text-xs text-surface-600 dark:text-surface-400 uppercase">Plays</div>
</div> </div>
<div> <div>
<div class="text-2xl font-bold text-surface-900 dark:text-surface-100">{{ funkwhaleActivity.stats.summary.all.uniqueArtists | default(0) }}</div> <div class="text-2xl font-bold text-surface-900 dark:text-surface-100">{{ funkwhaleActivity.stats.summary.all.uniqueArtists | default(0) }}</div>
<div class="text-xs text-surface-500 uppercase">Artists</div> <div class="text-xs text-surface-600 dark:text-surface-400 uppercase">Artists</div>
</div> </div>
<div> <div>
<div class="text-2xl font-bold text-surface-900 dark:text-surface-100">{{ funkwhaleActivity.stats.summary.all.uniqueTracks | default(0) }}</div> <div class="text-2xl font-bold text-surface-900 dark:text-surface-100">{{ funkwhaleActivity.stats.summary.all.uniqueTracks | default(0) }}</div>
<div class="text-xs text-surface-500 uppercase">Tracks</div> <div class="text-xs text-surface-600 dark:text-surface-400 uppercase">Tracks</div>
</div> </div>
</div> </div>
{# Top Artists #} {# Top Artists #}
@@ -206,7 +206,7 @@ withSidebar: true
{% for artist in funkwhaleActivity.stats.topArtists.all | head(5) %} {% for artist in funkwhaleActivity.stats.topArtists.all | head(5) %}
<div class="flex justify-between text-sm"> <div class="flex justify-between text-sm">
<span class="text-surface-600 dark:text-surface-400 truncate">{{ artist.name }}</span> <span class="text-surface-600 dark:text-surface-400 truncate">{{ artist.name }}</span>
<span class="text-surface-500 ml-2">{{ artist.playCount }}</span> <span class="text-surface-600 dark:text-surface-400 ml-2">{{ artist.playCount }}</span>
</div> </div>
{% endfor %} {% endfor %}
</div> </div>
@@ -225,15 +225,15 @@ withSidebar: true
<div class="grid grid-cols-3 gap-4 text-center"> <div class="grid grid-cols-3 gap-4 text-center">
<div> <div>
<div class="text-2xl font-bold text-surface-900 dark:text-surface-100">{{ lastfmActivity.stats.summary.all.totalPlays | default(0) }}</div> <div class="text-2xl font-bold text-surface-900 dark:text-surface-100">{{ lastfmActivity.stats.summary.all.totalPlays | default(0) }}</div>
<div class="text-xs text-surface-500 uppercase">Scrobbles</div> <div class="text-xs text-surface-600 dark:text-surface-400 uppercase">Scrobbles</div>
</div> </div>
<div> <div>
<div class="text-2xl font-bold text-surface-900 dark:text-surface-100">{{ lastfmActivity.stats.summary.all.uniqueArtists | default(0) }}</div> <div class="text-2xl font-bold text-surface-900 dark:text-surface-100">{{ lastfmActivity.stats.summary.all.uniqueArtists | default(0) }}</div>
<div class="text-xs text-surface-500 uppercase">Artists</div> <div class="text-xs text-surface-600 dark:text-surface-400 uppercase">Artists</div>
</div> </div>
<div> <div>
<div class="text-2xl font-bold text-surface-900 dark:text-surface-100">{{ lastfmActivity.stats.summary.all.lovedCount | default(0) }}</div> <div class="text-2xl font-bold text-surface-900 dark:text-surface-100">{{ lastfmActivity.stats.summary.all.lovedCount | default(0) }}</div>
<div class="text-xs text-surface-500 uppercase">Loved</div> <div class="text-xs text-surface-600 dark:text-surface-400 uppercase">Loved</div>
</div> </div>
</div> </div>
{# Top Artists from Last.fm #} {# Top Artists from Last.fm #}
@@ -244,7 +244,7 @@ withSidebar: true
{% for artist in lastfmActivity.stats.topArtists.all | head(5) %} {% for artist in lastfmActivity.stats.topArtists.all | head(5) %}
<div class="flex justify-between text-sm"> <div class="flex justify-between text-sm">
<span class="text-surface-600 dark:text-surface-400 truncate">{{ artist.name }}</span> <span class="text-surface-600 dark:text-surface-400 truncate">{{ artist.name }}</span>
<span class="text-surface-500 ml-2">{{ artist.playCount }}</span> <span class="text-surface-600 dark:text-surface-400 ml-2">{{ artist.playCount }}</span>
</div> </div>
{% endfor %} {% endfor %}
</div> </div>
@@ -294,7 +294,7 @@ withSidebar: true
<div class="text-right flex-shrink-0"> <div class="text-right flex-shrink-0">
<span class="inline-block px-2 py-0.5 text-xs font-medium bg-purple-100 dark:bg-purple-900/30 text-purple-700 dark:text-purple-400 rounded-full mb-1">Funkwhale</span> <span class="inline-block px-2 py-0.5 text-xs font-medium bg-purple-100 dark:bg-purple-900/30 text-purple-700 dark:text-purple-400 rounded-full mb-1">Funkwhale</span>
<span class="text-xs text-surface-500 block">{{ listening.relativeTime }}</span> <span class="text-xs text-surface-600 dark:text-surface-400 block">{{ listening.relativeTime }}</span>
<button <button
class="share-post-btn mt-1" class="share-post-btn mt-1"
data-share-url="{{ listening.trackUrl }}" data-share-url="{{ listening.trackUrl }}"
@@ -351,7 +351,7 @@ withSidebar: true
<div class="text-right flex-shrink-0"> <div class="text-right flex-shrink-0">
<span class="inline-block px-2 py-0.5 text-xs font-medium bg-purple-100 dark:bg-purple-900/30 text-purple-700 dark:text-purple-400 rounded-full mb-1">Last.fm</span> <span class="inline-block px-2 py-0.5 text-xs font-medium bg-purple-100 dark:bg-purple-900/30 text-purple-700 dark:text-purple-400 rounded-full mb-1">Last.fm</span>
<span class="text-xs text-surface-500 block">{{ scrobble.relativeTime }}</span> <span class="text-xs text-surface-600 dark:text-surface-400 block">{{ scrobble.relativeTime }}</span>
<button <button
class="share-post-btn mt-1" class="share-post-btn mt-1"
data-share-url="{{ scrobble.trackUrl }}" data-share-url="{{ scrobble.trackUrl }}"
@@ -391,7 +391,7 @@ withSidebar: 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"/> <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> </svg>
Loved Tracks Loved Tracks
<span class="text-sm font-normal text-surface-500">(Last.fm)</span> <span class="text-sm font-normal text-surface-600 dark:text-surface-400">(Last.fm)</span>
</h2> </h2>
<div class="grid gap-3 sm:gap-4 md:grid-cols-2"> <div class="grid gap-3 sm:gap-4 md:grid-cols-2">
@@ -452,7 +452,7 @@ withSidebar: 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"/> <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> </svg>
Favorite Tracks Favorite Tracks
<span class="text-sm font-normal text-surface-500">(Funkwhale)</span> <span class="text-sm font-normal text-surface-600 dark:text-surface-400">(Funkwhale)</span>
</h2> </h2>
<div class="grid gap-3 sm:gap-4 md:grid-cols-2"> <div class="grid gap-3 sm:gap-4 md:grid-cols-2">
@@ -478,7 +478,7 @@ withSidebar: true
</h3> </h3>
<p class="text-sm text-surface-600 dark:text-surface-400 truncate">{{ favorite.artist }}</p> <p class="text-sm text-surface-600 dark:text-surface-400 truncate">{{ favorite.artist }}</p>
{% if favorite.album %} {% if favorite.album %}
<p class="text-xs text-surface-500 truncate">{{ favorite.album }}</p> <p class="text-xs text-surface-600 dark:text-surface-400 truncate">{{ favorite.album }}</p>
{% endif %} {% endif %}
</div> </div>
<button <button

View File

@@ -10,10 +10,10 @@ withSidebar: true
<p class="text-surface-600 dark:text-surface-400"> <p class="text-surface-600 dark:text-surface-400">
Aggregated content from my favorite feeds Aggregated content from my favorite feeds
</p> </p>
<p class="text-xs text-surface-500 mt-2" x-show="lastUpdated"> <p class="text-xs text-surface-600 dark:text-surface-400 mt-2" x-show="lastUpdated">
Last updated: <span class="font-mono" x-text="formatDate(lastUpdated, 'full')"></span> Last updated: <span class="font-mono" x-text="formatDate(lastUpdated, 'full')"></span>
<button @click="refresh()" class="ml-2 text-accent-600 hover:text-accent-700 dark:text-accent-400 rounded" :disabled="loading"> <button @click="refresh()" class="ml-2 text-accent-600 hover:text-accent-700 dark:text-accent-400 rounded" :disabled="loading" aria-label="Refresh news">
<svg class="w-3 h-3 inline" :class="{ 'animate-spin': loading }" fill="none" stroke="currentColor" viewBox="0 0 24 24"> <svg class="w-3 h-3 inline" :class="{ 'animate-spin': loading }" 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"/> <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> </svg>
</button> </button>
@@ -21,8 +21,8 @@ withSidebar: true
</header> </header>
{# Loading State #} {# Loading State #}
<div x-show="loading && items.length === 0" class="text-center py-12"> <div x-show="loading && items.length === 0" class="text-center py-12" role="status">
<svg class="w-8 h-8 mx-auto text-accent-600 animate-spin mb-4" fill="none" viewBox="0 0 24 24"> <svg class="w-8 h-8 mx-auto text-accent-600 animate-spin mb-4" fill="none" viewBox="0 0 24 24" aria-hidden="true">
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle> <circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
<path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path> <path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
</svg> </svg>
@@ -30,7 +30,7 @@ withSidebar: true
</div> </div>
{# Error State #} {# Error State #}
<div x-show="error" class="bg-red-50 dark:bg-red-900/20 border border-red-200 dark:border-red-800 rounded-lg p-4 mb-6"> <div x-show="error" role="alert" class="bg-red-50 dark:bg-red-900/20 border border-red-200 dark:border-red-800 rounded-lg p-4 mb-6">
<p class="text-red-700 dark:text-red-400" x-text="error"></p> <p class="text-red-700 dark:text-red-400" x-text="error"></p>
<button @click="refresh()" class="mt-2 text-sm text-red-600 hover:text-red-700 underline">Try again</button> <button @click="refresh()" class="mt-2 text-sm text-red-600 hover:text-red-700 underline">Try again</button>
</div> </div>
@@ -45,6 +45,7 @@ withSidebar: true
<button <button
@click="viewMode = 'list'" @click="viewMode = 'list'"
:class="viewMode === 'list' ? 'bg-accent-600 text-white' : 'bg-surface-100 dark:bg-surface-800 text-surface-700 dark:text-surface-300 hover:bg-surface-200 dark:hover:bg-surface-700'" :class="viewMode === 'list' ? 'bg-accent-600 text-white' : 'bg-surface-100 dark:bg-surface-800 text-surface-700 dark:text-surface-300 hover:bg-surface-200 dark:hover:bg-surface-700'"
:aria-pressed="(viewMode === 'list').toString()"
class="px-3 py-2 rounded-lg text-sm font-medium transition-colors flex items-center gap-2" class="px-3 py-2 rounded-lg text-sm font-medium transition-colors flex items-center gap-2"
title="List view" title="List view"
> >
@@ -56,6 +57,7 @@ withSidebar: true
<button <button
@click="viewMode = 'card'" @click="viewMode = 'card'"
:class="viewMode === 'card' ? 'bg-accent-600 text-white' : 'bg-surface-100 dark:bg-surface-800 text-surface-700 dark:text-surface-300 hover:bg-surface-200 dark:hover:bg-surface-700'" :class="viewMode === 'card' ? 'bg-accent-600 text-white' : 'bg-surface-100 dark:bg-surface-800 text-surface-700 dark:text-surface-300 hover:bg-surface-200 dark:hover:bg-surface-700'"
:aria-pressed="(viewMode === 'card').toString()"
class="px-3 py-2 rounded-lg text-sm font-medium transition-colors flex items-center gap-2" class="px-3 py-2 rounded-lg text-sm font-medium transition-colors flex items-center gap-2"
title="Card view" title="Card view"
> >
@@ -67,6 +69,7 @@ withSidebar: true
<button <button
@click="viewMode = 'full'" @click="viewMode = 'full'"
:class="viewMode === 'full' ? 'bg-accent-600 text-white' : 'bg-surface-100 dark:bg-surface-800 text-surface-700 dark:text-surface-300 hover:bg-surface-200 dark:hover:bg-surface-700'" :class="viewMode === 'full' ? 'bg-accent-600 text-white' : 'bg-surface-100 dark:bg-surface-800 text-surface-700 dark:text-surface-300 hover:bg-surface-200 dark:hover:bg-surface-700'"
:aria-pressed="(viewMode === 'full').toString()"
class="px-3 py-2 rounded-lg text-sm font-medium transition-colors flex items-center gap-2" class="px-3 py-2 rounded-lg text-sm font-medium transition-colors flex items-center gap-2"
title="Expanded view" title="Expanded view"
> >
@@ -79,7 +82,9 @@ withSidebar: true
{# Feed Filter Dropdown #} {# Feed Filter Dropdown #}
<div class="relative" x-show="feeds.length > 1"> <div class="relative" x-show="feeds.length > 1">
<label for="news-feed-filter" class="sr-only">Filter by feed source</label>
<select <select
id="news-feed-filter"
x-model="filterFeed" x-model="filterFeed"
class="appearance-none bg-surface-100 dark:bg-surface-800 border border-surface-300 dark:border-surface-600 rounded-lg px-4 py-2 pr-8 text-sm text-surface-700 dark:text-surface-300 transition-colors" class="appearance-none bg-surface-100 dark:bg-surface-800 border border-surface-300 dark:border-surface-600 rounded-lg px-4 py-2 pr-8 text-sm text-surface-700 dark:text-surface-300 transition-colors"
> >
@@ -88,7 +93,7 @@ withSidebar: true
<option :value="feed.id" x-text="feed.title"></option> <option :value="feed.id" x-text="feed.title"></option>
</template> </template>
</select> </select>
<svg class="absolute right-2 top-1/2 transform -translate-y-1/2 w-4 h-4 text-surface-500 pointer-events-none" fill="none" stroke="currentColor" viewBox="0 0 24 24"> <svg class="absolute right-2 top-1/2 transform -translate-y-1/2 w-4 h-4 text-surface-600 dark:text-surface-400 pointer-events-none" 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"/> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"/>
</svg> </svg>
</div> </div>
@@ -97,11 +102,11 @@ withSidebar: true
{# Stats Bar #} {# Stats Bar #}
<div class="flex flex-wrap gap-4 mb-6 p-4 bg-surface-50 dark:bg-surface-800/50 rounded-lg border border-surface-200 dark:border-surface-700 shadow-sm text-sm"> <div class="flex flex-wrap gap-4 mb-6 p-4 bg-surface-50 dark:bg-surface-800/50 rounded-lg border border-surface-200 dark:border-surface-700 shadow-sm text-sm">
<div class="flex items-center gap-2"> <div class="flex items-center gap-2">
<span class="text-surface-500">Feeds:</span> <span class="text-surface-600 dark:text-surface-400">Feeds:</span>
<span class="font-medium font-mono text-surface-900 dark:text-surface-100" x-text="status?.stats?.feedsCount || feeds.length"></span> <span class="font-medium font-mono text-surface-900 dark:text-surface-100" x-text="status?.stats?.feedsCount || feeds.length"></span>
</div> </div>
<div class="flex items-center gap-2"> <div class="flex items-center gap-2">
<span class="text-surface-500">Items:</span> <span class="text-surface-600 dark:text-surface-400">Items:</span>
<span class="font-medium font-mono text-surface-900 dark:text-surface-100" x-text="status?.stats?.itemsCount || items.length"></span> <span class="font-medium font-mono text-surface-900 dark:text-surface-100" x-text="status?.stats?.itemsCount || items.length"></span>
</div> </div>
<div x-show="status?.status === 'syncing'" class="flex items-center gap-2 text-orange-600 dark:text-orange-400"> <div x-show="status?.status === 'syncing'" class="flex items-center gap-2 text-orange-600 dark:text-orange-400">
@@ -144,7 +149,7 @@ withSidebar: true
></a> ></a>
</h2> </h2>
<p x-show="item.description" class="text-sm text-surface-600 dark:text-surface-400 line-clamp-2 mb-2" x-text="item.description"></p> <p x-show="item.description" class="text-sm text-surface-600 dark:text-surface-400 line-clamp-2 mb-2" x-text="item.description"></p>
<div class="flex flex-wrap items-center gap-2 text-xs text-surface-500"> <div class="flex flex-wrap items-center gap-2 text-xs text-surface-600 dark:text-surface-400">
<a <a
:href="item.sourceUrl || getFeedUrl(item.feedId) || item.link" :href="item.sourceUrl || getFeedUrl(item.feedId) || item.link"
class="inline-flex items-center gap-1 px-2 py-0.5 bg-surface-100 dark:bg-surface-700 rounded-full hover:bg-surface-200 dark:hover:bg-surface-600 transition-colors" class="inline-flex items-center gap-1 px-2 py-0.5 bg-surface-100 dark:bg-surface-700 rounded-full hover:bg-surface-200 dark:hover:bg-surface-600 transition-colors"
@@ -210,7 +215,7 @@ withSidebar: true
></a> ></a>
</h2> </h2>
<p x-show="item.description" class="text-sm text-surface-600 dark:text-surface-400 line-clamp-3 mb-3" x-text="item.description"></p> <p x-show="item.description" class="text-sm text-surface-600 dark:text-surface-400 line-clamp-3 mb-3" x-text="item.description"></p>
<div class="flex items-center justify-between text-xs text-surface-500"> <div class="flex items-center justify-between text-xs text-surface-600 dark:text-surface-400">
<span class="truncate max-w-[60%]" x-text="truncate(item.sourceTitle || item.feedTitle, 20)"></span> <span class="truncate max-w-[60%]" x-text="truncate(item.sourceTitle || item.feedTitle, 20)"></span>
<time class="font-mono text-sm" :datetime="item.pubDate" x-text="formatDate(item.pubDate)"></time> <time class="font-mono text-sm" :datetime="item.pubDate" x-text="formatDate(item.pubDate)"></time>
</div> </div>
@@ -264,7 +269,7 @@ withSidebar: true
<span class="font-medium text-surface-700 dark:text-surface-300" x-text="item.sourceTitle || item.feedTitle"></span> <span class="font-medium text-surface-700 dark:text-surface-300" x-text="item.sourceTitle || item.feedTitle"></span>
</a> </a>
<span x-show="item.author" class="text-surface-600 dark:text-surface-400" x-text="'by ' + item.author"></span> <span x-show="item.author" class="text-surface-600 dark:text-surface-400" x-text="'by ' + item.author"></span>
<time :datetime="item.pubDate" class="font-mono text-sm text-surface-500" x-text="formatDate(item.pubDate, 'long')"></time> <time :datetime="item.pubDate" class="font-mono text-sm text-surface-600 dark:text-surface-400" x-text="formatDate(item.pubDate, 'long')"></time>
</div> </div>
<h2 class="text-xl sm:text-2xl font-bold text-surface-900 dark:text-surface-100 mb-4"> <h2 class="text-xl sm:text-2xl font-bold text-surface-900 dark:text-surface-100 mb-4">
@@ -349,11 +354,11 @@ withSidebar: true
{# Empty State #} {# Empty State #}
<div x-show="!loading && items.length === 0 && !error" class="text-center py-12"> <div x-show="!loading && items.length === 0 && !error" class="text-center py-12">
<svg class="w-16 h-16 mx-auto text-surface-300 dark:text-surface-600 mb-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"> <svg class="w-16 h-16 mx-auto text-surface-300 dark:text-surface-400 mb-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M19 20H5a2 2 0 01-2-2V6a2 2 0 012-2h10a2 2 0 012 2v1m2 13a2 2 0 01-2-2V7m2 13a2 2 0 002-2V9a2 2 0 00-2-2h-2m-4-3H9M7 16h6M7 8h6v4H7V8z"/> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M19 20H5a2 2 0 01-2-2V6a2 2 0 012-2h10a2 2 0 012 2v1m2 13a2 2 0 01-2-2V7m2 13a2 2 0 002-2V9a2 2 0 00-2-2h-2m-4-3H9M7 16h6M7 8h6v4H7V8z"/>
</svg> </svg>
<p class="text-surface-600 dark:text-surface-400 text-lg">No news items yet.</p> <p class="text-surface-600 dark:text-surface-400 text-lg">No news items yet.</p>
<p class="text-surface-500 text-sm mt-2">Add some RSS feeds to get started.</p> <p class="text-surface-600 dark:text-surface-400 text-sm mt-2">Add some RSS feeds to get started.</p>
</div> </div>
</div> </div>

View File

@@ -28,7 +28,7 @@ permalink: "notes/{% if pagination.pageNumber > 0 %}page/{{ pagination.pageNumbe
<li class="h-entry post-card border-l-[3px] border-l-teal-400 dark:border-l-teal-500"> <li class="h-entry post-card border-l-[3px] border-l-teal-400 dark:border-l-teal-500">
<div class="post-header"> <div class="post-header">
<a class="u-url" href="{{ post.url }}"> <a class="u-url" href="{{ post.url }}">
<time class="dt-published text-sm text-surface-500 dark:text-surface-400 font-medium font-mono" datetime="{{ post.date | isoDate }}"> <time class="dt-published text-sm text-surface-600 dark:text-surface-400 font-medium font-mono" datetime="{{ post.date | isoDate }}">
{{ post.date | dateDisplay }} {{ post.date | dateDisplay }}
</time> </time>
</a> </a>
@@ -69,7 +69,7 @@ permalink: "notes/{% if pagination.pageNumber > 0 %}page/{{ pagination.pageNumbe
Previous Previous
</a> </a>
{% else %} {% else %}
<span class="pagination-link disabled"> <span class="pagination-link disabled" aria-disabled="true">
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 19l-7-7 7-7"></path></svg> <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 19l-7-7 7-7"></path></svg>
Previous Previous
</span> </span>
@@ -81,7 +81,7 @@ permalink: "notes/{% if pagination.pageNumber > 0 %}page/{{ pagination.pageNumbe
<svg class="w-4 h-4" 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"></path></svg> <svg class="w-4 h-4" 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"></path></svg>
</a> </a>
{% else %} {% else %}
<span class="pagination-link disabled"> <span class="pagination-link disabled" aria-disabled="true">
Next Next
<svg class="w-4 h-4" 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"></path></svg> <svg class="w-4 h-4" 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"></path></svg>
</span> </span>

View File

@@ -77,7 +77,7 @@ permalink: "photos/{% if pagination.pageNumber > 0 %}page/{{ pagination.pageNumb
Previous Previous
</a> </a>
{% else %} {% else %}
<span class="pagination-link disabled"> <span class="pagination-link disabled" aria-disabled="true">
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 19l-7-7 7-7"></path></svg> <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 19l-7-7 7-7"></path></svg>
Previous Previous
</span> </span>
@@ -89,7 +89,7 @@ permalink: "photos/{% if pagination.pageNumber > 0 %}page/{{ pagination.pageNumb
<svg class="w-4 h-4" 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"></path></svg> <svg class="w-4 h-4" 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"></path></svg>
</a> </a>
{% else %} {% else %}
<span class="pagination-link disabled"> <span class="pagination-link disabled" aria-disabled="true">
Next Next
<svg class="w-4 h-4" 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"></path></svg> <svg class="w-4 h-4" 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"></path></svg>
</span> </span>

View File

@@ -14,10 +14,10 @@ permalink: /podroll/
<p class="text-surface-600 dark:text-surface-400"> <p class="text-surface-600 dark:text-surface-400">
My podcast subscriptions - recent episodes from <span x-text="sources.length" class="font-medium font-mono"></span> podcasts My podcast subscriptions - recent episodes from <span x-text="sources.length" class="font-medium font-mono"></span> podcasts
</p> </p>
<p class="text-xs text-surface-500 mt-2" x-show="status?.episodes?.lastSync"> <p class="text-xs text-surface-600 dark:text-surface-400 mt-2" x-show="status?.episodes?.lastSync">
Last synced: <span class="font-mono" x-text="formatDate(status?.episodes?.lastSync, 'full')"></span> Last synced: <span class="font-mono" x-text="formatDate(status?.episodes?.lastSync, 'full')"></span>
<button @click="refresh()" class="ml-2 text-orange-600 hover:text-orange-700 dark:text-orange-400 transition-colors rounded" :disabled="loading"> <button @click="refresh()" class="ml-2 text-orange-600 hover:text-orange-700 dark:text-orange-400 transition-colors rounded" :disabled="loading" aria-label="Refresh episodes">
<svg class="w-3 h-3 inline" :class="{ 'animate-spin': loading }" fill="none" stroke="currentColor" viewBox="0 0 24 24"> <svg class="w-3 h-3 inline" :class="{ 'animate-spin': loading }" 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"/> <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> </svg>
</button> </button>
@@ -28,8 +28,8 @@ permalink: /podroll/
{# Main Content - Episodes #} {# Main Content - Episodes #}
<div class="main-content"> <div class="main-content">
{# Loading State #} {# Loading State #}
<div x-show="loading && episodes.length === 0" class="text-center py-12"> <div x-show="loading && episodes.length === 0" class="text-center py-12" role="status">
<svg class="w-8 h-8 mx-auto text-orange-600 animate-spin mb-4" fill="none" viewBox="0 0 24 24"> <svg class="w-8 h-8 mx-auto text-orange-600 animate-spin mb-4" fill="none" viewBox="0 0 24 24" aria-hidden="true">
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle> <circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
<path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path> <path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
</svg> </svg>
@@ -37,7 +37,7 @@ permalink: /podroll/
</div> </div>
{# Error State #} {# Error State #}
<div x-show="error" class="bg-red-50 dark:bg-red-900/20 border border-red-200 dark:border-red-800 rounded-lg p-4 mb-6"> <div x-show="error" role="alert" class="bg-red-50 dark:bg-red-900/20 border border-red-200 dark:border-red-800 rounded-lg p-4 mb-6">
<p class="text-red-700 dark:text-red-400" x-text="error"></p> <p class="text-red-700 dark:text-red-400" x-text="error"></p>
<button @click="refresh()" class="mt-2 text-sm text-red-600 hover:text-red-700 underline">Try again</button> <button @click="refresh()" class="mt-2 text-sm text-red-600 hover:text-red-700 underline">Try again</button>
</div> </div>
@@ -45,7 +45,9 @@ permalink: /podroll/
{# Filter by Podcast #} {# Filter by Podcast #}
<div x-show="episodes.length > 0" class="mb-6"> <div x-show="episodes.length > 0" class="mb-6">
<div class="relative"> <div class="relative">
<label for="podroll-filter" class="sr-only">Filter by podcast</label>
<select <select
id="podroll-filter"
x-model="filterPodcast" x-model="filterPodcast"
class="w-full sm:w-auto appearance-none bg-surface-100 dark:bg-surface-800 border border-surface-300 dark:border-surface-600 rounded-lg px-4 py-2 pr-10 text-sm text-surface-700 dark:text-surface-300" class="w-full sm:w-auto appearance-none bg-surface-100 dark:bg-surface-800 border border-surface-300 dark:border-surface-600 rounded-lg px-4 py-2 pr-10 text-sm text-surface-700 dark:text-surface-300"
> >
@@ -54,7 +56,7 @@ permalink: /podroll/
<option :value="source.title" x-text="source.title"></option> <option :value="source.title" x-text="source.title"></option>
</template> </template>
</select> </select>
<svg class="absolute right-3 top-1/2 transform -translate-y-1/2 w-4 h-4 text-surface-500 pointer-events-none" fill="none" stroke="currentColor" viewBox="0 0 24 24"> <svg class="absolute right-3 top-1/2 transform -translate-y-1/2 w-4 h-4 text-surface-600 dark:text-surface-400 pointer-events-none" 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"/> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"/>
</svg> </svg>
</div> </div>
@@ -70,7 +72,7 @@ permalink: /podroll/
<h2 class="font-semibold text-lg text-surface-900 dark:text-surface-100 mb-1"> <h2 class="font-semibold text-lg text-surface-900 dark:text-surface-100 mb-1">
<a :href="episode.url" class="hover:text-orange-600 dark:hover:text-orange-400 transition-colors" target="_blank" rel="noopener" x-text="episode.title"></a> <a :href="episode.url" class="hover:text-orange-600 dark:hover:text-orange-400 transition-colors" target="_blank" rel="noopener" x-text="episode.title"></a>
</h2> </h2>
<div class="flex flex-wrap items-center gap-2 text-sm text-surface-500"> <div class="flex flex-wrap items-center gap-2 text-sm text-surface-600 dark:text-surface-400">
<a <a
x-show="episode.podcast" x-show="episode.podcast"
:href="episode.podcast?.url || '#'" :href="episode.podcast?.url || '#'"
@@ -130,7 +132,7 @@ permalink: /podroll/
<a <a
x-show="episode.podcast?.feedUrl" x-show="episode.podcast?.feedUrl"
:href="episode.podcast?.feedUrl" :href="episode.podcast?.feedUrl"
class="inline-flex items-center gap-2 text-sm text-surface-500 hover:text-surface-700 dark:hover:text-surface-300 transition-colors" class="inline-flex items-center gap-2 text-sm text-surface-600 dark:text-surface-400 hover:text-surface-700 dark:hover:text-surface-300 transition-colors"
target="_blank" target="_blank"
rel="noopener" rel="noopener"
title="Subscribe to feed" title="Subscribe to feed"
@@ -187,11 +189,11 @@ permalink: /podroll/
{# Empty State #} {# Empty State #}
<div x-show="!loading && episodes.length === 0 && !error" class="text-center py-12"> <div x-show="!loading && episodes.length === 0 && !error" class="text-center py-12">
<svg class="w-16 h-16 mx-auto text-surface-300 dark:text-surface-600 mb-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"> <svg class="w-16 h-16 mx-auto text-surface-300 dark:text-surface-400 mb-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M19 11a7 7 0 01-7 7m0 0a7 7 0 01-7-7m7 7v4m0 0H8m4 0h4m-4-8a3 3 0 01-3-3V5a3 3 0 116 0v6a3 3 0 01-3 3z"/> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M19 11a7 7 0 01-7 7m0 0a7 7 0 01-7-7m7 7v4m0 0H8m4 0h4m-4-8a3 3 0 01-3-3V5a3 3 0 116 0v6a3 3 0 01-3 3z"/>
</svg> </svg>
<p class="text-surface-600 dark:text-surface-400 text-lg">No podcast episodes yet.</p> <p class="text-surface-600 dark:text-surface-400 text-lg">No podcast episodes yet.</p>
<p class="text-surface-500 text-sm mt-2">Episodes will appear once the sync completes.</p> <p class="text-surface-600 dark:text-surface-400 text-sm mt-2">Episodes will appear once the sync completes.</p>
</div> </div>
</div> </div>
@@ -204,10 +206,10 @@ permalink: /podroll/
<path d="M4 4.44v2.83c7.03 0 12.73 5.7 12.73 12.73h2.83c0-8.59-6.97-15.56-15.56-15.56zm0 5.66v2.83c3.9 0 7.07 3.17 7.07 7.07h2.83c0-5.47-4.43-9.9-9.9-9.9z"/> <path d="M4 4.44v2.83c7.03 0 12.73 5.7 12.73 12.73h2.83c0-8.59-6.97-15.56-15.56-15.56zm0 5.66v2.83c3.9 0 7.07 3.17 7.07 7.07h2.83c0-5.47-4.43-9.9-9.9-9.9z"/>
</svg> </svg>
Subscriptions Subscriptions
<span class="text-sm font-normal font-mono text-surface-500" x-text="'(' + sources.length + ')'"></span> <span class="text-sm font-normal font-mono text-surface-600 dark:text-surface-400" x-text="'(' + sources.length + ')'"></span>
</h3> </h3>
<div x-show="sources.length === 0 && !loading" class="text-sm text-surface-500 text-center py-4"> <div x-show="sources.length === 0 && !loading" class="text-sm text-surface-600 dark:text-surface-400 text-center py-4">
No subscriptions loaded yet. No subscriptions loaded yet.
</div> </div>
@@ -225,7 +227,7 @@ permalink: /podroll/
</div> </div>
<div class="flex-1 min-w-0"> <div class="flex-1 min-w-0">
<p class="text-sm font-medium text-surface-900 dark:text-surface-100 truncate" x-text="source.title"></p> <p class="text-sm font-medium text-surface-900 dark:text-surface-100 truncate" x-text="source.title"></p>
<p x-show="source.category" class="text-xs text-surface-500 truncate" x-text="source.category"></p> <p x-show="source.category" class="text-xs text-surface-600 dark:text-surface-400 truncate" x-text="source.category"></p>
</div> </div>
<a <a
:href="source.xmlUrl" :href="source.xmlUrl"

View File

@@ -20,8 +20,8 @@ permalink: /readlater/
{# Main Content #} {# Main Content #}
<div class="main-content"> <div class="main-content">
{# Loading State #} {# Loading State #}
<div x-show="loading" class="text-center py-12"> <div x-show="loading" class="text-center py-12" role="status">
<svg class="w-8 h-8 mx-auto text-orange-600 animate-spin mb-4" fill="none" viewBox="0 0 24 24"> <svg class="w-8 h-8 mx-auto text-orange-600 animate-spin mb-4" fill="none" viewBox="0 0 24 24" aria-hidden="true">
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle> <circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
<path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path> <path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
</svg> </svg>
@@ -29,7 +29,7 @@ permalink: /readlater/
</div> </div>
{# Error State #} {# Error State #}
<div x-show="error" class="bg-red-50 dark:bg-red-900/20 border border-red-200 dark:border-red-800 rounded-lg p-4 mb-6"> <div x-show="error" role="alert" class="bg-red-50 dark:bg-red-900/20 border border-red-200 dark:border-red-800 rounded-lg p-4 mb-6">
<p class="text-red-700 dark:text-red-400" x-text="error"></p> <p class="text-red-700 dark:text-red-400" x-text="error"></p>
<button @click="fetchData()" class="mt-2 text-sm text-red-600 hover:text-red-700 underline">Try again</button> <button @click="fetchData()" class="mt-2 text-sm text-red-600 hover:text-red-700 underline">Try again</button>
</div> </div>
@@ -38,7 +38,9 @@ permalink: /readlater/
<div x-show="!loading && items.length > 0" class="mb-6"> <div x-show="!loading && items.length > 0" class="mb-6">
<div class="flex flex-wrap items-center gap-3"> <div class="flex flex-wrap items-center gap-3">
<div class="relative"> <div class="relative">
<label for="readlater-source" class="sr-only">Filter by source</label>
<select <select
id="readlater-source"
x-model="selectedSource" x-model="selectedSource"
@change="fetchData()" @change="fetchData()"
class="appearance-none bg-surface-50 dark:bg-surface-800 border border-surface-300 dark:border-surface-600 rounded-lg pl-3 pr-8 py-2 text-sm" class="appearance-none bg-surface-50 dark:bg-surface-800 border border-surface-300 dark:border-surface-600 rounded-lg pl-3 pr-8 py-2 text-sm"
@@ -53,7 +55,9 @@ permalink: /readlater/
</svg> </svg>
</div> </div>
<div class="relative flex-1 max-w-xs"> <div class="relative flex-1 max-w-xs">
<label for="readlater-search" class="sr-only">Search reading list</label>
<input <input
id="readlater-search"
type="search" type="search"
x-model.debounce.300ms="searchQuery" x-model.debounce.300ms="searchQuery"
@input="fetchData()" @input="fetchData()"
@@ -91,7 +95,7 @@ permalink: /readlater/
<h2 class="font-semibold text-surface-900 dark:text-surface-100 mb-1"> <h2 class="font-semibold text-surface-900 dark:text-surface-100 mb-1">
<a :href="item.url" class="hover:text-orange-600 dark:hover:text-orange-400 transition-colors" target="_blank" rel="noopener" x-text="item.title"></a> <a :href="item.url" class="hover:text-orange-600 dark:hover:text-orange-400 transition-colors" target="_blank" rel="noopener" x-text="item.title"></a>
</h2> </h2>
<div class="flex flex-wrap items-center gap-2 text-sm text-surface-500"> <div class="flex flex-wrap items-center gap-2 text-sm text-surface-600 dark:text-surface-400">
<span <span
class="inline-flex items-center px-2 py-0.5 bg-surface-100 dark:bg-surface-700 rounded-full text-xs" class="inline-flex items-center px-2 py-0.5 bg-surface-100 dark:bg-surface-700 rounded-full text-xs"
x-text="item.source" x-text="item.source"
@@ -132,11 +136,11 @@ permalink: /readlater/
{# Empty State #} {# Empty State #}
<div x-show="!loading && items.length === 0 && !error" class="text-center py-12"> <div x-show="!loading && items.length === 0 && !error" class="text-center py-12">
<svg class="w-16 h-16 mx-auto text-surface-300 dark:text-surface-600 mb-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"> <svg class="w-16 h-16 mx-auto text-surface-300 dark:text-surface-400 mb-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M5 5a2 2 0 012-2h10a2 2 0 012 2v16l-7-3.5L5 21V5z"/> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M5 5a2 2 0 012-2h10a2 2 0 012 2v16l-7-3.5L5 21V5z"/>
</svg> </svg>
<p class="text-surface-600 dark:text-surface-400 text-lg">No saved items yet.</p> <p class="text-surface-600 dark:text-surface-400 text-lg">No saved items yet.</p>
<p class="text-surface-500 text-sm mt-2">Save articles from around the web using the bookmark button.</p> <p class="text-surface-600 dark:text-surface-400 text-sm mt-2">Save articles from around the web using the bookmark button.</p>
</div> </div>
</div> </div>
@@ -148,11 +152,11 @@ permalink: /readlater/
<div class="grid grid-cols-2 gap-3 text-center"> <div class="grid grid-cols-2 gap-3 text-center">
<div class="p-3 bg-surface-50 dark:bg-surface-800 rounded-lg"> <div class="p-3 bg-surface-50 dark:bg-surface-800 rounded-lg">
<span class="text-2xl font-bold text-orange-600 dark:text-orange-400 block" x-text="items.length"></span> <span class="text-2xl font-bold text-orange-600 dark:text-orange-400 block" x-text="items.length"></span>
<span class="text-xs text-surface-500 uppercase">Saved</span> <span class="text-xs text-surface-600 dark:text-surface-400 uppercase">Saved</span>
</div> </div>
<div class="p-3 bg-surface-50 dark:bg-surface-800 rounded-lg"> <div class="p-3 bg-surface-50 dark:bg-surface-800 rounded-lg">
<span class="text-2xl font-bold text-orange-600 dark:text-orange-400 block" x-text="sources.length"></span> <span class="text-2xl font-bold text-orange-600 dark:text-orange-400 block" x-text="sources.length"></span>
<span class="text-xs text-surface-500 uppercase">Sources</span> <span class="text-xs text-surface-600 dark:text-surface-400 uppercase">Sources</span>
</div> </div>
</div> </div>
</div> </div>

View File

@@ -60,8 +60,8 @@ permalink: "replies/{% if pagination.pageNumber > 0 %}page/{{ pagination.pageNum
{% set protocol = replyTo | protocolType %} {% set protocol = replyTo | protocolType %}
{% unfurl replyTo %} {% unfurl replyTo %}
<p class="mt-2 text-sm flex items-center gap-2 flex-wrap"> <p class="mt-2 text-sm flex items-center gap-2 flex-wrap">
<span class="text-surface-500">In reply to:</span> <span class="text-surface-600">In reply to:</span>
<a class="u-in-reply-to text-xs text-surface-400 dark:text-surface-500 hover:underline break-all" href="{{ replyTo }}"> <a class="u-in-reply-to text-xs text-surface-600 dark:text-surface-400 hover:underline break-all" href="{{ replyTo }}">
{{ replyTo | replace("https://", "") | replace("http://", "") | truncate(60) }} {{ replyTo | replace("https://", "") | replace("http://", "") | truncate(60) }}
</a> </a>
{% if protocol == "atmosphere" %} {% if protocol == "atmosphere" %}
@@ -111,7 +111,7 @@ permalink: "replies/{% if pagination.pageNumber > 0 %}page/{{ pagination.pageNum
Previous Previous
</a> </a>
{% else %} {% else %}
<span class="pagination-link disabled"> <span class="pagination-link disabled" aria-disabled="true">
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 19l-7-7 7-7"></path></svg> <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 19l-7-7 7-7"></path></svg>
Previous Previous
</span> </span>
@@ -123,7 +123,7 @@ permalink: "replies/{% if pagination.pageNumber > 0 %}page/{{ pagination.pageNum
<svg class="w-4 h-4" 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"></path></svg> <svg class="w-4 h-4" 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"></path></svg>
</a> </a>
{% else %} {% else %}
<span class="pagination-link disabled"> <span class="pagination-link disabled" aria-disabled="true">
Next Next
<svg class="w-4 h-4" 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"></path></svg> <svg class="w-4 h-4" 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"></path></svg>
</span> </span>

View File

@@ -58,7 +58,7 @@ permalink: "reposts/{% if pagination.pageNumber > 0 %}page/{{ pagination.pageNum
{% set repostedUrl = post.data.repostOf or post.data.repost_of %} {% set repostedUrl = post.data.repostOf or post.data.repost_of %}
{% if repostedUrl %} {% if repostedUrl %}
{% unfurl repostedUrl %} {% unfurl repostedUrl %}
<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-600 dark:text-surface-400 hover:underline break-all mt-1 inline-block" href="{{ repostedUrl }}">
{{ repostedUrl }} {{ repostedUrl }}
</a> </a>
{% endif %} {% endif %}
@@ -87,7 +87,7 @@ permalink: "reposts/{% if pagination.pageNumber > 0 %}page/{{ pagination.pageNum
Previous Previous
</a> </a>
{% else %} {% else %}
<span class="pagination-link disabled"> <span class="pagination-link disabled" aria-disabled="true">
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 19l-7-7 7-7"></path></svg> <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 19l-7-7 7-7"></path></svg>
Previous Previous
</span> </span>
@@ -99,7 +99,7 @@ permalink: "reposts/{% if pagination.pageNumber > 0 %}page/{{ pagination.pageNum
<svg class="w-4 h-4" 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"></path></svg> <svg class="w-4 h-4" 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"></path></svg>
</a> </a>
{% else %} {% else %}
<span class="pagination-link disabled"> <span class="pagination-link disabled" aria-disabled="true">
Next Next
<svg class="w-4 h-4" 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"></path></svg> <svg class="w-4 h-4" 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"></path></svg>
</span> </span>

View File

@@ -14,7 +14,7 @@ pagefindIgnore: true
<noscript> <noscript>
<div class="p-6 bg-surface-100 dark:bg-surface-800 rounded-lg mt-4"> <div class="p-6 bg-surface-100 dark:bg-surface-800 rounded-lg mt-4">
<p class="text-surface-700 dark:text-surface-300">Search requires JavaScript to be enabled. Please enable JavaScript in your browser settings to use the search feature.</p> <p class="text-surface-700 dark:text-surface-300">Search requires JavaScript to be enabled. Please enable JavaScript in your browser settings to use the search feature.</p>
<p class="text-surface-500 text-sm mt-2">Alternatively, you can browse content via the <a href="/blog/" class="text-accent-600 dark:text-accent-400 hover:underline">blog archive</a> or <a href="/categories/" class="text-accent-600 dark:text-accent-400 hover:underline">categories</a>.</p> <p class="text-surface-600 dark:text-surface-400 text-sm mt-2">Alternatively, you can browse content via the <a href="/blog/" class="text-accent-600 dark:text-accent-400 hover:underline">blog archive</a> or <a href="/categories/" class="text-accent-600 dark:text-accent-400 hover:underline">categories</a>.</p>
</div> </div>
</noscript> </noscript>
@@ -23,16 +23,19 @@ pagefindIgnore: true
// Support ?q= query parameter and auto-focus // Support ?q= query parameter and auto-focus
window.addEventListener("DOMContentLoaded", () => { window.addEventListener("DOMContentLoaded", () => {
const input = document.querySelector("#search input[type='text']");
if (input) {
// Pagefind generates the input without a label — add one
input.setAttribute("aria-label", "Search all content");
}
const params = new URLSearchParams(window.location.search); const params = new URLSearchParams(window.location.search);
const query = params.get("q"); const query = params.get("q");
if (query) { if (query) {
const input = document.querySelector("#search input[type='text']");
if (input) { if (input) {
input.value = query; input.value = query;
input.dispatchEvent(new Event("input", { bubbles: true })); input.dispatchEvent(new Event("input", { bubbles: true }));
} }
} else { } else {
const input = document.querySelector("#search input[type='text']");
if (input) input.focus(); if (input) input.focus();
} }
}); });

View File

@@ -33,7 +33,7 @@ eleventyImport:
<p class="text-surface-600 dark:text-surface-400 mt-2">{{ page.data.title }}</p> <p class="text-surface-600 dark:text-surface-400 mt-2">{{ page.data.title }}</p>
{% endif %} {% endif %}
{% if page.data.updated %} {% if page.data.updated %}
<p class="text-sm text-surface-500 mt-2"> <p class="text-sm text-surface-600 dark:text-surface-400 mt-2">
Updated: <time class="font-mono text-sm" datetime="{{ page.data.updated | isoDate }}">{{ page.data.updated | dateDisplay }}</time> Updated: <time class="font-mono text-sm" datetime="{{ page.data.updated | isoDate }}">{{ page.data.updated | dateDisplay }}</time>
</p> </p>
{% endif %} {% endif %}
@@ -49,7 +49,7 @@ eleventyImport:
<li><code>@rmdes/indiekit-post-type-page</code> — registers the "page" post type with Indiekit, using root-level URL paths (<code>/slug</code> instead of <code>/type/YYYY/MM/DD/slug</code>)</li> <li><code>@rmdes/indiekit-post-type-page</code> — registers the "page" post type with Indiekit, using root-level URL paths (<code>/slug</code> instead of <code>/type/YYYY/MM/DD/slug</code>)</li>
<li><code>@rmdes/indiekit-endpoint-posts</code> — publishing UI that sends the <code>h=page</code> Micropub type so pages are created at root level</li> <li><code>@rmdes/indiekit-endpoint-posts</code> — publishing UI that sends the <code>h=page</code> Micropub type so pages are created at root level</li>
</ul> </ul>
<p class="text-surface-500 dark:text-surface-500 text-xs mt-3"> <p class="text-surface-600 dark:text-surface-400 text-xs mt-3">
Once both plugins are installed, "Page" appears as a post type in the Indiekit admin UI, and pages are published directly at <code>/slug</code>. Once both plugins are installed, "Page" appears as a post type in the Indiekit admin UI, and pages are published directly at <code>/slug</code>.
</p> </p>
</div> </div>

View File

@@ -38,7 +38,7 @@ eleventyExcludeFromCollections: true
<template x-if="loading"> <template x-if="loading">
<div class="text-center py-12"> <div class="text-center py-12">
<div class="inline-block w-8 h-8 border-4 border-emerald-200 border-t-emerald-600 rounded-full animate-spin"></div> <div class="inline-block w-8 h-8 border-4 border-emerald-200 border-t-emerald-600 rounded-full animate-spin"></div>
<p class="mt-4 text-surface-500">Loading starred repositories&hellip;</p> <p class="mt-4 text-surface-600 dark:text-surface-400">Loading starred repositories&hellip;</p>
</div> </div>
</template> </template>
@@ -53,13 +53,15 @@ eleventyExcludeFromCollections: true
{# ===== TAB BAR ===== #} {# ===== TAB BAR ===== #}
<div class="mb-4 -mx-4 px-4 overflow-x-auto scrollbar-thin"> <div class="mb-4 -mx-4 px-4 overflow-x-auto scrollbar-thin">
<div class="flex gap-1 min-w-max border-b border-surface-200 dark:border-surface-700"> <div class="flex gap-1 min-w-max border-b border-surface-200 dark:border-surface-700" role="tablist" aria-label="Starred repository lists">
{# All tab #} {# All tab #}
<button <button
@click="activeTab = 'all'; resetView()" @click="activeTab = 'all'; resetView()"
:class="activeTab === 'all' :class="activeTab === 'all'
? 'border-emerald-600 text-emerald-700 dark:text-emerald-400' ? 'border-emerald-600 text-emerald-700 dark:text-emerald-400'
: 'border-transparent text-surface-500 hover:text-surface-700 dark:hover:text-surface-300 hover:border-surface-300'" : 'border-transparent text-surface-600 dark:text-surface-400 hover:text-surface-700 dark:hover:text-surface-300 hover:border-surface-300'"
:aria-selected="(activeTab === 'all').toString()"
role="tab"
class="px-3 py-2 text-sm font-medium border-b-2 whitespace-nowrap transition-colors" class="px-3 py-2 text-sm font-medium border-b-2 whitespace-nowrap transition-colors"
> >
All All
@@ -72,7 +74,9 @@ eleventyExcludeFromCollections: true
@click="activeTab = list.slug; resetView()" @click="activeTab = list.slug; resetView()"
:class="activeTab === list.slug :class="activeTab === list.slug
? 'border-emerald-600 text-emerald-700 dark:text-emerald-400' ? 'border-emerald-600 text-emerald-700 dark:text-emerald-400'
: 'border-transparent text-surface-500 hover:text-surface-700 dark:hover:text-surface-300 hover:border-surface-300'" : 'border-transparent text-surface-600 dark:text-surface-400 hover:text-surface-700 dark:hover:text-surface-300 hover:border-surface-300'"
:aria-selected="(activeTab === list.slug).toString()"
role="tab"
class="px-3 py-2 text-sm font-medium border-b-2 whitespace-nowrap transition-colors" class="px-3 py-2 text-sm font-medium border-b-2 whitespace-nowrap transition-colors"
> >
<span x-text="list.name"></span> <span x-text="list.name"></span>
@@ -85,7 +89,9 @@ eleventyExcludeFromCollections: true
@click="activeTab = 'uncategorized'; resetView()" @click="activeTab = 'uncategorized'; resetView()"
:class="activeTab === 'uncategorized' :class="activeTab === 'uncategorized'
? 'border-emerald-600 text-emerald-700 dark:text-emerald-400' ? 'border-emerald-600 text-emerald-700 dark:text-emerald-400'
: 'border-transparent text-surface-500 hover:text-surface-700 dark:hover:text-surface-300 hover:border-surface-300'" : 'border-transparent text-surface-600 dark:text-surface-400 hover:text-surface-700 dark:hover:text-surface-300 hover:border-surface-300'"
:aria-selected="(activeTab === 'uncategorized').toString()"
role="tab"
class="px-3 py-2 text-sm font-medium border-b-2 whitespace-nowrap transition-colors" class="px-3 py-2 text-sm font-medium border-b-2 whitespace-nowrap transition-colors"
> >
Uncategorized Uncategorized
@@ -177,7 +183,7 @@ eleventyExcludeFromCollections: true
</div> </div>
{# ===== RESULTS SUMMARY ===== #} {# ===== RESULTS SUMMARY ===== #}
<div class="mb-4 text-sm text-surface-500"> <div class="mb-4 text-sm text-surface-600 dark:text-surface-400">
<span x-text="resultSummary"></span> <span x-text="resultSummary"></span>
</div> </div>
@@ -207,12 +213,12 @@ eleventyExcludeFromCollections: true
<span class="text-xs px-2 py-0.5 bg-surface-100 dark:bg-surface-700 text-surface-700 dark:text-surface-300 rounded" x-text="topic"></span> <span class="text-xs px-2 py-0.5 bg-surface-100 dark:bg-surface-700 text-surface-700 dark:text-surface-300 rounded" x-text="topic"></span>
</template> </template>
<template x-if="repo.topics.length > 5"> <template x-if="repo.topics.length > 5">
<span class="text-xs px-2 py-0.5 bg-surface-100 dark:bg-surface-700 text-surface-500 rounded" x-text="'+' + (repo.topics.length - 5)"></span> <span class="text-xs px-2 py-0.5 bg-surface-100 dark:bg-surface-700 text-surface-600 dark:text-surface-400 rounded" x-text="'+' + (repo.topics.length - 5)"></span>
</template> </template>
</div> </div>
</template> </template>
<div class="flex flex-wrap items-center gap-3 text-xs text-surface-500"> <div class="flex flex-wrap items-center gap-3 text-xs text-surface-600 dark:text-surface-400">
<template x-if="repo.language"> <template x-if="repo.language">
<span class="flex items-center gap-1"> <span class="flex items-center gap-1">
<span class="w-2.5 h-2.5 rounded-full" :style="'background:' + languageColor(repo.language)"></span> <span class="w-2.5 h-2.5 rounded-full" :style="'background:' + languageColor(repo.language)"></span>
@@ -243,7 +249,7 @@ eleventyExcludeFromCollections: true
{# ===== EMPTY FILTERED STATE ===== #} {# ===== EMPTY FILTERED STATE ===== #}
<template x-if="sortedStars.length === 0"> <template x-if="sortedStars.length === 0">
<div class="text-center py-12"> <div class="text-center py-12">
<p class="text-surface-500">No repos match your current filters.</p> <p class="text-surface-600 dark:text-surface-400">No repos match your current filters.</p>
<button @click="clearFilters()" class="mt-2 text-sm text-emerald-600 dark:text-emerald-400 hover:underline">Clear all filters</button> <button @click="clearFilters()" class="mt-2 text-sm text-emerald-600 dark:text-emerald-400 hover:underline">Clear all filters</button>
</div> </div>
</template> </template>

View File

@@ -52,7 +52,7 @@ pagefindIgnore: true
{{ post.url }} {{ post.url }}
</a> </a>
</td> </td>
<td class="p-2 text-xs text-surface-500 font-mono"> <td class="p-2 text-xs text-surface-600 dark:text-surface-400 font-mono">
{% if legacyUrls.length %} {% if legacyUrls.length %}
{% for legacyUrl in legacyUrls %} {% for legacyUrl in legacyUrls %}
<div>{{ legacyUrl }}</div> <div>{{ legacyUrl }}</div>
@@ -94,7 +94,7 @@ pagefindIgnore: true
{% for newUrl, oldUrls in aliasEntries | head(20) %} {% for newUrl, oldUrls in aliasEntries | head(20) %}
<tr> <tr>
<td class="p-2 text-xs break-all">{{ newUrl }}</td> <td class="p-2 text-xs break-all">{{ newUrl }}</td>
<td class="p-2 text-xs break-all text-surface-500"> <td class="p-2 text-xs break-all text-surface-600 dark:text-surface-400">
{% for oldUrl in oldUrls %} {% for oldUrl in oldUrls %}
<div>{{ oldUrl }}</div> <div>{{ oldUrl }}</div>
{% endfor %} {% endfor %}
@@ -115,7 +115,7 @@ pagefindIgnore: true
<ul class="space-y-1 font-mono text-xs"> <ul class="space-y-1 font-mono text-xs">
{% for wm in webmentions | head(30) %} {% for wm in webmentions | head(30) %}
<li class="p-2 bg-surface-50 dark:bg-surface-800/50 rounded"> <li class="p-2 bg-surface-50 dark:bg-surface-800/50 rounded">
<span class="text-surface-500">{{ wm["wm-property"] }}:</span> <span class="text-surface-600 dark:text-surface-400">{{ wm["wm-property"] }}:</span>
<span class="break-all">{{ wm["wm-target"] }}</span> <span class="break-all">{{ wm["wm-target"] }}</span>
</li> </li>
{% endfor %} {% endfor %}

View File

@@ -15,11 +15,13 @@ withSidebar: true
{# Multi-channel tabs #} {# Multi-channel tabs #}
{% if youtubeChannel.isMultiChannel and youtubeChannel.channels.length > 1 %} {% if youtubeChannel.isMultiChannel and youtubeChannel.channels.length > 1 %}
<div class="mb-6"> <div class="mb-6">
<div class="flex flex-wrap gap-2 border-b border-surface-200 dark:border-surface-700"> <div class="flex flex-wrap gap-2 border-b border-surface-200 dark:border-surface-700" role="tablist" aria-label="YouTube channels">
{% for channel in youtubeChannel.channels %} {% for channel in youtubeChannel.channels %}
<button <button
@click="activeChannel = {{ loop.index0 }}" @click="activeChannel = {{ loop.index0 }}"
:class="activeChannel === {{ loop.index0 }} ? 'border-red-500 text-red-600 dark:text-red-400' : 'border-transparent text-surface-600 dark:text-surface-400 hover:text-surface-900 dark:hover:text-surface-100'" :class="activeChannel === {{ loop.index0 }} ? 'border-red-500 text-red-600 dark:text-red-400' : 'border-transparent text-surface-600 dark:text-surface-400 hover:text-surface-900 dark:hover:text-surface-100'"
:aria-selected="(activeChannel === {{ loop.index0 }}).toString()"
role="tab" id="yt-tab-{{ loop.index0 }}" aria-controls="yt-panel-{{ loop.index0 }}"
class="px-4 py-2 font-medium border-b-2 -mb-px transition-colors" class="px-4 py-2 font-medium border-b-2 -mb-px transition-colors"
> >
{{ channel.configName or channel.title }} {{ channel.configName or channel.title }}
@@ -31,7 +33,7 @@ withSidebar: true
{# Channel sections #} {# Channel sections #}
{% for channel in youtubeChannel.channels %} {% for channel in youtubeChannel.channels %}
<div x-show="activeChannel === {{ loop.index0 }}" {% if not loop.first %}x-cloak{% endif %}> <div x-show="activeChannel === {{ loop.index0 }}" {% if not loop.first %}x-cloak{% endif %} role="tabpanel" id="yt-panel-{{ loop.index0 }}" aria-labelledby="yt-tab-{{ loop.index0 }}">
{# Channel Header #} {# Channel Header #}
<section class="mb-8"> <section class="mb-8">
<div class="flex flex-col sm:flex-row items-start sm:items-center gap-4 sm:gap-5 p-4 sm:p-6 bg-gradient-to-br from-red-500/10 to-red-600/5 dark:from-red-900/20 dark:to-red-800/10 rounded-xl sm:rounded-2xl border border-red-500/20"> <div class="flex flex-col sm:flex-row items-start sm:items-center gap-4 sm:gap-5 p-4 sm:p-6 bg-gradient-to-br from-red-500/10 to-red-600/5 dark:from-red-900/20 dark:to-red-800/10 rounded-xl sm:rounded-2xl border border-red-500/20">
@@ -57,7 +59,7 @@ withSidebar: true
</a> </a>
</h2> </h2>
{% if channel.customUrl %} {% if channel.customUrl %}
<p class="text-sm text-surface-500">{{ channel.customUrl }}</p> <p class="text-sm text-surface-600 dark:text-surface-400">{{ channel.customUrl }}</p>
{% endif %} {% endif %}
<div class="flex flex-wrap items-center gap-4 mt-2 text-sm text-surface-600 dark:text-surface-400"> <div class="flex flex-wrap items-center gap-4 mt-2 text-sm text-surface-600 dark:text-surface-400">
<span class="flex items-center gap-1"> <span class="flex items-center gap-1">
@@ -220,7 +222,7 @@ withSidebar: true
{{ video.title }} {{ video.title }}
</a> </a>
</h3> </h3>
<div class="flex items-center gap-3 text-sm text-surface-500"> <div class="flex items-center gap-3 text-sm text-surface-600 dark:text-surface-400">
<span class="flex items-center gap-1"> <span class="flex items-center gap-1">
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"> <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"/> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"/>