WCAG 2.2.2: Pause, Stop, Hide

Understanding WCAG 2.2.2: Pause, Stop, Hide

The WCAG 2.2.2 Pause, Stop, Hide success criterion (Level A) mandates that for any moving, blinking, scrolling, or auto-updating content, mechanisms must be provided to pause, stop, or hide it. This requirement applies unless the motion or update is essential to the activity being performed or the information being conveyed. This criterion is fundamental for creating an inclusive and user-friendly web experience, allowing individuals to control potentially distracting or discomforting content.

Why This Criterion Matters for Accessibility

Dynamic content, while often used for engagement, can pose significant barriers for many users. Providing control over such content addresses several critical accessibility impacts:

  • Cognitive Disabilities: Users with cognitive disabilities, Attention Deficit Hyperactivity Disorder (ADHD), or learning disabilities can find continuously moving or blinking content highly distracting. This can make it incredibly difficult to focus on other parts of the page, process information, or complete tasks, leading to sensory overload and frustration.
  • Vestibular Disorders and Motion Sickness: Rapidly moving, parallax scrolling, or even subtly animating content can trigger symptoms such as dizziness, nausea, or disorientation for individuals with vestibular disorders. The ability to stop such motion is vital for their physical comfort and safety.
  • Low Vision and Reading Difficulties: Tracking moving text or objects requires significant visual effort and speed. Users with low vision, those who rely on screen magnification, or individuals with reading difficulties may struggle to read or interact with content that moves too quickly or continuously, as they need more time to focus and comprehend.
  • Motor Impairments: For users with motor impairments, especially those who use assistive technologies like switch devices, interacting with moving targets can be extremely challenging, if not impossible. A target that constantly changes position requires precise timing that many users cannot achieve.
  • General User Experience: Even for users without specific disabilities, uncontrolled moving content can be annoying, distracting, and negatively impact the overall usability of a website, hindering their ability to consume content or complete tasks efficiently.

Success Criteria and Requirements Explained

WCAG 2.2.2 specifically targets four categories of dynamic content:

  • Moving Content: This encompasses elements that shift position, animate, or traverse the screen continuously. Common examples include carousels, image sliders, animated banners, and decorative animations.
  • Blinking Content: Any content that flashes or alternates between visible and invisible states. It’s important to differentiate this from WCAG 2.3.1 (Three Flashes or Below Threshold), which addresses flashing that can trigger seizures. 2.2.2 focuses on providing control over general blinking that might be distracting or irritating.
  • Scrolling Content: Content that automatically scrolls horizontally or vertically. Examples include news tickers, stock market updates, testimonial feeds, or automatically advancing text blocks.
  • Auto-Updating Content: Information that automatically refreshes or changes without direct user interaction, such as live chat feeds, sports scores, stock tickers, or countdown timers that constantly update.

For any of these types of content, the user must be provided with a mechanism to:

  • Pause: Temporarily halt the motion or updates, allowing the user to resume them at their discretion.
  • Stop: Completely end the motion or updates, preventing them from resuming automatically.
  • Hide: Remove the dynamic content from view entirely, making it disappear from the user’s interface.

The only exception to this criterion is if the motion or auto-updating is essential. The term "essential" carries a very high bar; it means that the activity cannot be accomplished without the motion, or the motion is part of an activity where it is explicitly expected (e.g., a real-time auction clock, a game animation that is integral to gameplay, or a scientific simulation). Most decorative, promotional, or informational animations are generally not considered essential.

Practical Guidelines for Compliance

To ensure your web content complies with WCAG 2.2.2, consider the following practical implementation guidelines:

  1. Provide Clear and Visible Controls: Ensure that pause, stop, or hide controls are easily discoverable, visually prominent, and intuitively labeled (e.g., using a play/pause icon, a "Stop Animation" button, or a "Hide Updates" link). Controls should be located in close proximity to the content they manage.
  2. Keyboard Accessibility: All controls must be fully operable via keyboard. Users should be able to navigate to these controls using the Tab key and activate them with the Enter or Space key.
  3. Screen Reader Accessibility: Ensure controls are programmatically identifiable and have accessible names for screen reader users (e.g., using an appropriate <button> element with descriptive text, or adding aria-label to iconic buttons).
  4. Persist User State: If a user pauses or stops content, that state should ideally be remembered if they navigate away and return to the page, or at least persist until they explicitly choose to unpause or restart.
  5. Default to Paused or Play Once: As a best practice, consider having carousels, animations, or auto-updating feeds paused by default, or designed to play through only once before stopping, requiring explicit user interaction to start or loop again.
  6. Respect User Preferences: Utilize the CSS media query @media (prefers-reduced-motion: reduce) to detect if a user has indicated a preference for less motion in their operating system settings. Automatically disable or significantly reduce animations for these users.
  7. Avoid Flashing Content: If content must blink, ensure it meets the requirements of WCAG 2.3.1 to avoid triggering seizures, in addition to providing a mechanism to stop or hide it as per 2.2.2.

