admin: tighten the editor form's rhythm
Short boxes under their captions, the stack closed one step, and the\nback link on the details page's caption-to-card spacing.

Assisted-by: GLM-5.3 via pi
change kkmyutzmszxwltvutlrlrvnpuruxyuzl
commit 8ba54e0228cc6fe0cdd9306dadaea0402333f0b0
author Alpha Chen <alpha@kejadlen.dev>
date
parent omovvxxk
diff --git a/lib/pro_tacts/admin/contacts_edit.rb b/lib/pro_tacts/admin/contacts_edit.rb
index 570e2cc..9f43cb2 100644
--- a/lib/pro_tacts/admin/contacts_edit.rb
+++ b/lib/pro_tacts/admin/contacts_edit.rb
@@ -39,36 +39,40 @@ module ProTacts
 
       def view_template
         render Layout.new(title: "Edit #{@contact.name || @contact.id}", notice: @notice) do
-          a(href: "/contacts/#{@contact.id}", class: "type-label") {
-            "‹ #{@contact.name || @contact.id}"
-          }
-          div(class: "card") do
-            div(class: "card-body") do
-              form(action: "/contacts/#{@contact.id}", method: "post", class: "field-stack") do
-                input(type: "hidden", name: "etag", value: @contact.etag)
-                label(class: "field") do
-                  plain "First"
-                  input(type: "text", name: "first", value: @first,
-                        required: true, autofocus: true)
-                end
-                label(class: "field") do
-                  plain "Last"
-                  input(type: "text", name: "last", value: @last)
-                end
-                label(class: "field") do
-                  plain "Nickname"
-                  input(type: "text", name: "nickname", value: @contact.nickname)
-                end
-                label(class: "field") do
-                  plain "Note"
-                  textarea(name: "note", rows: 4) { @contact.notes.to_s }
-                end
-                # Save is the form's submit; Cancel is navigation — a
-                # link in Gloss's `.btn` contract, which is what an
-                # anchor that acts like a button opts into.
-                div(class: "form-actions") do
-                  button(type: "submit", data: {variant: "primary"}) { "Save" }
-                  a(href: "/contacts/#{@contact.id}", class: "btn") { "Cancel" }
+          # The caption-to-card block the details page uses (.record in
+          # admin.css): the back link is this card's caption row too.
+          div(class: "record") do
+            a(href: "/contacts/#{@contact.id}", class: "type-label") {
+              "‹ #{@contact.name || @contact.id}"
+            }
+            div(class: "card") do
+              div(class: "card-body") do
+                form(action: "/contacts/#{@contact.id}", method: "post", class: "field-stack") do
+                  input(type: "hidden", name: "etag", value: @contact.etag)
+                  label(class: "field") do
+                    plain "First"
+                    input(type: "text", name: "first", value: @first,
+                          required: true, autofocus: true)
+                  end
+                  label(class: "field") do
+                    plain "Last"
+                    input(type: "text", name: "last", value: @last)
+                  end
+                  label(class: "field") do
+                    plain "Nickname"
+                    input(type: "text", name: "nickname", value: @contact.nickname)
+                  end
+                  label(class: "field") do
+                    plain "Note"
+                    textarea(name: "note", rows: 4) { @contact.notes.to_s }
+                  end
+                  # Save is the form's submit; Cancel is navigation — a
+                  # link in Gloss's `.btn` contract, which is what an
+                  # anchor that acts like a button opts into.
+                  div(class: "form-actions") do
+                    button(type: "submit", data: {variant: "primary"}) { "Save" }
+                    a(href: "/contacts/#{@contact.id}", class: "btn") { "Cancel" }
+                  end
                 end
               end
             end
diff --git a/public/admin.css b/public/admin.css
index 215f89b..ca39ade 100644
--- a/public/admin.css
+++ b/public/admin.css
@@ -26,6 +26,18 @@
   justify-content: space-between;
 }
 
+/* A form in a card keeps a tighter rhythm than Gloss's form-body
+   defaults: the stack closes from s to xs, and the boxes shed half
+   their block padding — the same shedding the header's search does —
+   so short boxes sit close under their captions. Scoped to the
+   card-body form rather than overriding field.css globally; the
+   label in the input selector out-specifies field.css's input rule
+   rather than relying on stylesheet order (see the header search's
+   note). */
+.card-body .field-stack { gap: var(--gl-space-xs); }
+.card-body label.field > input,
+.card-body label.field > textarea { padding-block: var(--gl-space-2xs); }
+
 /* The editor's submit row: the buttons sit beside each other under
    the stacked fields, rather than in the stack's own gap. */
 .form-actions {