Phase 2d: accessibility fixes, the newsletter on atelier pages, and the category banners #6

Merged
pjotr merged 11 commits from fix/phase-2d-a11y into main 2026-10-28 14:26:25 +00:00
Owner

Vera's gate on Phase 2c listed accessibility gaps and two bugs that were already live. This PR fixes them. Everything still works with a mouse exactly as before. In addition, the site now works with a keyboard and a screen reader. Only three things look different:

  • The home page's category labels get their textures.
  • The lightbox gets a "Sluiten" button.
  • Keyboard focus is easier to see.

Nothing changes on the server.

Commits:

  1. Newsletter on atelier pages (elements/newsletter.vue). mailerlite.js was loaded by a relative path. On /aanbod/<cat>/<atelier> that path 404ed, so the newsletter form never appeared there. It is now /assets/scripts/mailerlite.js. On Schilderkunst, the form now renders, with its email field.
  2. Category banners on the home page (elements/category.vue). The code asked for ..._Kinderen.svg, but the files are named ..._KINDEREN.svg. That gave 6 × 404 on every visit to /. The reference now uses capitals and a root-absolute path; the files are unchanged. Focus has no texture file, so it keeps its plain colour. Result: 0 × 404. Visible change: the textures for Kinderen, Jongeren, Volwassenen, Kunstkuur and Track now show (screenshots below).
  3. Gallery by keyboard (elements/images.vue, WCAG 2.1.1).
    • Each thumbnail is now a <button> named by the image's alt text. Enter and Space open the lightbox.
    • Keyboard focus shows as a black outline with a white inner line, drawn inside the thumbnail. Outside it, the surrounding container clips it.
    • The lightbox gets a small "Sluiten" button in the content's green button style, above the image on the right. Focus moves to it, and Tab stays on it.
    • On close (the button, Esc, or a click outside), focus returns to the thumbnail that opened the lightbox.
  4. Mobile menu toggle (WCAG 4.1.2). It now has aria-expanded, aria-controls="mobile-menu", and the label "Menu openen" or "Menu sluiten". The old label was "Toggle menu".
  5. Header nav.
    • The active link has aria-current="page". The hidden English "Current page:" text is gone, so screen readers announce it once, in the reader's language.
    • Enter on a nav link no longer blurs it (a Nuxt UI leftover), so focus stays on the link after navigating.
  6. Iframe titles (WCAG 4.1.2).
    • Maps: "Kaart: Hoofdschool", and so on for each location.
    • Vimeo: the video's description, or "Video " when there is none.
    • TimeTree: "Kalender" (in homepage.yml, only the attribute changes).
  7. Focus ring on the green buttons (WCAG 1.4.11). The ring was main-500 on the main-200 fill, which is 2.92:1. It is now black: 14.96:1 on the fill, 7.45:1 on hover, 21:1 on white. Rest and hover look the same.
  8. Landmarks and headings.
    • The page content sits in <main>.
    • The logo link is named "Sint-Lukas Academie, startpagina".
    • / gets an h1: the banner line "Dé academie voor beeldende kunsten...", which was a span.
    • /aanbod gets an h1: the banner title.
    • Markdown ### headings now render as <h2> with the h3 look, so a page no longer jumps from h1 to h3. This applies to the ateliers, /inschrijven and /contact (/academie has no ### headings). Every content heading is ###, so no level is lost. "Afgelopen trajecten" also becomes an h2.
    • No visible change.
  9. Link buttons without a link (OKAN, Tienerschool, Paviljoen and Sint-Jozef Woluwe on Kunstkuur). Before, each was a focusable <button> that did nothing. Now each is a <span> that looks the same at rest. It is not focusable, has no hover colour and no pointer cursor.
  10. README: "Toegankelijkheid" for content editors:
    • every image needs an alt;
    • every video needs a description;
    • a Link button without a link is plain text;
    • headings stay ###.
  11. Follow-up to 3: the thumbnail focus ring moved inside the thumbnail. The first version sat outside it and was cut off on two sides.

