Carbon: black shell, left repo rail, sharp lines, IBM Plex !63

merged merged by cmc on 2026-08-25 21:57 UTC · krz/gitbay:carbon into main

8 files changed, +187 −79

Layout: unified · split

internal/httpd/routes.go +5
@@ -40,6 +40,11 @@ func (s *Server) Routes() []Route {
40 Route{Method: "GET", Pattern: "/explore", Handler: s.explore}, 40 Route{Method: "GET", Pattern: "/explore", Handler: s.explore},
41 Route{Method: "GET", Pattern: "/privacy", Handler: s.privacy}, 41 Route{Method: "GET", Pattern: "/privacy", Handler: s.privacy},
42 Route{Method: "GET", Pattern: "/static/style.css", Handler: s.stylesheet}, 42 Route{Method: "GET", Pattern: "/static/style.css", Handler: s.stylesheet},
43 // Literal per-file routes: a {name} wildcard is ambiguous against
44 // /{owner}/{repo}/... patterns in ServeMux precedence.
45 Route{Method: "GET", Pattern: "/static/fonts/plex-sans.woff2", Handler: s.font},
46 Route{Method: "GET", Pattern: "/static/fonts/plex-mono-400.woff2", Handler: s.font},
47 Route{Method: "GET", Pattern: "/static/fonts/plex-mono-500.woff2", Handler: s.font},
43 Route{Method: "GET", Pattern: "/favicon.svg", Handler: s.favicon}, 48 Route{Method: "GET", Pattern: "/favicon.svg", Handler: s.favicon},
44 Route{Method: "GET", Pattern: "/{owner}", Handler: s.ownerPage}, 49 Route{Method: "GET", Pattern: "/{owner}", Handler: s.ownerPage},
45 Route{Method: "GET", Pattern: "/{owner}/{repo}", Handler: s.repoHome}, 50 Route{Method: "GET", Pattern: "/{owner}/{repo}", Handler: s.repoHome},
internal/httpd/web.go +13
@@ -62,6 +62,19 @@ func (s *Server) favicon(w http.ResponseWriter, r *http.Request) {
62 w.Write(web.FaviconSVG) 62 w.Write(web.FaviconSVG)
63} 63}
64 64
65// font serves the embedded IBM Plex subsets. Same-origin, so the CSP's
66// default-src 'self' covers it — no font CDN.
67func (s *Server) font(w http.ResponseWriter, r *http.Request) {
68 data, err := web.FontFS.ReadFile("static" + r.URL.Path[len("/static"):])
69 if err != nil {
70 http.NotFound(w, r)
71 return
72 }
73 w.Header().Set("Content-Type", "font/woff2")
74 w.Header().Set("Cache-Control", "public, max-age=604800, immutable")
75 w.Write(data)
76}
77
65// notFound renders the designed 404 page with a 404 status. Falls back to 78// notFound renders the designed 404 page with a 404 status. Falls back to
66// the stock plain-text response if the template fails. 79// the stock plain-text response if the template fails.
67func (s *Server) notFound(w http.ResponseWriter, r *http.Request) { 80func (s *Server) notFound(w http.ResponseWriter, r *http.Request) {
internal/web/static/fonts/plex-mono-400.woff2 added

Binary file not shown.

internal/web/static/fonts/plex-mono-500.woff2 added

Binary file not shown.

internal/web/static/fonts/plex-sans.woff2 added

Binary file not shown.

internal/web/static/style.css +157 −74
@@ -1,32 +1,62 @@
1/* gitbay stylesheet. One file, light and dark via prefers-color-scheme. 1/* gitbay stylesheet, Carbon-school: black shell, sharp lines, one blue.
2 Tokens first; components use tokens only. No external requests. */ 2 One file, light and dark via prefers-color-scheme. Tokens first;
3 components use tokens only. No external requests: IBM Plex is served
4 from this origin. */
5
6@font-face {
7 font-family: "IBM Plex Sans";
8 font-style: normal;
9 font-weight: 400 600;
10 font-display: swap;
11 src: url(/static/fonts/plex-sans.woff2) format("woff2");
12}
13@font-face {
14 font-family: "IBM Plex Mono";
15 font-style: normal;
16 font-weight: 400;
17 font-display: swap;
18 src: url(/static/fonts/plex-mono-400.woff2) format("woff2");
19}
20@font-face {
21 font-family: "IBM Plex Mono";
22 font-style: normal;
23 font-weight: 500;
24 font-display: swap;
25 src: url(/static/fonts/plex-mono-500.woff2) format("woff2");
26}
3 27
4:root { 28:root {
5 /* declare native light+dark support: correct form controls and 29 /* declare native light+dark support: correct form controls and
6 scrollbars per scheme, and opt out of browser auto-darkening */ 30 scrollbars per scheme, and opt out of browser auto-darkening */
7 color-scheme: light dark; 31 color-scheme: light dark;
8 32
9 /* palette */ 33 /* palette (Carbon g10) */
10 --bg: #ffffff; 34 --bg: #f4f4f4;
11 --fg: #1f2328; 35 --fg: #161616;
12 --muted: #667079; 36 --muted: #525252;
13 --faint: #eceff1; /* hairlines, row separators */ 37 --faint: #ececec; /* hairlines, row separators */
14 --line: #d7dde2; /* control borders, card edges */ 38 --line: #e0e0e0; /* control borders, tile edges */
15 --surface: #f6f8fa; /* cards, code, table hover */ 39 --surface: #ffffff; /* tiles, cards */
16 --accent: #0000f0; /* blue: links, active tab, primary */ 40 --hover: #f0f2f8; /* row hover on tiles */
41 --accent: #0f62fe; /* blue-60: links, active nav, primary */
17 --accent-fg: #ffffff; /* text on accent */ 42 --accent-fg: #ffffff; /* text on accent */
18 --ok: #1a7f37; 43 --ok: #198038;
19 --warn: #9a6700; 44 --warn: #8e6a00;
20 --bad: #cf222e; 45 --bad: #da1e28;
21 --done: #8250df; /* merged */ 46 --done: #8a3ffc; /* merged */
22 --neutral: #667079; 47 --neutral: #525252;
23 --code-bg: #f6f8fa; 48 --code-bg: #f4f4f4;
49 /* the shell is ink in both schemes */
50 --shell-bg: #161616;
51 --shell-fg: #f4f4f4;
52 --shell-muted: #c6c6c6;
53 --shell-line: #161616;
24 54
25 /* type */ 55 /* type */
26 --sans: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, 56 --sans: "IBM Plex Sans", -apple-system, BlinkMacSystemFont, "Segoe UI",
27 "Helvetica Neue", Arial, sans-serif; 57 Roboto, "Helvetica Neue", Arial, sans-serif;
28 --mono: ui-monospace, "SF Mono", SFMono-Regular, Menlo, Consolas, 58 --mono: "IBM Plex Mono", ui-monospace, "SF Mono", SFMono-Regular, Menlo,
29 "Liberation Mono", monospace; 59 Consolas, "Liberation Mono", monospace;
30 --fs-0: 0.8rem; /* 12.8px: chips, fine print */ 60 --fs-0: 0.8rem; /* 12.8px: chips, fine print */
31 --fs-1: 0.866rem; /* ~13px: metadata, mono in tables */ 61 --fs-1: 0.866rem; /* ~13px: metadata, mono in tables */
32 --fs-2: 0.933rem; /* 14px: dense table text */ 62 --fs-2: 0.933rem; /* 14px: dense table text */
@@ -42,28 +72,31 @@
42 --sp-5: 1.5rem; 72 --sp-5: 1.5rem;
43 --sp-6: 2.5rem; 73 --sp-6: 2.5rem;
44 74
45 /* radii */ 75 /* radii: sharp everywhere; only tags stay pills */
46 --r-sm: 4px; 76 --r-sm: 0;
47 --r-md: 6px; 77 --r-md: 0;
48 --r-lg: 8px; 78 --r-lg: 0;
49 --r-pill: 999px; 79 --r-pill: 999px;
50} 80}
51@media (prefers-color-scheme: dark) { 81@media (prefers-color-scheme: dark) {
52 :root { 82 :root {
53 --bg: #0d1117; 83 /* Carbon g100 */
54 --fg: #e6edf3; 84 --bg: #161616;
55 --muted: #8d96a0; 85 --fg: #f4f4f4;
56 --faint: #1d242c; 86 --muted: #a8a8a8;
57 --line: #30363d; 87 --faint: #2a2a2a;
58 --surface: #161b22; 88 --line: #393939;
59 --accent: #8899ff; 89 --surface: #262626;
60 --accent-fg: #0a1030; 90 --hover: #2e3440;
61 --ok: #3fb950; 91 --accent: #78a9ff; /* blue-40 reads on dark grounds */
62 --warn: #d29922; 92 --accent-fg: #161616;
63 --bad: #f85149; 93 --ok: #42be65;
64 --done: #a371f7; 94 --warn: #d2a106;
65 --neutral: #8d96a0; 95 --bad: #fa4d56;
66 --code-bg: #161b22; 96 --done: #a56eff;
97 --neutral: #a8a8a8;
98 --code-bg: #1e1e1e;
99 --shell-line: #393939;
67 } 100 }
68} 101}
69 102
@@ -100,36 +133,68 @@ code, pre, .code, td.mode, td.size {
100 margin: 0 auto; 133 margin: 0 auto;
101 padding: 0 var(--sp-5); 134 padding: 0 var(--sp-5);
102} 135}
136/* the shell: an ink bar in both schemes, Carbon UI-shell style */
103header { 137header {
104 border-bottom: 1px solid var(--faint); 138 background: var(--shell-bg);
105 padding: var(--sp-3) 0; 139 border-bottom: 1px solid var(--shell-line);
140 padding: 0;
106} 141}
107a.site { 142a.site {
108 font-weight: 650; 143 font-weight: 600;
109 color: var(--fg); 144 color: var(--shell-fg);
110 letter-spacing: -0.01em; 145 letter-spacing: -0.01em;
111 display: inline-flex; 146 display: inline-flex;
112 align-items: center; 147 align-items: center;
113 gap: var(--sp-2); 148 gap: var(--sp-2);
114} 149}
115a.site:hover { text-decoration: none; color: var(--accent); } 150a.site:hover { text-decoration: none; color: #ffffff; }
116a.site svg.mark { display: block; } 151a.site svg.mark { display: block; }
152a.site svg.mark rect { fill: #0f62fe; }
153a.site svg.mark path { stroke: #ffffff; }
117nav.topnav { 154nav.topnav {
118 display: flex; 155 display: flex;
119 align-items: center; 156 align-items: center;
120 gap: var(--sp-4); 157 gap: 0;
158 min-height: 3rem;
121} 159}
122nav.topnav .spacer { flex: 1; } 160nav.topnav .spacer { flex: 1; }
123nav.topnav > a:not(.site) { color: var(--fg); font-size: var(--fs-2); } 161nav.topnav > a:not(.site) {
124nav.topnav > a:not(.site):hover { color: var(--accent); text-decoration: none; } 162 color: var(--shell-muted);
125nav.topnav a.navuser { font-weight: 600; } 163 font-size: var(--fs-2);
126nav.topnav button.linklike { color: var(--muted); } 164 padding: 0.85rem var(--sp-3);
127nav.topnav button.linklike:hover { color: var(--accent); text-decoration: none; } 165 line-height: 1;
166}
167nav.topnav > a:not(.site):hover { color: #ffffff; background: #353535; text-decoration: none; }
168nav.topnav a.navuser {
169 font-weight: 600;
170 color: #ffffff;
171 box-shadow: inset 0 -3px 0 #0f62fe;
172}
173nav.topnav button.linklike { color: var(--shell-muted); padding: 0.85rem var(--sp-3); }
174nav.topnav button.linklike:hover { color: #ffffff; background: #353535; text-decoration: none; }
128main.container { 175main.container {
129 width: 100%; 176 width: 100%;
130 padding-top: var(--sp-3); 177 padding-top: var(--sp-3);
131 padding-bottom: var(--sp-6); 178 padding-bottom: var(--sp-6);
132} 179}
180/* repo pages carry a left rail: the repohead spans the top, the sidenav
181 takes column one, everything after flows in column two. Browsers
182 without :has() fall back to single-column flow, which still works. */
183main.container:has(> nav.tabs) {
184 display: grid;
185 grid-template-columns: 11.5rem minmax(0, 1fr);
186 column-gap: var(--sp-6);
187 max-width: 80rem;
188}
189main.container:has(> nav.tabs) > * { grid-column: 2; }
190main.container:has(> nav.tabs) > .repohead { grid-column: 1 / -1; }
191main.container:has(> nav.tabs) > nav.tabs {
192 grid-column: 1;
193 grid-row: 2 / span 99;
194 align-self: start;
195 position: sticky;
196 top: var(--sp-4);
197}
133 198
134/* tables */ 199/* tables */
135table { border-collapse: collapse; width: 100%; } 200table { border-collapse: collapse; width: 100%; }
@@ -164,24 +229,35 @@ button.pinbtn.pinned { color: var(--accent); border-color: var(--accent); }
164a.chip.label:hover, a.chip.topic:hover { text-decoration: none; filter: brightness(1.15); } 229a.chip.label:hover, a.chip.topic:hover { text-decoration: none; filter: brightness(1.15); }
165.repohead .desc { margin: 0 0 var(--sp-2); } 230.repohead .desc { margin: 0 0 var(--sp-2); }
166 231
167/* repo tabs */ 232/* repo rail: grouped vertical navigation on the left */
168nav.tabs { 233nav.tabs { display: flex; flex-direction: column; margin-top: var(--sp-1); }
169 display: flex; 234nav.tabs .glabel {
170 flex-wrap: wrap; 235 font-size: var(--fs-0);
171 gap: var(--sp-1); 236 color: var(--muted);
172 border-bottom: 1px solid var(--faint); 237 letter-spacing: 0.04em;
173 margin-top: var(--sp-3); 238 margin: var(--sp-4) 0 var(--sp-1);
174} 239}
240nav.tabs .glabel:first-child { margin-top: 0; }
175nav.tabs a { 241nav.tabs a {
176 color: var(--muted); 242 color: var(--fg);
177 padding: var(--sp-1) var(--sp-2); 243 padding: 0.32rem var(--sp-3);
178 border-radius: var(--r-md) var(--r-md) 0 0;
179 border-bottom: 2px solid transparent;
180 margin-bottom: -1px;
181 font-size: var(--fs-2); 244 font-size: var(--fs-2);
245 border-left: 3px solid transparent;
246}
247nav.tabs a:hover { background: var(--hover); text-decoration: none; }
248nav.tabs a.active {
249 border-left-color: var(--accent);
250 background: var(--surface);
251 font-weight: 600;
252}
253@media (max-width: 52rem) {
254 /* the rail folds back into a wrapping strip on narrow screens */
255 main.container:has(> nav.tabs) { display: block; }
256 nav.tabs { flex-direction: row; flex-wrap: wrap; gap: 0 var(--sp-1); border-bottom: 1px solid var(--line); padding-bottom: var(--sp-2); position: static; }
257 nav.tabs .glabel { display: none; }
258 nav.tabs a { border-left: none; border-bottom: 3px solid transparent; padding: var(--sp-1) var(--sp-2); }
259 nav.tabs a.active { border-bottom-color: var(--accent); background: none; }
182} 260}
183nav.tabs a:hover { color: var(--fg); background: var(--surface); text-decoration: none; }
184nav.tabs a.active { color: var(--fg); border-bottom-color: var(--accent); font-weight: 550; }
185 261
186.clone { color: var(--muted); font-size: var(--fs-2); } 262.clone { color: var(--muted); font-size: var(--fs-2); }
187.clone code { 263.clone code {
@@ -239,11 +315,12 @@ details.refmenu .refdrop a {
239 font-family: var(--mono); 315 font-family: var(--mono);
240 font-size: var(--fs-1); 316 font-size: var(--fs-1);
241} 317}
242details.refmenu .refdrop a:hover { background: var(--surface); text-decoration: none; } 318details.refmenu .refdrop a:hover { background: var(--hover); text-decoration: none; }
243details.refmenu .refdrop a.allrefs { color: var(--accent); font-family: var(--sans); border-top: 1px solid var(--faint); margin-top: var(--sp-1); } 319details.refmenu .refdrop a.allrefs { color: var(--accent); font-family: var(--sans); border-top: 1px solid var(--faint); margin-top: var(--sp-1); }
244 320
245/* file and refs tables: bordered cards, rows inside */ 321/* file and refs tables: bordered cards, rows inside */
246table.tree, table.refs { 322table.tree, table.refs {
323 background: var(--surface);
247 border: 1px solid var(--line); 324 border: 1px solid var(--line);
248 border-radius: var(--r-lg); 325 border-radius: var(--r-lg);
249 border-collapse: separate; 326 border-collapse: separate;
@@ -256,7 +333,7 @@ table.tree td, table.refs td {
256 border-bottom: 1px solid var(--faint); 333 border-bottom: 1px solid var(--faint);
257} 334}
258table.tree tr:last-child td, table.refs tr:last-child td { border-bottom: none; } 335table.tree tr:last-child td, table.refs tr:last-child td { border-bottom: none; }
259table.tree tr:hover td, table.refs tr:hover td { background: var(--surface); } 336table.tree tr:hover td, table.refs tr:hover td { background: var(--hover); }
260table.tree tr.cols th { 337table.tree tr.cols th {
261 text-align: left; 338 text-align: left;
262 font-weight: 500; 339 font-weight: 500;
@@ -264,9 +341,9 @@ table.tree tr.cols th {
264 color: var(--muted); 341 color: var(--muted);
265 padding: var(--sp-1) var(--sp-4); 342 padding: var(--sp-1) var(--sp-4);
266 border-bottom: 1px solid var(--line); 343 border-bottom: 1px solid var(--line);
267 background: var(--surface); 344 background: var(--bg);
268} 345}
269table.tree tr.cols:hover th { background: var(--surface); } 346table.tree tr.cols:hover th { background: var(--bg); }
270table.tree th.size { text-align: right; } 347table.tree th.size { text-align: right; }
271table.tree td.name { width: 100%; } 348table.tree td.name { width: 100%; }
272table.tree td.name a { color: var(--fg); } 349table.tree td.name a { color: var(--fg); }
@@ -286,7 +363,7 @@ table.refs td.sha { color: var(--muted); }
286 border-bottom: 1px solid var(--faint); 363 border-bottom: 1px solid var(--faint);
287 margin: calc(-1 * var(--sp-4)) calc(-1 * var(--sp-5)) var(--sp-4); 364 margin: calc(-1 * var(--sp-4)) calc(-1 * var(--sp-5)) var(--sp-4);
288 padding: var(--sp-2) var(--sp-5); 365 padding: var(--sp-2) var(--sp-5);
289 background: var(--surface); 366 background: var(--bg);
290 border-radius: var(--r-lg) var(--r-lg) 0 0; 367 border-radius: var(--r-lg) var(--r-lg) 0 0;
291} 368}
292 369
@@ -295,6 +372,7 @@ ul.loglist {
295 list-style: none; 372 list-style: none;
296 margin: var(--sp-3) 0; 373 margin: var(--sp-3) 0;
297 padding: 0; 374 padding: 0;
375 background: var(--surface);
298 border: 1px solid var(--line); 376 border: 1px solid var(--line);
299 border-radius: var(--r-lg); 377 border-radius: var(--r-lg);
300 overflow: hidden; 378 overflow: hidden;
@@ -307,7 +385,7 @@ ul.loglist li {
307 border-bottom: 1px solid var(--faint); 385 border-bottom: 1px solid var(--faint);
308} 386}
309ul.loglist li:last-child { border-bottom: none; } 387ul.loglist li:last-child { border-bottom: none; }
310ul.loglist li:hover { background: var(--surface); } 388ul.loglist li:hover { background: var(--hover); }
311ul.loglist .commitmain { flex: 1; min-width: 0; } 389ul.loglist .commitmain { flex: 1; min-width: 0; }
312ul.loglist p { margin: 0; } 390ul.loglist p { margin: 0; }
313ul.loglist .subject a { color: var(--fg); font-weight: 550; } 391ul.loglist .subject a { color: var(--fg); font-weight: 550; }
@@ -337,6 +415,7 @@ ul.repolist {
337 list-style: none; 415 list-style: none;
338 margin: var(--sp-3) 0 var(--sp-5); 416 margin: var(--sp-3) 0 var(--sp-5);
339 padding: 0; 417 padding: 0;
418 background: var(--surface);
340 border: 1px solid var(--line); 419 border: 1px solid var(--line);
341 border-radius: var(--r-lg); 420 border-radius: var(--r-lg);
342 overflow: hidden; 421 overflow: hidden;
@@ -352,16 +431,17 @@ ul.pinlist {
352 gap: var(--sp-2); 431 gap: var(--sp-2);
353} 432}
354ul.pinlist li { 433ul.pinlist li {
434 background: var(--surface);
355 border: 1px solid var(--line); 435 border: 1px solid var(--line);
356 border-radius: var(--r-md); 436 border-radius: var(--r-md);
357 padding: var(--sp-1) var(--sp-3); 437 padding: var(--sp-1) var(--sp-3);
358} 438}
359ul.pinlist li:hover { background: var(--surface); } 439ul.pinlist li:hover { background: var(--hover); }
360ul.pinlist li a { color: var(--fg); } 440ul.pinlist li a { color: var(--fg); }
361ul.pinlist li a:hover { color: var(--accent); text-decoration: none; } 441ul.pinlist li a:hover { color: var(--accent); text-decoration: none; }
362ul.pinlist .sep { color: var(--muted); margin: 0 0.15em; } 442ul.pinlist .sep { color: var(--muted); margin: 0 0.15em; }
363ul.repolist li:last-child { border-bottom: none; } 443ul.repolist li:last-child { border-bottom: none; }
364ul.repolist li:hover { background: var(--surface); } 444ul.repolist li:hover { background: var(--hover); }
365ul.repolist li.empty { color: var(--muted); } 445ul.repolist li.empty { color: var(--muted); }
366ul.repolist p { margin: 0; } 446ul.repolist p { margin: 0; }
367ul.repolist .reponame { font-size: var(--fs-3); } 447ul.repolist .reponame { font-size: var(--fs-3); }
@@ -451,7 +531,8 @@ button.linklike:hover { text-decoration: underline; filter: none; }
451 531
452/* cards: README, comments */ 532/* cards: README, comments */
453.readme, .code { 533.readme, .code {
454 border: 1px solid var(--faint); 534 background: var(--surface);
535 border: 1px solid var(--line);
455 border-radius: var(--r-lg); 536 border-radius: var(--r-lg);
456 padding: var(--sp-4) var(--sp-5); 537 padding: var(--sp-4) var(--sp-5);
457 margin-top: var(--sp-4); 538 margin-top: var(--sp-4);
@@ -605,13 +686,14 @@ nav.filters a {
605 padding: 0.05rem var(--sp-2); 686 padding: 0.05rem var(--sp-2);
606 border-radius: var(--r-pill); 687 border-radius: var(--r-pill);
607} 688}
608nav.filters a:hover { color: var(--fg); background: var(--surface); text-decoration: none; } 689nav.filters a:hover { color: var(--fg); background: var(--hover); text-decoration: none; }
609nav.filters a.active { color: var(--accent); background: color-mix(in srgb, var(--accent) 10%, transparent); } 690nav.filters a.active { color: var(--accent); background: color-mix(in srgb, var(--accent) 10%, transparent); }
610 691
611ul.issuelist { 692ul.issuelist {
612 list-style: none; 693 list-style: none;
613 margin: var(--sp-2) 0; 694 margin: var(--sp-2) 0;
614 padding: 0; 695 padding: 0;
696 background: var(--surface);
615 border: 1px solid var(--line); 697 border: 1px solid var(--line);
616 border-radius: var(--r-lg); 698 border-radius: var(--r-lg);
617 overflow: hidden; 699 overflow: hidden;
@@ -624,7 +706,7 @@ ul.issuelist li {
624 border-bottom: 1px solid var(--faint); 706 border-bottom: 1px solid var(--faint);
625} 707}
626ul.issuelist li:last-child { border-bottom: none; } 708ul.issuelist li:last-child { border-bottom: none; }
627ul.issuelist li:hover { background: var(--surface); } 709ul.issuelist li:hover { background: var(--hover); }
628ul.issuelist li.empty { display: block; color: var(--muted); } 710ul.issuelist li.empty { display: block; color: var(--muted); }
629ul.issuelist .issuemain { flex: 1; min-width: 0; } 711ul.issuelist .issuemain { flex: 1; min-width: 0; }
630ul.issuelist p { margin: 0; } 712ul.issuelist p { margin: 0; }
@@ -633,6 +715,7 @@ ul.issuelist .title a:hover { color: var(--accent); }
633 715
634/* releases */ 716/* releases */
635article.release { 717article.release {
718 background: var(--surface);
636 border: 1px solid var(--line); 719 border: 1px solid var(--line);
637 border-radius: var(--r-lg); 720 border-radius: var(--r-lg);
638 padding: var(--sp-3) var(--sp-4); 721 padding: var(--sp-3) var(--sp-4);
internal/web/templates/layout.html +9 −5
@@ -34,19 +34,23 @@
34{{if .Repo.Settings.Website}}<p class="meta repowebsite"><a href="{{.Repo.Settings.Website}}" rel="nofollow">{{.Repo.Settings.Website}}</a></p>{{end}} 34{{if .Repo.Settings.Website}}<p class="meta repowebsite"><a href="{{.Repo.Settings.Website}}" rel="nofollow">{{.Repo.Settings.Website}}</a></p>{{end}}
35{{if .Topics}}<p class="topics">{{range .Topics}}<span class="chip topic">{{.}}</span> {{end}}</p>{{end}} 35{{if .Topics}}<p class="topics">{{range .Topics}}<span class="chip topic">{{.}}</span> {{end}}</p>{{end}}
36{{range .Mirrors}}<p class="meta mirrorline">{{if eq .Direction "push"}}mirrors to{{else}}mirrors from{{end}} <a href="{{.URL}}" rel="nofollow">{{.Target}}</a>{{if .Error}} · <span class="mirrorerr">sync error: {{.Error}}</span>{{else if .Synced}} · synced {{.Synced}}{{end}}</p> 36{{range .Mirrors}}<p class="meta mirrorline">{{if eq .Direction "push"}}mirrors to{{else}}mirrors from{{end}} <a href="{{.URL}}" rel="nofollow">{{.Target}}</a>{{if .Error}} · <span class="mirrorerr">sync error: {{.Error}}</span>{{else if .Synced}} · synced {{.Synced}}{{end}}</p>
37{{end}}<nav class="tabs"> 37{{end}}</div>
38<nav class="tabs">
39 <span class="glabel">code</span>
38 <a {{if eq .Tab "files"}}class="active" {{end}}href="/{{.Repo.OwnerName}}/{{.Repo.Name}}">files</a> 40 <a {{if eq .Tab "files"}}class="active" {{end}}href="/{{.Repo.OwnerName}}/{{.Repo.Name}}">files</a>
39 <a {{if eq .Tab "log"}}class="active" {{end}}href="/{{.Repo.OwnerName}}/{{.Repo.Name}}/log">log</a> 41 <a {{if eq .Tab "log"}}class="active" {{end}}href="/{{.Repo.OwnerName}}/{{.Repo.Name}}/log">log</a>
40 <a {{if eq .Tab "refs"}}class="active" {{end}}href="/{{.Repo.OwnerName}}/{{.Repo.Name}}/refs">refs</a> 42 <a {{if eq .Tab "refs"}}class="active" {{end}}href="/{{.Repo.OwnerName}}/{{.Repo.Name}}/refs">refs</a>
41 <a {{if eq .Tab "releases"}}class="active" {{end}}href="/{{.Repo.OwnerName}}/{{.Repo.Name}}/releases">releases</a> 43 <span class="glabel">work</span>
42 <a {{if eq .Tab "builds"}}class="active" {{end}}href="/{{.Repo.OwnerName}}/{{.Repo.Name}}/builds">builds</a>
43 <a {{if eq .Tab "issues"}}class="active" {{end}}href="/{{.Repo.OwnerName}}/{{.Repo.Name}}/issues">issues</a> 44 <a {{if eq .Tab "issues"}}class="active" {{end}}href="/{{.Repo.OwnerName}}/{{.Repo.Name}}/issues">issues</a>
44 <a {{if eq .Tab "merge requests"}}class="active" {{end}}href="/{{.Repo.OwnerName}}/{{.Repo.Name}}/mrs">merge requests</a> 45 <a {{if eq .Tab "merge requests"}}class="active" {{end}}href="/{{.Repo.OwnerName}}/{{.Repo.Name}}/mrs">merge requests</a>
46 <a {{if eq .Tab "builds"}}class="active" {{end}}href="/{{.Repo.OwnerName}}/{{.Repo.Name}}/builds">builds</a>
47 <span class="glabel">publish</span>
48 <a {{if eq .Tab "releases"}}class="active" {{end}}href="/{{.Repo.OwnerName}}/{{.Repo.Name}}/releases">releases</a>
45 {{if .HasWiki}}<a {{if eq .Tab "wiki"}}class="active" {{end}}href="/{{.Repo.OwnerName}}/{{.Repo.Name}}/wiki">wiki</a> 49 {{if .HasWiki}}<a {{if eq .Tab "wiki"}}class="active" {{end}}href="/{{.Repo.OwnerName}}/{{.Repo.Name}}/wiki">wiki</a>
46 {{end}}<a {{if eq .Tab "search"}}class="active" {{end}}href="/{{.Repo.OwnerName}}/{{.Repo.Name}}/search">search</a> 50 {{end}}<span class="glabel">tools</span>
51 <a {{if eq .Tab "search"}}class="active" {{end}}href="/{{.Repo.OwnerName}}/{{.Repo.Name}}/search">search</a>
47 <a href="/{{.Repo.OwnerName}}/{{.Repo.Name}}/archive/{{.Ref}}.tar.gz">archive</a> 52 <a href="/{{.Repo.OwnerName}}/{{.Repo.Name}}/archive/{{.Ref}}.tar.gz">archive</a>
48</nav> 53</nav>
49</div>
50{{end}} 54{{end}}
51 55
52{{define "reporow"}}<li> 56{{define "reporow"}}<li>
internal/web/web.go +3
@@ -21,6 +21,9 @@ var StyleCSS []byte
21//go:embed static/favicon.svg 21//go:embed static/favicon.svg
22var FaviconSVG []byte 22var FaviconSVG []byte
23 23
24//go:embed static/fonts/*.woff2
25var FontFS embed.FS
26
24// version returns the short VCS revision baked into the binary, or "" when 27// version returns the short VCS revision baked into the binary, or "" when
25// built outside a checkout. Used by the layout footer. 28// built outside a checkout. Used by the layout footer.
26var version = sync.OnceValue(func() string { 29var version = sync.OnceValue(func() string {