Add keyboard navigation to commentary and stories pages

- commentary.html: verse card navigation with up/down, enter to view
- stories_index.html: 2D grid navigation for story cards
- stories_kids_index.html: 2D grid navigation for kids story cards

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-11-29 18:57:47 -05:00
parent b61baf0d9e
commit 78efc7e2fc
3 changed files with 163 additions and 2 deletions
+39 -2
View File
@@ -496,19 +496,56 @@ document.addEventListener('DOMContentLoaded', function() {
target.scrollIntoView({ behavior: 'smooth', block: 'center' });
target.style.transition = 'background-color 1s';
target.style.backgroundColor = 'rgba(255, 235, 59, 0.5)';
// Get verse number to show in toast
const verseMatch = window.location.hash.match(/verse-(\d+)/);
if (verseMatch && verseMatch[1]) {
showToast('Navigated to Verse ' + verseMatch[1]);
}
setTimeout(() => {
target.style.backgroundColor = '';
}, 2000);
}
}, 100);
}
// Keyboard navigation for verse cards
const verseCards = Array.from(document.querySelectorAll('.verse-card[id^="verse-"]'));
if (verseCards.length === 0) return;
let selectedIndex = -1;
function selectCard(index) {
if (selectedIndex >= 0 && selectedIndex < verseCards.length) {
verseCards[selectedIndex].style.outline = '';
verseCards[selectedIndex].style.outlineOffset = '';
}
selectedIndex = Math.max(0, Math.min(index, verseCards.length - 1));
verseCards[selectedIndex].style.outline = '2px solid #4a7c59';
verseCards[selectedIndex].style.outlineOffset = '4px';
verseCards[selectedIndex].scrollIntoView({ behavior: 'smooth', block: 'center' });
}
document.addEventListener('keydown', function(e) {
if (e.target.tagName === 'INPUT' || e.target.tagName === 'TEXTAREA') return;
if (e.key === 'ArrowDown' || e.key === 'j') {
e.preventDefault();
selectCard(selectedIndex < 0 ? 0 : selectedIndex + 1);
} else if (e.key === 'ArrowUp' || e.key === 'k') {
e.preventDefault();
if (selectedIndex <= 0) selectCard(0);
else selectCard(selectedIndex - 1);
} else if (e.key === 'ArrowLeft' || e.key === 'h') {
e.preventDefault();
history.back();
} else if (e.key === 'Enter' && selectedIndex >= 0) {
e.preventDefault();
const verseLink = verseCards[selectedIndex].querySelector('.verse-action[href*="/book/"]');
if (verseLink) window.location.href = verseLink.href;
}
});
});
</script>
{% endblock %}
+62
View File
@@ -450,6 +450,68 @@ document.addEventListener('DOMContentLoaded', function() {
hideDropdown();
}
});
// Keyboard navigation for story cards (2D grid)
var visibleStories = function() {
return Array.from(document.querySelectorAll('.story-card:not(.hidden)'));
};
var selectedCardIndex = -1;
function getGridColumns() {
var grid = document.querySelector('.stories-grid');
if (!grid) return 1;
var style = getComputedStyle(grid);
var cols = style.gridTemplateColumns.split(' ').length;
return cols || 1;
}
function selectStoryCard(index) {
var cards = visibleStories();
if (cards.length === 0) return;
if (selectedCardIndex >= 0 && selectedCardIndex < cards.length) {
cards[selectedCardIndex].style.outline = '';
cards[selectedCardIndex].style.outlineOffset = '';
}
selectedCardIndex = Math.max(0, Math.min(index, cards.length - 1));
cards[selectedCardIndex].style.outline = '2px solid #4a7c59';
cards[selectedCardIndex].style.outlineOffset = '4px';
cards[selectedCardIndex].scrollIntoView({ behavior: 'smooth', block: 'center' });
}
document.addEventListener('keydown', function(e) {
if (e.target.tagName === 'INPUT' || e.target.tagName === 'TEXTAREA') return;
var cards = visibleStories();
if (cards.length === 0) return;
var cols = getGridColumns();
if (e.key === 'ArrowDown' || e.key === 'j') {
e.preventDefault();
if (selectedCardIndex < 0) selectStoryCard(0);
else selectStoryCard(selectedCardIndex + cols);
} else if (e.key === 'ArrowUp' || e.key === 'k') {
e.preventDefault();
if (selectedCardIndex < 0) selectStoryCard(0);
else if (selectedCardIndex - cols >= 0) selectStoryCard(selectedCardIndex - cols);
} else if (e.key === 'ArrowRight' || e.key === 'l') {
e.preventDefault();
if (selectedCardIndex < 0) selectStoryCard(0);
else selectStoryCard(selectedCardIndex + 1);
} else if (e.key === 'ArrowLeft' || e.key === 'h') {
e.preventDefault();
if (selectedCardIndex <= 0) history.back();
else selectStoryCard(selectedCardIndex - 1);
} else if (e.key === 'Enter' && selectedCardIndex >= 0) {
e.preventDefault();
var cards = visibleStories();
if (cards[selectedCardIndex]) {
window.location.href = cards[selectedCardIndex].href;
}
}
});
});
</script>
{% endblock %}
@@ -488,6 +488,68 @@ document.addEventListener('DOMContentLoaded', function() {
hideDropdown();
}
});
// Keyboard navigation for story cards (2D grid)
var visibleStories = function() {
return Array.from(document.querySelectorAll('.kids-story-card:not(.hidden)'));
};
var selectedCardIndex = -1;
function getGridColumns() {
var grid = document.querySelector('.stories-grid');
if (!grid) return 1;
var style = getComputedStyle(grid);
var cols = style.gridTemplateColumns.split(' ').length;
return cols || 1;
}
function selectStoryCard(index) {
var cards = visibleStories();
if (cards.length === 0) return;
if (selectedCardIndex >= 0 && selectedCardIndex < cards.length) {
cards[selectedCardIndex].style.outline = '';
cards[selectedCardIndex].style.outlineOffset = '';
}
selectedCardIndex = Math.max(0, Math.min(index, cards.length - 1));
cards[selectedCardIndex].style.outline = '2px solid #8b5cf6';
cards[selectedCardIndex].style.outlineOffset = '4px';
cards[selectedCardIndex].scrollIntoView({ behavior: 'smooth', block: 'center' });
}
document.addEventListener('keydown', function(e) {
if (e.target.tagName === 'INPUT' || e.target.tagName === 'TEXTAREA') return;
var cards = visibleStories();
if (cards.length === 0) return;
var cols = getGridColumns();
if (e.key === 'ArrowDown' || e.key === 'j') {
e.preventDefault();
if (selectedCardIndex < 0) selectStoryCard(0);
else selectStoryCard(selectedCardIndex + cols);
} else if (e.key === 'ArrowUp' || e.key === 'k') {
e.preventDefault();
if (selectedCardIndex < 0) selectStoryCard(0);
else if (selectedCardIndex - cols >= 0) selectStoryCard(selectedCardIndex - cols);
} else if (e.key === 'ArrowRight' || e.key === 'l') {
e.preventDefault();
if (selectedCardIndex < 0) selectStoryCard(0);
else selectStoryCard(selectedCardIndex + 1);
} else if (e.key === 'ArrowLeft' || e.key === 'h') {
e.preventDefault();
if (selectedCardIndex <= 0) history.back();
else selectStoryCard(selectedCardIndex - 1);
} else if (e.key === 'Enter' && selectedCardIndex >= 0) {
e.preventDefault();
var cards = visibleStories();
if (cards[selectedCardIndex]) {
window.location.href = cards[selectedCardIndex].href;
}
}
});
});
</script>
{% endblock %}