Checked locally with Node 22.14.0 and pnpm 9.1.2 (Corepack). Both builds were made from clean worktrees, main (93b5696) and this branch, and served side by side. Third-party requests were blocked in both, and the home banner's frame timer was stopped in both.

  • A clean pnpm install --frozen-lockfile and pnpm build pass.

  • Pixels: all 22 routes at 375, 768 and 1280, full page (66 pairs).

    • 63 pairs are identical, 0 pixels. That includes every atelier, where the headings changed from h3 to h2 and the 4 dead buttons became spans.
    • The other 3 are / at each width. The difference is only the category label block (26.5k pixels, the textures), and every page keeps its height.
  • Category labels: the layout holds at 375, 768 and 1280: same size, same position. The black text stays readable on the textures: the median background contrast is 7.3:1 or more, and the text is 36 px bold. Kinderen improves from 4.8:1 to 13.3:1.

  • axe-core 4.13 (WCAG 2.0/2.1/2.2 A+AA plus best-practice) on /, /aanbod, Schilderkunst, Kunstkuur, /academie and /contact, at 1280 and 375. Fixed on every page where they appeared:

    • landmark-one-main;
    • region;
    • link-name (the logo);
    • page-has-heading-one (/ and /aanbod);
    • heading-order (Schilderkunst, Kunstkuur, /contact);
    • frame-title (3 Vimeo, 7 or 8 maps).

    /aanbod and Kunstkuur now have 0 violations.

  • Failed local requests: 6 on / before, 0 after; 1 on each atelier before (mailerlite.js), 0 after.

  • UI scanner (GL-028):

    • Schilderkunst and Kunstkuur go from FAIL (skipped heading) to PASS.
    • / passes in both builds.
    • The same 7 line-break advisories remain on Schilderkunst (content text).
  • Keyboard walk, at 375, 768 and 1280:

    • From the top of Schilderkunst, Tab reaches the first thumbnail. That is 10 stops at 1280 and 6 on mobile, and on main the thumbnails are never reached.
    • Every stop is visible (opacity 1, on screen, a focus indicator).
    • Enter opens the lightbox with focus on Sluiten. Tab and Shift+Tab stay there, and Enter closes it with focus back on the same thumbnail.
    • Space and Esc do the same on the next thumbnail.
    • With the mouse: click a thumbnail, click outside, and focus returns without a ring.
    • The page scroll lock is released.
  • Kunstkuur: 16 Tab stops before, 12 after. The 4 dead labels are gone from the Tab order.

  • Nav: Enter on "Academie" navigates, and focus stays on the link (on main it drops to the page). aria-current follows the page.

  • Mobile toggle: Enter and Space toggle it. The label and aria-expanded follow, and aria-controls points at the open panel.

Not checked:

  • Docker and Woodpecker (unchanged by this PR).
  • Browsers other than Chromium, and a real screen reader (VoiceOver or NVDA). The checks used the accessibility tree.
  • Each intermediate commit was not built on its own; only the branch tip was.
  • Real third-party content, apart from MailerLite on Schilderkunst, which was checked unblocked.

Found, not changed (already on main, outside this PR):

  • Images with no alt. On the home page, the animated banner image and the news thumbnails: news ignores alt, and its fallback image path is relative. On /academie and the ateliers, the team photos: 16 and 1.
  • Weak link contrast. "Bekijk in Google Maps" under each location is opacity-50, which fails contrast (7 on /academie and on /contact).
  • Generic gallery alt text. The Schilderkunst gallery alts say "atelier schilderkunst afbeelding 1/2/3". The thumbnails now carry those as button names, so better alts would help.
  • No active nav link on atelier pages. On an atelier page no nav link is active, including Aanbod. That is the same router behaviour as before.

Screenshots: qa-phase-2d/ in the vault (category labels main/branch at 3 widths, lightbox with Sluiten, thumbnail focus, green button focus main/branch, newsletter on an atelier page).

