From 64caaeb049b0085a3bd8add6d0b7bfb54d9746ad Mon Sep 17 00:00:00 2001 From: Michael Czechowski Date: Wed, 14 Jan 2026 17:51:34 +0100 Subject: [PATCH] feat: add header level pill on desktop, use native details/summary for sidebar - Add lesson indicator pill to header (visible on desktop only) - Logo stays centered, pill on left with burger menu - Replace emoji arrows with CSS triangles for iOS compatibility - Use native
/ for expand/collapse - Update tests for new implementation --- src/app.js | 2 + src/helpers/renderer.js | 55 +++++------------------ src/index.html | 9 ++-- src/main.css | 89 +++++++++++++++++++++++++++++++++---- tests/unit/renderer.test.js | 14 +++--- 5 files changed, 106 insertions(+), 63 deletions(-) diff --git a/src/app.js b/src/app.js index 5569c3b..b4f10c9 100644 --- a/src/app.js +++ b/src/app.js @@ -48,6 +48,7 @@ const elements = { prevBtn: document.getElementById("prev-btn"), nextBtn: document.getElementById("next-btn"), levelIndicator: document.getElementById("level-indicator"), + headerLevelPill: document.getElementById("header-level-pill"), // Sidebar sidebarDrawer: document.getElementById("sidebar-drawer"), @@ -478,6 +479,7 @@ function loadCurrentLesson() { // Update level indicator renderLevelIndicator(elements.levelIndicator, engineState.lessonIndex + 1, engineState.totalLessons); + renderLevelIndicator(elements.headerLevelPill, engineState.lessonIndex + 1, engineState.totalLessons); // Update active lesson in sidebar updateActiveLessonInSidebar(engineState.module.id, engineState.lessonIndex); diff --git a/src/helpers/renderer.js b/src/helpers/renderer.js index c679c78..4dbc0b5 100644 --- a/src/helpers/renderer.js +++ b/src/helpers/renderer.js @@ -32,30 +32,21 @@ export function renderModuleList(container, modules, onSelectModule, onSelectLes // Create list items for each module modules.forEach((module) => { // Create module container - const moduleContainer = document.createElement("div"); + // Use native
/ for expand/collapse + const moduleContainer = document.createElement("details"); moduleContainer.classList.add("module-container"); - moduleContainer.setAttribute("role", "treeitem"); + moduleContainer.dataset.moduleId = module.id; - // Create module header item (clickable to expand/collapse) - const moduleHeader = document.createElement("button"); - moduleHeader.type = "button"; + // Create module header using + const moduleHeader = document.createElement("summary"); moduleHeader.classList.add("module-list-item", "module-header"); moduleHeader.dataset.moduleId = module.id; - moduleHeader.setAttribute("aria-expanded", "false"); - moduleHeader.setAttribute("aria-controls", `lessons-${module.id}`); - // Create module title with expand/collapse indicator + // Create module title const moduleTitle = document.createElement("span"); moduleTitle.classList.add("module-title"); moduleTitle.textContent = module.title; - // Create expand/collapse icon - const expandIcon = document.createElement("span"); - expandIcon.classList.add("expand-icon"); - expandIcon.setAttribute("aria-hidden", "true"); - expandIcon.innerHTML = "▶"; // Right-pointing triangle - - moduleHeader.appendChild(expandIcon); moduleHeader.appendChild(moduleTitle); // Check if the module is completed @@ -63,13 +54,10 @@ export function renderModuleList(container, modules, onSelectModule, onSelectLes moduleHeader.classList.add("completed"); } - // Lessons container (initially hidden) + // Lessons container const lessonsContainer = document.createElement("div"); lessonsContainer.classList.add("lessons-container"); lessonsContainer.id = `lessons-${module.id}`; - lessonsContainer.setAttribute("role", "group"); - lessonsContainer.setAttribute("aria-label", `${module.title} lessons`); - lessonsContainer.style.display = "none"; // Initially collapsed // Create list items for each lesson in this module module.lessons.forEach((lesson, index) => { @@ -105,17 +93,6 @@ export function renderModuleList(container, modules, onSelectModule, onSelectLes lessonsContainer.appendChild(lessonItem); }); - // Toggle expand/collapse when clicking on module header - moduleHeader.addEventListener("click", () => { - // Toggle visibility of lessons container - const isExpanded = lessonsContainer.style.display !== "none"; - lessonsContainer.style.display = isExpanded ? "none" : "block"; - - // Update expand/collapse icon and ARIA state - expandIcon.innerHTML = isExpanded ? "▶" : "▼"; - moduleHeader.setAttribute("aria-expanded", String(!isExpanded)); - }); - // Add module header and lessons container to module container moduleContainer.appendChild(moduleHeader); moduleContainer.appendChild(lessonsContainer); @@ -233,20 +210,10 @@ export function updateActiveLessonInSidebar(moduleId, lessonIndex) { if (currentLessonItem) { currentLessonItem.classList.add("active"); - // Make sure parent module is expanded - const parentLessonsContainer = currentLessonItem.parentElement; - if (parentLessonsContainer && parentLessonsContainer.classList.contains("lessons-container")) { - parentLessonsContainer.style.display = "block"; - - // Update expand icon and ARIA state - const moduleHeader = parentLessonsContainer.previousElementSibling; - if (moduleHeader) { - moduleHeader.setAttribute("aria-expanded", "true"); - const expandIcon = moduleHeader.querySelector(".expand-icon"); - if (expandIcon) { - expandIcon.innerHTML = "▼"; // Down arrow when expanded - } - } + // Make sure parent module is expanded (native
element) + const parentDetails = currentLessonItem.closest("details.module-container"); + if (parentDetails) { + parentDetails.open = true; // Scroll to the top of the page document.querySelector("html").scrollTop = 0; diff --git a/src/index.html b/src/index.html index 7c0aafb..0fd4434 100644 --- a/src/index.html +++ b/src/index.html @@ -15,9 +15,12 @@
- +
+ + +