feat(layout): stick the title row too, and chain the sticky offsets

"All items · N items" scrolled away like the pager did. It now sticks at
the scrollport top (padding-top/negative margin cancel .content's padding
so nothing shows through the gap), and the three things below it offset
by --hdr-h: batch bar, pagination, and the table header (--hdr-h + --pag-h).
Both vars are measured on .content in renderEntries.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
r-zakarya
2026-07-22 11:31:52 +01:00
parent 60cf26b259
commit 24d74df2ab
2 changed files with 19 additions and 7 deletions
+13 -4
View File
@@ -800,7 +800,15 @@ input[type="range"]::-webkit-slider-thumb {
.dropdown-item svg { color: var(--text-dim); } .dropdown-item svg { color: var(--text-dim); }
.content { flex: 1; padding: 24px 32px; overflow-y: auto; } .content { flex: 1; padding: 24px 32px; overflow-y: auto; }
.content-header { display: flex; align-items: baseline; gap: 12px; margin-bottom: 20px; } /* Sticky title row. The negative top margin cancels .content's padding-top so
nothing scrolls through the gap above it. Everything else that sticks
(batch bar, pagination, table header) offsets by --hdr-h. */
.content-header {
display: flex; align-items: baseline; gap: 12px; margin-bottom: 20px;
position: sticky; top: 0; z-index: 12;
background: var(--bg);
padding-top: 24px; margin-top: -24px;
}
.content-header h2 { font-size: 22px; font-weight: 600; margin: 0; } .content-header h2 { font-size: 22px; font-weight: 600; margin: 0; }
.content-meta { color: var(--text-faint); font-size: 12px; } .content-meta { color: var(--text-faint); font-size: 12px; }
@@ -1000,7 +1008,7 @@ input[type="range"]::-webkit-slider-thumb {
/* Batch action bar (appears when selection is non-empty) */ /* Batch action bar (appears when selection is non-empty) */
.batch-bar { .batch-bar {
position: sticky; position: sticky;
top: -1px; top: var(--hdr-h, 0px);
z-index: 10; z-index: 10;
display: flex; align-items: center; gap: 8px; display: flex; align-items: center; gap: 8px;
padding: 10px 14px; padding: 10px 14px;
@@ -2581,6 +2589,7 @@ body[data-editor-position="center"]:has(#settingsPanel.is-open)::before {
.nav-item { justify-content: center; padding: 8px; } .nav-item { justify-content: center; padding: 8px; }
.topbar { padding: 12px 16px; } .topbar { padding: 12px 16px; }
.content { padding: 16px; } .content { padding: 16px; }
.content-header { padding-top: 16px; margin-top: -16px; }
.slideover { width: 100%; } .slideover { width: 100%; }
/* On a narrow viewport the slideover fills the screen — no point /* On a narrow viewport the slideover fills the screen — no point
pushing the shell, the table isn't visible anyway. */ pushing the shell, the table isn't visible anyway. */
@@ -2676,7 +2685,7 @@ body[data-editor-position="center"]:has(#settingsPanel.is-open)::before {
border-bottom: 1px solid var(--border); border-bottom: 1px solid var(--border);
background: var(--bg-elev); background: var(--bg-elev);
user-select: none; user-select: none;
position: sticky; top: var(--pag-h, 0px); position: sticky; top: calc(var(--hdr-h, 0px) + var(--pag-h, 0px));
z-index: 1; z-index: 1;
} }
.entry-table th.is-sortable { cursor: pointer; } .entry-table th.is-sortable { cursor: pointer; }
@@ -2968,7 +2977,7 @@ input[type="password"]::-ms-clear {
is pushed down by --pag-h (measured in renderEntries). */ is pushed down by --pag-h (measured in renderEntries). */
.pagination { .pagination {
position: sticky; position: sticky;
top: 0; top: var(--hdr-h, 0px);
z-index: 2; z-index: 2;
background: var(--bg); background: var(--bg);
display: flex; align-items: center; gap: 6px; display: flex; align-items: center; gap: 6px;
+6 -3
View File
@@ -2296,13 +2296,16 @@ function renderGrid() {
const start = (state.currentPage - 1) * state.pageSize; const start = (state.currentPage - 1) * state.pageSize;
const pageList = list.slice(start, start + state.pageSize); const pageList = list.slice(start, start + state.pageSize);
// Sticky offsets: the pagination bar parks under the title row, and the
// table header under both. Measured, so a wrapped pager still lines up.
const content = $('.content');
content.style.setProperty('--hdr-h', $('.content-header').offsetHeight + 'px');
if (total > 10) { if (total > 10) {
const pag = renderPagination(total, totalPages); const pag = renderPagination(total, totalPages);
grid.appendChild(pag); grid.appendChild(pag);
// The table header sticks below the sticky pagination bar, not under it. content.style.setProperty('--pag-h', pag.offsetHeight + 'px');
grid.style.setProperty('--pag-h', pag.offsetHeight + 'px');
} else { } else {
grid.style.setProperty('--pag-h', '0px'); content.style.setProperty('--pag-h', '0px');
} }
if (state.viewMode === 'table') { if (state.viewMode === 'table') {