Examples of Correct and Incorrect Implementations

Correct Implementation: Accessible Carousel with Pause/Play Button

This example demonstrates an image carousel with explicit pause/play controls, ensuring it’s keyboard accessible and provides appropriate feedback for screen reader users.

<div class="carousel" role="region" aria-label="Image Carousel">
  <div class="carousel-slides" aria-live="off">
    <img src="https://via.placeholder.com/600x300/FF5733/FFFFFF?text=Slide+1" alt="Beautiful mountain landscape at sunset" class="active">
    <img src="https://via.placeholder.com/600x300/33FF57/000000?text=Slide+2" alt="City skyline at night with vibrant lights">
    <img src="https://via.placeholder.com/600x300/3357FF/FFFFFF?text=Slide+3" alt="Tranquil forest with a winding river">
  </div>
  <div class="carousel-controls">
    <button id="playPauseBtn" aria-label="Toggle Play or Pause Carousel">❚❚</button>
    <button id="prevBtn" aria-label="Previous Slide">◀</button>
    <button id="nextBtn" aria-label="Next Slide">▶</button>
  </div>
</div>

<style>
  .carousel {
    position: relative;
    width: 100%;
    max-width: 600px;
    margin: 20px auto;
    overflow: hidden;
    border: 1px solid #ccc;
    box-shadow: 0 2px 5px rgba(0,0,0,0.1);
  }
  .carousel-slides {
    display: flex;
    transition: transform 0.5s ease-in-out;
  }
  .carousel-slides img {
    width: 100%;
    flex-shrink: 0;
    display: none;
    height: 300px; /* Consistent height */
    object-fit: cover;
  }
  .carousel-slides img.active {
    display: block;
  }
  .carousel-controls {
    text-align: center;
    padding: 10px;
    background-color: #f9f9f9;
  }
  .carousel-controls button {
    padding: 8px 15px;
    margin: 0 5px;
    cursor: pointer;
    border: 1px solid #bbb;
    background-color: #e0e0e0;
    font-size: 1.2em;
    color: #333;
    border-radius: 4px;
  }
  .carousel-controls button:hover, .carousel-controls button:focus {
    background-color: #d0d0d0;
    outline: 2px solid #007bff;
    outline-offset: 2px;
  }
</style>

<script>
  const slides = document.querySelectorAll('.carousel-slides img');
  const playPauseBtn = document.getElementById('playPauseBtn');
  const prevBtn = document.getElementById('prevBtn');
  const nextBtn = document.getElementById('nextBtn');
  let currentSlide = 0;
  let intervalId;
  let isPlaying = true;

  function showSlide(index) {
    slides.forEach((slide, i) => {
      slide.classList.remove('active');
      if (i === index) {
        slide.classList.add('active');
      }
    });
  }

  function nextSlide() {
    currentSlide = (currentSlide + 1) % slides.length;
    showSlide(currentSlide);
  }

  function startCarousel() {
    if (!isPlaying) {
      intervalId = setInterval(nextSlide, 3000);
      isPlaying = true;
      playPauseBtn.innerHTML = '❚❚';
      playPauseBtn.setAttribute('aria-label', 'Pause Carousel');
    }
  }

  function pauseCarousel() {
    if (isPlaying) {
      clearInterval(intervalId);
      isPlaying = false;
      playPauseBtn.innerHTML = '►';
      playPauseBtn.setAttribute('aria-label', 'Play Carousel');
    }
  }

  playPauseBtn.addEventListener('click', () => {
    if (isPlaying) {
      pauseCarousel();
    } else {
      startCarousel();
    }
  });

  prevBtn.addEventListener('click', () => {
    pauseCarousel(); 
    currentSlide = (currentSlide - 1 + slides.length) % slides.length;
    showSlide(currentSlide);
  });

  nextBtn.addEventListener('click', () => {
    pauseCarousel(); 
    nextSlide();
  });

  showSlide(currentSlide);

  // Check for prefers-reduced-motion before starting autoplay
  if (window.matchMedia('(prefers-reduced-motion: reduce)').matches) {
    pauseCarousel(); // If user prefers reduced motion, start paused
  } else {
    startCarousel(); // Otherwise, start autoplay
  }
</script>

Incorrect Implementation: Autoplaying Carousel Without Controls

This example demonstrates a common pitfall: a carousel that continuously plays without any mechanism for the user to pause, stop, or hide it.