Vera's gate on Phase 2c listed accessibility gaps and two bugs that were already live. This PR fixes them. Everything still works with a mouse exactly as before. In addition, the site now works with a keyboard and a screen reader. Only three things look different: - The home page's category labels get their textures. - The lightbox gets a "Sluiten" button. - Keyboard focus is easier to see. Nothing changes on the server. Commits: 1. **Newsletter on atelier pages** (`elements/newsletter.vue`). `mailerlite.js` was loaded by a relative path. On `/aanbod/<cat>/<atelier>` that path 404ed, so the newsletter form never appeared there. It is now `/assets/scripts/mailerlite.js`. On Schilderkunst, the form now renders, with its email field. 2. **Category banners on the home page** (`elements/category.vue`). The code asked for `..._Kinderen.svg`, but the files are named `..._KINDEREN.svg`. That gave 6 × 404 on every visit to `/`. The reference now uses capitals and a root-absolute path; the files are unchanged. Focus has no texture file, so it keeps its plain colour. Result: 0 × 404. **Visible change:** the textures for Kinderen, Jongeren, Volwassenen, Kunstkuur and Track now show (screenshots below). 3. **Gallery by keyboard** (`elements/images.vue`, WCAG 2.1.1). - Each thumbnail is now a `<button>` named by the image's alt text. Enter and Space open the lightbox. - Keyboard focus shows as a black outline with a white inner line, drawn inside the thumbnail. Outside it, the surrounding container clips it. - The lightbox gets a small **"Sluiten"** button in the content's green button style, above the image on the right. Focus moves to it, and Tab stays on it. - On close (the button, Esc, or a click outside), focus returns to the thumbnail that opened the lightbox. 4. **Mobile menu toggle** (WCAG 4.1.2). It now has `aria-expanded`, `aria-controls="mobile-menu"`, and the label "Menu openen" or "Menu sluiten". The old label was "Toggle menu". 5. **Header nav.** - The active link has `aria-current="page"`. The hidden English "Current page:" text is gone, so screen readers announce it once, in the reader's language. - Enter on a nav link no longer blurs it (a Nuxt UI leftover), so focus stays on the link after navigating. 6. **Iframe titles** (WCAG 4.1.2). - Maps: "Kaart: Hoofdschool", and so on for each location. - Vimeo: the video's `description`, or "Video <n>" when there is none. - TimeTree: "Kalender" (in `homepage.yml`, only the attribute changes). 7. **Focus ring on the green buttons** (WCAG 1.4.11). The ring was main-500 on the main-200 fill, which is 2.92:1. It is now black: 14.96:1 on the fill, 7.45:1 on hover, 21:1 on white. Rest and hover look the same. 8. **Landmarks and headings.** - The page content sits in `<main>`. - The logo link is named "Sint-Lukas Academie, startpagina". - `/` gets an h1: the banner line "Dé academie voor beeldende kunsten...", which was a span. - `/aanbod` gets an h1: the banner title. - Markdown `###` headings now render as `<h2>` with the h3 look, so a page no longer jumps from h1 to h3. This applies to the ateliers, `/inschrijven` and `/contact` (`/academie` has no `###` headings). Every content heading is `###`, so no level is lost. "Afgelopen trajecten" also becomes an h2. - No visible change. 9. **Link buttons without a link** (OKAN, Tienerschool, Paviljoen and Sint-Jozef Woluwe on Kunstkuur). Before, each was a focusable `<button>` that did nothing. Now each is a `<span>` that looks the same at rest. It is not focusable, has no hover colour and no pointer cursor. 10. **README: "Toegankelijkheid"** for content editors: - every image needs an alt; - every video needs a description; - a Link button without a link is plain text; - headings stay `###`. 11. **Follow-up to 3:** the thumbnail focus ring moved inside the thumbnail. The first version sat outside it and was cut off on two sides. **Checked** locally with Node 22.14.0 and pnpm 9.1.2 (Corepack). Both builds were made from clean worktrees, `main` (`93b5696`) and this branch, and served side by side. Third-party requests were blocked in both, and the home banner's frame timer was stopped in both. - **A clean `pnpm install --frozen-lockfile` and `pnpm build` pass.** - **Pixels: all 22 routes at 375, 768 and 1280, full page (66 pairs).** - 63 pairs are identical, 0 pixels. That includes every atelier, where the headings changed from h3 to h2 and the 4 dead buttons became spans. - The other 3 are `/` at each width. The difference is only the category label block (26.5k pixels, the textures), and every page keeps its height. - **Category labels:** the layout holds at 375, 768 and 1280: same size, same position. The black text stays readable on the textures: the median background contrast is 7.3:1 or more, and the text is 36 px bold. Kinderen improves from 4.8:1 to 13.3:1. - **axe-core 4.13** (WCAG 2.0/2.1/2.2 A+AA plus best-practice) on `/`, `/aanbod`, Schilderkunst, Kunstkuur, `/academie` and `/contact`, at 1280 and 375. Fixed on every page where they appeared: - `landmark-one-main`; - `region`; - `link-name` (the logo); - `page-has-heading-one` (`/` and `/aanbod`); - `heading-order` (Schilderkunst, Kunstkuur, `/contact`); - `frame-title` (3 Vimeo, 7 or 8 maps). `/aanbod` and Kunstkuur now have 0 violations. - **Failed local requests:** 6 on `/` before, 0 after; 1 on each atelier before (`mailerlite.js`), 0 after. - **UI scanner (GL-028):** - Schilderkunst and Kunstkuur go from FAIL (skipped heading) to PASS. - `/` passes in both builds. - The same 7 line-break advisories remain on Schilderkunst (content text). - **Keyboard walk, at 375, 768 and 1280:** - From the top of Schilderkunst, Tab reaches the first thumbnail. That is 10 stops at 1280 and 6 on mobile, and on `main` the thumbnails are never reached. - Every stop is visible (opacity 1, on screen, a focus indicator). - Enter opens the lightbox with focus on Sluiten. Tab and Shift+Tab stay there, and Enter closes it with focus back on the same thumbnail. - Space and Esc do the same on the next thumbnail. - With the mouse: click a thumbnail, click outside, and focus returns without a ring. - The page scroll lock is released. - **Kunstkuur:** 16 Tab stops before, 12 after. The 4 dead labels are gone from the Tab order. - **Nav:** Enter on "Academie" navigates, and focus stays on the link (on `main` it drops to the page). `aria-current` follows the page. - **Mobile toggle:** Enter and Space toggle it. The label and `aria-expanded` follow, and `aria-controls` points at the open panel. Not checked: - Docker and Woodpecker (unchanged by this PR). - Browsers other than Chromium, and a real screen reader (VoiceOver or NVDA). The checks used the accessibility tree. - Each intermediate commit was not built on its own; only the branch tip was. - Real third-party content, apart from MailerLite on Schilderkunst, which was checked unblocked. Found, not changed (already on `main`, outside this PR): - **Images with no alt.** On the home page, the animated banner image and the news thumbnails: news ignores alt, and its fallback image path is relative. On `/academie` and the ateliers, the team photos: 16 and 1. - **Weak link contrast.** "Bekijk in Google Maps" under each location is `opacity-50`, which fails contrast (7 on `/academie` and on `/contact`). - **Generic gallery alt text.** The Schilderkunst gallery alts say "atelier schilderkunst afbeelding 1/2/3". The thumbnails now carry those as button names, so better alts would help. - **No active nav link on atelier pages.** On an atelier page no nav link is active, including Aanbod. That is the same router behaviour as before. Screenshots: `qa-phase-2d/` in the vault (category labels main/branch at 3 widths, lightbox with Sluiten, thumbnail focus, green button focus main/branch, newsletter on an atelier page).
The footer's newsletter block loaded assets/scripts/mailerlite.js by a
relative path. On nested pages (/aanbod/<cat>/<atelier>) that resolved to
/aanbod/<cat>/assets/scripts/mailerlite.js and 404ed, so the MailerLite form
never rendered there. It is now /assets/scripts/mailerlite.js on every page.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The home page's category list asked for "Banner aanbod 600x60px_Kinderen.svg"
and five more, by a relative path, but the files are named in capitals
(_KINDEREN.svg). All six requests 404ed, so the textures never showed and each
category was a plain colour block. The reference now upper-cases the name and
is root-absolute; the files are unchanged.

