style.css
1 * { 2 margin: 0; 3 padding: 0; 4 box-sizing: border-box; 5 } 6 7 :root { 8 --bg-primary: #0d1117; 9 --bg-secondary: #161b22; 10 --bg-tertiary: #21262d; 11 --border-color: #30363d; 12 --text-primary: #e6edf3; 13 --text-secondary: #8b949e; 14 --text-muted: #6e7681; 15 --accent-blue: #58a6ff; 16 --accent-green: #3fb950; 17 --accent-red: #f85149; 18 --accent-yellow: #d29922; 19 --accent-orange: #db6d28; 20 --accent-purple: #a371f7; 21 } 22 23 body { 24 font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, sans-serif; 25 background: var(--bg-primary); 26 color: var(--text-primary); 27 min-height: 100vh; 28 line-height: 1.5; 29 } 30 31 header { 32 background: var(--bg-secondary); 33 border-bottom: 1px solid var(--border-color); 34 padding: 1rem 2rem; 35 display: flex; 36 justify-content: space-between; 37 align-items: center; 38 position: sticky; 39 top: 0; 40 z-index: 100; 41 } 42 43 header h1 { 44 font-size: 1.5rem; 45 font-weight: 600; 46 } 47 48 .header-right { 49 display: flex; 50 align-items: center; 51 gap: 1rem; 52 } 53 54 .system-stats { 55 display: flex; 56 gap: 0.75rem; 57 } 58 59 .system-stats .stat { 60 display: flex; 61 flex-direction: column; 62 align-items: center; 63 padding: 0.375rem 0.75rem; 64 background: var(--bg-tertiary); 65 border-radius: 6px; 66 border: 1px solid var(--border-color); 67 min-width: 60px; 68 } 69 70 .system-stats .stat-label { 71 font-size: 0.625rem; 72 font-weight: 600; 73 color: var(--text-muted); 74 text-transform: uppercase; 75 letter-spacing: 0.05em; 76 } 77 78 .system-stats .stat-value { 79 font-size: 0.875rem; 80 font-weight: 600; 81 font-family: 'SF Mono', Monaco, 'Cascadia Code', monospace; 82 } 83 84 .stat-value.stat-normal { 85 color: var(--accent-green); 86 } 87 88 .stat-value.stat-warning { 89 color: var(--accent-yellow); 90 } 91 92 .stat-value.stat-critical { 93 color: var(--accent-red); 94 } 95 96 .stat-value.stat-offline { 97 color: var(--text-muted); 98 } 99 100 .system-stats-container { 101 display: flex; 102 gap: 1rem; 103 } 104 105 .system-stats .server-label { 106 font-size: 0.625rem; 107 font-weight: 700; 108 color: var(--text-secondary); 109 text-transform: uppercase; 110 letter-spacing: 0.05em; 111 padding: 0.375rem 0; 112 border-right: 1px solid var(--border-color); 113 padding-right: 0.75rem; 114 margin-right: 0.25rem; 115 } 116 117 .health-status { 118 display: flex; 119 align-items: center; 120 gap: 0.5rem; 121 padding: 0.5rem 1rem; 122 background: var(--bg-tertiary); 123 border-radius: 6px; 124 border: 1px solid var(--border-color); 125 } 126 127 .status-dot { 128 width: 10px; 129 height: 10px; 130 border-radius: 50%; 131 background: var(--text-muted); 132 animation: pulse 2s infinite; 133 } 134 135 .health-status.healthy .status-dot { 136 background: var(--accent-green); 137 } 138 139 .health-status.unhealthy .status-dot { 140 background: var(--accent-red); 141 } 142 143 .health-status.degraded .status-dot { 144 background: var(--accent-yellow); 145 } 146 147 @keyframes pulse { 148 0%, 100% { opacity: 1; } 149 50% { opacity: 0.5; } 150 } 151 152 main { 153 padding: 2rem; 154 max-width: 1800px; 155 margin: 0 auto; 156 } 157 158 .dashboard-grid { 159 display: grid; 160 grid-template-columns: repeat(auto-fit, minmax(400px, 1fr)); 161 gap: 1.5rem; 162 } 163 164 .card { 165 background: var(--bg-secondary); 166 border: 1px solid var(--border-color); 167 border-radius: 8px; 168 overflow: hidden; 169 } 170 171 .card.full-width { 172 grid-column: 1 / -1; 173 } 174 175 .card h2 { 176 padding: 1rem 1.5rem; 177 font-size: 1rem; 178 font-weight: 600; 179 background: var(--bg-tertiary); 180 border-bottom: 1px solid var(--border-color); 181 display: flex; 182 align-items: center; 183 gap: 0.75rem; 184 } 185 186 .badge { 187 background: var(--accent-blue); 188 color: white; 189 padding: 0.125rem 0.5rem; 190 border-radius: 10px; 191 font-size: 0.75rem; 192 font-weight: 600; 193 } 194 195 .card-content { 196 padding: 1rem; 197 max-height: 400px; 198 overflow-y: auto; 199 } 200 201 .repos-card .card-content { 202 max-height: none; 203 overflow-y: visible; 204 } 205 206 /* Tables */ 207 table { 208 width: 100%; 209 border-collapse: collapse; 210 font-size: 0.875rem; 211 } 212 213 th, td { 214 padding: 0.75rem 1rem; 215 text-align: left; 216 border-bottom: 1px solid var(--border-color); 217 } 218 219 th { 220 font-weight: 600; 221 color: var(--text-secondary); 222 text-transform: uppercase; 223 font-size: 0.75rem; 224 letter-spacing: 0.05em; 225 } 226 227 tbody tr:hover { 228 background: var(--bg-tertiary); 229 } 230 231 tbody tr:last-child td { 232 border-bottom: none; 233 } 234 235 .loading { 236 color: var(--text-muted); 237 text-align: center; 238 padding: 2rem; 239 } 240 241 /* Status badges */ 242 .status-badge { 243 display: inline-flex; 244 align-items: center; 245 gap: 0.375rem; 246 padding: 0.25rem 0.625rem; 247 border-radius: 4px; 248 font-size: 0.75rem; 249 font-weight: 500; 250 } 251 252 .status-badge.running { 253 background: rgba(88, 166, 255, 0.15); 254 color: var(--accent-blue); 255 } 256 257 .status-badge.idle { 258 background: rgba(139, 148, 158, 0.15); 259 color: var(--text-secondary); 260 } 261 262 .status-badge.success, .status-badge.passed { 263 background: rgba(63, 185, 80, 0.15); 264 color: var(--accent-green); 265 } 266 267 .status-badge.failed, .status-badge.failure { 268 background: rgba(248, 81, 73, 0.15); 269 color: var(--accent-red); 270 } 271 272 .status-badge.pending, .status-badge.queued { 273 background: rgba(210, 153, 34, 0.15); 274 color: var(--accent-yellow); 275 } 276 277 .status-badge.skipped { 278 background: rgba(110, 118, 129, 0.15); 279 color: var(--text-muted); 280 } 281 282 .status-badge.cancelled { 283 background: rgba(219, 109, 40, 0.15); 284 color: var(--accent-orange); 285 } 286 287 /* CI Job status icons */ 288 .ci-status { 289 display: inline-flex; 290 align-items: center; 291 justify-content: center; 292 width: 24px; 293 height: 24px; 294 border-radius: 4px; 295 font-size: 0.875rem; 296 } 297 298 .ci-status.pass { 299 background: rgba(63, 185, 80, 0.15); 300 color: var(--accent-green); 301 } 302 303 .ci-status.fail { 304 background: rgba(248, 81, 73, 0.15); 305 color: var(--accent-red); 306 } 307 308 .ci-status.skip { 309 background: rgba(110, 118, 129, 0.15); 310 color: var(--text-muted); 311 } 312 313 .ci-status.running { 314 background: rgba(88, 166, 255, 0.15); 315 color: var(--accent-blue); 316 } 317 318 .ci-status.pending { 319 background: rgba(210, 153, 34, 0.15); 320 color: var(--accent-yellow); 321 } 322 323 /* Queue list */ 324 .job-list { 325 list-style: none; 326 } 327 328 .job-list li { 329 padding: 0.75rem 1rem; 330 border-bottom: 1px solid var(--border-color); 331 display: flex; 332 justify-content: space-between; 333 align-items: center; 334 } 335 336 .job-list li:last-child { 337 border-bottom: none; 338 } 339 340 .job-list li:hover { 341 background: var(--bg-tertiary); 342 } 343 344 .job-info { 345 display: flex; 346 flex-direction: column; 347 gap: 0.25rem; 348 } 349 350 .job-name { 351 font-weight: 500; 352 } 353 354 .job-meta { 355 font-size: 0.75rem; 356 color: var(--text-secondary); 357 } 358 359 .job-position { 360 color: var(--text-muted); 361 font-size: 0.875rem; 362 } 363 364 .empty-state { 365 text-align: center; 366 padding: 2rem; 367 color: var(--text-muted); 368 } 369 370 /* Links */ 371 a { 372 color: var(--accent-blue); 373 text-decoration: none; 374 } 375 376 a:hover { 377 text-decoration: underline; 378 } 379 380 /* Footer */ 381 footer { 382 padding: 1rem 2rem; 383 border-top: 1px solid var(--border-color); 384 background: var(--bg-secondary); 385 display: flex; 386 justify-content: space-between; 387 color: var(--text-muted); 388 font-size: 0.875rem; 389 } 390 391 /* Scrollbar styling */ 392 ::-webkit-scrollbar { 393 width: 8px; 394 height: 8px; 395 } 396 397 ::-webkit-scrollbar-track { 398 background: var(--bg-primary); 399 } 400 401 ::-webkit-scrollbar-thumb { 402 background: var(--border-color); 403 border-radius: 4px; 404 } 405 406 ::-webkit-scrollbar-thumb:hover { 407 background: var(--text-muted); 408 } 409 410 /* Responsive */ 411 @media (max-width: 900px) { 412 .dashboard-grid { 413 grid-template-columns: 1fr; 414 } 415 416 header { 417 flex-direction: column; 418 gap: 1rem; 419 } 420 421 main { 422 padding: 1rem; 423 } 424 } 425 426 /* Runner name styling */ 427 .runner-name { 428 font-weight: 500; 429 color: var(--accent-purple); 430 } 431 432 /* Repo name styling */ 433 .repo-name { 434 font-weight: 500; 435 } 436 437 /* Branch styling */ 438 .branch-name { 439 font-family: 'SF Mono', Monaco, 'Cascadia Code', monospace; 440 font-size: 0.8125rem; 441 background: var(--bg-tertiary); 442 padding: 0.125rem 0.5rem; 443 border-radius: 4px; 444 } 445 446 /* Time ago styling */ 447 .time-ago { 448 color: var(--text-secondary); 449 font-size: 0.8125rem; 450 } 451 452 /* Duration styling */ 453 .duration { 454 font-family: 'SF Mono', Monaco, 'Cascadia Code', monospace; 455 font-size: 0.8125rem; 456 color: var(--text-secondary); 457 } 458 459 /* Repository list with sync status */ 460 .repo-list { 461 list-style: none; 462 display: grid; 463 grid-template-columns: repeat(auto-fill, minmax(180px, 1fr)); 464 gap: 0.5rem; 465 } 466 467 .repo-item { 468 display: flex; 469 align-items: center; 470 gap: 0.5rem; 471 padding: 0.5rem 0.75rem; 472 background: var(--bg-tertiary); 473 border-radius: 6px; 474 border: 1px solid var(--border-color); 475 } 476 477 .sync-indicator { 478 width: 12px; 479 height: 12px; 480 border-radius: 50%; 481 flex-shrink: 0; 482 } 483 484 .sync-indicator.sync-green { 485 background: var(--accent-green); 486 box-shadow: 0 0 6px var(--accent-green); 487 } 488 489 .sync-indicator.sync-yellow { 490 background: var(--accent-yellow); 491 box-shadow: 0 0 6px var(--accent-yellow); 492 } 493 494 .sync-indicator.sync-red { 495 background: var(--accent-red); 496 box-shadow: 0 0 6px var(--accent-red); 497 } 498 499 .repo-item .repo-name { 500 font-size: 0.875rem; 501 overflow: hidden; 502 text-overflow: ellipsis; 503 white-space: nowrap; 504 } 505 506 /* Queue progress display */ 507 .queue-item { 508 padding: 0.75rem 1rem; 509 border-bottom: 1px solid var(--border-color); 510 } 511 512 .queue-item:last-child { 513 border-bottom: none; 514 } 515 516 .queue-info { 517 display: flex; 518 justify-content: space-between; 519 align-items: center; 520 margin-bottom: 0.5rem; 521 } 522 523 .queue-repo { 524 font-weight: 500; 525 font-size: 0.9375rem; 526 } 527 528 .queue-progress { 529 font-size: 0.8125rem; 530 color: var(--text-secondary); 531 font-family: 'SF Mono', Monaco, 'Cascadia Code', monospace; 532 } 533 534 .queue-bar { 535 height: 6px; 536 background: var(--bg-tertiary); 537 border-radius: 3px; 538 overflow: hidden; 539 } 540 541 .queue-bar-fill { 542 height: 100%; 543 background: var(--accent-blue); 544 border-radius: 3px; 545 transition: width 0.3s ease; 546 } 547 548 .queue-item.running .queue-bar-fill { 549 background: var(--accent-blue); 550 } 551 552 .queue-item.pending .queue-bar-fill { 553 background: var(--accent-yellow); 554 } 555 556 .queue-item.done .queue-bar-fill { 557 background: var(--accent-green); 558 } 559