mirror of
https://github.com/svemagie/blog-eleventy-indiekit.git
synced 2026-04-02 08:44:56 +02:00
- 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
114 lines
4.8 KiB
Plaintext
114 lines
4.8 KiB
Plaintext
{# h-card - IndieWeb identity microformat #}
|
|
{# See: https://microformats.org/wiki/h-card #}
|
|
{#
|
|
This is the canonical h-card component for the site.
|
|
Include in sidebar widgets, author cards, etc.
|
|
#}
|
|
{% set id = homepageConfig.identity if (homepageConfig and homepageConfig.identity) else {} %}
|
|
{% set authorName = id.name or site.author.name %}
|
|
{% set authorAvatar = id.avatar or site.author.avatar %}
|
|
{% set authorTitle = id.title or site.author.title %}
|
|
{% set authorBio = id.bio or site.author.bio %}
|
|
{% set authorUrl = id.url or site.author.url %}
|
|
{% set authorPronoun = id.pronoun or site.author.pronoun %}
|
|
{% set authorLocality = id.locality or site.author.locality %}
|
|
{% set authorCountry = id.country or site.author.country %}
|
|
{% set authorLocation = site.author.location %}
|
|
{% set authorOrg = id.org or site.author.org %}
|
|
{% set authorEmail = id.email or site.author.email %}
|
|
{% set authorKeyUrl = id.keyUrl or site.author.keyUrl %}
|
|
{% set authorCategories = id.categories if (id.categories and id.categories.length) else site.author.categories %}
|
|
{% set socialLinks = id.social if (id.social and id.social.length) else site.social %}
|
|
|
|
<div class="h-card p-author" itemscope itemtype="http://schema.org/Person">
|
|
{# Hidden u-photo for reliable microformat parsing (some parsers struggle with img inside links) #}
|
|
<data class="u-photo hidden" value="{{ authorAvatar }}"></data>
|
|
|
|
<div class="flex items-center gap-4">
|
|
<a href="{{ authorUrl }}" class="u-url u-uid" rel="me" itemprop="url">
|
|
<img
|
|
src="{{ authorAvatar }}"
|
|
alt="{{ authorName }}"
|
|
width="64"
|
|
height="64"
|
|
class="w-16 h-16 rounded-full object-cover shadow-lg"
|
|
loading="lazy"
|
|
itemprop="image"
|
|
>
|
|
</a>
|
|
<div>
|
|
<a href="{{ authorUrl }}" class="u-url p-name font-bold text-lg block hover:text-accent-600 dark:hover:text-accent-400" itemprop="name">
|
|
{{ authorName }}
|
|
</a>
|
|
{% if authorPronoun %}
|
|
<span class="p-pronoun text-xs text-surface-600 dark:text-surface-400">({{ authorPronoun }})</span>
|
|
{% endif %}
|
|
<p class="p-job-title text-sm text-surface-600 dark:text-surface-400" itemprop="jobTitle">{{ authorTitle }}</p>
|
|
{# Structured address #}
|
|
<p class="p-adr h-adr text-sm text-surface-600 dark:text-surface-400" itemprop="address" itemscope itemtype="http://schema.org/PostalAddress">
|
|
{% if authorLocality %}
|
|
<span class="p-locality" itemprop="addressLocality">{{ authorLocality }}</span>{% if authorCountry %}, {% endif %}
|
|
{% endif %}
|
|
{% if authorCountry %}
|
|
<span class="p-country-name" itemprop="addressCountry">{{ authorCountry }}</span>
|
|
{% endif %}
|
|
{# Fallback to legacy location field #}
|
|
{% if not authorLocality and authorLocation %}
|
|
<span class="p-locality">{{ authorLocation }}</span>
|
|
{% endif %}
|
|
</p>
|
|
</div>
|
|
</div>
|
|
|
|
{# Bio #}
|
|
<p class="p-note mt-3 text-sm text-surface-700 dark:text-surface-300" itemprop="description">{{ authorBio }}</p>
|
|
|
|
{# Organization #}
|
|
{% if authorOrg %}
|
|
<p class="mt-2 text-sm text-surface-600 dark:text-surface-400">
|
|
<span class="p-org" itemprop="worksFor">{{ authorOrg }}</span>
|
|
</p>
|
|
{% endif %}
|
|
|
|
{# Email and PGP Key #}
|
|
<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">
|
|
✉️ {{ authorEmail | obfuscateEmail | safe }}
|
|
</a>
|
|
{% endif %}
|
|
{% if authorKeyUrl %}
|
|
<a href="{{ authorKeyUrl }}" class="u-key text-surface-600 dark:text-surface-400 hover:underline" rel="pgpkey">
|
|
🔐 PGP Key
|
|
</a>
|
|
{% endif %}
|
|
</div>
|
|
|
|
{# Categories / Skills #}
|
|
{% if authorCategories and authorCategories.length %}
|
|
<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 %}
|
|
<li class="p-category text-xs px-2 py-0.5 bg-surface-100 dark:bg-surface-800 rounded-full">{{ category }}</li>
|
|
{% endfor %}
|
|
</ul>
|
|
{% endif %}
|
|
|
|
{# Social links with rel="me" - critical for IndieWeb identity verification #}
|
|
{% from "components/social-icon.njk" import socialIcon %}
|
|
{% if socialLinks and socialLinks.length %}
|
|
<nav class="flex flex-wrap gap-3 mt-3" aria-label="Social links">
|
|
{% for link in socialLinks %}
|
|
<a
|
|
href="{{ link.url }}"
|
|
rel="{{ link.rel }} noopener"
|
|
class="u-url text-surface-600 dark:text-surface-400 hover:text-accent-600 dark:hover:text-accent-400 transition-colors"
|
|
aria-label="{{ link.name }} (opens in new tab)"
|
|
target="_blank">
|
|
{{ socialIcon(link.icon, "w-5 h-5") }}
|
|
</a>
|
|
{% endfor %}
|
|
</nav>
|
|
{% endif %}
|
|
</div>
|