Migration This is my new public git source viewer. Source coming soon. Please contact with concerns/bugs. Contact me

static/css/base.css

554bcfc63721b22faf8bca78d5518d76e24a4d20 · 14.3 KB · 542 lines raw

1 /* gsg — a quiet, text-first interface in the spirit of cgit: hairlines
2 instead of boxes, no shadows, no hover animation. The one indulgence is
3 a black-and-purple signal color -- used for links, hashes, and a thin
4 two-tone stripe at the very top of the page -- so the theme still reads
5 as its own thing rather than "yet another dark git UI".
6
7 Loaded on every page: design tokens, reset, nav, page chrome, and the
8 handful of components (cards, tables, chips, facts-bar, footer,
9 notices) shared by more than one view. View-specific rules live in the
10 other files under static/css/, linked only by the views that need
11 them. */
12
13 :root {
14 /* Base — near-black ink */
15 --bg: #0F111A;
16 --bg-elev: #141725;
17 --surface: #1C2032;
18 --surface-2: #24283C;
19 --line: #2A3045;
20 --line-soft: #2E354E;
21
22 /* Foreground */
23 --fg: #FFFFFF;
24 --fg-muted: #9AA0B3;
25 --fg-subtle: #6B7280;
26
27 /* Accent — one signal color, not a rainbow */
28 --accent: #A78BFA;
29 --accent-2: #7C3AED;
30 --accent-grad: linear-gradient(90deg, var(--accent), var(--accent-2));
31
32 /* Status colors */
33 --add: #10B981;
34 --add-bg: rgba(16, 185, 129, 0.15);
35 --del: #EF4444;
36 --del-bg: rgba(239, 68, 68, 0.15);
37
38 /* Operator notice severities. One token pair per type; the .notice-*
39 modifiers below are the only place the palette is applied. */
40 --notice-critical: #EF4444;
41 --notice-critical-bg: rgba(239, 68, 68, 0.15);
42 --notice-warning: #F59E0B;
43 --notice-warning-bg: rgba(245, 158, 11, 0.15);
44 --notice-info: #60A5FA;
45 --notice-info-bg: rgba(96, 165, 250, 0.15);
46 --notice-fix: #10B981;
47 --notice-fix-bg: rgba(16, 185, 129, 0.15);
48
49 /* Counterpart to --accent -- tags and other "second kind of thing"
50 markers use this instead of a shade too close to the purple accent. */
51 --tag: #FAA78B;
52
53 /* Syntax highlighting -- deliberately just four colours (keyword,
54 string, number, name) plus a muted comment, not a full-spectrum
55 scheme. Blob view only; see static/css/blob.css. */
56 --syn-kw: #818CF8;
57 --syn-str: #34D399;
58 --syn-num: #FAA78B;
59 --syn-fn: #60A5FA;
60
61 /* Lane colors — spectrum for telling graph lanes apart, independent
62 of the brand accent above */
63 --lane-0: #818CF8;
64 --lane-1: #10B981;
65 --lane-2: #F59E0B;
66 --lane-3: #EC4899;
67 --lane-4: #3B82F6;
68 --lane-5: #84CC16;
69
70 /* Typography */
71 --mono: ui-monospace, "SFMono-Regular", "JetBrains Mono", "Cascadia Mono",
72 Menlo, Consolas, "Liberation Mono", monospace;
73 --sans: ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
74
75 /* Spacing & radius */
76 --r: 8px;
77 --r-sm: 4px;
78 --pad: clamp(.85rem, 2.5vw, 1.5rem);
79 }
80
81 /**@media (prefers-color-scheme: light) {
82 :root {
83 --bg: #F3F4F6;
84 --bg-elev: #FFFFFF;
85 --surface: #FFFFFF;
86 --surface-2: #F9FAFB;
87 --line: #E5E7EB;
88 --line-soft: #E5E7EB;
89 --fg: #111827;
90 --fg-muted: #6B7280;
91 --fg-subtle: #9CA3AF;
92 --accent: #7C3AED;
93 --accent-2: #5B21B6;
94 --add: #059669;
95 --del: #DC2626;
96 --tag: #B45309;
97 --notice-critical: #DC2626;
98 --notice-warning: #B45309;
99 --notice-info: #1D4ED8;
100 --notice-fix: #059669;
101 --syn-kw: #4F46E5;
102 --syn-str: #047857;
103 --syn-num: #B45309;
104 --syn-fn: #1D4ED8;
105 }
106 } **/
107
108 /* ---------------------------------------------------------------- reset & base */
109
110 *, *::before, *::after { box-sizing: border-box; }
111
112 html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; }
113
114 body {
115 margin: 0;
116 background: var(--bg);
117 color: var(--fg);
118 font-family: var(--mono);
119 font-size: 13px;
120 line-height: 1.55;
121 font-variant-ligatures: none;
122 -webkit-font-smoothing: antialiased;
123 }
124
125 /* The one graphic flourish: a thin two-tone stripe, always at the very
126 top of the viewport. Small enough to be quiet, present enough to be a
127 mark -- the rest of the page is just text and hairlines. */
128 body::before {
129 content: "";
130 position: fixed;
131 top: 0; left: 0; right: 0;
132 height: 3px;
133 background: var(--accent-grad);
134 z-index: 200;
135 }
136
137 a { color: var(--accent); text-decoration: none; }
138 a:hover { text-decoration: underline; }
139
140 /* Full viewport width, minus a small gutter -- no centered max-width
141 column. Anything wide (tables, diffs, the graph) scrolls inside its own
142 container instead of forcing the page to scroll horizontally. */
143 .wrap { width: 100%; margin: 0; padding-inline: var(--pad); }
144
145 /* ---------------------------------------------------------------- navigation */
146
147 .nav {
148 position: sticky; top: 3px; z-index: 100;
149 background: var(--bg-elev);
150 border-bottom: 1px solid var(--line);
151 }
152 .nav-inner {
153 display: grid;
154 grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr);
155 align-items: center;
156 gap: .75rem;
157 height: 3rem;
158 }
159
160 .nav-start {
161 display: flex; align-items: center; gap: .75rem;
162 grid-column: 1;
163 min-width: 0;
164 }
165
166 .nav-end {
167 grid-column: 3;
168 justify-self: end;
169 display: flex; align-items: center; gap: .75rem;
170 }
171
172 .nav-user {
173 font-size: .78rem;
174 color: var(--fg-muted);
175 white-space: nowrap;
176 }
177 .nav-user a { color: var(--fg-muted); }
178 .nav-user a:hover { color: var(--accent); }
179
180 .brand {
181 display: flex; align-items: center; gap: .5rem;
182 color: var(--fg); font-weight: 700; font-size: .95rem;
183 letter-spacing: -.02em;
184 }
185 .brand .logo {
186 width: 22px; height: 22px;
187 background: var(--accent);
188 border-radius: var(--r-sm);
189 display: grid; place-items: center;
190 color: var(--bg); font-weight: 700; font-size: .78rem;
191 }
192 .brand:hover { color: var(--accent); text-decoration: none; }
193
194 .repo-name {
195 color: var(--fg); font-weight: 600;
196 margin-left: .5rem;
197 }
198 .nav-repo-name { color: var(--line); }
199 .nav-repo-name:hover { color: var(--accent); }
200
201 /* Tabs — plain text, an underline marks the current page */
202 .tabs {
203 display: flex; gap: 1rem;
204 grid-column: 2;
205 justify-self: center;
206 }
207 .tab {
208 padding: .2rem 0;
209 color: var(--fg-muted);
210 font-size: .78rem;
211 border-bottom: 2px solid transparent;
212 }
213 .tab:hover {
214 color: var(--fg);
215 text-decoration: none;
216 }
217 .tab.active {
218 color: var(--fg);
219 border-bottom-color: var(--accent);
220 }
221
222 /* Ref picker — plain, borderless select */
223 .refpicker {
224 position: relative;
225 }
226 .refpicker select {
227 font: inherit; font-size: .78rem;
228 color: var(--fg-muted);
229 background: none;
230 border: 1px solid var(--line);
231 border-radius: var(--r-sm);
232 padding: .25rem 1.75rem .25rem .6rem;
233 cursor: pointer;
234 appearance: none;
235 background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'%3E%3Cpath fill='%236B7280' d='M6 8L1 3h10z'/%3E%3C/svg%3E");
236 background-repeat: no-repeat;
237 background-position: right .6rem center;
238 background-size: 10px 10px;
239 }
240 .refpicker select:hover { border-color: var(--accent); color: var(--fg); }
241 .refpicker select:focus-visible {
242 outline: 2px solid var(--accent);
243 outline-offset: -2px;
244 }
245
246 /* ---------------------------------------------------------------- page layout */
247
248 main.wrap { padding-top: 1.25rem; padding-bottom: 2rem; }
249
250 /* Hero: a plain text header with a hairline underneath -- no box, no
251 gradient. The page title carries the weight instead of a card. */
252 .hero {
253 margin-bottom: 1.25rem;
254 padding-bottom: .9rem;
255 border-bottom: 1px solid var(--line);
256 }
257
258 .page-title {
259 margin: 0;
260 font-size: 1.3rem;
261 font-weight: 700;
262 letter-spacing: -.02em;
263 line-height: 1.25;
264 /* Repository titles are often long unbroken slugs ("my-project-name").
265 Without this they push past the viewport instead of wrapping. Balance
266 avoids a single dangling word on the second line. */
267 overflow-wrap: anywhere;
268 text-wrap: balance;
269 }
270 .page-subtitle {
271 margin: .35rem 0 0;
272 color: var(--fg-muted);
273 font-size: .82rem;
274 }
275
276 .breadcrumbs {
277 display: flex; flex-wrap: wrap; gap: .4rem;
278 align-items: baseline;
279 margin-top: 1rem;
280 font-size: .9rem;
281 color: var(--fg-muted);
282 }
283 .breadcrumbs a { color: var(--accent); }
284 .breadcrumbs .here { color: var(--fg); }
285
286 /* ---------------------------------------------------------------- cards & containers */
287
288 /* Reserved for content that genuinely needs a boundary (a diff, a grouped
289 sub-section) -- a thin border, nothing else. Most pages don't use it. */
290 .card {
291 border: 1px solid var(--line);
292 border-radius: var(--r);
293 padding: .7rem .9rem;
294 margin-bottom: .75rem;
295 }
296
297 /* Facts bar — a plain line of label: value pairs, cgit-style, not a grid
298 of stat tiles. */
299 .facts-bar {
300 display: flex;
301 flex-wrap: wrap;
302 column-gap: 1.5rem;
303 row-gap: .3rem;
304 margin-bottom: 1rem;
305 font-size: .82rem;
306 }
307 .fact { display: flex; gap: .4rem; align-items: baseline; }
308 .fact-label {
309 color: var(--fg-subtle);
310 }
311 .fact-label::after { content: ":"; }
312 .fact-value {
313 color: var(--fg);
314 font-weight: 600;
315 }
316
317 /* ---------------------------------------------------------------- tables */
318
319 /* Borderless, hairline-separated rows -- like cgit's listings -- rather
320 than a bordered card. Tables can still be wider than the viewport (long
321 paths, many columns on mobile); scrolling lives on the table itself so
322 the page body never gains a horizontal scrollbar. */
323 .table-wrap { overflow-x: auto; }
324 .table {
325 width: 100%;
326 border-collapse: collapse;
327 }
328 .table th {
329 color: var(--fg-subtle);
330 font-size: .68rem;
331 text-transform: uppercase;
332 letter-spacing: .07em;
333 padding: .4rem .6rem;
334 text-align: left;
335 font-weight: 600;
336 border-bottom: 1px solid var(--line);
337 }
338 .table td {
339 padding: .4rem .6rem;
340 border-bottom: 1px solid var(--line-soft);
341 vertical-align: middle;
342 }
343 .table tr:hover td {
344 background: var(--surface-2);
345 }
346
347 /* ---------------------------------------------------------------- chips & badges */
348
349 .chips {
350 display: flex;
351 flex-wrap: wrap;
352 gap: .4rem;
353 list-style: none;
354 margin: 0;
355 padding: 0;
356 }
357 /* Plain bordered tags, not filled pill badges. */
358 .chip {
359 display: inline-flex;
360 align-items: center;
361 gap: .35rem;
362 font-size: .7rem;
363 padding: .2rem .5rem;
364 border: 1px solid var(--line);
365 border-radius: var(--r-sm);
366 color: var(--fg-muted);
367 background: none;
368 }
369 .chip:hover {
370 border-color: var(--accent);
371 color: var(--fg);
372 text-decoration: none;
373 }
374 .chip-tag:hover {
375 border-color: var(--tag);
376 }
377 .chip-branch { color: var(--accent); border-color: currentColor; }
378 .chip-tag { color: var(--tag); border-color: currentColor; }
379 .chip-ok { color: var(--add); border-color: currentColor; }
380 .chip-bad { color: var(--del); border-color: currentColor; }
381
382 .compare-form {
383 display: flex;
384 align-items: center;
385 gap: .4rem;
386 }
387 .compare-input {
388 font: inherit; font-size: .78rem;
389 color: var(--fg);
390 background: none;
391 border: 1px solid var(--line);
392 border-radius: var(--r-sm);
393 padding: .25rem .6rem;
394 min-width: 0;
395 flex: 1 1 20rem;
396 }
397 .compare-input:hover { border-color: var(--accent); }
398 .compare-input:focus-visible {
399 outline: 2px solid var(--accent);
400 outline-offset: -2px;
401 }
402 .compare-form .chip {
403 cursor: pointer;
404 font: inherit;
405 font-size: .7rem;
406 }
407
408 /* ---------------------------------------------------------------- footer */
409
410 footer {
411 margin-top: 1.5rem;
412 padding: 1rem 0;
413 border-top: 1px solid var(--line);
414 display: flex;
415 justify-content: space-between;
416 align-items: center;
417 flex-wrap: wrap;
418 gap: .75rem;
419 font-size: .8rem;
420 }
421 footer .footer-left {
422 display: flex;
423 gap: .75rem;
424 align-items: center;
425 }
426 footer .footer-right {
427 color: var(--fg-subtle);
428 font-size: .75rem;
429 }
430 footer a { color: var(--accent); }
431
432 /* ---------------------------------------------------------------- notices */
433
434 .notice {
435 padding: .5rem .8rem;
436 border-left: 2px solid var(--accent);
437 color: var(--fg-muted);
438 font-size: .8rem;
439 margin: .75rem 0;
440 }
441
442 /* Operator notices. The severity only changes the left rule and the
443 background tint; the base .notice keeps the shared shape. */
444 .notices { margin: .75rem 0 1rem; }
445 .notice-critical { border-left-color: var(--notice-critical); background: var(--notice-critical-bg); }
446 .notice-warning { border-left-color: var(--notice-warning); background: var(--notice-warning-bg); }
447 .notice-info { border-left-color: var(--notice-info); background: var(--notice-info-bg); }
448 .notice-fix { border-left-color: var(--notice-fix); background: var(--notice-fix-bg); }
449 .notice-title { display: block; color: var(--fg); }
450 .notice-body { white-space: pre-wrap; }
451 .notice-link { margin-left: .35rem; }
452
453 /* ---------------------------------------------------------------- error page */
454
455 .error-page { padding-top: 3rem; padding-bottom: 3rem; text-align: center; }
456 .error-code {
457 font-size: 3rem;
458 font-weight: 700;
459 color: var(--accent);
460 margin: 0;
461 letter-spacing: -.03em;
462 }
463 .error-page .page-title { margin-top: .25rem; }
464 .error-page .page-subtitle { margin-bottom: 1rem; }
465
466 /* ---------------------------------------------------------------- responsive */
467
468 @media (max-width: 640px) {
469 .nav-inner {
470 display: flex;
471 flex-wrap: wrap;
472 height: auto; padding-block: .5rem;
473 }
474 .nav-start {
475 flex: 1 1 auto;
476 /* Let the repo name drop below the brand rather than overflow the row. */
477 flex-wrap: wrap;
478 row-gap: .15rem;
479 }
480 .nav-repo-name {
481 min-width: 0;
482 margin-left: 0;
483 overflow-wrap: anywhere;
484 }
485 .nav-end { margin-left: auto; }
486 .tabs {
487 justify-self: auto;
488 width: 100%;
489 order: 3;
490 gap: .75rem;
491 }
492 .facts-bar {
493 flex-direction: column;
494 gap: .25rem;
495 }
496 .hero { padding-bottom: .7rem; }
497 .page-title {
498 font-size: 1.1rem;
499 }
500 }
501
502 @media (prefers-reduced-motion: reduce) {
503 *, *::before, *::after {
504 animation: none !important;
505 transition: none !important;
506 }
507 }
508
509 /* ---------------------------------------------------------------- utilities */
510
511 .hidden { display: none !important; }
512 .muted { color: var(--fg-muted); }
513 .text-accent { color: var(--accent); }
514 .font-mono { font-family: var(--mono); }
515 .w-full { width: 100%; }
516 .text-right { text-align: right !important; }
517 .text-center { text-align: center !important; }
518 .text-sm { font-size: .85rem; }
519 .text-xs { font-size: .75rem; }
520
521 /* Focus states for accessibility */
522 :focus-visible {
523 outline: 2px solid var(--accent);
524 outline-offset: 2px;
525 }
526
527 /* Scrollbar styling */
528 ::-webkit-scrollbar {
529 height: 8px;
530 width: 8px;
531 }
532 ::-webkit-scrollbar-track {
533 background: var(--bg-elev);
534 border-radius: 4px;
535 }
536 ::-webkit-scrollbar-thumb {
537 background: var(--line);
538 border-radius: 4px;
539 }
540 ::-webkit-scrollbar-thumb:hover {
541 background: var(--fg-subtle);
542 }