mirror of
https://github.com/kennethreitz/kjvstudy.org.git
synced 2026-06-05 23:00:16 +00:00
2a2e8ebc08
- Add thermometer-style green fill on "Your Reading Plans" items - Singular/plural heading based on number of plans with progress - Keyboard navigation includes "Your Reading Plans" items first - j/k navigates through your plans then into the grid 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
374 lines
12 KiB
HTML
374 lines
12 KiB
HTML
{% extends "base.html" %}
|
|
|
|
{% block title %}Bible Reading Plans - KJV Study{% endblock %}
|
|
{% block description %}Structured Bible reading schedules for systematic Scripture study{% endblock %}
|
|
|
|
{% block head %}
|
|
<style>
|
|
.plan-grid {
|
|
display: grid;
|
|
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
|
|
gap: 1.5rem;
|
|
max-width: 90%;
|
|
margin: 2rem 0;
|
|
}
|
|
|
|
.plan-card {
|
|
display: block;
|
|
padding: 1.5rem;
|
|
border: 1px solid var(--border-color);
|
|
border-radius: 4px;
|
|
transition: all 0.2s;
|
|
text-decoration: none;
|
|
color: inherit;
|
|
cursor: pointer;
|
|
}
|
|
|
|
.plan-card:hover {
|
|
border-color: var(--border-color-darker);
|
|
box-shadow: 0 2px 12px rgba(0,0,0,0.1);
|
|
transform: translateY(-2px);
|
|
}
|
|
|
|
.plan-name {
|
|
font-size: 1.4rem;
|
|
font-weight: 600;
|
|
margin-bottom: 0.5rem;
|
|
color: var(--link-color);
|
|
}
|
|
|
|
.plan-card:hover .plan-name {
|
|
color: var(--link-hover);
|
|
}
|
|
|
|
.plan-description {
|
|
font-size: 1rem;
|
|
color: var(--text-secondary);
|
|
line-height: 1.6;
|
|
margin: 0.5rem 0;
|
|
}
|
|
|
|
.plan-duration {
|
|
font-size: 0.9rem;
|
|
color: var(--text-tertiary);
|
|
font-style: italic;
|
|
margin-top: 0.75rem;
|
|
}
|
|
|
|
.plan-progress {
|
|
margin-top: 0.75rem;
|
|
display: none;
|
|
}
|
|
|
|
.plan-progress.has-progress {
|
|
display: block;
|
|
}
|
|
|
|
.plan-progress-bar {
|
|
height: 6px;
|
|
background: var(--border-color);
|
|
border-radius: 3px;
|
|
overflow: hidden;
|
|
}
|
|
|
|
.plan-progress-fill {
|
|
height: 100%;
|
|
background: #4a7c59;
|
|
transition: width 0.3s;
|
|
}
|
|
|
|
.plan-progress-text {
|
|
font-size: 0.8rem;
|
|
color: #4a7c59;
|
|
margin-top: 0.3rem;
|
|
font-weight: 500;
|
|
}
|
|
|
|
.your-plans {
|
|
display: none;
|
|
}
|
|
|
|
.your-plans.has-plans {
|
|
display: block;
|
|
}
|
|
|
|
.your-plans-list {
|
|
list-style: none;
|
|
padding: 0;
|
|
margin: 1rem 0;
|
|
max-width: 60%;
|
|
}
|
|
|
|
.your-plans-list li {
|
|
padding: 0.75rem 1rem;
|
|
border: 1px solid var(--border-color);
|
|
border-radius: 4px;
|
|
margin-bottom: 0.5rem;
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 1rem;
|
|
background: linear-gradient(to right, rgba(74, 124, 89, 0.15) var(--progress, 0%), var(--code-bg) var(--progress, 0%));
|
|
position: relative;
|
|
}
|
|
|
|
.your-plans-list li:hover {
|
|
border-color: #4a7c59;
|
|
}
|
|
|
|
.your-plan-link {
|
|
font-weight: 600;
|
|
color: var(--link-color);
|
|
text-decoration: none;
|
|
border-bottom: none;
|
|
}
|
|
|
|
.your-plan-link:hover {
|
|
color: #4a7c59;
|
|
}
|
|
|
|
.your-plan-next {
|
|
color: var(--text-secondary);
|
|
font-size: 0.9rem;
|
|
margin-left: auto;
|
|
}
|
|
|
|
.your-plan-progress {
|
|
font-size: 0.85rem;
|
|
color: #4a7c59;
|
|
font-weight: 500;
|
|
}
|
|
|
|
.your-plan-completed {
|
|
color: #4a7c59;
|
|
font-weight: 600;
|
|
}
|
|
|
|
.intro-text {
|
|
max-width: 60%;
|
|
font-size: 1.2rem;
|
|
line-height: 1.9;
|
|
margin: 1rem 0;
|
|
}
|
|
</style>
|
|
{% endblock %}
|
|
|
|
{% block content %}
|
|
<h1>Bible Reading Plans</h1>
|
|
<p class="subtitle">Structured Schedules for Systematic Scripture Study</p>
|
|
|
|
<section>
|
|
<p class="intro-text"><span class="newthought">Systematic Bible reading</span> provides structure for comprehensive Scripture engagement. These curated plans offer various approaches—chronological progression, thematic focus, or testament-specific study—each designed to facilitate sustained interaction with God's Word.</p>
|
|
|
|
<p class="intro-text">Whether pursuing canonical familiarity through complete Bible reading or concentrating on specific portions, these schedules provide daily guidance for disciplined Scripture intake. Select a plan suited to your current season and study objectives.</p>
|
|
</section>
|
|
|
|
<section class="your-plans" id="your-plans">
|
|
<h2 id="your-plans-heading">Your Reading Plans</h2>
|
|
<ul class="your-plans-list" id="your-plans-list"></ul>
|
|
</section>
|
|
|
|
<section>
|
|
<h2>Available Reading Plans</h2>
|
|
|
|
<div class="plan-grid">
|
|
{% for plan in plans %}
|
|
<a href="/reading-plans/{{ plan.id }}" class="plan-card" data-plan-id="{{ plan.id }}" data-total-days="{{ plan.days }}">
|
|
<div class="plan-name">{{ plan.name }}</div>
|
|
<div class="plan-description">{{ plan.description }}</div>
|
|
<div class="plan-duration">{{ plan.days }} days</div>
|
|
<div class="plan-progress">
|
|
<div class="plan-progress-bar">
|
|
<div class="plan-progress-fill" style="width: 0%"></div>
|
|
</div>
|
|
<div class="plan-progress-text"></div>
|
|
</div>
|
|
</a>
|
|
{% endfor %}
|
|
</div>
|
|
</section>
|
|
|
|
<section>
|
|
<h2>Using Reading Plans</h2>
|
|
<p class="intro-text">Select a plan above to view its complete schedule. Each plan provides daily readings with thematic notes. Consider marking your progress and maintaining consistency—even brief daily reading surpasses sporadic lengthy sessions in forming lasting Scripture engagement habits.</p>
|
|
</section>
|
|
|
|
<script>
|
|
(function() {
|
|
// Load and display progress for each plan
|
|
const cards = Array.from(document.querySelectorAll('.plan-card'));
|
|
var yourPlansSection = document.getElementById('your-plans');
|
|
var yourPlansList = document.getElementById('your-plans-list');
|
|
var plansWithProgress = [];
|
|
|
|
cards.forEach(function(card) {
|
|
var planId = card.dataset.planId;
|
|
var totalDays = parseInt(card.dataset.totalDays);
|
|
var storageKey = 'reading-plan-' + planId;
|
|
var saved = localStorage.getItem(storageKey);
|
|
|
|
if (saved) {
|
|
try {
|
|
var data = JSON.parse(saved);
|
|
var completed = data.completed ? data.completed.length : 0;
|
|
if (completed > 0) {
|
|
var percent = Math.round((completed / totalDays) * 100);
|
|
var progressEl = card.querySelector('.plan-progress');
|
|
var fillEl = card.querySelector('.plan-progress-fill');
|
|
var textEl = card.querySelector('.plan-progress-text');
|
|
|
|
progressEl.classList.add('has-progress');
|
|
fillEl.style.width = percent + '%';
|
|
|
|
if (completed === totalDays) {
|
|
textEl.textContent = 'Completed!';
|
|
} else {
|
|
textEl.textContent = completed + '/' + totalDays + ' days (' + percent + '%)';
|
|
}
|
|
|
|
// Add to your plans list
|
|
var planName = card.querySelector('.plan-name').textContent;
|
|
var nextDay = completed + 1;
|
|
var isComplete = completed >= totalDays;
|
|
|
|
plansWithProgress.push({
|
|
id: planId,
|
|
name: planName,
|
|
completed: completed,
|
|
totalDays: totalDays,
|
|
nextDay: nextDay,
|
|
isComplete: isComplete,
|
|
percent: percent
|
|
});
|
|
}
|
|
} catch(e) {}
|
|
}
|
|
});
|
|
|
|
// Populate "Your Reading Plans" section
|
|
if (plansWithProgress.length > 0) {
|
|
yourPlansSection.classList.add('has-plans');
|
|
var heading = document.getElementById('your-plans-heading');
|
|
if (plansWithProgress.length === 1) {
|
|
heading.textContent = 'Your Reading Plan';
|
|
}
|
|
plansWithProgress.forEach(function(plan) {
|
|
var li = document.createElement('li');
|
|
var statusText;
|
|
if (plan.isComplete) {
|
|
statusText = '<span class="your-plan-completed">Completed!</span>';
|
|
} else {
|
|
statusText = '<span class="your-plan-next">Up next: Day ' + plan.nextDay + '</span>';
|
|
}
|
|
li.style.setProperty('--progress', plan.percent + '%');
|
|
li.innerHTML = '<a href="/reading-plans/' + plan.id + '" class="your-plan-link">' + plan.name + '</a>' +
|
|
'<span class="your-plan-progress">' + plan.percent + '%</span>' +
|
|
statusText;
|
|
yourPlansList.appendChild(li);
|
|
});
|
|
}
|
|
|
|
// Keyboard navigation - combine your plans list items + grid cards
|
|
var yourPlanItems = Array.from(document.querySelectorAll('.your-plans-list li'));
|
|
var allNavItems = yourPlanItems.concat(Array.from(cards));
|
|
|
|
if (allNavItems.length === 0) return;
|
|
|
|
var selectedIndex = -1;
|
|
var yourPlansCount = yourPlanItems.length;
|
|
|
|
function getGridColumns() {
|
|
if (window.innerWidth <= 768) return 1;
|
|
const grid = document.querySelector('.plan-grid');
|
|
if (!grid) return 1;
|
|
const style = getComputedStyle(grid);
|
|
const cols = style.gridTemplateColumns.split(' ').length;
|
|
return cols || 1;
|
|
}
|
|
|
|
function clearSelection() {
|
|
if (selectedIndex >= 0 && selectedIndex < allNavItems.length) {
|
|
allNavItems[selectedIndex].style.outline = '';
|
|
allNavItems[selectedIndex].style.outlineOffset = '';
|
|
allNavItems[selectedIndex].classList.remove('selected');
|
|
}
|
|
}
|
|
|
|
function selectItem(index) {
|
|
clearSelection();
|
|
selectedIndex = Math.max(0, Math.min(index, allNavItems.length - 1));
|
|
allNavItems[selectedIndex].style.outline = '2px solid #4a7c59';
|
|
allNavItems[selectedIndex].style.outlineOffset = '2px';
|
|
allNavItems[selectedIndex].classList.add('selected');
|
|
allNavItems[selectedIndex].scrollIntoView({ behavior: 'auto', block: 'center' });
|
|
}
|
|
|
|
function getItemLink(item) {
|
|
// For li items, find the link inside; for cards, the item IS the link
|
|
if (item.tagName === 'LI') {
|
|
var link = item.querySelector('a');
|
|
return link ? link.href : null;
|
|
}
|
|
return item.href;
|
|
}
|
|
|
|
document.addEventListener('keydown', function(e) {
|
|
if (e.target.tagName === 'INPUT' || e.target.tagName === 'TEXTAREA') return;
|
|
|
|
var cols = getGridColumns();
|
|
var inYourPlans = selectedIndex >= 0 && selectedIndex < yourPlansCount;
|
|
var inGrid = selectedIndex >= yourPlansCount;
|
|
|
|
if (e.key === 'ArrowDown' || e.key === 'j') {
|
|
e.preventDefault();
|
|
if (selectedIndex < 0) {
|
|
selectItem(0);
|
|
} else if (inYourPlans) {
|
|
// Move down in your plans list, or to grid
|
|
if (selectedIndex + 1 < yourPlansCount) {
|
|
selectItem(selectedIndex + 1);
|
|
} else {
|
|
selectItem(yourPlansCount); // First grid item
|
|
}
|
|
} else {
|
|
// In grid, move down by columns
|
|
selectItem(selectedIndex + cols);
|
|
}
|
|
} else if (e.key === 'ArrowUp' || e.key === 'k') {
|
|
e.preventDefault();
|
|
if (selectedIndex < 0) {
|
|
selectItem(0);
|
|
} else if (inYourPlans) {
|
|
if (selectedIndex > 0) selectItem(selectedIndex - 1);
|
|
} else {
|
|
// In grid
|
|
var newIndex = selectedIndex - cols;
|
|
if (newIndex >= yourPlansCount) {
|
|
selectItem(newIndex);
|
|
} else if (yourPlansCount > 0) {
|
|
// Jump to last your-plan item
|
|
selectItem(yourPlansCount - 1);
|
|
}
|
|
}
|
|
} else if (e.key === 'ArrowRight' || e.key === 'l') {
|
|
e.preventDefault();
|
|
if (selectedIndex < 0) selectItem(0);
|
|
else if (inGrid) selectItem(selectedIndex + 1);
|
|
} else if (e.key === 'ArrowLeft' || e.key === 'h') {
|
|
e.preventDefault();
|
|
if (selectedIndex <= 0) history.back();
|
|
else if (inGrid && selectedIndex > yourPlansCount) selectItem(selectedIndex - 1);
|
|
else if (inYourPlans) history.back();
|
|
} else if (e.key === 'Enter' && selectedIndex >= 0) {
|
|
e.preventDefault();
|
|
var href = getItemLink(allNavItems[selectedIndex]);
|
|
if (href) window.location.href = href;
|
|
} else if (e.key === 'Escape') {
|
|
e.preventDefault();
|
|
clearSelection();
|
|
selectedIndex = -1;
|
|
}
|
|
});
|
|
})();
|
|
</script>
|
|
{% endblock %}
|