Focus has no texture file, so it gets no background image (its colour stays):
0 banner 404s instead of 6.

Visible change: the textures for Kinderen, Jongeren, Volwassenen, Kunstkuur
and Track now appear behind the category names on /.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
WCAG 2.1.1. The gallery thumbnails were divs with a click handler, so the
lightbox opened only by mouse or touch. Each thumbnail is now a <button>
named by the image's alt text: Tab reaches it, Enter and Space open the
lightbox, and a black outline (2 px, offset 2 px) shows keyboard focus. The
box, the green behind the image and the hover zoom are unchanged.

The lightbox gets a visible close button, "Sluiten", in the content's green
button style (small, right-aligned above the image). Focus moves to it when
the lightbox opens; Tab stays on it; on close (button, Esc or a click outside
the image) focus returns to the thumbnail that opened it. Before, focus sat on
the dialog itself with no visible indicator, and there was nothing to Tab to.

Visible change: the Sluiten button above the image in the open lightbox, and
the focus outline on a thumbnail.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
WCAG 4.1.2. The mobile menu toggle had an English label ("Toggle menu") and
no state. It now has aria-expanded (false/true), aria-controls pointing at
the menu panel (id="mobile-menu"), and the label "Menu openen" or "Menu
sluiten". It is also type="button". No visible change.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The active desktop nav link announced a hidden English "Current page:" and had
no aria-current. It now has aria-current="page", and the hidden text is gone,
so screen readers say it once, in the reader's own language.

