feat(phase 4e): collapsible detail-page sections with smart defaults + localStorage
Each major section on /i/{path} is now wrapped in a native <details>
element with a smart-default `open` attribute. The inline JS overrides
the default from localStorage so m's per-item collapse state survives
reloads.
## Smart defaults (server-rendered open attr)
- Tasks: open if any linked calendar has >=1 open VTODO
- Issues: open if total open issues <= 10
- Documents: open if dated link count <= 5
- Public listing: closed by default
## Persistence
localStorage["projax.section." + item_id + "." + section] = "open" | "closed".
Inline JS reads on boot, writes on toggle. The "reset section state" link
in the form actions wipes every key for the current item and reloads —
smart defaults take over again.
## What's not collapsed
- Title + status/tags chip line (always visible breadcrumb)
- The inline edit form's standard fields (title/slug/parents/content)
Only the auxiliary sections — Tasks, Issues, Documents, Public listing —
collapse. m always sees what an item *is* without expanding anything.
## Tests
- TestDetailIncludesSectionToggleScript — script fragments ship
- TestDetailSectionsWrappedInDetails — every section has its wrapper
- TestDetailDocumentsClosedDefaultsWhenManyItems — 0-doc baseline is open
## docs/design.md
New section before §15 documents thresholds, persistence semantics, and
the non-collapsible carve-outs.
This commit is contained in:
@@ -700,3 +700,52 @@ fieldset.public-listing label { margin-top: 8px; }
|
||||
padding: 4px 10px; font-size: 0.9em; cursor: pointer; min-height: 0;
|
||||
}
|
||||
.public-screenshot-add:hover { background: var(--accent); color: var(--accent-fg); }
|
||||
|
||||
/* --- Detail-page collapsibles (Phase 4e) --- */
|
||||
details.proj-section {
|
||||
margin: 16px 0;
|
||||
border-top: 1px solid var(--border);
|
||||
padding-top: 8px;
|
||||
}
|
||||
details.proj-section[open] {
|
||||
border-bottom: 1px dotted var(--border);
|
||||
padding-bottom: 8px;
|
||||
}
|
||||
details.proj-section > summary.proj-section-summary {
|
||||
cursor: pointer;
|
||||
list-style: none;
|
||||
font-size: 1.05em;
|
||||
font-weight: 600;
|
||||
padding: 4px 0 4px 22px;
|
||||
position: relative;
|
||||
user-select: none;
|
||||
}
|
||||
details.proj-section > summary.proj-section-summary::-webkit-details-marker {
|
||||
display: none;
|
||||
}
|
||||
details.proj-section > summary.proj-section-summary::before {
|
||||
content: "▸";
|
||||
position: absolute;
|
||||
left: 4px;
|
||||
color: var(--muted);
|
||||
font-size: 0.9em;
|
||||
transition: transform 0.12s;
|
||||
display: inline-block;
|
||||
}
|
||||
details.proj-section[open] > summary.proj-section-summary::before {
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
details.proj-section > summary.proj-section-summary:hover { color: var(--accent); }
|
||||
details.proj-section > summary.proj-section-summary small { font-weight: 400; }
|
||||
|
||||
.proj-section-reset {
|
||||
color: var(--muted);
|
||||
font-size: 0.85em;
|
||||
text-decoration: none;
|
||||
margin-left: auto;
|
||||
}
|
||||
.proj-section-reset:hover { color: var(--accent); text-decoration: underline; }
|
||||
.visually-hidden {
|
||||
position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
|
||||
overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user