From 2a94971a38b5114e76f5bbf96cc41acc31c5170f Mon Sep 17 00:00:00 2001
From: Kenneth Reitz
Date: Sun, 30 Nov 2025 09:03:33 -0500
Subject: [PATCH] Refactor book view to use drilldown navigation with green box
styling
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
**Book View Changes:**
- Chapter section is now a single item with green box outline
- Press ↓ to select chapter section
- Press Enter to drill down into individual chapters
- Press Esc to exit drilldown mode
- All paragraphs now use green box outline styling (consistent UX)
**Chapter View Changes:**
- Removed 'c' keyboard shortcut (reserved keyword)
- Updated help text
**Navigation Flow:**
1. Start: Press ↓ → selects chapter section (green box)
2. Press Enter → drills into first chapter number
3. Navigate ↑/↓ through chapter numbers
4. Press Enter on a chapter → navigate to that chapter
5. Press Esc → exit back to chapter section
6. Press ↓ from chapter section → move to paragraphs below
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude
---
kjvstudy_org/templates/book.html | 148 +++++++++++++++++++++-------
kjvstudy_org/templates/chapter.html | 21 ++--
2 files changed, 119 insertions(+), 50 deletions(-)
diff --git a/kjvstudy_org/templates/book.html b/kjvstudy_org/templates/book.html
index 88abf0c..b5bf543 100644
--- a/kjvstudy_org/templates/book.html
+++ b/kjvstudy_org/templates/book.html
@@ -25,22 +25,42 @@
outline-color: rgba(107, 155, 122, 0.4);
}
-/* Selected content paragraphs */
+/* Selected chapter section (green box) */
+.chapters-section.selected {
+ outline: 3px solid #4a7c59;
+ outline-offset: 4px;
+ background: rgba(74, 124, 89, 0.05);
+ padding: 1rem;
+ margin-left: -1rem;
+ margin-right: -1rem;
+ border-radius: 4px;
+ transition: all 0.15s ease;
+}
+
+[data-theme="dark"] .chapters-section.selected {
+ outline-color: #6b9b7a;
+ background: rgba(107, 155, 122, 0.1);
+}
+
+/* Selected content paragraphs (green box) */
section:not(.chapters-section) p.selected,
section:not(.chapters-section) li.selected,
section:not(.chapters-section) blockquote.selected {
- background: rgba(74, 124, 89, 0.1);
+ outline: 3px solid #4a7c59;
+ outline-offset: 4px;
+ background: rgba(74, 124, 89, 0.05);
+ padding: 1rem;
margin-left: -1rem;
- padding-left: 1rem;
- border-left: 3px solid #4a7c59;
+ margin-right: -1rem;
+ border-radius: 4px;
transition: all 0.15s ease;
}
[data-theme="dark"] section:not(.chapters-section) p.selected,
[data-theme="dark"] section:not(.chapters-section) li.selected,
[data-theme="dark"] section:not(.chapters-section) blockquote.selected {
- background: rgba(107, 155, 122, 0.15);
- border-left-color: #6b9b7a;
+ outline-color: #6b9b7a;
+ background: rgba(107, 155, 122, 0.1);
}
.popular-chapter {
@@ -386,17 +406,23 @@ document.addEventListener('DOMContentLoaded', function() {
linkVerseReferences(section);
});
- // Two-zone keyboard navigation: chapter links and content paragraphs
+ // Navigation with chapter drilldown
+ const chapterSection = document.querySelector('.chapters-section');
const chapterLinks = document.querySelectorAll('.chapters-section a[data-chapter]');
const contentParagraphs = document.querySelectorAll('section:not(.chapters-section) p, section:not(.chapters-section) li, section:not(.chapters-section) blockquote');
+
let selectedChapterIndex = -1;
let selectedParagraphIndex = -1;
- let inChapterZone = true;
+ let chapterSectionSelected = false;
+ let inChapterDrilldown = false; // Are we drilling down into individual chapters?
// Get current book index for left/right navigation
const currentBookIndex = bibleBooks.findIndex(b => b.toLowerCase() === bookName.toLowerCase());
function clearAllSelections() {
+ if (chapterSection) {
+ chapterSection.classList.remove('selected');
+ }
if (selectedChapterIndex >= 0 && selectedChapterIndex < chapterLinks.length) {
chapterLinks[selectedChapterIndex].classList.remove('selected');
}
@@ -405,9 +431,26 @@ document.addEventListener('DOMContentLoaded', function() {
}
}
+ function selectChapterSection() {
+ clearAllSelections();
+ chapterSectionSelected = true;
+ inChapterDrilldown = false;
+ selectedChapterIndex = -1;
+ selectedParagraphIndex = -1;
+
+ if (chapterSection) {
+ chapterSection.classList.add('selected');
+ chapterSection.scrollIntoView({
+ behavior: 'smooth',
+ block: 'center'
+ });
+ }
+ }
+
function selectChapter(index) {
clearAllSelections();
- inChapterZone = true;
+ chapterSectionSelected = false;
+ inChapterDrilldown = true;
selectedParagraphIndex = -1;
// Update index with bounds checking
@@ -425,7 +468,8 @@ document.addEventListener('DOMContentLoaded', function() {
function selectParagraph(index) {
clearAllSelections();
- inChapterZone = false;
+ chapterSectionSelected = false;
+ inChapterDrilldown = false;
selectedChapterIndex = -1;
// Update index with bounds checking
@@ -450,58 +494,90 @@ document.addEventListener('DOMContentLoaded', function() {
// Up arrow or k: Previous item
if (e.key === 'ArrowUp' || e.key === 'k') {
e.preventDefault();
- if (inChapterZone) {
- if (KJVNav.isSelectionOffScreen(chapterLinks, selectedChapterIndex)) {
- selectChapter(KJVNav.findFirstVisibleIndex(chapterLinks));
- } else if (selectedChapterIndex > 0) {
+ if (inChapterDrilldown) {
+ // Navigating within chapter numbers
+ if (selectedChapterIndex > 0) {
selectChapter(selectedChapterIndex - 1);
+ } else {
+ // Exit drilldown, go back to chapter section
+ selectChapterSection();
}
- } else {
+ } else if (chapterSectionSelected) {
+ // At chapter section, can't go up (it's the first item)
+ // Stay here
+ } else if (selectedParagraphIndex >= 0) {
// In paragraph zone
if (selectedParagraphIndex === 0) {
- // Move back to last chapter
- selectChapter(chapterLinks.length - 1);
- } else if (selectedParagraphIndex < 0) {
- // No selection, start at last paragraph
- selectParagraph(contentParagraphs.length - 1);
+ // Move back to chapter section
+ selectChapterSection();
} else {
selectParagraph(selectedParagraphIndex - 1);
}
+ } else {
+ // No selection, start at chapter section
+ selectChapterSection();
}
}
// Down arrow or j: Next item
if (e.key === 'ArrowDown' || e.key === 'j') {
e.preventDefault();
- if (inChapterZone) {
- if (selectedChapterIndex >= chapterLinks.length - 1) {
- // Move to first paragraph
+ if (inChapterDrilldown) {
+ // Navigating within chapter numbers
+ if (selectedChapterIndex < chapterLinks.length - 1) {
+ selectChapter(selectedChapterIndex + 1);
+ } else {
+ // Exit drilldown to first paragraph
if (contentParagraphs.length > 0) {
selectParagraph(0);
+ } else {
+ // No paragraphs, exit drilldown back to chapter section
+ selectChapterSection();
}
- } else if (KJVNav.isSelectionOffScreen(chapterLinks, selectedChapterIndex)) {
- selectChapter(KJVNav.findFirstVisibleIndex(chapterLinks));
- } else {
- selectChapter(selectedChapterIndex + 1);
}
- } else {
- // In paragraph zone
- if (selectedParagraphIndex < 0) {
- // No selection, start at first paragraph
+ } else if (chapterSectionSelected) {
+ // At chapter section, move to first paragraph
+ if (contentParagraphs.length > 0) {
selectParagraph(0);
- } else if (selectedParagraphIndex < contentParagraphs.length - 1) {
+ }
+ } else if (selectedParagraphIndex >= 0) {
+ // In paragraph zone
+ if (selectedParagraphIndex < contentParagraphs.length - 1) {
selectParagraph(selectedParagraphIndex + 1);
}
+ } else {
+ // No selection, start at chapter section
+ selectChapterSection();
}
}
- // Enter: Go to selected chapter or do nothing for paragraphs
+ // Enter: Drill into chapter section or navigate to chapter
if (e.key === 'Enter') {
e.preventDefault();
- if (inChapterZone && selectedChapterIndex >= 0) {
+ if (chapterSectionSelected) {
+ // Drill down into first chapter
+ selectChapter(0);
+ } else if (inChapterDrilldown && selectedChapterIndex >= 0) {
+ // Navigate to selected chapter
window.location.href = chapterLinks[selectedChapterIndex].href;
}
- // Paragraphs don't navigate anywhere, just read them
+ // Paragraphs and chapter section don't navigate anywhere
+ }
+
+ // Escape: Exit drilldown mode
+ if (e.key === 'Escape') {
+ e.preventDefault();
+ if (inChapterDrilldown) {
+ // Exit drilldown back to chapter section
+ selectChapterSection();
+ } else {
+ // Clear all selections
+ clearAllSelections();
+ chapterSectionSelected = false;
+ inChapterDrilldown = false;
+ selectedChapterIndex = -1;
+ selectedParagraphIndex = -1;
+ }
}
// Left arrow: Previous book
@@ -563,7 +639,7 @@ document.addEventListener('DOMContentLoaded', function() {
= 7 %}class="popular-chapter"{% endif %} data-chapter="{{ chapter }}">{{ chapter }}{% if not loop.last %} · {% endif %}
{% endfor %}
- Tip: ↑/↓ to navigate chapters & paragraphs • Enter to read chapter • ←/→ for adjacent books
+ Tip: ↑/↓ to navigate • Enter to drill into chapters • Esc to exit • ←/→ for adjacent books
{% if book_intro and book_intro.introduction %}
diff --git a/kjvstudy_org/templates/chapter.html b/kjvstudy_org/templates/chapter.html
index 615b9c9..474cd32 100644
--- a/kjvstudy_org/templates/chapter.html
+++ b/kjvstudy_org/templates/chapter.html
@@ -352,7 +352,7 @@ p[id^="verse-"].selected {
- Tip: ↑/↓ to navigate • Enter to select • ←/→ chapters • i=interlinear • p=PDF • c=chapter selector
+ Tip: ↑/↓ to navigate • Enter to select • ←/→ chapters • i=interlinear • p=PDF
@@ -434,15 +434,15 @@ document.addEventListener('DOMContentLoaded', function() {
highlightVerses();
window.addEventListener('hashchange', highlightVerses);
- // Three-zone navigation: action buttons, chapter selector, and verses
+ // Three-zone navigation: action buttons, verses, and chapter selector
const actionButtons = Array.from(document.querySelectorAll('.chapter-actions .action-btn'));
- const chapterSelect = document.querySelector('.chapter-select');
- const chapterNavControls = document.querySelector('.chapter-nav-controls');
const verses = document.querySelectorAll('p[id^="verse-"]');
+ const chapterNavControls = document.querySelector('.chapter-nav-controls');
+ const chapterSelect = document.querySelector('.chapter-select');
let selectedVerseIndex = -1;
let selectedActionIndex = -1;
+ let chapterSelectorSelected = false;
let inActionZone = false;
- let inChapterSelectorZone = false;
// Initialize from hash if present (e.g., #verse-24)
function initFromHash() {
@@ -473,12 +473,12 @@ document.addEventListener('DOMContentLoaded', function() {
if (chapterNavControls) {
chapterNavControls.classList.remove('selected');
}
+ chapterSelectorSelected = false;
}
function selectAction(index) {
clearAllSelections();
inActionZone = true;
- inChapterSelectorZone = false;
selectedActionIndex = Math.max(0, Math.min(index, actionButtons.length - 1));
selectedVerseIndex = -1;
actionButtons[selectedActionIndex].classList.add('selected');
@@ -491,9 +491,9 @@ document.addEventListener('DOMContentLoaded', function() {
function selectChapterSelector() {
clearAllSelections();
inActionZone = false;
- inChapterSelectorZone = true;
selectedActionIndex = -1;
selectedVerseIndex = -1;
+ chapterSelectorSelected = true;
if (chapterNavControls) {
chapterNavControls.classList.add('selected');
chapterNavControls.scrollIntoView({
@@ -506,7 +506,6 @@ document.addEventListener('DOMContentLoaded', function() {
function selectVerse(index) {
clearAllSelections();
inActionZone = false;
- inChapterSelectorZone = false;
selectedActionIndex = -1;
// Update index with bounds checking
@@ -690,12 +689,6 @@ document.addEventListener('DOMContentLoaded', function() {
if (pdfBtn) window.location.href = pdfBtn.href;
}
- // c: Jump to chapter selector
- if (e.key === 'c') {
- e.preventDefault();
- selectChapterSelector();
- }
-
// Space: Read selected verse aloud
if (e.key === ' ' && selectedVerseIndex >= 0) {
e.preventDefault();