admin: wrap the edit page back link in .record-nav
A bare link is a flex item at the label metrics (step--2/1) while
.record-nav takes its own inherited line box (step-0/1.55), so the
caption line and the card sat about 15px apart between modes. The note
on the pinned-button commit claimed otherwise, and is corrected here.
Assisted-by: Claude Opus 5 via Claude Code
diff --git a/lib/pro_tacts/admin/contacts_edit.rb b/lib/pro_tacts/admin/contacts_edit.rb
index 9f43cb2..8344fe5 100644
--- a/lib/pro_tacts/admin/contacts_edit.rb
+++ b/lib/pro_tacts/admin/contacts_edit.rb
@@ -40,11 +40,17 @@ module ProTacts
def view_template
render Layout.new(title: "Edit #{@contact.name || @contact.id}", notice: @notice) do
# The caption-to-card block the details page uses (.record in
- # admin.css): the back link is this card's caption row too.
+ # admin.css): the back link is this card's caption row too,
+ # inside the .record-nav that page wraps it in. The row's
+ # height comes from that element rather than the label, so a
+ # bare link here is the shorter of the two rows and the card
+ # under it lands higher than it does on the details page.
div(class: "record") do
- a(href: "/contacts/#{@contact.id}", class: "type-label") {
- "‹ #{@contact.name || @contact.id}"
- }
+ div(class: "record-nav") do
+ a(href: "/contacts/#{@contact.id}", class: "type-label") {
+ "‹ #{@contact.name || @contact.id}"
+ }
+ end
div(class: "card") do
div(class: "card-body") do
form(action: "/contacts/#{@contact.id}", method: "post", class: "field-stack") do
diff --git a/public/admin.css b/public/admin.css
index 4bfbc93..0e4ccf8 100644
--- a/public/admin.css
+++ b/public/admin.css
@@ -94,10 +94,11 @@
the right edge, pinned the way the dashboard's section-head pins
its action. In flow the button — half again taller than a
type-label line — grew the row, and the card under it moved with
- it: the details page's row was the button's height while the edit
- page's was the label's, so the mode change shifted the line and the
- card together. Pinned, the row is the label's own height on both
- pages and nothing jumps. */
+ it. Pinning takes the button out of flow, which leaves the row at
+ this element's own inherited line box — step-0/1.55, not the inline
+ label's step--2/1 — so both record pages have to wrap the back link
+ in this div. A bare link is a flex item of the label's height
+ instead, a good 15px shorter, and the card jumps between modes. */
.record-nav {
position: relative;
}
diff --git a/test/pro_tacts/admin/test_contacts_pages.rb b/test/pro_tacts/admin/test_contacts_pages.rb
index de534b4..8af593d 100644
--- a/test/pro_tacts/admin/test_contacts_pages.rb
+++ b/test/pro_tacts/admin/test_contacts_pages.rb
@@ -131,6 +131,27 @@ class AdminContactsPagesTest < Minitest::Test
assert_includes last_response.body, "No birthdays to show."
end
+ # The caption row's height comes from .record-nav's own inherited
+ # line box, not from the type-label inside it (see admin.css), so
+ # the two record pages have to open the block the same way. A bare
+ # link on one of them is the shorter row, and the card under it —
+ # along with the line itself — moves when you switch modes.
+ def test_both_record_pages_open_the_block_with_the_same_row
+ with_contacts({"ada" => ADA}) do
+ get "/contacts/ada"
+ show = last_response.body
+
+ get "/contacts/ada/edit"
+ edit = last_response.body
+
+ {"details" => show, "edit" => edit}.each do |page, body|
+ record = body.split('<div class="record">').last
+ assert_match(/\A<div class="record-nav"><a href="[^"]+" class="type-label">/,
+ record, "the #{page} page opens .record with a different row")
+ end
+ end
+ end
+
# The search lives in the page header (docs/DESIGN.md), rendered by
# the layout on every screen now — not just the dashboard's — so
# the header's height never changes between pages and finding a