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

static/css/base.css

38fa4e6cd9bb9dbf0ae23ef02e50b1c75cd84ebb · 14.1 KB · 533 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 }
170
171 .brand {
172 display: flex; align-items: center; gap: .5rem;
173 color: var(--fg); font-weight: 700; font-size: .95rem;
174 letter-spacing: -.02em;
175 }
176 .brand .logo {
177 width: 22px; height: 22px;
178 background: var(--accent);
179 border-radius: var(--r-sm);
180 display: grid; place-items: center;
181 color: var(--bg); font-weight: 700; font-size: .78rem;
182 }
183 .brand:hover { color: var(--accent); text-decoration: none; }
184
185 .repo-name {
186 color: var(--fg); font-weight: 600;
187 margin-left: .5rem;
188 }
189 .nav-repo-name { color: var(--line); }
190 .nav-repo-name:hover { color: var(--accent); }
191
192 /* Tabs — plain text, an underline marks the current page */
193 .tabs {
194 display: flex; gap: 1rem;
195 grid-column: 2;
196 justify-self: center;
197 }
198 .tab {
199 padding: .2rem 0;
200 color: var(--fg-muted);
201 font-size: .78rem;
202 border-bottom: 2px solid transparent;
203 }
204 .tab:hover {
205 color: var(--fg);
206 text-decoration: none;
207 }
208 .tab.active {
209 color: var(--fg);
210 border-bottom-color: var(--accent);
211 }
212
213 /* Ref picker — plain, borderless select */
214 .refpicker {
215 position: relative;
216 }
217 .refpicker select {
218 font: inherit; font-size: .78rem;
219 color: var(--fg-muted);
220 background: none;
221 border: 1px solid var(--line);
222 border-radius: var(--r-sm);
223 padding: .25rem 1.75rem .25rem .6rem;
224 cursor: pointer;
225 appearance: none;
226 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");
227 background-repeat: no-repeat;
228 background-position: right .6rem center;
229 background-size: 10px 10px;
230 }
231 .refpicker select:hover { border-color: var(--accent); color: var(--fg); }
232 .refpicker select:focus-visible {
233 outline: 2px solid var(--accent);
234 outline-offset: -2px;
235 }
236
237 /* ---------------------------------------------------------------- page layout */
238
239 main.wrap { padding-top: 1.25rem; padding-bottom: 2rem; }
240
241 /* Hero: a plain text header with a hairline underneath -- no box, no
242 gradient. The page title carries the weight instead of a card. */
243 .hero {
244 margin-bottom: 1.25rem;
245 padding-bottom: .9rem;
246 border-bottom: 1px solid var(--line);
247 }
248
249 .page-title {
250 margin: 0;
251 font-size: 1.3rem;
252 font-weight: 700;
253 letter-spacing: -.02em;
254 line-height: 1.25;
255 /* Repository titles are often long unbroken slugs ("my-project-name").
256 Without this they push past the viewport instead of wrapping. Balance
257 avoids a single dangling word on the second line. */
258 overflow-wrap: anywhere;
259 text-wrap: balance;
260 }
261 .page-subtitle {
262 margin: .35rem 0 0;
263 color: var(--fg-muted);
264 font-size: .82rem;
265 }
266
267 .breadcrumbs {
268 display: flex; flex-wrap: wrap; gap: .4rem;
269 align-items: baseline;
270 margin-top: 1rem;
271 font-size: .9rem;
272 color: var(--fg-muted);
273 }
274 .breadcrumbs a { color: var(--accent); }
275 .breadcrumbs .here { color: var(--fg); }
276
277 /* ---------------------------------------------------------------- cards & containers */
278
279 /* Reserved for content that genuinely needs a boundary (a diff, a grouped
280 sub-section) -- a thin border, nothing else. Most pages don't use it. */
281 .card {
282 border: 1px solid var(--line);
283 border-radius: var(--r);
284 padding: .7rem .9rem;
285 margin-bottom: .75rem;
286 }
287
288 /* Facts bar — a plain line of label: value pairs, cgit-style, not a grid
289 of stat tiles. */
290 .facts-bar {
291 display: flex;
292 flex-wrap: wrap;
293 column-gap: 1.5rem;
294 row-gap: .3rem;
295 margin-bottom: 1rem;
296 font-size: .82rem;
297 }
298 .fact { display: flex; gap: .4rem; align-items: baseline; }
299 .fact-label {
300 color: var(--fg-subtle);
301 }
302 .fact-label::after { content: ":"; }
303 .fact-value {
304 color: var(--fg);
305 font-weight: 600;
306 }
307
308 /* ---------------------------------------------------------------- tables */
309
310 /* Borderless, hairline-separated rows -- like cgit's listings -- rather
311 than a bordered card. Tables can still be wider than the viewport (long
312 paths, many columns on mobile); scrolling lives on the table itself so
313 the page body never gains a horizontal scrollbar. */
314 .table-wrap { overflow-x: auto; }
315 .table {
316 width: 100%;
317 border-collapse: collapse;
318 }
319 .table th {
320 color: var(--fg-subtle);
321 font-size: .68rem;
322 text-transform: uppercase;
323 letter-spacing: .07em;
324 padding: .4rem .6rem;
325 text-align: left;
326 font-weight: 600;
327 border-bottom: 1px solid var(--line);
328 }
329 .table td {
330 padding: .4rem .6rem;
331 border-bottom: 1px solid var(--line-soft);
332 vertical-align: middle;
333 }
334 .table tr:hover td {
335 background: var(--surface-2);
336 }
337
338 /* ---------------------------------------------------------------- chips & badges */
339
340 .chips {
341 display: flex;
342 flex-wrap: wrap;
343 gap: .4rem;
344 list-style: none;
345 margin: 0;
346 padding: 0;
347 }
348 /* Plain bordered tags, not filled pill badges. */
349 .chip {
350 display: inline-flex;
351 align-items: center;
352 gap: .35rem;
353 font-size: .7rem;
354 padding: .2rem .5rem;
355 border: 1px solid var(--line);
356 border-radius: var(--r-sm);
357 color: var(--fg-muted);
358 background: none;
359 }
360 .chip:hover {
361 border-color: var(--accent);
362 color: var(--fg);
363 text-decoration: none;
364 }
365 .chip-tag:hover {
366 border-color: var(--tag);
367 }
368 .chip-branch { color: var(--accent); border-color: currentColor; }
369 .chip-tag { color: var(--tag); border-color: currentColor; }
370 .chip-ok { color: var(--add); border-color: currentColor; }
371 .chip-bad { color: var(--del); border-color: currentColor; }
372
373 .compare-form {
374 display: flex;
375 align-items: center;
376 gap: .4rem;
377 }
378 .compare-input {
379 font: inherit; font-size: .78rem;
380 color: var(--fg);
381 background: none;
382 border: 1px solid var(--line);
383 border-radius: var(--r-sm);
384 padding: .25rem .6rem;
385 min-width: 0;
386 flex: 1 1 20rem;
387 }
388 .compare-input:hover { border-color: var(--accent); }
389 .compare-input:focus-visible {
390 outline: 2px solid var(--accent);
391 outline-offset: -2px;
392 }
393 .compare-form .chip {
394 cursor: pointer;
395 font: inherit;
396 font-size: .7rem;
397 }
398
399 /* ---------------------------------------------------------------- footer */
400
401 footer {
402 margin-top: 1.5rem;
403 padding: 1rem 0;
404 border-top: 1px solid var(--line);
405 display: flex;
406 justify-content: space-between;
407 align-items: center;
408 flex-wrap: wrap;
409 gap: .75rem;
410 font-size: .8rem;
411 }
412 footer .footer-left {
413 display: flex;
414 gap: .75rem;
415 align-items: center;
416 }
417 footer .footer-right {
418 color: var(--fg-subtle);
419 font-size: .75rem;
420 }
421 footer a { color: var(--accent); }
422
423 /* ---------------------------------------------------------------- notices */
424
425 .notice {
426 padding: .5rem .8rem;
427 border-left: 2px solid var(--accent);
428 color: var(--fg-muted);
429 font-size: .8rem;
430 margin: .75rem 0;
431 }
432
433 /* Operator notices. The severity only changes the left rule and the
434 background tint; the base .notice keeps the shared shape. */
435 .notices { margin: .75rem 0 1rem; }
436 .notice-critical { border-left-color: var(--notice-critical); background: var(--notice-critical-bg); }
437 .notice-warning { border-left-color: var(--notice-warning); background: var(--notice-warning-bg); }
438 .notice-info { border-left-color: var(--notice-info); background: var(--notice-info-bg); }
439 .notice-fix { border-left-color: var(--notice-fix); background: var(--notice-fix-bg); }
440 .notice-title { display: block; color: var(--fg); }
441 .notice-body { white-space: pre-wrap; }
442 .notice-link { margin-left: .35rem; }
443
444 /* ---------------------------------------------------------------- error page */
445
446 .error-page { padding-top: 3rem; padding-bottom: 3rem; text-align: center; }
447 .error-code {
448 font-size: 3rem;
449 font-weight: 700;
450 color: var(--accent);
451 margin: 0;
452 letter-spacing: -.03em;
453 }
454 .error-page .page-title { margin-top: .25rem; }
455 .error-page .page-subtitle { margin-bottom: 1rem; }
456
457 /* ---------------------------------------------------------------- responsive */
458
459 @media (max-width: 640px) {
460 .nav-inner {
461 display: flex;
462 flex-wrap: wrap;
463 height: auto; padding-block: .5rem;
464 }
465 .nav-start {
466 flex: 1 1 auto;
467 /* Let the repo name drop below the brand rather than overflow the row. */
468 flex-wrap: wrap;
469 row-gap: .15rem;
470 }
471 .nav-repo-name {
472 min-width: 0;
473 margin-left: 0;
474 overflow-wrap: anywhere;
475 }
476 .nav-end { margin-left: auto; }
477 .tabs {
478 justify-self: auto;
479 width: 100%;
480 order: 3;
481 gap: .75rem;
482 }
483 .facts-bar {
484 flex-direction: column;
485 gap: .25rem;
486 }
487 .hero { padding-bottom: .7rem; }
488 .page-title {
489 font-size: 1.1rem;
490 }
491 }
492
493 @media (prefers-reduced-motion: reduce) {
494 *, *::before, *::after {
495 animation: none !important;
496 transition: none !important;
497 }
498 }
499
500 /* ---------------------------------------------------------------- utilities */
501
502 .hidden { display: none !important; }
503 .muted { color: var(--fg-muted); }
504 .text-accent { color: var(--accent); }
505 .font-mono { font-family: var(--mono); }
506 .w-full { width: 100%; }
507 .text-right { text-align: right !important; }
508 .text-center { text-align: center !important; }
509 .text-sm { font-size: .85rem; }
510 .text-xs { font-size: .75rem; }
511
512 /* Focus states for accessibility */
513 :focus-visible {
514 outline: 2px solid var(--accent);
515 outline-offset: 2px;
516 }
517
518 /* Scrollbar styling */
519 ::-webkit-scrollbar {
520 height: 8px;
521 width: 8px;
522 }
523 ::-webkit-scrollbar-track {
524 background: var(--bg-elev);
525 border-radius: 4px;
526 }
527 ::-webkit-scrollbar-thumb {
528 background: var(--line);
529 border-radius: 4px;
530 }
531 ::-webkit-scrollbar-thumb:hover {
532 background: var(--fg-subtle);
533 }