docs/specs/2026-09-11-web-ux-sweep-design.md
79 lines · 4160 bytes
Web UI/UX sweep
Closes #182. The findings posted on that issue after walking every route in
internal/httpd/routes.go on gitbay.org at v1.20.1, anonymous and logged
in, and the rules chosen to fix them.
Rules
- Confirmation. A control that destroys data nothing else holds asks
the person to type the object's name into a text field beside the
button; the handler refuses with a flash line when the text differs.
No JavaScript: the instance CSP is
script-src 'none'. Covered: release delete (the tag), snippet delete (the id), snippet file remove (the file name), team delete (the team name), label remove (the label), SSH key remove (the 8 characters afterSHA256:in the fingerprint; a label can be empty), email remove (the address), PGP key remove (the first 8 characters of the fingerprint). Reversible state keeps a plain button: close/reopen, merge, protect/unprotect, attach/detach, resolve, cancel, make primary, org member remove. - Refusal wording. Control-command messages a web form can trigger
must not name a flag or a CLI command. The message is rewritten at the
source, in
internal/control, since the CLI reads the same text; no rewrite layer in the web. - Login return-to.
requireUserstores the requested local path in a short-lived cookie; the login page says where the person is going; the emailed-link andweb loginpaths both redirect there once and clear it. Only a path starting with a single/is honoured. - Dates. One absolute format on every page:
2006-01-02 15:04 UTCthrough the existingwhenhelper. Tree and blob listings keep their relative time with the absolute one in atitleattribute. - Tags. Version-aware order on the refs page, newest first; a tag that does not parse as a version sorts after the ones that do, by name.
- Editor. When the repository requires signed commits, or the ref
refuses direct pushes, the edit page explains that and shows no form.
A branch that does not exist is a 404; a path that does not exist on
a real branch is a new-file form that says so, since
commit-filecreates it. - Repository settings. Every Save names its field.
- Empty states. Sidebars use "none yet" for things and "nobody yet" for people; lists keep their sentence and, where a command creates the thing, name it.
Findings and their fixes
- Destructive controls without confirmation: the rule above, applied to
account.html,settings.html(no change: unprotect and detach are reversible),owner.html(team delete only),labels.html,releases.html,snippet.html. - Refusals verbatim: audit and rewrite.
account.htmlsaysgitbay auth token mint; the command isauth token create.- Login return-to.
- Four date formats.
- Refs page sorts tags as strings.
- Editor offered where it cannot succeed.
- Twelve unlabelled Save buttons on repository settings.
- Labels page: colour column shown when no label has a colour; the column and the per-row colour form appear only when a label has a colour or the viewer can write.
- Empty-state wording.
- Issue close-event line reads "closed by commit X by Y"; becomes "closed by Y in commit X" with the time inline.
- Issue rows repeat the state chip under a single-state filter; the milestone reads like a label. The chip appears only under "all"; the milestone renders as "in ".
- Merged MR page: "at" becomes "merged at"; a source branch that no longer exists is marked "branch deleted".
- Global search shows a count and the query. No context line.
- Repository home shows both clone URLs, HTTPS and SSH.
- Landing page: "mint a browser session" becomes "log in from your terminal". The account page's markup picker already sits beside its label; that finding was wrong and nothing changes.
- Left alone: the anonymous "1 bookmark" stat, since the count is public by design (the bookmarks page says so).
Out of scope
A context line on global search results (a search backend change), the
POST-only routes, /settings/export, archives, badges and Atom feeds.