web: line the accounts row's controls up in columns !457

merged merged by cmc on 2026-09-22 04:01 UTC · krz/gitbay:web-accounts-columns into main

3 files changed, +45 −8

Layout: unified · split

CHANGELOG.org +6 −1
@@ -48,7 +48,12 @@ Eleven web findings, mostly alignment and affordance (#247).
4848 active, rather than leaving the cell short, and the page is wide. At
4949 48rem the Actions cell clipped the second form's button, which left
5050 its confirm field looking like it belonged to the button before it —
51 an active row read as though Promote wanted the name typed.
51 an active row read as though Promote wanted the name typed. The cell
52 is now two fixed slots, each right-aligned, so the role button, the
53 confirm field and the access button each hold one column whichever
54 controls a row carries: an admin's row has a field on both forms, a
55 disabled account's enable form has none. The field is 14rem, which
56 fits "type <name> to confirm" for a name of ordinary length.
5257- The profile's inactive tabs draw their underline in =--line=, so the
5358 strip reads as a track the current tab is marked in.
5459- =.listhead= sheds its children's block margins, which is what kept a
internal/web/static/style.css +34 −3
@@ -920,9 +920,40 @@ table.keys td.act, table.assets td.size { text-align: right; }
920920 keep one line each and .tablewrap scrolls what does not fit */
921921table.keys.nowrap th, table.keys.nowrap td { white-space: nowrap; }
922922/* The account list types a username to confirm, which is longer than
923 the key prefixes the confirm field is sized for elsewhere. */
924table.accounts input[name="confirm"] { width: 11rem; }
925table.accounts form.actions { display: inline-flex; gap: var(--sp-2); margin: 0 var(--sp-2) var(--sp-2) 0; }
923 the key prefixes the confirm field is sized for elsewhere. 14rem, not
924 11rem, because "type <name> to confirm" was cut mid-word for a name of
925 ordinary length; a long enough name still truncates and the field's
926 aria-label carries the instruction in full either way.
927
928 The cell is two fixed slots, each right-aligned. A form cannot span
929 table cells, so one column per control — which would let the table
930 align them — is out without form="" plumbing, and the forms hold
931 different numbers of controls: only an admin's role form has a
932 confirm field, and the enable form has none. Sizing the slot instead
933 of the form is what puts every button in one column and every field
934 in another; right-aligning the cell alone left the disabled row's
935 Promote where other rows keep their field. A slot is a field, a gap
936 and a button wide, so a form that is only a button still ends on the
937 same rule. No element stands in for the controls a row lacks. */
938table.accounts { --acct-field: 14rem; --acct-btn: 6rem; }
939table.accounts td.acts { display: flex; justify-content: flex-end; align-items: center; gap: var(--sp-2); }
940table.accounts input[name="confirm"] { width: var(--acct-field); }
941table.accounts form.actions {
942 flex: none;
943 display: inline-flex;
944 /* the generic form.actions wraps and grows its button to fill; both
945 have to go, or the slot takes the button to a second line and the
946 field sits above it */
947 flex-wrap: nowrap;
948 justify-content: flex-end;
949 align-items: center;
950 gap: var(--sp-2);
951 width: calc(var(--acct-field) + var(--acct-btn) + var(--sp-2));
952 margin: 0;
953}
954/* a set width, not a minimum: it makes the slot's arithmetic exact, and
955 6rem clears the widest of Promote, Demote, Disable and Enable */
956table.accounts form.actions button { flex: none; width: var(--acct-btn); justify-content: center; }
926957table.assets td.name { width: 100%; font-family: var(--mono); font-size: var(--fs-1); }
927958table.assets { margin-top: var(--sp-2); }
928959
internal/web/templates/adminusers.html +5 −4
@@ -25,7 +25,7 @@
2525 <td><span class="chip {{if eq .State "active"}}chip-open{{else if eq .State "disabled"}}chip-closed{{else}}chip-neutral{{end}}">{{.State}}</span></td>
2626 <td>{{when .CreatedAt}}</td>
2727 <td>{{if .LastSeen}}{{when .LastSeen}}{{else}}never{{end}}</td>
28 <td>
28 <td class="acts">
2929 {{if .Admin}}
3030 <form method="post" action="/admin/users" class="actions">
3131 <input type="hidden" name="field" value="demote">
@@ -42,10 +42,11 @@
4242 </form>
4343 {{else}}
4444 {{/* An account that is not active cannot be promoted, but the row
45 still draws the control so every Actions cell is the same
46 shape. Both parts are disabled, so the form never posts. */}}
45 still draws the button so the cell keeps its shape. It carries
46 no confirm field: nothing is confirmed, and a second field with
47 the same placeholder as the disable form's reads as a duplicate
48 of it. Disabled, so the form never posts. */}}
4749 <form method="post" action="/admin/users" class="actions" title="Only an active account can be promoted">
48 <input type="text" name="confirm" aria-label="Type {{.Username}} to confirm" placeholder="type {{.Username}} to confirm" size="{{len .Username}}" autocomplete="off" disabled>
4950 <button type="submit" class="btn" disabled>Promote</button>
5051 </form>
5152 {{end}}