From bb27cbc7618c1c68368478723107e05382aa446d Mon Sep 17 00:00:00 2001 From: itziarZG Date: Fri, 10 Jul 2026 08:29:10 +0200 Subject: [PATCH 01/15] feat(programa): improve accessibility of Pretalx widget MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add global skip link to main content (3 i18n locales) - Wrap schedule in region with aria-labelledby, add live region announcing session count changes after filtering - Reinforce dialog ARIA (aria-modal, role, aria-labelledby) via Shadow DOM CSS/attribute injection - Add labels to search input and timezone select inside the widget - Restore focus to filter trigger when dialog closes - Set body[modality] attribute so widget's :focus-visible rings appear for keyboard users - Override --pretalx-clr-success to a distinguishable green Fixes 3 issues from initial a11y pass: - Widget uses Shadow DOM (Vue 3 attachShadow), not light DOM — all queries now use shadow root - modality must be a direct attribute, not data-modality - success color override must live on host's style attribute to cross the shadow boundary --- src/components/ProgramaPage.astro | 10 +- src/i18n/menu/ca.ts | 1 + src/i18n/menu/en.ts | 1 + src/i18n/menu/es.ts | 1 + src/i18n/programa/ca.ts | 7 + src/i18n/programa/en.ts | 7 + src/i18n/programa/es.ts | 7 + src/layouts/Layout.astro | 39 +++++ src/pages/[lang]/programa.astro | 252 ++++++++++++++++++++++++++++-- 9 files changed, 307 insertions(+), 18 deletions(-) diff --git a/src/components/ProgramaPage.astro b/src/components/ProgramaPage.astro index 6c9da84..c2686b6 100644 --- a/src/components/ProgramaPage.astro +++ b/src/components/ProgramaPage.astro @@ -10,15 +10,19 @@ const t = programaTexts[lang as keyof typeof programaTexts] const menuT = menuTexts[lang as keyof typeof menuTexts] --- -
-

{t.title}

+
+

{t.title}

{t.intro}

+ style="--pretalx-clr-primary: #03004b; --pretalx-clr-success: #2e7d32"> + +
+ + From 39584b0dbb690c566010a0e05656b6bf563e4e31 Mon Sep 17 00:00:00 2001 From: itziarZG Date: Fri, 10 Jul 2026 10:34:15 +0200 Subject: [PATCH 02/15] feat(programa): add accessible list view as alternative to Pretalx widget MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Addresses #216 — the Pretalx widget's CSS grid layout is fundamentally hard to navigate with a screen reader, no matter how much ARIA we inject. Adds a parallel accessible list view rendered server-side from the Pretalx API, with the widget preserved as an opt-in visual alternative. Data layer: - src/lib/schedule.ts — fetches schedule.json at build time, normalises per-day structure, returns null on failure (graceful fallback) - src/types/schedule.ts — TS types for the Pretalx export shape UI: - src/components/ScheduleList.astro — semantic
    per day with
    per session,