Pressing Enter on a nav link also blurred it (a Nuxt UI leftover), which
dropped keyboard focus to the page after navigating. That handler is removed;
focus stays on the link.

No visible change: the active underline is the same router state as before.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
WCAG 4.1.2. The OpenStreetMap maps and the Vimeo players had no title, so
screen readers announced them as an unnamed frame.

- Maps (/academie, /contact): "Kaart: <location name>", e.g. "Kaart: Hoofdschool".
- Vimeo (Kunstkuur): the video's `description` from the content, or "Video <n>"
  when a video has none.
- TimeTree calendar (/): the embed code in homepage.yml said title="Calendar";
  it now says "Kalender". Only the attribute changes.

No visible change.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
WCAG 1.4.11. The green Link buttons (and the 404 page's button) showed
keyboard focus as a main-500 ring inside a main-200 fill: 2.92:1, under the
3:1 the focus indicator needs. The ring is now black-500 (#000), the same
2 px inset ring: 14.96:1 against the fill, 7.45:1 against the hover fill,
21:1 against the white page.

Only :focus-visible changes; rest and hover look the same. The black
Inschrijven button keeps its main-500 ring (5.13:1 on black, 4.10:1 on white).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- layouts/default.vue: the page content sits in <main>; header and footer stay
  outside it. Every page had no main landmark (axe landmark-one-main, region).
- The logo link had no accessible name; it is now "Sint-Lukas Academie,
  startpagina".
- / had no h1: the visible banner line ("Dé academie voor beeldende kunsten")
  becomes the h1, with the classes of the span it was.
- /aanbod had no h1: the banner title ("Vind jouw atelier. Ontdek onze extra
  muros projecten.") is now an h1 when a banner has a title; banners without a
  title (atelier, academie, inschrijven, contact) stay a div.
- Markdown "###" headings went straight from the page's h1 to h3 (ateliers,
  /academie, /inschrijven, /contact). ProseH3 now renders them as
  <h2 class="md-heading">, and main.css gives that class the look prose gave
  the h3 (size, weight, line height, margins, the sidebar's uppercase). All
  content headings are ###, so no level is lost. The video block's "Afgelopen
  trajecten" heading is an h2 too, with the same class.

No visible change: the 22 routes at 375, 768 and 1280 are pixel-identical to
main apart from the home page's category banners (an earlier commit).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
On Kunstkuur, four Link buttons (OKAN, Tienerschool, Paviljoen, Sint-Jozef
Woluwe) have no link in the content. They rendered as a focusable <button>
that did nothing, so keyboard and screen-reader users met four controls with
no action. Without a link, elements/button.vue now renders a <span> with the
same look at rest: same fill, padding, size and position (pixel-identical).
It is not focusable, and it no longer changes colour on hover or shows the
pointer cursor, since there is nothing to click.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
What content editors must fill in for the site to stay accessible: an alt
for every image (it also names the gallery thumbnail button), a description
for every video (it titles the player), and that a Link button without a
link is plain text. Also: Markdown headings stay ###, which the site renders
as the level under the page title.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The focus outline from the gallery commit sat 2 px outside each thumbnail.
The gallery lives in elements/container.vue, which is overflow-x-hidden and
ends exactly at the thumbnails' outer edges, so the outline was cut off on
the left and bottom of the first column and row (seen in the keyboard walk).

Keyboard focus now draws inside the box: a 2 px black outline on the edge
(outline-offset -2px) and a 2 px white line just inside it (an ::after
border). One of the two stands out against any photo. Rest and hover are
unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
pjotr merged commit 3f14de0f6a into main 2026-10-28 14:26:25 +00:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
sintlukas/sla-website!6
No description provided.