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
+54 -124
View File
@@ -223,8 +223,10 @@
/* Course-outline sidebar tree - scoped under .lesson-sidebar so
these don't collide with this page's own .lesson-title (the H1)
and similarly-named rules. Mirrors course_dashboard.html's tree
styling (shared macro, see _course_tree.html) at a denser size
for the narrower column. */
styling (shared macro, see _course_tree.html): a flat list, no
per-row background/border, a small leading chevron that doubles
as the expand affordance and rotates via :has() rather than any
JS touching it directly. */
.lesson-sidebar .tree-container-header {
display: flex;
justify-content: flex-end;
@@ -246,40 +248,33 @@
margin-top: 6px;
}
.lesson-sidebar .tree-item {
margin-bottom: 5px;
margin-bottom: 0;
}
.lesson-sidebar .tree-header {
background: var(--bg-tertiary);
padding: 10px 12px;
padding: 5px 6px;
border-radius: var(--radius);
cursor: pointer;
display: flex;
align-items: center;
justify-content: space-between;
transition: background 0.3s;
border-left: 3px solid #666;
gap: 6px;
transition: background 0.15s;
}
.lesson-sidebar .tree-header:hover {
background: var(--bg-tertiary-hover);
background: var(--bg-tertiary);
}
.lesson-sidebar .tree-header.directory {
border-left-color: var(--accent);
}
.lesson-sidebar .tree-title {
display: flex;
align-items: center;
gap: 8px;
flex: 1;
min-width: 0;
}
.lesson-sidebar .tree-icon {
font-size: 1.1em;
width: 18px;
text-align: center;
.lesson-sidebar .tree-toggle-icon {
display: inline-block;
width: 12px;
flex-shrink: 0;
color: var(--accent);
font-size: 0.8em;
transition: transform 0.15s ease;
}
.lesson-sidebar .tree-header:has(+ .tree-content.expanded) .tree-toggle-icon {
transform: rotate(90deg);
}
.lesson-sidebar .tree-name {
font-weight: 500;
font-weight: 600;
flex: 1;
min-width: 0;
overflow: hidden;
@@ -288,132 +283,72 @@
font-size: 0.9em;
}
.lesson-sidebar .tree-stats {
font-size: 0.75em;
font-size: 0.7em;
color: var(--text-muted);
margin-left: 8px;
flex-shrink: 0;
white-space: nowrap;
}
.lesson-sidebar .tree-toggle {
background: none;
border: none;
color: var(--text-primary);
font-size: 1.1em;
cursor: pointer;
padding: 4px;
border-radius: 3px;
transition: background 0.3s;
}
.lesson-sidebar .tree-toggle:hover {
background: #555;
}
.lesson-sidebar .tree-content {
margin-left: 16px;
margin-top: 5px;
margin-left: 14px;
margin-top: 1px;
display: none;
}
.lesson-sidebar .tree-content.expanded {
display: block;
}
.lesson-sidebar .lesson-item {
background: var(--bg-tertiary);
padding: 8px 12px;
padding: 4px 6px;
border-radius: var(--radius);
margin-bottom: 5px;
display: flex;
flex-wrap: wrap;
justify-content: space-between;
align-items: center;
transition: all 0.2s;
cursor: pointer;
border-left: 3px solid #666;
position: relative;
overflow: hidden;
gap: 4px;
}
.lesson-sidebar .lesson-item:hover {
border-left-color: var(--accent);
background: var(--bg-tertiary-hover);
}
.lesson-sidebar .lesson-item.completed {
border-left-color: var(--success);
}
.lesson-sidebar .lesson-item.in-progress {
border-left-color: var(--accent);
}
.lesson-sidebar .lesson-item.current {
border-left-color: var(--accent);
background: var(--bg-tertiary-hover);
cursor: default;
font-weight: 600;
}
.lesson-sidebar .lesson-progress-track {
position: absolute;
bottom: 0;
left: 0;
right: 0;
height: 3px;
background: rgba(255, 255, 255, 0.08);
}
.lesson-sidebar .lesson-progress-fill {
height: 100%;
background: var(--accent);
}
.lesson-sidebar .lesson-item.completed .lesson-progress-fill {
background: var(--success);
}
.lesson-sidebar .watched-badge {
font-size: 0.7em;
background: var(--bg-primary);
color: var(--accent);
padding: 1px 6px;
border-radius: 3px;
border: 1px solid var(--accent);
white-space: nowrap;
}
.lesson-sidebar .lesson-item .lesson-title {
margin-bottom: 0;
display: flex;
align-items: center;
gap: 6px;
flex: 1;
min-width: 0;
transition: background 0.15s;
cursor: pointer;
}
.lesson-sidebar .lesson-item:hover {
background: var(--bg-tertiary);
}
.lesson-sidebar .lesson-item.current {
background: var(--bg-tertiary);
cursor: default;
}
.lesson-sidebar .watched-badge {
font-size: 0.65em;
color: var(--accent);
white-space: nowrap;
flex-shrink: 0;
}
.lesson-sidebar .lesson-icon {
font-size: 1em;
font-size: 0.85em;
color: var(--text-muted);
flex-shrink: 0;
}
.lesson-sidebar .lesson-name {
font-size: 0.85em;
font-size: 0.8em;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
flex: 1;
min-width: 0;
}
.lesson-sidebar .lesson-meta {
font-size: 0.75em;
color: var(--text-muted);
display: flex;
align-items: center;
gap: 4px;
flex-shrink: 0;
}
.lesson-sidebar .lesson-type {
display: none;
.lesson-sidebar .lesson-item.current .lesson-name {
font-weight: 600;
color: var(--accent);
}
.lesson-sidebar .lesson-duration {
font-size: 0.75em;
font-size: 0.7em;
color: var(--text-muted);
white-space: nowrap;
flex-shrink: 0;
}
.lesson-sidebar .status-icon {
font-size: 1.1em;
font-weight: bold;
font-size: 0.9em;
flex-shrink: 0;
}
.lesson-sidebar .status-icon.completed {
color: var(--success);
}
.lesson-sidebar .status-icon.pending {
color: var(--text-muted);
}
video, audio {
width: 100%;
max-width: 100%;
@@ -1262,12 +1197,13 @@
// ---- Course outline sidebar (shared tree markup/macro with the
// dashboard's loaded-course view - see _course_tree.html) ----
// The chevron's rotation is pure CSS (:has(+ .tree-content.
// expanded) in this file's tree styles), so toggling this one
// class is the entire job - nothing here ever touches the icon.
function toggleTree(element) {
const content = element.nextElementSibling;
const toggleBtn = element.querySelector('.tree-toggle');
if (content && content.classList.contains('tree-content')) {
const isExpanded = content.classList.toggle('expanded');
if (toggleBtn) toggleBtn.textContent = isExpanded ? '▼' : '▶';
if (content.dataset.sectionPath) saveSectionExpanded(content.dataset.sectionPath, isExpanded);
}
}
@@ -1292,9 +1228,6 @@
function expandSection(content) {
content.classList.add('expanded');
const header = content.previousElementSibling;
const toggleBtn = header ? header.querySelector('.tree-toggle') : null;
if (toggleBtn) toggleBtn.textContent = '▼';
}
(function restoreExpandedSections() {
@@ -1334,9 +1267,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';