Header, right side: avatar, full name and theme switch only; Sign out moves into an account menu #10
Labels
No labels
accessibility
authentication
breaking change
bug
documentation
enhancement
interface
internationalisation
observability
security
tier
1
tier
2
tier
3
tier/4
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Depends on
Reference
Postulo/postulo#10
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Observation
What exists today
The right cluster of the header (
src/postulo/templates/base.html, line 57 onwards) holds four things: Capture (secondary button), Record (primary button), the display name as a link to Your details, and a Sign out form with its own button. No avatar, no switch, no menu.display_name(accounts/models.py, line 71) is the full name when there is one and the local part of the email address otherwise; #2 makes the full name obligatory, after which the header always shows a name.Two facts shape the implementation:
ACCOUNT_LOGOUT_ON_GETis not set anywhere inconfig/settings/), and rightly so: a GET logout is a one-line CSRF. The menu entry therefore stays a form with a button, styled as a menu item; it does not become a link.script-src 'self'; the project's client-side behaviour is one delegated-events file (static/js/app.js). A menu has to fit that pattern.Shape
1. The cluster becomes exactly three things, in the order given:
display_nameuntil #2), with a small chevron (#8) so it reads as a menu and not as a link. On narrow screens the name hides and the avatar alone remains the trigger.2. The account menu, opened by the trigger:
base.html, line 47) goes away with it, which shortens the bar. Administrators reach Server settings from this menu (#24), so nothing is further away than before.3. Capture and Record leave the header. "Only" means only. Both already exist as page-level buttons where they belong: Record an application on the Applications list (twice), the Board and Insights; Capture a posting on the captures list. The one page that relies on the header buttons is the dashboard, which has neither of its own (
core/dashboard.htmllinks to export and nothing else in that family). So the dashboard gains its own two actions in its page header, which is where a day starts anyway. Nothing else loses a button.How the menu is built
Proposal:
<details>and<summary>, which the project already uses (jobs/capture_form.html, line 22). It opens and closes with no script, the trigger is focusable and keyboard-operable out of the box, and assistive technology announces the expanded state. This is the native form of the WAI-ARIA disclosure navigation menu pattern, which is the recommended one for site navigation (notrole="menu", which is for application menus and brings its own keyboard contract).What the native element does not do is close on a click elsewhere or on Escape. That is a few lines in
app.js, delegated from the document likedata-autosubmit: any opendetails[data-menu]closes when the click lands outside it or Escape is pressed, and focus returns to the trigger. Markup swapped by htmx keeps working, because nothing is bound to an element.The alternative is the Popover API (
popoveron the panel,popovertargeton the button), which gives light dismiss and Escape natively and needs no JS at all. Its drawback is placement: a popover lands in the top layer centred in the viewport by default, so putting it under the trigger needs CSS anchor positioning or, again, a few lines of JS. Either is acceptable;<details>is the one already in the codebase.Details that matter either way:
card-like surface:border-ink-200 bg-white dark:border-ink-800 dark:bg-ink-900).openattribute.Classification
Enhancement. Not breaking: no schema, no setting, no URL changes; the only behaviour that moves is where two buttons and one form live.
Depends on
Open questions
Shipped in
6f283b7(with a follow-up in6f32f55). The trigger uses the initials tile as its avatar; #7 adds the picture on top of it and no longer blocks this. The dependency is removed so the issue can close.