git-ents.gitmain
⌘K
foforge
commit 4bb6ab1
web: polish pass against the workbench mock

Playwright screenshots of every page against the design handoff turned up systemic spacing gaps, all from markup bypassing the design system: forms sat flush against card edges (no inner padding), bare h2 section headings and p.muted lead-ins had no margins at all, and several pages rendered forms naked on the page background.

  • .card > form gets the mock composer’s 18/20 padding, action row right-aligned; every add/define/import form now lives in a card with a header

  • bare h2 / p.muted in page flow get the mock’s section-heading rhythm

  • .disclosure card-lets for the fold-shut edit affordances (issue edit, login mapping)

  • meta index: stacked accent rows, "Repo & governance" title, real em dashes; commit rows: sans author/when per the mock; stray lowercase card headers dropped; form labels Title-cased; .readable centered

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

Joseph D. Carpinelli · 29 days ago

Reviews

No reviews of this commit yet — record a verdict below.

Start a review

verdict

crates/cli/ents-web/src/assets/ents.css @@ -252,7 +252,7 @@ /* ===================== CONTENT / SPLIT ===================== */ .content { max-width: 1120px; width: 100%; margin: 0 auto; padding: 30px 34px 60px; flex: 1; } -.readable { max-width: 760px; } +.readable { max-width: 760px; margin-inline: auto; } /* Master-detail (`crate::pages::layout_split`): a sticky scrollable * sidebar beside a `min-height:0` detail pane. */ .split { display: grid; grid-template-columns: 300px minmax(0, 1fr); height: calc(100vh - 56px); } @@ -306,6 +306,12 @@ } /* ===================== PAGE HEADER ===================== */ +/* Bare `h2` and `p.muted` in page flow are the section-heading and + * lead-in idiom (mock: 14px/600 with 26px above; muted lead-ins breathe + * instead of sitting flush on the card below). */ +.content h2, .pane h2 { margin: 26px 0 12px; font-size: 14px; font-weight: 600; } +.content h2:first-child, .pane h2:first-child { margin-top: 0; } +p.muted { margin-bottom: 16px; font-size: 13px; text-wrap: pretty; } .page-header { margin-bottom: 22px; display: flex; align-items: flex-end; justify-content: space-between; gap: 16px; flex-wrap: wrap; } .page-title { font-size: 22px; font-weight: 600; letter-spacing: -.3px; line-height: 1.25; } .page-sub { margin-top: 5px; color: var(--ink-3); font-size: 13px; } @@ -322,6 +328,15 @@ font-size: 13px; font-weight: 600; color: var(--ink); } .card-header .btn-ghost { margin-left: auto; } +/* A form as a card's body gets the mock composer's inner padding, its + * action row right-aligned as the mock's composer footer. */ +.card > form { padding: 18px 20px; margin-bottom: 0; } +.card > form .composer-buttons { justify-content: flex-end; } +.tree-head .btn { height: 24px; } +/* Stacked card rows (meta index): name over blurb, sans, left-aligned. */ +.card-row.stack { flex-direction: column; align-items: flex-start; gap: 2px; padding: 13px 16px; font-family: var(--sans); } +.card-row.stack a { flex: none; font-size: 13.5px; font-weight: 600; color: var(--accent); text-transform: capitalize; } +.card-row.stack span { font-size: 12.5px; color: var(--ink-3); } .card-row { display: flex; align-items: center; gap: 10px; padding: 9px 16px; font-family: var(--mono); font-size: 12.5px; @@ -352,6 +367,9 @@ .history .card-row a:hover { text-decoration: underline; } .desk-subject { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-family: var(--sans); font-size: 13px; color: var(--ink); } .desk-when { flex: none; font-size: 12px; color: var(--ink-3); } +/* Commit-row trailing author/when: sans, quiet, never wrapping (mock). */ +.row-author { flex: none; font-family: var(--sans); font-size: 12px; color: var(--ink-3); } +.row-when { flex: none; font-family: var(--sans); font-size: 12px; color: var(--ink-4); white-space: nowrap; } @media (max-width: 1100px) { .desk { grid-template-columns: minmax(0, 1fr); } } /* ===================== BUTTONS ===================== */ @@ -359,6 +377,7 @@ display: inline-flex; align-items: center; gap: 6px; height: 34px; padding: 0 16px; font-size: 13px; font-weight: 500; color: #fff; background: var(--accent); border: none; border-radius: var(--radius-inner); cursor: pointer; + white-space: nowrap; flex: none; } .btn:hover { background: var(--accent-2); text-decoration: none; color: #fff; } .btn-sm { height: 24px; padding: 0 10px; font-size: 11.5px; border-radius: var(--radius-chip); } @@ -486,7 +505,7 @@ text-align: center; padding: 40px 24px; border: 1px dashed var(--line); border-radius: var(--radius-card); background: var(--surface); } -.blankslate h2 { font-size: 13.5px; font-weight: 600; color: var(--ink-2); margin-bottom: 4px; } +.blankslate h2 { font-size: 13.5px; font-weight: 600; color: var(--ink-2); margin: 0 0 4px; } .blankslate p { color: var(--ink-3); font-size: 12.5px; } .blankslate code { font-family: var(--mono); background: var(--surface-2); border: 1px solid var(--line); padding: 1px 6px; border-radius: 5px; font-size: 12px; } .card .blankslate { border: none; } @@ -544,6 +563,21 @@ } .label-chip.on { color: var(--accent); background: var(--accent-soft); border-color: var(--accent-line); font-weight: 600; } +/* ===================== DISCLOSURES ===================== * + * An edit affordance folded shut (issue edit, login-mapping edit): a + * bordered card-let whose summary is the whole closed state. */ +.disclosure { + background: var(--surface); border: 1px solid var(--line); border-radius: 10px; + overflow: hidden; margin-bottom: 12px; +} +.disclosure > summary { + padding: 11px 16px; cursor: pointer; list-style: none; user-select: none; -webkit-user-select: none; + font-size: 12.5px; font-weight: 500; color: var(--ink-2); +} +.disclosure > summary::-webkit-details-marker { display: none; } +.disclosure > summary:hover { color: var(--accent); } +.disclosure form { padding: 4px 16px 16px; margin-bottom: 0; } + /* ===================== CRUMBS ===================== */ .crumbs { display: flex; flex-wrap: wrap; align-items: center; gap: 7px; margin-bottom: 16px; font-family: var(--mono); font-size: 12px; color: var(--ink-3); word-break: break-all; } .crumbs a { color: var(--ink-3); }
crates/cli/ents-web/src/pages/account.rs @@ -76,7 +76,7 @@ "Signed in as the member below. Every web edit is a " "mutation commit signed with this key, exactly as " code { "git ents" } - " itself would sign it -- a local root has no separate login." + " itself would sign it — a local root has no separate login." } (super::members::member_card(username.as_str(), member, true)) } @@ -104,15 +104,15 @@ p.muted { "A hosted deployment maps an external login to an enrolled " "member so its pushes can be attributed. A local root never " - "needs one -- the key above is the identity." + "needs one — the key above is the identity." } (view) - details { + details.disclosure { summary { "Edit login mapping" } form method="post" action="/account" { (super::csrf_input(&session)) - label { "member" input type="text" name="member" value=(member_value) list="members"; } - label { "login" input type="text" name="login" value=(login_value); } + label { "Member" input type="text" name="member" value=(member_value) list="members"; } + label { "Login" input type="text" name="login" value=(login_value); } button type="submit" { "Save" } } (super::members_datalist(&state))
crates/cli/ents-web/src/pages/comments.rs @@ -101,8 +101,10 @@ (listing_card(&state, id, comment)) } } - h2 { "Add a Comment" } - (add_form(&query.rev, &session, &query.file, &query.lines)) + div.card { + div.card-header { "Add a comment" } + (add_form(&query.rev, &session, &query.file, &query.lines)) + } } }, )) @@ -484,7 +486,7 @@ form method="post" action=(format!("/comments/{id}/reply")) { (super::csrf_input(session)) input type="hidden" name="return_to" value=(return_to); - label { "reply" textarea name="body" {} } + label { "Reply" textarea name="body" {} } button type="submit" { "Reply" } } @if resolved { @@ -578,10 +580,10 @@ html! { form method="post" action="/comments" { (super::csrf_input(session)) - label { "path" input type="text" name="path" value=(prefill_path); } - label { "rev" input type="text" name="rev" value=(default_rev); } - label { "lines" input type="text" name="lines" value=(prefill_lines); } - label { "body" textarea name="body" {} } + label { "Path" input type="text" name="path" value=(prefill_path); } + label { "Rev" input type="text" name="rev" value=(default_rev); } + label { "Lines" input type="text" name="lines" value=(prefill_lines); } + label { "Body" textarea name="body" {} } button type="submit" { "Comment" } } }
crates/cli/ents-web/src/pages/commits.rs @@ -102,7 +102,6 @@ (blankslate()) } @else { div.card.history { - div.card-header { "commits" } @for row in &rows { (commit_row(row)) } @@ -203,8 +202,8 @@ }, None => { span.desk-subject { (row.subject) } }, } - span.muted { (row.author) } - span.entry-size { (row.ago) } + span.row-author { (row.author) } + span.row-when { (row.ago) } } } } @@ -530,7 +529,7 @@ form method="post" action=(format!("/reviews/{target}/{member}/comment")) { (super::csrf_input(session)) input type="hidden" name="return_to" value=(return_to); - label { "comment on this review" textarea name="body" {} } + label { "Comment on this review" textarea name="body" {} } button type="submit" { "Comment" } } } @@ -626,7 +625,7 @@ "comment" } } - label { "body" textarea name="body" {} } + label { "Body" textarea name="body" {} } button type="submit" { "Start a Review" } } }
crates/cli/ents-web/src/pages/effects.rs @@ -57,8 +57,10 @@ html! { (crate::render::unreadable_disclosure(&failures)) (table) - h2 { "Define an Effect" } - (add_form(&session)) + div.card { + div.card-header { "Define an effect" } + (add_form(&session)) + } }, )) } @@ -69,14 +71,14 @@ html! { form method="post" action="/effects" { (super::csrf_input(session)) - label { "name" input type="text" name="name"; } + label { "Name" input type="text" name="name"; } label { - "trigger" + "Trigger" input type="text" name="trigger" placeholder="query.grammar trigger"; } - label { "run" input type="text" name="run" placeholder="command to run"; } + label { "Run" input type="text" name="run" placeholder="command to run"; } label { - "toolchains" + "Toolchains" input type="text" name="toolchains" placeholder="rust, node"; } button type="submit" { "Define Effect" }
crates/cli/ents-web/src/pages/files.rs @@ -343,7 +343,6 @@ }); html! { div.card { - div.card-header { "files" } @if entries.is_empty() { div.card-row.muted { "Empty directory." } }
crates/cli/ents-web/src/pages/issues.rs @@ -203,7 +203,7 @@ } div.doc-body { (body) } } - details { + details.disclosure { summary { "Edit state, assignees, labels" } (edit_form(&session, &issue, &labels)) (super::members_datalist(&state)) @@ -217,8 +217,10 @@ } @else { (crate::pages::comments::thread_section(&state, &session, &thread, &return_to)) } - h2 { "Add a Comment" } - (comment_form(&session, &id)) + div.card { + div.card-header { "Add a comment" } + (comment_form(&session, &id)) + } } }, )) @@ -412,14 +414,14 @@ html! { form method="post" action="/issues" { (super::csrf_input(session)) - label { "title" input type="text" name="title"; } + label { "Title" input type="text" name="title"; } div { - label { "state" } + label { "State" } (state_picker("open")) } - label { "assignees" input type="text" name="assignees" placeholder="alice, bob" list="members"; } + label { "Assignees" input type="text" name="assignees" placeholder="alice, bob" list="members"; } (label_picker(known_labels, &[])) - label { "body" textarea name="body" {} } + label { "Body" textarea name="body" {} } div.composer-buttons { a.composer-cancel href="/issues" { "Cancel" } button type="submit" { "Open Issue" } @@ -437,11 +439,11 @@ form method="post" action="" { (super::csrf_input(session)) div { - label { "state" } + label { "State" } (state_picker(&issue.state)) } label { - "assignees" + "Assignees" input type="text" name="assignees" value=(join_members(&issue.assignees)) list="members"; } (label_picker(known_labels, &issue.labels)) @@ -455,7 +457,7 @@ html! { form method="post" action=(format!("/issues/{id}/comment")) { (super::csrf_input(session)) - label { "body" textarea name="body" {} } + label { "Body" textarea name="body" {} } button type="submit" { "Comment" } } } @@ -585,7 +587,7 @@ fn label_picker(known: &[String], current: &[String]) -> Markup { html! { div { - label { "labels" } + label { "Labels" } @if !known.is_empty() { div.picker { @for label in known {
crates/cli/ents-web/src/pages/meta.rs @@ -23,16 +23,16 @@ &super::RepoHeader::from_state(&state), &super::identity_label(&state), "/meta", - "Meta", + "Repo & governance", html! { p.muted { - "All project metadata -- members, effects, toolchains, " - "redactions, and the adoption inbox -- lives in this " + "All project metadata — members, effects, toolchains, " + "redactions, and the adoption inbox — lives in this " "repository as git objects." } div.card { @for section in super::META_SECTIONS { - div.card-row { + div.card-row.stack { a href=(section.href) { (section.name) } span { (section.blurb) } }
crates/cli/ents-web/src/pages/toolchains.rs @@ -70,8 +70,10 @@ html! { (crate::render::unreadable_disclosure(&failures)) (listing) - h2 { "Import a Toolchain" } - (import_form(&session)) + div.card { + div.card-header { "Import a toolchain" } + (import_form(&session)) + } }, )) } @@ -82,9 +84,9 @@ html! { form method="post" action="/toolchains" { (super::csrf_input(session)) - label { "name" input type="text" name="name"; } + label { "Name" input type="text" name="name"; } label { - "recipe" + "Recipe" textarea name="recipe" placeholder="embedded <tree-oid>\nor:\ndownloaded\n<url> <sha256> <strip> [dest]" {} }