Redesign expandable lists to a leaner, flat style

Course tree (lesson-page sidebar + dashboard's loaded-course view) and
File Management's Manage Library browser both move from boxed,
bordered rows to a flat list: no per-row background/border, a small
leading chevron that doubles as the expand affordance instead of a
separate right-edge button, indentation for hierarchy. The chevron's
rotation is pure CSS (:has(+ .tree-content.expanded) / :has(+
.curate-children.expanded)) rather than JS swapping glyph text, which
also let a fair amount of now-redundant JS come out.

The course tree's new styles are scoped under .tree-container (course
dashboard) and .lesson-sidebar (lesson page) so they don't leak into
the Library folder browser and other rows that reuse the same base
.tree-header/.lesson-item/etc. class names elsewhere in
course_dashboard.html - confirmed those are visually untouched.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-26 10:45:38 -04:00
co-authored by Claude Sonnet 5
parent 016acacdb5
commit bf0a02d008
7 changed files with 197 additions and 187 deletions
+88 -12
View File
@@ -682,6 +682,89 @@
display: block;
}
/* Course-outline tree: a leaner, flatter look than the boxed rows
above - those stay as-is since directoryRowHtml (the Library
folder browser) reuses the same base .tree-header/.tree-icon/
.lesson-item/etc. classes and isn't part of this redesign.
Scoped under .tree-container (exclusive to the course tree) so
nothing outside it is affected. No per-row background/border; a
small leading chevron doubles as the expand affordance instead
of a separate right-edge button, rotating via :has() so JS
never has to touch the icon directly - toggling is just
add/remove of one class on .tree-content. */
.tree-container .tree-header {
background: none;
border-left: none;
padding: 6px 8px;
gap: 8px;
}
.tree-container .tree-header:hover {
background: var(--bg-tertiary);
}
.tree-container .tree-toggle-icon {
display: inline-block;
width: 14px;
flex-shrink: 0;
color: var(--accent);
font-size: 0.85em;
transition: transform 0.15s ease;
}
.tree-container .tree-header:has(+ .tree-content.expanded) .tree-toggle-icon {
transform: rotate(90deg);
}
.tree-container .tree-name {
font-weight: 600;
}
.tree-container .tree-content {
margin-left: 20px;
margin-top: 2px;
}
.tree-container .lesson-item {
background: none;
border-left: none;
padding: 5px 8px;
margin-bottom: 0;
gap: 8px;
justify-content: flex-start;
transform: none;
}
.tree-container .lesson-item .lesson-name {
flex: 1;
min-width: 0;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
font-weight: 400;
font-size: 0.95em;
}
.tree-container .lesson-item:hover {
background: var(--bg-tertiary);
transform: none;
}
.tree-container .lesson-item.completed {
background: none;
}
.tree-container .lesson-item.completed:hover {
background: var(--bg-tertiary);
}
.tree-container .lesson-item.current {
background: var(--bg-tertiary);
cursor: default;
}
.tree-container .lesson-item.current .lesson-name {
font-weight: 600;
color: var(--accent);
}
.tree-container .lesson-item .lesson-icon {
font-size: 0.9em;
color: var(--text-muted);
}
.tree-container .lesson-item .lesson-duration {
font-size: 0.8em;
color: var(--text-muted);
flex-shrink: 0;
}
.library-toolbar {
display: flex;
gap: 10px;
@@ -1376,13 +1459,13 @@
}
function toggleTree(element) {
// Find the content div that comes after this header
// Find the content div that comes after this header - the
// chevron's rotation is pure CSS (:has(+ .tree-content.expanded)
// in _course_tree.html's consumers), so toggling this one class
// is the entire job here.
const content = element.nextElementSibling;
const toggle = element.querySelector('.tree-toggle');
if (content && content.classList.contains('tree-content')) {
const isExpanded = content.classList.toggle('expanded');
if (toggle) toggle.textContent = isExpanded ? '▼' : '▶';
if (content.dataset.sectionPath) saveSectionExpanded(content.dataset.sectionPath, isExpanded);
}
}
@@ -1410,11 +1493,7 @@
const saved = getExpandedSectionPaths();
if (!saved.size) return;
document.querySelectorAll('.tree-content[data-section-path]').forEach(function(content) {
if (!saved.has(content.dataset.sectionPath)) return;
content.classList.add('expanded');
const header = content.previousElementSibling;
const toggle = header ? header.querySelector('.tree-toggle') : null;
if (toggle) toggle.textContent = '▼';
if (saved.has(content.dataset.sectionPath)) content.classList.add('expanded');
});
}
restoreExpandedSections();
@@ -1430,9 +1509,6 @@
const shouldExpand = !contents.every(c => c.classList.contains('expanded'));
contents.forEach(function(content) {
content.classList.toggle('expanded', shouldExpand);
const header = content.previousElementSibling;
const toggle = header ? header.querySelector('.tree-toggle') : null;
if (toggle) toggle.textContent = shouldExpand ? '▼' : '▶';
if (content.dataset.sectionPath) saveSectionExpanded(content.dataset.sectionPath, shouldExpand);
});
if (btnEl) btnEl.textContent = shouldExpand ? 'Collapse all' : 'Expand all';