WCAG 2.2.1: Możliwość regulacji czasu
WCAG 2.1 Kryterium Sukcesu 2.2.1: Możliwość regulacji czasu (Timing Adjustable)
Kryterium Sukcesu 2.2.1 (Możliwość regulacji czasu) jest kluczowym elementem wytycznych WCAG 2.1, mającym na celu zapewnienie, że użytkownicy mają wystarczająco dużo czasu na interakcję z treścią. Stanowi ono, że jeśli na stronie internetowej lub w aplikacji ustalone są limity czasowe, użytkownicy muszą mieć możliwość ich wyłączenia, dostosowania lub przedłużenia. Celem tego kryterium jest zapobieganie frustracji, błędom i niemożności ukończenia zadań przez użytkowników, którzy z różnych powodów potrzebują więcej czasu na przetworzenie informacji lub interakcję z interfejsem.
Istnieją jednak ściśle określone wyjątki, w których te wymagania nie mają zastosowania, takie jak sytuacje, gdy limit czasu jest niezbędny dla funkcji, dotyczy zdarzeń w czasie rzeczywistym, lub gdy limit jest dłuższy niż 20 godzin.
Dlaczego to jest ważne? (Wpływ na dostępność)
Zapewnienie możliwości regulacji limitów czasowych ma fundamentalne znaczenie dla dostępności, wpływając na szerokie grono użytkowników:
- Osoby z niepełnosprawnościami poznawczymi: Użytkownicy z dysleksją, ADHD, zaburzeniami pamięci lub innymi trudnościami poznawczymi mogą potrzebować znacznie więcej czasu na przeczytanie, zrozumienie i przetworzenie informacji. Nagle pojawiające się limity czasu mogą prowadzić do stresu, błędów i niemożności wykonania zadań.
- Osoby z niepełnosprawnościami ruchowymi: Użytkownicy, którzy korzystają z technologii wspomagających (np. klawiatury ekranowej, przełączników) lub mają ograniczoną sprawność manualną, potrzebują więcej czasu na precyzyjne interakcje z elementami interfejsu, takimi jak przyciski, pola formularzy czy rozwijane menu.
- Osoby starsze: Wiele osób starszych przetwarza informacje wolniej i może potrzebować więcej czasu na wykonanie skomplikowanych zadań online.
- Osoby z dysleksją: Mogą potrzebować więcej czasu na czytanie tekstów, zwłaszcza tych o dużym zagęszczeniu informacji.
- Osoby korzystające z technologii wspomagających: Proces interakcji za pomocą czytników ekranu, powiększalników czy alternatywnych urządzeń wprowadzania może być wolniejszy niż przy użyciu standardowej myszy i klawiatury.
- Osoby nieposiadające języka ojczystego: Mogą potrzebować więcej czasu na zrozumienie treści w obcym języku.
Brak kontroli nad limitami czasowymi może prowadzić do utraty danych (np. wprowadzonego tekstu w formularzu), konieczności ponownego rozpoczynania zadań, a w konsekwencji do wykluczenia i frustracji. Zapewnienie elastyczności czasowej poprawia ogólne doświadczenie użytkownika dla wszystkich, niezależnie od ich indywidualnych potrzeb.
Szczegółowe wymagania i kryteria sukcesu
Aby spełnić WCAG 2.1 Kryterium Sukcesu 2.2.1, treść internetowa musi spełniać co najmniej jedno z poniższych wymagań, gdy ustalone są limity czasowe:
- Wyłączenie: Użytkownik musi mieć możliwość wyłączenia limitu czasu przed jego upłynięciem.
- Dostosowanie: Użytkownik musi mieć możliwość dostosowania limitu czasu przed jego upłynięciem do co najmniej dziesięciu razy większej wartości niż domyślna.
- Przedłużenie: Użytkownik musi zostać ostrzeżony o zbliżającym się upływie limitu czasu przed jego upłynięciem (przynajmniej 20 sekund wcześniej) i mieć możliwość przedłużenia go o co najmniej 20 sekund, za pomocą prostego działania (np. naciśnięcia klawisza lub kliknięcia myszką). Ta możliwość musi być dostępna co najmniej dziesięć razy.
Istnieją określone wyjątki, w których te wymagania nie mają zastosowania:
- Limit dłuższy niż 20 godzin: Jeśli limit czasu jest dłuższy niż 20 godzin, nie jest wymagana możliwość jego regulacji.
- Niezbędność: Limit czasu jest niezbędny dla funkcji, a jego usunięcie uniemożliwiłoby jej działanie (np. licytacja online, gra na czas, system rezerwacji, gdzie rezerwacja jest utrzymywana tylko przez określony czas).
- Zdarzenia w czasie rzeczywistym: Limit czasu jest częścią zdarzenia w czasie rzeczywistym, takiego jak strumień wideo na żywo, czat czy telekonferencja, gdzie niemożliwe jest dostosowanie czasu bez zmiany istoty lub synchroniczności zdarzenia.
Praktyczne wskazówki dotyczące zgodności
Poniżej przedstawiono praktyczne podejścia do zapewnienia zgodności z Kryterium Sukcesu 2.2.1:
- Zawsze oferuj możliwość wyłączenia, jeśli to możliwe: Tam, gdzie limit czasu nie jest absolutnie niezbędny dla funkcji, zapewnij opcję całkowitego wyłączenia limitu czasu.
- Ostrzegaj i oferuj przedłużenie: Jeśli limit czasu jest konieczny, zawsze wyświetlaj wyraźne ostrzeżenie przed jego upływem (co najmniej 20 sekund wcześniej). Ostrzeżenie powinno być łatwo zauważalne i powinno zawierać opcję przedłużenia sesji lub limitu za pomocą prostego działania.
- Pozwól na wielokrotne przedłużenia: Użytkownicy powinni mieć możliwość wielokrotnego przedłużania limitu czasu, a nie tylko jednokrotnego (co najmniej 10 razy).
- Zapewnij duży margines czasowy: Domyślne limity czasu powinny być wystarczająco długie, aby większość użytkowników mogła komfortowo wykonać zadanie. Możliwość dostosowania powinna oferować znaczący dodatkowy czas (np. do co najmniej dziesięciu razy dłuższego niż domyślny czas).
- Widoczność i dostępność kontrolek: Opcje regulacji czasu (przycisk przedłużenia, ustawienia) powinny być łatwo dostępne i widoczne, a także w pełni obsługiwalne za pomocą klawiatury i technologii wspomagających (np. czytników ekranu).
- Persystencja preferencji: Jeśli użytkownik zmieni ustawienia limitu czasu, warto zapamiętać te preferencje na przyszłość (np. w ciasteczkach lub w profilu użytkownika), aby nie musiał ich każdorazowo konfigurować.
- Zarządzanie fokusem: Gdy pojawia się ostrzeżenie o zbliżającym się limicie czasu, automatycznie przenieś fokus klawiatury na przycisk „Przedłuż” lub podobną kontrolkę, aby użytkownicy korzystający z klawiatury lub czytników ekranu mogli szybko zareagować.
Przykłady implementacji
Poprawna implementacja: Formularz z ostrzeżeniem o sesji
W tym przykładzie użytkownik, który wypełnia formularz, jest ostrzegany o zbliżającym się wygaśnięciu sesji i może ją przedłużyć wielokrotnie. Modal ostrzegawczy jest dostępny za pomocą klawiatury i jest poprawnie oznakowany dla czytników ekranu.
<!DOCTYPE html>
<html lang="pl">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Formularz z regulacją czasu sesji (WCAG 2.1 Zgodne)</title>
<style>
body { font-family: Arial, sans-serif; margin: 20px; }
.session-timeout-modal {
display: none;
position: fixed;
z-index: 1000;
left: 0; top: 0;
width: 100%; height: 100%;
overflow: auto;
background-color: rgba(0,0,0,0.4);
justify-content: center;
align-items: center;
}
.session-timeout-content {
background-color: #fefefe;
margin: auto;
padding: 20px;
border: 1px solid #888;
width: 80%;
max-width: 500px;
box-shadow: 0 4px 8px 0 rgba(0,0,0,0.2);
text-align: center;
position: relative;
}
.session-timeout-content button {
padding: 10px 20px;
margin: 10px;
cursor: pointer;
}
#sessionTimer {
font-weight: bold;
color: red;
}
</style>
</head>
<body>
<h1>Wypełnij formularz</h1>
<p>Ta sesja wygaśnie za <span id="sessionDisplay"></span> sekund.</p>
<form>
<label for="name">Imię:</label>
<input type="text" id="name" name="name"><br><br>
<label for="message">Wiadomość:</label><br>
<textarea id="message" name="message" rows="5" cols="30"></textarea><br><br>
<button type="submit">Wyślij</button>
</form>
<div id="sessionTimeoutModal" class="session-timeout-modal" role="dialog" aria-modal="true" aria-labelledby="modalTitle" aria-describedby="modalDescription">
<div class="session-timeout-content">
<h2 id="modalTitle">Twoja sesja wygaśnie wkrótce!</h2>
<p id="modalDescription">Pozostało <span id="sessionTimer"></span> sekund. Czy chcesz przedłużyć sesję?</p>
<button id="extendSessionBtn">Przedłuż sesję</button>
<button id="logoutBtn">Wyloguj</button>
</div>
</div>
<script>
const SESSION_DURATION = 120; // Domyślny czas sesji w sekundach
const WARNING_TIME = 20; // Czas ostrzeżenia przed wygaśnięciem sesji w sekundach (min. 20s)
const EXTENSION_TIME = 60; // Czas przedłużenia sesji w sekundach (min. 20s)
let timeLeft = SESSION_DURATION;
let sessionInterval;
let warningTimeout;
let extendCount = 0;
const MAX_EXTENSIONS = 10; // Maksymalna liczba przedłużeń (min. 10)
const sessionDisplay = document.getElementById('sessionDisplay');
const sessionTimeoutModal = document.getElementById('sessionTimeoutModal');
const sessionTimer = document.getElementById('sessionTimer');
const extendSessionBtn = document.getElementById('extendSessionBtn');
const logoutBtn = document.getElementById('logoutBtn');
function updateTimerDisplay() {
sessionDisplay.textContent = timeLeft;
sessionTimer.textContent = timeLeft;
}
function startSessionTimer() {
clearInterval(sessionInterval);
clearTimeout(warningTimeout);
// Zapewnij, że timeLeft jest aktualizowane po przedłużeniu
if (sessionTimeoutModal.style.display !== 'flex') {
// Tylko resetuj do SESSION_DURATION, jeśli nie jesteśmy w trybie przedłużania
// W przeciwnym razie timeLeft już zawiera przedłużony czas
}
updateTimerDisplay();
sessionInterval = setInterval(() => {
timeLeft--;
updateTimerDisplay();
if (timeLeft === WARNING_TIME && sessionTimeoutModal.style.display !== 'flex') {
showWarning();
}
if (timeLeft <= 0) {
endSession();
}
}, 1000);
}
function showWarning() {
if (extendCount < MAX_EXTENSIONS) {
sessionTimeoutModal.style.display = 'flex';
extendSessionBtn.focus(); // Przenieś fokus na przycisk przedłużenia dla dostępności
// Użycie aria-live="assertive" na komunikacie w modalu byłoby też dobrym pomysłem
} else {
// Opcjonalnie: poinformuj użytkownika, że nie można już przedłużyć sesji
console.log('Osiągnięto maksymalną liczbę przedłużeń sesji. Sesja wygaśnie automatycznie.');
}
}
function extendSession() {
if (extendCount < MAX_EXTENSIONS) {
timeLeft += EXTENSION_TIME; // Dodaj czas przedłużenia
sessionTimeoutModal.style.display = 'none';
startSessionTimer(); // Zrestartuj timer z nowym, przedłużonym czasem
extendCount++;
console.log(`Sesja przedłużona. Liczba przedłużeń: ${extendCount}`);
} else {
alert('Osiągnięto maksymalną liczbę przedłużeń sesji. Twoja sesja wkrótce wygaśnie.');
sessionTimeoutModal.style.display = 'none';
// Nie wywołuj endSession() od razu, pozwól, aby timer dobiegł końca.
}
}
function endSession() {
clearInterval(sessionInterval);
alert('Twoja sesja wygasła. Zostaniesz wylogowany.');
// Tutaj logika do wylogowania użytkownika i przekierowania
window.location.reload();
}
extendSessionBtn.addEventListener('click', extendSession);
logoutBtn.addEventListener('click', endSession);
startSessionTimer(); // Rozpocznij sesję po załadowaniu strony
// Obsługa klawiatury dla modala (np. ESC by wylogować/zamknąć)
sessionTimeoutModal.addEventListener('keydown', (e) => {
if (e.key === 'Escape' && sessionTimeoutModal.style.display === 'flex') {
// Dla tego przykładu, Escape działa jak wylogowanie w modalu timeout.
// W bardziej złożonych scenariuszach może być inna obsługa.
endSession();
}
});
</script>
</body>
</html>
Niepoprawna implementacja: Quiz z twardym limitem czasu
W tym przykładzie quiz ma stały, krótki limit czasu, bez możliwości przedłużenia ani wyłączenia. Użytkownik, który nie zdąży odpowiedzieć, automatycznie traci szansę, a jego postępy są resetowane, co jest niezgodne z WCAG 2.1.
<!DOCTYPE html>
<html lang="pl">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Quiz z twardym limitem czasu (Niedostępne)</title>
<style>
body { font-family: Arial, sans-serif; margin: 20px; }
#timerDisplay { font-weight: bold; color: red; margin-bottom: 20px; }
.question { margin-bottom: 15px; }
</style>
</head>
<body>
<h1>Quiz wiedzy ogólnej</h1>
<p>Masz 30 sekund na ukończenie quizu! Brak możliwości przedłużenia.</p>
<div id="timerDisplay">Pozostało: <span id="timeLeft">30</span> sekund</div>
<form id="quizForm">
<div class="question">
<p>1. Stolica Polski to:</p>
<input type="radio" id="q1a" name="q1" value="Warszawa">
<label for="q1a">Warszawa</label><br>
<input type="radio" id="q1b" name="q1" value="Kraków">
<label for="q1b">Kraków</label><br>
</div>
<div class="question">
<p>2. Ile kolorów ma tęcza?</p>
<input type="radio" id="q2a" name="q2" value="6">
<label for="q2a">6</label><br>
<input type="radio" id="q2b" name="q2" value="7">
<label for="q2b">7</label><br>
</div>
<button type="submit">Zakończ quiz</button>
</form>
<script>
const QUIZ_DURATION = 30; // 30 sekund na quiz, bez możliwości zmiany
let quizTimeLeft = QUIZ_DURATION;
let quizInterval;
const timeLeftSpan = document.getElementById('timeLeft');
const quizForm = document.getElementById('quizForm');
function startQuizTimer() {
quizInterval = setInterval(() => {
quizTimeLeft--;
timeLeftSpan.textContent = quizTimeLeft;
if (quizTimeLeft <= 0) {
clearInterval(quizInterval);
alert('Czas minął! Quiz zakończony bez zapisu. Rozpocznij ponownie.');
quizForm.reset(); // Resetuje formularz, usuwając postępy użytkownika
// W rzeczywistej aplikacji nastąpiłoby przekierowanie lub wyświetlenie wyników/błędu
// bez możliwości kontynuacji czy przedłużenia, co jest niezgodne z WCAG.
}
}, 1000);
}
quizForm.addEventListener('submit', (e) => {
e.preventDefault();
clearInterval(quizInterval);
alert('Quiz zakończony! Twoje odpowiedzi zostały przesłane.');
// Logika przetwarzania wyników
});
startQuizTimer();
</script>
</body>
</html>
Najlepsze praktyki i typowe pułapki
Najlepsze praktyki:
- Unikaj limitów czasowych, jeśli to możliwe: Najlepszym rozwiązaniem jest projektowanie interfejsów, które w ogóle nie narzucają limitów czasowych, chyba że są one absolutnie niezbędne.
- Jasne i widoczne ostrzeżenia: Zawsze upewnij się, że ostrzeżenia o zbliżającym się limicie są wyraźne, umieszczone w widocznym miejscu i odczytywalne przez technologie wspomagające. Komunikaty powinny być zrozumiałe i nie budzące paniki.
- Umożliwiaj wielokrotne przedłużenia: Daj użytkownikowi możliwość przedłużania sesji wielokrotnie, a nie tylko raz (minimum 10 razy).
- Domyślne długie czasy: Jeśli limit czasu jest konieczny, ustaw go na tyle długo, aby większość użytkowników miała wystarczający czas na wykonanie zadania.
- Zapis postępów: W przypadku długich formularzy lub procesów, jeśli sesja wygaśnie, postaraj się zapisać wprowadzone dane użytkownika, aby mógł wznowić pracę po ponownym zalogowaniu, zamiast tracić wszystkie wprowadzone dane.
- Persystencja preferencji: Jeśli użytkownik dostosuje czas trwania sesji, zapamiętaj tę preferencję (np. w ciasteczkach, local storage lub w profilu użytkownika), aby nie musiał jej ustawiać ponownie przy każdej wizycie.
- Testowanie z technologiami wspomagającymi: Testuj działanie ostrzeżeń o limitach czasu z czytnikami ekranu, aby upewnić się, że komunikaty są prawidłowo przekazywane, a fokus przenosi się w odpowiednie miejsce.
Typowe pułapki:
- Niewystarczający czas przedłużenia: Oferowanie zbyt krótkiego czasu przedłużenia (np. tylko 5 sekund), który nadal jest niewystarczający dla użytkowników z trudnościami. Minimalnie 20 sekund.
- Ukryte lub niewidoczne ostrzeżenia: Komunikaty o wygaśnięciu sesji, które są małe, słabo kontrastujące, pojawiają się w nieoczekiwanym miejscu lub są jedynie wizualne, niedostępne dla czytników ekranu.
- Brak opcji wyłączenia lub dostosowania: Hardkodowane limity czasowe bez żadnej kontroli użytkownika lub bez spełnienia minimalnych wymagań dotyczących dostosowania.
- Automatyczne odświeżanie/przekierowanie: Strony, które automatycznie odświeżają się lub przekierowują po upływie limitu czasu bez ostrzeżenia i opcji interwencji ze strony użytkownika.
- Błędne interpretowanie „niezbędności” lub „zdarzenia w czasie rzeczywistym”: Twórcy często błędnie klasyfikują standardowe formularze, quizy czy proste aplikacje jako „niezbędne” lub „real-time”, ignorując fakt, że limit czasu nie jest krytyczny dla ich funkcji, a jego brak poprawiłby dostępność.
- Brak zarządzania fokusem: Gdy pojawia się modal z ostrzeżeniem o sesji, fokus klawiatury nie jest automatycznie przenoszony na przycisk akcji (np. „Przedłuż sesję”), co utrudnia interakcję użytkownikom klawiatury i czytników ekranu.