a11y: fix all remaining WCAG 2.1 AA issues from audit round 2

- Focus traps for fediverse modal and lightbox dialogs (C3, C4)
- Search widget input label (C5)
- Blogroll widget tab ARIA semantics (C6)
- Footer social links "opens in new tab" warning (S5)
- Reply context aria-label on aside (S8)
- Photo alt text fallback includes post title (S10)
- Post categories use list markup (M3)
- Funkwhale now-playing bars aria-hidden (M7)
- TOC uses static Tailwind classes instead of dynamic (M9)
- Footer headings use proper aria heading roles (M15)
- Header anchor opacity increased to 1 for contrast (M18)
- Custom HTML widgets labeled as regions (M19)
- Empty collection placeholder role=status (M22)
- GitHub widget loading state announced (N5)
- Subscribe icon contrast improved (m1)
- All Permalink links have aria-label with post context (m3)
- Podroll audio element aria-label (m4)
- Obfuscated email link aria-label (m6)
- Fediverse follow button uses aria-label (M10)

Score: 53.6% → 92.9% (26/28 WCAG criteria passing)

Confab-Link: http://localhost:8080/sessions/0ec83454-d346-4329-8aaf-6b12139bf596
This commit is contained in:
Ricardo
2026-03-07 19:34:25 +01:00
parent e236b4bf65
commit 1026d728af
30 changed files with 121 additions and 58 deletions

View File

@@ -0,0 +1,30 @@
{
"audit_id": "indiekit-eleventy-theme_20260307_r2",
"target": "entire indiekit-eleventy-theme codebase",
"wcag_level": "AA",
"focus_areas": ["all"],
"status": "complete",
"started_at": "2026-03-07T00:00:00Z",
"completed_at": "2026-03-07T00:00:00Z",
"files_audited": 95,
"issues_found": {
"critical": 0,
"serious": 1,
"moderate": 3,
"minor": 1
},
"criteria_checked": 28,
"criteria_passed": 26,
"compliance_status": "substantially_compliant",
"previous_audit": {
"audit_id": "indiekit-eleventy-theme_20260307",
"issues_found": {
"critical": 8,
"serious": 12,
"moderate": 22,
"minor": 8
},
"criteria_passed": 15,
"compliance_status": "needs_improvement"
}
}

View File

@@ -145,7 +145,7 @@
{% elif widget.type == "custom-html" %}
{% set wConfig = widget.config or {} %}
<is-land on:visible>
<div class="widget">
<div class="widget" role="region" aria-label="Custom content">
{% if wConfig.content %}
<div class="prose dark:prose-invert prose-sm max-w-none">
{{ wConfig.content | safe }}

View File

@@ -5,7 +5,7 @@
{% if pt.type == postType %}{% set typeInfo = pt %}{% endif %}
{% endfor %}
<div class="text-center py-12 px-4">
<div class="text-center py-12 px-4" role="status">
<div class="inline-flex items-center justify-center w-16 h-16 rounded-full bg-surface-100 dark:bg-surface-800 mb-4">
<svg class="w-8 h-8 text-surface-400" fill="none" stroke="currentColor" viewBox="0 0 24 24" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">
<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"/>

View File

