forge
ExploreLog in

Tahinli/forge

public
0 watching
★ 0
69.1 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: 1280px;
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: 60rem;
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 control: a dropdown whose summary shows the current level. Each level is a small POST form
3170// inside the menu; the current one carries a check mark.
3171.watch-control {
3172 display: inline-flex;
3173 flex-wrap: wrap;
3174 align-items: center;
3175 gap: 0.5rem;
3176 margin: 0;
3177}
3178
3179.watch-menu {
3180 summary {
3181 position: relative;
3182 display: inline-flex;
3183 align-items: center;
3184 gap: 6px;
3185 height: 28px;
3186 padding: 0 26px 0 8px;
3187 font-size: 13px;
3188 font-weight: 500;
3189 color: var(--text);
3190 background: var(--surface);
3191 border: 1px solid var(--border-strong);
3192 white-space: nowrap;
3193
3194 // The caret: a small rotated border, so no glyph is needed.
3195 &::after {
3196 content: "";
3197 position: absolute;
3198 right: 11px;
3199 top: 50%;
3200 width: 5px;
3201 height: 5px;
3202 border-right: 1.5px solid currentColor;
3203 border-bottom: 1.5px solid currentColor;
3204 transform: translateY(-70%) rotate(45deg);
3205 }
3206 }
3207
3208 &[open] summary {
3209 border-color: var(--accent);
3210 }
3211}
3212
3213.watch-eye {
3214 flex: none;
3215 width: 15px;
3216 height: 15px;
3217 fill: none;
3218 stroke: currentColor;
3219 stroke-width: 2;
3220 stroke-linecap: round;
3221 stroke-linejoin: round;
3222}
3223
3224.watch-list {
3225 left: 0;
3226 right: auto;
3227 min-width: 280px;
3228 max-width: calc(100vw - 24px);
3229}
3230
3231.watch-option-form {
3232 margin: 0;
3233}
3234
3235.watch-list button.watch-option {
3236 display: grid;
3237 grid-template-columns: 18px 1fr;
3238 column-gap: 4px;
3239 width: 100%;
3240 margin: 0;
3241 padding: 8px 14px;
3242 text-align: left;
3243 font: inherit;
3244 color: var(--text);
3245 background: none;
3246 border: 0;
3247 border-radius: 0;
3248 cursor: pointer;
3249
3250 &:hover {
3251 background: var(--surface-2);
3252 }
3253
3254 &:focus-visible {
3255 outline: 2px solid var(--focus);
3256 outline-offset: -2px;
3257 }
3258
3259 // The check mark, drawn with borders. Only the current level shows it.
3260 &::before {
3261 content: "";
3262 grid-column: 1;
3263 grid-row: 1 / span 2;
3264 justify-self: center;
3265 align-self: start;
3266 width: 5px;
3267 height: 9px;
3268 margin-top: 3px;
3269 border: solid transparent;
3270 border-width: 0 2px 2px 0;
3271 transform: rotate(45deg);
3272 }
3273
3274 &.is-current::before {
3275 border-color: var(--accent);
3276 }
3277}
3278
3279.watch-option-title {
3280 grid-column: 2;
3281 grid-row: 1;
3282 font-weight: 500;
3283}
3284
3285.watch-option-desc {
3286 grid-column: 2;
3287 grid-row: 2;
3288 font-size: 12px;
3289 line-height: 1.4;
3290 color: var(--muted);
3291 white-space: normal;
3292}
3293
3294@media (max-width: 40rem) {
3295 .notif-row {
3296 flex-direction: column;
3297 align-items: flex-start;
3298 }
3299}
3300
3301// Signed commit badges: on the commit list and the commit page.
3302.sig-badge {
3303 display: inline-block;
3304 font-size: 0.75rem;
3305 font-weight: 600;
3306 padding: 2px 8px;
3307 border-radius: 999px;
3308 vertical-align: middle;
3309 white-space: nowrap;
3310}
3311
3312.sig-verified {
3313 background: var(--public-bg);
3314 color: var(--public-text);
3315}
3316
3317.sig-unverified {
3318 background: var(--private-bg);
3319 color: var(--private-text);
3320}
3321
3322// Releases: list cards, the latest-release card on the repo home, and the asset tables.
3323.release-list-head {
3324 display: flex;
3325 align-items: center;
3326 justify-content: space-between;
3327 gap: 12px;
3328 flex-wrap: wrap;
3329}
3330
3331.release-card {
3332 border-top: 1px solid var(--border);
3333 padding: 16px 0;
3334
3335 &:first-of-type {
3336 border-top: 0;
3337 }
3338
3339 h3 {
3340 margin: 0 0 4px;
3341 }
3342}
3343
3344.release-asset-list {
3345 margin: 8px 0;
3346 padding-left: 20px;
3347}
3348
3349.release-actions {
3350 display: flex;
3351 align-items: center;
3352 gap: 16px;
3353 flex-wrap: wrap;
3354}
3355
3356.release-form {
3357 max-width: 960px;
3358
3359 textarea {
3360 width: 100%;
3361 font-family: inherit;
3362 }
3363}
3364
3365.release-assets td code {
3366 word-break: break-all;
3367}
3368
3369.badge.latest {
3370 background: var(--public-bg);
3371 color: var(--public-text);
3372}
3373
3374.badge.draft {
3375 background: var(--private-bg);
3376 color: var(--private-text);
3377}
3378
3379// CI: run list, run page, job page and log.
3380.ci-state {
3381 display: inline-block;
3382 font-size: 0.75rem;
3383 font-weight: 600;
3384 padding: 2px 8px;
3385 border-radius: 999px;
3386 background: var(--private-bg);
3387 color: var(--private-text);
3388
3389 &::before {
3390 content: "";
3391 display: inline-block;
3392 width: 0.5em;
3393 height: 0.5em;
3394 margin-right: 6px;
3395 border-radius: 50%;
3396 background: currentColor;
3397 vertical-align: middle;
3398 }
3399
3400 &.ci-success {
3401 background: var(--public-bg);
3402 color: var(--public-text);
3403 }
3404
3405 &.ci-queued,
3406 &.ci-running,
3407 &.ci-blocked,
3408 &.ci-waiting_approval {
3409 background: var(--private-bg);
3410 color: var(--warning);
3411 }
3412
3413 &.ci-failure,
3414 &.ci-error {
3415 background: var(--danger-bg);
3416 color: var(--danger);
3417 }
3418}
3419
3420.ci-actions {
3421 display: flex;
3422 align-items: center;
3423 gap: 12px;
3424 flex-wrap: wrap;
3425 margin-top: 12px;
3426}
3427
3428.ci-manual-form {
3429 display: flex;
3430 align-items: center;
3431 gap: 12px;
3432 flex-wrap: wrap;
3433}
3434
3435.ci-config-error {
3436 white-space: pre-wrap;
3437 color: var(--danger);
3438}
3439
3440.ci-log {
3441 max-height: 70vh;
3442 overflow: auto;
3443 white-space: pre-wrap;
3444 word-break: break-word;
3445 font-size: 0.85rem;
3446 margin: 0;
3447}
3448
3449.ci-artifacts {
3450 margin: 0;
3451 padding-left: 20px;
3452}
3453
3454/* Theme and language preferences (T1). The palettes live at the top of this file: the dark
3455 variables apply under :root:not([data-theme="light"]) and under :root[data-theme="dark"]. */
3456.theme-picker {
3457 display: inline-flex;
3458 align-items: center;
3459 gap: 8px;
3460 margin-left: auto;
3461}
3462
3463.theme-picker label {
3464 display: inline-flex;
3465 align-items: center;
3466 gap: 6px;
3467}
3468
3469.theme-picker label span {
3470 color: var(--muted);
3471}
3472
3473// Snippets (wave C, S1): list, editor, file views and revisions.
3474.snippet-list .item-main a {
3475 text-decoration: none;
3476}
3477
3478// Editor: a console-style page. A header row (title, visibility, Save), then one document
3479// column: the title card, the file cards and a row with "Add file". 880px wide by default. The
3480// code area can be dragged wider; the form then grows with it, up to the page column's width.
3481.snippet-form {
3482 display: flex;
3483 flex-direction: column;
3484 gap: 16px;
3485 width: fit-content;
3486 min-width: min(880px, 100%);
3487 max-width: 100%;
3488
3489 svg {
3490 flex: none;
3491 width: 16px;
3492 height: 16px;
3493 fill: none;
3494 stroke: currentColor;
3495 stroke-width: 2;
3496 stroke-linecap: round;
3497 stroke-linejoin: round;
3498 }
3499}
3500
3501.snippet-head {
3502 display: flex;
3503 flex-wrap: wrap;
3504 align-items: center;
3505 gap: 12px 16px;
3506
3507 h1 {
3508 margin: 0;
3509 font-size: 1.5rem;
3510 line-height: 1.25;
3511 }
3512}
3513
3514.snippet-head-actions {
3515 display: flex;
3516 flex-wrap: wrap;
3517 align-items: center;
3518 gap: 10px;
3519 margin-left: auto;
3520}
3521
3522// Visibility: three icon-and-word segments in one 32px pill. The radios are hidden; the focus
3523// ring is drawn on the segment's text, and the long description is the segment's title.
3524.snippet-visibility {
3525 min-width: 0;
3526 margin: 0;
3527 padding: 0;
3528 border: 0;
3529}
3530
3531.snippet-segmented {
3532 display: inline-flex;
3533 align-items: center;
3534 gap: 2px;
3535 height: 32px;
3536 box-sizing: border-box;
3537 max-width: 100%;
3538 padding: 2px;
3539 border: 1px solid var(--border);
3540 border-radius: 999px;
3541 background: var(--surface-2);
3542}
3543
3544.snippet-segmented .segment {
3545 position: relative;
3546 display: inline-flex;
3547 margin: 0;
3548 cursor: pointer;
3549
3550 input[type="radio"] {
3551 position: absolute;
3552 width: 1px;
3553 height: 1px;
3554 margin: 0;
3555 padding: 0;
3556 border: 0;
3557 opacity: 0;
3558 }
3559
3560 > span {
3561 display: inline-flex;
3562 align-items: center;
3563 height: 26px;
3564 padding: 0 12px;
3565 border-radius: 999px;
3566 font-size: 0.8125rem;
3567 font-weight: 500;
3568 line-height: 1;
3569 color: var(--muted);
3570 white-space: nowrap;
3571 }
3572
3573 > svg {
3574 position: absolute;
3575 left: 12px;
3576 top: 50%;
3577 transform: translateY(-50%);
3578 width: 14px;
3579 height: 14px;
3580 pointer-events: none;
3581 }
3582
3583 > span {
3584 padding-left: 32px;
3585 }
3586
3587 &:hover > span {
3588 color: var(--text);
3589 }
3590
3591 input[type="radio"]:checked ~ span {
3592 background: var(--surface);
3593 color: var(--accent-ink);
3594 box-shadow: 0 1px 2px rgba(30, 36, 32, .12);
3595 }
3596
3597 input[type="radio"]:checked ~ svg {
3598 color: var(--accent-ink);
3599 }
3600
3601 input[type="radio"]:focus-visible ~ span {
3602 box-shadow: 0 0 0 2px var(--focus);
3603 }
3604}
3605
3606// The document: title and description on one card, then the file cards.
3607.snippet-doc {
3608 display: flex;
3609 flex-direction: column;
3610 gap: 16px;
3611 min-width: 0;
3612}
3613
3614.snippet-meta {
3615 display: flex;
3616 flex-direction: column;
3617 min-width: 0;
3618 padding: 20px 22px 16px;
3619 background: var(--surface);
3620 border: 1px solid var(--border);
3621 border-radius: 12px;
3622 box-shadow: var(--shadow);
3623
3624 &:focus-within {
3625 border-color: var(--focus);
3626 box-shadow: 0 0 0 3px var(--accent-soft), var(--shadow);
3627 }
3628}
3629
3630// Borderless inputs: the card carries the border and the focus ring.
3631.snippet-form .snippet-title,
3632.snippet-form .snippet-desc {
3633 display: block;
3634 box-sizing: border-box;
3635 width: 100%;
3636 min-width: 0;
3637 margin: 0;
3638 padding: 0;
3639 border: 0;
3640 border-radius: 0;
3641 background: transparent;
3642 box-shadow: none;
3643 color: var(--text);
3644 font: inherit;
3645 outline: none;
3646
3647 &:focus {
3648 border: 0;
3649 box-shadow: none;
3650 outline: none;
3651 }
3652
3653 &::placeholder {
3654 color: var(--muted);
3655 opacity: 1;
3656 }
3657}
3658
3659.snippet-form .snippet-title {
3660 font-size: 1.25rem;
3661 font-weight: 600;
3662 line-height: 1.3;
3663}
3664
3665.snippet-form .snippet-desc {
3666 margin-top: 12px;
3667 padding-top: 12px;
3668 border-top: 1px solid var(--border);
3669 color: var(--text-2);
3670 font-size: 0.9375rem;
3671 line-height: 1.5;
3672}
3673
3674// The files card: a tab strip, then the panel of the checked file. Each tab is a radio and a
3675// label, so the strip works with JavaScript off. The panels are generated per file slot below.
3676.snippet-form .snippet-files {
3677 display: flex;
3678 flex-direction: column;
3679 min-width: 0;
3680 max-width: 100%;
3681 overflow: hidden;
3682 background: var(--surface);
3683 border: 1px solid var(--border);
3684 border-radius: 12px;
3685 box-shadow: var(--shadow);
3686
3687 // Only the filename and the contents light the card; tab radios and buttons do not.
3688 &:has(.snippet-name:focus, .snippet-content:focus) {
3689 border-color: var(--focus);
3690 box-shadow: 0 0 0 3px var(--accent-soft), var(--shadow);
3691 }
3692}
3693
3694// Tab strip: the tabs scroll sideways inside it, the counter sits at the right end.
3695.snippet-tabbar {
3696 display: flex;
3697 flex-wrap: wrap;
3698 align-items: stretch;
3699 gap: 0 12px;
3700 padding-right: 12px;
3701 background: var(--surface-2);
3702 // A long tab strip scrolls inside the card; it must not widen the form on its own.
3703 contain: inline-size;
3704}
3705
3706.snippet-tabs {
3707 display: flex;
3708 align-items: flex-end;
3709 flex: 1 1 auto;
3710 min-width: 0;
3711 max-width: 100%;
3712 overflow-x: auto;
3713 overflow-y: hidden;
3714 padding-left: 6px;
3715 scrollbar-width: thin;
3716}
3717
3718.snippet-count {
3719 flex: none;
3720 margin-left: auto;
3721 align-self: center;
3722 white-space: nowrap;
3723 font-variant-numeric: tabular-nums;
3724}
3725
3726// The radio is hidden; the label is the tab. The focus ring is drawn on the label.
3727.snippet-tab-radio {
3728 position: absolute;
3729 width: 1px;
3730 height: 1px;
3731 margin: 0;
3732 padding: 0;
3733 border: 0;
3734 opacity: 0;
3735 outline: none;
3736}
3737
3738.snippet-form .snippet-tab {
3739 flex: none;
3740 display: inline-flex;
3741 align-items: center;
3742 box-sizing: border-box;
3743 max-width: 200px;
3744 height: 36px;
3745 margin: 0 1px;
3746 padding: 0 14px;
3747 border: 1px solid transparent;
3748 border-bottom: 0;
3749 border-radius: 8px 8px 0 0;
3750 color: var(--muted);
3751 font-family: var(--mono);
3752 font-size: 0.8125rem;
3753 white-space: nowrap;
3754 overflow: hidden;
3755 text-overflow: ellipsis;
3756 cursor: pointer;
3757 user-select: none;
3758
3759 &:hover {
3760 color: var(--text);
3761 }
3762}
3763
3764.snippet-tab-radio:checked + .snippet-tab {
3765 background: var(--surface);
3766 border-color: var(--border);
3767 box-shadow: inset 0 2px 0 var(--accent);
3768 color: var(--text);
3769}
3770
3771.snippet-tab-radio:focus-visible + .snippet-tab {
3772 outline: 2px solid var(--focus);
3773 outline-offset: -2px;
3774}
3775
3776// "Add file" as a compact "+" at the end of the strip.
3777.snippet-form .snippet-tab-add {
3778 flex: none;
3779 display: inline-flex;
3780 align-items: center;
3781 justify-content: center;
3782 align-self: center;
3783 width: 30px;
3784 height: 30px;
3785 margin: 0 0 3px 4px;
3786 padding: 0;
3787 border: 0;
3788 border-radius: 8px;
3789 background: transparent;
3790 color: var(--muted);
3791
3792 &:hover {
3793 background: var(--surface);
3794 color: var(--text);
3795 }
3796
3797 &:focus-visible {
3798 outline: none;
3799 box-shadow: 0 0 0 2px var(--focus);
3800 }
3801}
3802
3803// One panel per file: the name row, then the code area. Hidden unless its tab is checked.
3804.snippet-form .snippet-panel {
3805 display: none;
3806 min-width: 0;
3807 margin: 0;
3808 padding: 0;
3809 border: 0;
3810}
3811
3812.snippet-form .snippet-slot-head {
3813 display: flex;
3814 align-items: center;
3815 gap: 10px;
3816 height: 40px;
3817 box-sizing: border-box;
3818 padding: 0 6px 0 14px;
3819 background: var(--surface);
3820 border-bottom: 1px solid var(--border);
3821 color: var(--muted);
3822}
3823
3824.snippet-form .snippet-name {
3825 flex: 1 1 auto;
3826 min-width: 0;
3827 height: 100%;
3828 margin: 0;
3829 padding: 0;
3830 border: 0;
3831 border-radius: 0;
3832 background: transparent;
3833 box-shadow: none;
3834 color: var(--text);
3835 font-family: var(--mono);
3836 font-size: 0.8125rem;
3837 outline: none;
3838
3839 &:focus {
3840 border: 0;
3841 box-shadow: none;
3842 outline: none;
3843 }
3844
3845 &::placeholder {
3846 color: var(--muted);
3847 opacity: 1;
3848 }
3849}
3850
3851.snippet-form .snippet-remove {
3852 align-self: auto;
3853 flex: none;
3854 width: 28px;
3855 height: 28px;
3856 padding: 0;
3857 justify-content: center;
3858 border: 0;
3859 border-radius: 8px;
3860 background: transparent;
3861 color: var(--muted);
3862
3863 &:hover {
3864 background: var(--surface-2);
3865 color: var(--text);
3866 }
3867
3868 &:focus-visible {
3869 outline: none;
3870 box-shadow: 0 0 0 2px var(--focus);
3871 }
3872}
3873
3874// The code area fills the screen: the page header, title card and tabs sit above it, so the
3875// height is the viewport less that offset, kept between 240px and 720px. Resizable by hand, in
3876// both directions from 600px up (the width is capped by the card, which is capped by the page).
3877.snippet-form .snippet-content {
3878 display: block;
3879 box-sizing: border-box;
3880 width: 100%;
3881 min-width: 0;
3882 max-width: 100%;
3883 height: 480px;
3884 height: clamp(240px, calc(100dvh - 400px), 720px);
3885 margin: 0;
3886 padding: 14px 16px;
3887 border: 0;
3888 border-radius: 0;
3889 background: var(--surface-2);
3890 box-shadow: none;
3891 color: var(--text);
3892 font-family: var(--mono);
3893 font-size: 13px;
3894 line-height: 1.6;
3895 tab-size: 4;
3896 resize: both;
3897 outline: none;
3898
3899 &:focus {
3900 border: 0;
3901 box-shadow: none;
3902 outline: none;
3903 }
3904}
3905
3906// Each panel shows when its tab is checked. One rule per file slot (MAX_FILES in Rust).
3907@for $i from 0 through 9 {
3908 .snippet-files:has(#snippet-tab-#{$i}:checked) .snippet-panel-#{$i} {
3909 display: block;
3910 }
3911}
3912
3913@media (max-width: 599px) {
3914 .snippet-form .snippet-content {
3915 resize: vertical;
3916 }
3917}
3918
3919@media (max-width: 480px) {
3920 // Below 480px the visibility switch shows icons only, so Save fits beside it.
3921 .snippet-head-actions {
3922 justify-content: space-between;
3923 margin-left: 0;
3924 width: 100%;
3925 }
3926
3927 .snippet-segmented .segment {
3928 > span {
3929 width: 26px;
3930 padding: 0;
3931 font-size: 0;
3932 line-height: 0;
3933 }
3934
3935 > svg {
3936 left: 9px;
3937 }
3938 }
3939
3940 .snippet-form .snippet-meta {
3941 padding: 16px 16px 14px;
3942 }
3943
3944 .snippet-form .snippet-slot-head {
3945 padding-left: 12px;
3946 }
3947}
3948
3949.snippet-description {
3950 color: var(--muted);
3951}
3952
3953.snippet-file {
3954 margin-top: 16px;
3955}
3956
3957.snippet-code {
3958 margin: 0;
3959 overflow-x: auto;
3960 padding: 12px;
3961}
3962
3963.inline-form {
3964 display: inline;
3965}
3966
3967/* Discussions: threads of comments, replies indented under their parent, the accepted answer. */
3968.discussion-thread {
3969 margin-bottom: 0.5rem;
3970}
3971
3972.discussion-replies {
3973 margin-left: 2rem;
3974 padding-left: 0.75rem;
3975 border-left: 2px solid var(--border);
3976}
3977
3978.discussion-accepted {
3979 border-left: 3px solid var(--ok);
3980 padding-left: 0.75rem;
3981}
3982
3983.discussion-reply {
3984 display: inline-block;
3985 margin-right: 0.75rem;
3986}
3987
3988.discussion-reply > form {
3989 margin-top: 0.5rem;
3990}
3991
3992.discussion-new label {
3993 display: block;
3994}
3995
3996// Sage redesign partials. Loaded last so they override the older rules above.
3997@import "shell";
3998@import "heatmap";
3999@import "repo";
4000@import "pull";
4001@import "dashboard";
4002
4003// Code inside cards, notices and settings panels must stay inside the card. Long tokens and
4004// commands wrap instead of running past the right edge. The rendered README card (.readme) and
4005// file views, diffs and highlighted blobs are left alone so their code keeps horizontal scrolling.
4006.card:not(.readme),
4007.notice {
4008 code {
4009 overflow-wrap: anywhere;
4010 word-break: normal;
4011 }
4012
4013 pre {
4014 max-width: 100%;
4015 margin: 0 0 12px;
4016 padding: 12px;
4017 overflow-x: auto;
4018 white-space: pre-wrap;
4019 overflow-wrap: anywhere;
4020 background: var(--code-bg);
4021 border: 1px solid var(--border);
4022 border-radius: var(--radius);
4023 color: var(--text);
4024
4025 code {
4026 padding: 0;
4027 background: none;
4028 white-space: inherit;
4029 overflow-wrap: anywhere;
4030 }
4031 }
4032}