From be3a00a8f5ac672887c578a68586c0f084141058 Mon Sep 17 00:00:00 2001 From: mwiegand Date: Sun, 17 May 2026 21:12:31 +0200 Subject: [PATCH] feat(css): state-cluster, inspection-strip, compact player grids Co-Authored-By: Claude Sonnet 4.6 --- l4d2web/l4d2web/static/css/components.css | 171 ++++++++++++++++++---- l4d2web/l4d2web/static/css/tokens.css | 2 + 2 files changed, 143 insertions(+), 30 deletions(-) diff --git a/l4d2web/l4d2web/static/css/components.css b/l4d2web/l4d2web/static/css/components.css index c629fe0..7a1a956 100644 --- a/l4d2web/l4d2web/static/css/components.css +++ b/l4d2web/l4d2web/static/css/components.css @@ -873,63 +873,174 @@ div.modal.modal-wide { color: var(--color-muted); } -/* Live-state panel — current + recent players on the server detail page. - Avatars are fetched as the 184x184 Steam "full" size and downscaled in - CSS so high-DPI screens render crisply. */ +/* ============================================================ + State cluster (top of server detail) + ============================================================ */ -.live-state .player-grid { - list-style: none; - padding: 0; - margin: var(--space-m) 0 0; +.state-cluster { display: flex; - flex-wrap: wrap; + flex-direction: column; gap: var(--space-m); } -.live-state .player-card { - display: grid; - grid-template-columns: auto 1fr; - column-gap: var(--space-s); - align-items: center; +.state-cluster > * + * { + border-top: 1px dashed var(--color-border-muted); + padding-top: var(--space-m); } -.live-state .player-link { +/* Config grid — auto-fit so it scales with field count */ +.config-grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); + gap: var(--space-s) var(--space-m); +} +.config-field-label { + font-size: 0.75em; + text-transform: uppercase; + letter-spacing: 0.06em; + color: var(--color-muted); + margin-bottom: 0.15em; +} +.config-field-value { font-size: 0.95em; } + +/* ============================================================ + Live-state — compact player grids + ============================================================ */ + +.player-grid { + list-style: none; + padding: 0; + margin: var(--space-s) 0 0; + display: grid; + gap: var(--space-xs); +} +.player-grid.current { grid-template-columns: repeat(4, 1fr); } +.player-grid.recent { grid-template-columns: repeat(5, 1fr); } + +.player-card { + display: grid; + grid-template-columns: auto 1fr; + column-gap: var(--space-xs); + align-items: center; + padding: var(--space-xs); +} +.player-link { display: contents; color: inherit; text-decoration: none; } +.player-link:hover .name { text-decoration: underline; } -.live-state .player-link:hover .name { - text-decoration: underline; -} - -.live-state .avatar { - width: 64px; - height: 64px; +.player-card .avatar { border-radius: var(--radius-s); object-fit: cover; - grid-row: 1 / span 2; +} +.current-card .avatar { width: 22px; height: 22px; grid-row: 1 / span 2; } +.recent-chip .avatar { width: 16px; height: 16px; } + +.player-card .name { + grid-column: 2; + font-size: 0.9em; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} +.current-card .name { font-weight: 600; } +.recent-chip .name { font-weight: 500; } + +.player-card .meta { + grid-column: 2; + color: var(--color-muted); + font-size: 0.75em; +} +.recent-chip .meta { + grid-column: auto; + font-size: 0.8em; } -.live-state .avatar.placeholder { +.avatar.placeholder { display: inline-block; background: color-mix(in srgb, var(--color-muted) 30%, transparent); } -.live-state .name { - grid-column: 2; +.recent-header { + margin: var(--space-m) 0 var(--space-xs); + font-size: 0.75em; + text-transform: uppercase; + letter-spacing: 0.06em; + color: var(--color-muted); font-weight: 600; } +.recent-header-trigger { + background: transparent; + border: 0; + padding: 0; + font: inherit; + color: var(--color-link); + text-transform: inherit; + letter-spacing: inherit; + text-decoration: underline dotted; + text-underline-offset: 2px; + cursor: pointer; +} +.recent-header-trigger:hover { text-decoration-style: solid; } -.live-state .meta { - grid-column: 2; +.server-live-summary { font-size: 1.05em; } + +/* Narrow viewports — collapse fixed grids so chips don't crush */ +@media (max-width: 600px) { + .player-grid.current, + .player-grid.recent { + grid-template-columns: repeat(auto-fit, minmax(110px, 1fr)); + } +} + +/* ============================================================ + Inspection strip — tabbed Log / Console / Files + ============================================================ */ + +.inspection-strip { + display: flex; + flex-direction: column; + padding: 0; + overflow: hidden; +} +.tab-bar { + display: flex; + align-items: center; + border-bottom: 1px solid var(--color-border-muted); + padding: 0 var(--space-s); +} +.tab-bar [role="tab"] { + background: transparent; + border: 0; + padding: var(--space-xs) var(--space-m); color: var(--color-muted); - font-size: 0.85em; + cursor: pointer; + font: inherit; + border-bottom: 2px solid transparent; } +.tab-bar [role="tab"][aria-selected="true"] { + color: var(--color-text); + border-bottom-color: var(--color-link); +} +.tab-bar .strip-expand { + margin-left: auto; + background: transparent; + border: 0; + padding: var(--space-xs) var(--space-s); + color: var(--color-muted); + cursor: pointer; +} +.tab-bar .strip-expand:hover { color: var(--color-text); } -.live-state .server-live-summary { - font-size: 1.05em; +.tab-pane { + padding: var(--space-s); + max-height: 12rem; + overflow: auto; } +.tab-pane[hidden] { display: none; } +.tab-pane .log-stream { max-height: none; } /* let pane handle scrolling */ /* ============================================================ RCON console panel — server detail page diff --git a/l4d2web/l4d2web/static/css/tokens.css b/l4d2web/l4d2web/static/css/tokens.css index 2c48305..f8f8728 100644 --- a/l4d2web/l4d2web/static/css/tokens.css +++ b/l4d2web/l4d2web/static/css/tokens.css @@ -5,6 +5,7 @@ --color-text: #18181b; --color-muted: #60646c; --color-border: #d4d4d8; + --color-border-muted: rgba(0, 0, 0, 0.12); --color-link: #1d4ed8; --color-primary: #1d4ed8; --color-danger: #b42318; @@ -58,6 +59,7 @@ --color-text: #f4f4f5; --color-muted: #a1a1aa; --color-border: #3f3f46; + --color-border-muted: rgba(255, 255, 255, 0.12); --color-link: #93c5fd; --color-primary: #93c5fd; --color-danger: #fca5a5;