forge
ExploreLog in

Tahinli/forge

public
0 watching
★ 0
66.3 KiB
1// forge web UI. Plain CSS custom properties with a prefers-color-scheme dark theme.
2// No JavaScript; everything here is layout and presentation.
3
4// Fonts are bundled, not fetched from a CDN (the CSP is 'self' only). Files live in
5// style/fonts/ and are served by crates/forge-web/src/pages/assets.rs under /assets/fonts/.
6// Figtree (SIL OFL 1.1) is split into latin and latin-ext subsets, so browsers only fetch
7// the glyphs a page uses; the unicode-range values come from @fontsource-variable/figtree.
8@font-face {
9 font-family: "Figtree";
10 font-style: normal;
11 font-weight: 300 900;
12 font-display: swap;
13 src: url("/assets/fonts/figtree-5.3.0-latin-wght.woff2") format("woff2");
14 unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304,
15 U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
16}
17
18@font-face {
19 font-family: "Figtree";
20 font-style: normal;
21 font-weight: 300 900;
22 font-display: swap;
23 src: url("/assets/fonts/figtree-5.3.0-latin-ext-wght.woff2") format("woff2");
24 unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308,
25 U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113,
26 U+2C60-2C7F, U+A720-A7FF;
27}
28
29// IBM Plex Mono (SIL OFL 1.1), unmodified; "Plex" is a Reserved Font Name.
30@font-face {
31 font-family: "IBM Plex Mono";
32 font-style: normal;
33 font-weight: 400;
34 font-display: swap;
35 src: url("/assets/fonts/ibm-plex-mono-2.5.0-400.woff2") format("woff2");
36}
37
38@font-face {
39 font-family: "IBM Plex Mono";
40 font-style: normal;
41 font-weight: 500;
42 font-display: swap;
43 src: url("/assets/fonts/ibm-plex-mono-2.5.0-500.woff2") format("woff2");
44}
45
46@font-face {
47 font-family: "IBM Plex Mono";
48 font-style: normal;
49 font-weight: 600;
50 font-display: swap;
51 src: url("/assets/fonts/ibm-plex-mono-2.5.0-600.woff2") format("woff2");
52}
53
54@font-face {
55 font-family: "IBM Plex Mono";
56 font-style: italic;
57 font-weight: 400;
58 font-display: swap;
59 src: url("/assets/fonts/ibm-plex-mono-2.5.0-400-italic.woff2") format("woff2");
60}
61
62// Palette: sage mock tokens. The dark values are repeated in the two dark blocks
63// below and must stay identical.
64:root {
65 --bg: #F6F8F5;
66 --surface: #FFFFFF;
67 --surface-2: #EEF2EC;
68 --text: #1E2420;
69 --text-2: #3A433D;
70 --muted: #69736B;
71 --border: #DDE3DB;
72 --border-strong: #C5CFC3;
73 --accent: #5E7D63;
74 --accent-ink: #4A6750;
75 --accent-soft: #E4ECE3;
76 --accent-text: #FFFFFF;
77 --focus: #4A6750;
78 --ok: #1D7A62;
79 --warn: #9A6200;
80 --bad: #B0413A;
81 --danger: #B0413A;
82 --caution: #B0413A;
83 --danger-bg: #F8E1DE;
84 --alert-bg: #F8E1DE;
85 --alert-text: #7D2B25;
86 --code-bg: #EEF2EC;
87 --public-bg: #E4ECE3;
88 --public-text: #4A6750;
89 --private-bg: #EEF2EC;
90 --private-text: #3A433D;
91 --add-bg: #DCF2EA;
92 --add-text: #174D3F;
93 --add-mark: #1D7A62;
94 --del-bg: #F8E1DE;
95 --del-text: #7D2B25;
96 --del-mark: #B0413A;
97 --hunk-bg: #EDF1EB;
98 --hunk-text: #3F5F46;
99 --ln-text: #69736B;
100 --row-target: #FFF4C2;
101 --note: #4A6750;
102 --tip: #1D7A62;
103 --important: #6A4BB0;
104 --warning: #9A6200;
105 --h0: #E6ECE4;
106 --h1: #C6D9C8;
107 --h2: #9DBDA2;
108 --h3: #6F9676;
109 --h4: #3F5F46;
110 --shadow: 0 10px 28px rgba(30, 36, 32, .10);
111 --radius: 10px;
112 --font: "Figtree", system-ui, -apple-system, "Segoe UI", sans-serif;
113 --mono: "IBM Plex Mono", ui-monospace, "SFMono-Regular", Menlo, Consolas, monospace;
114 --topbar-h: 52px;
115 --sidebar-w: 232px;
116 color-scheme: light;
117 // Native checkboxes, radios and progress bars pick up the sage accent in every browser.
118 accent-color: var(--accent);
119}
120
121@media (prefers-color-scheme: dark) {
122 :root:not([data-theme="light"]) {
123 --bg: #121613;
124 --surface: #191E1A;
125 --surface-2: #1F261F;
126 --text: #E3E8E2;
127 --text-2: #C4CCC3;
128 --muted: #97A198;
129 --border: #2A322C;
130 --border-strong: #3A443C;
131 --accent: #9DBEA2;
132 --accent-ink: #B5D2B9;
133 --accent-soft: #233026;
134 --accent-text: #0F1510;
135 --focus: #9DBEA2;
136 --ok: #7CCBAE;
137 --warn: #E6B765;
138 --bad: #F09A90;
139 --danger: #F09A90;
140 --caution: #F09A90;
141 --danger-bg: #3A1E1C;
142 --alert-bg: #3A1E1C;
143 --alert-text: #F2B8B0;
144 --code-bg: #1F261F;
145 --public-bg: #233026;
146 --public-text: #B5D2B9;
147 --private-bg: #1F261F;
148 --private-text: #C4CCC3;
149 --add-bg: #143029;
150 --add-text: #A6E3CF;
151 --add-mark: #7CCBAE;
152 --del-bg: #3A1E1C;
153 --del-text: #F2B8B0;
154 --del-mark: #F09A90;
155 --hunk-bg: #1E2620;
156 --hunk-text: #9DBEA2;
157 --ln-text: #97A198;
158 --row-target: #3A3510;
159 --note: #B5D2B9;
160 --tip: #7CCBAE;
161 --important: #C4A7FF;
162 --warning: #E6B765;
163 --h0: #1F2722;
164 --h1: #2D3D31;
165 --h2: #41604A;
166 --h3: #628A6C;
167 --h4: #9DBEA2;
168 --shadow: 0 10px 28px rgba(0, 0, 0, .45);
169 color-scheme: dark;
170 }
171}
172
173:root[data-theme="light"] {
174 color-scheme: light;
175}
176
177:root[data-theme="dark"] {
178 --bg: #121613;
179 --surface: #191E1A;
180 --surface-2: #1F261F;
181 --text: #E3E8E2;
182 --text-2: #C4CCC3;
183 --muted: #97A198;
184 --border: #2A322C;
185 --border-strong: #3A443C;
186 --accent: #9DBEA2;
187 --accent-ink: #B5D2B9;
188 --accent-soft: #233026;
189 --accent-text: #0F1510;
190 --focus: #9DBEA2;
191 --ok: #7CCBAE;
192 --warn: #E6B765;
193 --bad: #F09A90;
194 --danger: #F09A90;
195 --caution: #F09A90;
196 --danger-bg: #3A1E1C;
197 --alert-bg: #3A1E1C;
198 --alert-text: #F2B8B0;
199 --code-bg: #1F261F;
200 --public-bg: #233026;
201 --public-text: #B5D2B9;
202 --private-bg: #1F261F;
203 --private-text: #C4CCC3;
204 --add-bg: #143029;
205 --add-text: #A6E3CF;
206 --add-mark: #7CCBAE;
207 --del-bg: #3A1E1C;
208 --del-text: #F2B8B0;
209 --del-mark: #F09A90;
210 --hunk-bg: #1E2620;
211 --hunk-text: #9DBEA2;
212 --ln-text: #97A198;
213 --row-target: #3A3510;
214 --note: #B5D2B9;
215 --tip: #7CCBAE;
216 --important: #C4A7FF;
217 --warning: #E6B765;
218 --h0: #1F2722;
219 --h1: #2D3D31;
220 --h2: #41604A;
221 --h3: #628A6C;
222 --h4: #9DBEA2;
223 --shadow: 0 10px 28px rgba(0, 0, 0, .45);
224 color-scheme: dark;
225}
226
227*,
228*::before,
229*::after {
230 box-sizing: border-box;
231}
232
233html {
234 -webkit-text-size-adjust: 100%;
235}
236
237body {
238 margin: 0;
239 background: var(--bg);
240 color: var(--text);
241 font-family: var(--font);
242 font-size: 15px;
243 line-height: 1.55;
244 -webkit-font-smoothing: antialiased;
245}
246
247a {
248 color: var(--accent-ink);
249 text-decoration: none;
250
251 &:hover {
252 text-decoration: underline;
253 }
254}
255
256:focus-visible {
257 outline: 2px solid var(--focus);
258 outline-offset: 2px;
259 border-radius: 6px;
260}
261
262h1,
263h2 {
264 line-height: 1.25;
265 margin: 0 0 0.5rem;
266 font-weight: 600;
267 letter-spacing: -0.01em;
268 color: var(--text);
269}
270
271h1 {
272 font-size: 1.625rem;
273}
274
275h2 {
276 font-size: 1rem;
277}
278
279code,
280pre {
281 font-family: var(--mono);
282 font-size: 0.875rem;
283}
284
285.container {
286 max-width: 960px;
287 margin: 0 auto;
288 padding: 0 16px;
289}
290
291.muted {
292 color: var(--muted);
293}
294
295.small {
296 font-size: 0.8125rem;
297}
298
299.lead {
300 font-size: 1.0625rem;
301 color: var(--muted);
302}
303
304// User menu (details/summary, works without JavaScript)
305
306.menu {
307 position: relative;
308
309 summary {
310 cursor: pointer;
311 list-style: none;
312 padding: 6px 10px;
313 border: 1px solid var(--border);
314 border-radius: var(--radius);
315
316 &::-webkit-details-marker {
317 display: none;
318 }
319 }
320
321 &[open] summary {
322 border-color: var(--accent);
323 }
324}
325
326.menu-list {
327 position: absolute;
328 right: 0;
329 top: calc(100% + 6px);
330 min-width: 200px;
331 margin: 0;
332 padding: 6px 0;
333 list-style: none;
334 background: var(--surface);
335 border: 1px solid var(--border);
336 border-radius: var(--radius);
337 box-shadow: var(--shadow);
338 z-index: 10;
339
340 li a,
341 .link-button {
342 display: block;
343 width: 100%;
344 padding: 8px 14px;
345 color: var(--text);
346 text-align: left;
347 }
348}
349
350// Main content
351
352main {
353 padding-top: 24px;
354 padding-bottom: 48px;
355 min-height: 70vh;
356}
357
358.site-footer {
359 color: var(--muted);
360 font-size: 0.8125rem;
361 padding-top: 16px;
362 padding-bottom: 24px;
363 border-top: 1px solid var(--border);
364}
365
366.page-head {
367 display: flex;
368 align-items: center;
369 justify-content: space-between;
370 gap: 12px;
371 flex-wrap: wrap;
372 margin-bottom: 16px;
373}
374
375.repo-title {
376 word-break: break-all;
377}
378
379.hero {
380 padding: 48px 0;
381 text-align: left;
382
383 h1 {
384 font-size: 2rem;
385 }
386}
387
388.actions {
389 display: flex;
390 gap: 12px;
391 flex-wrap: wrap;
392 margin-top: 16px;
393}
394
395// Cards and sections
396
397.card {
398 background: var(--surface);
399 border: 1px solid var(--border);
400 border-radius: var(--radius);
401 padding: 20px;
402 margin-bottom: 16px;
403
404 &.narrow {
405 max-width: 440px;
406 margin-left: auto;
407 margin-right: auto;
408 }
409
410 &.notice {
411 border-color: var(--accent);
412 }
413}
414
415// Buttons. Every button (including submits) is a neutral outline by default; the
416// accent fill is opt-in via class="primary", and destructive actions use "danger".
417
418.button,
419button {
420 display: inline-flex;
421 align-self: flex-start; // keep natural width inside flex columns (.stack, cards)
422 align-items: center;
423 gap: 8px;
424 height: 34px;
425 padding: 0 14px;
426 font: inherit;
427 font-size: 0.875rem;
428 font-weight: 500;
429 white-space: nowrap;
430 border-radius: var(--radius);
431 border: 1px solid var(--border-strong);
432 background: var(--surface);
433 color: var(--text);
434 cursor: pointer;
435 text-decoration: none;
436
437 &:hover {
438 text-decoration: none;
439 background: var(--surface-2);
440 }
441
442 &:disabled {
443 opacity: 0.5;
444 cursor: not-allowed;
445 }
446
447 &.primary {
448 background: var(--accent);
449 border-color: var(--accent);
450 color: var(--accent-text);
451
452 &:hover {
453 background: var(--accent-ink);
454 border-color: var(--accent-ink);
455 }
456 }
457
458 &.danger {
459 color: var(--danger);
460 border-color: var(--danger);
461 background: transparent;
462
463 &:hover {
464 background: var(--danger-bg);
465 }
466 }
467}
468
469// Text-style button (no chrome, reads as a link). Declared after the generic button
470// rules and qualified with `button.` so it beats `button[type=submit]`-style rules.
471.link-button,
472button.link-button {
473 display: inline;
474 height: auto;
475 padding: 0;
476 margin: 0;
477 gap: 0;
478 background: none;
479 border: 0;
480 border-radius: 0;
481 font: inherit;
482 font-size: inherit;
483 font-weight: inherit;
484 white-space: normal;
485 cursor: pointer;
486 color: var(--accent-ink);
487
488 &:hover {
489 background: none;
490 text-decoration: underline;
491 }
492}
493
494// Forms
495
496.stack {
497 display: flex;
498 flex-direction: column;
499 gap: 14px;
500}
501
502label {
503 display: flex;
504 flex-direction: column;
505 gap: 4px;
506
507 &.inline {
508 flex-direction: row;
509 align-items: center;
510 gap: 8px;
511 }
512
513 > span {
514 font-weight: 500;
515 font-size: 0.875rem;
516 color: var(--text-2);
517 }
518}
519
520input,
521select,
522textarea {
523 font: inherit;
524 color: var(--text);
525 background: var(--surface);
526 border: 1px solid var(--border-strong);
527 border-radius: var(--radius);
528 padding: 8px 12px;
529 width: 100%;
530 max-width: 100%;
531
532 &::placeholder {
533 color: var(--muted);
534 }
535
536 &:focus {
537 outline: none;
538 border-color: var(--focus);
539 box-shadow: 0 0 0 3px var(--accent-soft);
540 }
541}
542
543// Radios and checkboxes are not text fields: no full width, padding or border.
544input[type="radio"],
545input[type="checkbox"] {
546 flex: 0 0 auto;
547 width: 16px;
548 height: 16px;
549 min-width: 16px;
550 max-width: none;
551 margin: 0;
552 padding: 0;
553 border: 0;
554 accent-color: var(--accent);
555 box-shadow: none;
556}
557
558// Native dropdown lists take their colours from the option rule where the browser honours it;
559// color-scheme (set on :root above) covers the rest.
560select option,
561select optgroup {
562 background: var(--surface);
563 color: var(--text);
564}
565
566// Customizable selects (Chrome 135+): the opened list becomes a styled popover instead of the
567// square system menu. Other browsers keep the native popup, which follows color-scheme.
568// Multiple and listbox selects (size attribute) keep the native control.
569@supports (appearance: base-select) {
570 select:not([multiple]):not([size]),
571 select:not([multiple]):not([size])::picker(select) {
572 appearance: base-select;
573 }
574
575 select:not([multiple]):not([size])::picker-icon {
576 color: var(--muted);
577 transition: rotate 0.2s ease;
578 }
579
580 select:not([multiple]):not([size]):open::picker-icon {
581 rotate: 180deg;
582 }
583
584 select:not([multiple]):not([size])::picker(select) {
585 background: var(--surface);
586 border: 1px solid var(--border);
587 border-radius: var(--radius);
588 box-shadow: var(--shadow);
589 padding: 4px;
590 margin-top: 4px;
591 max-height: 320px;
592 overflow-y: auto;
593 outline: none;
594 }
595
596 select:not([multiple]):not([size]) option {
597 padding: 7px 10px;
598 border-radius: 6px;
599 color: var(--text);
600 background: transparent;
601 font: inherit;
602 }
603
604 select:not([multiple]):not([size]) option:hover,
605 select:not([multiple]):not([size]) option:focus-visible {
606 background: var(--surface-2);
607 }
608
609 select:not([multiple]):not([size]) option:checked {
610 background: var(--accent-soft);
611 color: var(--accent-ink);
612 font-weight: 600;
613 }
614
615 // The highlight already marks the selection, so the default tick is hidden.
616 select:not([multiple]):not([size]) option::checkmark {
617 display: none;
618 }
619
620 select:not([multiple]):not([size]) option:disabled {
621 color: var(--muted);
622 }
623}
624
625// File inputs are not text fields: no box, just the browser's button restyled as our neutral button.
626input[type="file"] {
627 width: auto;
628 max-width: 100%;
629 padding: 0;
630 border: 0;
631 border-radius: 0;
632 background: transparent;
633 box-shadow: none;
634 font-size: 0.875rem;
635 color: var(--text-2);
636}
637
638input[type="file"]::file-selector-button,
639input[type="file"]::-webkit-file-upload-button {
640 font: inherit;
641 font-size: 0.875rem;
642 font-weight: 500;
643 height: 34px;
644 padding: 0 14px;
645 margin-right: 10px;
646 border: 1px solid var(--border-strong);
647 border-radius: var(--radius);
648 background: var(--surface-2);
649 color: var(--text);
650 cursor: pointer;
651
652 &:hover {
653 background: var(--border);
654 }
655}
656
657textarea {
658 font-family: var(--mono);
659 font-size: 0.875rem;
660 resize: vertical;
661}
662
663fieldset {
664 border: 1px solid var(--border);
665 border-radius: var(--radius);
666 padding: 12px 14px;
667 margin: 0;
668 display: flex;
669 flex-direction: column;
670 gap: 8px;
671
672 legend {
673 font-weight: 600;
674 padding: 0 6px;
675 }
676}
677
678.inline-form {
679 display: inline-block;
680}
681
682.alert {
683 background: var(--alert-bg);
684 color: var(--alert-text);
685 border: 1px solid var(--bad);
686 padding: 10px 14px;
687 border-radius: var(--radius);
688 margin: 0 0 12px;
689}
690
691// Code blocks
692
693pre.code {
694 background: var(--code-bg);
695 border: 1px solid var(--border);
696 border-radius: var(--radius);
697 padding: 12px;
698 margin: 6px 0 14px;
699 overflow-x: auto;
700 white-space: pre;
701
702 code {
703 background: none;
704 padding: 0;
705 }
706}
707
708code {
709 background: var(--code-bg);
710 padding: 1px 5px;
711 border-radius: 4px;
712 word-break: break-all;
713}
714
715// Badges
716
717.badge {
718 display: inline-block;
719 font-size: 0.75rem;
720 font-weight: 500;
721 line-height: 18px;
722 white-space: nowrap;
723 border: 1px solid transparent;
724 padding: 0 9px;
725 border-radius: 999px;
726 margin-left: 6px;
727 vertical-align: middle;
728
729 &.public {
730 background: var(--public-bg);
731 color: var(--public-text);
732 }
733
734 &.private {
735 background: var(--private-bg);
736 color: var(--private-text);
737 border-color: var(--border-strong);
738 }
739}
740
741// Lists
742
743.repo-list,
744.branch-list,
745.item-list {
746 list-style: none;
747 margin: 0;
748 padding: 0;
749}
750
751.repo-item {
752 padding: 12px 0;
753 border-bottom: 1px solid var(--border);
754
755 &:last-child {
756 border-bottom: 0;
757 }
758
759 p {
760 margin: 4px 0 0;
761 }
762}
763
764.repo-name {
765 font-family: var(--mono);
766 font-weight: 500;
767 font-size: 0.9375rem;
768 color: var(--text);
769}
770
771.item {
772 display: flex;
773 align-items: center;
774 justify-content: space-between;
775 gap: 12px;
776 flex-wrap: wrap;
777 padding: 12px 0;
778 border-bottom: 1px solid var(--border);
779
780 &:last-child {
781 border-bottom: 0;
782 }
783
784 .item-main {
785 display: flex;
786 flex-direction: column;
787 gap: 2px;
788 min-width: 0;
789 word-break: break-word;
790 }
791}
792
793.branch-list li {
794 padding: 6px 0;
795}
796
797// Tables
798
799.table-wrap {
800 overflow-x: auto;
801}
802
803.table {
804 width: 100%;
805 border-collapse: collapse;
806 font-size: 0.875rem;
807
808 th,
809 td {
810 text-align: left;
811 padding: 12px 12px 12px 0;
812 border-bottom: 1px solid var(--border);
813 vertical-align: middle;
814 }
815
816 th {
817 padding-top: 10px;
818 padding-bottom: 10px;
819 font-size: 0.78125rem;
820 color: var(--muted);
821 font-weight: 500;
822 }
823}
824
825// Small screens
826
827@media (max-width: 600px) {
828 .card {
829 padding: 16px;
830 }
831
832 .page-head h1 {
833 font-size: 1.25rem;
834 }
835
836 .item {
837 align-items: flex-start;
838 }
839}
840
841// Code search
842
843.repo-search {
844 margin: 0 0 12px;
845
846 input {
847 width: 100%;
848 max-width: 360px;
849 }
850}
851
852.search-form {
853 margin: 0 0 16px;
854}
855
856.search-row {
857 display: flex;
858 flex-wrap: wrap;
859 gap: 8px;
860 align-items: center;
861 margin: 0 0 8px;
862
863 input[type="search"] {
864 flex: 1 1 280px;
865 }
866
867 input[type="text"] {
868 width: 200px;
869 }
870}
871
872.search-options label {
873 display: inline-flex;
874 gap: 4px;
875 align-items: center;
876}
877
878.search-file {
879 margin: 0 0 16px;
880}
881
882.search-file-name {
883 font-size: 1rem;
884 margin: 0 0 4px;
885}
886
887.search-hits {
888 list-style: none;
889 margin: 0;
890 padding: 0;
891
892 li {
893 display: flex;
894 gap: 8px;
895 padding: 2px 0;
896 }
897}
898
899.search-line {
900 white-space: pre-wrap;
901 word-break: break-word;
902 min-width: 0;
903}
904
905.search-results mark {
906 background: var(--row-target);
907 color: inherit;
908 border-radius: 2px;
909}
910
911// Repo tabs
912
913.repo-tabs {
914 display: flex;
915 gap: 4px;
916 border-bottom: 1px solid var(--border);
917 margin: 0 0 16px;
918 overflow-x: auto;
919 overflow-y: hidden;
920 scrollbar-width: none;
921
922 &::-webkit-scrollbar {
923 display: none;
924 }
925}
926
927.tab {
928 padding: 8px 12px;
929 margin-bottom: 0;
930 box-shadow: inset 0 -2px 0 transparent;
931 color: var(--muted);
932 white-space: nowrap;
933
934 &:hover {
935 color: var(--text);
936 text-decoration: none;
937 }
938
939 &.active {
940 color: var(--text);
941 box-shadow: inset 0 -2px 0 var(--accent);
942 font-weight: 600;
943 }
944}
945
946// Browse bar: ref selector, breadcrumb, archive links
947
948.browse-bar {
949 display: flex;
950 align-items: center;
951 gap: 10px 14px;
952 flex-wrap: wrap;
953 margin-bottom: 12px;
954 min-width: 0;
955}
956
957.archive-links {
958 display: flex;
959 gap: 12px;
960 margin-left: auto;
961 font-size: 0.875rem;
962}
963
964.ref-select {
965 position: relative;
966
967 summary {
968 cursor: pointer;
969 list-style: none;
970 display: inline-flex;
971 align-items: center;
972 gap: 6px;
973 padding: 5px 10px;
974 border: 1px solid var(--border);
975 border-radius: var(--radius);
976 background: var(--surface);
977
978 &::-webkit-details-marker {
979 display: none;
980 }
981 }
982
983 &[open] summary {
984 border-color: var(--accent);
985 }
986
987 code {
988 background: none;
989 padding: 0;
990 }
991}
992
993.ref-menu {
994 position: absolute;
995 left: 0;
996 top: calc(100% + 6px);
997 z-index: 10;
998 min-width: 240px;
999 max-height: 60vh;
1000 overflow-y: auto;
1001 padding: 8px 0;
1002 background: var(--surface);
1003 border: 1px solid var(--border);
1004 border-radius: var(--radius);
1005 box-shadow: 0 8px 24px rgba(0, 0, 0, 0.12);
1006}
1007
1008.ref-heading {
1009 margin: 6px 14px 4px;
1010 font-size: 0.75rem;
1011 font-weight: 600;
1012 color: var(--muted);
1013 text-transform: uppercase;
1014 letter-spacing: 0.04em;
1015}
1016
1017.ref-list {
1018 list-style: none;
1019 margin: 0;
1020 padding: 0;
1021
1022 a {
1023 display: block;
1024 padding: 6px 14px;
1025 color: var(--text);
1026 word-break: break-all;
1027
1028 &.current {
1029 color: var(--accent);
1030 font-weight: 600;
1031 }
1032 }
1033}
1034
1035.crumbs {
1036 min-width: 0;
1037 font-size: 0.9375rem;
1038
1039 ol {
1040 display: flex;
1041 flex-wrap: wrap;
1042 align-items: center;
1043 gap: 2px;
1044 list-style: none;
1045 margin: 0;
1046 padding: 0;
1047 }
1048
1049 li {
1050 display: flex;
1051 align-items: center;
1052 min-width: 0;
1053 }
1054
1055 li + li::before {
1056 content: "/";
1057 color: var(--muted);
1058 padding: 0 6px;
1059 }
1060
1061 a,
1062 span {
1063 word-break: break-all;
1064 }
1065
1066 span[aria-current] {
1067 font-weight: 600;
1068 }
1069}
1070
1071// Directory listing and ref tables
1072
1073.tree-table,
1074.refs-table {
1075 table-layout: auto;
1076
1077 .age {
1078 width: 1%;
1079 white-space: nowrap;
1080 text-align: right;
1081 color: var(--muted);
1082 font-size: 0.8125rem;
1083 }
1084
1085 th.age {
1086 text-align: right;
1087 }
1088}
1089
1090.tree-name {
1091 white-space: nowrap;
1092 max-width: 50vw;
1093 overflow: hidden;
1094 text-overflow: ellipsis;
1095
1096 a,
1097 span:not(.kind) {
1098 white-space: nowrap;
1099 }
1100}
1101
1102.tree-commit {
1103 max-width: 0;
1104 width: 100%;
1105
1106 a {
1107 display: block;
1108 overflow: hidden;
1109 text-overflow: ellipsis;
1110 white-space: nowrap;
1111 color: var(--text);
1112 }
1113}
1114
1115// Name cell content: name and short oid (or badge) spaced by gap, not by whitespace.
1116.tree-label {
1117 display: inline-flex;
1118 flex-wrap: wrap;
1119 align-items: baseline;
1120 gap: 8px;
1121 max-width: 100%;
1122 min-width: 0;
1123}
1124
1125.mono {
1126 font-family: var(--mono);
1127}
1128
1129// Commit history list (commits page): one row per commit, unnumbered, with a bottom border.
1130.commit-list {
1131 list-style: none;
1132 margin: 0;
1133 padding: 0;
1134 border-top: 1px solid var(--border);
1135}
1136
1137.commit-row {
1138 display: flex;
1139 flex-wrap: wrap;
1140 align-items: center;
1141 justify-content: space-between;
1142 gap: 8px 16px;
1143 padding: 12px 0;
1144 border-bottom: 1px solid var(--border);
1145}
1146
1147.commit-main {
1148 display: flex;
1149 flex-direction: column;
1150 gap: 2px;
1151 flex: 1 1 260px;
1152 min-width: 0;
1153}
1154
1155.commit-summary {
1156 font-weight: 600;
1157 color: var(--text);
1158 overflow-wrap: anywhere;
1159}
1160
1161.commit-meta {
1162 display: flex;
1163 flex-wrap: wrap;
1164 align-items: center;
1165 gap: 4px 8px;
1166}
1167
1168.commit-actions {
1169 display: flex;
1170 flex-wrap: wrap;
1171 align-items: center;
1172 gap: 12px;
1173 margin-left: auto;
1174}
1175
1176.commit-oid {
1177 padding: 2px 8px;
1178 border: 1px solid var(--border);
1179 border-radius: 999px;
1180 background: var(--code-bg);
1181 color: var(--text);
1182 font-size: 0.8125rem;
1183 line-height: 1.5;
1184
1185 &:hover {
1186 border-color: var(--accent);
1187 text-decoration: none;
1188 }
1189}
1190
1191.kind {
1192 display: inline-block;
1193 min-width: 4.5em;
1194 margin-right: 8px;
1195 font-size: 0.6875rem;
1196 font-weight: 600;
1197 text-transform: uppercase;
1198 letter-spacing: 0.03em;
1199 color: var(--muted);
1200}
1201
1202// Card with a README
1203
1204.readme {
1205 margin-top: 16px;
1206}
1207
1208.readme-name {
1209 margin: 0 0 12px;
1210 font-family: var(--mono);
1211}
1212
1213.readme-text {
1214 margin: 0;
1215 white-space: pre-wrap;
1216 overflow-x: auto;
1217 font-family: var(--mono);
1218 font-size: 0.875rem;
1219}
1220
1221// Markdown body (README and markdown files)
1222
1223.markdown-body {
1224 font-size: 1rem;
1225 line-height: 1.6;
1226 overflow-wrap: break-word;
1227 min-width: 0;
1228
1229 > *:first-child {
1230 margin-top: 0;
1231 }
1232
1233 h1,
1234 h2,
1235 h3,
1236 h4,
1237 h5,
1238 h6 {
1239 margin: 1.4em 0 0.6em;
1240 line-height: 1.25;
1241 font-weight: 600;
1242 scroll-margin-top: 72px;
1243 }
1244
1245 h1 {
1246 font-size: 1.75rem;
1247 padding-bottom: 0.3em;
1248 border-bottom: 1px solid var(--border);
1249 }
1250
1251 h2 {
1252 font-size: 1.375rem;
1253 padding-bottom: 0.3em;
1254 border-bottom: 1px solid var(--border);
1255 }
1256
1257 h3 {
1258 font-size: 1.125rem;
1259 }
1260
1261 h4,
1262 h5,
1263 h6 {
1264 font-size: 1rem;
1265 color: var(--muted);
1266 }
1267
1268 p,
1269 ul,
1270 ol,
1271 blockquote,
1272 table,
1273 pre,
1274 details {
1275 margin: 0 0 1em;
1276 }
1277
1278 ul,
1279 ol {
1280 padding-left: 2em;
1281 }
1282
1283 li + li {
1284 margin-top: 0.25em;
1285 }
1286
1287 li > ul,
1288 li > ol {
1289 margin-top: 0.25em;
1290 margin-bottom: 0;
1291 }
1292
1293 .task-list-item {
1294 list-style: none;
1295
1296 input[type="checkbox"] {
1297 width: auto;
1298 margin: 0 0.4em 0 -1.4em;
1299 vertical-align: middle;
1300 }
1301 }
1302
1303 a {
1304 overflow-wrap: anywhere;
1305 }
1306
1307 img {
1308 max-width: 100%;
1309 height: auto;
1310 background: var(--surface);
1311 }
1312
1313 hr {
1314 border: 0;
1315 border-top: 1px solid var(--border);
1316 margin: 1.5em 0;
1317 }
1318
1319 blockquote {
1320 padding: 0 1em;
1321 color: var(--muted);
1322 border-left: 0.25em solid var(--border);
1323 }
1324
1325 code {
1326 padding: 0.15em 0.35em;
1327 font-size: 0.85em;
1328 word-break: normal;
1329 }
1330
1331 pre {
1332 padding: 14px;
1333 overflow-x: auto;
1334 background: var(--code-bg);
1335 border: 1px solid var(--border);
1336 border-radius: var(--radius);
1337 line-height: 1.45;
1338
1339 code {
1340 padding: 0;
1341 background: none;
1342 font-size: 0.875rem;
1343 word-break: normal;
1344 }
1345 }
1346
1347 table {
1348 display: block;
1349 width: max-content;
1350 max-width: 100%;
1351 overflow-x: auto;
1352 border-collapse: collapse;
1353
1354 th,
1355 td {
1356 padding: 6px 13px;
1357 border: 1px solid var(--border);
1358 text-align: left;
1359 }
1360
1361 th {
1362 font-weight: 600;
1363 background: var(--code-bg);
1364 }
1365 }
1366
1367 details {
1368 border: 1px solid var(--border);
1369 border-radius: var(--radius);
1370 padding: 8px 12px;
1371
1372 summary {
1373 cursor: pointer;
1374 font-weight: 600;
1375 }
1376 }
1377
1378 .markdown-alert {
1379 padding: 0.5em 1em;
1380 margin: 0 0 1em;
1381 color: var(--text);
1382 border-left: 0.25em solid var(--alert-accent, var(--border));
1383 border-radius: 0 var(--radius) var(--radius) 0;
1384 background: var(--surface);
1385
1386 > :last-child {
1387 margin-bottom: 0;
1388 }
1389 }
1390
1391 .markdown-alert-title {
1392 display: flex;
1393 align-items: center;
1394 gap: 6px;
1395 margin: 0 0 0.3em;
1396 font-weight: 600;
1397 color: var(--alert-accent, var(--text));
1398 }
1399
1400 .markdown-alert-note {
1401 --alert-accent: var(--note);
1402 }
1403
1404 .markdown-alert-tip {
1405 --alert-accent: var(--tip);
1406 }
1407
1408 .markdown-alert-important {
1409 --alert-accent: var(--important);
1410 }
1411
1412 .markdown-alert-warning {
1413 --alert-accent: var(--warning);
1414 }
1415
1416 .markdown-alert-caution {
1417 --alert-accent: var(--caution);
1418 }
1419}
1420
1421// File header, notices and the line-numbered viewer
1422
1423.file {
1424 min-width: 0;
1425}
1426
1427.file-head {
1428 display: flex;
1429 align-items: center;
1430 justify-content: space-between;
1431 gap: 8px 16px;
1432 flex-wrap: wrap;
1433 padding: 8px 12px;
1434 background: var(--surface);
1435 border: 1px solid var(--border);
1436 border-bottom: 0;
1437 border-radius: var(--radius) var(--radius) 0 0;
1438}
1439
1440.file-actions {
1441 display: flex;
1442 gap: 14px;
1443 flex-wrap: wrap;
1444 font-size: 0.875rem;
1445 margin-left: auto;
1446}
1447
1448.file-view,
1449.diff-file-body {
1450 max-width: 100%;
1451 overflow-x: auto;
1452 -webkit-overflow-scrolling: touch;
1453 background: var(--code-bg);
1454 border: 1px solid var(--border);
1455 border-radius: 0 0 var(--radius) var(--radius);
1456}
1457
1458.file-table,
1459.diff-table {
1460 min-width: 100%;
1461 border-collapse: collapse;
1462 font-family: var(--mono);
1463 font-size: 0.8125rem;
1464 line-height: 1.5;
1465
1466 td {
1467 padding: 0;
1468 vertical-align: top;
1469 }
1470
1471 tbody tr:target td {
1472 background: var(--row-target);
1473 }
1474}
1475
1476.file-table {
1477 width: max-content;
1478
1479 .ln {
1480 position: sticky;
1481 left: 0;
1482 z-index: 1;
1483 width: 1%;
1484 min-width: 3.5em;
1485 padding: 0 10px 0 12px;
1486 text-align: right;
1487 white-space: nowrap;
1488 user-select: none;
1489 background: var(--code-bg);
1490 border-right: 1px solid var(--border);
1491
1492 a {
1493 color: var(--ln-text);
1494 }
1495 }
1496
1497 .code {
1498 padding: 0 16px 0 14px;
1499 white-space: pre;
1500 word-break: normal;
1501 overflow-wrap: normal;
1502 }
1503
1504 tr[id]:target .ln {
1505 color: var(--text);
1506 font-weight: 600;
1507 }
1508}
1509
1510.file-image {
1511 display: flex;
1512 justify-content: center;
1513 padding: 16px;
1514 background: var(--code-bg);
1515 border: 1px solid var(--border);
1516 border-radius: 0 0 var(--radius) var(--radius);
1517 overflow: auto;
1518
1519 img {
1520 max-width: 100%;
1521 height: auto;
1522 }
1523}
1524
1525.notice {
1526 margin: 12px 0;
1527 padding: 10px 12px;
1528 border: 1px solid var(--border);
1529 border-radius: var(--radius);
1530 background: var(--surface);
1531 color: var(--muted);
1532 overflow-wrap: anywhere;
1533
1534 code {
1535 color: var(--text);
1536 }
1537}
1538
1539// Commit diffs (rendered by the commit page). Each file: .diff-file > .diff-file-header,
1540// then .diff-file-body > table.diff-table with rows .diff-line.{add,del,ctx,hunk}, cells
1541// .diff-ln (line numbers) and .diff-code. Long lines scroll inside .diff-file-body.
1542
1543.diff-file {
1544 min-width: 0;
1545 margin: 0 0 16px;
1546 border: 1px solid var(--border);
1547 border-radius: var(--radius);
1548 background: var(--surface);
1549 overflow: hidden;
1550}
1551
1552.diff-file-header {
1553 display: flex;
1554 align-items: center;
1555 justify-content: space-between;
1556 gap: 8px 12px;
1557 flex-wrap: wrap;
1558 padding: 8px 12px;
1559 border-bottom: 1px solid var(--border);
1560 font-family: var(--mono);
1561 font-size: 0.875rem;
1562 word-break: break-all;
1563 min-width: 0;
1564}
1565
1566.diff-stats {
1567 display: inline-flex;
1568 gap: 8px;
1569 font-family: var(--mono);
1570 font-size: 0.8125rem;
1571 white-space: nowrap;
1572
1573 .add {
1574 color: var(--add-text);
1575 }
1576
1577 .del {
1578 color: var(--del-text);
1579 }
1580}
1581
1582.diff-file-body {
1583 border: 0;
1584 border-radius: 0;
1585}
1586
1587.diff-table {
1588 width: max-content;
1589
1590 .diff-line {
1591 td {
1592 white-space: pre;
1593 }
1594
1595 &.add {
1596 background: var(--add-bg);
1597
1598 .diff-code {
1599 color: var(--add-text);
1600 }
1601 }
1602
1603 &.del {
1604 background: var(--del-bg);
1605
1606 .diff-code {
1607 color: var(--del-text);
1608 }
1609 }
1610
1611 &.hunk {
1612 background: var(--hunk-bg);
1613 color: var(--hunk-text);
1614
1615 .diff-code {
1616 color: var(--hunk-text);
1617 }
1618 }
1619 }
1620
1621 .diff-ln {
1622 width: 1%;
1623 min-width: 3.5em;
1624 padding: 0 8px;
1625 text-align: right;
1626 white-space: nowrap;
1627 user-select: none;
1628 color: var(--ln-text);
1629 background: var(--code-bg);
1630 border-right: 1px solid var(--border);
1631 position: sticky;
1632 left: 0;
1633 z-index: 1;
1634 }
1635
1636 .diff-code {
1637 padding: 0 14px;
1638 white-space: pre;
1639 }
1640}
1641
1642.diff-collapsed {
1643 padding: 8px 12px;
1644 color: var(--muted);
1645 font-size: 0.875rem;
1646 background: var(--code-bg);
1647 border-top: 1px dashed var(--border);
1648 border-bottom: 1px dashed var(--border);
1649}
1650
1651// Narrow screens
1652
1653@media (max-width: 600px) {
1654 .tree-name {
1655 max-width: 60vw;
1656 }
1657
1658 .archive-links {
1659 margin-left: 0;
1660 }
1661
1662 .file-actions {
1663 margin-left: 0;
1664 }
1665
1666 .ref-menu {
1667 max-width: calc(100vw - 32px);
1668 }
1669}
1670
1671// profile page
1672
1673.profile-page {
1674 display: flex;
1675 flex-direction: column;
1676 gap: 24px;
1677 padding-top: 8px;
1678}
1679
1680.profile-head {
1681 display: flex;
1682 align-items: center;
1683 gap: 24px;
1684 flex-wrap: wrap;
1685
1686 h1 {
1687 margin: 0 0 4px;
1688 overflow-wrap: anywhere;
1689 }
1690
1691 p {
1692 margin: 0;
1693 }
1694}
1695
1696.profile-avatar {
1697 width: 160px;
1698 height: 160px;
1699 max-width: 100%;
1700 border-radius: 50%;
1701 border: 1px solid var(--border);
1702 background: var(--surface);
1703 object-fit: cover;
1704}
1705
1706.profile-about {
1707 padding: 16px 20px;
1708 background: var(--surface);
1709 border: 1px solid var(--border);
1710 border-radius: var(--radius);
1711 overflow-wrap: anywhere;
1712}
1713
1714.profile-repos {
1715 h2 {
1716 font-size: 1.125rem;
1717 margin: 0 0 8px;
1718 }
1719}
1720
1721@media (max-width: 600px) {
1722 .profile-avatar {
1723 width: 112px;
1724 height: 112px;
1725 }
1726
1727 .profile-about {
1728 padding: 12px 14px;
1729 }
1730}
1731
1732// Avatars: the nav thumbnail, the settings preview and the upload row.
1733.avatar {
1734 display: inline-block;
1735 border-radius: 50%;
1736 object-fit: cover;
1737 background: var(--surface-2);
1738 vertical-align: middle;
1739}
1740
1741.avatar-sm {
1742 width: 24px;
1743 height: 24px;
1744 margin-right: 0.4em;
1745}
1746
1747.avatar-lg {
1748 width: 96px;
1749 height: 96px;
1750}
1751
1752.avatar-row {
1753 display: flex;
1754 flex-wrap: wrap;
1755 gap: 1.25rem;
1756 align-items: flex-start;
1757}
1758
1759// Settings pages: the card stack and the form field widths.
1760.settings-layout {
1761 display: grid;
1762 gap: 1rem;
1763}
1764
1765.settings-layout textarea {
1766 width: 100%;
1767 max-width: 40rem;
1768 box-sizing: border-box;
1769}
1770
1771.settings-layout select {
1772 max-width: 24rem;
1773}
1774
1775@media (max-width: 600px) {
1776 .avatar-row {
1777 flex-direction: column;
1778 }
1779}
1780
1781// branch protection
1782// Settings page: rules table with a Remove form per row, then the add/update form.
1783.branch-rules {
1784 .remove-form {
1785 margin: 0;
1786 }
1787
1788 tr.muted td {
1789 color: var(--muted);
1790 }
1791}
1792
1793.branch-form {
1794 max-width: 40rem;
1795
1796 input[type="text"] {
1797 max-width: 24rem;
1798 }
1799}
1800
1801// labels & milestones
1802// Label list and create/edit forms on the labels page. Chips get their colors from labels.css.
1803.label-list,
1804.milestone-list {
1805 list-style: none;
1806 margin: 0;
1807 padding: 0;
1808}
1809
1810.label-row,
1811.milestone-row {
1812 padding: 12px 0;
1813 border-bottom: 1px solid var(--border);
1814
1815 &:last-child {
1816 border-bottom: none;
1817 }
1818
1819 h3 {
1820 margin: 0 0 4px;
1821 font-size: 1rem;
1822 }
1823
1824 p {
1825 margin: 4px 0;
1826 }
1827}
1828
1829.label-head {
1830 display: flex;
1831 flex-wrap: wrap;
1832 align-items: center;
1833 gap: 8px;
1834}
1835
1836.label-chip {
1837 display: inline-block;
1838 padding: 2px 10px;
1839 border-radius: 999px;
1840 font-size: 0.8125rem;
1841 font-weight: 600;
1842 text-decoration: none;
1843}
1844
1845.label-actions {
1846 display: flex;
1847 flex-wrap: wrap;
1848 align-items: flex-start;
1849 gap: 12px;
1850 margin-top: 8px;
1851
1852 details {
1853 flex: 1 1 20rem;
1854 }
1855
1856 summary {
1857 cursor: pointer;
1858 color: var(--accent);
1859 }
1860
1861 .remove-form {
1862 margin: 0;
1863 }
1864}
1865
1866.subnav {
1867 display: flex;
1868 flex-wrap: wrap;
1869 gap: 16px;
1870 margin: 12px 0;
1871 border-bottom: 1px solid var(--border);
1872}
1873
1874.subnav-link {
1875 padding: 6px 0;
1876 color: var(--muted);
1877 text-decoration: none;
1878 border-bottom: 2px solid transparent;
1879
1880 &.current {
1881 color: var(--text);
1882 border-bottom-color: var(--accent);
1883 font-weight: 600;
1884 }
1885}
1886
1887// Milestones: progress bar with the open and closed counts beside it.
1888.milestone-progress {
1889 display: flex;
1890 flex-wrap: wrap;
1891 align-items: center;
1892 gap: 12px;
1893 margin: 8px 0;
1894
1895 progress {
1896 width: 16rem;
1897 max-width: 100%;
1898 height: 0.75rem;
1899 accent-color: var(--accent);
1900 }
1901}
1902
1903.badge.overdue {
1904 background: var(--danger-bg);
1905 color: var(--danger);
1906 margin-left: 0;
1907}
1908
1909.label-row,
1910.milestone-row {
1911 .remove-form {
1912 margin: 0;
1913 }
1914}
1915
1916.milestone-row .markdown-body {
1917 margin: 4px 0;
1918}
1919
1920@media (max-width: 600px) {
1921 .milestone-progress progress {
1922 width: 100%;
1923 }
1924}
1925
1926// issues
1927// Issue list and new-issue form. Label chip colors come from the per-repo labels.css.
1928.tab-count {
1929 display: inline-block;
1930 min-width: 1.6em;
1931 padding: 0 0.4em;
1932 border-radius: 999px;
1933 background: var(--code-bg);
1934 color: var(--muted);
1935 font-size: 0.85em;
1936 text-align: center;
1937}
1938
1939.issue-toolbar {
1940 display: flex;
1941 flex-wrap: wrap;
1942 align-items: center;
1943 justify-content: space-between;
1944 gap: 0.75rem;
1945 margin: 1rem 0 0.75rem;
1946}
1947
1948.issue-tabs {
1949 display: flex;
1950 gap: 0.25rem;
1951
1952 a {
1953 padding: 0.35rem 0.75rem;
1954 border-radius: var(--radius);
1955 color: var(--muted);
1956 text-decoration: none;
1957
1958 &:hover {
1959 color: var(--text);
1960 }
1961
1962 &.current {
1963 border: 1px solid var(--border);
1964 background: var(--surface);
1965 color: var(--text);
1966 font-weight: 600;
1967 }
1968 }
1969}
1970
1971.issue-search {
1972 display: flex;
1973 flex-wrap: wrap;
1974 align-items: center;
1975 gap: 0.5rem;
1976 margin-bottom: 0.5rem;
1977
1978 input[type="search"] {
1979 flex: 1 1 14rem;
1980 max-width: 32rem;
1981 }
1982
1983 select {
1984 width: auto;
1985 }
1986}
1987
1988.issue-list {
1989 list-style: none;
1990 margin: 0;
1991 padding: 0;
1992 border: 1px solid var(--border);
1993 border-radius: var(--radius);
1994 overflow: hidden;
1995}
1996
1997.issue-row {
1998 display: flex;
1999 align-items: flex-start;
2000 gap: 0.75rem;
2001 padding: 0.75rem 1rem;
2002 border-top: 1px solid var(--border);
2003 background: var(--surface);
2004
2005 &:first-child {
2006 border-top: 0;
2007 }
2008}
2009
2010.issue-state {
2011 flex: none;
2012 width: 0.9rem;
2013 height: 0.9rem;
2014 margin-top: 0.3rem;
2015 border: 2px solid;
2016 border-radius: 50%;
2017}
2018
2019.issue-state-open {
2020 border-color: var(--public-text);
2021 background: var(--public-bg);
2022}
2023
2024.issue-state-closed {
2025 border-color: var(--important);
2026 background: var(--private-bg);
2027}
2028
2029.pinned-issues {
2030 margin-bottom: 1rem;
2031
2032 h2 {
2033 margin: 0 0 0.5rem;
2034 color: var(--muted);
2035 font-size: 0.8rem;
2036 font-weight: 600;
2037 letter-spacing: 0.04em;
2038 text-transform: uppercase;
2039 }
2040}
2041
2042.pinned-list {
2043 display: grid;
2044 grid-template-columns: repeat(auto-fill, minmax(14rem, 1fr));
2045 gap: 0.75rem;
2046 list-style: none;
2047 margin: 0;
2048 padding: 0;
2049}
2050
2051.pinned-card {
2052 display: flex;
2053 flex-direction: column;
2054 gap: 0.35rem;
2055 padding: 0.75rem 1rem;
2056 border: 1px solid var(--border);
2057 border-radius: var(--radius);
2058 background: var(--surface);
2059
2060 .pinned-title {
2061 font-weight: 600;
2062 overflow-wrap: anywhere;
2063 }
2064}
2065
2066.issue-main {
2067 flex: 1 1 auto;
2068 min-width: 0;
2069}
2070
2071.issue-title-line {
2072 display: flex;
2073 flex-wrap: wrap;
2074 align-items: center;
2075 gap: 0.4rem;
2076}
2077
2078.issue-title {
2079 color: var(--text);
2080 font-weight: 600;
2081 text-decoration: none;
2082
2083 &:hover {
2084 text-decoration: underline;
2085 }
2086}
2087
2088.issue-meta {
2089 margin-top: 0.2rem;
2090 display: flex;
2091 flex-wrap: wrap;
2092 gap: 0.3rem;
2093}
2094
2095.issue-aside {
2096 flex: none;
2097 display: flex;
2098 align-items: center;
2099 gap: 0.35rem;
2100}
2101
2102.label-chip {
2103 display: inline-block;
2104 padding: 0 0.6rem;
2105 border-radius: 999px;
2106 font-size: 0.8rem;
2107 font-weight: 600;
2108 line-height: 1.6;
2109 text-decoration: none;
2110}
2111
2112.issue-new {
2113 max-width: 48rem;
2114
2115 textarea {
2116 min-height: 14rem;
2117 font-family: var(--mono);
2118 }
2119}
2120
2121// issue view
2122// Issue page: header, a two-column layout (timeline and sidebar), timeline items with a header
2123// strip, event lines, and the comment and edit forms. Label chips come from `.label-chip` above.
2124.issue-heading {
2125 margin: 0.25rem 0;
2126 font-size: 1.5rem;
2127 line-height: 1.3;
2128 overflow-wrap: anywhere;
2129}
2130
2131.issue-number {
2132 color: var(--muted);
2133 font-weight: 400;
2134}
2135
2136.issue-byline {
2137 display: flex;
2138 flex-wrap: wrap;
2139 align-items: center;
2140 gap: 0.35rem;
2141 margin: 0.25rem 0 1rem;
2142 color: var(--muted);
2143 font-size: 0.9rem;
2144}
2145
2146.issue-badge {
2147 display: inline-block;
2148 padding: 2px 10px;
2149 border-radius: 999px;
2150 font-size: 0.85rem;
2151 font-weight: 600;
2152
2153 &.issue-badge-open {
2154 background: var(--add-bg);
2155 color: var(--add-text);
2156 }
2157
2158 &.issue-badge-completed {
2159 background: var(--hunk-bg);
2160 color: var(--important);
2161 }
2162
2163 &.issue-badge-not-planned {
2164 background: var(--private-bg);
2165 color: var(--private-text);
2166 }
2167}
2168
2169.issue-layout {
2170 display: grid;
2171 grid-template-columns: minmax(0, 1fr) 15rem;
2172 gap: 1.5rem;
2173 align-items: start;
2174}
2175
2176.issue-column {
2177 display: flex;
2178 flex-direction: column;
2179 gap: 1rem;
2180 min-width: 0;
2181}
2182
2183@media (max-width: 700px) {
2184 .issue-layout {
2185 grid-template-columns: minmax(0, 1fr);
2186 }
2187}
2188
2189.issue-timeline {
2190 display: flex;
2191 flex-direction: column;
2192 gap: 1rem;
2193 min-width: 0;
2194}
2195
2196.issue-item {
2197 min-width: 0;
2198 border: 1px solid var(--border);
2199 border-radius: var(--radius);
2200 background: var(--surface);
2201 overflow: hidden;
2202}
2203
2204.issue-item-head {
2205 display: flex;
2206 flex-wrap: wrap;
2207 align-items: center;
2208 gap: 0.5rem;
2209 padding: 0.5rem 0.75rem;
2210 border-bottom: 1px solid var(--border);
2211 background: var(--code-bg);
2212 font-size: 0.9rem;
2213}
2214
2215.issue-item-actions {
2216 display: inline-flex;
2217 align-items: center;
2218 gap: 0.6rem;
2219 margin-left: auto;
2220
2221 form {
2222 margin: 0;
2223 }
2224}
2225
2226.issue-item-body {
2227 padding: 0.75rem 1rem;
2228 overflow-wrap: anywhere;
2229}
2230
2231.reactions {
2232 display: flex;
2233 flex-wrap: wrap;
2234 align-items: center;
2235 gap: 0.35rem;
2236 padding: 0 1rem 0.75rem;
2237}
2238
2239.reaction-form {
2240 display: inline;
2241 margin: 0;
2242}
2243
2244.reaction {
2245 display: inline-flex;
2246 align-items: center;
2247 gap: 0.3rem;
2248 padding: 0.15rem 0.5rem;
2249 border: 1px solid var(--border);
2250 border-radius: 999px;
2251 background: var(--surface);
2252 color: var(--text);
2253 font: inherit;
2254 font-size: 0.85rem;
2255 line-height: 1.4;
2256 cursor: pointer;
2257
2258 &:hover {
2259 border-color: var(--accent);
2260 }
2261}
2262
2263span.reaction {
2264 cursor: default;
2265}
2266
2267.reaction-active {
2268 border-color: var(--accent);
2269 background: var(--public-bg);
2270}
2271
2272.reaction-count {
2273 color: var(--muted);
2274 font-variant-numeric: tabular-nums;
2275}
2276
2277.reaction-picker {
2278 position: relative;
2279
2280 > summary {
2281 list-style: none;
2282 padding: 0.15rem 0.5rem;
2283 border: 1px dashed var(--border);
2284 border-radius: 999px;
2285 color: var(--muted);
2286 font-size: 0.85rem;
2287 cursor: pointer;
2288 }
2289
2290 > summary::-webkit-details-marker {
2291 display: none;
2292 }
2293}
2294
2295.reaction-options {
2296 display: flex;
2297 flex-wrap: wrap;
2298 gap: 0.35rem;
2299 margin-top: 0.35rem;
2300}
2301
2302.issue-edited {
2303 color: var(--muted);
2304 font-size: 0.85rem;
2305}
2306
2307.issue-event {
2308 display: flex;
2309 flex-wrap: wrap;
2310 align-items: center;
2311 gap: 0.35rem;
2312 padding: 0 0.75rem;
2313 color: var(--muted);
2314 font-size: 0.9rem;
2315}
2316
2317.issue-comment,
2318.issue-edit {
2319 h2 {
2320 margin-top: 0;
2321 }
2322}
2323
2324.issue-comment-form textarea,
2325.issue-edit textarea {
2326 width: 100%;
2327 min-height: 8rem;
2328 box-sizing: border-box;
2329 font: inherit;
2330}
2331
2332.issue-actions {
2333 display: flex;
2334 flex-wrap: wrap;
2335 align-items: center;
2336 gap: 0.5rem;
2337}
2338
2339.issue-sidebar {
2340 display: flex;
2341 flex-direction: column;
2342 gap: 0.75rem;
2343 font-size: 0.9rem;
2344}
2345
2346.issue-side-section {
2347 padding-bottom: 0.75rem;
2348 border-bottom: 1px solid var(--border);
2349
2350 h2 {
2351 margin: 0 0 0.5rem;
2352 color: var(--muted);
2353 font-size: 0.8rem;
2354 font-weight: 600;
2355 letter-spacing: 0.04em;
2356 text-transform: uppercase;
2357 }
2358
2359 input[type="text"],
2360 select {
2361 width: 100%;
2362 box-sizing: border-box;
2363 }
2364}
2365
2366.issue-check {
2367 display: flex;
2368 align-items: center;
2369 gap: 0.4rem;
2370 margin: 0.2rem 0;
2371}
2372
2373.issue-chips {
2374 display: flex;
2375 flex-wrap: wrap;
2376 gap: 0.3rem;
2377}
2378
2379.issue-names {
2380 margin: 0;
2381 padding: 0;
2382 list-style: none;
2383}
2384
2385.issue-tools {
2386 display: flex;
2387 flex-wrap: wrap;
2388 align-items: center;
2389 gap: 0.5rem;
2390
2391 nav {
2392 display: flex;
2393 gap: 0.5rem;
2394 }
2395}
2396
2397// Repo header actions (fork and star)
2398
2399.header-actions {
2400 display: flex;
2401 align-items: center;
2402 gap: 8px;
2403 flex-wrap: wrap;
2404}
2405
2406.fork-form,
2407.star-form {
2408 margin: 0;
2409}
2410
2411// Pull request list
2412
2413.issue-badge.issue-badge-closed {
2414 background: var(--danger-bg);
2415 color: var(--danger);
2416}
2417
2418// Merged pull requests: solid purple, the same accent as the completed issue badge.
2419.issue-badge.issue-badge-merged {
2420 background: var(--important);
2421 color: var(--accent-text);
2422}
2423
2424.issue-state.pull-state-closed {
2425 border-color: var(--danger);
2426 background: var(--danger-bg);
2427}
2428
2429.issue-state.pull-state-merged {
2430 border-color: var(--important);
2431 background: var(--important);
2432}
2433
2434// pull request view
2435// Merge box on the Conversation tab: a status headline, the conflicting files and the merge
2436// form. The merged notice uses the same box. Badges come from `.issue-badge` above.
2437.merge-box {
2438 display: flex;
2439 flex-direction: column;
2440 gap: 0.75rem;
2441 padding: 1rem 1.25rem;
2442 border: 1px solid var(--border);
2443 border-radius: var(--radius);
2444 background: var(--surface);
2445
2446 &.merge-ok {
2447 border-color: var(--add-text);
2448 background: var(--add-bg);
2449 }
2450
2451 &.merge-bad {
2452 border-color: var(--danger);
2453 background: var(--danger-bg);
2454 }
2455
2456 &.merge-muted {
2457 color: var(--muted);
2458 }
2459
2460 &.merge-merged {
2461 border-color: var(--important);
2462 }
2463}
2464
2465.merge-headline {
2466 margin: 0;
2467 font-weight: 600;
2468}
2469
2470// A refused merge, shown inside the merge box. The box's gap spaces it, so drop the alert margin.
2471.merge-error {
2472 margin: 0;
2473}
2474
2475.merge-files {
2476 margin: 0;
2477 padding-left: 1.25rem;
2478}
2479
2480.merge-form {
2481 margin: 0;
2482}
2483
2484.merge-styles {
2485 display: flex;
2486 flex-wrap: wrap;
2487 align-items: center;
2488 gap: 0.5rem 1.25rem;
2489 margin: 0;
2490 padding: 0;
2491 border: 0;
2492
2493 legend {
2494 width: 100%;
2495 padding: 0;
2496 font-weight: 600;
2497 }
2498}
2499
2500// Review comments (Files tab, and the Conversation's threads later). A line's "+" link shows
2501// on row hover (or keyboard focus). Threads and the inline form sit in a full-width row under
2502// their line: tr.review-row > td > .review-threads.
2503
2504.diff-table {
2505 .diff-plus {
2506 width: 1%;
2507 padding: 0 4px;
2508 text-align: center;
2509 white-space: nowrap;
2510 user-select: none;
2511 background: var(--code-bg);
2512 border-right: 1px solid var(--border);
2513
2514 a {
2515 display: inline-block;
2516 width: 18px;
2517 line-height: 18px;
2518 border-radius: 4px;
2519 background: var(--accent);
2520 color: var(--accent-text);
2521 font-family: var(--mono);
2522 text-decoration: none;
2523 text-align: center;
2524 opacity: 0;
2525 }
2526
2527 a:hover,
2528 a:focus-visible {
2529 opacity: 1;
2530 }
2531 }
2532
2533 tr:hover .diff-plus a {
2534 opacity: 1;
2535 }
2536
2537 .review-row > td {
2538 white-space: normal;
2539 padding: 8px 12px;
2540 background: var(--surface);
2541 border-top: 1px solid var(--border);
2542 border-bottom: 1px solid var(--border);
2543 }
2544}
2545
2546.review-threads {
2547 display: grid;
2548 gap: 12px;
2549 width: min(760px, 90vw);
2550 max-width: 100%;
2551 padding: 4px 0;
2552}
2553
2554.review-thread {
2555 border: 1px solid var(--border);
2556 border-radius: var(--radius);
2557 background: var(--surface);
2558 overflow: hidden;
2559
2560 summary {
2561 cursor: pointer;
2562 padding: 8px 12px;
2563 font-size: 0.875rem;
2564 color: var(--muted);
2565 }
2566}
2567
2568.review-thread-head {
2569 display: flex;
2570 flex-wrap: wrap;
2571 align-items: center;
2572 gap: 8px;
2573 padding: 8px 12px;
2574 border-bottom: 1px solid var(--border);
2575 background: var(--code-bg);
2576 font-size: 0.875rem;
2577 word-break: break-all;
2578}
2579
2580.review-hunk {
2581 margin: 0;
2582 padding: 8px 12px;
2583 border-bottom: 1px solid var(--border);
2584 background: var(--code-bg);
2585 font-family: var(--mono);
2586 font-size: 0.8125rem;
2587 overflow-x: auto;
2588}
2589
2590.review-comment {
2591 padding: 10px 12px;
2592 border-bottom: 1px solid var(--border);
2593
2594 &:last-child {
2595 border-bottom: 0;
2596 }
2597}
2598
2599.review-comment-head {
2600 display: flex;
2601 flex-wrap: wrap;
2602 align-items: center;
2603 gap: 8px;
2604 font-size: 0.875rem;
2605 color: var(--muted);
2606
2607 strong {
2608 color: var(--text);
2609 }
2610}
2611
2612.review-comment-actions {
2613 margin-left: auto;
2614 display: inline-flex;
2615 align-items: center;
2616 gap: 8px;
2617}
2618
2619.review-comment-body {
2620 margin-top: 6px;
2621}
2622
2623.review-thread-actions {
2624 display: flex;
2625 gap: 12px;
2626 padding: 6px 12px;
2627 border-top: 1px solid var(--border);
2628 font-size: 0.875rem;
2629}
2630
2631.review-reply {
2632 padding: 10px 12px;
2633 border-top: 1px solid var(--border);
2634}
2635
2636.review-inline {
2637 display: inline;
2638 margin: 0;
2639}
2640
2641.review-badge {
2642 display: inline-block;
2643 padding: 0 8px;
2644 border-radius: 999px;
2645 font-size: 0.75rem;
2646 font-weight: 600;
2647 line-height: 20px;
2648}
2649
2650.review-badge-outdated {
2651 background: var(--private-bg);
2652 color: var(--private-text);
2653}
2654
2655.review-badge-pending {
2656 background: var(--hunk-bg);
2657 color: var(--hunk-text);
2658}
2659
2660.review-bar {
2661 margin: 0 0 16px;
2662
2663 h2 {
2664 margin: 0 0 4px;
2665 font-size: 1rem;
2666 }
2667}
2668
2669.review-choices {
2670 display: flex;
2671 flex-wrap: wrap;
2672 gap: 12px;
2673 margin: 0;
2674 padding: 0;
2675 border: 0;
2676
2677 legend {
2678 padding: 0;
2679 margin-bottom: 4px;
2680 font-size: 0.875rem;
2681 color: var(--muted);
2682 }
2683
2684 label {
2685 display: inline-flex;
2686 align-items: center;
2687 gap: 4px;
2688 }
2689}
2690
2691.review-inline-form {
2692 padding: 10px 12px;
2693 border: 1px solid var(--accent);
2694 border-radius: var(--radius);
2695 background: var(--surface);
2696 white-space: normal;
2697}
2698
2699// Review timeline (Conversation tab): a submitted review with its verb, body and threads; a
2700// dismissed review's struck verb and reason; the review summary box with its Dismiss forms.
2701
2702.review-item {
2703 > .review-thread {
2704 margin-top: 8px;
2705 }
2706
2707 .review-verb {
2708 font-weight: 600;
2709 }
2710
2711 .review-verb-dismissed {
2712 color: var(--muted);
2713 text-decoration: line-through;
2714 }
2715}
2716
2717.review-dismissed {
2718 margin: 0 0 8px;
2719 font-size: 0.875rem;
2720 color: var(--muted);
2721}
2722
2723.review-box {
2724 display: grid;
2725 gap: 8px;
2726 padding: 12px 16px;
2727 border: 1px solid var(--border);
2728 border-radius: var(--radius);
2729 background: var(--surface);
2730}
2731
2732.review-summary {
2733 margin: 0;
2734 font-size: 0.9rem;
2735}
2736
2737.review-dismiss {
2738 display: flex;
2739 flex-wrap: wrap;
2740 align-items: flex-end;
2741 gap: 8px;
2742
2743 label {
2744 flex: 1 1 240px;
2745 display: grid;
2746 gap: 4px;
2747 }
2748}
2749
2750// Recent pushes banner (repo home, code tab): one notice per branch the viewer pushed to lately,
2751// with a compare link. The list sits above the tree and clone card.
2752.recent-pushes {
2753 display: grid;
2754 gap: 8px;
2755 margin: 0 0 12px;
2756}
2757
2758.recent-push {
2759 background: var(--surface);
2760 border: 1px solid var(--border);
2761 border-left: 3px solid var(--accent);
2762 border-radius: var(--radius);
2763 margin: 0;
2764 padding: 10px 12px;
2765}
2766
2767.recent-push a {
2768 font-weight: 600;
2769 white-space: nowrap;
2770}
2771
2772// Mirrors: the "Mirror of" line under the repo header, the new-repo mirror fields, and the
2773// Mirrors settings page (the list, the add and edit forms, and the error details).
2774.mirror-line {
2775 margin: 4px 0 0;
2776 overflow-wrap: anywhere;
2777}
2778
2779.mirror-fields {
2780 border: 0;
2781 margin: 0;
2782 padding: 0;
2783 display: grid;
2784 gap: 6px;
2785
2786 legend {
2787 font-weight: 600;
2788 margin-bottom: 6px;
2789 padding: 0;
2790 }
2791
2792 input[type="url"],
2793 input[type="password"] {
2794 max-width: 32rem;
2795 }
2796}
2797
2798.mirrors {
2799 td code {
2800 overflow-wrap: anywhere;
2801 }
2802
2803 details summary {
2804 cursor: pointer;
2805 }
2806
2807 .mirror-error {
2808 font-family: var(--mono);
2809 font-size: 0.8125rem;
2810 overflow-wrap: anywhere;
2811 white-space: pre-wrap;
2812 }
2813}
2814
2815.mirror-form {
2816 max-width: 40rem;
2817
2818 input[type="url"],
2819 input[type="password"] {
2820 max-width: 32rem;
2821 }
2822}
2823
2824// Webhooks (repo settings): the hook list, the hook form, and the delivery log on the hook page.
2825// Each delivery is a <details>; its body holds the request and response in <pre> blocks.
2826.webhooks {
2827 td code {
2828 overflow-wrap: anywhere;
2829 }
2830}
2831
2832.webhook-form {
2833 max-width: 40rem;
2834
2835 input[type="url"],
2836 input[type="password"] {
2837 max-width: 32rem;
2838 }
2839}
2840
2841.event-choices {
2842 border: 0;
2843 margin: 0;
2844 padding: 0;
2845 display: grid;
2846 gap: 6px;
2847
2848 legend {
2849 font-weight: 600;
2850 margin-bottom: 6px;
2851 padding: 0;
2852 }
2853}
2854
2855.hook-state {
2856 display: inline-block;
2857 font-size: 0.75rem;
2858 font-weight: 600;
2859 padding: 2px 8px;
2860 border-radius: 999px;
2861 background: var(--private-bg);
2862 color: var(--private-text);
2863
2864 &.ok {
2865 background: var(--public-bg);
2866 color: var(--public-text);
2867 }
2868
2869 &.failed,
2870 &.pending {
2871 background: var(--private-bg);
2872 color: var(--warning);
2873 }
2874
2875 &.gave_up {
2876 background: var(--danger-bg);
2877 color: var(--danger);
2878 }
2879}
2880
2881.delivery {
2882 border: 1px solid var(--border);
2883 border-radius: var(--radius);
2884 margin: 0 0 8px;
2885 padding: 8px 12px;
2886
2887 summary {
2888 cursor: pointer;
2889 overflow-wrap: anywhere;
2890 }
2891
2892 .delivery-body {
2893 margin-top: 10px;
2894
2895 h4 {
2896 margin: 12px 0 4px;
2897 font-size: 0.875rem;
2898 }
2899
2900 .code-block {
2901 background: var(--code-bg);
2902 border-radius: var(--radius);
2903 font-family: var(--mono);
2904 font-size: 0.8125rem;
2905 margin: 0;
2906 max-height: 24rem;
2907 overflow: auto;
2908 padding: 8px 10px;
2909 white-space: pre-wrap;
2910 overflow-wrap: anywhere;
2911 }
2912 }
2913}
2914
2915// Commit statuses: the combined icon with its context dropdown (commit list, commit page, PR
2916// commits tab) and the checks line in the PR merge box.
2917.status-icon {
2918 display: inline-flex;
2919 align-items: center;
2920 justify-content: center;
2921 min-width: 1.25em;
2922 font-weight: 700;
2923 line-height: 1;
2924
2925 &.status-success {
2926 color: var(--add-text);
2927 }
2928
2929 &.status-failure {
2930 color: var(--danger);
2931 }
2932
2933 &.status-pending {
2934 color: var(--warning);
2935 }
2936}
2937
2938.commit-status {
2939 position: relative;
2940
2941 > summary {
2942 cursor: pointer;
2943 list-style: none;
2944 padding: 2px 4px;
2945 border-radius: 4px;
2946
2947 &::-webkit-details-marker {
2948 display: none;
2949 }
2950
2951 &:hover {
2952 background: var(--code-bg);
2953 }
2954 }
2955
2956 &[open] > .status-list {
2957 position: absolute;
2958 z-index: 10;
2959 right: 0;
2960 top: 100%;
2961 min-width: 280px;
2962 max-width: 420px;
2963 margin-top: 4px;
2964 padding: 8px 12px;
2965 background: var(--surface);
2966 border: 1px solid var(--border);
2967 border-radius: var(--radius);
2968 box-shadow: 0 4px 16px rgb(0 0 0 / 12%);
2969 }
2970}
2971
2972.status-list {
2973 list-style: none;
2974 margin: 0;
2975 padding: 0;
2976 display: flex;
2977 flex-direction: column;
2978 gap: 6px;
2979}
2980
2981.status-item {
2982 display: flex;
2983 flex-wrap: wrap;
2984 align-items: baseline;
2985 gap: 6px;
2986 font-size: 0.9rem;
2987}
2988
2989.checks-summary {
2990 display: flex;
2991 flex-wrap: wrap;
2992 align-items: center;
2993 gap: 8px;
2994
2995 &.status-success > .status-icon {
2996 color: var(--add-text);
2997 }
2998
2999 &.status-failure > .status-icon {
3000 color: var(--danger);
3001 }
3002
3003 &.status-pending > .status-icon {
3004 color: var(--warning);
3005 }
3006
3007 .checks-details {
3008 flex-basis: 100%;
3009
3010 > summary {
3011 cursor: pointer;
3012 }
3013
3014 > .status-list {
3015 margin-top: 8px;
3016 }
3017 }
3018}
3019
3020// Notifications: the header bell, the inbox, the sidebar box and the watch control.
3021
3022.bell {
3023 display: inline-flex;
3024 align-items: center;
3025 gap: 0.35rem;
3026 color: var(--text);
3027 text-decoration: none;
3028}
3029
3030.bell-count {
3031 display: inline-block;
3032 min-width: 1.4rem;
3033 padding: 0 0.4rem;
3034 border-radius: 999px;
3035 background: var(--accent);
3036 color: var(--accent-text);
3037 font-size: 0.75rem;
3038 line-height: 1.4rem;
3039 text-align: center;
3040}
3041
3042.notif-tabs {
3043 display: flex;
3044 gap: 1rem;
3045 margin: 0.75rem 0 1rem;
3046 border-bottom: 1px solid var(--border);
3047
3048 a {
3049 padding: 0.4rem 0;
3050 color: var(--muted);
3051 text-decoration: none;
3052 border-bottom: 2px solid transparent;
3053 }
3054
3055 a.active {
3056 color: var(--text);
3057 border-bottom-color: var(--accent);
3058 }
3059}
3060
3061.notif-group {
3062 margin-bottom: 1rem;
3063 padding: 0;
3064 overflow: hidden;
3065}
3066
3067.notif-group-head {
3068 display: flex;
3069 align-items: center;
3070 justify-content: space-between;
3071 gap: 0.75rem;
3072 padding: 0.6rem 0.9rem;
3073 background: var(--code-bg);
3074 border-bottom: 1px solid var(--border);
3075}
3076
3077.notif-repo {
3078 font-weight: 600;
3079 color: var(--text);
3080 text-decoration: none;
3081}
3082
3083.notif-list {
3084 list-style: none;
3085 margin: 0;
3086 padding: 0;
3087}
3088
3089.notif-row {
3090 display: flex;
3091 flex-wrap: wrap;
3092 align-items: center;
3093 gap: 0.5rem 0.75rem;
3094 padding: 0.6rem 0.9rem;
3095 border-bottom: 1px solid var(--border);
3096
3097 &:last-child {
3098 border-bottom: none;
3099 }
3100
3101 &.unread .notif-title {
3102 font-weight: 600;
3103 }
3104}
3105
3106.notif-title {
3107 flex: 1 1 18rem;
3108 min-width: 0;
3109 overflow-wrap: anywhere;
3110 color: var(--text);
3111 text-decoration: none;
3112
3113 &:hover {
3114 color: var(--accent);
3115 text-decoration: underline;
3116 }
3117}
3118
3119.notif-icon {
3120 flex: 0 0 auto;
3121 display: inline-block;
3122 width: 0.7rem;
3123 height: 0.7rem;
3124 border-radius: 50%;
3125 border: 2px solid var(--muted);
3126
3127 &.issue-open,
3128 &.pull-open {
3129 border-color: var(--public-text);
3130 background: var(--public-bg);
3131 }
3132
3133 &.issue-closed,
3134 &.pull-closed {
3135 border-color: var(--danger);
3136 background: var(--danger-bg);
3137 }
3138}
3139
3140// Visually hidden text for screen readers, used next to icons.
3141.notif-sr {
3142 position: absolute;
3143 width: 1px;
3144 height: 1px;
3145 overflow: hidden;
3146 clip: rect(0 0 0 0);
3147 white-space: nowrap;
3148}
3149
3150.notif-inline-form {
3151 display: inline;
3152 margin: 0;
3153}
3154
3155.notif-more {
3156 text-align: center;
3157}
3158
3159.notif-box {
3160 h2 {
3161 font-size: 1rem;
3162 }
3163
3164 p {
3165 margin: 0.4rem 0 0.6rem;
3166 }
3167}
3168
3169.watch-form {
3170 display: inline-flex;
3171 flex-wrap: wrap;
3172 align-items: center;
3173 gap: 0.5rem;
3174 margin: 0;
3175
3176 select {
3177 padding: 0.3rem 0.5rem;
3178 border: 1px solid var(--border);
3179 border-radius: var(--radius);
3180 background: var(--surface);
3181 color: var(--text);
3182 font: inherit;
3183 }
3184}
3185
3186@media (max-width: 40rem) {
3187 .notif-row {
3188 flex-direction: column;
3189 align-items: flex-start;
3190 }
3191}
3192
3193// Signed commit badges: on the commit list and the commit page.
3194.sig-badge {
3195 display: inline-block;
3196 font-size: 0.75rem;
3197 font-weight: 600;
3198 padding: 2px 8px;
3199 border-radius: 999px;
3200 vertical-align: middle;
3201 white-space: nowrap;
3202}
3203
3204.sig-verified {
3205 background: var(--public-bg);
3206 color: var(--public-text);
3207}
3208
3209.sig-unverified {
3210 background: var(--private-bg);
3211 color: var(--private-text);
3212}
3213
3214// Releases: list cards, the latest-release card on the repo home, and the asset tables.
3215.release-list-head {
3216 display: flex;
3217 align-items: center;
3218 justify-content: space-between;
3219 gap: 12px;
3220 flex-wrap: wrap;
3221}
3222
3223.release-card {
3224 border-top: 1px solid var(--border);
3225 padding: 16px 0;
3226
3227 &:first-of-type {
3228 border-top: 0;
3229 }
3230
3231 h3 {
3232 margin: 0 0 4px;
3233 }
3234}
3235
3236.release-asset-list {
3237 margin: 8px 0;
3238 padding-left: 20px;
3239}
3240
3241.release-actions {
3242 display: flex;
3243 align-items: center;
3244 gap: 16px;
3245 flex-wrap: wrap;
3246}
3247
3248.release-form {
3249 max-width: 720px;
3250
3251 textarea {
3252 width: 100%;
3253 font-family: inherit;
3254 }
3255}
3256
3257.release-assets td code {
3258 word-break: break-all;
3259}
3260
3261.badge.latest {
3262 background: var(--public-bg);
3263 color: var(--public-text);
3264}
3265
3266.badge.draft {
3267 background: var(--private-bg);
3268 color: var(--private-text);
3269}
3270
3271// CI: run list, run page, job page and log.
3272.ci-state {
3273 display: inline-block;
3274 font-size: 0.75rem;
3275 font-weight: 600;
3276 padding: 2px 8px;
3277 border-radius: 999px;
3278 background: var(--private-bg);
3279 color: var(--private-text);
3280
3281 &::before {
3282 content: "";
3283 display: inline-block;
3284 width: 0.5em;
3285 height: 0.5em;
3286 margin-right: 6px;
3287 border-radius: 50%;
3288 background: currentColor;
3289 vertical-align: middle;
3290 }
3291
3292 &.ci-success {
3293 background: var(--public-bg);
3294 color: var(--public-text);
3295 }
3296
3297 &.ci-queued,
3298 &.ci-running,
3299 &.ci-blocked,
3300 &.ci-waiting_approval {
3301 background: var(--private-bg);
3302 color: var(--warning);
3303 }
3304
3305 &.ci-failure,
3306 &.ci-error {
3307 background: var(--danger-bg);
3308 color: var(--danger);
3309 }
3310}
3311
3312.ci-actions {
3313 display: flex;
3314 align-items: center;
3315 gap: 12px;
3316 flex-wrap: wrap;
3317 margin-top: 12px;
3318}
3319
3320.ci-manual-form {
3321 display: flex;
3322 align-items: center;
3323 gap: 12px;
3324 flex-wrap: wrap;
3325}
3326
3327.ci-config-error {
3328 white-space: pre-wrap;
3329 color: var(--danger);
3330}
3331
3332.ci-log {
3333 max-height: 70vh;
3334 overflow: auto;
3335 white-space: pre-wrap;
3336 word-break: break-word;
3337 font-size: 0.85rem;
3338 margin: 0;
3339}
3340
3341.ci-artifacts {
3342 margin: 0;
3343 padding-left: 20px;
3344}
3345
3346/* Theme and language preferences (T1). The palettes live at the top of this file: the dark
3347 variables apply under :root:not([data-theme="light"]) and under :root[data-theme="dark"]. */
3348.theme-picker {
3349 display: inline-flex;
3350 align-items: center;
3351 gap: 8px;
3352 margin-left: auto;
3353}
3354
3355.theme-picker label {
3356 display: inline-flex;
3357 align-items: center;
3358 gap: 6px;
3359}
3360
3361.theme-picker label span {
3362 color: var(--muted);
3363}
3364
3365// Snippets (wave C, S1): list, editor, file views and revisions.
3366.snippet-list .item-main a {
3367 text-decoration: none;
3368}
3369
3370// Editor: a console-style page. A header row (title, visibility, Save), then one document
3371// column: the title card, the file cards and a row with "Add file". 880px wide by default. The
3372// code area can be dragged wider; the form then grows with it, up to the page column's width.
3373.snippet-form {
3374 display: flex;
3375 flex-direction: column;
3376 gap: 16px;
3377 width: fit-content;
3378 min-width: min(880px, 100%);
3379 max-width: 100%;
3380
3381 svg {
3382 flex: none;
3383 width: 16px;
3384 height: 16px;
3385 fill: none;
3386 stroke: currentColor;
3387 stroke-width: 2;
3388 stroke-linecap: round;
3389 stroke-linejoin: round;
3390 }
3391}
3392
3393.snippet-head {
3394 display: flex;
3395 flex-wrap: wrap;
3396 align-items: center;
3397 gap: 12px 16px;
3398
3399 h1 {
3400 margin: 0;
3401 font-size: 1.5rem;
3402 line-height: 1.25;
3403 }
3404}
3405
3406.snippet-head-actions {
3407 display: flex;
3408 flex-wrap: wrap;
3409 align-items: center;
3410 gap: 10px;
3411 margin-left: auto;
3412}
3413
3414// Visibility: three icon-and-word segments in one 32px pill. The radios are hidden; the focus
3415// ring is drawn on the segment's text, and the long description is the segment's title.
3416.snippet-visibility {
3417 min-width: 0;
3418 margin: 0;
3419 padding: 0;
3420 border: 0;
3421}
3422
3423.snippet-segmented {
3424 display: inline-flex;
3425 align-items: center;
3426 gap: 2px;
3427 height: 32px;
3428 box-sizing: border-box;
3429 max-width: 100%;
3430 padding: 2px;
3431 border: 1px solid var(--border);
3432 border-radius: 999px;
3433 background: var(--surface-2);
3434}
3435
3436.snippet-segmented .segment {
3437 position: relative;
3438 display: inline-flex;
3439 margin: 0;
3440 cursor: pointer;
3441
3442 input[type="radio"] {
3443 position: absolute;
3444 width: 1px;
3445 height: 1px;
3446 margin: 0;
3447 padding: 0;
3448 border: 0;
3449 opacity: 0;
3450 }
3451
3452 > span {
3453 display: inline-flex;
3454 align-items: center;
3455 height: 26px;
3456 padding: 0 12px;
3457 border-radius: 999px;
3458 font-size: 0.8125rem;
3459 font-weight: 500;
3460 line-height: 1;
3461 color: var(--muted);
3462 white-space: nowrap;
3463 }
3464
3465 > svg {
3466 position: absolute;
3467 left: 12px;
3468 top: 50%;
3469 transform: translateY(-50%);
3470 width: 14px;
3471 height: 14px;
3472 pointer-events: none;
3473 }
3474
3475 > span {
3476 padding-left: 32px;
3477 }
3478
3479 &:hover > span {
3480 color: var(--text);
3481 }
3482
3483 input[type="radio"]:checked ~ span {
3484 background: var(--surface);
3485 color: var(--accent-ink);
3486 box-shadow: 0 1px 2px rgba(30, 36, 32, .12);
3487 }
3488
3489 input[type="radio"]:checked ~ svg {
3490 color: var(--accent-ink);
3491 }
3492
3493 input[type="radio"]:focus-visible ~ span {
3494 box-shadow: 0 0 0 2px var(--focus);
3495 }
3496}
3497
3498// The document: title and description on one card, then the file cards.
3499.snippet-doc {
3500 display: flex;
3501 flex-direction: column;
3502 gap: 16px;
3503 min-width: 0;
3504}
3505
3506.snippet-meta {
3507 display: flex;
3508 flex-direction: column;
3509 min-width: 0;
3510 padding: 20px 22px 16px;
3511 background: var(--surface);
3512 border: 1px solid var(--border);
3513 border-radius: 12px;
3514 box-shadow: var(--shadow);
3515
3516 &:focus-within {
3517 border-color: var(--focus);
3518 box-shadow: 0 0 0 3px var(--accent-soft), var(--shadow);
3519 }
3520}
3521
3522// Borderless inputs: the card carries the border and the focus ring.
3523.snippet-form .snippet-title,
3524.snippet-form .snippet-desc {
3525 display: block;
3526 box-sizing: border-box;
3527 width: 100%;
3528 min-width: 0;
3529 margin: 0;
3530 padding: 0;
3531 border: 0;
3532 border-radius: 0;
3533 background: transparent;
3534 box-shadow: none;
3535 color: var(--text);
3536 font: inherit;
3537 outline: none;
3538
3539 &:focus {
3540 border: 0;
3541 box-shadow: none;
3542 outline: none;
3543 }
3544
3545 &::placeholder {
3546 color: var(--muted);
3547 opacity: 1;
3548 }
3549}
3550
3551.snippet-form .snippet-title {
3552 font-size: 1.25rem;
3553 font-weight: 600;
3554 line-height: 1.3;
3555}
3556
3557.snippet-form .snippet-desc {
3558 margin-top: 12px;
3559 padding-top: 12px;
3560 border-top: 1px solid var(--border);
3561 color: var(--text-2);
3562 font-size: 0.9375rem;
3563 line-height: 1.5;
3564}
3565
3566// The files card: a tab strip, then the panel of the checked file. Each tab is a radio and a
3567// label, so the strip works with JavaScript off. The panels are generated per file slot below.
3568.snippet-form .snippet-files {
3569 display: flex;
3570 flex-direction: column;
3571 min-width: 0;
3572 max-width: 100%;
3573 overflow: hidden;
3574 background: var(--surface);
3575 border: 1px solid var(--border);
3576 border-radius: 12px;
3577 box-shadow: var(--shadow);
3578
3579 // Only the filename and the contents light the card; tab radios and buttons do not.
3580 &:has(.snippet-name:focus, .snippet-content:focus) {
3581 border-color: var(--focus);
3582 box-shadow: 0 0 0 3px var(--accent-soft), var(--shadow);
3583 }
3584}
3585
3586// Tab strip: the tabs scroll sideways inside it, the counter sits at the right end.
3587.snippet-tabbar {
3588 display: flex;
3589 flex-wrap: wrap;
3590 align-items: stretch;
3591 gap: 0 12px;
3592 padding-right: 12px;
3593 background: var(--surface-2);
3594 // A long tab strip scrolls inside the card; it must not widen the form on its own.
3595 contain: inline-size;
3596}
3597
3598.snippet-tabs {
3599 display: flex;
3600 align-items: flex-end;
3601 flex: 1 1 auto;
3602 min-width: 0;
3603 max-width: 100%;
3604 overflow-x: auto;
3605 overflow-y: hidden;
3606 padding-left: 6px;
3607 scrollbar-width: thin;
3608}
3609
3610.snippet-count {
3611 flex: none;
3612 margin-left: auto;
3613 align-self: center;
3614 white-space: nowrap;
3615 font-variant-numeric: tabular-nums;
3616}
3617
3618// The radio is hidden; the label is the tab. The focus ring is drawn on the label.
3619.snippet-tab-radio {
3620 position: absolute;
3621 width: 1px;
3622 height: 1px;
3623 margin: 0;
3624 padding: 0;
3625 border: 0;
3626 opacity: 0;
3627 outline: none;
3628}
3629
3630.snippet-form .snippet-tab {
3631 flex: none;
3632 display: inline-flex;
3633 align-items: center;
3634 box-sizing: border-box;
3635 max-width: 200px;
3636 height: 36px;
3637 margin: 0 1px;
3638 padding: 0 14px;
3639 border: 1px solid transparent;
3640 border-bottom: 0;
3641 border-radius: 8px 8px 0 0;
3642 color: var(--muted);
3643 font-family: var(--mono);
3644 font-size: 0.8125rem;
3645 white-space: nowrap;
3646 overflow: hidden;
3647 text-overflow: ellipsis;
3648 cursor: pointer;
3649 user-select: none;
3650
3651 &:hover {
3652 color: var(--text);
3653 }
3654}
3655
3656.snippet-tab-radio:checked + .snippet-tab {
3657 background: var(--surface);
3658 border-color: var(--border);
3659 box-shadow: inset 0 2px 0 var(--accent);
3660 color: var(--text);
3661}
3662
3663.snippet-tab-radio:focus-visible + .snippet-tab {
3664 outline: 2px solid var(--focus);
3665 outline-offset: -2px;
3666}
3667
3668// "Add file" as a compact "+" at the end of the strip.
3669.snippet-form .snippet-tab-add {
3670 flex: none;
3671 display: inline-flex;
3672 align-items: center;
3673 justify-content: center;
3674 align-self: center;
3675 width: 30px;
3676 height: 30px;
3677 margin: 0 0 3px 4px;
3678 padding: 0;
3679 border: 0;
3680 border-radius: 8px;
3681 background: transparent;
3682 color: var(--muted);
3683
3684 &:hover {
3685 background: var(--surface);
3686 color: var(--text);
3687 }
3688
3689 &:focus-visible {
3690 outline: none;
3691 box-shadow: 0 0 0 2px var(--focus);
3692 }
3693}
3694
3695// One panel per file: the name row, then the code area. Hidden unless its tab is checked.
3696.snippet-form .snippet-panel {
3697 display: none;
3698 min-width: 0;
3699 margin: 0;
3700 padding: 0;
3701 border: 0;
3702}
3703
3704.snippet-form .snippet-slot-head {
3705 display: flex;
3706 align-items: center;
3707 gap: 10px;
3708 height: 40px;
3709 box-sizing: border-box;
3710 padding: 0 6px 0 14px;
3711 background: var(--surface);
3712 border-bottom: 1px solid var(--border);
3713 color: var(--muted);
3714}
3715
3716.snippet-form .snippet-name {
3717 flex: 1 1 auto;
3718 min-width: 0;
3719 height: 100%;
3720 margin: 0;
3721 padding: 0;
3722 border: 0;
3723 border-radius: 0;
3724 background: transparent;
3725 box-shadow: none;
3726 color: var(--text);
3727 font-family: var(--mono);
3728 font-size: 0.8125rem;
3729 outline: none;
3730
3731 &:focus {
3732 border: 0;
3733 box-shadow: none;
3734 outline: none;
3735 }
3736
3737 &::placeholder {
3738 color: var(--muted);
3739 opacity: 1;
3740 }
3741}
3742
3743.snippet-form .snippet-remove {
3744 align-self: auto;
3745 flex: none;
3746 width: 28px;
3747 height: 28px;
3748 padding: 0;
3749 justify-content: center;
3750 border: 0;
3751 border-radius: 8px;
3752 background: transparent;
3753 color: var(--muted);
3754
3755 &:hover {
3756 background: var(--surface-2);
3757 color: var(--text);
3758 }
3759
3760 &:focus-visible {
3761 outline: none;
3762 box-shadow: 0 0 0 2px var(--focus);
3763 }
3764}
3765
3766// The code area fills the screen: the page header, title card and tabs sit above it, so the
3767// height is the viewport less that offset, kept between 240px and 720px. Resizable by hand, in
3768// both directions from 600px up (the width is capped by the card, which is capped by the page).
3769.snippet-form .snippet-content {
3770 display: block;
3771 box-sizing: border-box;
3772 width: 100%;
3773 min-width: 0;
3774 max-width: 100%;
3775 height: 480px;
3776 height: clamp(240px, calc(100dvh - 400px), 720px);
3777 margin: 0;
3778 padding: 14px 16px;
3779 border: 0;
3780 border-radius: 0;
3781 background: var(--surface-2);
3782 box-shadow: none;
3783 color: var(--text);
3784 font-family: var(--mono);
3785 font-size: 13px;
3786 line-height: 1.6;
3787 tab-size: 4;
3788 resize: both;
3789 outline: none;
3790
3791 &:focus {
3792 border: 0;
3793 box-shadow: none;
3794 outline: none;
3795 }
3796}
3797
3798// Each panel shows when its tab is checked. One rule per file slot (MAX_FILES in Rust).
3799@for $i from 0 through 9 {
3800 .snippet-files:has(#snippet-tab-#{$i}:checked) .snippet-panel-#{$i} {
3801 display: block;
3802 }
3803}
3804
3805@media (max-width: 599px) {
3806 .snippet-form .snippet-content {
3807 resize: vertical;
3808 }
3809}
3810
3811@media (max-width: 480px) {
3812 // Below 480px the visibility switch shows icons only, so Save fits beside it.
3813 .snippet-head-actions {
3814 justify-content: space-between;
3815 margin-left: 0;
3816 width: 100%;
3817 }
3818
3819 .snippet-segmented .segment {
3820 > span {
3821 width: 26px;
3822 padding: 0;
3823 font-size: 0;
3824 line-height: 0;
3825 }
3826
3827 > svg {
3828 left: 9px;
3829 }
3830 }
3831
3832 .snippet-form .snippet-meta {
3833 padding: 16px 16px 14px;
3834 }
3835
3836 .snippet-form .snippet-slot-head {
3837 padding-left: 12px;
3838 }
3839}
3840
3841.snippet-description {
3842 color: var(--muted);
3843}
3844
3845.snippet-file {
3846 margin-top: 16px;
3847}
3848
3849.snippet-code {
3850 margin: 0;
3851 overflow-x: auto;
3852 padding: 12px;
3853}
3854
3855.inline-form {
3856 display: inline;
3857}
3858
3859/* Discussions: threads of comments, replies indented under their parent, the accepted answer. */
3860.discussion-thread {
3861 margin-bottom: 0.5rem;
3862}
3863
3864.discussion-replies {
3865 margin-left: 2rem;
3866 padding-left: 0.75rem;
3867 border-left: 2px solid var(--border);
3868}
3869
3870.discussion-accepted {
3871 border-left: 3px solid var(--ok);
3872 padding-left: 0.75rem;
3873}
3874
3875.discussion-reply {
3876 display: inline-block;
3877 margin-right: 0.75rem;
3878}
3879
3880.discussion-reply > form {
3881 margin-top: 0.5rem;
3882}
3883
3884.discussion-new label {
3885 display: block;
3886}
3887
3888// Sage redesign partials. Loaded last so they override the older rules above.
3889@import "shell";
3890@import "heatmap";
3891@import "repo";
3892@import "pull";
3893@import "dashboard";