detached 0e6522c

60-mr.css RawHistoryFolder

1@layer components {
2  /* merge requests: the review page. Hue only where it carries a verdict. */
3  .mr-h .show-kv { margin-top: 0; }
4
5  .mr-state[data-state="open"]::before { content: "● "; color: var(--teal); }
6  .mr-state[data-state="merged"]::before { content: "✓ "; color: var(--teal); }
7  .mr-state[data-state="closed"]::before { content: "✗ "; color: var(--red); }
8  .mr-state[data-state="draft"]::before { content: "◌ "; color: var(--ink-dim); }
9  /* the description: Markdown as a README, as wide as the header card above;
10     the text inside keeps the reading measure */
11  .mr-desc.prose { max-width: none; padding: var(--s4) var(--s5); background: var(--bg-surface); border: 1px solid var(--border); border-radius: var(--r); }
12  .mr-desc.prose > * { max-width: var(--measure); }
13  .mr-desc.prose > pre, .mr-desc.prose > table { max-width: 100%; }
14
15  .since {
16    display: inline-flex; flex-wrap: wrap; gap: var(--s1); padding: var(--s1);
17    background: var(--bg-raised); border-radius: var(--r-s);
18    & a { padding: var(--s1) var(--s3); border-radius: 4px; text-decoration: none; color: var(--ink-dim); font-size: var(--fs-s); }
19    & a:hover { color: var(--ink-hi); }
20    & a[aria-current] { color: var(--ink-hi); font-weight: 700; background: var(--bg-surface); box-shadow: 0 0 0 1px var(--border-strong); }
21    & .mute { font-weight: 400; font-family: var(--mono); }
22  }
23
24  /* the bar above the diff of a merge request: the diff tools, the checks and
25     the menus of the owner, on one line that follows the review */
26  /* on a narrow screen the bar takes two lines: the diff tools, then the
27     checks and the menus; the file headers stick under both */
28  @media (max-width: 860px) {
29    body:has(.mr-bar) { --bar-h: 96px; }
30    .mr-bar .grow { flex-basis: 100%; height: 0; }
31  }
32  .mr-bar {
33    gap: var(--s2);
34    & .mr-ck { margin-left: 0; height: 32px; padding: 0 var(--s2); border-radius: var(--r-s); text-decoration: none; color: var(--ink-dim); font-size: var(--fs-s); }
35    & .mr-ck:hover { background: var(--bg-raised); color: var(--ink-hi); }
36  }
37
38  /* a menu of the bar: a <details> whose panel opens under its button */
39  .menu {
40    position: relative;
41    & > summary { list-style: none; }
42    & > summary::-webkit-details-marker { display: none; }
43    & > summary.primary::after { content: ""; width: 0; height: 0; margin-left: 2px; border: 4px solid transparent; border-top-color: currentColor; transform: translateY(2px); }
44    & .menu-pop {
45      position: absolute; right: 0; top: calc(100% + var(--s1)); z-index: 6; width: 320px; max-width: calc(100vw - 2 * var(--pad));
46      display: grid; gap: var(--s3); padding: var(--s3) var(--s4);
47      background: var(--bg-surface); border: 1px solid var(--border-strong); border-radius: var(--r); box-shadow: 0 8px 24px rgb(0 0 0 / .18);
48      font-size: var(--fs-s);
49    }
50    & .kv { margin: 0; }
51  }
52  .mmenu {
53    & .d-modes { display: grid; gap: var(--s1); border: 0; padding: 0; margin: 0; }
54    & .d-modes label, & .d-check {
55      display: flex; gap: var(--s2); align-items: center; padding: 4px var(--s2); margin: 0 calc(-1 * var(--s2));
56      border-radius: var(--r-s); color: var(--ink); cursor: pointer;
57    }
58    & .d-modes label:hover, & .d-check:hover { background: var(--bg-raised); }
59    & .d-modes label:has(:checked) { background: var(--bg-raised); color: var(--ink-hi); font-weight: 600; }
60    & .d-modes kbd, & .d-check kbd { margin-left: auto; }
61    & input { accent-color: var(--ink-hi); margin: 0; }
62    & .d-check { color: var(--ink-dim); }
63    & .d-btns { display: flex; margin-top: var(--s1); }
64    & .d-btns .btn { flex: 1; justify-content: center; }
65    & .d-draft { color: var(--ink-dim); }
66  }
67  .omenu {
68    & > summary { font-size: 18px; line-height: 1; }
69    & .menu-pop { width: 280px; }
70    & form .btn { width: 100%; justify-content: flex-start; }
71    & .btn kbd { margin-left: auto; }
72    & .d-or > summary { cursor: pointer; list-style: none; color: var(--ink-mute); }
73    & .d-or > summary::-webkit-details-marker { display: none; }
74    & .d-or > summary::before { content: "▸ "; }
75    & .d-or[open] > summary::before { content: "▾ "; }
76    & .d-or > summary:hover { color: var(--ink-hi); }
77    & .d-or[open] > .clone { margin-top: var(--s2); }
78    & .clone code { white-space: pre-wrap; overflow-wrap: anywhere; }
79  }
80
81  /* the rail of a merge request follows the review: it sticks under the
82     header, one screen high, and scrolls on its own to its checks, commits
83     and settings. The list of files keeps its own scroll, under a height, so
84     that the blocks below it stay in reach. Only on a two-column layout. */
85  @media (min-width: 1081px) {
86    .rail:has(.r-files) {
87      position: sticky; top: calc(var(--hdr-h, 0px) + var(--s4));
88      max-height: calc(100dvh - var(--hdr-h, 0px) - var(--s4) * 2);
89      overflow-y: auto; overscroll-behavior: contain; scrollbar-width: thin;
90    }
91    /* the blocks keep their height: the rail scrolls, it does not squeeze them */
92    .rail:has(.r-files) > * { flex: none; }
93    .rail .r-files > .files { max-height: 55dvh; overflow-y: auto; overscroll-behavior: contain; scrollbar-width: thin; }
94  }
95  /* on one column the rail goes under the diff: the list of files keeps its
96     own scroll, under a height */
97  @media (max-width: 1080px) {
98    .rail .r-files > .files { max-height: 50dvh; overflow-y: auto; overscroll-behavior: contain; }
99  }
100  .r-files {
101    & > h2 kbd { margin-left: auto; }
102    & > h2 .fold { width: 24px; height: 24px; margin: -2px -6px -2px 0; background: none; border-color: transparent; color: var(--ink-dim); }
103    & > h2 .fold:hover { color: var(--ink-hi); border-color: var(--border-strong); }
104    & > h2 .fold .ico { transition: transform .15s; }
105    &[data-folded] > h2 { border-bottom: 0; }
106    &[data-folded] > h2 .fold .ico { transform: rotate(-90deg); }
107    &[data-folded] > :not(h2) { display: none; }
108    & > .ffilter { margin: var(--s2) var(--s3) 0; }
109    & > .ffilter input {
110      width: 100%; height: 30px; padding: 0 var(--s2); font: inherit; font-size: var(--fs-s); color: inherit;
111      background: var(--bg-inset); border: 1px solid var(--border-strong); border-radius: var(--r-s);
112    }
113    & > .files { margin: var(--s2) 0 !important; padding: 0 var(--s2); }
114  }
115
116  /* a file of the diff whose window is not loaded yet: about its room */
117  .dlazy {
118    display: flex; align-items: center; justify-content: center;
119    background: repeating-linear-gradient(var(--bg-surface) 0 var(--lh-code), var(--paper-alt) var(--lh-code) calc(var(--lh-code) * 2));
120    border: 1px solid var(--border); border-top: 0; border-radius: 0 0 var(--r) var(--r);
121    font-size: var(--fs-s);
122    & a { color: var(--ink-mute); }
123    &.dlazy-1 { height: 120px; } &.dlazy-2 { height: 360px; } &.dlazy-3 { height: 900px; } &.dlazy-4 { height: 1600px; }
124    /* with scripts the window comes by itself: the link of the page without
125       scripts would only flash */
126    html.js & > a { visibility: hidden; }
127    & .notice { margin: 0; }
128  }
129  .dloose {
130    padding: var(--s2) var(--s3) var(--s3); background: var(--bg-surface);
131    border: 1px solid var(--border); border-top: 0; border-radius: 0 0 var(--r) var(--r);
132  }
133
134  .pcommits { font-size: var(--fs-s); display: grid; gap: var(--s1); }
135  .pcommits li { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; padding: 2px var(--s2); margin: 0 calc(-1 * var(--s2)); border-radius: 4px; }
136  .pcommits li a { font-family: var(--mono); }
137  .pcommits li[data-new] { background: var(--bg-raised); }
138  .pcommits .s { color: var(--ink); }
139
140
141  /* a note: a ruled block, under its line in the diff and in the list of comments */
142  .cmt {
143    padding: var(--s3) var(--s4); margin-top: var(--s3); max-width: var(--measure);
144    background: var(--bg-surface); border: 1px solid var(--border); border-left: 3px solid var(--ink-hi); border-radius: var(--r-s);
145    white-space: normal; font-family: var(--font); font-size: var(--fs-s); line-height: 22px;
146    & p { white-space: normal; }
147    & .c-who { font-weight: bold; }
148    & .c-state { color: var(--ink-dim); }
149    & .c-where { font-size: var(--fs-s); overflow-wrap: anywhere; }
150    & .c-act { margin-top: var(--s2); }
151    & .c-act button { height: 26px; font-size: var(--fs-xs); }
152    &[data-resolved="true"] { border-left-color: var(--border-strong); }
153    &[data-resolved="true"] .c-body { color: var(--ink-dim); }
154  }
155  .dt .fnote td { background: var(--bg-canvas); padding: var(--s2) var(--s3); white-space: normal; }
156  .dt .fnote .cmt, .dt .fnote .cform { margin: 0; }
157  .dt .fnote .cmt + .cmt, .dt .fnote .cmt + .cform { margin-top: var(--s2); }
158  .ln a { color: inherit; text-decoration: none; }
159  .ln a:hover, .ln a:focus-visible { text-decoration: underline; color: var(--ink-hi); }
160
161  /* the forms: a comment, a new merge request */
162  .cform {
163    display: grid; gap: var(--s2); margin-top: var(--s3); max-width: var(--measure);
164    font-family: var(--font); font-size: var(--fs-s);
165    & label { color: var(--ink-dim); font-weight: 600; }
166    & textarea, & input[type="text"] {
167      display: block; width: 100%; font: inherit; font-size: var(--fs); color: inherit; resize: vertical;
168      background: var(--bg-surface); border: 1px solid var(--border-strong); border-radius: var(--r-s); padding: var(--s2) var(--s3);
169    }
170    & textarea::placeholder { color: var(--ink-mute); opacity: 1; }
171    & .f-btns { display: flex; gap: var(--s2); align-items: center; margin-top: var(--s1); }
172  }
173
174  .list.mrs {
175    & .c-n { width: 64px; }
176    & .c-state { width: 110px; }
177    & .c-subject { overflow: hidden; text-overflow: ellipsis; }
178    & .c-who { width: 140px; }
179    & .c-when { width: 100px; }
180    & tr[data-state="open"] .c-state { font-weight: bold; }
181    & tr[data-state="open"] .c-state::before { content: "● "; color: var(--teal); }
182    & tr[data-state="merged"] .c-state::before { content: "✓ "; color: var(--teal); }
183    & tr[data-state="closed"] .c-state::before { content: "✗ "; color: var(--red); }
184  }
185  #mr-notice:empty { display: none; }
186
187  @media (max-width: 700px) {
188    .list.mrs .c-who { display: none; }
189  }
190}
191
192@layer components {
193  .c-body { white-space: normal; overflow-wrap: anywhere; }
194  .c-body.prose > * { margin-block: .5em; }
195  .c-body .ref { font-variant-numeric: tabular-nums; text-decoration: underline; }
196  .cmt > .cmt { margin-inline-start: 2ch; }
197}

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 2

Tags 46

↑↓ moveenter openesc close