WCAG 2.2.2: Wstrzymaj, Zatrzymaj, Ukryj
Wprowadzenie do kryterium 2.2.2: Wstrzymaj, Zatrzymaj, Ukryj
Kryterium sukcesu 2.2.2 WCAG (Web Content Accessibility Guidelines) na poziomie A, znane jako „Wstrzymaj, Zatrzymaj, Ukryj”, dotyczy zapewnienia użytkownikom kontroli nad dynamiczną treścią na stronach internetowych. Jego celem jest umożliwienie zatrzymania, ukrycia lub wstrzymania wszelkich treści, które poruszają się, migają, przewijają lub automatycznie się aktualizują, chyba że ich ruch lub aktualizacja jest niezbędna dla funkcjonowania lub zrozumienia treści.
Dynamiczne elementy, takie jak karuzele, automatycznie aktualizujące się kanały informacyjne czy animowane reklamy, mogą być źródłem rozproszenia, frustracji, a nawet fizycznego dyskomfortu dla wielu użytkowników. To kryterium ma na celu zminimalizowanie tych barier, dając użytkownikowi możliwość decydowania o interakcji z taką treścią.
Dlaczego to kryterium jest ważne? (Wpływ na dostępność)
Zapewnienie kontroli nad ruchomą lub automatycznie aktualizującą się treścią jest kluczowe dla szerokiej gamy użytkowników:
- Użytkownicy z zaburzeniami poznawczymi: Ruchoma treść może być niezwykle rozpraszająca, utrudniając koncentrację na głównym zadaniu lub czytanie innej części strony. Mogą potrzebować więcej czasu na przetworzenie informacji, a treść poruszająca się lub znikająca zbyt szybko uniemożliwia jej konsumpcję.
- Użytkownicy z zaburzeniami uwagi (np. ADHD): Ci użytkownicy są szczególnie wrażliwi na bodźce zewnętrzne. Ruchome elementy mogą całkowicie odwrócić ich uwagę od celu wizyty na stronie, co czyni ją bezużyteczną.
- Użytkownicy z zaburzeniami motorycznymi: Szybkie interakcje z poruszającymi się elementami (np. kliknięcie małego przycisku na karuzeli, zanim slajd się zmieni) mogą być niemożliwe dla osób korzystających z myszy głowowej, trackballa, klawiatury alternatywnej lub innych technologii wspomagających.
- Użytkownicy z niską wizją lub używający powiększenia: Treść poruszająca się może szybko zniknąć poza obszarem widzenia, co wymaga ciągłego przewijania i manewrowania, aby ją śledzić. Jest to męczące i często uniemożliwia odczytanie całej treści.
- Użytkownicy z zaburzeniami równowagi (np. zawroty głowy): Niektóre ruchome animacje mogą wywoływać nudności, zawroty głowy lub ogólny dyskomfort.
Wymagania i interpretacja kryterium
Kryterium 2.2.2 wymaga, aby treści spełniające określone warunki były kontrolowane przez użytkownika. Istnieją cztery główne kategorie treści dynamicznych, których to dotyczy, oraz mechanizmy kontroli, które należy zapewnić.
Kluczowe aspekty i definicje
- Treść poruszająca się (Moving content): Treść, która przesuwa się lub zmienia pozycję na ekranie. Przykłady to karuzele, slidery, automatycznie przewijające się panele wiadomości, przewijane banery.
- Treść migająca (Blinking content): Treść, która jest okresowo widoczna i niewidoczna, szybko zmieniając swój stan. Przykłady to animowane GIF-y o dużej częstotliwości, tekst z efektem migania. Należy odróżnić od migania powodującego ataki padaczkowe, które jest objęte kryterium 2.3.1. Tutaj chodzi bardziej o rozproszenie uwagi.
- Treść przewijająca (Scrolling content): Treść, która automatycznie przesuwa się w jednym kierunku, ujawniając nowe informacje i ukrywając stare. Przykładem są tickersy wiadomości, automatycznie przewijane teksty, np. napisy końcowe.
- Treść automatycznie aktualizująca się (Auto-updating content): Treść, która jest dynamicznie odświeżana bez interwencji użytkownika. Przykłady to aktualizacje wyników sportowych na żywo, kursy giełdowe, chaty na żywo, liczniki czasu.
- Niezbędne (Essential): Ruch, miganie, przewijanie lub automatyczna aktualizacja są uznawane za niezbędne, jeśli są fundamentalnym elementem aktywności, a ich usunięcie fundamentalnie zmieniłoby informację lub funkcjonalność. Na przykład, animacja pokazująca, jak działa złożone urządzenie, lub gra, w której ruch jest kluczowy.
Wymagane mechanizmy kontroli
Dla każdej ruchomej, migającej, przewijającej się lub automatycznie aktualizującej się treści (jeśli nie jest niezbędna), należy zapewnić jeden lub więcej z następujących mechanizmów:
- Wstrzymaj (Pause): Użytkownik może tymczasowo zatrzymać ruch lub aktualizację, a następnie wznowić ją w dowolnym momencie. Przykładowo, przycisk „Pauza/Wznów” na karuzeli.
- Zatrzymaj (Stop): Użytkownik może trwale zatrzymać ruch lub aktualizację, bez możliwości wznowienia. Treść pozostaje widoczna w swoim ostatnim stanie. Przykładowo, przycisk „Zatrzymaj” na animacji.
- Ukryj (Hide): Użytkownik może całkowicie usunąć ruchomą lub aktualizującą się treść z widoku. Przykładowo, przycisk „Ukryj” dla banera reklamowego lub kanału aktualności.
Ważne jest, aby te mechanizmy były łatwo dostępne, wyraźnie oznakowane i możliwe do aktywowania za pomocą klawiatury oraz technologii wspomagających.
Praktyczne wytyczne dla zapewnienia zgodności
Aby spełnić kryterium 2.2.2, należy zastosować następujące praktyki:
- Dla karuzel i sliderów: Zawsze umieszczaj widoczne i dostępne przyciski do wstrzymywania/wznawiania automatycznego odtwarzania, a także do ręcznej nawigacji (poprzedni/następny slajd). Upewnij się, że kontrolki są dostępne dla klawiatury i czytników ekranu (np. z odpowiednimi etykietami
aria-label
). Idealnie, karuzele powinny domyślnie być wstrzymane lub oferować opcję wyłączenia automatycznego odtwarzania w ustawieniach. - Dla automatycznie aktualizujących się treści (np. kanały wiadomości, wyniki na żywo): Zapewnij wyraźny przycisk „Wstrzymaj” lub „Zatrzymaj” w pobliżu aktualizowanej treści. Po wstrzymaniu, treść powinna pozostać widoczna i statyczna.
- Dla animacji i migających elementów: Jeśli animacja nie jest niezbędna, upewnij się, że użytkownik może ją zatrzymać lub ukryć. Rozważ użycie standardów takich jak
@media (prefers-reduced-motion)
w CSS, aby automatycznie dostosować animacje dla użytkowników, którzy preferują mniej ruchu. - Dostępność kontrolek: Wszystkie kontrolki do zarządzania dynamiczną treścią muszą być dostępne dla klawiatury i innych technologii wspomagających. Muszą posiadać czytelne etykiety tekstowe i być poprawnie oznaczone semantycznie.
Przykłady implementacji
Przykłady prawidłowej implementacji
Karusela z kontrolkami Pauza/Wznów oraz nawigacją
W tym przykładzie karuzela ma wyraźne przyciski do nawigacji oraz do wstrzymywania i wznawiania automatycznego odtwarzania. Przyciski są dostępne z klawiatury i posiadają etykiety dla czytników ekranu.
<div class="carousel" aria-live="polite" aria-atomic="true">
<div class="carousel-inner">
<div class="carousel-item active">Slajd 1: Ważna informacja.</div>
<div class="carousel-item">Slajd 2: Kolejna ważna treść.</div>
<div class="carousel-item">Slajd 3: Ostatnia wiadomość.</div>
</div>
<div class="carousel-controls">
<button type="button" aria-label="Poprzedni slajd" class="prev">◀</button>
<button type="button" aria-label="Wstrzymaj automatyczne odtwarzanie" class="play-pause">❚❚</button> <!-- Ikona pauzy -->
<button type="button" aria-label="Następny slajd" class="next">▶</button>
</div>
</div>
.carousel {
position: relative;
width: 100%;
overflow: hidden;
border: 1px solid #ccc;
padding: 15px;
box-sizing: border-box;
}
.carousel-inner {
display: flex;
transition: transform 0.5s ease-in-out;
}
.carousel-item {
min-width: 100%;
box-sizing: border-box;
text-align: center;
padding: 20px 0;
font-size: 1.2em;
}
.carousel-controls {
text-align: center;
margin-top: 10px;
}
.carousel-controls button {
padding: 8px 15px;
margin: 0 5px;
cursor: pointer;
font-size: 1em;
border: 1px solid #000;
background-color: #eee;
}
.carousel-controls button:hover,
.carousel-controls button:focus {
background-color: #ddd;
outline: 2px solid blue;
}
document.addEventListener('DOMContentLoaded', () => {
const carousel = document.querySelector('.carousel-inner');
const items = document.querySelectorAll('.carousel-item');
const prevButton = document.querySelector('.prev');
const nextButton = document.querySelector('.next');
const playPauseButton = document.querySelector('.play-pause');
let currentIndex = 0;
let isPlaying = true;
let interval;
const showSlide = (index) => {
carousel.style.transform = `translateX(-${index * 100}%)`;
items.forEach((item, i) => {
if (i === index) {
item.setAttribute('aria-hidden', 'false');
item.setAttribute('tabindex', '0');
} else {
item.setAttribute('aria-hidden', 'true');
item.setAttribute('tabindex', '-1');
}
});
};
const nextSlide = () => {
currentIndex = (currentIndex + 1) % items.length;
showSlide(currentIndex);
};
const prevSlide = () => {
currentIndex = (currentIndex - 1 + items.length) % items.length;
showSlide(currentIndex);
};
const startAutoPlay = () => {
if (!isPlaying) {
isPlaying = true;
playPauseButton.setAttribute('aria-label', 'Wstrzymaj automatyczne odtwarzanie');
playPauseButton.innerHTML = '❚❚'; // Pauza
interval = setInterval(nextSlide, 3000);
}
};
const pauseAutoPlay = () => {
if (isPlaying) {
isPlaying = false;
playPauseButton.setAttribute('aria-label', 'Wznów automatyczne odtwarzanie');
playPauseButton.innerHTML = '▶'; // Play
clearInterval(interval);
}
};
prevButton.addEventListener('click', () => {
pauseAutoPlay();
prevSlide();
});
nextButton.addEventListener('click', () => {
pauseAutoPlay();
nextSlide();
});
playPauseButton.addEventListener('click', () => {
if (isPlaying) {
pauseAutoPlay();
} else {
startAutoPlay();
}
});
showSlide(currentIndex);
startAutoPlay(); // Karuzela startuje automatycznie, ale ma kontrolki
});
Kanał aktualności z przyciskiem „Wstrzymaj”
Poniższy przykład przedstawia kanał aktualności, który automatycznie pobiera i wyświetla nowe wiadomości. Użytkownik ma możliwość wstrzymania automatycznych aktualizacji.
<div class="news-feed">
<h3>Najnowsze aktualności</h3>
<div class="feed-content" aria-live="polite" aria-atomic="false">
<p>Ostatnia wiadomość: Trwa konferencja branżowa.</p>
</div>
<button type="button" class="toggle-feed-update" aria-label="Wstrzymaj aktualizacje kanału wiadomości">Wstrzymaj aktualizacje</button>
</div>
.news-feed {
border: 1px solid #ddd;
padding: 15px;
margin-top: 20px;
}
.feed-content {
min-height: 80px;
padding: 10px;
background-color: #f9f9f9;
margin-bottom: 10px;
}
.feed-content p {
margin: 0 0 5px 0;
}
.toggle-feed-update {
padding: 8px 15px;
cursor: pointer;
border: 1px solid #000;
background-color: #eee;
}
.toggle-feed-update:hover,
.toggle-feed-update:focus {
background-color: #ddd;
outline: 2px solid blue;
}
document.addEventListener('DOMContentLoaded', () => {
const feedContent = document.querySelector('.feed-content');
const toggleButton = document.querySelector('.toggle-feed-update');
let isUpdating = true;
let updateInterval;
let messageCount = 1;
const fetchNews = () => {
// Symulacja pobierania nowych wiadomości
const newMessage = document.createElement('p');
newMessage.textContent = `Nowa wiadomość (${messageCount++}): Kolejny raport został opublikowany.`;
feedContent.prepend(newMessage);
if (feedContent.children.length > 5) { // Usuń stare wiadomości
feedContent.lastChild.remove();
}
};
const startUpdates = () => {
if (!isUpdating) {
isUpdating = true;
toggleButton.textContent = 'Wstrzymaj aktualizacje';
toggleButton.setAttribute('aria-label', 'Wstrzymaj aktualizacje kanału wiadomości');
updateInterval = setInterval(fetchNews, 5000);
}
};
const pauseUpdates = () => {
if (isUpdating) {
isUpdating = false;
toggleButton.textContent = 'Wznów aktualizacje';
toggleButton.setAttribute('aria-label', 'Wznów aktualizacje kanału wiadomości');
clearInterval(updateInterval);
}
};
toggleButton.addEventListener('click', () => {
if (isUpdating) {
pauseUpdates();
} else {
startUpdates();
}
});
fetchNews(); // Initial load
startUpdates(); // Start automatic updates
});
Przykłady nieprawidłowej implementacji
Karusela bez żadnych kontrolek
Ta karuzela automatycznie odtwarza slajdy bez możliwości ich zatrzymania, wstrzymania lub nawigacji. Użytkownicy z zaburzeniami poznawczymi, motorycznymi lub niską wizją nie będą w stanie skutecznie korzystać z tej treści.
<div class="carousel-broken">
<div class="carousel-item-broken">Slajd 1: Zniknę szybko!</div>
<div class="carousel-item-broken">Slajd 2: Nie zdążysz mnie przeczytać!</div>
</div>
.carousel-broken {
position: relative;
width: 100%;
overflow: hidden;
border: 1px solid red; /* Wizualne oznaczenie problemu */
padding: 15px;
box-sizing: border-box;
}
.carousel-item-broken {
animation: slide-broken 5s infinite linear; /* Automatyczny ruch bez kontroli */
text-align: center;
padding: 20px 0;
font-size: 1.2em;
background-color: #fee;
}
@keyframes slide-broken {
0% { transform: translateX(0%); }
33% { transform: translateX(0%); }
36% { transform: translateX(-100%); }
66% { transform: translateX(-100%); }
69% { transform: translateX(-200%); }
99% { transform: translateX(-200%); }
100% { transform: translateX(0%); }
}
Migający tekst bez możliwości wyłączenia
Ten przykład pokazuje tekst, który miga, co może być bardzo rozpraszające i uciążliwe dla wielu użytkowników, a w skrajnych przypadkach może wywoływać dyskomfort fizyczny. Brak kontroli nad tym efektem jest niezgodny z WCAG 2.2.2.
<p class="blinking-text">UWAGA! Migający tekst!</p>
.blinking-text {
animation: blink 1s infinite alternate; /* Migający tekst bez kontroli */
font-weight: bold;
color: red;
font-size: 1.5em;
}
@keyframes blink {
0% { opacity: 0; }
100% { opacity: 1; }
}
Najlepsze praktyki i typowe pułapki
Najlepsze praktyki
- Domyślny stan wstrzymania: Rozważ ustawienie automatycznych karuzel i aktualizacji na wstrzymane domyślnie lub zapamiętywanie preferencji użytkownika (np. za pomocą Local Storage).
- Wyraźne i duże kontrolki: Upewnij się, że przyciski kontrolne są łatwe do znalezienia, wystarczająco duże do kliknięcia/dotknięcia i mają kontrast wystarczający dla użytkowników z niską wizją.
- Dostępność klawiatury i czytników ekranu: Wszystkie kontrolki muszą być w pełni dostępne za pomocą klawiatury (możliwość nawigacji tabulatorem, aktywacji Enterem/Spacją) i posiadać odpowiednie atrybuty ARIA (
aria-label
,aria-live
) dla czytników ekranu. - Użycie
prefers-reduced-motion
: Wykorzystaj media feature@media (prefers-reduced-motion: reduce)
w CSS, aby automatycznie zmniejszyć lub wyłączyć animacje dla użytkowników, którzy to preferują w ustawieniach swojego systemu operacyjnego. - Informuj o zmianach: W przypadku treści automatycznie aktualizujących się, rozważ powiadamianie użytkowników o nowych informacjach (np. subtelna wizualna wskazówka lub użycie
aria-live
dla czytników ekranu). - Minimalizuj niepotrzebny ruch: Zawsze zastanów się, czy ruch jest naprawdę konieczny. Często statyczne obrazy lub tekst są bardziej efektywne i dostępne.
Typowe pułapki
- Brak kontrolek: Najczęstsze naruszenie, gdzie dynamiczna treść jest po prostu odtwarzana bez żadnej interakcji.
- Niewidoczne lub trudne do znalezienia kontrolki: Kontrolki są obecne, ale są zbyt małe, mają słaby kontrast, są ukryte lub pojawiają się tylko po najechaniu myszą, co utrudnia ich użycie.
- Kontrolki niedostępne z klawiatury: Użytkownicy klawiatury nie mogą dotrzeć do przycisków kontrolnych lub aktywować ich.
- Niewłaściwe użycie ARIA: Np.
aria-live
ustawione naoff
dla ważnej, aktualizującej się treści, którą chcemy ogłosić, lub brakaria-label
dla ikonograficznych przycisków. - Brak obsługi stanu po wstrzymaniu: Po wstrzymaniu, treść powinna pozostać w ostatnim widocznym stanie, a nie zniknąć lub zresetować się.
- Niewłaściwe rozumienie „niezbędne”: Projektanci często uznają ruch za „niezbędny” z powodów estetycznych lub marketingowych, ignorując jego negatywny wpływ na dostępność. Ruch jest niezbędny tylko wtedy, gdy jego usunięcie uniemożliwiłoby funkcjonalność lub zrozumienie treści.
Podsumowanie
Kryterium 2.2.2 „Wstrzymaj, Zatrzymaj, Ukryj” jest fundamentalne dla stworzenia dostępnych i przyjaznych dla użytkownika stron internetowych. Dając użytkownikom kontrolę nad ruchomymi, migającymi, przewijającymi się lub automatycznie aktualizującymi się treściami, eliminujemy znaczące bariery dla osób z różnymi rodzajami niepełnosprawności. Pamiętaj, aby zawsze stawiać kontrolę użytkownika na pierwszym miejscu i upewnić się, że mechanizmy te są łatwo dostępne i intuicyjne w użyciu. Przestrzeganie tego kryterium przyczynia się do bardziej inkluzywnego i komfortowego doświadczenia dla wszystkich użytkowników.