<div class="carousel-incorrect">
  <img src="https://via.placeholder.com/600x300/FF5733/FFFFFF?text=Promo+1" alt="Promotional image 1" class="active">
  <img src="https://via.placeholder.com/600x300/33FF57/000000?text=Promo+2" alt="Promotional image 2">
  <img src="https://via.placeholder.com/600x300/3357FF/FFFFFF?text=Promo+3" alt="Promotional image 3">
</div>

<style>
  .carousel-incorrect {
    position: relative;
    width: 100%;
    max-width: 600px;
    margin: 20px auto;
    overflow: hidden;
    height: 300px; /* Fixed height */
    border: 1px solid #ccc;
    box-shadow: 0 2px 5px rgba(0,0,0,0.1);
  }
  .carousel-incorrect img {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    opacity: 0;
    transition: opacity 1s ease-in-out;
    object-fit: cover;
  }
  .carousel-incorrect img.active {
    opacity: 1;
  }
</style>

<script>
  const imagesIncorrect = document.querySelectorAll('.carousel-incorrect img');
  let currentImageIncorrect = 0;

  function showNextImageIncorrect() {
    imagesIncorrect.forEach((img, i) => {
      img.classList.remove('active');
      if (i === currentImageIncorrect) {
        img.classList.add('active');
      }
    });
    currentImageIncorrect = (currentImageIncorrect + 1) % imagesIncorrect.length;
  }

  // Auto-play without any user control to pause/stop
  setInterval(showNextImageIncorrect, 3000);

  // Initial call
  showNextImageIncorrect();
</script>

Correct Implementation: Auto-Updating News Feed with Pause Button

This demonstrates a live news feed where new items appear, but the user can pause the updates, providing necessary control.

<div class="news-feed">
  <h3>Live News Updates</h3>
  <button id="toggleFeedBtn" aria-live="off" aria-label="Pause or Play News Feed Updates">❚❚ Pause Updates</button>
  <ul id="feedList" aria-live="polite">
    <li>Latest: Economic forecast shows steady growth.</li>
  </ul>
</div>

<style>
  .news-feed {
    border: 1px solid #ddd;
    padding: 15px;
    margin: 20px auto;
    max-width: 600px;
    background-color: #fdfdfd;
  }
  #feedList {
    list-style: none;
    padding: 0;
    margin-top: 10px;
    max-height: 200px;
    overflow-y: auto;
    border-top: 1px solid #eee;
    padding-top: 10px;
    background-color: #fff;
  }
  #feedList li {
    padding: 8px 0;
    border-bottom: 1px dotted #eee;
    font-size: 0.95em;
  }
  #feedList li:last-child {
    border-bottom: none;
  }
  #toggleFeedBtn {
    padding: 8px 12px;
    margin-bottom: 10px;
    cursor: pointer;
    background-color: #007bff;
    color: white;
    border: none;
    border-radius: 4px;
  }
  #toggleFeedBtn:hover, #toggleFeedBtn:focus {
    background-color: #0056b3;
    outline: 2px solid #007bff;
    outline-offset: 2px;
  }
</style>

<script>
  const feedList = document.getElementById('feedList');
  const toggleFeedBtn = document.getElementById('toggleFeedBtn');
  let isFeedPaused = false;
  let updateInterval;

  const newsItems = [
    "Breaking: New policy announced today by the government.",    "Market Update: Stocks gain for third consecutive day, investors optimistic.",    "Local News: Community event draws large crowd and positive feedback.",    "Tech Insights: AI development reaches new milestone in natural language processing.",    "Weather Alert: Heavy rainfall expected in northern regions tonight."
  ];
  let newsIndex = 0;

  function addNewItem() {
    if (isFeedPaused) return;

    const newItem = document.createElement('li');
    newItem.textContent = newsItems[newsIndex % newsItems.length];
    feedList.prepend(newItem); 
    newsIndex++;
    // Keep the list to a reasonable number of items
    if (feedList.children.length > 5) {
      feedList.removeChild(feedList.lastChild);
    }
  }

  function startUpdates() {
    updateInterval = setInterval(addNewItem, 5000);
    toggleFeedBtn.innerHTML = '❚❚ Pause Updates';
    toggleFeedBtn.setAttribute('aria-label', 'Pause News Feed Updates');
    isFeedPaused = false;
  }

  function pauseUpdates() {
    clearInterval(updateInterval);
    toggleFeedBtn.innerHTML = '► Play Updates';
    toggleFeedBtn.setAttribute('aria-label', 'Play News Feed Updates');
    isFeedPaused = true;
  }

  toggleFeedBtn.addEventListener('click', () => {
    if (isFeedPaused) {
      startUpdates();
    } else {
      pauseUpdates();
    }
  });

  // Initial start
  startUpdates();
