Files
Commonwealth-Online-Public/ui/views/CommonwealthOnline/browser/styles.css
T
andrew fb4e238fcc Add dynamic UI theme for server browser
Introduce runtime-driven UI theming for the server browser

- Expose PushUITheme() in F4TServerBrowserBridge and implement ColorToJson + PushUIThemeInternal to serialize HUD colors and invoke a JS hook.
- Call PushUITheme on UI initialization and when the browser is shown so the webview receives current HUD colors.
- Add frontend handler for "uiTheme" in app.js and a new components/theme.js that implements window.applyCoTheme to set CSS vars and tint the cursor SVG.
- Update styles.css to use RGB component CSS variables (--co-r/--co-g/--co-b and background equivalents) so colors can be overridden at runtime.
- Update index.html to include the new theme script and tweak cursor SVG stroke width; cursor asset updated as well.

This enables the plugin to synchronize the in-game HUD colors with the browser UI dynamically.
2026-06-09 15:19:03 +12:00

689 lines
14 KiB
CSS
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
@font-face {
font-family: "Roboto Condensed";
src: url("./assets/fonts/RobotoCondensed-SemiBold.ttf") format("truetype");
font-weight: 600 700;
font-style: normal;
font-display: block;
}
:root {
/* RGB components — overridden at runtime from Fallout4Prefs.ini via plugin. */
--co-r: 18;
--co-g: 255;
--co-b: 21;
--co-bg-r: 0;
--co-bg-g: 10;
--co-bg-b: 0;
--co-green: rgb(var(--co-r), var(--co-g), var(--co-b));
--co-green-soft: rgb(var(--co-r), var(--co-g), var(--co-b));
--co-green-dim: rgba(var(--co-r), var(--co-g), var(--co-b), 0.22);
--co-bg: rgba(var(--co-bg-r), var(--co-bg-g), var(--co-bg-b), 0.62);
--co-bg-dark: rgba(var(--co-bg-r), var(--co-bg-g), var(--co-bg-b), 0.72);
--co-bg-list: transparent;
--co-selected: rgb(var(--co-r), var(--co-g), var(--co-b));
--co-selected-text: rgb(
calc(var(--co-r) * 0.05),
calc(var(--co-g) * 0.05),
calc(var(--co-b) * 0.05)
);
--co-border: rgb(var(--co-r), var(--co-g), var(--co-b));
--co-text-dim: rgba(var(--co-r), var(--co-g), var(--co-b), 0.5);
--co-ping-good: rgb(var(--co-r), var(--co-g), var(--co-b));
--co-ping-mid: #c8ff28;
--co-ping-bad: #ffd428;
--co-danger: #ff6a6a;
--co-font: "Roboto Condensed", "Bahnschrift", "Arial Narrow", Arial, sans-serif;
--co-transition: 80ms linear;
--co-border-stroke: 5px;
--co-border-strip-height: 11px;
--co-border-cap: 10px;
--co-border-edge: 4px;
/*
* Fallout 4 submenu alignment (1280×720, like ADD-ONS panel):
* Left — tight gap, no overlap (~454px)
* Top — MULTIPLAYER row (~286px)
* Bottom — main menu box bottom (~146px inset)
*/
--co-panel-left: max(35.6vw, 454px);
--co-panel-right: max(2vw, 24px);
--co-panel-top: max(39.5vh, 286px);
--co-panel-bottom: max(20.5vh, 146px);
--co-font-xs: clamp(9px, 0.75vw, 11px);
--co-font-sm: clamp(10px, 0.85vw, 12px);
--co-font-md: clamp(13px, 1.05vw, 16px);
--co-font-lg: clamp(16px, 1.3vw, 20px);
--co-nav-width: clamp(152px, 14vw, 196px);
/*
* In-game cursor is rendered by PrismaUI_F4 from Data/PrismaUI_F4/misc/cursor.png
* (deployed from assets/F4MouseCursor.png). CSS cursor only applies in a browser.
*/
--co-cursor: url("./assets/F4MouseCursor.png") 1 1, default;
}
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
html,
body {
width: 100vw;
height: 100vh;
overflow: hidden;
font-family: var(--co-font);
font-weight: 600;
color: var(--co-green);
background: transparent;
user-select: none;
-webkit-font-smoothing: antialiased;
cursor: var(--co-cursor);
}
.co-root {
position: relative;
width: 100vw;
height: 100vh;
pointer-events: none;
background: transparent;
}
/* Submenu panel — SVG border overlay + translucent fill. */
.co-panel {
position: absolute;
left: var(--co-panel-left);
right: var(--co-panel-right);
top: var(--co-panel-top);
bottom: var(--co-panel-bottom);
min-height: 0;
display: flex;
flex-direction: column;
border: none;
background: var(--co-bg);
pointer-events: auto;
transition: opacity var(--co-transition);
padding: var(--co-border-cap) var(--co-border-edge);
overflow: visible;
}
.co-panel.co-loading {
opacity: 0.85;
}
/*
* Top/bottom border strips (from assets/MenuBoarder.svg).
* Split so each edge anchors correctly — a single stretched SVG misaligns in Ultralight.
*/
.co-panel-border {
position: absolute;
left: 0;
width: 100%;
height: var(--co-border-strip-height);
pointer-events: none;
z-index: 10;
}
.co-panel-border-top {
top: 0;
}
.co-panel-border-bottom {
bottom: 0;
}
.co-panel-border-line {
fill: none;
stroke: var(--co-border);
stroke-width: var(--co-border-stroke);
stroke-miterlimit: 10;
vector-effect: non-scaling-stroke;
}
.co-subpanel {
flex: 1;
min-height: 0;
display: flex;
overflow: hidden;
}
/* Left nav — mirrors Settings / Load category list. */
.co-nav-list {
flex: 0 0 var(--co-nav-width);
display: flex;
flex-direction: column;
padding: 6px 0;
border-right: 1px solid var(--co-green-dim);
overflow-y: auto;
}
.co-nav-item {
appearance: none;
border: none;
background: transparent;
color: var(--co-green-soft);
font: inherit;
font-size: var(--co-font-md);
font-weight: 700;
letter-spacing: 0.12em;
text-transform: uppercase;
text-align: left;
padding: 10px 16px;
cursor: var(--co-cursor);
transition: background var(--co-transition), color var(--co-transition);
}
.co-nav-item:hover:not(.active) {
background: rgba(var(--co-r), var(--co-g), var(--co-b), 0.08);
}
.co-nav-item.active {
background: var(--co-selected);
color: var(--co-selected-text);
}
.co-nav-item.focused:not(.active) {
background: rgba(var(--co-r), var(--co-g), var(--co-b), 0.12);
}
.co-nav-list.focused .co-nav-item.focused:not(.active) {
background: rgba(var(--co-r), var(--co-g), var(--co-b), 0.12);
}
/* Right content */
.co-subpanel-content {
flex: 1;
min-width: 0;
display: flex;
flex-direction: column;
overflow: hidden;
}
.co-tab-content {
flex: 1;
min-height: 0;
overflow: hidden;
}
.co-tab-pane {
display: none;
flex-direction: column;
height: 100%;
min-height: 0;
}
.co-tab-pane.active {
display: flex;
}
.co-subpanel-header {
flex-shrink: 0;
display: flex;
align-items: baseline;
justify-content: space-between;
gap: 16px;
padding: 10px 16px 8px;
}
.co-subpanel-title {
font-size: var(--co-font-sm);
font-weight: 700;
letter-spacing: 0.16em;
text-transform: uppercase;
color: var(--co-text-dim);
}
.co-subpanel-meta {
display: flex;
gap: 10px;
font-size: var(--co-font-xs);
font-weight: 700;
letter-spacing: 0.12em;
text-transform: uppercase;
color: var(--co-text-dim);
white-space: nowrap;
}
.co-subpanel-meta .co-count {
color: var(--co-green-soft);
}
/* Preview — Load-menu thumbnail (favorites / recent only). */
.co-preview-area {
flex-shrink: 0;
margin: 0 12px 8px;
border: 1px solid var(--co-green-dim);
background: var(--co-bg-dark);
overflow: hidden;
}
.co-preview-area:empty,
.co-preview-area.co-preview-hidden {
display: none;
}
.co-preview {
width: 100%;
height: clamp(72px, 14vh, 120px);
display: flex;
align-items: center;
justify-content: center;
color: var(--co-text-dim);
font-size: var(--co-font-sm);
letter-spacing: 0.1em;
text-transform: uppercase;
overflow: hidden;
}
.co-preview img {
width: 100%;
height: 100%;
object-fit: cover;
}
.co-preview-caption {
padding: 6px 10px;
border-top: 1px solid var(--co-green-dim);
font-size: var(--co-font-xs);
letter-spacing: 0.08em;
text-transform: uppercase;
line-height: 1.35;
}
.co-preview-caption .co-preview-name {
font-size: var(--co-font-sm);
font-weight: 700;
color: var(--co-green);
}
.co-preview-caption .co-preview-stats {
color: var(--co-text-dim);
}
/* Filters */
.co-filters {
flex-shrink: 0;
padding: 0 12px 10px;
display: flex;
flex-direction: column;
gap: 6px;
}
.co-filters.focused .co-filter-row.focused .co-filter-input,
.co-filters.focused .co-filter-row.focused .co-filter-select,
.co-filters.focused .co-filter-row.focused .co-checkbox {
outline: 1px solid var(--co-green);
outline-offset: 0;
}
.co-filters-row {
display: flex;
flex-wrap: wrap;
align-items: flex-end;
gap: 6px 10px;
}
.co-filters-row-selects .co-filter-row {
flex: 1 1 90px;
min-width: 80px;
}
.co-filters-row-options {
align-items: center;
}
.co-filter-row {
display: flex;
flex-direction: column;
gap: 3px;
min-width: 0;
}
.co-filter-row[data-filter-index="0"] {
width: 100%;
}
.co-filter-row.co-filter-checks {
flex-direction: row;
align-items: center;
gap: 5px;
flex: 0 0 auto;
}
.co-filter-label {
font-size: var(--co-font-xs);
font-weight: 700;
letter-spacing: 0.12em;
text-transform: uppercase;
color: var(--co-text-dim);
}
.co-filter-input,
.co-filter-select {
width: 100%;
background: var(--co-bg-dark);
border: 1px solid var(--co-green-dim);
color: var(--co-green);
font: inherit;
font-size: var(--co-font-sm);
font-weight: 600;
padding: 5px 8px;
border-radius: 0;
cursor: var(--co-cursor);
}
.co-filter-input::placeholder {
color: var(--co-text-dim);
}
.co-filter-input:focus,
.co-filter-select:focus {
border-color: var(--co-border);
outline: none;
}
.co-checkbox-row {
display: flex;
align-items: center;
gap: 5px;
font-size: var(--co-font-xs);
font-weight: 700;
letter-spacing: 0.06em;
text-transform: uppercase;
cursor: var(--co-cursor);
}
.co-checkbox {
width: 13px;
height: 13px;
border: 1px solid var(--co-border);
background: transparent;
display: inline-flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
}
.co-checkbox.checked::after {
content: "X";
font-size: 9px;
font-weight: 700;
color: var(--co-green);
line-height: 1;
}
.co-btn {
appearance: none;
border: 1px solid var(--co-green-dim);
background: transparent;
color: var(--co-green);
font: inherit;
font-size: var(--co-font-xs);
font-weight: 700;
letter-spacing: 0.1em;
text-transform: uppercase;
padding: 5px 10px;
cursor: var(--co-cursor);
margin-left: auto;
flex-shrink: 0;
transition: background var(--co-transition);
}
.co-btn:hover {
background: rgba(var(--co-r), var(--co-g), var(--co-b), 0.1);
}
.co-btn.focused {
background: rgba(var(--co-r), var(--co-g), var(--co-b), 0.18);
outline: 1px solid var(--co-green);
}
/* Server list — save-file style rows */
.co-server-list-wrap {
flex: 1;
min-height: 0;
display: flex;
flex-direction: column;
position: relative;
}
.co-server-list-wrap.focused {
background: rgba(var(--co-r), var(--co-g), var(--co-b), 0.03);
}
.co-server-list {
flex: 1;
overflow-y: auto;
scrollbar-width: thin;
scrollbar-color: var(--co-green-dim) transparent;
padding: 0 4px;
}
.co-server-list::-webkit-scrollbar {
width: 4px;
}
.co-server-list::-webkit-scrollbar-thumb {
background: var(--co-green-dim);
}
.co-server-row {
display: flex;
align-items: center;
justify-content: space-between;
gap: 16px;
padding: 8px 12px;
font-size: var(--co-font-md);
line-height: 1.2;
cursor: var(--co-cursor);
transition: background var(--co-transition);
}
.co-server-row:hover:not(.selected) {
background: rgba(var(--co-r), var(--co-g), var(--co-b), 0.06);
}
.co-server-row.selected {
background: var(--co-selected);
color: var(--co-selected-text);
}
.co-server-row-left {
display: flex;
align-items: center;
gap: 10px;
min-width: 0;
flex: 1;
}
.co-server-row-name {
font-size: var(--co-font-lg);
font-weight: 700;
letter-spacing: 0.06em;
text-transform: uppercase;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.co-server-row-right {
display: flex;
align-items: center;
gap: 20px;
flex-shrink: 0;
font-size: var(--co-font-md);
font-weight: 700;
letter-spacing: 0.04em;
}
.co-server-row.selected .co-server-row-right,
.co-server-row.selected .co-fav,
.co-server-row.selected .co-ping {
color: var(--co-selected-text);
}
.co-fav {
color: var(--co-text-dim);
font-size: 15px;
flex-shrink: 0;
}
.co-fav.on {
color: var(--co-green);
}
.co-server-row.selected .co-fav.on {
color: var(--co-selected-text);
}
.co-ping-good { color: var(--co-ping-good); }
.co-ping-mid { color: var(--co-ping-mid); }
.co-ping-bad { color: var(--co-ping-bad); }
.co-server-row.selected .co-ping-good,
.co-server-row.selected .co-ping-mid,
.co-server-row.selected .co-ping-bad {
color: var(--co-selected-text);
}
.co-scroll-indicator {
flex-shrink: 0;
text-align: center;
padding: 2px 0 4px;
font-size: var(--co-font-xs);
letter-spacing: 0.15em;
color: var(--co-text-dim);
}
.co-scroll-indicator.hidden {
display: none;
}
/* Footer prompts */
.co-action-bar {
flex-shrink: 0;
display: flex;
align-items: center;
justify-content: flex-end;
padding: 6px 14px 8px;
border-top: 1px solid var(--co-green-dim);
}
.co-prompts {
display: flex;
gap: 16px;
flex-wrap: wrap;
justify-content: flex-end;
}
.co-prompt {
display: flex;
align-items: center;
gap: 6px;
font-size: var(--co-font-xs);
font-weight: 700;
letter-spacing: 0.08em;
text-transform: uppercase;
color: var(--co-green-soft);
}
.co-prompt-key {
min-width: 18px;
height: 18px;
border: 1px solid var(--co-border);
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-size: 8px;
font-weight: 700;
padding: 0 3px;
}
.co-direct-connect {
padding: 16px 20px;
max-width: 480px;
}
.co-direct-row {
margin-bottom: 12px;
}
.co-direct-row label {
display: block;
font-size: var(--co-font-xs);
font-weight: 700;
letter-spacing: 0.1em;
text-transform: uppercase;
color: var(--co-text-dim);
margin-bottom: 4px;
}
.co-empty {
padding: 24px 16px;
text-align: center;
color: var(--co-text-dim);
font-size: var(--co-font-md);
letter-spacing: 0.08em;
text-transform: uppercase;
}
.co-join-overlay {
position: absolute;
inset: 0;
background: rgba(0, 0, 0, 0.75);
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
z-index: 10;
pointer-events: auto;
}
.co-join-overlay.hidden {
display: none;
}
.co-join-overlay p {
font-size: var(--co-font-md);
letter-spacing: 0.1em;
text-transform: uppercase;
margin-top: 14px;
}
.co-join-overlay.co-error p {
color: var(--co-danger);
}
.co-spinner {
width: 22px;
height: 22px;
border: 2px solid var(--co-green-dim);
border-top-color: var(--co-green);
border-radius: 50%;
animation: co-spin 0.8s linear infinite;
}
@keyframes co-spin {
to { transform: rotate(360deg); }
}
@media (max-width: 1280px) {
:root {
--co-panel-left: max(35.8vw, 450px);
--co-panel-right: max(2vw, 16px);
--co-panel-top: max(40vh, 280px);
--co-panel-bottom: max(21vh, 142px);
--co-nav-width: clamp(136px, 15vw, 168px);
}
}
@media (min-width: 2560px) {
:root {
--co-panel-left: max(35.6vw, 908px);
--co-panel-right: max(2.5vw, 48px);
--co-panel-top: max(39.5vh, 572px);
--co-panel-bottom: max(20.5vh, 292px);
}
}