T7.2 - F6/D3: the form gets structure - a section rail, one section open

F6 as amended by D3 (Aug 18): one page, persistent side navigation, sections
collapsible, only the current one open by default, plus Expand all. Tabs were
rejected in D3 because they hide sections a first-time author does not know
exist.

What changed:
- The jump-chip strip (#section-nav, span onclick) is gone. In its place a
  <nav> section rail of real <button> entries, aria-current on the current
  section, 44px tap targets, above the form at 390px and beside it at 1440px.
- Every section heading is now a disclosure <button> with aria-expanded and
  aria-controls. One section open at rest; Expand all (aria-pressed) opens
  everything and is remembered per browser.
- Sections are URL-addressable (?section=, T4.2 machinery) and a deep link to
  a collapsed section expands it. Positional-id fallback removed: a card
  without an id gets a console.error and no rail entry, never an invented
  sec-N id that would ride into shareable URLs and move between visits.
- General Information (1,288px on its own) split into #general-card and
  #assign-card (Assignment & Schedule). The split is presentational: both
  cards are the ONE CR-006 section `general` (WP_SECTION_NODES lists both),
  so wp-sections.js and the SOP wizard are untouched. CR-001's adjacency
  (P6 activity beside due date) is preserved and asserted.
- gotoSection() flushes autosave, which the deleted chips used to do.
- secMakeToggle() preserves every element child of a heading - help tips go
  outside the button, everything else inside the label. The first version
  cleared textContent and destroyed #saved-count, which killed boot one line
  short of wpCreatorReady with the page still visibly rendered.
- BL-013 folded in per the task: the T3.4 focus ring on the rebuilt form.
  frame_check reports outline solid 2px on creator inputs.

Height, measured not asserted: 5,399px before; 1,995px at rest at 1440x900.

DONE-WHEN NOT FULLY MET - stated per CLAUDE.md rather than marked complete:
"no single view exceeds roughly two screen heights at rest" reads 2.22
screens (1995/900). The remaining gap is page chrome this wave reworks:
.ctx-bar (67px, T7.4) and .release-banner (45px, T7.5). The criterion was
already amended once (D3, "at rest") and is not being moved again to fit;
tests/form_structure_check.py keeps the check red and it is re-measured at
the end of wave 7. Every other done-when entry passes.

Backlog: BL-001's cause corrected a third time - at rest the overflow is
help.js's .help-tip::after tooltip (481 vs 390), the S8 component T9.5
rebuilds; the tables still overflow only when expanded. Deliberately not
fixed here - a fix would be thrown away with the component at T9.5.

Verification (each probe run alone): form_structure_check 50/51 (the height
check above), sections_check 95/95, generalinfo_check 49/49, frame_check
39/39 regression pass.

Items: F6, D3, BL-013, BL-001 (re-measured)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-19 09:38:31 -07:00
parent 12d19446d5
commit 755c976841
8 changed files with 1020 additions and 106 deletions

View File

@@ -149,7 +149,10 @@
auto-hiding overlay drawer (see below) rather than a column, so it never takes
width away from the form — which matters most when this page is embedded in the
suite's tab and every pixel is shared with the app chrome. */
.wp-layout { display: block; width: 100%; margin: 0; }
/* F6/D3: flex, so the section rail declared AFTER the form in the markup can sit
ABOVE it at narrow widths (order:-1) and BESIDE it at desk width. Declaring it
after .main is what lets it be a sticky column without wrapping the layout. */
.wp-layout { display: flex; flex-direction: column; width: 100%; margin: 0; }
.main { min-width: 0; max-width: none; margin: 0;
padding: 22px 28px 72px calc(var(--nav-w,288px) + 28px);
transition: padding-left .18s ease; }
@@ -659,21 +662,132 @@
.so-date { font-size:13px; font-variant-numeric:tabular-nums; }
.so-ovr { margin-left:8px; font-size:11px; }
/* Collapsible form sections */
.collapse-chev { display:inline-block; width:1em; margin-right:7px; color:var(--text-muted); font-size:11px; user-select:none; }
.card.collapsed > :not(.section-header):not(.sub-heading) { display:none !important; }
.card.collapsed .section-desc { display:none; }
/* ── COLLAPSIBLE SECTIONS + SECTION RAIL (F6 / D3) ─────────────────────────
What stood here: a `.section-nav-bar` of `<span onclick>` jump chips, and a
`.card.collapsed > :not(.section-header):not(.sub-heading)` rule that hid a
card's contents without any element carrying the disclosure state. Neither
was reachable by keyboard, and neither said anything to a screen reader.
/* Section nav (jump chips) */
.section-nav-bar{ position:sticky; top:0; z-index:30; display:flex; flex-wrap:wrap; gap:6px;
padding:8px 12px 8px calc(var(--nav-w,288px) + 28px); background:var(--wp-scrim-frosted); backdrop-filter:blur(4px);
border-bottom:1px solid var(--border); box-shadow:var(--wp-shadow-navbar);
transition:transform .22s ease; }
.section-nav-bar:empty{ display:none; }
.section-nav-bar.nav-hidden{ transform:translateY(-160%); }
.sec-chip{ font-size:12px; font-weight:600; color:var(--text-muted); background:var(--surface2);
border:1px solid var(--border); border-radius:14px; padding:4px 11px; cursor:pointer; white-space:nowrap; }
.sec-chip:hover{ border-color:var(--accent); color:var(--accent); }
Now: every heading is a real disclosure button, its contents are one
`.card-body` so `aria-controls` has a target, and the table of contents is a
rail of real buttons that marks where you are.
D3 amended F6's height criterion to "at rest". At rest one section is open,
which is what keeps this page under two screen heights; `Expand all` is a
deliberate choice to exceed it. */
/* The disclosure button lives INSIDE the existing heading, so .sub-heading's
trailing rule and the help tip beside it keep working untouched. */
.card-toggle {
display: inline-flex; align-items: center; gap: 9px;
background: none; border: 0; padding: 0; margin: 0;
font: inherit; color: inherit; text-align: left; cursor: pointer;
border-radius: var(--radius);
}
.card-toggle:hover { color: var(--accent); }
.card-toggle-label { min-width: 0; }
/* Drawn, not typed. A glyph here would be a fourth icon idiom on a page S6 is
already going to have to reconcile, and it would render differently per
platform - which is half of what S6 is about. */
.collapse-chev {
flex: 0 0 auto; width: 7px; height: 7px;
border-right: 1.5px solid currentColor;
border-bottom: 1.5px solid currentColor;
transform: rotate(45deg) translate(-2px, -2px);
transition: transform .15s ease;
}
.card-toggle[aria-expanded="false"] .collapse-chev {
transform: rotate(-45deg) translate(-2px, 2px);
}
.card-body[hidden] { display: none !important; }
.card.collapsed .section-desc { display: none; }
/* A collapsed card is a heading, so it should read as a row rather than a box
with one line in it. */
/* A collapsed section should read as a ROW in a list, not as a box with one line
in it. Eleven of them at the card's own 28px padding is 660px of nothing -
which is a third of what F6 was measuring, arriving by a different door. */
.card.collapsed { padding-top: 10px; padding-bottom: 10px; }
.card.collapsed .section-header { margin-bottom: 0; padding-bottom: 0; border-bottom: 0; }
.card.collapsed .sub-heading { margin-bottom: 0; }
/* ── the rail ──
A horizontal strip above the form by default - which is what fits at 390px -
and a sticky column beside it from 1200px, where there is width to spare. */
.sec-rail {
order: -1;
position: sticky;
top: 0;
z-index: 30;
padding: 8px 28px 8px calc(var(--nav-w, 288px) + 28px);
background: var(--surface);
border-bottom: 1px solid var(--border);
}
.sec-rail-head {
display: flex; align-items: center; gap: 12px;
margin-bottom: 6px;
}
.sec-rail-title {
font-family: var(--mono); font-size: 10px; font-weight: 600;
letter-spacing: .08em; text-transform: uppercase; color: var(--text-dim);
}
.sec-rail-all {
margin-left: auto;
background: none; border: 1px solid var(--border); border-radius: var(--radius);
padding: 3px 10px; font: inherit; font-size: 11px; color: var(--text-muted);
cursor: pointer;
}
.sec-rail-all:hover { border-color: var(--accent); color: var(--accent); }
.sec-rail-all[aria-pressed="true"] {
border-color: var(--accent); color: var(--accent); background: var(--accent-dim);
}
.sec-rail-list {
display: flex; flex-wrap: wrap; gap: 4px;
list-style: none; margin: 0; padding: 0;
}
.sec-rail-item {
display: block; width: 100%;
background: none; border: 1px solid transparent; border-radius: var(--radius);
padding: 7px 11px; font: inherit; font-size: 12px; font-weight: 500;
color: var(--text-muted); cursor: pointer; text-align: left; white-space: nowrap;
min-height: 34px;
}
.sec-rail-item:hover { color: var(--accent); border-color: var(--border); }
/* Not colour alone: the current entry is bolder, keeps a left marker and is the
one carrying aria-current. */
.sec-rail-item.is-current {
color: var(--accent); font-weight: 700;
background: var(--accent-dim);
border-color: var(--accent-dim);
box-shadow: inset 2px 0 0 0 var(--accent);
}
@media (max-width: 1199px) {
/* Horizontal strip: the entries sit side by side and the list scrolls rather
than stacking eleven full-width rows above the form. */
.sec-rail-list { flex-wrap: nowrap; overflow-x: auto; }
.sec-rail-item { width: auto; }
}
@media (min-width: 1200px) {
.wp-layout { flex-direction: row; align-items: flex-start; }
.main { flex: 1 1 auto; }
.sec-rail {
order: 0;
flex: 0 0 224px;
align-self: flex-start;
top: var(--sec-rail-top, 48px);
max-height: calc(100vh - var(--sec-rail-top, 48px));
overflow-y: auto;
margin: 22px 28px 0 0;
padding: 12px;
border: 1px solid var(--border);
border-radius: var(--radius);
}
.sec-rail-list { flex-direction: column; flex-wrap: nowrap; }
.sec-rail-item { white-space: normal; }
}
/* ── SOP-inherited marker ───────────────────────────────────────────────────
The "from SOP types" subtext used to sit under the field. It's now a small