+
+ @@ -137,12 +185,14 @@ const schedule = await getSchedule() try { const saved = localStorage.getItem(STORAGE_KEY) - if (saved === 'list' || saved === 'grid') { - const target = document.querySelector(`input[name="schedule-view"][value="${saved}"]`) - if (target) { - target.checked = true - applyView(saved) - } + // Only restore the saved view if it matches the HTML default. Legacy + // values (e.g. 'list' from when list was the default) are ignored so + // the page always opens with the current default and there's no jump + // from a stale saved value. + const defaultChecked = document.querySelector('input[name="schedule-view"]:checked') + const defaultValue = defaultChecked ? defaultChecked.value : null + if ((saved === 'list' || saved === 'grid') && saved === defaultValue) { + applyView(saved) } } catch (e) {} })() @@ -177,10 +227,31 @@ const schedule = await getSchedule() document.querySelectorAll('.favorite-btn').forEach((btn) => { const code = btn.getAttribute('data-session-code') if (!code) return - btn.setAttribute('aria-pressed', favs.has(code) ? 'true' : 'false') + const isFav = favs.has(code) + btn.setAttribute('aria-pressed', isFav ? 'true' : 'false') + const li = btn.closest('li') + if (li) li.classList.toggle('is-favorite', isFav) + }) + updateFavoritesCount() + updateEmptyState() + } + + function updateFavoritesCount() { + const count = readFavs().length + document.querySelectorAll('[data-favorites-count]').forEach((el) => { + el.textContent = String(count) }) } + function updateEmptyState() { + const empty = document.querySelector('[data-favorites-empty]') + const listView = document.getElementById('schedule-list-view') + if (!empty || !listView) return + const filterActive = listView.classList.contains('filter-favorites-only') + const count = readFavs().length + empty.hidden = !(filterActive && count === 0) + } + // Click handler: toggle, persist, update visual state and announce document.addEventListener('click', (e) => { const btn = e.target.closest && e.target.closest('.favorite-btn') @@ -222,6 +293,18 @@ const schedule = await getSchedule() if (e.key === FAV_KEY) syncFavorites() }) + // Filter button: show only favourited sessions + const filterBtn = document.querySelector('[data-filter-favorites]') + if (filterBtn) { + filterBtn.addEventListener('click', () => { + const pressed = filterBtn.getAttribute('aria-pressed') === 'true' + filterBtn.setAttribute('aria-pressed', pressed ? 'false' : 'true') + const listView = document.getElementById('schedule-list-view') + if (listView) listView.classList.toggle('filter-favorites-only', !pressed) + updateEmptyState() + }) + } + // Initial render syncFavorites() })() diff --git a/src/i18n/programa/ca.ts b/src/i18n/programa/ca.ts index 0aa6260..3d814f9 100644 --- a/src/i18n/programa/ca.ts +++ b/src/i18n/programa/ca.ts @@ -29,6 +29,8 @@ export const ca = { favorite: 'Preferit', favoriteAdded: 'Afegit a preferits', favoriteRemoved: 'Tret de preferits', + favoritesOnly: 'Només preferits', + noFavorites: "Encara no tens preferits. Marca les sessions que t'interessin amb el cor.", showAbstract: 'Mostra la descripció', hideAbstract: 'Amaga la descripció', talkLink: 'Veure detalls de la xerrada a Pretalx', diff --git a/src/i18n/programa/en.ts b/src/i18n/programa/en.ts index a19b14c..0d004b2 100644 --- a/src/i18n/programa/en.ts +++ b/src/i18n/programa/en.ts @@ -14,7 +14,7 @@ export const en = { }, list: { viewModeLegend: 'Schedule view mode', - viewModeList: 'Accessible list', + viewModeList: 'List', viewModeGrid: 'Visual grid', dayN: (n: number) => `Day ${n}`, speakers: (count: number) => (count === 1 ? 'Speaker' : 'Speakers'), @@ -29,6 +29,8 @@ export const en = { favorite: 'Favourite', favoriteAdded: 'Added to favourites', favoriteRemoved: 'Removed from favourites', + favoritesOnly: 'Only favourites', + noFavorites: "You don't have any favourites yet. Tap the heart on the sessions you're interested in.", showAbstract: 'Show description', hideAbstract: 'Hide description', talkLink: 'View talk details on Pretalx', diff --git a/src/i18n/programa/es.ts b/src/i18n/programa/es.ts index 9354b9e..1676901 100644 --- a/src/i18n/programa/es.ts +++ b/src/i18n/programa/es.ts @@ -29,6 +29,8 @@ export const es = { favorite: 'Favorito', favoriteAdded: 'Añadido a favoritos', favoriteRemoved: 'Quitado de favoritos', + favoritesOnly: 'Solo favoritos', + noFavorites: 'No tienes favoritos todavía. Marca las sesiones que te interesen con el corazón.', showAbstract: 'Ver descripción', hideAbstract: 'Ocultar descripción', talkLink: 'Ver detalles de la charla en Pretalx', From df69d2dbdf090c3813c5b17e6b7a25aee0190416 Mon Sep 17 00:00:00 2001 From: itziarZG Date: Sun, 12 Jul 2026 20:11:26 +0200 Subject: [PATCH 10/15] fix(programa): wire favourites filter through event delegation + add status MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The 'Only favourites' filter button wasn't doing anything because two click handlers were firing on each click: 1. The new event-delegation handler that toggles aria-pressed and the CSS class 2. The old direct listener (from the previous commit) that toggled aria-pressed again Net effect: aria-pressed went true → false → true, leaving the button exactly as it was. No visible change, no class added. Fix: remove the old direct listener; the event-delegation handler in the document click listener now owns both the per-session favourite toggle and the filter toggle. Also refactored to share a single announce() helper. While here: - Replace the buggy 'has-[:checked]:' Tailwind utilities (the button has no :checked state, it uses aria-pressed) with the correct 'aria-pressed:' variants — those have proper specificity and the orange-pressed look now actually applies - Add status announcements for the filter toggle via the existing #favorite-status live region: 'Favourites filter on. Showing N favourite session(s).' / 'Favourites filter off. Showing all sessions.' - Decompose the i18n message into prefix + singular + plural words (filterOnPrefix, filterOnSingular, filterOnPlural, filterOff) so the script can build the count-aware message at runtime --- src/components/ProgramaPage.astro | 113 +++++++++++++++++------------- src/i18n/programa/ca.ts | 4 ++ src/i18n/programa/en.ts | 4 ++ src/i18n/programa/es.ts | 4 ++ 4 files changed, 75 insertions(+), 50 deletions(-) diff --git a/src/components/ProgramaPage.astro b/src/components/ProgramaPage.astro index 2257d7a..f89518c 100644 --- a/src/components/ProgramaPage.astro +++ b/src/components/ProgramaPage.astro @@ -51,9 +51,14 @@ const schedule = await getSchedule()
- +
+ +