Skip to content

# PR Review - Accessibility Issues #18

@ctrl-alt-d

Description

@ctrl-alt-d

PR Review - Accessibility Issues

1. Decorative characters announced by screen readers

Several decorative characters throughout the page will be read aloud by screen readers, creating a confusing experience for users relying on assistive technology.

Affected locations:

Line 17 - Subtitle:

&gt; <span id="subtitle">Initialising system...</span><span class="animate-pulse">_</span>

The > prefix and _ cursor are purely decorative but will be announced as "greater than" and "underscore".

Line 31 - Sponsor button:

<span id="sponsor-text">&lt; BECOME A SPONSOR /&gt;</span>

The < and /> will be read as "less than" and "slash greater than".

Line ~97 (JS) - Copied feedback:

sponsorText.innerText = '[ EMAIL COPIED! ]'

Brackets will be announced as "left bracket" and "right bracket".

Suggested fix

Wrap decorative characters in elements with aria-hidden="true" to hide them from screen readers:

<!-- Line 17 - Subtitle -->
<h2 class="mt-4 text-xl md:text-2xl text-green-400 font-mono h-8">
  <span aria-hidden="true">&gt; </span><span id="subtitle">Initialising system...</span><span aria-hidden="true" class="animate-pulse">_</span>
</h2>

<!-- Line 31 - Sponsor button -->
<span id="sponsor-text">
  <span aria-hidden="true">&lt; </span>BECOME A SPONSOR<span aria-hidden="true"> /&gt;</span>
</span>

For the JavaScript part, you'll need to preserve the structure when updating the text:

// Around line 97 - Update to preserve aria-hidden spans
sponsorBtn.onclick = async () => {
  try {
    await navigator.clipboard.writeText('sponsors@2026.es.pycon.org')
    const originalHTML = sponsorText.innerHTML
    sponsorText.innerHTML = '<span aria-hidden="true">[ </span>EMAIL COPIED!<span aria-hidden="true"> ]</span>'
    setTimeout(() => {
      sponsorText.innerHTML = originalHTML
    }, 2000)
  } catch (err) {
    console.error('Failed to copy', err)
    window.location.href = 'mailto:sponsors@2026.es.pycon.org'
  }
}

2. Respect prefers-reduced-motion for animations

What is prefers-reduced-motion?

prefers-reduced-motion is a CSS media query that detects if the user has requested the operating system to minimise non-essential motion. Users enable this setting for various reasons:

  • Vestibular disorders (can cause dizziness, nausea, or disorientation)
  • Motion sensitivity
  • Cognitive conditions where motion is distracting
  • Personal preference

It has two values:

  • no-preference - User hasn't indicated a preference
  • reduce - User prefers reduced motion

Where to apply it in this project:

A) The matrix/glitch effect on <h1> (lines 48-70)

This effect rapidly changes characters, which can be disorienting.

const prefersReducedMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches

const init = () => {
  const h1 = document.querySelector('h1')
  // ...existing code...

  if (h1) {
    if (!prefersReducedMotion) {
      runMatrixEffect(h1)
      h1.onmouseover = () => runMatrixEffect(h1)
    }
    // If reduced motion is preferred, the text just appears instantly (no effect needed)
  }
  // ...existing code...
}

B) The pulsing cursor animation (line 17)

The animate-pulse class creates a continuous blinking effect.

/* In global.css or a <style> tag */
@media (prefers-reduced-motion: reduce) {
  .animate-pulse {
    animation: none;
  }
}

Or using Tailwind's built-in support:

<span aria-hidden="true" class="animate-pulse motion-reduce:animate-none">_</span>

Originally posted by @ctrl-alt-d in #16 (review)

Metadata

Metadata

Assignees

Labels

🦮 a11yAccessibility Issue

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions