Server settings: keep the sub-menu in view while the page scrolls #699

Closed
opened 2026-10-03 01:59:10 +00:00 by tiagoagueda · 0 comments
Owner

On a long Server settings page the list of sections scrolls away with the page, and the way to another section is back at the top. The maintainer's list asks for it to stay put: "Server settings: sub-menu floating always on top-left."

What exists today

  • The frame is server/base.html: a lg:flex lg:items-start lg:gap-10 row holding an <aside class="mb-6 lg:mb-0 lg:w-56 lg:shrink-0"> and the page's column (src/postulo/templates/server/base.html:7-15). The aside is not sticky. Its content is the area's name as a paragraph and {% server_sidebar %}, which draws partials/sidebar.html: a <nav> of ten sections (core/server_sections.py: Overview, People, Sign-in, Email, Plugins, Capture, Data protection, Logs, Defaults, Design), a column from lg up and a horizontal strip that scrolls sideways below it.
  • The two other frames with a sidebar already stick. Your details and Your career give the aside lg:sticky lg:top-[calc(var(--header-height,3.5rem)+1rem)] (templates/accounts/profile.html:30, templates/resume/overview.html:31), where --header-height is the masthead's height as app.js measures it, with 3.5rem where scripts do not run (app.js:1143-1157, base.html comment on the floating header, #195). tests/test_header.py::test_the_header_floats_and_everything_that_has_to_clear_it_reads_one_height already pins the class on the Your details page.
  • Settings has the same plain aside as Server settings (templates/settings/base.html:9-20); the maintainer's list names Server settings only.
  • Several Server settings pages are long: Overview, Logs, People with a crowd, Defaults with its list of every language, Design. On all of them the person who has scrolled has to return to the top to change section.
  • A different fault on the same sidebar is open and is not this one: four People sub-pages mark no section as current (#486).
  • The aside is lg:items-start, which is what lets a sticky child keep its own height inside a flex row; the frame needs nothing else to start sticking.

Checked against main at f09b6010b.

Proposal

  1. Stick the aside from lg up, with the pattern the other two frames use: lg:sticky lg:top-[calc(var(--header-height,3.5rem)+1rem)] on the <aside> in server/base.html. Below lg the sidebar stays a strip above the page, as it is.
  2. Never taller than the window. Ten rows and a label are about 400 pixels, which is more than a short window at lg leaves under the masthead (a 1024 by 500 tablet in landscape). So the aside also takes lg:max-h-[calc(100dvh-var(--header-height,3.5rem)-2rem)] and lg:overflow-y-auto, and the last row can always be reached by scrolling the aside or by Tab. A little padding on the scrolling box so a focus ring on a link at its edge is not clipped.
  3. Logical sides only. "Top-left" is the start edge: the aside is the first item of the row, so in a right-to-left language it is at the right, and nothing here names a side (tests/test_template_lint.py).
  4. Rebuild the stylesheet (npm run build:css) and commit it; the new utilities exist in no compiled CSS yet (tests/test_stylesheet.py).

Decisions

  • Settings too. The list names Server settings only, but settings/base.html is the same markup with the same problem, and two sidebars of one design that stick in one place and not the other would be an arbitrary difference. Recommended: the same change to settings/base.html in the same commit, with the issue's tests covering both; if the maintainer wants Server settings alone it is one template less.
  • Height cap. A cap with its own scroll, or let a tall sidebar run past the window. Recommended: the cap: a sticky box taller than the window cannot be scrolled to its end by the page, which would hide the last sections exactly where this issue wants them reachable. Settings can grow past ten rows, because a plugin may register a section there; the Server list is fixed.
  • Not the label. The area's name above the list stays a static label, as settings/base.html explains (#227). Showing a section title there, as #677 proposes for the pages made of anchors, does not apply: these entries are pages, and the current one is already marked.

Builds on

#195 (the floating header and --header-height), #282 and #299 (the masthead and its measured height), #320 (the frame takes the screen and each page keeps its own measure). Related: #486 (the current section is not marked on four People sub-pages), #677 (the sub-menu of Your details and Your career).

Done when

  • At 1280 by 800, after scrolling a long Server settings page to its foot (Logs or People with a crowd of accounts), the sidebar is still in view under the masthead, and a click on another section goes there. A new case in tests/e2e/test_sticky_header.py, with the administrator fixture the People e2e file uses (tests/e2e/test_people_menu.py:88).
  • At 1024 by 480 the aside is no taller than the space under the masthead, scrolls inside itself, and its last link can be reached by Tab; the same case.
  • Below lg (390 wide) the strip is where it was and scrolls sideways inside itself without widening the page (tests/e2e/test_reflow.py covers the page).
  • The markup test beside the Your details one: lg:sticky and the --header-height offset are in a Server settings page's HTML (tests/test_server_settings.py, next to test_every_section_renders_inside_the_sidebar), and in a Settings page if Settings is included.
  • The axe run over the Server settings pages in both themes (tests/e2e/test_accessibility.py) stays clean, including the rule for a scrollable region without a focusable child.
  • tests/test_stylesheet.py passes with the rebuilt app.css. No new string, so the catalogues and Weblate (#349) need nothing.

From the list of improvements of 2026-10-02.

On a long Server settings page the list of sections scrolls away with the page, and the way to another section is back at the top. The maintainer's list asks for it to stay put: "Server settings: sub-menu floating always on top-left." ## What exists today - The frame is `server/base.html`: a `lg:flex lg:items-start lg:gap-10` row holding an `<aside class="mb-6 lg:mb-0 lg:w-56 lg:shrink-0">` and the page's column (`src/postulo/templates/server/base.html:7-15`). The aside is not sticky. Its content is the area's name as a paragraph and `{% server_sidebar %}`, which draws `partials/sidebar.html`: a `<nav>` of ten sections (`core/server_sections.py`: Overview, People, Sign-in, Email, Plugins, Capture, Data protection, Logs, Defaults, Design), a column from `lg` up and a horizontal strip that scrolls sideways below it. - The two other frames with a sidebar already stick. Your details and Your career give the aside `lg:sticky lg:top-[calc(var(--header-height,3.5rem)+1rem)]` (`templates/accounts/profile.html:30`, `templates/resume/overview.html:31`), where `--header-height` is the masthead's height as `app.js` measures it, with 3.5rem where scripts do not run (`app.js:1143-1157`, `base.html` comment on the floating header, #195). `tests/test_header.py::test_the_header_floats_and_everything_that_has_to_clear_it_reads_one_height` already pins the class on the Your details page. - Settings has the same plain aside as Server settings (`templates/settings/base.html:9-20`); the maintainer's list names Server settings only. - Several Server settings pages are long: Overview, Logs, People with a crowd, Defaults with its list of every language, Design. On all of them the person who has scrolled has to return to the top to change section. - A different fault on the same sidebar is open and is not this one: four People sub-pages mark no section as current (#486). - The aside is `lg:items-start`, which is what lets a sticky child keep its own height inside a flex row; the frame needs nothing else to start sticking. Checked against `main` at `f09b6010b`. ## Proposal 1. **Stick the aside from `lg` up**, with the pattern the other two frames use: `lg:sticky lg:top-[calc(var(--header-height,3.5rem)+1rem)]` on the `<aside>` in `server/base.html`. Below `lg` the sidebar stays a strip above the page, as it is. 2. **Never taller than the window.** Ten rows and a label are about 400 pixels, which is more than a short window at `lg` leaves under the masthead (a 1024 by 500 tablet in landscape). So the aside also takes `lg:max-h-[calc(100dvh-var(--header-height,3.5rem)-2rem)]` and `lg:overflow-y-auto`, and the last row can always be reached by scrolling the aside or by Tab. A little padding on the scrolling box so a focus ring on a link at its edge is not clipped. 3. **Logical sides only.** "Top-left" is the start edge: the aside is the first item of the row, so in a right-to-left language it is at the right, and nothing here names a side (`tests/test_template_lint.py`). 4. **Rebuild the stylesheet** (`npm run build:css`) and commit it; the new utilities exist in no compiled CSS yet (`tests/test_stylesheet.py`). ## Decisions - **Settings too.** The list names Server settings only, but `settings/base.html` is the same markup with the same problem, and two sidebars of one design that stick in one place and not the other would be an arbitrary difference. **Recommended:** the same change to `settings/base.html` in the same commit, with the issue's tests covering both; if the maintainer wants Server settings alone it is one template less. - **Height cap.** A cap with its own scroll, or let a tall sidebar run past the window. **Recommended:** the cap: a sticky box taller than the window cannot be scrolled to its end by the page, which would hide the last sections exactly where this issue wants them reachable. Settings can grow past ten rows, because a plugin may register a section there; the Server list is fixed. - **Not the label.** The area's name above the list stays a static label, as `settings/base.html` explains (#227). Showing a section title there, as #677 proposes for the pages made of anchors, does not apply: these entries are pages, and the current one is already marked. ## Builds on #195 (the floating header and `--header-height`), #282 and #299 (the masthead and its measured height), #320 (the frame takes the screen and each page keeps its own measure). Related: #486 (the current section is not marked on four People sub-pages), #677 (the sub-menu of Your details and Your career). ## Done when - At 1280 by 800, after scrolling a long Server settings page to its foot (Logs or People with a crowd of accounts), the sidebar is still in view under the masthead, and a click on another section goes there. A new case in `tests/e2e/test_sticky_header.py`, with the administrator fixture the People e2e file uses (`tests/e2e/test_people_menu.py:88`). - At 1024 by 480 the aside is no taller than the space under the masthead, scrolls inside itself, and its last link can be reached by Tab; the same case. - Below `lg` (390 wide) the strip is where it was and scrolls sideways inside itself without widening the page (`tests/e2e/test_reflow.py` covers the page). - The markup test beside the Your details one: `lg:sticky` and the `--header-height` offset are in a Server settings page's HTML (`tests/test_server_settings.py`, next to `test_every_section_renders_inside_the_sidebar`), and in a Settings page if Settings is included. - The axe run over the Server settings pages in both themes (`tests/e2e/test_accessibility.py`) stays clean, including the rule for a scrollable region without a focusable child. - `tests/test_stylesheet.py` passes with the rebuilt `app.css`. No new string, so the catalogues and Weblate (#349) need nothing. From the list of improvements of 2026-10-02.
tiagoagueda added this to the 0.5.0 milestone 2026-10-03 01:59:10 +00:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
Postulo/postulo#699
No description provided.