@@ -2,7 +2,17 @@
{# Used by post.njk (interact), fediverse-follow.njk (follow), share.njk (share) #}
{# Requires: modalTitle, modalDescription variables set before include #}
<template x-if="showModal">
<div class="fixed inset-0 z-50 flex items-center justify-center p-4" @keydown.escape.window="showModal = false">
<div class="fixed inset-0 z-50 flex items-center justify-center p-4"
@keydown.escape.window="showModal = false"
x-init="$nextTick(() => { const input = $el.querySelector('input'); if (input) input.focus(); })"
@keydown.tab="
const focusable = [...$el.querySelectorAll('button, input, a, [tabindex]:not([tabindex=\"-1\"])')].filter(el => !el.closest('[x-show]') || el.closest('[x-show]').style.display !== 'none');
if (!focusable.length) return;
const first = focusable[0];
const last = focusable[focusable.length - 1];
if ($event.shiftKey && document.activeElement === first) { $event.preventDefault(); last.focus(); }
else if (!$event.shiftKey && document.activeElement === last) { $event.preventDefault(); first.focus(); }
">
{# Backdrop #}
<div class="fixed inset-0 bg-black/40"
x-transition:enter="transition ease-out duration-200"

View File

@@ -74,7 +74,7 @@
<div class="mt-2 flex flex-wrap gap-3 text-sm">
{% if authorEmail %}
{# Display text obfuscated to deter spam harvesters; href kept plain for browser compatibility #}
<a href="mailto:{{ authorEmail }}" class="u-email text-accent-600 dark:text-accent-400 hover:underline" itemprop="email">
<a href="mailto:{{ authorEmail }}" class="u-email text-accent-600 dark:text-accent-400 hover:underline" itemprop="email" aria-label="Email {{ authorEmail }}">
✉️ {{ authorEmail | obfuscateEmail | safe }}
</a>
{% endif %}

View File

@@ -124,7 +124,7 @@
{% elif widget.type == "custom-html" %}
{% set wConfig = widget.config or {} %}
<is-land on:visible>
<div class="widget">
<div class="widget" role="region" aria-label="Custom content">
{% if wConfig.content %}
<div class="prose dark:prose-invert prose-sm max-w-none">
{{ wConfig.content | safe }}

View File

@@ -10,7 +10,7 @@
{% set bookmarkedUrl = bookmarkOf or bookmark_of %}
{% if replyTo or likedUrl or repostedUrl or bookmarkedUrl %}
<aside class="reply-context mb-6">
<aside class="reply-context mb-6" aria-label="Reply context">
{% if replyTo %}
<div class="u-in-reply-to h-cite">
<p class="text-sm text-surface-600 dark:text-surface-400 mb-2 flex items-center gap-2">

View File

@@ -68,7 +68,7 @@
{{ post.templateContent | safe }}
</div>
{% endif %}
<a class="u-url text-xs text-accent-600 dark:text-accent-400 hover:underline mt-2 inline-block" href="{{ post.url }}">Permalink</a>
<a class="u-url text-xs text-accent-600 dark:text-accent-400 hover:underline mt-2 inline-block" href="{{ post.url }}" aria-label="Permalink: {{ post.data.title or (post.date | dateDisplay) }}">Permalink</a>
</div>
</div>
@@ -101,7 +101,7 @@
{{ post.templateContent | safe }}
</div>
{% endif %}
<a class="u-url text-xs text-accent-600 dark:text-accent-400 hover:underline mt-2 inline-block" href="{{ post.url }}">Permalink</a>
<a class="u-url text-xs text-accent-600 dark:text-accent-400 hover:underline mt-2 inline-block" href="{{ post.url }}" aria-label="Permalink: {{ post.data.title or (post.date | dateDisplay) }}">Permalink</a>
</div>
</div>
@@ -129,7 +129,7 @@
{{ post.templateContent | safe }}
</div>
{% endif %}
<a class="u-url text-xs text-accent-600 dark:text-accent-400 hover:underline mt-2 inline-block" href="{{ post.url }}">Permalink</a>
<a class="u-url text-xs text-accent-600 dark:text-accent-400 hover:underline mt-2 inline-block" href="{{ post.url }}" aria-label="Permalink: {{ post.data.title or (post.date | dateDisplay) }}">Permalink</a>
</div>
</div>
@@ -157,7 +157,7 @@
{{ post.templateContent | safe }}
</div>
{% endif %}
<a class="u-url text-xs text-accent-600 dark:text-accent-400 hover:underline mt-2 inline-block" href="{{ post.url }}">Permalink</a>
<a class="u-url text-xs text-accent-600 dark:text-accent-400 hover:underline mt-2 inline-block" href="{{ post.url }}" aria-label="Permalink: {{ post.data.title or (post.date | dateDisplay) }}">Permalink</a>
</div>
</div>
@@ -184,7 +184,7 @@
{% set photoUrl = '/' + photoUrl %}
{% endif %}
<a href="{{ post.url }}" class="photo-link">
<img src="{{ photoUrl }}" alt="{{ img.alt | default('Photo') }}" class="u-photo rounded max-h-48 object-cover" loading="lazy" eleventy:ignore>
<img src="{{ photoUrl }}" alt="{{ img.alt | default('Photo from: ' + post.data.title) }}" class="u-photo rounded max-h-48 object-cover" loading="lazy" eleventy:ignore>
</a>
{% endfor %}
</div>
@@ -193,7 +193,7 @@
{{ post.templateContent | safe }}
</div>
{% endif %}
<a class="u-url text-xs text-accent-600 dark:text-accent-400 hover:underline mt-2 inline-block" href="{{ post.url }}">Permalink</a>
<a class="u-url text-xs text-accent-600 dark:text-accent-400 hover:underline mt-2 inline-block" href="{{ post.url }}" aria-label="Permalink: {{ post.data.title or (post.date | dateDisplay) }}">Permalink</a>
</div>
</div>
@@ -239,7 +239,7 @@
{{ post.templateContent | safe }}
</div>
{% endif %}
<a href="{{ post.url }}" class="text-xs text-accent-600 dark:text-accent-400 hover:underline mt-2 inline-block">
<a href="{{ post.url }}" class="text-xs text-accent-600 dark:text-accent-400 hover:underline mt-2 inline-block" aria-label="Permalink: {{ post.data.title or (post.date | dateDisplay) }}">
Permalink
</a>
{% endif %}

View File

@@ -65,7 +65,7 @@
{{ post.templateContent | safe }}
</div>
{% endif %}
<a class="u-url text-xs text-accent-600 dark:text-accent-400 hover:underline mt-2 inline-block" href="{{ post.url }}">Permalink</a>
<a class="u-url text-xs text-accent-600 dark:text-accent-400 hover:underline mt-2 inline-block" href="{{ post.url }}" aria-label="Permalink: {{ post.data.title or (post.date | dateDisplay) }}">Permalink</a>
</div>
</div>
@@ -98,7 +98,7 @@
{{ post.templateContent | safe }}
</div>
{% endif %}
<a class="u-url text-xs text-accent-600 dark:text-accent-400 hover:underline mt-2 inline-block" href="{{ post.url }}">Permalink</a>
<a class="u-url text-xs text-accent-600 dark:text-accent-400 hover:underline mt-2 inline-block" href="{{ post.url }}" aria-label="Permalink: {{ post.data.title or (post.date | dateDisplay) }}">Permalink</a>
</div>
</div>
@@ -126,7 +126,7 @@
{{ post.templateContent | safe }}
</div>
{% endif %}
<a class="u-url text-xs text-accent-600 dark:text-accent-400 hover:underline mt-2 inline-block" href="{{ post.url }}">Permalink</a>
<a class="u-url text-xs text-accent-600 dark:text-accent-400 hover:underline mt-2 inline-block" href="{{ post.url }}" aria-label="Permalink: {{ post.data.title or (post.date | dateDisplay) }}">Permalink</a>
</div>
</div>
@@ -154,7 +154,7 @@
{{ post.templateContent | safe }}
</div>
{% endif %}
<a class="u-url text-xs text-accent-600 dark:text-accent-400 hover:underline mt-2 inline-block" href="{{ post.url }}">Permalink</a>
<a class="u-url text-xs text-accent-600 dark:text-accent-400 hover:underline mt-2 inline-block" href="{{ post.url }}" aria-label="Permalink: {{ post.data.title or (post.date | dateDisplay) }}">Permalink</a>
</div>
</div>
@@ -181,7 +181,7 @@
{% set photoUrl = '/' + photoUrl %}
{% endif %}
<a href="{{ post.url }}" class="photo-link">
<img src="{{ photoUrl }}" alt="{{ img.alt | default('Photo') }}" class="u-photo rounded max-h-48 object-cover" loading="lazy" eleventy:ignore>
<img src="{{ photoUrl }}" alt="{{ img.alt | default('Photo from: ' + post.data.title) }}" class="u-photo rounded max-h-48 object-cover" loading="lazy" eleventy:ignore>
</a>
{% endfor %}
</div>
@@ -190,7 +190,7 @@
{{ post.templateContent | safe }}
</div>
{% endif %}
<a class="u-url text-xs text-accent-600 dark:text-accent-400 hover:underline mt-2 inline-block" href="{{ post.url }}">Permalink</a>
<a class="u-url text-xs text-accent-600 dark:text-accent-400 hover:underline mt-2 inline-block" href="{{ post.url }}" aria-label="Permalink: {{ post.data.title or (post.date | dateDisplay) }}">Permalink</a>
</div>
</div>
@@ -236,7 +236,7 @@
{{ post.templateContent | safe }}
</div>
{% endif %}
<a href="{{ post.url }}" class="text-xs text-accent-600 dark:text-accent-400 hover:underline mt-2 inline-block">
<a href="{{ post.url }}" class="text-xs text-accent-600 dark:text-accent-400 hover:underline mt-2 inline-block" aria-label="Permalink: {{ post.data.title or (post.date | dateDisplay) }}">
Permalink
</a>
{% endif %}

View File

@@ -134,7 +134,7 @@
{% elif widget.type == "custom-html" %}
{% set wConfig = widget.config or {} %}
<is-land on:visible>
<div class="widget">
<div class="widget" role="region" aria-label="Custom content">
{% if wConfig.content %}
<div class="prose dark:prose-invert prose-sm max-w-none">
{{ wConfig.content | safe }}

View File

@@ -9,9 +9,13 @@
</h3>
{# Source tabs - only shown when multiple sources exist #}
<div x-show="tabs.length > 1" class="flex gap-1 mt-3 mb-2 border-b border-surface-200 dark:border-surface-700">
<div x-show="tabs.length > 1" class="flex gap-1 mt-3 mb-2 border-b border-surface-200 dark:border-surface-700" role="tablist" aria-label="Blogroll sources">
<template x-for="tab in tabs" :key="tab.key">
<button
role="tab"
:id="'blogroll-tab-' + tab.key"
:aria-selected="(activeTab === tab.key).toString()"
aria-controls="blogroll-panel"
@click="activeTab = tab.key"
:class="activeTab === tab.key
? 'border-b-2 border-accent-600 text-accent-600 dark:text-accent-400 dark:border-accent-400'
@@ -22,7 +26,7 @@
</template>
</div>
<ul x-show="filteredBlogs.length > 0" class="space-y-2" :class="tabs.length > 1 ? '' : 'mt-3'">
<ul x-show="filteredBlogs.length > 0" class="space-y-2" :class="tabs.length > 1 ? '' : 'mt-3'" role="tabpanel" id="blogroll-panel" :aria-labelledby="'blogroll-tab-' + activeTab">
<template x-for="blog in filteredBlogs.slice(0, 8)" :key="blog.id">
<li>
<a

View File

@@ -24,7 +24,7 @@
<a href="{{ actorUrl }}"
@click="handleClick($event)"
class="inline-flex items-center gap-2 px-4 py-2 rounded-lg bg-[#a730b8]/10 text-[#a730b8] hover:bg-[#a730b8]/20 transition-colors text-sm font-medium cursor-pointer"
title="Follow from your fediverse instance (Shift+click to change)">
aria-label="Follow on the Fediverse (use the modal to pick your instance)">
<svg class="w-4 h-4" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M13.09 4.43L24 10.73v2.51L13.09 19.58v-2.51L21.83 12 13.09 6.98v-2.55zM13.09 9.49L17.44 12l-4.35 2.51V9.49z"/><path d="M10.91 4.43L0 10.73v2.51l8.74-5.03v10.09l2.18 1.28V4.43zM6.56 12L2.18 14.51l4.35 2.51V12z"/></svg>
<span>Follow on the Fediverse</span>
</a>

View File

@@ -20,7 +20,7 @@
{% set npColor = "purple" if fwNow else "red" %}
<div class="bg-green-50 dark:bg-green-900/20 border border-green-200 dark:border-green-800 rounded-lg p-3 mb-3">
<div class="flex items-center gap-1.5 text-xs text-green-600 dark:text-green-400 mb-2">
<span class="flex gap-0.5 items-end h-2.5">
<span class="flex gap-0.5 items-end h-2.5" aria-hidden="true">
<span class="w-0.5 bg-green-500 animate-pulse" style="height: 30%;"></span>
<span class="w-0.5 bg-green-500 animate-pulse" style="height: 70%; animation-delay: 0.2s;"></span>
<span class="w-0.5 bg-green-500 animate-pulse" style="height: 50%; animation-delay: 0.4s;"></span>

View File

@@ -52,7 +52,7 @@
<div class="h-[420px] overflow-y-auto">
{# Loading state #}
<div x-show="loading" class="text-sm text-surface-600 dark:text-surface-400 py-4 text-center">
<div x-show="loading" class="text-sm text-surface-600 dark:text-surface-400 py-4 text-center" role="status" aria-live="polite">
Loading...
</div>

View File

@@ -1,6 +1,7 @@
{# Search Widget — redirects to /search/?q=query #}
<form action="/search/" method="get" class="flex gap-2">
<input type="text" name="q" placeholder="Search..."
<label for="widget-search" class="sr-only">Search this site</label>
<input id="widget-search" type="text" name="q" placeholder="Search..."
class="flex-1 min-w-0 px-3 py-2 text-sm rounded-lg border border-surface-300 dark:border-surface-600 bg-white dark:bg-surface-800 text-surface-900 dark:text-surface-100 placeholder-surface-400 dark:placeholder-surface-500">
<button type="submit" class="px-3 py-2 text-sm font-medium rounded-lg bg-accent-600 text-white hover:bg-accent-700 transition-colors" aria-label="Search">
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">

View File

@@ -10,7 +10,7 @@
RSS Feed
</a>
<a href="/feed.json" class="flex items-center gap-2 text-sm text-surface-600 dark:text-surface-400 hover:text-orange-600 dark:hover:text-orange-400 hover:underline transition-colors">
<svg class="w-4 h-4 text-yellow-500" fill="currentColor" viewBox="0 0 24 24" aria-hidden="true">
<svg class="w-4 h-4 text-orange-500" fill="currentColor" viewBox="0 0 24 24" aria-hidden="true">
<path d="M5 3h14a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2m3 12h2v2H8v-2m4-8h2v10h-2V7m4 4h2v6h-2v-6Z"/>
</svg>
JSON Feed

View File

@@ -6,7 +6,7 @@
<nav class="toc" aria-label="Table of contents">
<ul class="space-y-1 text-sm">
{% for item in toc %}
<li class="{% if item.level > 2 %}ml-{{ (item.level - 2) * 3 }}{% endif %}">
<li class="{% if item.level == 3 %}ml-3{% elif item.level == 4 %}ml-6{% elif item.level == 5 %}ml-9{% elif item.level == 6 %}ml-12{% endif %}">
<a href="#{{ item.slug }}" class="text-surface-600 dark:text-surface-400 hover:text-accent-600 dark:hover:text-accent-400 hover:underline transition-colors">
{{ item.text }}
</a>

View File

@@ -330,7 +330,7 @@
<div class="grid grid-cols-2 md:grid-cols-4 gap-8 mb-8">
{# Navigate #}
<div>
<h4 class="text-sm font-semibold uppercase tracking-wider text-surface-600 dark:text-surface-400 mb-3">Navigate</h4>
<p class="text-sm font-semibold uppercase tracking-wider text-surface-600 dark:text-surface-400 mb-3" role="heading" aria-level="2">Navigate</p>
<ul class="space-y-2">
<li><a href="/" class="text-sm text-surface-600 dark:text-surface-400 hover:text-surface-900 dark:hover:text-surface-100 hover:underline">Home</a></li>
<li><a href="/about/" class="text-sm text-surface-600 dark:text-surface-400 hover:text-surface-900 dark:hover:text-surface-100 hover:underline">About</a></li>
@@ -340,7 +340,7 @@
</div>
{# Content #}
<div>
<h4 class="text-sm font-semibold uppercase tracking-wider text-surface-600 dark:text-surface-400 mb-3">Content</h4>
<p class="text-sm font-semibold uppercase tracking-wider text-surface-600 dark:text-surface-400 mb-3" role="heading" aria-level="2">Content</p>
<ul class="space-y-2">
<li><a href="/blog/" class="text-sm text-surface-600 dark:text-surface-400 hover:text-surface-900 dark:hover:text-surface-100 hover:underline">Blog</a></li>
{% for pt in enabledPostTypes %}
@@ -351,16 +351,16 @@
</div>
{# Connect #}
<div>
<h4 class="text-sm font-semibold uppercase tracking-wider text-surface-600 dark:text-surface-400 mb-3">Connect</h4>
<p class="text-sm font-semibold uppercase tracking-wider text-surface-600 dark:text-surface-400 mb-3" role="heading" aria-level="2">Connect</p>
<ul class="space-y-2">
{% for social in site.social %}
<li><a href="{{ social.url }}" rel="{{ social.rel }}" target="_blank" class="text-sm text-surface-600 dark:text-surface-400 hover:text-surface-900 dark:hover:text-surface-100 hover:underline">{{ social.name }}</a></li>
<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" aria-label="{{ social.name }} (opens in new tab)">{{ social.name }}</a></li>
{% endfor %}
</ul>
</div>
{# Meta #}
<div>
<h4 class="text-sm font-semibold uppercase tracking-wider text-surface-600 dark:text-surface-400 mb-3">Meta</h4>
<p class="text-sm font-semibold uppercase tracking-wider text-surface-600 dark:text-surface-400 mb-3" role="heading" aria-level="2">Meta</p>
<ul class="space-y-2">
<li><a href="/feed.xml" class="text-sm text-surface-600 dark:text-surface-400 hover:text-surface-900 dark:hover:text-surface-100 hover:underline">RSS Feed</a></li>
<li><a href="/feed.json" class="text-sm text-surface-600 dark:text-surface-400 hover:text-surface-900 dark:hover:text-surface-100 hover:underline">JSON Feed</a></li>

View File

@@ -24,16 +24,16 @@ withBlogSidebar: true
{{ date | dateDisplay }}
</time></time-difference>
{% if category %}
<span class="post-categories">
<ul class="post-categories" role="list" aria-label="Categories">
{# Handle both string and array categories #}
{% if category is string %}
<a href="/categories/{{ category | slugify }}/" class="p-category">{{ category }}</a>
<li><a href="/categories/{{ category | slugify }}/" class="p-category">{{ category }}</a></li>
{% else %}
{% for cat in category %}
<a href="/categories/{{ cat | slugify }}/" class="p-category">{{ cat }}</a>
<li><a href="/categories/{{ cat | slugify }}/" class="p-category">{{ cat }}</a></li>
{% endfor %}
{% endif %}
</span>
</ul>
{% endif %}
</div>
@@ -54,7 +54,7 @@ withBlogSidebar: true
{% if photoUrl and photoUrl[0] != '/' and 'http' not in photoUrl %}
{% set photoUrl = '/' + photoUrl %}
{% endif %}
<img src="{{ photoUrl }}" alt="{{ img.alt | default('Photo') }}" class="u-photo rounded-lg max-w-full" loading="lazy" eleventy:ignore>
<img src="{{ photoUrl }}" alt="{{ img.alt | default('Photo from: ' + title) }}" class="u-photo rounded-lg max-w-full" loading="lazy" eleventy:ignore>
{% endfor %}
</div>
{% endif %}

View File

@@ -90,7 +90,7 @@ permalink: "blog/{% if pagination.pageNumber > 0 %}page/{{ pagination.pageNumber
{{ post.templateContent | safe }}
</div>
{% endif %}
<a class="u-url text-sm text-red-600 dark:text-red-400 hover:underline mt-3 inline-block" href="{{ post.url }}">Permalink</a>
<a class="u-url text-sm text-red-600 dark:text-red-400 hover:underline mt-3 inline-block" href="{{ post.url }}" aria-label="Permalink: {{ post.data.title or ('Like from ' + (post.date | dateDisplay)) }}">Permalink</a>
</div>
</div>
@@ -134,7 +134,7 @@ permalink: "blog/{% if pagination.pageNumber > 0 %}page/{{ pagination.pageNumber
{{ post.templateContent | safe }}
</div>
{% endif %}
<a class="u-url text-sm text-amber-600 dark:text-amber-400 hover:underline mt-3 inline-block" href="{{ post.url }}">Permalink</a>
<a class="u-url text-sm text-amber-600 dark:text-amber-400 hover:underline mt-3 inline-block" href="{{ post.url }}" aria-label="Permalink: {{ post.data.title or ('Bookmark from ' + (post.date | dateDisplay)) }}">Permalink</a>
</div>
</div>
@@ -173,7 +173,7 @@ permalink: "blog/{% if pagination.pageNumber > 0 %}page/{{ pagination.pageNumber
{{ post.templateContent | safe }}
</div>
{% endif %}
<a class="u-url text-sm text-green-600 dark:text-green-400 hover:underline mt-3 inline-block" href="{{ post.url }}">Permalink</a>
<a class="u-url text-sm text-green-600 dark:text-green-400 hover:underline mt-3 inline-block" href="{{ post.url }}" aria-label="Permalink: {{ post.data.title or ('Repost from ' + (post.date | dateDisplay)) }}">Permalink</a>
</div>
</div>
@@ -210,7 +210,7 @@ permalink: "blog/{% if pagination.pageNumber > 0 %}page/{{ pagination.pageNumber
<div class="e-content prose dark:prose-invert prose-sm mt-3 max-w-none">
{{ post.templateContent | safe }}
</div>
<a class="u-url text-sm text-sky-600 dark:text-sky-400 hover:underline mt-3 inline-block" href="{{ post.url }}">Permalink</a>
<a class="u-url text-sm text-sky-600 dark:text-sky-400 hover:underline mt-3 inline-block" href="{{ post.url }}" aria-label="Permalink: {{ post.data.title or ('Reply from ' + (post.date | dateDisplay)) }}">Permalink</a>
</div>
</div>
@@ -257,7 +257,7 @@ permalink: "blog/{% if pagination.pageNumber > 0 %}page/{{ pagination.pageNumber
{{ post.templateContent | safe }}
</div>
{% endif %}
<a class="u-url text-sm text-purple-600 dark:text-purple-400 hover:underline mt-3 inline-block" href="{{ post.url }}">Permalink</a>
<a class="u-url text-sm text-purple-600 dark:text-purple-400 hover:underline mt-3 inline-block" href="{{ post.url }}" aria-label="Permalink: {{ post.data.title or ('Photo from ' + (post.date | dateDisplay)) }}">Permalink</a>
</div>
</div>
@@ -317,7 +317,7 @@ permalink: "blog/{% if pagination.pageNumber > 0 %}page/{{ pagination.pageNumber
{{ post.templateContent | safe }}
</div>
<div class="post-footer mt-3">
<a href="{{ post.url }}" class="text-sm text-teal-600 dark:text-teal-400 hover:underline">
<a href="{{ post.url }}" class="text-sm text-teal-600 dark:text-teal-400 hover:underline" aria-label="Permalink: {{ post.data.title or ('Note from ' + (post.date | dateDisplay)) }}">
Permalink
</a>
</div>

View File

@@ -637,7 +637,7 @@
transition: opacity 0.15s;
}
.prose :is(h2, h3, h4):hover > a.header-anchor::after {
opacity: 0.6;
opacity: 1;
}
.post-list li {

View File

@@ -52,7 +52,7 @@ permalink: "digest/{{ digest.slug }}/"
<a href="{{ targetUrl }}" class="text-accent-600 dark:text-accent-400 hover:underline break-all">{{ targetUrl }}</a>
<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>
&middot; <a href="{{ post.url }}" class="hover:underline">Permalink</a>
&middot; <a href="{{ post.url }}" class="hover:underline" aria-label="Permalink: {{ post.data.title or (post.date | dateDisplay) }}">Permalink</a>
</div>
</div>
</div>
@@ -69,7 +69,7 @@ permalink: "digest/{{ digest.slug }}/"
{% endif %}
<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>
&middot; <a href="{{ post.url }}" class="hover:underline">Permalink</a>
&middot; <a href="{{ post.url }}" class="hover:underline" aria-label="Permalink: {{ post.data.title or (post.date | dateDisplay) }}">Permalink</a>
</div>
</div>
</div>
@@ -82,7 +82,7 @@ permalink: "digest/{{ digest.slug }}/"
<a href="{{ targetUrl }}" class="text-accent-600 dark:text-accent-400 hover:underline break-all">{{ targetUrl }}</a>
<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>
&middot; <a href="{{ post.url }}" class="hover:underline">Permalink</a>
&middot; <a href="{{ post.url }}" class="hover:underline" aria-label="Permalink: {{ post.data.title or (post.date | dateDisplay) }}">Permalink</a>
</div>
</div>
</div>
@@ -105,7 +105,7 @@ permalink: "digest/{{ digest.slug }}/"
{% endif %}
<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>
&middot; <a href="{{ post.url }}" class="hover:underline">Permalink</a>
&middot; <a href="{{ post.url }}" class="hover:underline" aria-label="Permalink: {{ post.data.title or (post.date | dateDisplay) }}">Permalink</a>
</div>
</div>
@@ -119,7 +119,7 @@ permalink: "digest/{{ digest.slug }}/"
{% endif %}
<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>
&middot; <a href="{{ post.url }}" class="hover:underline">Permalink</a>
&middot; <a href="{{ post.url }}" class="hover:underline" aria-label="Permalink: {{ post.data.title or (post.date | dateDisplay) }}">Permalink</a>
</div>
</div>
@@ -128,7 +128,7 @@ permalink: "digest/{{ digest.slug }}/"
<p class="text-surface-700 dark:text-surface-300">{{ post.templateContent | striptags | truncate(200) }}</p>
<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>
&middot; <a href="{{ post.url }}" class="hover:underline">Permalink</a>
&middot; <a href="{{ post.url }}" class="hover:underline" aria-label="Permalink: {{ post.data.title or (post.date | dateDisplay) }}">Permalink</a>
</div>
</div>
{% endif %}

View File

@@ -137,7 +137,7 @@ permalink: "featured/{% if pagination.pageNumber > 0 %}page/{{ pagination.pageNu
<div class="e-content prose dark:prose-invert prose-sm mt-3 max-w-none">{{ post.templateContent | safe }}</div>
{% endif %}
<div class="post-footer mt-3">
<a href="{{ post.url }}" class="text-sm text-accent-600 dark:text-accent-400 hover:underline">Permalink</a>
<a href="{{ post.url }}" class="text-sm text-accent-600 dark:text-accent-400 hover:underline" aria-label="Permalink: {{ post.data.title or ('Post from ' + (post.date | dateDisplay)) }}">Permalink</a>
</div>
{% endif %}

View File

@@ -96,6 +96,23 @@ document.addEventListener("alpine:init", () => {
if (e.key === "Escape") this.close();
if (e.key === "ArrowRight") this.next();
if (e.key === "ArrowLeft") this.prev();
if (e.key === "Tab") {
const dialog = document.querySelector('[role="dialog"][aria-modal="true"]');
if (!dialog) return;
const focusable = Array.from(
dialog.querySelectorAll('button, [tabindex]:not([tabindex="-1"])')
).filter((el) => el.offsetParent !== null);
if (!focusable.length) return;
const first = focusable[0];
const last = focusable[focusable.length - 1];
if (e.shiftKey && document.activeElement === first) {
e.preventDefault();
last.focus();
} else if (!e.shiftKey && document.activeElement === last) {
e.preventDefault();
first.focus();
}
}
},
}));
});

View File

@@ -62,7 +62,7 @@ permalink: "likes/{% if pagination.pageNumber > 0 %}page/{{ pagination.pageNumbe
{{ post.templateContent | safe }}
</div>
{% endif %}
<a class="u-url text-sm text-red-600 dark:text-red-400 hover:underline mt-3 inline-block" href="{{ post.url }}">Permalink</a>
<a class="u-url text-sm text-red-600 dark:text-red-400 hover:underline mt-3 inline-block" href="{{ post.url }}" aria-label="Permalink: {{ post.data.title or ('Like from ' + (post.date | dateDisplay)) }}">Permalink</a>
</div>
</div>
</li>

View File

@@ -48,7 +48,7 @@ permalink: "notes/{% if pagination.pageNumber > 0 %}page/{{ pagination.pageNumbe
{{ post.templateContent | safe }}
</div>
<div class="post-footer mt-3">
<a href="{{ post.url }}" class="text-sm text-teal-600 dark:text-teal-400 hover:underline">
<a href="{{ post.url }}" class="text-sm text-teal-600 dark:text-teal-400 hover:underline" aria-label="Permalink: {{ post.data.title or ('Note from ' + (post.date | dateDisplay)) }}">
Permalink
</a>
</div>

View File

@@ -59,7 +59,7 @@ permalink: "photos/{% if pagination.pageNumber > 0 %}page/{{ pagination.pageNumb
{% if post.templateContent %}
<div class="e-content photo-caption prose dark:prose-invert prose-sm mt-3 max-w-none">{{ post.templateContent | safe }}</div>
{% endif %}
<a class="u-url text-sm text-purple-600 dark:text-purple-400 hover:underline mt-3 inline-block" href="{{ post.url }}">Permalink</a>
<a class="u-url text-sm text-purple-600 dark:text-purple-400 hover:underline mt-3 inline-block" href="{{ post.url }}" aria-label="Permalink: {{ post.data.title or ('Photo from ' + (post.date | dateDisplay)) }}">Permalink</a>
</li>
{% endfor %}
</ul>

View File

@@ -104,6 +104,7 @@ permalink: /podroll/
preload="none"
class="w-full h-10 rounded-lg"
:src="episode.enclosure?.url"
:aria-label="'Play: ' + episode.title"
>
Your browser does not support the audio element.
</audio>

View File

@@ -91,7 +91,7 @@ permalink: "replies/{% if pagination.pageNumber > 0 %}page/{{ pagination.pageNum
<div class="e-content prose dark:prose-invert prose-sm mt-3 max-w-none">
{{ post.templateContent | safe }}
</div>
<a class="u-url text-sm text-sky-600 dark:text-sky-400 hover:underline mt-3 inline-block" href="{{ post.url }}">Permalink</a>
<a class="u-url text-sm text-sky-600 dark:text-sky-400 hover:underline mt-3 inline-block" href="{{ post.url }}" aria-label="Permalink: {{ post.data.title or ('Reply from ' + (post.date | dateDisplay)) }}">Permalink</a>
</div>
</div>
</li>

View File

@@ -67,7 +67,7 @@ permalink: "reposts/{% if pagination.pageNumber > 0 %}page/{{ pagination.pageNum
{{ post.templateContent | safe }}
</div>
{% endif %}
<a class="u-url text-sm text-green-600 dark:text-green-400 hover:underline mt-3 inline-block" href="{{ post.url }}">Permalink</a>
<a class="u-url text-sm text-green-600 dark:text-green-400 hover:underline mt-3 inline-block" href="{{ post.url }}" aria-label="Permalink: {{ post.data.title or ('Repost from ' + (post.date | dateDisplay)) }}">Permalink</a>
</div>
</div>
</li>