Commit 51291d1c02
51291d1c02fd3e621d144dfcd9d55a3ba5662e24
parent: 673d65c169
Verified · cmc
cmc <hello@cleberg.net> · 2026-09-19 01:44 UTC
web: the About block sits beside the file table on a wide overview
Above 62rem the repository overview is a two-column grid: the path
bar, file table and README on the left, About and clone commands in
an 18rem column on the right. Below 62rem the order is unchanged —
table, README, then clone and about.
Closes #228
e2e/design_test.go
+8 −1
| @@ -390,8 +390,15 @@ func TestTreeSearchCodeAndClone(t *testing.T) { |
| 390 | 390 | if !strings.Contains(body, `<span class="fieldname">SSH</span>`) || !strings.Contains(body, `<span class="fieldname">HTTPS</span>`) { |
| 391 | 391 | t.Error("clone blocks are not labelled") |
| 392 | 392 | } |
| 393 | if !strings.Contains(body, `class="overview withfacts"`) { |
| 394 | t.Error("root overview is not the two-column withfacts layout") |
| 395 | } |
| 393 | 396 | // Clone and about belong to the repository root, not a subdirectory. |
| 394 | | if _, sub := inst.get(t, "/alice/app/tree/main/docs"); strings.Contains(sub, `<div class="facts">`) { |
| 397 | _, sub := inst.get(t, "/alice/app/tree/main/docs") |
| 398 | if strings.Contains(sub, `<div class="facts">`) { |
| 395 | 399 | t.Error("subdirectory listing still shows the clone/about facts block") |
| 396 | 400 | } |
| 401 | if !strings.Contains(sub, `class="overview"`) || strings.Contains(sub, `class="overview withfacts"`) { |
| 402 | t.Error("subdirectory listing should have a plain overview, not withfacts") |
| 403 | } |
| 397 | 404 | } |
internal/web/static/style.css
+10
| @@ -1323,6 +1323,12 @@ details.refmenu .refdrop a.allrefs { |
| 1323 | 1323 | margin-top: var(--sp-1); |
| 1324 | 1324 | } |
| 1325 | 1325 | |
| 1326 | /* ---- repository overview layout ---- */ |
| 1327 | .overview { display: block; } |
| 1328 | .overview.withfacts { display: grid; grid-template-columns: minmax(0, 1fr) 18rem; gap: var(--sp-6); align-items: start; } |
| 1329 | .overviewmain { min-width: 0; } |
| 1330 | .overview.withfacts .facts { display: block; margin-top: 0; max-width: none; } |
| 1331 | |
| 1326 | 1332 | /* ---- repository facts ---- */ |
| 1327 | 1333 | .facts { display: grid; grid-template-columns: 1fr 1fr; gap: var(--sp-5); margin: var(--sp-5) 0 var(--sp-4); max-width: 72rem; } |
| 1328 | 1334 | .facts h2 { |
| @@ -1335,9 +1341,11 @@ details.refmenu .refdrop a.allrefs { |
| 1335 | 1341 | } |
| 1336 | 1342 | .clone { margin: 0 0 var(--sp-3); } |
| 1337 | 1343 | .facts .about { min-width: 0; } |
| 1344 | .facts .about { margin-bottom: var(--sp-5); } |
| 1338 | 1345 | .clone .fieldname { font-size: var(--fs-1); color: var(--muted); font-weight: 500; margin-bottom: 2px; } |
| 1339 | 1346 | .clone pre { margin-bottom: var(--sp-3); user-select: all; } |
| 1340 | 1347 | .factgrid { display: grid; grid-template-columns: repeat(4, auto); gap: var(--sp-2) var(--sp-4); font-size: var(--fs-2); margin-bottom: var(--sp-3); } |
| 1348 | .overview.withfacts .facts .factgrid { grid-template-columns: repeat(2, auto); } |
| 1341 | 1349 | .factgrid b { font-weight: 600; } |
| 1342 | 1350 | .factgrid a { color: var(--fg); } |
| 1343 | 1351 | .factgrid a:hover { color: var(--link); } |
| @@ -1457,6 +1465,8 @@ svg.icon { vertical-align: -0.125em; } |
| 1457 | 1465 | the activity feed, which reads after the queues */ |
| 1458 | 1466 | .withaside .aside { order: -1; } |
| 1459 | 1467 | .withaside.feedlast .aside { order: 0; } |
| 1468 | .overview.withfacts { grid-template-columns: 1fr; } |
| 1469 | .overview.withfacts .facts { display: grid; grid-template-columns: 1fr 1fr; margin-top: var(--sp-5); } |
| 1460 | 1470 | } |
| 1461 | 1471 | |
| 1462 | 1472 | @media (max-width: 52rem) { |
internal/web/templates/tree.html
+9 −5
| @@ -3,6 +3,8 @@ |
| 3 | 3 | {{define "content"}} |
| 4 | 4 | {{if .Notice}}<p class="error" role="alert">{{.Notice}}</p>{{end}} |
| 5 | 5 | {{if .DirPath}}<h1 class="vh">{{.DirPath}}</h1>{{end}} |
| 6 | <div class="overview{{if and .Entries (not .DirPath)}} withfacts{{end}}"> |
| 7 | <div class="overviewmain"> |
| 6 | 8 | <div class="pathbar"> |
| 7 | 9 | {{template "refmenu" .}} |
| 8 | 10 | <span class="crumbs"><a href="/{{.Repo.OwnerName}}/{{.Repo.Name}}">{{.Repo.Name}}</a>/{{range .Crumbs}}<a href="{{.URL}}">{{.Name}}</a>/{{end}}</span> |
| @@ -29,12 +31,8 @@ |
| 29 | 31 | <div class="cardhead"><a href="/{{.Repo.OwnerName}}/{{.Repo.Name}}/blob/{{.Ref}}/{{.Prefix}}{{.ReadmeName}}">{{.ReadmeName}}</a></div> |
| 30 | 32 | <div class="rendered">{{.ReadmeHTML}}</div> |
| 31 | 33 | </section>{{end}} |
| 34 | </div> |
| 32 | 35 | {{if and .Entries (not .DirPath)}}<div class="facts"> |
| 33 | | <div class="clone"> |
| 34 | | <h2>Clone</h2> |
| 35 | | <span class="fieldname">SSH</span><pre><code>git clone {{.SSHCloneURL}}</code></pre> |
| 36 | | <span class="fieldname">HTTPS</span><pre><code>git clone {{.CloneURL}}</code></pre> |
| 37 | | </div> |
| 38 | 36 | {{if .Facts.Commits}}{{$r := printf "/%s/%s" .Repo.OwnerName .Repo.Name}}<div class="about"> |
| 39 | 37 | <h2>About</h2> |
| 40 | 38 | <div class="factgrid"> |
| @@ -50,5 +48,11 @@ |
| 50 | 48 | <p class="langs">{{range .}}<span class="lang-name"><span class="dot lang-{{slug .Name}}"></span>{{.Name}} <span class="muted">{{pct .Percent}}%</span></span>{{end}}</p>{{end}} |
| 51 | 49 | {{with .Facts.Contributors}}<p class="contribs"><span class="label">{{len .}} contributor{{if ne (len .) 1}}s{{end}}</span>{{range .}}{{template "authorname" dict "Name" .Name "User" .User "Email" .Title}}{{end}}</p>{{end}} |
| 52 | 50 | </div>{{end}} |
| 51 | <div class="clone"> |
| 52 | <h2>Clone</h2> |
| 53 | <span class="fieldname">SSH</span><pre><code>git clone {{.SSHCloneURL}}</code></pre> |
| 54 | <span class="fieldname">HTTPS</span><pre><code>git clone {{.CloneURL}}</code></pre> |
| 55 | </div> |
| 53 | 56 | </div>{{end}} |
| 57 | </div> |
| 54 | 58 | {{end}} |