A CLI-first git forge.

cli forge git self-hosted

https://gitbay.org

Commit 069bd1e107

069bd1e10730364492cbb50ac43b86fcc3a35c40

parent: 479e6687f8

Verified · cmc

cmc <hello@cleberg.net> · 2026-08-24T18:05:34Z

web: align layout grid, card-based listings

One shared .container aligns header, main, and footer to the same
64rem column, so the wordmark and content share a left edge on wide
screens. Index and owner repo lists become a responsive card grid
(auto-fill 19rem columns, meta pinned to card bottom, single column
under 40rem). Owner pages get a bordered profile header and a
repository count. Tree/refs tables, log rows, and issue lists share
the bordered-card treatment with row hover; discussion threads cap at
a 48rem reading measure.
internal/web/static/style.css +109 −25
@@ -94,10 +94,15 @@ code, pre, .code, td.mode, td.size {
9494 font-size: var(--fs-1);
9595 }
9696
97/* layout */
97/* layout: one shared container aligns header, content, and footer */
98.container {
99 max-width: 64rem;
100 margin: 0 auto;
101 padding: 0 var(--sp-5);
102}
98103 header {
99104 border-bottom: 1px solid var(--faint);
100 padding: var(--sp-3) var(--sp-5);
105 padding: var(--sp-3) 0;
101106 }
102107 a.site {
103108 font-weight: 650;
@@ -109,10 +114,10 @@ a.site {
109114 }
110115 a.site:hover { text-decoration: none; color: var(--accent); }
111116 a.site svg.mark { display: block; }
112main {
113 max-width: 64rem;
114 margin: 0 auto;
115 padding: var(--sp-3) var(--sp-5) var(--sp-6);
117main.container {
118 width: 100%;
119 padding-top: var(--sp-3);
120 padding-bottom: var(--sp-6);
116121 }
117122
118123 /* tables */
@@ -186,7 +191,21 @@ nav.tabs a.active { color: var(--fg); border-bottom-color: var(--accent); font-w
186191 }
187192 .pathbar .crumbs strong { color: var(--fg); }
188193
189/* file table */
194/* file and refs tables: bordered cards, rows inside */
195table.tree, table.refs {
196 border: 1px solid var(--line);
197 border-radius: var(--r-lg);
198 border-collapse: separate;
199 border-spacing: 0;
200 overflow: hidden;
201 margin-bottom: var(--sp-4);
202}
203table.tree td, table.refs td {
204 padding: var(--sp-2) var(--sp-4);
205 border-bottom: 1px solid var(--faint);
206}
207table.tree tr:last-child td, table.refs tr:last-child td { border-bottom: none; }
208table.tree tr:hover td, table.refs tr:hover td { background: var(--surface); }
190209 table.tree td.name { width: 100%; }
191210 table.tree td.name a { color: var(--fg); }
192211 table.tree td.name a:hover { color: var(--accent); }
@@ -210,14 +229,23 @@ table.refs td.sha { color: var(--muted); }
210229 }
211230
212231 /* log */
213ul.loglist { list-style: none; margin: var(--sp-3) 0; padding: 0; }
232ul.loglist {
233 list-style: none;
234 margin: var(--sp-3) 0;
235 padding: 0;
236 border: 1px solid var(--line);
237 border-radius: var(--r-lg);
238 overflow: hidden;
239}
214240 ul.loglist li {
215241 display: flex;
216242 align-items: baseline;
217243 gap: var(--sp-3);
218 padding: var(--sp-2) 0;
244 padding: var(--sp-2) var(--sp-4);
219245 border-bottom: 1px solid var(--faint);
220246 }
247ul.loglist li:last-child { border-bottom: none; }
248ul.loglist li:hover { background: var(--surface); }
221249 ul.loglist .commitmain { flex: 1; min-width: 0; }
222250 ul.loglist p { margin: 0; }
223251 ul.loglist .subject a { color: var(--fg); font-weight: 550; }
@@ -242,14 +270,59 @@ code.fullsha { color: var(--muted); overflow-wrap: anywhere; }
242270 svg.icon { vertical-align: -0.125em; }
243271 .lede { font-size: var(--fs-3); margin: var(--sp-2) 0; }
244272
245/* repo listings (index, owner pages) */
246ul.repolist { list-style: none; margin: var(--sp-2) 0; padding: 0; }
247ul.repolist li { padding: var(--sp-3) 0; border-bottom: 1px solid var(--faint); }
248ul.repolist li.empty { color: var(--muted); }
249ul.repolist p { margin: 0; }
250ul.repolist .reponame { font-size: var(--fs-3); font-weight: 550; }
251ul.repolist .desc { margin-top: 0.1rem; }
252ul.repolist .meta { margin-top: 0.1rem; }
273/* repo listings (index, owner pages): responsive card grid */
274.repogrid {
275 display: grid;
276 grid-template-columns: repeat(auto-fill, minmax(19rem, 1fr));
277 gap: var(--sp-3);
278 margin: var(--sp-3) 0 var(--sp-5);
279}
280.repogrid .empty { color: var(--muted); grid-column: 1 / -1; }
281.repocard {
282 display: flex;
283 flex-direction: column;
284 gap: var(--sp-1);
285 border: 1px solid var(--line);
286 border-radius: var(--r-lg);
287 padding: var(--sp-3) var(--sp-4);
288 min-width: 0;
289}
290.repocard p { margin: 0; }
291.repocard .reponame {
292 font-weight: 550;
293 overflow: hidden;
294 text-overflow: ellipsis;
295 white-space: nowrap;
296}
297.repocard .desc {
298 font-size: var(--fs-2);
299 display: -webkit-box;
300 -webkit-line-clamp: 2;
301 -webkit-box-orient: vertical;
302 overflow: hidden;
303}
304.repocard .meta { margin-top: auto; padding-top: var(--sp-1); }
305.repocard .reponame .sep { color: var(--muted); margin: 0 0.1em; }
306
307/* owner profile header */
308.profilehead {
309 border-bottom: 1px solid var(--faint);
310 padding-bottom: var(--sp-4);
311 margin-bottom: var(--sp-4);
312}
313.profilehead h1 { margin: var(--sp-4) 0 var(--sp-1); }
314.profilehead .lede { margin: 0 0 var(--sp-1); }
315.profilehead .meta { margin: var(--sp-1) 0 0; }
316h2 .count { color: var(--muted); font-weight: 400; font-size: var(--fs-2); }
317
318/* heading row with actions on the right */
319.headrow {
320 display: flex;
321 align-items: baseline;
322 gap: var(--sp-4);
323 flex-wrap: wrap;
324}
325.headrow .spacer { flex: 1; }
253326
254327 /* member and org chips on owner pages */
255328 a.memberchip {
@@ -394,14 +467,23 @@ nav.filters a {
394467 nav.filters a:hover { color: var(--fg); background: var(--surface); text-decoration: none; }
395468 nav.filters a.active { color: var(--accent); background: color-mix(in srgb, var(--accent) 10%, transparent); }
396469
397ul.issuelist { list-style: none; margin: var(--sp-2) 0; padding: 0; }
470ul.issuelist {
471 list-style: none;
472 margin: var(--sp-2) 0;
473 padding: 0;
474 border: 1px solid var(--line);
475 border-radius: var(--r-lg);
476 overflow: hidden;
477}
398478 ul.issuelist li {
399479 display: flex;
400480 align-items: baseline;
401481 gap: var(--sp-3);
402 padding: var(--sp-3) 0;
482 padding: var(--sp-3) var(--sp-4);
403483 border-bottom: 1px solid var(--faint);
404484 }
485ul.issuelist li:last-child { border-bottom: none; }
486ul.issuelist li:hover { background: var(--surface); }
405487 ul.issuelist li.empty { display: block; color: var(--muted); }
406488 ul.issuelist .issuemain { flex: 1; min-width: 0; }
407489 ul.issuelist p { margin: 0; }
@@ -414,7 +496,10 @@ h2.issuetitle { margin-bottom: var(--sp-1); font-size: var(--fs-5); }
414496 .issuemeta { color: var(--muted); font-size: var(--fs-2); margin-top: 0; }
415497 .review { font-size: var(--fs-2); }
416498
417/* comment cards */
499/* comment cards; discussion stays at a readable measure */
500article.comment, form.commentform {
501 max-width: 48rem;
502}
418503 article.comment {
419504 border: 1px solid var(--faint);
420505 border-radius: var(--r-lg);
@@ -484,10 +569,8 @@ button:hover { filter: brightness(1.08); }
484569
485570 /* footer */
486571 footer {
487 max-width: 64rem;
488 margin: 0 auto;
489 padding: var(--sp-4) var(--sp-5);
490572 border-top: 1px solid var(--faint);
573 padding: var(--sp-4) 0;
491574 color: var(--muted);
492575 font-size: var(--fs-1);
493576 }
@@ -541,8 +624,9 @@ footer a:hover { color: var(--accent); }
541624
542625 /* mobile */
543626 @media (max-width: 40rem) {
544 header { padding: var(--sp-3) var(--sp-4); }
545 main { padding: var(--sp-2) var(--sp-4) var(--sp-6); }
627 .container { padding-left: var(--sp-4); padding-right: var(--sp-4); }
628 main.container { padding-top: var(--sp-2); }
629 .repogrid { grid-template-columns: 1fr; }
546630 .thread { margin-left: var(--sp-3); }
547631 table.tree td.mode { display: none; }
548632 .readme, .code { padding: var(--sp-3); }
internal/web/templates/index.html +14 −11
@@ -1,23 +1,26 @@
11 {{define "title"}}{{.Site}}{{end}}
22 {{define "content"}}
3<div class="headrow">
4<h1>repositories</h1>
5<span class="spacer"></span>
36 {{if .Viewer}}<p class="toolbar">logged in as {{.Viewer}} · <a href="/new">new repository</a> ·
47 <form method="post" action="/logout" class="inline"><button type="submit" class="linklike">logout</button></form></p>{{end}}
5<h1>repositories</h1>
6<ul class="repolist">
7{{range .Repos}}<li>
8 <p class="reponame"><a href="/{{.OwnerName}}">{{.OwnerName}}</a>/<a href="/{{.OwnerName}}/{{.Name}}">{{.Name}}</a>{{if .Settings.Archived}} <span class="chip chip-stale">archived</span>{{end}}</p>
8</div>
9<div class="repogrid">
10{{range .Repos}}<div class="repocard">
11 <p class="reponame"><a href="/{{.OwnerName}}">{{.OwnerName}}</a><span class="sep">/</span><a href="/{{.OwnerName}}/{{.Name}}">{{.Name}}</a>{{if .Settings.Archived}} <span class="chip chip-stale">archived</span>{{end}}</p>
912 {{if .Desc}}<p class="desc">{{.Desc}}</p>{{end}}
1013 <p class="meta">{{template "branchicon"}} {{.DefaultBranch}}</p>
11</li>
12{{else}}<li class="empty">no public repositories yet</li>{{end}}
13</ul>
14</div>
15{{else}}<p class="empty">no public repositories yet</p>{{end}}
16</div>
1417 {{if .Mine}}<h2>your private repositories</h2>
15<ul class="repolist">
16{{range .Mine}}<li>
18<div class="repogrid">
19{{range .Mine}}<div class="repocard">
1720 <p class="reponame"><a href="/{{.OwnerName}}/{{.Name}}">{{.OwnerName}}/{{.Name}}</a> <span class="chip chip-neutral">{{.Visibility}}</span></p>
1821 {{if .Desc}}<p class="desc">{{.Desc}}</p>{{end}}
1922 <p class="meta">{{template "branchicon"}} {{.DefaultBranch}}</p>
20</li>
23</div>
2124 {{end}}
22</ul>{{end}}
25</div>{{end}}
2326 {{end}}
internal/web/templates/layout.html +3 −3
@@ -9,13 +9,13 @@
99 </head>
1010 <body>
1111 <header>
12 <nav><a class="site" href="/"><svg class="mark" width="20" height="20" viewBox="0 0 24 24" aria-hidden="true"><rect x="1" y="1" width="22" height="22" rx="6" fill="var(--accent)"/><path d="M5 9.5c2-2.5 4-2.5 6 0s4 2.5 6 0" stroke="var(--accent-fg)" stroke-width="2.2" fill="none" stroke-linecap="round"/><path d="M5 15c2-2.5 4-2.5 6 0s4 2.5 6 0" stroke="var(--accent-fg)" stroke-width="2.2" fill="none" stroke-linecap="round"/></svg>{{.Site}}</a></nav>
12 <nav class="container"><a class="site" href="/"><svg class="mark" width="20" height="20" viewBox="0 0 24 24" aria-hidden="true"><rect x="1" y="1" width="22" height="22" rx="6" fill="var(--accent)"/><path d="M5 9.5c2-2.5 4-2.5 6 0s4 2.5 6 0" stroke="var(--accent-fg)" stroke-width="2.2" fill="none" stroke-linecap="round"/><path d="M5 15c2-2.5 4-2.5 6 0s4 2.5 6 0" stroke="var(--accent-fg)" stroke-width="2.2" fill="none" stroke-linecap="round"/></svg>{{.Site}}</a></nav>
1313 </header>
14<main>
14<main class="container">
1515 {{template "content" .}}
1616 </main>
1717 <footer>
18 <p>powered by <a href="https://gitbay.org/krz/gitbay">gitbay</a>{{with gitbayVersion}} · <code>{{.}}</code>{{end}}</p>
18 <div class="container"><p>powered by <a href="https://gitbay.org/krz/gitbay">gitbay</a>{{with gitbayVersion}} · <code>{{.}}</code>{{end}}</p></div>
1919 </footer>
2020 </body>
2121 </html>{{end}}
internal/web/templates/owner.html +8 −5
@@ -1,16 +1,19 @@
11 {{define "title"}}{{.Owner}} · {{.Site}}{{end}}
22 {{define "content"}}
3<section class="profilehead">
34 <h1>{{.Owner}} <span class="chip chip-neutral">{{.Kind}}</span></h1>
45 {{if .Profile.Description}}<p class="desc lede">{{.Profile.Description}}</p>{{end}}
56 {{if .Profile.Website}}<p class="meta"><a href="{{.Profile.Website}}" rel="nofollow me">{{.Profile.Website}}</a></p>{{end}}
67 {{if .Orgs}}<p class="meta">member of {{range .Orgs}}<a class="memberchip" href="/{{.Username}}">{{.Username}}</a> {{end}}</p>{{end}}
78 {{if .Members}}<p class="meta">members {{range .Members}}<a class="memberchip" href="/{{.Username}}">{{.Username}} <span class="role">{{.Role}}</span></a> {{end}}</p>{{end}}
8<ul class="repolist">
9{{range .Repos}}<li>
9</section>
10<h2>repositories <span class="count">{{len .Repos}}</span></h2>
11<div class="repogrid">
12{{range .Repos}}<div class="repocard">
1013 <p class="reponame"><a href="/{{.OwnerName}}/{{.Name}}">{{.Name}}</a>{{if eq .Visibility "private"}} <span class="chip chip-neutral">private</span>{{end}}{{if .Settings.Archived}} <span class="chip chip-stale">archived</span>{{end}}</p>
1114 {{if .Desc}}<p class="desc">{{.Desc}}</p>{{end}}
1215 <p class="meta">{{template "branchicon"}} {{.DefaultBranch}}</p>
13</li>
14{{else}}<li class="empty">no visible repositories</li>{{end}}
15</ul>
16</div>
17{{else}}<p class="empty">no visible repositories</p>{{end}}
18</div>
1619 {{end}}