Three density modes: Overview (~3 min), Standard (~12 min), Deep (~25 min). Applied to researcher.html — 7 sections tagged with data-reading-level. Persists user preference in localStorage. Auto-injects toggle UI. Reusable component for architectural-alignment papers. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
114 lines
3.9 KiB
JavaScript
114 lines
3.9 KiB
JavaScript
/**
|
|
* Reading Mode Toggle
|
|
* Provides overview/standard/deep density modes for long research papers.
|
|
*
|
|
* Usage: Add data-reading-level="overview|standard|deep" to content sections.
|
|
* Sections without the attribute are always visible.
|
|
*
|
|
* The toggle is auto-injected at the top of any page that includes this script
|
|
* and has elements with data-reading-level attributes.
|
|
*/
|
|
(function() {
|
|
'use strict';
|
|
|
|
var STORAGE_KEY = 'tractatus-reading-mode';
|
|
var MODES = ['overview', 'standard', 'deep'];
|
|
var MODE_LABELS = {
|
|
overview: { label: 'Overview', desc: 'Key points only' },
|
|
standard: { label: 'Standard', desc: 'Main content' },
|
|
deep: { label: 'Deep', desc: 'Full detail' }
|
|
};
|
|
|
|
function init() {
|
|
// Only activate if page has reading-level content
|
|
var leveledContent = document.querySelectorAll('[data-reading-level]');
|
|
if (leveledContent.length === 0) return;
|
|
|
|
var savedMode = localStorage.getItem(STORAGE_KEY) || 'standard';
|
|
|
|
// Inject toggle UI after the first h1 or at the top of main content
|
|
var heroOrH1 = document.querySelector('.hero-section, .page-hero, h1');
|
|
var insertPoint = (heroOrH1 && heroOrH1.parentElement)
|
|
|| document.querySelector('main')
|
|
|| document.body;
|
|
|
|
var toggle = document.createElement('div');
|
|
toggle.className = 'reading-mode-toggle';
|
|
toggle.setAttribute('role', 'radiogroup');
|
|
toggle.setAttribute('aria-label', 'Reading depth');
|
|
toggle.innerHTML = '<span class="reading-mode-label">Reading depth:</span>' +
|
|
MODES.map(function(mode) {
|
|
return '<button class="reading-mode-btn ' + (mode === savedMode ? 'active' : '') + '" ' +
|
|
'data-mode="' + mode + '" ' +
|
|
'role="radio" ' +
|
|
'aria-checked="' + (mode === savedMode) + '" ' +
|
|
'title="' + MODE_LABELS[mode].desc + '">' +
|
|
MODE_LABELS[mode].label +
|
|
'</button>';
|
|
}).join('') +
|
|
'<span class="reading-mode-estimate"></span>';
|
|
|
|
// Insert after the hero/h1 section
|
|
if (insertPoint.nextSibling) {
|
|
insertPoint.parentNode.insertBefore(toggle, insertPoint.nextSibling);
|
|
} else {
|
|
insertPoint.parentNode.appendChild(toggle);
|
|
}
|
|
|
|
// Apply initial mode
|
|
applyMode(savedMode);
|
|
|
|
// Handle clicks
|
|
toggle.addEventListener('click', function(e) {
|
|
var btn = e.target.closest('.reading-mode-btn');
|
|
if (!btn) return;
|
|
|
|
var mode = btn.dataset.mode;
|
|
localStorage.setItem(STORAGE_KEY, mode);
|
|
|
|
// Update button states
|
|
var buttons = toggle.querySelectorAll('.reading-mode-btn');
|
|
for (var i = 0; i < buttons.length; i++) {
|
|
var b = buttons[i];
|
|
if (b.dataset.mode === mode) {
|
|
b.classList.add('active');
|
|
b.setAttribute('aria-checked', 'true');
|
|
} else {
|
|
b.classList.remove('active');
|
|
b.setAttribute('aria-checked', 'false');
|
|
}
|
|
}
|
|
|
|
applyMode(mode);
|
|
});
|
|
}
|
|
|
|
function applyMode(mode) {
|
|
var elements = document.querySelectorAll('[data-reading-level]');
|
|
var modeIndex = MODES.indexOf(mode);
|
|
|
|
for (var i = 0; i < elements.length; i++) {
|
|
var el = elements[i];
|
|
var level = el.dataset.readingLevel;
|
|
var levelIndex = MODES.indexOf(level);
|
|
// overview shows only overview; standard shows overview+standard; deep shows all
|
|
var visible = levelIndex <= modeIndex;
|
|
el.style.display = visible ? '' : 'none';
|
|
el.setAttribute('aria-hidden', !visible);
|
|
}
|
|
|
|
// Update reading time estimate if present
|
|
var counter = document.querySelector('.reading-mode-estimate');
|
|
if (counter) {
|
|
var labels = { overview: '~3 min read', standard: '~12 min read', deep: '~25 min read' };
|
|
counter.textContent = labels[mode] || '';
|
|
}
|
|
}
|
|
|
|
// Init on DOM ready
|
|
if (document.readyState === 'loading') {
|
|
document.addEventListener('DOMContentLoaded', init);
|
|
} else {
|
|
init();
|
|
}
|
|
})();
|