Skip to content

Improve carousel accessibility: pause control, reduced motion, and focus-safe navigation - #287

Open
Castellon-ACM wants to merge 11 commits into
trunkfrom
carousel-a11y-pause-reduced-motion
Open

Improve carousel accessibility: pause control, reduced motion, and focus-safe navigation#287
Castellon-ACM wants to merge 11 commits into
trunkfrom
carousel-a11y-pause-reduced-motion

Conversation

@Castellon-ACM

@Castellon-ACM Castellon-ACM commented Sep 4, 2026

Copy link
Copy Markdown
Contributor

Summary

Closes #259.

Makes FrontBlocks carousels safe and usable by default for keyboard, screen-reader, and motion-sensitive visitors, without changing the block's existing layout/options UI or saved attributes.

  • Reduced motion: autoplay never starts for visitors matching prefers-reduced-motion: reduce, regardless of the configured autoplay value.
  • Accessible pause/resume control: an always-present <button class="glide__pause"> is rendered whenever autoplay is actually running — independent of the optional "pause on hover/focus" setting, since visitors need a guaranteed way to stop auto-advancing content (WCAG 2.2.2), not just a convenience tied to their input method. Its aria-label/aria-pressed update correctly on click ("Pause automatic slideshow" ⇄ "Play automatic slideshow").
  • Focus-safe navigation: keyboard/AT focus anywhere inside the carousel pauses autoplay and resumes it on blur — coordinated with hover and the manual pause button via a shared pause-reasons set, so a manual pause correctly survives the mouse leaving or focus moving away (previously Glide's own hoverpause and a naive focus handler would have fought each other).
  • Visible focus indicator on arrows: .glide__arrow previously had outline: none on :focus with nothing replacing it — a real WCAG 2.4.7 violation. Now shows a visible outline for keyboard focus (suppressed only for mouse clicks via :focus-visible).
  • 24×24 CSS px hit targets on bullets: the clickable/tappable button box is enlarged to the WCAG 2.5.8 minimum via a centered pseudo-element, while the visible dot itself stays at its original 10px so the design is unchanged.
  • Inactive slides stay in the accessibility tree: verified — Glide already keeps every slide in the DOM (transform-based, not display:none/aria-hidden), so no change was needed there.
  • Existing markup/attributes preserved: no new block attributes, no changes to the inspector options UI — purely frontend JS/CSS behavior.

Test plan

  • New JS unit tests (npm run test:carousel-accessibility, 9 tests): reduced-motion forcing autoplay off, the pause button only rendering when autoplay actually runs, click toggling pause/play and its own label/aria-pressed state, focus pausing/resuming, the pause-on-hover opt-out, and that a manual pause survives the mouse leaving the carousel.
  • Wired into CI alongside the existing JS test jobs.
  • Live-verified in a real browser (Playwright, two real carousels — bullets+autoplay and arrows+no-autoplay): reduced motion confirmed blocking autoplay via prefers-reduced-motion emulation; pause button click actually froze/resumed slide advancement (transform tracked over multiple seconds); real keyboard Tab navigation paused/resumed autoplay on focus/blur; a manual pause survived focus leaving the carousel; arrow focus outline confirmed via computed styles (outline-style: solid, 2px, visible); bullet hit target confirmed via getBoundingClientRect() (24×24) vs. the visible dot's computed size (10×10). No JS console/page errors in any scenario.
  • Full PHPUnit suite unaffected (no PHP touched).
  • php -l / node -c clean on all touched files.
Open WordPress Playground Preview

Framework-free, unit-testable functions for the checks needed by
issue #261: WCAG contrast ratio calculation, non-text contrast
(1.4.11), autoplay pause-control and timing safety (2.2.2), and
missing image label detection. Exposed as window.FrontBlocksA11y so
any block's InspectorControls can reuse them without a build step.
Registers frontblocks-a11y-utils globally and loads it as a
dependency of both the Carousel and Before/After block editor
scripts, so their InspectorControls can use window.FrontBlocksA11y.
The block's images are always rendered with alt="" (decorative);
the before/after label is the only accessible text describing what
each image shows. Flags it, advisory-only, when an image is set but
its label is empty or whitespace.
Adds a real "Pause on hover/focus" toggle (frblPauseOnHover, default
true) so autoplaying carousels have an actual way to be paused,
including via keyboard/assistive-tech focus (Glide's own hoverpause
only reacts to mouse hover).

Surfaces three advisory, non-blocking inspector warnings using the
shared accessibility utils:
- autoplay enabled with no pause control (WCAG 2.2.2)
- autoplay interval under the 5-second safety minimum
- button color vs. background color below the 3:1 non-text contrast
  minimum (WCAG 1.4.11), only when both colors are set
Covers the frblPauseOnHover default (true) in both attribute
registration filters, and its data-pause-on-hover output in the
grid block content filter, including when explicitly disabled.
The JavaScript tests job only ran test:cookie-notice, so the new
accessibility-utils test suite added in this branch wasn't actually
enforced by CI.
- Never auto-start autoplay for visitors matching
  prefers-reduced-motion: reduce, regardless of the configured value.
- Render an always-present, accessible pause/resume button whenever
  autoplay is actually running (WCAG 2.2.2), independent of the
  optional "pause on hover/focus" setting — visitors need a
  guaranteed way to stop auto-advancing content, not just a
  convenience tied to their input method.
- Replace Glide's built-in hoverpause (mouse-only) with a manual
  pause-reason coordinator (hover, keyboard/AT focus, and the pause
  button) so none of them fight over Glide's play/pause state — e.g.
  a manual pause now survives the mouse leaving the carousel.
- Arrows no longer set outline: none on :focus with no replacement —
  they now get a real visible focus indicator (WCAG 2.4.7),
  suppressed only for mouse clicks via :focus-visible.
- Bullets grow their actual clickable/tappable button box to the
  WCAG 2.5.8 minimum of 24x24 CSS px via a centered pseudo-element,
  while keeping the visible dot itself at its original 10px so the
  design doesn't change.
- Styles the new accessible pause/resume button (28x28, visible
  focus indicator).
Covers: reduced-motion forcing autoplay off, the pause button only
rendering when autoplay actually runs, click toggling pause/play and
its own label/aria-pressed state, focus pausing/resuming, the
pause-on-hover opt-out, and that a manual pause survives the mouse
leaving the carousel.
@chatgpt-codex-connector

Copy link
Copy Markdown

Codex usage limits have been reached for code reviews. Please check with the admins of this repo to increase the limits by adding credits.
Credits must be used to enable repository wide code reviews.

@davidperezgar

Copy link
Copy Markdown
Contributor

Reviewed the current diff. I did not find a blocking issue in this PR. Published CI checks are green.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Improve carousel accessibility: pause control, reduced motion, and focus-safe navigation

2 participants