Files
blog-eleventy-indiekit/_includes/components/sections/cv-education.njk
Ricardo e236b4bf65 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
2026-03-07 18:58:08 +01:00

89 lines
4.1 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{#
CV Education Section - collapsible education cards (accordion)
Data fetched from /cv/data.json via homepage plugin
Each card gets a distinct color via cycling palette
#}
{% set hasEducation = cv and cv.education and cv.education.length %}
{% if hasEducation %}
<section class="mb-8 sm:mb-12" id="education" x-data="{ expanded: {} }">
<h2 class="text-xl sm:text-2xl font-bold text-surface-900 dark:text-surface-100 mb-4 sm:mb-6">
{{ section.config.title or "Education" }}
</h2>
<div class="grid grid-cols-1 sm:grid-cols-2 gap-4 items-start">
{% for item in cv.education %}
{% if not filterType or item.educationType == filterType or not item.educationType %}
{% set ci = loop.index0 % 8 %}
<div class="bg-surface-50 dark:bg-surface-800 rounded-lg border border-surface-200 dark:border-surface-700 shadow-sm transition-colors overflow-hidden border-l-[3px]
{% if ci == 0 %}border-l-amber-400 dark:border-l-amber-500
{% elif ci == 1 %}border-l-emerald-400 dark:border-l-emerald-500
{% elif ci == 2 %}border-l-sky-400 dark:border-l-sky-500
{% elif ci == 3 %}border-l-rose-400 dark:border-l-rose-500
{% elif ci == 4 %}border-l-purple-400 dark:border-l-purple-500
{% elif ci == 5 %}border-l-orange-400 dark:border-l-orange-500
{% elif ci == 6 %}border-l-teal-400 dark:border-l-teal-500
{% elif ci == 7 %}border-l-indigo-400 dark:border-l-indigo-500
{% endif %}">
{# Summary row — always visible, clickable #}
<button
class="w-full p-4 flex items-center justify-between gap-2 cursor-pointer text-left hover:bg-surface-50 dark:hover:bg-surface-700/50 transition-colors"
@click="expanded[{{ loop.index0 }}] = !expanded[{{ loop.index0 }}]"
:aria-expanded="expanded[{{ loop.index0 }}] ? 'true' : 'false'"
>
<div class="min-w-0 flex-1">
<h3 class="font-semibold text-surface-900 dark:text-surface-100 truncate">{{ item.degree }}</h3>
<p class="text-sm text-surface-600 dark:text-surface-400 truncate">
{{ item.institution }}{% if item.location %} &middot; {{ item.location }}{% endif %}
</p>
</div>
<div class="flex items-center gap-2 shrink-0">
{% if item.startDate %}
<span class="text-xs text-surface-600 dark:text-surface-400 hidden sm:inline font-mono">
{{ item.startDate }}{% if item.endDate %} {{ item.endDate }}{% else %} Present{% endif %}
</span>
{% elif item.year %}
<span class="text-xs text-surface-600 dark:text-surface-400 hidden sm:inline font-mono">{{ item.year }}</span>
{% endif %}
<svg
class="w-4 h-4 text-surface-400 transition-transform duration-200"
:class="expanded[{{ loop.index0 }}] && '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>
</div>
</button>
{# Detail section — collapsible #}
<div
x-show="expanded[{{ loop.index0 }}]"
x-transition:enter="transition ease-out duration-200"
x-transition:enter-start="opacity-0 -translate-y-1"
x-transition:enter-end="opacity-100 translate-y-0"
x-transition:leave="transition ease-in duration-150"
x-transition:leave-start="opacity-100 translate-y-0"
x-transition:leave-end="opacity-0 -translate-y-1"
x-cloak
class="px-4 pb-4"
>
{% if item.startDate %}
<p class="text-xs text-surface-600 dark:text-surface-400 mb-1 sm:hidden font-mono">
{{ item.startDate }}{% if item.endDate %} {{ item.endDate }}{% else %} Present{% endif %}
</p>
{% elif item.year %}
<p class="text-xs text-surface-600 dark:text-surface-400 mb-1 sm:hidden font-mono">{{ item.year }}</p>
{% endif %}
{% if item.description %}
<p class="text-sm text-surface-600 dark:text-surface-400">{{ item.description }}</p>
{% endif %}
</div>
</div>
{% endif %}
{% endfor %}
</div>
</section>
{% endif %}