</script>

Incorrect Implementation: Auto-Updating News Feed Without Pause

This illustrates a news feed that updates automatically without any user control, making it inaccessible and potentially distracting for many users.

<div class="news-feed-incorrect">
  <h3>Live News Updates (No Control)</h3>
  <ul id="feedListIncorrect" aria-live="polite">
    <li>Initial news item about local events.</li>
  </ul>
</div>

<style>
  .news-feed-incorrect {
    border: 1px solid #ddd;
    padding: 15px;
    margin: 20px auto;
    max-width: 600px;
    background-color: #fdfdfd;
  }
  #feedListIncorrect {
    list-style: none;
    padding: 0;
    margin-top: 10px;
    max-height: 200px;
    overflow-y: auto;
    border-top: 1px solid #eee;
    padding-top: 10px;
    background-color: #fff;
  }
  #feedListIncorrect li {
    padding: 8px 0;
    border-bottom: 1px dotted #eee;
    font-size: 0.95em;
  }
  #feedListIncorrect li:last-child {
    border-bottom: none;
  }
</style>

<script>
  const feedListIncorrect = document.getElementById('feedListIncorrect');
  const newsItemsIncorrect = [
    "Breaking: System update required for critical security patch.",    "Sports: Local team wins championship game in dramatic fashion.",    "Culture: New art exhibit opens next week at the downtown gallery."
  ];
  let newsIndexIncorrect = 0;

  function addNextItemIncorrect() {
    const newItem = document.createElement('li');
    newItem.textContent = newsItemsIncorrect[newsIndexIncorrect % newsItemsIncorrect.length];
    feedListIncorrect.prepend(newItem);
    newsIndexIncorrect++;
    if (feedListIncorrect.children.length > 5) {
      feedListIncorrect.removeChild(feedListIncorrect.lastChild);
    }
  }

  // Auto-updates without any user control to pause/stop
  setInterval(addNextItemIncorrect, 4000);

  // Initial call
  addNextItemIncorrect();
</script>

Best Practices and Common Pitfalls

Best Practices

  • Default State for Motion: Whenever possible, default dynamic elements (like carousels or animations) to a paused state, or configure them to play once and then stop, requiring explicit user interaction to start or loop again.
  • Prominent and Consistent Controls: Ensure pause/play/stop/hide buttons are clearly visible, logically placed near the content they control, and maintain consistent styling and iconography across the site.
  • Robust Keyboard and Screen Reader Support: Verify that all controls are fully accessible via keyboard navigation (Tab, Enter, Space) and have accurate aria-label or descriptive text for screen reader users.
  • Leverage prefers-reduced-motion: Actively use the @media (prefers-reduced-motion: reduce) CSS media query to automatically disable or significantly reduce animations and transitions for users who have enabled this preference in their operating system settings.
  • Provide Alternative Content: For highly complex or visually busy animations, consider offering a static image or a simplified version as an alternative for users who choose to hide or stop the original content.
  • Contextual Labels: Use descriptive labels for controls, such as "Pause Carousel," "Stop Animation," or "Hide Ticker," to clearly communicate their function.

Common Pitfalls

  • Missing Controls Entirely: The most significant and common failure is the complete absence of any pause, stop, or hide mechanism for content that falls under this criterion.
  • Obscure or Inaccessible Controls: Controls that are too small, visually blend into the background, or only appear on mouse hover (making them inaccessible for keyboard-only users, touch users, or those with motor impairments) are non-compliant.
  • Misinterpretation of "Essential": Developers often incorrectly deem decorative or promotional content as "essential," thereby omitting controls. Most dynamic content is *not* essential and must offer user control.
  • Failure to Persist State: If a user pauses content, but it automatically resumes playing upon navigation to another page or a page refresh, it violates the spirit of providing user control.
  • Over-reliance on aria-live for Auto-Updating Content: While aria-live can be useful for announcing new content, it must be carefully managed. If new content is pushed too frequently without a pause mechanism, it can create an overwhelming stream of announcements for screen reader users, making the content unusable.
  • Using Only CSS for Control: Relying solely on CSS properties like animation-play-state without providing a visible, interactive control for the user to manipulate it.

By diligently applying the guidelines and avoiding common pitfalls associated with WCAG 2.2.2, developers and content creators can significantly enhance the usability and accessibility of their websites for a diverse range of users, ensuring that dynamic content enriches rather than hinders the user experience.

Privacy Overview

This website uses cookies so that we can provide you with the best user experience possible. Cookie information is stored in your browser and performs functions such as recognising you when you return to our website and helping our team to understand which sections of the website you find most interesting and useful.