forge
ExploreLog in

Tahinli/forge

public
0 watching
★ 0
12.1 KiB
1// pull: sage restyle of the pull request view (header, subnav, merge box, checks, side panel),
2// its files and commits diffs, the timeline cards, and the issue and pull request lists.
3// Imported after main.scss, so these rules win on equal specificity. Colour comes from tokens,
4// except the merged purple defined once below.
5
6// Merged state: a softened purple that sits beside the sage accent without matching it.
7:root {
8 --merged: #6b4fa0;
9 --merged-soft: #efeaf6;
10}
11
12@media (prefers-color-scheme: dark) {
13 :root:not([data-theme="light"]) {
14 --merged: #c7b3ee;
15 --merged-soft: #2b2440;
16 }
17}
18
19:root[data-theme="dark"] {
20 --merged: #c7b3ee;
21 --merged-soft: #2b2440;
22}
23
24// Subnav: underline tabs. Scrolls sideways on narrow screens instead of wrapping.
25
26.subnav {
27 flex-wrap: nowrap;
28 gap: 26px;
29 margin: 16px 0 0;
30 border-bottom: 1px solid var(--border);
31 overflow-x: auto;
32 overflow-y: hidden;
33 -webkit-overflow-scrolling: touch;
34}
35
36.subnav-link {
37 position: relative;
38 display: inline-flex;
39 align-items: center;
40 gap: 8px;
41 padding: 10px 0 12px;
42 border-bottom: 0;
43 color: var(--muted);
44 font-weight: 500;
45 white-space: nowrap;
46
47 &:hover {
48 color: var(--text);
49 text-decoration: none;
50 }
51
52 &.current {
53 color: var(--text);
54 font-weight: 600;
55
56 &::after {
57 content: "";
58 position: absolute;
59 left: 0;
60 right: 0;
61 bottom: 0;
62 height: 2px;
63 border-radius: 2px;
64 background: var(--accent);
65 }
66 }
67}
68
69// Pull request header and byline
70
71.issue-heading {
72 margin: 0.25rem 0 0;
73 font-size: 1.5rem;
74 font-weight: 600;
75 line-height: 1.3;
76 letter-spacing: -0.01em;
77}
78
79.issue-number {
80 font-family: var(--mono);
81 font-size: 0.9em;
82 font-weight: 500;
83}
84
85.issue-byline {
86 gap: 8px 12px;
87 margin: 12px 0 0;
88 font-size: 14px;
89
90 strong {
91 color: var(--text);
92 font-weight: 600;
93 }
94
95 code {
96 padding: 1px 6px;
97 border-radius: 6px;
98 background: var(--surface-2);
99 color: var(--text);
100 font-family: var(--mono);
101 font-size: 12.5px;
102 }
103}
104
105// State badges: open is --ok, closed is --bad, merged is the soft purple.
106
107.issue-badge {
108 display: inline-flex;
109 align-items: center;
110 gap: 6px;
111 height: 26px;
112 padding: 0 11px;
113 border: 1px solid currentColor;
114 border-radius: 999px;
115 background: var(--surface-2);
116 font-size: 13.5px;
117 font-weight: 600;
118 line-height: 1;
119
120 &.issue-badge-open {
121 color: var(--ok);
122 background: var(--surface);
123 }
124
125 &.issue-badge-closed {
126 color: var(--bad);
127 background: var(--surface);
128 }
129
130 &.issue-badge-merged {
131 border-color: transparent;
132 color: var(--merged);
133 background: var(--merged-soft);
134 }
135
136 &.issue-badge-completed {
137 border-color: transparent;
138 color: var(--merged);
139 background: var(--merged-soft);
140 }
141
142 &.issue-badge-not-planned {
143 color: var(--muted);
144 }
145}
146
147// Lists: rows with hairline borders, hover tint.
148
149.issue-list {
150 border-color: var(--border);
151 border-radius: var(--radius);
152 background: var(--surface);
153 box-shadow: none;
154}
155
156.issue-row {
157 padding: 14px 16px;
158 border-top-color: var(--border);
159 transition: background 0.12s ease;
160
161 &:hover {
162 background: var(--surface-2);
163 }
164}
165
166.issue-title {
167 color: var(--text);
168 font-weight: 600;
169}
170
171.issue-meta {
172 font-size: 13.5px;
173}
174
175.issue-state {
176 width: 14px;
177 height: 14px;
178 margin-top: 0.2rem;
179 border-width: 2px;
180}
181
182.issue-state-open {
183 border-color: var(--ok);
184 background: var(--surface);
185}
186
187.issue-state-closed {
188 border-color: var(--merged);
189 background: var(--merged);
190}
191
192.issue-state.pull-state-closed {
193 border-color: var(--bad);
194 background: var(--bad);
195}
196
197.issue-state.pull-state-merged {
198 border-color: var(--merged);
199 background: var(--merged);
200}
201
202// Layout: the side panel sits in a 260px column from 1000px wide.
203
204.issue-layout {
205 align-items: start;
206}
207
208@media (min-width: 1000px) {
209 .issue-layout {
210 grid-template-columns: minmax(0, 1fr) 260px;
211 gap: 40px;
212 }
213}
214
215// Breathing room between the header meta line (or the PR tabs) and the discussion.
216.issue-head + .issue-layout,
217.subnav + .issue-layout {
218 margin-top: 16px;
219}
220
221.issue-sidebar {
222 gap: 0;
223 align-content: start;
224 min-width: 0;
225 font-size: 14px;
226
227 h2 {
228 margin: 0 0 8px;
229 color: var(--muted);
230 font-size: 12px;
231 font-weight: 600;
232 letter-spacing: 0.04em;
233 text-transform: uppercase;
234 }
235}
236
237// Side sections: 16px rhythm with a hairline between them. Controls are compact (32px),
238// Save buttons are small outline buttons. A control and its Save button share a row; the
239// help text sits directly under the control.
240.issue-side-section {
241 padding-bottom: 16px;
242 margin-bottom: 16px;
243 border-bottom: 1px solid var(--border);
244
245 input[type="text"],
246 select {
247 height: 32px;
248 box-sizing: border-box;
249 padding: 0 8px;
250 font-size: 13px;
251 }
252
253 button {
254 height: 28px;
255 padding: 0 10px;
256 font-size: 13px;
257 color: var(--text);
258 background: var(--surface);
259 border: 1px solid var(--border-strong);
260 border-radius: var(--radius);
261 white-space: nowrap;
262 }
263
264 form.stack {
265 display: grid;
266 grid-template-columns: minmax(0, 1fr) auto;
267 align-items: center;
268 gap: 8px;
269
270 > * {
271 grid-column: 1 / -1;
272 }
273
274 > input[type="text"],
275 > select {
276 grid-column: 1;
277 grid-row: 1;
278 }
279
280 > input[type="text"] + p,
281 > select + p {
282 margin-top: -4px;
283 }
284
285 // A control followed by a Save button: the button goes to the right, on the same row.
286 &:has(> input[type="text"], > select) > div {
287 grid-column: 2;
288 grid-row: 1;
289 }
290 }
291}
292
293// Timeline items and reviews as cards: a surface-2 head strip over the body.
294
295.issue-item,
296.review-item {
297 border: 1px solid var(--border);
298 border-radius: var(--radius);
299 background: var(--surface);
300 overflow: hidden;
301}
302
303.issue-item-head {
304 padding: 10px 14px;
305 border-bottom: 1px solid var(--border);
306 background: var(--surface-2);
307 font-size: 13.5px;
308}
309
310.issue-item-body {
311 padding: 14px 16px;
312 line-height: 1.6;
313}
314
315// Merge box: a status headline with a coloured rule on the left, then the merge button.
316
317.merge-box {
318 gap: 14px;
319 padding: 18px 20px;
320 border: 1px solid var(--border);
321 border-left: 3px solid var(--border-strong);
322 border-radius: var(--radius);
323 background: var(--surface);
324
325 &.merge-ok {
326 border-left-color: var(--ok);
327 background: var(--surface);
328 }
329
330 &.merge-bad {
331 border-left-color: var(--bad);
332 background: var(--surface);
333 }
334
335 &.merge-muted {
336 border-left-color: var(--border-strong);
337 color: var(--muted);
338 }
339
340 &.merge-merged {
341 border-left-color: var(--merged);
342 }
343}
344
345.merge-headline {
346 display: flex;
347 align-items: center;
348 gap: 8px;
349 font-size: 1rem;
350 font-weight: 600;
351 color: var(--text);
352
353 &::before {
354 content: "";
355 flex: none;
356 width: 8px;
357 height: 8px;
358 border-radius: 50%;
359 background: currentColor;
360 }
361}
362
363.merge-ok .merge-headline {
364 color: var(--ok);
365}
366
367.merge-bad .merge-headline {
368 color: var(--bad);
369}
370
371.merge-merged .merge-headline {
372 color: var(--merged);
373}
374
375.merge-muted .merge-headline {
376 color: var(--muted);
377}
378
379.merge-box button[type="submit"] {
380 width: 100%;
381 height: 38px;
382 border: 1px solid var(--accent);
383 border-radius: var(--radius);
384 background: var(--accent);
385 color: var(--accent-text);
386 font-weight: 600;
387 cursor: pointer;
388
389 &:hover {
390 border-color: var(--accent-ink);
391 background: var(--accent-ink);
392 }
393}
394
395// Checks: status icons in the status colours, one row each.
396
397.checks-summary {
398 gap: 8px;
399 font-size: 14px;
400
401 &.status-success > .status-icon {
402 color: var(--ok);
403 }
404
405 &.status-failure > .status-icon {
406 color: var(--bad);
407 }
408
409 &.status-pending > .status-icon {
410 color: var(--warn);
411 }
412}
413
414.status-icon {
415 &.status-success {
416 color: var(--ok);
417 }
418
419 &.status-failure {
420 color: var(--bad);
421 }
422
423 &.status-pending {
424 color: var(--warn);
425 }
426}
427
428.status-list {
429 gap: 10px;
430}
431
432.status-item {
433 align-items: center;
434 gap: 8px;
435 font-size: 14px;
436}
437
438// Diffs: a file card with a sticky header strip, mono code, and add/del rows marked on the left.
439
440.diff-file {
441 max-width: 100%;
442 margin: 0 0 24px;
443 border-color: var(--border);
444 border-radius: var(--radius);
445 background: var(--surface);
446 overflow: visible;
447}
448
449.diff-file-header {
450 position: sticky;
451 top: var(--topbar-h);
452 z-index: 2;
453 display: flex;
454 align-items: center;
455 justify-content: space-between;
456 flex-wrap: wrap;
457 gap: 6px 16px;
458 padding: 10px 14px;
459 border-radius: var(--radius) var(--radius) 0 0;
460 background: var(--surface-2);
461 font-size: 13.5px;
462}
463
464// Path on the left; status and counts grouped on the right.
465.diff-path {
466 min-width: 0;
467 overflow-wrap: anywhere;
468 font-family: var(--mono);
469 color: var(--text);
470}
471
472.diff-file-meta {
473 display: inline-flex;
474 align-items: center;
475 flex-shrink: 0;
476 gap: 12px;
477}
478
479.diff-file-body {
480 border: 0;
481 border-radius: 0 0 var(--radius) var(--radius);
482 background: var(--surface);
483 overflow-x: auto;
484 overflow-y: hidden;
485}
486
487.diff-stats {
488 font-size: 13px;
489
490 .add {
491 color: var(--add-mark);
492 }
493
494 .del {
495 color: var(--del-mark);
496 }
497}
498
499.diff-table {
500 font-size: 12.5px;
501 line-height: 20px;
502
503 .diff-line {
504 > .diff-ln {
505 background: var(--surface);
506 }
507
508 &.add {
509 background: var(--add-bg);
510
511 > .diff-ln {
512 background: var(--add-bg);
513 }
514
515 > td:first-child {
516 box-shadow: inset 3px 0 0 var(--add-mark);
517 }
518
519 .diff-code {
520 color: var(--add-text);
521 }
522 }
523
524 &.del {
525 background: var(--del-bg);
526
527 > .diff-ln {
528 background: var(--del-bg);
529 }
530
531 > td:first-child {
532 box-shadow: inset 3px 0 0 var(--del-mark);
533 }
534
535 .diff-code {
536 color: var(--del-text);
537 }
538 }
539
540 &.ctx .diff-code {
541 color: var(--text);
542 }
543
544 &.hunk {
545 background: var(--hunk-bg);
546
547 > td {
548 background: var(--hunk-bg);
549 color: var(--muted);
550 }
551
552 .diff-code {
553 padding-top: 4px;
554 padding-bottom: 4px;
555 color: var(--muted);
556 }
557 }
558 }
559
560 .diff-ln {
561 min-width: 3.5em;
562 padding: 0 10px;
563 color: var(--ln-text);
564 border-right: 1px solid var(--border);
565 }
566
567 .diff-code {
568 padding: 0 18px 0 12px;
569 }
570}
571
572.diff-collapsed {
573 margin: 0;
574 padding: 12px 14px;
575 border: 0;
576 background: var(--surface);
577 color: var(--muted);
578 font-size: 14px;
579}
580
581// Changed-file summary: a compact list of mono paths with their counts, no bullets.
582
583.diff-index {
584 list-style: none;
585 margin: 8px 0 16px;
586 padding: 0;
587 font-size: 13px;
588
589 li {
590 display: flex;
591 flex-wrap: wrap;
592 align-items: baseline;
593 gap: 2px 10px;
594 padding: 5px 0;
595 min-width: 0;
596 border-bottom: 1px solid var(--border);
597
598 &:last-child {
599 border-bottom: 0;
600 }
601 }
602
603 a {
604 min-width: 0;
605 overflow-wrap: anywhere;
606 font-family: var(--mono);
607 color: var(--text);
608
609 &:hover {
610 color: var(--accent-text);
611 }
612 }
613}
614
615// New issue: label checkboxes sit in a wrapping row, box beside name.
616
617.label-picker {
618 flex-direction: row;
619 flex-wrap: wrap;
620 gap: 8px 18px;
621
622 label {
623 flex-direction: row;
624 align-items: center;
625 gap: 8px;
626 font-weight: 400;
627 }
628
629 input[type="checkbox"] {
630 width: auto;
631 margin: 0;
632 }
633}
634
635// Review bar: radios as a row of options, actions as a row with Submit primary and Discard quiet.
636
637.review-bar {
638 .review-choices {
639 flex-direction: row;
640 flex-wrap: wrap;
641 align-items: center;
642 justify-content: flex-start;
643 gap: 8px 22px;
644
645 label {
646 flex-direction: row;
647 align-items: center;
648 gap: 8px;
649 font-weight: 400;
650 }
651
652 input[type="radio"] {
653 width: auto;
654 margin: 0;
655 }
656 }
657
658 .review-actions {
659 display: flex;
660 flex-wrap: wrap;
661 align-items: center;
662 gap: 8px 18px;
663 margin-top: 14px;
664
665 .review-inline .link-button {
666 color: var(--muted);
667 text-decoration: underline;
668 text-underline-offset: 3px;
669
670 &:hover {
671 color: var(--text);
672 }
673 }
674 }
675}
676
677// Narrow screens: the diff scrolls inside its own box, never the page.
678
679@media (max-width: 600px) {
680 .subnav {
681 gap: 20px;
682 }
683
684 .diff-file-header {
685 padding: 8px 12px;
686 }
687
688 .diff-table .diff-code {
689 padding-right: 14px;
690 }
691}