Comprehensive keyboard navigation improvements

- Remove study guides from homepage keyboard nav (too inline/sloppy)
- Add paragraph-level nav to resource_index.html (soteriology, etc.)
- Stories index: add category navigation, Enter jumps to first story in category
- Story kids: add adult callout and PDF button to nav
- Detail pages (topic, resource, reading_plan, parable): include paragraphs,
  verse items, and PDF buttons in keyboard nav
- Chapter page: add 'i' for interlinear, 'p' for PDF shortcuts
- Book page: add 'p' for PDF shortcut
- Chapter interlinear: add 'p' for PDF shortcut

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

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-11-30 00:24:33 -05:00
parent a401de6b3f
commit 40b86ffaf7
11 changed files with 304 additions and 144 deletions
+7
View File
@@ -429,6 +429,13 @@ document.addEventListener('DOMContentLoaded', function() {
e.preventDefault();
window.location.href = '/book/' + encodeURIComponent(bibleBooks[currentBookIndex + 1]);
}
// p: Download PDF
if (e.key === 'p') {
e.preventDefault();
var pdfBtn = document.querySelector('.print-btn');
if (pdfBtn) window.location.href = pdfBtn.href;
}
});
// Clicking on chapter links should just navigate normally
+15 -1
View File
@@ -328,7 +328,7 @@ p[id^="verse-"].selected {
</a>
</div>
<div class="nav-help">
Tip: ↑/↓ or j/k to select verses • Enter to view • ←/→ for chapters
Tip: ↑/↓ to select verses • Enter to view • ←/→ chapters • i=interlinear • p=PDF
</div>
</nav>
@@ -515,6 +515,20 @@ document.addEventListener('DOMContentLoaded', function() {
}
selectedVerseIndex = -1;
}
// i: Go to interlinear view
if (e.key === 'i') {
e.preventDefault();
var interlinearBtn = document.querySelector('.interlinear-btn');
if (interlinearBtn) window.location.href = interlinearBtn.href;
}
// p: Download PDF
if (e.key === 'p') {
e.preventDefault();
var pdfBtn = document.querySelector('.pdf-btn');
if (pdfBtn) window.location.href = pdfBtn.href;
}
});
// Click to select verse
@@ -890,6 +890,10 @@ document.addEventListener('click', function(e) {
// Enter word mode at first word
selectWord(0);
}
} else if (e.key === 'p') {
e.preventDefault();
var pdfBtn = document.querySelector('.print-btn');
if (pdfBtn) window.location.href = pdfBtn.href;
}
});
})();
+3 -28
View File
@@ -151,24 +151,6 @@
outline-color: #6b9b7a;
}
/* Study guide links */
.study-guide-link {
padding: 0.15rem 0.4rem;
border-radius: 3px;
transition: all 0.2s;
}
.study-guide-link.selected {
background: rgba(74, 124, 89, 0.15);
outline: 2px solid #4a7c59;
outline-offset: 2px;
}
[data-theme="dark"] .study-guide-link.selected {
background: rgba(107, 155, 122, 0.15);
outline-color: #6b9b7a;
}
/* Daily verse link */
.daily-verse-link {
padding: 0.15rem 0.4rem;
@@ -477,7 +459,7 @@
{% for category, guides in study_guides.items() %}
<p><span class="newthought">{{ category }}</span>
{% for guide in guides %}<a href="/study-guides/{{ guide.slug }}" class="study-guide-link">{{ guide.title }}</a>{% if not loop.last %}, {% endif %}{% endfor %}</p>
{% for guide in guides %}<a href="/study-guides/{{ guide.slug }}">{{ guide.title }}</a>{% if not loop.last %}, {% endif %}{% endfor %}</p>
{% endfor %}
</section>
@@ -592,15 +574,14 @@ function handleSearch(event) {
return false;
}
// Combined keyboard navigation for daily verse + search + nav links + feature cards + explore links + study guides
// Combined keyboard navigation for daily verse + search + nav links + feature cards + explore links
var dailyVerseLink = Array.from(document.querySelectorAll('.daily-verse-link'));
var searchSection = document.getElementById('search-section');
var searchItems = searchSection ? [searchSection] : [];
var navLinks = Array.from(document.querySelectorAll('.nav-links a'));
var featureCards = Array.from(document.querySelectorAll('.feature-card'));
var exploreLinks = Array.from(document.querySelectorAll('.explore-link'));
var studyGuideLinks = Array.from(document.querySelectorAll('.study-guide-link'));
var allCards = dailyVerseLink.concat(searchItems).concat(navLinks).concat(featureCards).concat(exploreLinks).concat(studyGuideLinks);
var allCards = dailyVerseLink.concat(searchItems).concat(navLinks).concat(featureCards).concat(exploreLinks);
var selectedCardIndex = -1;
function getGridColumns() {
@@ -609,7 +590,6 @@ function getGridColumns() {
var searchEnd = dailyVerseEnd + searchItems.length;
var navLinksEnd = searchEnd + navLinks.length;
var featureCardsEnd = navLinksEnd + featureCards.length;
var exploreLinksEnd = featureCardsEnd + exploreLinks.length;
if (selectedCardIndex < dailyVerseEnd) {
// Daily verse is a single item
@@ -626,11 +606,6 @@ function getGridColumns() {
return navLinks.length;
}
if (selectedCardIndex >= exploreLinksEnd) {
// Study guide links - treat as single row navigation
return studyGuideLinks.length;
}
if (window.innerWidth <= 760) {
// Mobile: feature cards = 1 col, explore = 2 cols
return selectedCardIndex < featureCardsEnd ? 1 : 2;
+19 -17
View File
@@ -118,20 +118,21 @@
<script>
(function() {
const verses = Array.from(document.querySelectorAll('.verse-item'));
if (verses.length === 0) return;
// Include description paragraphs, intro text, and verse items
const elements = Array.from(document.querySelectorAll('.parable-description p, .intro-text, .verse-item'));
if (elements.length === 0) return;
let selectedIndex = -1;
function selectVerse(index) {
if (selectedIndex >= 0 && selectedIndex < verses.length) {
verses[selectedIndex].style.outline = '';
verses[selectedIndex].style.outlineOffset = '';
function selectElement(index) {
if (selectedIndex >= 0 && selectedIndex < elements.length) {
elements[selectedIndex].style.outline = '';
elements[selectedIndex].style.outlineOffset = '';
}
selectedIndex = Math.max(0, Math.min(index, verses.length - 1));
verses[selectedIndex].style.outline = '2px solid #4a7c59';
verses[selectedIndex].style.outlineOffset = '4px';
verses[selectedIndex].scrollIntoView({ behavior: 'smooth', block: 'center' });
selectedIndex = Math.max(0, Math.min(index, elements.length - 1));
elements[selectedIndex].style.outline = '2px solid #4a7c59';
elements[selectedIndex].style.outlineOffset = '8px';
elements[selectedIndex].scrollIntoView({ behavior: 'smooth', block: 'center' });
}
document.addEventListener('keydown', function(e) {
@@ -139,23 +140,24 @@
if (e.key === 'ArrowDown' || e.key === 'j') {
e.preventDefault();
selectVerse(selectedIndex < 0 ? 0 : selectedIndex + 1);
selectElement(selectedIndex < 0 ? 0 : selectedIndex + 1);
} else if (e.key === 'ArrowUp' || e.key === 'k') {
e.preventDefault();
if (selectedIndex <= 0) selectVerse(0);
else selectVerse(selectedIndex - 1);
if (selectedIndex <= 0) selectElement(0);
else selectElement(selectedIndex - 1);
} else if (e.key === 'ArrowLeft' || e.key === 'h') {
e.preventDefault();
history.back();
} else if (e.key === 'Enter' && selectedIndex >= 0) {
e.preventDefault();
const link = verses[selectedIndex].querySelector('.verse-ref a');
var el = elements[selectedIndex];
var link = el.querySelector('.verse-ref a') || el.querySelector('a');
if (link) window.location.href = link.href;
} else if (e.key === 'Escape') {
e.preventDefault();
if (selectedIndex >= 0 && selectedIndex < verses.length) {
verses[selectedIndex].style.outline = '';
verses[selectedIndex].style.outlineOffset = '';
if (selectedIndex >= 0 && selectedIndex < elements.length) {
elements[selectedIndex].style.outline = '';
elements[selectedIndex].style.outlineOffset = '';
}
selectedIndex = -1;
}
+28 -15
View File
@@ -222,20 +222,21 @@
<script>
(function() {
const days = Array.from(document.querySelectorAll('.day-entry'));
if (days.length === 0) return;
// Include overview, intro text, day entries, and PDF button
const elements = Array.from(document.querySelectorAll('.plan-overview, .intro-text, .day-entry, .print-btn'));
if (elements.length === 0) return;
let selectedIndex = -1;
function selectDay(index) {
if (selectedIndex >= 0 && selectedIndex < days.length) {
days[selectedIndex].style.outline = '';
days[selectedIndex].style.outlineOffset = '';
function selectElement(index) {
if (selectedIndex >= 0 && selectedIndex < elements.length) {
elements[selectedIndex].style.outline = '';
elements[selectedIndex].style.outlineOffset = '';
}
selectedIndex = Math.max(0, Math.min(index, days.length - 1));
days[selectedIndex].style.outline = '2px solid #4a7c59';
days[selectedIndex].style.outlineOffset = '2px';
days[selectedIndex].scrollIntoView({ behavior: 'smooth', block: 'center' });
selectedIndex = Math.max(0, Math.min(index, elements.length - 1));
elements[selectedIndex].style.outline = '2px solid #4a7c59';
elements[selectedIndex].style.outlineOffset = '8px';
elements[selectedIndex].scrollIntoView({ behavior: 'smooth', block: 'center' });
}
document.addEventListener('keydown', function(e) {
@@ -243,18 +244,30 @@
if (e.key === 'ArrowDown' || e.key === 'j') {
e.preventDefault();
selectDay(selectedIndex < 0 ? 0 : selectedIndex + 1);
selectElement(selectedIndex < 0 ? 0 : selectedIndex + 1);
} else if (e.key === 'ArrowUp' || e.key === 'k') {
e.preventDefault();
if (selectedIndex <= 0) selectDay(0);
else selectDay(selectedIndex - 1);
if (selectedIndex <= 0) selectElement(0);
else selectElement(selectedIndex - 1);
} else if (e.key === 'ArrowLeft' || e.key === 'h') {
e.preventDefault();
history.back();
} else if (e.key === 'Enter' && selectedIndex >= 0) {
e.preventDefault();
const firstLink = days[selectedIndex].querySelector('.reading-ref a');
if (firstLink) window.location.href = firstLink.href;
var el = elements[selectedIndex];
if (el.tagName === 'A' && el.href) {
window.location.href = el.href;
} else {
var link = el.querySelector('.reading-ref a') || el.querySelector('a');
if (link) window.location.href = link.href;
}
} else if (e.key === 'Escape') {
e.preventDefault();
if (selectedIndex >= 0 && selectedIndex < elements.length) {
elements[selectedIndex].style.outline = '';
elements[selectedIndex].style.outlineOffset = '';
}
selectedIndex = -1;
}
});
})();
+28 -15
View File
@@ -172,20 +172,21 @@
<script>
(function() {
const verseItems = Array.from(document.querySelectorAll('.verse-item'));
if (verseItems.length === 0) return;
// Include description paragraphs, verse items, and PDF button
const elements = Array.from(document.querySelectorAll('.resource-description p, .verse-item, .print-btn'));
if (elements.length === 0) return;
let selectedIndex = -1;
function selectItem(index) {
if (selectedIndex >= 0 && selectedIndex < verseItems.length) {
verseItems[selectedIndex].style.outline = '';
verseItems[selectedIndex].style.outlineOffset = '';
function selectElement(index) {
if (selectedIndex >= 0 && selectedIndex < elements.length) {
elements[selectedIndex].style.outline = '';
elements[selectedIndex].style.outlineOffset = '';
}
selectedIndex = Math.max(0, Math.min(index, verseItems.length - 1));
verseItems[selectedIndex].style.outline = '2px solid #4a7c59';
verseItems[selectedIndex].style.outlineOffset = '4px';
verseItems[selectedIndex].scrollIntoView({ behavior: 'smooth', block: 'center' });
selectedIndex = Math.max(0, Math.min(index, elements.length - 1));
elements[selectedIndex].style.outline = '2px solid #4a7c59';
elements[selectedIndex].style.outlineOffset = '8px';
elements[selectedIndex].scrollIntoView({ behavior: 'smooth', block: 'center' });
}
document.addEventListener('keydown', function(e) {
@@ -193,18 +194,30 @@
if (e.key === 'ArrowDown' || e.key === 'j') {
e.preventDefault();
selectItem(selectedIndex < 0 ? 0 : selectedIndex + 1);
selectElement(selectedIndex < 0 ? 0 : selectedIndex + 1);
} else if (e.key === 'ArrowUp' || e.key === 'k') {
e.preventDefault();
if (selectedIndex <= 0) selectItem(0);
else selectItem(selectedIndex - 1);
if (selectedIndex <= 0) selectElement(0);
else selectElement(selectedIndex - 1);
} else if (e.key === 'ArrowLeft' || e.key === 'h') {
e.preventDefault();
history.back();
} else if (e.key === 'Enter' && selectedIndex >= 0) {
e.preventDefault();
const link = verseItems[selectedIndex].querySelector('a');
if (link) window.location.href = link.href;
var el = elements[selectedIndex];
if (el.tagName === 'A' && el.href) {
window.location.href = el.href;
} else {
var link = el.querySelector('.verse-ref a') || el.querySelector('a');
if (link) window.location.href = link.href;
}
} else if (e.key === 'Escape') {
e.preventDefault();
if (selectedIndex >= 0 && selectedIndex < elements.length) {
elements[selectedIndex].style.outline = '';
elements[selectedIndex].style.outlineOffset = '';
}
selectedIndex = -1;
}
});
})();
+24 -15
View File
@@ -298,21 +298,21 @@ document.addEventListener('DOMContentLoaded', function() {
tocList.appendChild(li);
});
// Keyboard navigation for resource entries
const entries = Array.from(document.querySelectorAll('.resource-entry'));
if (entries.length === 0) return;
// Keyboard navigation for intro, description paragraphs, and verses
const elements = Array.from(document.querySelectorAll('.intro-text, .resource-item-description p, .verse-item'));
if (elements.length === 0) return;
let selectedIndex = -1;
function selectEntry(index) {
if (selectedIndex >= 0 && selectedIndex < entries.length) {
entries[selectedIndex].style.outline = '';
entries[selectedIndex].style.outlineOffset = '';
function selectElement(index) {
if (selectedIndex >= 0 && selectedIndex < elements.length) {
elements[selectedIndex].style.outline = '';
elements[selectedIndex].style.outlineOffset = '';
}
selectedIndex = Math.max(0, Math.min(index, entries.length - 1));
entries[selectedIndex].style.outline = '2px solid #4a7c59';
entries[selectedIndex].style.outlineOffset = '4px';
entries[selectedIndex].scrollIntoView({ behavior: 'smooth', block: 'center' });
selectedIndex = Math.max(0, Math.min(index, elements.length - 1));
elements[selectedIndex].style.outline = '2px solid #4a7c59';
elements[selectedIndex].style.outlineOffset = '8px';
elements[selectedIndex].scrollIntoView({ behavior: 'smooth', block: 'center' });
}
document.addEventListener('keydown', function(e) {
@@ -320,18 +320,27 @@ document.addEventListener('DOMContentLoaded', function() {
if (e.key === 'ArrowDown' || e.key === 'j') {
e.preventDefault();
selectEntry(selectedIndex < 0 ? 0 : selectedIndex + 1);
selectElement(selectedIndex < 0 ? 0 : selectedIndex + 1);
} else if (e.key === 'ArrowUp' || e.key === 'k') {
e.preventDefault();
if (selectedIndex <= 0) selectEntry(0);
else selectEntry(selectedIndex - 1);
if (selectedIndex <= 0) selectElement(0);
else selectElement(selectedIndex - 1);
} else if (e.key === 'ArrowLeft' || e.key === 'h') {
e.preventDefault();
history.back();
} else if (e.key === 'Enter' && selectedIndex >= 0) {
e.preventDefault();
const link = entries[selectedIndex].querySelector('.resource-name a');
var el = elements[selectedIndex];
// Check for link in resource entry title or verse reference
var link = el.querySelector('.resource-name a') || el.querySelector('.verse-ref a') || el.querySelector('a');
if (link) window.location.href = link.href;
} else if (e.key === 'Escape') {
e.preventDefault();
if (selectedIndex >= 0 && selectedIndex < elements.length) {
elements[selectedIndex].style.outline = '';
elements[selectedIndex].style.outlineOffset = '';
}
selectedIndex = -1;
}
});
});
+118 -21
View File
@@ -34,6 +34,17 @@
border-color: var(--link-color);
transform: translateY(-1px);
}
.story-toc-item.selected {
border-color: #4a7c59;
outline: 2px solid #4a7c59;
outline-offset: 2px;
background: rgba(74, 124, 89, 0.05);
}
[data-theme="dark"] .story-toc-item.selected {
border-color: #6b9b7a;
outline-color: #6b9b7a;
background: rgba(107, 155, 122, 0.1);
}
.story-toc-item strong {
display: block;
}
@@ -451,13 +462,26 @@ document.addEventListener('DOMContentLoaded', function() {
}
});
// Keyboard navigation for story cards (2D grid)
// Keyboard navigation for categories and story cards
var categoryItems = Array.from(document.querySelectorAll('.story-toc-item'));
var visibleStories = function() {
return Array.from(document.querySelectorAll('.story-card:not(.hidden)'));
};
// Track which section we're in: 'categories' or 'stories'
var currentSection = 'stories';
var selectedCatIndex = -1;
var selectedCardIndex = -1;
function getGridColumns() {
function getCatGridColumns() {
var toc = document.querySelector('.story-toc ul');
if (!toc) return 1;
var style = getComputedStyle(toc);
var cols = style.gridTemplateColumns.split(' ').length;
return cols || 1;
}
function getCardGridColumns() {
var grid = document.querySelector('.stories-grid');
if (!grid) return 1;
var style = getComputedStyle(grid);
@@ -465,15 +489,30 @@ document.addEventListener('DOMContentLoaded', function() {
return cols || 1;
}
function clearAllSelections() {
categoryItems.forEach(function(c) { c.classList.remove('selected'); });
visibleStories().forEach(function(c) {
c.style.outline = '';
c.style.outlineOffset = '';
});
}
function selectCategory(index) {
clearAllSelections();
currentSection = 'categories';
selectedCardIndex = -1;
selectedCatIndex = Math.max(0, Math.min(index, categoryItems.length - 1));
categoryItems[selectedCatIndex].classList.add('selected');
categoryItems[selectedCatIndex].scrollIntoView({ behavior: 'smooth', block: 'center' });
}
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 = '';
}
clearAllSelections();
currentSection = 'stories';
selectedCatIndex = -1;
selectedCardIndex = Math.max(0, Math.min(index, cards.length - 1));
cards[selectedCardIndex].style.outline = '2px solid #4a7c59';
cards[selectedCardIndex].style.outlineOffset = '4px';
@@ -484,32 +523,90 @@ document.addEventListener('DOMContentLoaded', function() {
if (e.target.tagName === 'INPUT' || e.target.tagName === 'TEXTAREA') return;
var cards = visibleStories();
if (cards.length === 0) return;
var cols = getGridColumns();
var catCols = getCatGridColumns();
var cardCols = getCardGridColumns();
if (e.key === 'ArrowDown' || e.key === 'j') {
e.preventDefault();
if (selectedCardIndex < 0) selectStoryCard(0);
else selectStoryCard(selectedCardIndex + cols);
if (currentSection === 'categories') {
// Move down within categories or transition to stories
if (selectedCatIndex + catCols >= categoryItems.length) {
// Transition to story cards
selectStoryCard(0);
} else {
selectCategory(selectedCatIndex + catCols);
}
} else {
// In stories section
if (selectedCardIndex < 0) selectStoryCard(0);
else selectStoryCard(selectedCardIndex + cardCols);
}
} else if (e.key === 'ArrowUp' || e.key === 'k') {
e.preventDefault();
if (selectedCardIndex < 0) selectStoryCard(0);
else if (selectedCardIndex - cols >= 0) selectStoryCard(selectedCardIndex - cols);
if (currentSection === 'stories') {
if (selectedCardIndex < 0) {
selectStoryCard(0);
} else if (selectedCardIndex - cardCols < 0) {
// Transition to categories (last row)
var lastRowStart = Math.floor((categoryItems.length - 1) / catCols) * catCols;
selectCategory(Math.min(lastRowStart + (selectedCardIndex % cardCols), categoryItems.length - 1));
} else {
selectStoryCard(selectedCardIndex - cardCols);
}
} else {
// In categories
if (selectedCatIndex - catCols >= 0) {
selectCategory(selectedCatIndex - catCols);
}
}
} else if (e.key === 'ArrowRight' || e.key === 'l') {
e.preventDefault();
if (selectedCardIndex < 0) selectStoryCard(0);
else selectStoryCard(selectedCardIndex + 1);
if (currentSection === 'categories') {
if (selectedCatIndex < categoryItems.length - 1) {
selectCategory(selectedCatIndex + 1);
}
} else {
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) {
if (currentSection === 'categories') {
if (selectedCatIndex > 0) {
selectCategory(selectedCatIndex - 1);
} else {
history.back();
}
} else {
if (selectedCardIndex <= 0) history.back();
else selectStoryCard(selectedCardIndex - 1);
}
} else if (e.key === 'Enter') {
e.preventDefault();
var cards = visibleStories();
if (cards[selectedCardIndex]) {
if (currentSection === 'categories' && selectedCatIndex >= 0) {
// Get the target section from the category link
var targetId = categoryItems[selectedCatIndex].getAttribute('href').substring(1);
var targetSection = document.getElementById(targetId);
if (targetSection) {
// Find the first story card in this section
var firstCard = targetSection.querySelector('.story-card:not(.hidden)');
if (firstCard) {
var allCards = visibleStories();
var cardIndex = allCards.indexOf(firstCard);
if (cardIndex >= 0) {
selectStoryCard(cardIndex);
}
}
}
} else if (currentSection === 'stories' && selectedCardIndex >= 0 && cards[selectedCardIndex]) {
window.location.href = cards[selectedCardIndex].href;
}
} else if (e.key === 'Escape') {
e.preventDefault();
clearAllSelections();
currentSection = 'stories';
selectedCatIndex = -1;
selectedCardIndex = -1;
}
});
});
+34 -14
View File
@@ -371,23 +371,25 @@ hr.story-divider::before {
<script>
(function() {
// Collect paragraphs for up/down navigation
var paragraphs = Array.from(document.querySelectorAll('article section p'));
// Collect paragraphs, adult callout, and PDF button for up/down navigation
var elements = Array.from(document.querySelectorAll('article section p, .adult-callout, .print-btn'));
// Find the index of the first paragraph (to start selection there)
var firstParagraphIndex = elements.findIndex(function(el) { return el.tagName === 'P'; });
var selectedIndex = -1;
function selectParagraph(index) {
function selectElement(index) {
// Remove previous selection
if (selectedIndex >= 0 && selectedIndex < paragraphs.length) {
paragraphs[selectedIndex].style.outline = '';
paragraphs[selectedIndex].style.outlineOffset = '';
if (selectedIndex >= 0 && selectedIndex < elements.length) {
elements[selectedIndex].style.outline = '';
elements[selectedIndex].style.outlineOffset = '';
}
selectedIndex = Math.max(0, Math.min(index, paragraphs.length - 1));
selectedIndex = Math.max(0, Math.min(index, elements.length - 1));
// Add selection to new paragraph
paragraphs[selectedIndex].style.outline = '2px solid #8b5cf6';
paragraphs[selectedIndex].style.outlineOffset = '8px';
paragraphs[selectedIndex].scrollIntoView({ behavior: 'smooth', block: 'center' });
// Add selection to new element
elements[selectedIndex].style.outline = '2px solid #8b5cf6';
elements[selectedIndex].style.outlineOffset = '8px';
elements[selectedIndex].scrollIntoView({ behavior: 'smooth', block: 'center' });
}
document.addEventListener('keydown', function(e) {
@@ -395,11 +397,12 @@ hr.story-divider::before {
if (e.key === 'ArrowDown' || e.key === 'j') {
e.preventDefault();
selectParagraph(selectedIndex < 0 ? 0 : selectedIndex + 1);
// Start at first paragraph, not metadata
selectElement(selectedIndex < 0 ? firstParagraphIndex : selectedIndex + 1);
} else if (e.key === 'ArrowUp' || e.key === 'k') {
e.preventDefault();
if (selectedIndex <= 0) selectParagraph(0);
else selectParagraph(selectedIndex - 1);
if (selectedIndex <= 0) selectElement(0);
else selectElement(selectedIndex - 1);
} else if (e.key === 'ArrowLeft' || e.key === 'h') {
e.preventDefault();
history.back();
@@ -407,6 +410,23 @@ hr.story-divider::before {
e.preventDefault();
var nextLink = document.querySelector('.nav-next .nav-link');
if (nextLink) window.location.href = nextLink.href;
} else if (e.key === 'Enter' && selectedIndex >= 0) {
e.preventDefault();
var el = elements[selectedIndex];
// Check if the element itself is a link (like PDF button)
if (el.tagName === 'A' && el.href) {
window.location.href = el.href;
} else {
var link = el.querySelector('a');
if (link) window.location.href = link.href;
}
} else if (e.key === 'Escape') {
e.preventDefault();
if (selectedIndex >= 0 && selectedIndex < elements.length) {
elements[selectedIndex].style.outline = '';
elements[selectedIndex].style.outlineOffset = '';
}
selectedIndex = -1;
}
});
})();
+24 -18
View File
@@ -200,20 +200,21 @@
<script>
(function() {
const sections = Array.from(document.querySelectorAll('.subtopic-section'));
if (sections.length === 0) return;
// Include overview, intro paragraphs, verse items, and PDF button
const elements = Array.from(document.querySelectorAll('.topic-overview, .intro-text, .verse-item, .print-btn'));
if (elements.length === 0) return;
let selectedIndex = -1;
function selectSection(index) {
if (selectedIndex >= 0 && selectedIndex < sections.length) {
sections[selectedIndex].style.outline = '';
sections[selectedIndex].style.outlineOffset = '';
function selectElement(index) {
if (selectedIndex >= 0 && selectedIndex < elements.length) {
elements[selectedIndex].style.outline = '';
elements[selectedIndex].style.outlineOffset = '';
}
selectedIndex = Math.max(0, Math.min(index, sections.length - 1));
sections[selectedIndex].style.outline = '2px solid #4a7c59';
sections[selectedIndex].style.outlineOffset = '4px';
sections[selectedIndex].scrollIntoView({ behavior: 'smooth', block: 'start' });
selectedIndex = Math.max(0, Math.min(index, elements.length - 1));
elements[selectedIndex].style.outline = '2px solid #4a7c59';
elements[selectedIndex].style.outlineOffset = '8px';
elements[selectedIndex].scrollIntoView({ behavior: 'smooth', block: 'center' });
}
document.addEventListener('keydown', function(e) {
@@ -221,23 +222,28 @@
if (e.key === 'ArrowDown' || e.key === 'j') {
e.preventDefault();
selectSection(selectedIndex < 0 ? 0 : selectedIndex + 1);
selectElement(selectedIndex < 0 ? 0 : selectedIndex + 1);
} else if (e.key === 'ArrowUp' || e.key === 'k') {
e.preventDefault();
if (selectedIndex <= 0) selectSection(0);
else selectSection(selectedIndex - 1);
if (selectedIndex <= 0) selectElement(0);
else selectElement(selectedIndex - 1);
} else if (e.key === 'ArrowLeft' || e.key === 'h') {
e.preventDefault();
history.back();
} else if (e.key === 'Enter' && selectedIndex >= 0) {
e.preventDefault();
const firstLink = sections[selectedIndex].querySelector('.verse-ref a');
if (firstLink) window.location.href = firstLink.href;
var el = elements[selectedIndex];
if (el.tagName === 'A' && el.href) {
window.location.href = el.href;
} else {
var link = el.querySelector('.verse-ref a') || el.querySelector('a');
if (link) window.location.href = link.href;
}
} else if (e.key === 'Escape') {
e.preventDefault();
if (selectedIndex >= 0 && selectedIndex < sections.length) {
sections[selectedIndex].style.outline = '';
sections[selectedIndex].style.outlineOffset = '';
if (selectedIndex >= 0 && selectedIndex < elements.length) {
elements[selectedIndex].style.outline = '';
elements[selectedIndex].style.outlineOffset = '';
}
selectedIndex = -1;
}