@fix-portal/ci-frontend 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/board.css +1079 -0
- package/dist/index.d.ts +98 -0
- package/dist/index.js +889 -0
- package/dist/index.js.map +1 -0
- package/dist/tokens.css +55 -0
- package/package.json +40 -0
package/dist/board.css
ADDED
|
@@ -0,0 +1,1079 @@
|
|
|
1
|
+
/*
|
|
2
|
+
@fixportal/ci-frontend — CI dashboard board styles.
|
|
3
|
+
|
|
4
|
+
The board's component rules, scoped under .ci-embed so its bespoke
|
|
5
|
+
(non-Tailwind) rules and app-local tokens cannot leak into or collide with a
|
|
6
|
+
host application's global styles.
|
|
7
|
+
|
|
8
|
+
TOKEN CONTRACT: this sheet DEFINES its app-local tokens (--surface-2, --run,
|
|
9
|
+
--pkg, --unknown, --no-ci, --r-* radii, the --space-* / --fs-* scales) on
|
|
10
|
+
.ci-page, but CONSUMES ~15 "universal" tokens it does not define (--app-bg,
|
|
11
|
+
--card-bg, --border, --border-strong, --text, --text-muted, --text-faint,
|
|
12
|
+
--brand, --ok-border, --bad-solid, --bad-text, --warn-text, --warn-fill-deep,
|
|
13
|
+
--font-sans, --font-mono). Supply them by importing EITHER:
|
|
14
|
+
- this package's tokens.css (the vendored standalone set), OR
|
|
15
|
+
- your own design system that defines the same custom-property names
|
|
16
|
+
(e.g. the FixPortal simulator's @fixportal/design tokens).
|
|
17
|
+
Import board.css AFTER the token sheet so the cascade resolves.
|
|
18
|
+
|
|
19
|
+
Keyframes are named ci-live-pulse (NOT live-pulse) to avoid colliding with a
|
|
20
|
+
host's global animations. The .site-footer block (outside .ci-embed) styles
|
|
21
|
+
both the bundled DefaultFooter (band + tagline) and a richer footer a consumer
|
|
22
|
+
may pass via CiBoard's footerSlot (the __wordmark / __attrib classes). */
|
|
23
|
+
|
|
24
|
+
/* Page frame: app-local tokens hoisted here so both .ci-embed and .site-footer
|
|
25
|
+
(siblings inside .ci-page) inherit them. The universal tokens come from the
|
|
26
|
+
token sheet described above. */
|
|
27
|
+
.ci-page {
|
|
28
|
+
min-height: 100vh;
|
|
29
|
+
display: flex;
|
|
30
|
+
flex-direction: column;
|
|
31
|
+
|
|
32
|
+
--surface-2: #f9fafb;
|
|
33
|
+
--run: #2563eb;
|
|
34
|
+
--pkg: #7c3aed;
|
|
35
|
+
--unknown: #94a0b3;
|
|
36
|
+
--no-ci: #6b72b0;
|
|
37
|
+
--r-control: 4px;
|
|
38
|
+
--r-chip: 6px;
|
|
39
|
+
--r-panel: 8px;
|
|
40
|
+
|
|
41
|
+
/* Spacing scale (4px base) + type tiers — the shared sheet publishes neither,
|
|
42
|
+
so they live here next to --r-* and every spacing/font declaration reads
|
|
43
|
+
them. Suffix = px ÷ 4; --space-hair is the 0.5 step. */
|
|
44
|
+
--space-hair: 2px;
|
|
45
|
+
--space-1: 4px;
|
|
46
|
+
--space-2: 8px;
|
|
47
|
+
--space-3: 12px;
|
|
48
|
+
--space-4: 16px;
|
|
49
|
+
--space-5: 20px;
|
|
50
|
+
--space-6: 24px;
|
|
51
|
+
--space-8: 32px;
|
|
52
|
+
--space-12: 48px;
|
|
53
|
+
--fs-10: 10px;
|
|
54
|
+
--fs-11: 11px;
|
|
55
|
+
--fs-12: 12px;
|
|
56
|
+
--fs-13: 13px;
|
|
57
|
+
--fs-14: 14px;
|
|
58
|
+
--fs-24: 24px;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
:root[data-theme="dark"] .ci-page {
|
|
62
|
+
--surface-2: #1c2538;
|
|
63
|
+
--run: #60a5fa;
|
|
64
|
+
--pkg: #a78bfa;
|
|
65
|
+
--unknown: #5d6880;
|
|
66
|
+
--no-ci: #7c83b8;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/* Keyframes must be top-level (cannot be nested). Renamed from the dashboard's
|
|
70
|
+
`live-pulse` to avoid any collision with simulator-global animations. */
|
|
71
|
+
@keyframes ci-live-pulse {
|
|
72
|
+
0%, 100% { opacity: 1; }
|
|
73
|
+
50% { opacity: 0.35; }
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
.ci-page .ci-embed {
|
|
77
|
+
flex: 1;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
.ci-embed {
|
|
81
|
+
background: var(--app-bg);
|
|
82
|
+
color: var(--text);
|
|
83
|
+
font-family: var(--font-sans);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
.ci-embed__header {
|
|
87
|
+
position: sticky;
|
|
88
|
+
top: 0;
|
|
89
|
+
z-index: 10;
|
|
90
|
+
display: flex;
|
|
91
|
+
align-items: center;
|
|
92
|
+
gap: var(--space-3);
|
|
93
|
+
padding: var(--space-3) var(--space-6);
|
|
94
|
+
background: var(--card-bg);
|
|
95
|
+
border-bottom: 1px solid var(--border);
|
|
96
|
+
}
|
|
97
|
+
.ci-embed__lockup {
|
|
98
|
+
display: flex;
|
|
99
|
+
align-items: flex-end;
|
|
100
|
+
gap: var(--space-3);
|
|
101
|
+
}
|
|
102
|
+
.ci-embed__wordmark { display: block; color: var(--text); }
|
|
103
|
+
/* Text fallback used when no `logo` node is passed to CiBoard. */
|
|
104
|
+
.ci-embed__wordmark-text {
|
|
105
|
+
font-family: var(--font-sans);
|
|
106
|
+
font-weight: 700;
|
|
107
|
+
font-size: var(--fs-24);
|
|
108
|
+
color: var(--brand);
|
|
109
|
+
}
|
|
110
|
+
.ci-embed__descriptor {
|
|
111
|
+
font-family: var(--font-mono);
|
|
112
|
+
font-size: var(--fs-13);
|
|
113
|
+
letter-spacing: 0.05em;
|
|
114
|
+
color: var(--text-muted);
|
|
115
|
+
padding-bottom: var(--space-2);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
.ci-embed {
|
|
119
|
+
/* Visually-hidden but exposed to assistive tech. Carries the spoken status word
|
|
120
|
+
into each chip's accessible name so state is never colour-only (WCAG 1.4.1). */
|
|
121
|
+
.sr-only {
|
|
122
|
+
position: absolute;
|
|
123
|
+
width: 1px;
|
|
124
|
+
height: 1px;
|
|
125
|
+
padding: 0;
|
|
126
|
+
/* stylelint-disable-next-line declaration-property-value-disallowed-list -- the -1px clip is the canonical visually-hidden idiom, not chrome spacing. */
|
|
127
|
+
margin: -1px;
|
|
128
|
+
overflow: hidden;
|
|
129
|
+
clip: rect(0, 0, 0, 0);
|
|
130
|
+
white-space: nowrap;
|
|
131
|
+
border: 0;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/* ---- Dashboard layout ---------------------------------------------------- */
|
|
135
|
+
|
|
136
|
+
.dashboard-page {
|
|
137
|
+
width: 100%;
|
|
138
|
+
max-width: 1100px;
|
|
139
|
+
margin: 0 auto;
|
|
140
|
+
padding: var(--space-6) var(--space-6) var(--space-12);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
.dashboard__toolbar {
|
|
144
|
+
display: flex;
|
|
145
|
+
align-items: center;
|
|
146
|
+
justify-content: space-between;
|
|
147
|
+
gap: var(--space-3);
|
|
148
|
+
margin-bottom: var(--space-4);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
.dashboard__scope {
|
|
152
|
+
font-size: var(--fs-12);
|
|
153
|
+
color: var(--text-muted);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
.dashboard__refreshed {
|
|
157
|
+
display: inline-flex;
|
|
158
|
+
align-items: center;
|
|
159
|
+
gap: var(--space-2);
|
|
160
|
+
font-family: var(--font-mono);
|
|
161
|
+
font-size: var(--fs-12);
|
|
162
|
+
color: var(--text-muted);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
.live-dot {
|
|
166
|
+
width: 7px;
|
|
167
|
+
height: 7px;
|
|
168
|
+
border-radius: 50%;
|
|
169
|
+
background: var(--ok-border);
|
|
170
|
+
box-shadow: 0 0 0 3px color-mix(in srgb, var(--ok-border) 30%, transparent);
|
|
171
|
+
animation: ci-live-pulse 2.4s ease-in-out infinite;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
@media (prefers-reduced-motion: reduce) {
|
|
175
|
+
.live-dot { animation: none; }
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
.state-msg {
|
|
179
|
+
padding: var(--space-12) var(--space-6);
|
|
180
|
+
text-align: center;
|
|
181
|
+
color: var(--text-muted);
|
|
182
|
+
font-size: var(--fs-14);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
.state-msg--error { color: var(--bad-text); }
|
|
186
|
+
|
|
187
|
+
/* ---- Summary panels ------------------------------------------------------ */
|
|
188
|
+
|
|
189
|
+
.summary-panels {
|
|
190
|
+
display: grid;
|
|
191
|
+
grid-template-columns: 0.9fr 1.3fr 1fr;
|
|
192
|
+
gap: var(--space-3);
|
|
193
|
+
/* Stretch all three panels to a uniform height so the row reads as one band —
|
|
194
|
+
the CI-status panel is tallest (it carries the 24h trend bar), and matching
|
|
195
|
+
Review and Inventory to it keeps their bottom edges aligned. */
|
|
196
|
+
align-items: stretch;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
.summary-panel {
|
|
200
|
+
display: flex;
|
|
201
|
+
flex-direction: column;
|
|
202
|
+
gap: var(--space-3);
|
|
203
|
+
padding: var(--space-4);
|
|
204
|
+
background: var(--card-bg);
|
|
205
|
+
border: 1px solid var(--border);
|
|
206
|
+
border-radius: var(--r-panel);
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/* The review panel is a different kind of thing (work to do, not pipeline health)
|
|
210
|
+
so it reads distinctly — a brand-tinted edge sets it apart from the CI counts. */
|
|
211
|
+
.summary-panel--review {
|
|
212
|
+
border-color: color-mix(in srgb, var(--brand) 30%, var(--border));
|
|
213
|
+
background: color-mix(in srgb, var(--brand) 5%, var(--card-bg));
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
.summary-panel__title {
|
|
217
|
+
font-family: var(--font-mono);
|
|
218
|
+
font-size: var(--fs-11);
|
|
219
|
+
letter-spacing: 0.12em;
|
|
220
|
+
text-transform: uppercase;
|
|
221
|
+
color: var(--text-muted);
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
.summary-panel__items {
|
|
225
|
+
display: flex;
|
|
226
|
+
flex-wrap: wrap;
|
|
227
|
+
gap: var(--space-4) var(--space-6);
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
.summary__item {
|
|
231
|
+
display: flex;
|
|
232
|
+
flex-direction: column;
|
|
233
|
+
gap: var(--space-1);
|
|
234
|
+
min-width: 64px;
|
|
235
|
+
text-decoration: none;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
.summary__item--btn {
|
|
239
|
+
background: none;
|
|
240
|
+
border: none;
|
|
241
|
+
font: inherit;
|
|
242
|
+
text-align: left;
|
|
243
|
+
cursor: pointer;
|
|
244
|
+
}
|
|
245
|
+
.summary__item--btn:hover .summary__label { color: var(--text); }
|
|
246
|
+
.summary__item--btn:focus-visible { outline: 2px solid var(--brand); outline-offset: 3px; border-radius: var(--r-control); }
|
|
247
|
+
.summary__item--btn:disabled { cursor: default; }
|
|
248
|
+
|
|
249
|
+
.summary__count {
|
|
250
|
+
font-family: var(--font-mono);
|
|
251
|
+
font-weight: 600;
|
|
252
|
+
font-size: var(--fs-24);
|
|
253
|
+
line-height: 1;
|
|
254
|
+
color: var(--text-muted);
|
|
255
|
+
font-variant-numeric: tabular-nums;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
.summary__label {
|
|
259
|
+
font-size: var(--fs-11);
|
|
260
|
+
letter-spacing: 0.06em;
|
|
261
|
+
text-transform: uppercase;
|
|
262
|
+
color: var(--text-muted);
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
/* Non-zero counts take their chip's colour; zero / inventory stay quiet. */
|
|
266
|
+
.summary__item[data-tone="alert"] .summary__count { color: var(--warn-text); }
|
|
267
|
+
.summary__item[data-tone="fail"] .summary__count { color: var(--bad-text); }
|
|
268
|
+
.summary__item[data-tone="run"] .summary__count { color: var(--run); }
|
|
269
|
+
.summary__item[data-tone="review"] .summary__count { color: var(--brand); }
|
|
270
|
+
.summary__item[data-tone="no-ci"] .summary__count { color: var(--no-ci); }
|
|
271
|
+
|
|
272
|
+
/* CI status uses a fixed 3-col grid so Running/Deploys-Running and
|
|
273
|
+
Failing/Deploys-Failing align in columns regardless of which items are present. */
|
|
274
|
+
.summary-panel--ci .summary-panel__items {
|
|
275
|
+
display: grid;
|
|
276
|
+
grid-template-columns: repeat(3, max-content);
|
|
277
|
+
gap: var(--space-4) var(--space-6);
|
|
278
|
+
}
|
|
279
|
+
.summary-panel--ci .summary__item[data-key="running"] { grid-column: 1; grid-row: 1; }
|
|
280
|
+
.summary-panel--ci .summary__item[data-key="failing"] { grid-column: 2; grid-row: 1; }
|
|
281
|
+
.summary-panel--ci .summary__item[data-key="packages-failing"] { grid-column: 3; grid-row: 1; }
|
|
282
|
+
.summary-panel--ci .summary__item[data-key="deploys-running"] { grid-column: 1; grid-row: 2; }
|
|
283
|
+
.summary-panel--ci .summary__item[data-key="deploys-failing"] { grid-column: 2; grid-row: 2; }
|
|
284
|
+
.summary-panel--ci .summary__item[data-key="no-ci"] { grid-column: 3; grid-row: 2; }
|
|
285
|
+
|
|
286
|
+
/* ---- Repo boards --------------------------------------------------------- */
|
|
287
|
+
|
|
288
|
+
.repo-list {
|
|
289
|
+
display: flex;
|
|
290
|
+
flex-direction: column;
|
|
291
|
+
gap: var(--space-4);
|
|
292
|
+
margin-top: var(--space-4);
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
.repo-section {
|
|
296
|
+
display: flex;
|
|
297
|
+
align-items: center;
|
|
298
|
+
gap: var(--space-2);
|
|
299
|
+
padding: var(--space-1) var(--space-1) var(--space-2);
|
|
300
|
+
width: 100%;
|
|
301
|
+
background: none;
|
|
302
|
+
border: none;
|
|
303
|
+
border-bottom: 1px solid var(--border);
|
|
304
|
+
cursor: pointer;
|
|
305
|
+
text-align: left;
|
|
306
|
+
}
|
|
307
|
+
.repo-section__chevron {
|
|
308
|
+
font-size: var(--fs-10);
|
|
309
|
+
color: var(--text-muted);
|
|
310
|
+
}
|
|
311
|
+
.repo-section__label {
|
|
312
|
+
font-size: var(--fs-10);
|
|
313
|
+
font-family: var(--font-mono);
|
|
314
|
+
letter-spacing: 0.12em;
|
|
315
|
+
text-transform: uppercase;
|
|
316
|
+
font-weight: 600;
|
|
317
|
+
color: var(--text-muted);
|
|
318
|
+
}
|
|
319
|
+
.repo-section__count {
|
|
320
|
+
font-size: var(--fs-10);
|
|
321
|
+
font-family: var(--font-mono);
|
|
322
|
+
color: var(--text-muted);
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
.repo-board {
|
|
326
|
+
background: var(--card-bg);
|
|
327
|
+
border: 1px solid var(--border);
|
|
328
|
+
border-radius: var(--r-panel);
|
|
329
|
+
overflow: hidden;
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
/* No-CI repos: a repo with zero workflows. The indigo left edge + muted toggle
|
|
333
|
+
name + "No CI" tag mark them apart; an earlier element-level opacity dimmed
|
|
334
|
+
the indigo accent and stacked on the already-muted name, hurting legibility —
|
|
335
|
+
so the recede is carried by the tonal cues, accent + text at full strength. */
|
|
336
|
+
.repo-board--no-ci {
|
|
337
|
+
box-shadow: inset 3px 0 0 var(--no-ci);
|
|
338
|
+
}
|
|
339
|
+
.repo-board--no-ci .repo-board__toggle {
|
|
340
|
+
color: var(--text-muted);
|
|
341
|
+
font-weight: 400;
|
|
342
|
+
}
|
|
343
|
+
.repo-board__noci-tag {
|
|
344
|
+
font-family: var(--font-mono);
|
|
345
|
+
font-size: var(--fs-11);
|
|
346
|
+
letter-spacing: 0.06em;
|
|
347
|
+
text-transform: uppercase;
|
|
348
|
+
color: var(--no-ci);
|
|
349
|
+
background: color-mix(in srgb, var(--no-ci) 16%, transparent);
|
|
350
|
+
border: 1px solid var(--no-ci);
|
|
351
|
+
border-radius: var(--r-control);
|
|
352
|
+
padding: var(--space-hair) var(--space-2);
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
.repo-board > header {
|
|
356
|
+
display: flex;
|
|
357
|
+
align-items: center;
|
|
358
|
+
justify-content: space-between;
|
|
359
|
+
gap: var(--space-3);
|
|
360
|
+
padding: var(--space-4);
|
|
361
|
+
border-bottom: 1px solid var(--border);
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
.repo-board__toggle {
|
|
365
|
+
margin: 0;
|
|
366
|
+
display: inline-flex;
|
|
367
|
+
align-items: center;
|
|
368
|
+
gap: var(--space-2);
|
|
369
|
+
font-family: var(--font-sans);
|
|
370
|
+
font-size: var(--fs-14);
|
|
371
|
+
font-weight: 600;
|
|
372
|
+
letter-spacing: 0.01em;
|
|
373
|
+
color: var(--text);
|
|
374
|
+
background: none;
|
|
375
|
+
border: none;
|
|
376
|
+
padding: 0;
|
|
377
|
+
cursor: pointer;
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
.repo-board__chev {
|
|
381
|
+
color: var(--text-muted);
|
|
382
|
+
font-size: var(--fs-12);
|
|
383
|
+
transition: transform 0.15s ease;
|
|
384
|
+
}
|
|
385
|
+
.repo-board:not(.repo-board--collapsed) .repo-board__chev { transform: rotate(90deg); }
|
|
386
|
+
|
|
387
|
+
.repo-board__gh-link {
|
|
388
|
+
font-family: var(--font-mono);
|
|
389
|
+
font-size: var(--fs-11);
|
|
390
|
+
letter-spacing: 0.04em;
|
|
391
|
+
color: var(--text-muted);
|
|
392
|
+
text-decoration: none;
|
|
393
|
+
white-space: nowrap;
|
|
394
|
+
flex-shrink: 0;
|
|
395
|
+
}
|
|
396
|
+
.repo-board__gh-link:hover { color: var(--text); text-decoration: underline; }
|
|
397
|
+
|
|
398
|
+
.dashboard__toolbar-right {
|
|
399
|
+
display: inline-flex;
|
|
400
|
+
align-items: center;
|
|
401
|
+
gap: var(--space-4);
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
.dashboard__collapse-all,
|
|
405
|
+
.dashboard__hide-noci {
|
|
406
|
+
font-family: var(--font-mono);
|
|
407
|
+
font-size: var(--fs-11);
|
|
408
|
+
letter-spacing: 0.06em;
|
|
409
|
+
text-transform: uppercase;
|
|
410
|
+
color: var(--text-muted);
|
|
411
|
+
background: var(--card-bg);
|
|
412
|
+
border: 1px solid var(--border);
|
|
413
|
+
border-radius: var(--r-chip);
|
|
414
|
+
padding: var(--space-2) var(--space-3);
|
|
415
|
+
cursor: pointer;
|
|
416
|
+
white-space: nowrap;
|
|
417
|
+
}
|
|
418
|
+
.dashboard__collapse-all:hover,
|
|
419
|
+
.dashboard__hide-noci:hover { border-color: var(--border-strong); }
|
|
420
|
+
.dashboard__hide-noci--on {
|
|
421
|
+
color: var(--no-ci);
|
|
422
|
+
border-color: var(--no-ci);
|
|
423
|
+
background: color-mix(in srgb, var(--no-ci) 16%, transparent);
|
|
424
|
+
}
|
|
425
|
+
.dashboard__hide-noci--on:hover { border-color: var(--no-ci); }
|
|
426
|
+
|
|
427
|
+
/* On narrow widths the name + metrics line stop competing for one row and stack. */
|
|
428
|
+
@media (max-width: 720px) {
|
|
429
|
+
.repo-board > header {
|
|
430
|
+
flex-direction: column;
|
|
431
|
+
align-items: flex-start;
|
|
432
|
+
gap: var(--space-2);
|
|
433
|
+
}
|
|
434
|
+
.repo-metrics { gap: var(--space-3); }
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
.repo-top-signals {
|
|
438
|
+
display: flex;
|
|
439
|
+
gap: var(--space-2);
|
|
440
|
+
flex-wrap: wrap;
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
/* Labelled workflow row — the count grounds the top-line "Workflows" total in a
|
|
444
|
+
visible per-repo number, and mirrors the Deploys row's label treatment. */
|
|
445
|
+
.repo-workflows {
|
|
446
|
+
display: flex;
|
|
447
|
+
align-items: baseline;
|
|
448
|
+
gap: var(--space-3);
|
|
449
|
+
flex-wrap: wrap;
|
|
450
|
+
padding: var(--space-4);
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
.repo-workflows__label {
|
|
454
|
+
font-family: var(--font-mono);
|
|
455
|
+
font-size: var(--fs-11);
|
|
456
|
+
letter-spacing: 0.06em;
|
|
457
|
+
text-transform: uppercase;
|
|
458
|
+
color: var(--text-muted);
|
|
459
|
+
white-space: nowrap;
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
/* ---- Signal chip (the signature element) --------------------------------- */
|
|
463
|
+
|
|
464
|
+
.chip {
|
|
465
|
+
display: inline-flex;
|
|
466
|
+
align-items: center;
|
|
467
|
+
gap: var(--space-2);
|
|
468
|
+
padding: var(--space-1) var(--space-2);
|
|
469
|
+
border: 1px solid var(--border);
|
|
470
|
+
border-radius: var(--r-chip);
|
|
471
|
+
background: var(--surface-2);
|
|
472
|
+
color: var(--text-muted);
|
|
473
|
+
text-decoration: none;
|
|
474
|
+
font-size: var(--fs-12);
|
|
475
|
+
line-height: 1;
|
|
476
|
+
transition: border-color 0.12s ease, background 0.12s ease, transform 0.12s ease;
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
.chip:hover {
|
|
480
|
+
border-color: var(--border-strong);
|
|
481
|
+
transform: translateY(-1px);
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
.chip:focus-visible {
|
|
485
|
+
outline: 2px solid var(--brand);
|
|
486
|
+
outline-offset: 2px;
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
/* Non-interactive chip — a workflow with no run, or a private repo whose run URL
|
|
490
|
+
would 404 for the anonymous audience. Present the status without inviting a
|
|
491
|
+
click: no pointer cursor, no hover lift. */
|
|
492
|
+
.chip--static { cursor: default; }
|
|
493
|
+
.chip--static:hover { border-color: var(--border); transform: none; }
|
|
494
|
+
|
|
495
|
+
.chip__dot {
|
|
496
|
+
flex: none;
|
|
497
|
+
width: 7px;
|
|
498
|
+
height: 7px;
|
|
499
|
+
border-radius: 50%;
|
|
500
|
+
background: var(--unknown);
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
.chip__label {
|
|
504
|
+
font-weight: 600;
|
|
505
|
+
color: var(--text);
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
.chip__meta {
|
|
509
|
+
font-family: var(--font-mono);
|
|
510
|
+
font-size: var(--fs-11);
|
|
511
|
+
color: var(--text-muted);
|
|
512
|
+
font-variant-numeric: tabular-nums;
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
/* Unknown reads as absence, never as alarm: hollow dot, dashed, dimmed. */
|
|
516
|
+
.chip--unknown {
|
|
517
|
+
background: transparent;
|
|
518
|
+
border-style: dashed;
|
|
519
|
+
opacity: 0.72;
|
|
520
|
+
}
|
|
521
|
+
.chip--unknown .chip__dot {
|
|
522
|
+
width: 8px;
|
|
523
|
+
height: 8px;
|
|
524
|
+
background: transparent;
|
|
525
|
+
border: 1.5px solid var(--unknown);
|
|
526
|
+
}
|
|
527
|
+
.chip--unknown .chip__label {
|
|
528
|
+
color: var(--text-muted);
|
|
529
|
+
font-weight: 500;
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
/* Failure/warn SIGNALS use --bad-solid / --warn-fill-deep, not the -border step:
|
|
533
|
+
--bad-border resolves to a dark maroon in dark mode (it's a badge-border tint)
|
|
534
|
+
and --warn-border goes fuchsia — both wrong for a vivid status dot/fill on the
|
|
535
|
+
dark ops surface. Coloured-text counts keep the AA-safe --bad-text/--warn-text. */
|
|
536
|
+
|
|
537
|
+
/* Org-wide workflow states. success/failure mirror the ok/bad treatments;
|
|
538
|
+
running pulses on the brand colour to read as in-flight, not settled. */
|
|
539
|
+
.chip--success {
|
|
540
|
+
background: color-mix(in srgb, var(--ok-border) 7%, var(--surface-2));
|
|
541
|
+
}
|
|
542
|
+
.chip--success .chip__dot {
|
|
543
|
+
background: var(--ok-border);
|
|
544
|
+
box-shadow: 0 0 0 3px color-mix(in srgb, var(--ok-border) 30%, transparent);
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
.chip--failure {
|
|
548
|
+
background: color-mix(in srgb, var(--bad-solid) 9%, var(--surface-2));
|
|
549
|
+
border-color: color-mix(in srgb, var(--bad-solid) 32%, var(--border));
|
|
550
|
+
}
|
|
551
|
+
/* Failure is the one state that must survive grayscale / red-green CVD: a square
|
|
552
|
+
LED reads distinctly from the circular passing/running dots without relying on
|
|
553
|
+
the red. 2px is a deliberate data-viz micro-radius (cf. the .ci-weather block),
|
|
554
|
+
exempt from the 4/6/8 chrome radius scale. */
|
|
555
|
+
.chip--failure .chip__dot {
|
|
556
|
+
background: var(--bad-solid);
|
|
557
|
+
border-radius: 2px;
|
|
558
|
+
box-shadow: 0 0 0 3px color-mix(in srgb, var(--bad-solid) 30%, transparent);
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
.chip--running {
|
|
562
|
+
background: color-mix(in srgb, var(--run) 12%, var(--surface-2));
|
|
563
|
+
border-color: color-mix(in srgb, var(--run) 45%, var(--border));
|
|
564
|
+
}
|
|
565
|
+
.chip--running .chip__label { color: var(--run); }
|
|
566
|
+
.chip--running .chip__dot {
|
|
567
|
+
background: var(--run);
|
|
568
|
+
box-shadow: 0 0 0 3px color-mix(in srgb, var(--run) 30%, transparent);
|
|
569
|
+
animation: ci-live-pulse 1.6s ease-in-out infinite;
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
@media (prefers-reduced-motion: reduce) {
|
|
573
|
+
.chip--running .chip__dot { animation: none; }
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
/* Repo with no workflows: quiet, dashed, matching the "unknown" absence cue. */
|
|
577
|
+
.repo-board__empty {
|
|
578
|
+
padding: var(--space-4);
|
|
579
|
+
font-family: var(--font-mono);
|
|
580
|
+
font-size: var(--fs-12);
|
|
581
|
+
color: var(--text-muted);
|
|
582
|
+
border-top: 1px dashed var(--border);
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
/* ---- Repo metrics line (in the card header) ------------------------------ */
|
|
586
|
+
.repo-metrics {
|
|
587
|
+
display: flex;
|
|
588
|
+
gap: var(--space-4);
|
|
589
|
+
margin: 0;
|
|
590
|
+
flex-wrap: wrap;
|
|
591
|
+
font-family: var(--font-mono);
|
|
592
|
+
font-size: var(--fs-12);
|
|
593
|
+
color: var(--text-muted);
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
.repo-metrics div {
|
|
597
|
+
display: flex;
|
|
598
|
+
gap: var(--space-2);
|
|
599
|
+
align-items: baseline;
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
.repo-metrics dt {
|
|
603
|
+
letter-spacing: 0.06em;
|
|
604
|
+
text-transform: uppercase;
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
.repo-metrics dd {
|
|
608
|
+
margin: 0;
|
|
609
|
+
color: var(--text-muted);
|
|
610
|
+
font-variant-numeric: tabular-nums;
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
/* Functions above the complexity threshold draw the eye, like a warning chip. */
|
|
614
|
+
.repo-metrics__complex dd { color: var(--warn-text); }
|
|
615
|
+
|
|
616
|
+
/* ---- Open PRs (below the signals) ---------------------------------------- */
|
|
617
|
+
.repo-prs {
|
|
618
|
+
padding: var(--space-3) var(--space-4);
|
|
619
|
+
border-top: 1px solid var(--border);
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
.repo-prs__count {
|
|
623
|
+
font-family: var(--font-mono);
|
|
624
|
+
font-size: var(--fs-11);
|
|
625
|
+
letter-spacing: 0.06em;
|
|
626
|
+
text-transform: uppercase;
|
|
627
|
+
color: var(--text-muted);
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
.repo-prs ul {
|
|
631
|
+
list-style: none;
|
|
632
|
+
margin: var(--space-2) 0 0;
|
|
633
|
+
padding: 0;
|
|
634
|
+
display: flex;
|
|
635
|
+
flex-direction: column;
|
|
636
|
+
gap: var(--space-2);
|
|
637
|
+
}
|
|
638
|
+
|
|
639
|
+
.repo-prs__item a {
|
|
640
|
+
display: inline-flex;
|
|
641
|
+
gap: var(--space-2);
|
|
642
|
+
text-decoration: none;
|
|
643
|
+
color: var(--text);
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
.repo-prs__item a:hover .repo-prs__title { text-decoration: underline; }
|
|
647
|
+
|
|
648
|
+
.repo-prs__num {
|
|
649
|
+
font-family: var(--font-mono);
|
|
650
|
+
color: var(--text-muted);
|
|
651
|
+
font-variant-numeric: tabular-nums;
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
.repo-prs__meta {
|
|
655
|
+
display: block;
|
|
656
|
+
font-size: var(--fs-11);
|
|
657
|
+
color: var(--text-muted);
|
|
658
|
+
margin-top: var(--space-hair);
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
/* Drafts read as not-yet-live: dimmed title. */
|
|
662
|
+
.repo-prs__item--draft .repo-prs__title { color: var(--text-muted); }
|
|
663
|
+
|
|
664
|
+
/* ---- Job-lane rows (deploys, packages) ----------------------------------- */
|
|
665
|
+
.repo-joblane {
|
|
666
|
+
display: flex;
|
|
667
|
+
align-items: center;
|
|
668
|
+
gap: var(--space-3);
|
|
669
|
+
flex-wrap: wrap;
|
|
670
|
+
padding: var(--space-3) var(--space-4);
|
|
671
|
+
border-top: 1px solid var(--border);
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
.repo-joblane__label {
|
|
675
|
+
font-family: var(--font-mono);
|
|
676
|
+
font-size: var(--fs-11);
|
|
677
|
+
letter-spacing: 0.06em;
|
|
678
|
+
text-transform: uppercase;
|
|
679
|
+
white-space: nowrap;
|
|
680
|
+
}
|
|
681
|
+
|
|
682
|
+
/* Each lane gets its own accent so deploys and packages are told apart at a
|
|
683
|
+
glance; the chips keep their state colours. */
|
|
684
|
+
.repo-joblane--deploys .repo-joblane__label { color: var(--brand); }
|
|
685
|
+
.repo-joblane--packages .repo-joblane__label { color: var(--pkg); }
|
|
686
|
+
|
|
687
|
+
.repo-joblane__chips {
|
|
688
|
+
display: flex;
|
|
689
|
+
gap: var(--space-2);
|
|
690
|
+
flex-wrap: wrap;
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
/* Lane chips reuse the state colours but stay solid (never dashed) so they read
|
|
694
|
+
as first-class signals next to the workflow chips. */
|
|
695
|
+
.chip--joblane { border-style: solid; }
|
|
696
|
+
|
|
697
|
+
/* ---- Status legend (board footer, decodes the dot colour language) ------- */
|
|
698
|
+
.status-legend {
|
|
699
|
+
margin-top: var(--space-6);
|
|
700
|
+
padding: var(--space-3) var(--space-4);
|
|
701
|
+
border: 1px dashed var(--border);
|
|
702
|
+
border-radius: var(--r-panel);
|
|
703
|
+
display: flex;
|
|
704
|
+
flex-wrap: wrap;
|
|
705
|
+
align-items: center;
|
|
706
|
+
gap: var(--space-2) var(--space-4);
|
|
707
|
+
font-family: var(--font-mono);
|
|
708
|
+
font-size: var(--fs-11);
|
|
709
|
+
color: var(--text-muted);
|
|
710
|
+
}
|
|
711
|
+
.status-legend__title {
|
|
712
|
+
letter-spacing: 0.08em;
|
|
713
|
+
text-transform: uppercase;
|
|
714
|
+
color: var(--text-muted);
|
|
715
|
+
}
|
|
716
|
+
.status-legend__item {
|
|
717
|
+
display: inline-flex;
|
|
718
|
+
align-items: center;
|
|
719
|
+
gap: var(--space-2);
|
|
720
|
+
}
|
|
721
|
+
|
|
722
|
+
/* Swatches mirror the live dots: same colours, same square-failure shape cue.
|
|
723
|
+
2px is the data-viz micro-radius exception (cf. .ci-weather / failure dot). */
|
|
724
|
+
.status-legend__dot {
|
|
725
|
+
flex: none;
|
|
726
|
+
width: 8px;
|
|
727
|
+
height: 8px;
|
|
728
|
+
border-radius: 50%;
|
|
729
|
+
background: var(--unknown);
|
|
730
|
+
}
|
|
731
|
+
.status-legend__dot[data-state="success"] {
|
|
732
|
+
background: var(--ok-border);
|
|
733
|
+
box-shadow: 0 0 0 3px color-mix(in srgb, var(--ok-border) 30%, transparent);
|
|
734
|
+
}
|
|
735
|
+
.status-legend__dot[data-state="failure"] {
|
|
736
|
+
background: var(--bad-solid);
|
|
737
|
+
border-radius: 2px;
|
|
738
|
+
box-shadow: 0 0 0 3px color-mix(in srgb, var(--bad-solid) 30%, transparent);
|
|
739
|
+
}
|
|
740
|
+
.status-legend__dot[data-state="running"] {
|
|
741
|
+
background: var(--run);
|
|
742
|
+
box-shadow: 0 0 0 3px color-mix(in srgb, var(--run) 30%, transparent);
|
|
743
|
+
}
|
|
744
|
+
.status-legend__dot[data-state="unknown"] {
|
|
745
|
+
background: transparent;
|
|
746
|
+
border: 1.5px dashed var(--unknown);
|
|
747
|
+
}
|
|
748
|
+
/* No-CI repos are marked by an indigo left edge on the card; the swatch shows it. */
|
|
749
|
+
.status-legend__noci {
|
|
750
|
+
flex: none;
|
|
751
|
+
width: 18px;
|
|
752
|
+
height: 12px;
|
|
753
|
+
border: 1px solid var(--border);
|
|
754
|
+
border-radius: 2px;
|
|
755
|
+
box-shadow: inset 3px 0 0 var(--no-ci);
|
|
756
|
+
background: var(--card-bg);
|
|
757
|
+
}
|
|
758
|
+
.status-legend__gloss b {
|
|
759
|
+
color: var(--text-muted);
|
|
760
|
+
font-weight: 600;
|
|
761
|
+
}
|
|
762
|
+
.status-legend + .metrics-legend {
|
|
763
|
+
margin-top: var(--space-2);
|
|
764
|
+
}
|
|
765
|
+
|
|
766
|
+
/* ---- Metrics legend (board footer) --------------------------------------- */
|
|
767
|
+
.metrics-legend {
|
|
768
|
+
margin-top: var(--space-6);
|
|
769
|
+
padding: var(--space-3) var(--space-4);
|
|
770
|
+
border: 1px dashed var(--border);
|
|
771
|
+
border-radius: var(--r-panel);
|
|
772
|
+
display: flex;
|
|
773
|
+
flex-wrap: wrap;
|
|
774
|
+
align-items: baseline;
|
|
775
|
+
gap: var(--space-2) var(--space-4);
|
|
776
|
+
font-family: var(--font-mono);
|
|
777
|
+
font-size: var(--fs-11);
|
|
778
|
+
color: var(--text-muted);
|
|
779
|
+
}
|
|
780
|
+
|
|
781
|
+
.metrics-legend__title {
|
|
782
|
+
letter-spacing: 0.08em;
|
|
783
|
+
text-transform: uppercase;
|
|
784
|
+
color: var(--text-muted);
|
|
785
|
+
}
|
|
786
|
+
|
|
787
|
+
.metrics-legend__item b {
|
|
788
|
+
color: var(--text-muted);
|
|
789
|
+
font-weight: 600;
|
|
790
|
+
}
|
|
791
|
+
|
|
792
|
+
/* ---- Per-repo header activity indicator ---------------------------------- */
|
|
793
|
+
.repo-activity {
|
|
794
|
+
margin-left: auto;
|
|
795
|
+
display: inline-flex;
|
|
796
|
+
align-items: center;
|
|
797
|
+
gap: var(--space-3);
|
|
798
|
+
}
|
|
799
|
+
|
|
800
|
+
.repo-activity__pr {
|
|
801
|
+
font-family: var(--font-mono);
|
|
802
|
+
font-size: var(--fs-11);
|
|
803
|
+
letter-spacing: 0.04em;
|
|
804
|
+
color: var(--brand);
|
|
805
|
+
background: color-mix(in srgb, var(--brand) 12%, transparent);
|
|
806
|
+
border: 1px solid color-mix(in srgb, var(--brand) 30%, var(--border));
|
|
807
|
+
border-radius: var(--r-control);
|
|
808
|
+
padding: var(--space-hair) var(--space-2);
|
|
809
|
+
}
|
|
810
|
+
|
|
811
|
+
.repo-activity__sig {
|
|
812
|
+
display: inline-flex;
|
|
813
|
+
align-items: center;
|
|
814
|
+
gap: var(--space-1);
|
|
815
|
+
font-family: var(--font-mono);
|
|
816
|
+
font-size: var(--fs-10);
|
|
817
|
+
letter-spacing: 0.08em;
|
|
818
|
+
color: var(--text-muted);
|
|
819
|
+
}
|
|
820
|
+
|
|
821
|
+
.repo-activity__dot {
|
|
822
|
+
width: 8px;
|
|
823
|
+
height: 8px;
|
|
824
|
+
border-radius: 50%;
|
|
825
|
+
background: transparent;
|
|
826
|
+
border: 1.5px solid var(--unknown);
|
|
827
|
+
}
|
|
828
|
+
.repo-activity__dot[data-activity="success"] { background: var(--ok-border); border-color: var(--ok-border); box-shadow: 0 0 0 3px color-mix(in srgb, var(--ok-border) 30%, transparent); }
|
|
829
|
+
.repo-activity__dot[data-activity="running"] { background: var(--run); border-color: var(--run); box-shadow: 0 0 0 3px color-mix(in srgb, var(--run) 30%, transparent); }
|
|
830
|
+
/* Square failure LED — the same non-colour shape cue as the failure chip dot. */
|
|
831
|
+
.repo-activity__dot[data-activity="failure"] { background: var(--bad-solid); border-color: var(--bad-solid); border-radius: 2px; box-shadow: 0 0 0 3px color-mix(in srgb, var(--bad-solid) 30%, transparent); }
|
|
832
|
+
|
|
833
|
+
/* ---- Responsive: phones + touch ------------------------------------------ */
|
|
834
|
+
|
|
835
|
+
/* Summary panels go full-width and stack rather than squeezing side by side. */
|
|
836
|
+
@media (max-width: 560px) {
|
|
837
|
+
.summary-panels { grid-template-columns: 1fr; }
|
|
838
|
+
}
|
|
839
|
+
|
|
840
|
+
/* Comfortable tap targets on touch pointers. */
|
|
841
|
+
@media (pointer: coarse) {
|
|
842
|
+
.chip { padding: var(--space-2) var(--space-3); }
|
|
843
|
+
.summary__item--btn { min-height: 44px; }
|
|
844
|
+
}
|
|
845
|
+
|
|
846
|
+
/* ---- PR stepper modal ---------------------------------------------------- */
|
|
847
|
+
/* Native <dialog> handles modality, centering and the top layer; .pr-modal is
|
|
848
|
+
now the card itself (formerly .pr-modal__panel), and ::backdrop replaces the
|
|
849
|
+
old .pr-modal__backdrop element. */
|
|
850
|
+
.pr-modal {
|
|
851
|
+
width: min(440px, calc(100vw - 32px));
|
|
852
|
+
background: var(--card-bg);
|
|
853
|
+
border: 1px solid var(--border-strong);
|
|
854
|
+
border-radius: var(--r-panel);
|
|
855
|
+
box-shadow: 0 24px 60px rgba(0, 0, 0, 0.55);
|
|
856
|
+
padding: var(--space-4);
|
|
857
|
+
}
|
|
858
|
+
.pr-modal:focus { outline: none; }
|
|
859
|
+
.pr-modal::backdrop { background: rgba(3, 6, 12, 0.62); }
|
|
860
|
+
.pr-modal__top { display: flex; align-items: center; gap: var(--space-3); margin-bottom: var(--space-4); }
|
|
861
|
+
.pr-modal__title { font-size: var(--fs-13); font-weight: 600; }
|
|
862
|
+
.pr-modal__counter { font-family: var(--font-mono); font-size: var(--fs-12); color: var(--text-muted); }
|
|
863
|
+
.pr-modal__x {
|
|
864
|
+
margin-left: auto; width: 26px; height: 26px; border-radius: var(--r-chip);
|
|
865
|
+
border: 1px solid var(--border); background: var(--surface-2); color: var(--text-muted); cursor: pointer;
|
|
866
|
+
}
|
|
867
|
+
.pr-card { border: 1px solid var(--border); border-left: 3px solid var(--unknown); border-radius: var(--r-chip); padding: var(--space-4); background: var(--surface-2); }
|
|
868
|
+
.pr-card--amber { border-left-color: var(--warn-fill-deep); }
|
|
869
|
+
.pr-card--red { border-left-color: var(--bad-solid); }
|
|
870
|
+
.pr-card--draft { opacity: 0.72; }
|
|
871
|
+
.pr-card__head { display: flex; align-items: center; gap: var(--space-2); font-family: var(--font-mono); font-size: var(--fs-12); color: var(--text-muted); }
|
|
872
|
+
.pr-card__repo { color: var(--text-muted); }
|
|
873
|
+
.pr-card__meta { margin-left: auto; }
|
|
874
|
+
.pr-card__title { margin: var(--space-3) 0 var(--space-2); font-size: var(--fs-14); font-weight: 600; line-height: 1.3; color: var(--text); }
|
|
875
|
+
.pr-card__foot { display: flex; align-items: center; justify-content: space-between; }
|
|
876
|
+
.pr-card__author { font-family: var(--font-mono); font-size: var(--fs-12); color: var(--text-muted); }
|
|
877
|
+
.pr-card__gh { font-size: var(--fs-12); color: var(--brand); border: 1px solid color-mix(in srgb, var(--brand) 40%, var(--border)); border-radius: var(--r-chip); padding: var(--space-1) var(--space-2); text-decoration: none; }
|
|
878
|
+
.pr-modal__nav { display: flex; align-items: center; justify-content: space-between; margin-top: var(--space-4); }
|
|
879
|
+
.pr-modal__nav button {
|
|
880
|
+
font-family: var(--font-sans); font-size: var(--fs-12); color: var(--text);
|
|
881
|
+
background: var(--surface-2); border: 1px solid var(--border); border-radius: var(--r-chip); padding: var(--space-2) var(--space-4); cursor: pointer;
|
|
882
|
+
}
|
|
883
|
+
.pr-modal__nav button:disabled { opacity: 0.4; cursor: default; }
|
|
884
|
+
.pr-modal__nav button:not(:disabled):hover { border-color: var(--border-strong); }
|
|
885
|
+
|
|
886
|
+
.summary-panel__merged,
|
|
887
|
+
.summary-panel__next {
|
|
888
|
+
display: block;
|
|
889
|
+
margin-top: var(--space-3);
|
|
890
|
+
padding-top: var(--space-2);
|
|
891
|
+
border-top: 1px solid var(--border);
|
|
892
|
+
font-family: var(--font-mono);
|
|
893
|
+
font-size: var(--fs-11);
|
|
894
|
+
line-height: 1.5;
|
|
895
|
+
color: var(--text-muted);
|
|
896
|
+
text-decoration: none;
|
|
897
|
+
}
|
|
898
|
+
.summary-panel__q-lab { display: block; margin-bottom: var(--space-1); letter-spacing: 0.08em; text-transform: uppercase; color: var(--text-muted); }
|
|
899
|
+
/* The "(2m ago)" age rides the uppercased label but stays sentence-case itself. */
|
|
900
|
+
.summary-panel__q-age { text-transform: none; letter-spacing: 0; }
|
|
901
|
+
.summary-panel__q-body { color: var(--text-muted); }
|
|
902
|
+
.summary-panel__q-title { display: block; color: var(--text-muted); }
|
|
903
|
+
.summary-panel__merged:hover .summary-panel__q-title { text-decoration: underline; }
|
|
904
|
+
.summary-panel__trend {
|
|
905
|
+
margin-top: var(--space-3);
|
|
906
|
+
padding-top: var(--space-2);
|
|
907
|
+
border-top: 1px solid var(--border);
|
|
908
|
+
}
|
|
909
|
+
.ci-weather {
|
|
910
|
+
display: flex;
|
|
911
|
+
gap: var(--space-hair);
|
|
912
|
+
height: 26px;
|
|
913
|
+
}
|
|
914
|
+
/* 2px: deliberate data-viz micro-radius (sparkline-style blocks), exempt from the chrome 4/6/8 scale — cf. SessionCard square-corner exception. */
|
|
915
|
+
.ci-weather__block {
|
|
916
|
+
flex: 1;
|
|
917
|
+
border-radius: 2px;
|
|
918
|
+
background: var(--unknown);
|
|
919
|
+
}
|
|
920
|
+
.ci-weather__block[data-state="passing"] { background: var(--ok-border); }
|
|
921
|
+
.ci-weather__block[data-state="failing"] { background: var(--bad-solid); }
|
|
922
|
+
.ci-weather__block[data-state="noData"] { background: color-mix(in srgb, var(--unknown) 35%, transparent); }
|
|
923
|
+
.ci-weather__readout {
|
|
924
|
+
display: block;
|
|
925
|
+
margin-top: var(--space-1);
|
|
926
|
+
font-family: var(--font-mono);
|
|
927
|
+
font-size: var(--fs-11);
|
|
928
|
+
color: var(--text-muted);
|
|
929
|
+
font-variant-numeric: tabular-nums;
|
|
930
|
+
}
|
|
931
|
+
/* Label row wraps the trend label + info button. position:relative anchors
|
|
932
|
+
the absolute popover; margin-top replaces the former display:block margin
|
|
933
|
+
on the label itself. */
|
|
934
|
+
.summary-panel__trend-label-row {
|
|
935
|
+
position: relative;
|
|
936
|
+
display: flex;
|
|
937
|
+
align-items: center;
|
|
938
|
+
gap: var(--space-2);
|
|
939
|
+
margin-top: var(--space-1);
|
|
940
|
+
}
|
|
941
|
+
.summary-panel__trend-label-row .summary-panel__trend-lab {
|
|
942
|
+
display: inline;
|
|
943
|
+
margin-top: 0;
|
|
944
|
+
}
|
|
945
|
+
.summary-panel__trend-lab {
|
|
946
|
+
display: block;
|
|
947
|
+
margin-top: var(--space-1);
|
|
948
|
+
font-family: var(--font-mono);
|
|
949
|
+
font-size: var(--fs-11);
|
|
950
|
+
letter-spacing: 0.08em;
|
|
951
|
+
text-transform: uppercase;
|
|
952
|
+
color: var(--text-muted);
|
|
953
|
+
}
|
|
954
|
+
|
|
955
|
+
.ci-trend-info-btn {
|
|
956
|
+
width: 14px;
|
|
957
|
+
height: 14px;
|
|
958
|
+
flex: none;
|
|
959
|
+
border-radius: 50%;
|
|
960
|
+
border: 1px solid var(--border-strong);
|
|
961
|
+
background: none;
|
|
962
|
+
color: var(--text-muted);
|
|
963
|
+
font-family: Georgia, serif;
|
|
964
|
+
font-style: italic;
|
|
965
|
+
font-size: var(--fs-10);
|
|
966
|
+
line-height: 1;
|
|
967
|
+
cursor: pointer;
|
|
968
|
+
display: inline-flex;
|
|
969
|
+
align-items: center;
|
|
970
|
+
justify-content: center;
|
|
971
|
+
padding: 0;
|
|
972
|
+
transition: border-color 0.12s ease, color 0.12s ease;
|
|
973
|
+
}
|
|
974
|
+
.ci-trend-info-btn:hover,
|
|
975
|
+
.ci-trend-info-btn[aria-expanded="true"] {
|
|
976
|
+
border-color: var(--brand);
|
|
977
|
+
color: var(--brand);
|
|
978
|
+
}
|
|
979
|
+
.ci-trend-info-btn:focus-visible {
|
|
980
|
+
outline: 2px solid var(--brand);
|
|
981
|
+
outline-offset: 2px;
|
|
982
|
+
}
|
|
983
|
+
|
|
984
|
+
.ci-trend-popover {
|
|
985
|
+
position: absolute;
|
|
986
|
+
bottom: calc(100% + 8px);
|
|
987
|
+
left: 0;
|
|
988
|
+
width: 260px;
|
|
989
|
+
background: var(--card-bg);
|
|
990
|
+
border: 1px solid color-mix(in srgb, var(--brand) 60%, var(--border));
|
|
991
|
+
border-radius: var(--r-panel);
|
|
992
|
+
padding: var(--space-3);
|
|
993
|
+
font-size: var(--fs-11);
|
|
994
|
+
color: var(--text-muted);
|
|
995
|
+
line-height: 1.55;
|
|
996
|
+
z-index: 20;
|
|
997
|
+
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.45);
|
|
998
|
+
}
|
|
999
|
+
.ci-trend-popover p {
|
|
1000
|
+
margin: 0 0 var(--space-2);
|
|
1001
|
+
}
|
|
1002
|
+
.ci-trend-popover p:last-of-type {
|
|
1003
|
+
margin-bottom: 0;
|
|
1004
|
+
}
|
|
1005
|
+
.ci-trend-popover__swatch--fail { color: var(--bad-solid); }
|
|
1006
|
+
.ci-trend-popover__swatch--pass { color: var(--ok-border); }
|
|
1007
|
+
.ci-trend-popover__title {
|
|
1008
|
+
font-size: var(--fs-10);
|
|
1009
|
+
letter-spacing: 0.1em;
|
|
1010
|
+
text-transform: uppercase;
|
|
1011
|
+
color: var(--brand);
|
|
1012
|
+
font-weight: 600;
|
|
1013
|
+
margin-bottom: var(--space-2);
|
|
1014
|
+
}
|
|
1015
|
+
.ci-trend-popover__caret {
|
|
1016
|
+
position: absolute;
|
|
1017
|
+
bottom: -5px;
|
|
1018
|
+
left: var(--space-5);
|
|
1019
|
+
width: 8px;
|
|
1020
|
+
height: 8px;
|
|
1021
|
+
background: var(--card-bg);
|
|
1022
|
+
border-right: 1px solid color-mix(in srgb, var(--brand) 60%, var(--border));
|
|
1023
|
+
border-bottom: 1px solid color-mix(in srgb, var(--brand) 60%, var(--border));
|
|
1024
|
+
transform: rotate(45deg);
|
|
1025
|
+
}
|
|
1026
|
+
}
|
|
1027
|
+
|
|
1028
|
+
/* ---- Site footer (brand band + attribution) --------------------------------
|
|
1029
|
+
Rendered by SiteFooter outside .ci-embed in CiBoard.tsx. Mirrored from
|
|
1030
|
+
fixportal-ci-dashboard-ui/src/index.css. */
|
|
1031
|
+
.site-footer {
|
|
1032
|
+
margin-top: auto;
|
|
1033
|
+
display: flex;
|
|
1034
|
+
flex-direction: column;
|
|
1035
|
+
align-items: center;
|
|
1036
|
+
gap: var(--space-3);
|
|
1037
|
+
padding: var(--space-8) var(--space-6);
|
|
1038
|
+
border-top: 1px solid var(--border);
|
|
1039
|
+
}
|
|
1040
|
+
/* Decorative inner brand band: keep aria-hidden="true" on this wrapper in the
|
|
1041
|
+
HTML (SiteFooter.tsx) — pointer-events:none here only removes mouse hits, the
|
|
1042
|
+
aria-hidden is what keeps it out of the accessibility tree. */
|
|
1043
|
+
.site-footer__band {
|
|
1044
|
+
display: flex;
|
|
1045
|
+
flex-direction: column;
|
|
1046
|
+
align-items: center;
|
|
1047
|
+
gap: var(--space-2);
|
|
1048
|
+
pointer-events: none;
|
|
1049
|
+
}
|
|
1050
|
+
.site-footer__wordmark {
|
|
1051
|
+
color: var(--text);
|
|
1052
|
+
opacity: 0.85;
|
|
1053
|
+
}
|
|
1054
|
+
.site-footer__tagline {
|
|
1055
|
+
font-family: var(--font-mono);
|
|
1056
|
+
font-size: var(--fs-10);
|
|
1057
|
+
letter-spacing: 0.34em;
|
|
1058
|
+
text-transform: uppercase;
|
|
1059
|
+
color: var(--text-faint);
|
|
1060
|
+
}
|
|
1061
|
+
.site-footer__attrib {
|
|
1062
|
+
font-family: var(--font-mono);
|
|
1063
|
+
font-size: var(--fs-11);
|
|
1064
|
+
line-height: 1.6;
|
|
1065
|
+
color: var(--text-muted);
|
|
1066
|
+
}
|
|
1067
|
+
.site-footer__attrib a {
|
|
1068
|
+
color: var(--brand);
|
|
1069
|
+
text-decoration: none;
|
|
1070
|
+
border-bottom: 1px solid color-mix(in srgb, var(--brand) 45%, transparent);
|
|
1071
|
+
}
|
|
1072
|
+
.site-footer__attrib a:hover {
|
|
1073
|
+
border-bottom-color: var(--brand);
|
|
1074
|
+
}
|
|
1075
|
+
.site-footer__attrib a:focus-visible {
|
|
1076
|
+
outline: 2px solid var(--brand);
|
|
1077
|
+
outline-offset: 2px;
|
|
1078
|
+
border-radius: var(--r-control);
|
|
1079
|
+
}
|