Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .Jules/palette.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,3 +31,7 @@
## 2024-05-21 - Enhanced Accessibility for Dynamic Content and State Toggles
**Learning:** For dynamic applications with real-time filtering and mode switching, using `aria-live` and `aria-pressed` significantly improves the experience for assistive technology users by announcing state changes that are otherwise purely visual.
**Action:** Always pair visual state changes (like "active" classes) with corresponding ARIA attributes (`aria-pressed`, `aria-expanded`) and use `aria-live` regions for status updates like search result counts.

## 2025-05-22 - Predictable Keyboard Navigation for Overlays
**Learning:** For interactive search inputs and side panels, the 'Escape' key must handle focus and visibility states predictably. Ensuring that 'Escape' blurs and closes an active search section even when focus is inside the input improves navigation flow for power users and assistive technology.
**Action:** Implement 'Escape' key handlers that explicitly manage both DOM state (closing sections) and ARIA states (`aria-expanded="false"`) across all navigation toggles.
8 changes: 4 additions & 4 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -76,19 +76,19 @@ <h1 class="brand-title">Ruh Al Tarikh</h1>
</div>
</div>

<nav class="navbar-nav">
<nav class="navbar-nav" id="navbar-nav">
<button class="nav-link" data-action="scroll-to-episodes">
<i class="fas fa-film"></i>
<span>Episodes</span>
</button>
<button class="nav-link" id="searchToggleBtn">
<button class="nav-link" id="searchToggleBtn" aria-controls="searchSection" aria-expanded="false">
<i class="fas fa-search"></i>
<span>Search</span>
</button>
</nav>

<div class="navbar-actions">
<button id="watchLaterBtn" class="action-btn badge-btn" aria-label="View Saved Episodes" title="View Saved Episodes">
<button id="watchLaterBtn" class="action-btn badge-btn" aria-label="View Saved Episodes" title="View Saved Episodes" aria-controls="watchLaterPage" aria-expanded="false">
<i class="fas fa-bookmark" aria-hidden="true"></i>
<span class="badge" id="watchLaterBadge">0</span>
</button>
Expand All @@ -115,7 +115,7 @@ <h1 class="brand-title">Ruh Al Tarikh</h1>
<button class="mode-btn active px-3 py-1 text-[10px] rounded-md transition-all font-bold uppercase tracking-wider" data-mode="archive" aria-pressed="true">Archive</button>
<button class="mode-btn px-3 py-1 text-[10px] rounded-md transition-all font-bold uppercase tracking-wider" data-mode="creator" aria-pressed="false">Studio</button>
</div>
<button id="menuToggleBtn" class="menu-toggle" aria-label="Menu">
<button id="menuToggleBtn" class="menu-toggle" aria-label="Menu" aria-controls="navbar-nav" aria-expanded="false">
<span></span>
<span></span>
<span></span>
Expand Down
35 changes: 27 additions & 8 deletions js/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ const DOM = {
scrollToTop: document.getElementById('scrollToTop'),

// Watch Later
watchLaterBtn: document.getElementById('watchLaterBtn'),
watchLaterBadge: document.getElementById('watchLaterBadge'),
watchLaterCount: document.getElementById('watchLaterCount'),
watchLaterPage: document.getElementById('watchLaterPage'),
Expand Down Expand Up @@ -705,6 +706,7 @@ function openWatchLater() {
renderWatchLater();
DOM.watchLaterPage.style.display = 'block';
DOM.watchLaterPage.setAttribute('aria-hidden', 'false');
if (DOM.watchLaterBtn) DOM.watchLaterBtn.setAttribute('aria-expanded', 'true');
Utils.trapFocus(DOM.watchLaterPage);
DOM.body.style.overflow = 'hidden';
DOM.body.classList.add('modal-open');
Expand All @@ -714,6 +716,7 @@ function closeWatchLater() {
if (!DOM.watchLaterPage) return;
DOM.watchLaterPage.style.display = "none";
DOM.watchLaterPage.setAttribute("aria-hidden", "true");
if (DOM.watchLaterBtn) DOM.watchLaterBtn.setAttribute('aria-expanded', 'false');
DOM.body.style.overflow = "";
DOM.body.classList.remove("modal-open");
if (AppState.lastFocused) { AppState.lastFocused.focus(); AppState.lastFocused = null; }
Expand Down Expand Up @@ -1204,15 +1207,17 @@ function bindEvents() {
// Mobile Menu Toggle
if (DOM.menuToggle) {
DOM.menuToggle.addEventListener('click', () => {
document.body.classList.toggle('mobile-nav-active');
const isActive = document.body.classList.toggle('mobile-nav-active');
DOM.menuToggle.setAttribute('aria-expanded', isActive ? 'true' : 'false');
});
}

// Search Section Toggle
if (DOM.searchToggle && DOM.searchSection) {
DOM.searchToggle.addEventListener('click', () => {
DOM.searchSection.classList.toggle('active');
if (DOM.searchSection.classList.contains('active')) {
const isActive = DOM.searchSection.classList.toggle('active');
DOM.searchToggle.setAttribute('aria-expanded', isActive ? 'true' : 'false');
if (isActive) {
DOM.search.focus();
}
});
Expand Down Expand Up @@ -1273,7 +1278,7 @@ function bindEvents() {
}

// Watch Later
if (DOM.watchLaterBadge) DOM.watchLaterBadge.addEventListener('click', openWatchLater);
if (DOM.watchLaterBtn) DOM.watchLaterBtn.addEventListener('click', openWatchLater);
if (DOM.closeWatchLater) DOM.closeWatchLater.addEventListener('click', closeWatchLater);
if (DOM.watchLaterPage) {
DOM.watchLaterPage.addEventListener('click', (e) => {
Expand Down Expand Up @@ -1353,14 +1358,20 @@ function bindEvents() {

// Keyboard shortcuts
document.addEventListener('keydown', (e) => {
// Don't interfere with input fields
const key = e.key.toLowerCase();

// Handle Escape for input fields
if (e.target.tagName === 'INPUT' || e.target.tagName === 'TEXTAREA') {
if (e.key === 'Escape') e.target.blur();
if (key === 'escape') {
e.target.blur();
if (e.target === DOM.search && DOM.searchSection && DOM.searchSection.classList.contains('active')) {
DOM.searchSection.classList.remove('active');
if (DOM.searchToggle) DOM.searchToggle.setAttribute('aria-expanded', 'false');
}
}
return;
}

const key = e.key.toLowerCase();

// Search focus
if (key === '/' && DOM.search) {
e.preventDefault();
Expand All @@ -1372,6 +1383,14 @@ function bindEvents() {
closeVideo();
closeWatchLater();
closeDashboard();
if (DOM.searchToggle && DOM.searchSection && DOM.searchSection.classList.contains('active')) {
DOM.searchSection.classList.remove('active');
DOM.searchToggle.setAttribute('aria-expanded', 'false');
}
if (document.body.classList.contains('mobile-nav-active')) {
document.body.classList.remove('mobile-nav-active');
if (DOM.menuToggle) DOM.menuToggle.setAttribute('aria-expanded', 'false');
}
if (DOM.sharePanel) { DOM.sharePanel.style.display = "none"; DOM.sharePanel.setAttribute("aria-hidden", "true"); }
if (DOM.transcriptPanel) { DOM.transcriptPanel.style.display = "none"; DOM.transcriptPanel.setAttribute("aria-hidden", "true"); }
DOM.body.style.overflow = "";
Expand Down
Loading