Nav tree scroll performance A/B (typology icons)
This page records a numeric A/B investigation of left-rail scroll smoothness on the DevCentr docs hub (tip 7d69041, nav-typology solids / SoftNav seamlessness), comparing typology icons ON vs OFF and several cheap variants against the shallower HCI-Nerdz control site.
Verdict (short)
-
Typology
<svg><use>icons are a real paint cost when many are visible (expand-all: Paint ~99 ms with icons vs ~36 ms without; Layerize ~123 ms vs ~38 ms). -
On a normal deep Tools page only ~19 of 246 icons are visible; the other ~227 sit in collapsed ancestors. In that default shape, icon removal trims scroll TaskDuration only modestly (~98 ms → ~77 ms in headless CDP) and does not close the gap to HCI.
-
The dominant HCI gap is forest size / DOM, not icons alone: DevCentr ~360
nav-item`s / ~1963 DOM nodes / 45 nested `ul.nav-listvs HCI ~38 items / ~361 nodes / 8 lists. -
SoftNav + debounced
sessionStoragescroll persist is weak as a scroll-jank cause at these sizes. -
Nested horizontal overflow is already defeated (
overflow-x: hiddenon the SoftNav rail); forcingoverflow-x: autodid not recreate a clear headless regression. -
Production icons stay ON. Hub helper gains a temporary kill-switch
site.keys.nav_typology_icons: 'false'for rebuild A/B; no permanent icon removal shipped.
Method
What was measured
| Surface | Detail |
|---|---|
Local tip build |
Existing Antora output under |
Live Pages |
|
HCI control |
|
Browser |
Chrome via Puppeteer 25 headless on Ryan’s Windows machine ( |
Harness |
|
Variants
| Id | Mutation |
|---|---|
A icons ON |
As built ( |
B icons removed |
Runtime |
C |
Icons remain in DOM; not painted. |
C2 CSS marks |
Remove SVG; inject small |
D SoftNav/scroll persist off |
Clone scroll panel (drop listeners); stub |
E expand-all |
Every |
E−svg expand-all, no SVG |
Expand-all then remove icons. |
F force |
Re-introduce nested horizontal scrollport + nowrap labels. |
G strip filter/shadow on nav chrome |
|
Test page for DevCentr: deep Tools / Agent Rules (/tools/agent-rules/) with the full forest rail.
Structural counts (load)
| Page | nav-items | typology SVG | visible SVG | collapsed SVG | DOM nodes (page) | nested ul.nav-list |
|---|---|---|---|---|---|---|
Local Tools/agent-rules A |
360 |
246 |
19 |
227 |
1963 |
45 |
Local B (icons removed) |
360 |
0 |
0 |
0 |
1471 |
45 |
Local home A (mostly collapsed) |
360 |
246 |
2 |
244 |
1977 |
45 |
Live Tools/agent-rules A |
360 |
246 |
19 |
227 |
1963 |
45 |
HCI control |
38 |
0 |
0 |
0 |
361 |
8 |
Removing icons drops ~492 DOM nodes (svg + use each), matching the HTML class/use counts on tip and live.
Metrics tables
Scroll window (headless rAF, ~2 s, 64 px steps)
Headless frame pacing pinned near the display cadence (~144 FPS, p95 ~7 ms) for every variant — not a useful jank discriminator. Prefer CDP TaskDuration / ScriptDuration deltas during that window and the tracing table below.
| Variant | TaskDuration ms | ScriptDuration ms | SVG tot/vis | DOM nodes |
|---|---|---|---|---|
Local A icons ON |
98.5 |
9.2 |
246/19 |
1963 |
Local B icons removed |
77.5 |
7.9 |
0/0 |
1471 |
Local C display:none |
83.6 |
8.1 |
246/0 |
1964 |
Local C2 CSS marks |
94.6 |
8.8 |
0/0 |
1472 |
Local D SoftNav/scroll off |
89.4 |
8.7 |
246/19 |
1963 |
Local E expand-all |
158.0 |
9.0 |
246/246 |
1963 |
Local E2 collapse non-path |
94.8 |
9.0 |
246/19 |
1963 |
Local F overflow-x auto |
80.1 |
7.8 |
246/19 |
1964 |
Local G no shadow/filter |
87.8 |
8.6 |
246/19 |
1964 |
Local home A (no rail scroll room) |
52.1 |
1.1 |
246/2 |
1977 |
Live A |
80.4 |
8.0 |
246/19 |
1963 |
Live B icons removed |
82.6 |
8.0 |
0/0 |
1471 |
HCI control |
48.0 |
1.1 |
0/0 |
361 |
Tracing during wheel scroll (expand stress)
When the whole forest is open, typology SVGs dominate paint/layerize:
| Variant | Paint ms | Layerize ms | GPUTask ms | iconLayout×40 ms |
|---|---|---|---|---|
E expand-all + icons |
99.5 |
123.2 |
144.3 |
17.5 |
E expand-all, icons removed |
35.9 |
38.0 |
101.7 |
0.1 |
A default path (19 visible) |
~0 in selected timeline slice |
Layerize ~9.8 |
GPUTask ~35 |
3.9 |
B default path, icons removed |
~0 |
Layerize ~8.8 |
GPUTask ~29 |
0.1 |
HCI |
29.9 (many small paints) |
7.6 |
105.3 |
0.1 |
Forced flex getComputedStyle on every nav row (×10): DevCentr ~24–42 ms vs HCI ~2.7 ms — another forest-size signal independent of icons.
Debounced scroll sessionStorage writes (×400 microbench): ~0.7–3 ms total — cheap relative to paint when expanded.
Theory scorecard
| Theory | Verdict | Evidence |
|---|---|---|
Scroll lag from number of visible and/or hidden SVGs ( |
Supported (visible) / weak (hidden-only) |
Expand-all Paint/Layerize ~2.8× / 3.2× with icons. Default path: only 19 visible; TaskDuration 98 → 77 when removed. Hidden SVGs still cost ~492 DOM nodes. |
Custom JS (SoftNav, scroll restore, site-nav-tree) slows middle-button / rail scroll |
Weak |
Disabling persist/listeners: TaskDuration 89 vs 98. Persist microbench tiny. SoftNav is about navigation swaps, not continuous wheel cost. |
Nested horizontal scroll in nav causes jank |
Rejected (current CSS) / historical |
Measured |
Deep forest / many nodes vs HCI shallower zero-SVG layout |
Supported (primary) |
360 vs 38 items; 1963 vs 361 nodes; flex-row style reads ~10×; HCI TaskDuration ~48 vs DevCentr ~80–98 even after icon removal. |
SoftNav flash / nav wipe |
Already fixed on tip; live matches |
No |
Page-change slowness vs DeepWiki |
Out of scope (not timed here) |
Scroll-focused run. SoftNav |
DOM / nested |
Supported |
B still has 360 items / 45 lists; TaskDuration remains ~77 vs HCI 48. |
|
Weak / needed for correctness |
Sticky + panel scrollport is the intentional fix for coupled page scroll; not singled out as jank vs HCI in these traces. |
Scroll listener / |
Weak |
Debounced 100 ms; microbench negligible; D variant small delta. |
|
Weak |
G variant TaskDuration 88 vs 98; brand CSS already avoids heavy nav shadows for tiles elsewhere. |
Font/icon flex layout per row |
Supported as forest amplifier |
Every row is |
CSS |
CSS marks ≈ no-icon DOM when collapsed; SVG hurts when expanded |
C2 TaskDuration ~95 (noise vs A). Expand stress: SVG paint is the clear loser vs no icons. |
Recommendations
-
Keep typology icons in production for the default collapsed/path-expanded forest — the UX win is large; the default-path scroll delta is modest.
-
Prefer lazy / visible-only icons if expand-all (or readers who open many roots) feels janky:
-
emit SVG only for
is-activeancestry + visible rows, or -
swap to CSS-only marks for off-screen / collapsed subtrees, or
-
keep a single sprite but avoid per-row
<use>until expand (JS enhance).
-
-
Treat forest depth / node count as the main HCI gap: curation (
site-nav-treeinclude allowlist), fewer anonymous wrappers, and avoiding “expand everything” are higher leverage than deleting icons. -
Keep horizontal overflow clipped; do not reintroduce nested
overflow-x: autoon the SoftNav rail. -
Leave SoftNav + scroll restore on; optimize only if profiling shows listener time on real GPU wheels (not seen as primary here).
-
Hub A/B rebuild flag (temporary):
site:
keys:
nav_typology_diataxis: 'true'
# temporary lab kill-switch — omit or any value other than 'false' keeps icons
nav_typology_icons: 'false'
Implemented in supplemental-ui/helpers/nav-typology-icon.js (typologyIconsEnabled). Default remains icons on.
Limits (honest)
-
Headless Chrome rAF scroll hit a ~144 FPS ceiling; it under-reports interactive middle-button / trackpad jank on real GPUs.
-
Runtime DOM removal approximates a rebuild with
nav_typology_icons: 'false'but is not identical to a clean Antora pass (listener identity, SoftNav region wiring). -
Tracing
Layoutcounters were often 0 in selected categories while Paint/Layerize/GPUTask still moved — read the paint/layerize columns for expand stress. -
Live Pages matched tip icon counts for this run; if Pages lags a future tip, re-check before blaming icons.
Related
-
Antora UI branding — Valentus supplemental chrome, typology accent notes.
-
Antora essential extensions —
site-nav-treeforest curation. -
Lab scripts:
scripts/nav-scroll-ab-bench.mjs,scripts/nav-scroll-cost-bench.mjs. -
Raw JSON (repo images copy):
nav-scroll-ab/results.json,nav-scroll-ab/cost-results.json.