network segmentation + throwback theme
All checks were successful
ci/woodpecker/push/infra-ci Pipeline was successful
ci/woodpecker/push/deploy Pipeline was successful
ci/woodpecker/push/ci Pipeline was successful

Replace flat intrastack network with 5 purpose-scoped zones (gateway,
storage, data, observability, ci) so compromised containers cannot
reach unrelated services. Add local-only and infra-headers Traefik
middlewares with per-service assignments via reef mw key.

Rename nes-sports theme to throwback with Tecmo Super Bowl palette:
deep navy backgrounds, white text, patriotic red/blue accents. Remove
CRT scanlines, power-on animation, starfield, and neon glow effects.
This commit is contained in:
kert
2026-02-28 17:47:20 -05:00
parent 58cbb318be
commit 5d512ff2ed
8 changed files with 847 additions and 968 deletions

View File

@@ -511,7 +511,7 @@ stack/
├── gitea/
│ └── custom/
│ └── public/assets/css/
│ └── theme-nes-sports.css
│ └── theme-throwback.css
├── grafana/
│ ├── provisioning/
│ │ ├── dashboards/

View File

@@ -1,6 +1,22 @@
networks:
intrastack:
name: intrastack
gateway:
name: gateway
driver: bridge
internal: false
storage:
name: storage
driver: bridge
internal: true
data:
name: data
driver: bridge
internal: true
observability:
name: observability
driver: bridge
internal: true
ci:
name: ci
driver: bridge
internal: false
@@ -9,7 +25,9 @@ services:
image: traefik:v3.3
container_name: traefik
networks:
- intrastack
- gateway
- observability
- storage
ports:
- "80:80"
- "443:443"
@@ -30,7 +48,8 @@ services:
container_name: rustfs
user: "10001:10001"
networks:
- intrastack
- storage
- ci
environment:
- RUSTFS_ACCESS_KEY=${RUSTFS_ACCESS_KEY}
- RUSTFS_SECRET_KEY=${RUSTFS_SECRET_KEY}
@@ -45,7 +64,7 @@ services:
image: localhost:3000/homelab/postgresql:latest
container_name: postgres
networks:
- intrastack
- storage
environment:
- POSTGRESQL_PASSWORD=${POSTGRES_PASSWORD:-changeme}
- POSTGRESQL_DATABASE=gitea
@@ -59,7 +78,9 @@ services:
image: gitea/gitea:1.25.4-rootless
container_name: gitea
networks:
- intrastack
- gateway
- storage
- ci
environment:
- GITEA__database__DB_TYPE=postgres
- GITEA__database__HOST=postgres:5432
@@ -89,8 +110,8 @@ services:
- GITEA__server__ROOT_URL=http://gitea.${DOMAIN:-homelab.fhirworx.io}/
- GITEA__server__SSH_DOMAIN=${DOMAIN:-homelab.fhirworx.io}
- GITEA__webhook__ALLOWED_HOST_LIST=woodpecker-server,ci.${DOMAIN:-homelab.fhirworx.io},${HOST_IP:-192.168.1.192},172.19.0.0/16
- GITEA__ui__THEMES=gitea-auto,gitea-light,gitea-dark,nes-sports
- GITEA__ui__DEFAULT_THEME=nes-sports
- GITEA__ui__THEMES=gitea-auto,gitea-light,gitea-dark,throwback
- GITEA__ui__DEFAULT_THEME=throwback
volumes:
- gitea_data:/var/lib/gitea
- gitea_config:/etc/gitea
@@ -106,7 +127,9 @@ services:
image: woodpeckerci/woodpecker-server:v3.13.0
container_name: woodpecker-server
networks:
- intrastack
- gateway
- storage
- ci
extra_hosts:
- "gitea.${DOMAIN:-homelab.fhirworx.io}:${HOST_IP:-192.168.1.192}"
environment:
@@ -135,14 +158,14 @@ services:
image: woodpeckerci/woodpecker-agent:v3.13.0
container_name: woodpecker-agent
networks:
- intrastack
- ci
privileged: true
environment:
- WOODPECKER_SERVER=woodpecker-server:9000
- WOODPECKER_AGENT_SECRET=${WOODPECKER_AGENT_SECRET}
- WOODPECKER_MAX_WORKFLOWS=4
- WOODPECKER_BACKEND=docker
- WOODPECKER_BACKEND_DOCKER_NETWORK=intrastack
- WOODPECKER_BACKEND_DOCKER_NETWORK=ci
- DOCKER_API_VERSION=1.44
volumes:
- ${DOCKER_SOCK:-/run/user/1000/docker.sock}:/var/run/docker.sock
@@ -155,7 +178,7 @@ services:
image: localhost:3000/homelab/notebooks:latest
container_name: notebooks
networks:
- intrastack
- gateway
environment:
- PYTHONPATH=/home/kert/src
volumes:
@@ -179,7 +202,7 @@ services:
image: localhost:3000/homelab/zotero:latest
container_name: zotero
networks:
- intrastack
- gateway
runtime: nvidia
stdin_open: true
tty: true
@@ -216,7 +239,10 @@ services:
image: ghcr.io/projectnessie/nessie:latest
container_name: nessie
networks:
- intrastack
- gateway
- storage
- data
- observability
environment:
- NESSIE_VERSION_STORE_TYPE=JDBC
- QUARKUS_DATASOURCE_JDBC_URL=jdbc:postgresql://postgres:5432/nessie
@@ -244,7 +270,10 @@ services:
image: trinodb/trino:latest
container_name: trino
networks:
- intrastack
- gateway
- storage
- data
- observability
environment:
- NESSIE_S3_ACCESS_KEY=${NESSIE_S3_ACCESS_KEY}
- NESSIE_S3_SECRET_KEY=${NESSIE_S3_SECRET_KEY}
@@ -264,7 +293,10 @@ services:
image: apache/polaris:latest
container_name: polaris
networks:
- intrastack
- gateway
- storage
- data
- observability
environment:
# PostgreSQL backend
- POLARIS_PERSISTENCE_TYPE=relational-jdbc
@@ -295,11 +327,11 @@ services:
image: nginx:alpine
container_name: dashboard
networks:
- intrastack
- gateway
volumes:
- ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro
- ./nginx/index.html:/usr/share/nginx/html/index.html:ro
- ./nginx/nes-theme.css:/usr/share/nginx/html/nes-theme.css:ro
- ./nginx/throwback.css:/usr/share/nginx/html/throwback.css:ro
security_opt:
- no-new-privileges:true
restart: unless-stopped
@@ -309,7 +341,7 @@ services:
image: jaegertracing/all-in-one:latest
container_name: jaeger
networks:
- intrastack
- observability
environment:
- COLLECTOR_OTLP_ENABLED=true
- COLLECTOR_ZIPKIN_HOST_PORT=:9411
@@ -331,7 +363,7 @@ services:
image: grafana/loki:latest
container_name: loki
networks:
- intrastack
- observability
volumes:
- ./loki/loki-config.yml:/etc/loki/local-config.yaml:ro
- loki_data:/loki
@@ -354,7 +386,7 @@ services:
image: grafana/promtail:latest
container_name: promtail
networks:
- intrastack
- observability
volumes:
- ./loki/promtail-config.yml:/etc/promtail/config.yml:ro
- /home/kert/.local/share/docker/containers:/var/lib/docker/containers:ro
@@ -367,7 +399,8 @@ services:
image: prom/prometheus:latest
container_name: prometheus
networks:
- intrastack
- gateway
- observability
volumes:
- ./prometheus/prometheus.yml:/etc/prometheus/prometheus.yml:ro
- ./prometheus/targets:/etc/prometheus/targets:ro
@@ -394,7 +427,8 @@ services:
image: grafana/grafana:latest
container_name: grafana
networks:
- intrastack
- gateway
- observability
environment:
- GF_SECURITY_ADMIN_USER=admin
- GF_SECURITY_ADMIN_PASSWORD=${GF_ADMIN_PASSWORD:-admin}

View File

@@ -1,349 +0,0 @@
/* NES SPORTS THEME FOR GITEA - RBI Baseball / Tecmo Super Bowl */
@import url('https://fonts.googleapis.com/css2?family=Press+Start+2P&family=IBM+Plex+Mono:wght@400;500;600&display=swap');
/* Theme metadata */
gitea-theme-meta-info {
--theme-display-name: "NES Sports";
}
:root {
--is-dark-theme: true;
/* NES Color Palette */
--nes-black: #0f0f0f;
--nes-dark-gray: #1a1a2e;
--nes-gray: #4a4a68;
--nes-light-gray: #9090a8;
--nes-white: #f0f0f0;
--nes-dark-blue: #1a1a40;
--nes-blue: #2840a0;
--nes-mid-blue: #4060c0;
--nes-light-blue: #70a0e0;
--nes-sky: #90c0f0;
--nes-red: #d03030;
--nes-orange: #e07020;
--nes-yellow: #e0b040;
--nes-green: #40a040;
--nes-light-green: #80d080;
--nes-cyan: #40b0c0;
--nes-cream: #e8d8c0;
/* Gitea color mappings */
--color-primary: var(--nes-mid-blue);
--color-primary-light: var(--nes-light-blue);
--color-primary-dark: var(--nes-blue);
--color-primary-alpha-10: rgba(64, 96, 192, 0.1);
--color-primary-alpha-20: rgba(64, 96, 192, 0.2);
--color-primary-alpha-40: rgba(64, 96, 192, 0.4);
--color-secondary: var(--nes-gray);
--color-secondary-light: var(--nes-light-gray);
--color-secondary-dark: var(--nes-dark-gray);
/* Background colors */
--color-body: var(--nes-dark-blue);
--color-box-body: var(--nes-black);
--color-box-header: var(--nes-dark-gray);
/* Text colors */
--color-text: var(--nes-white);
--color-text-light: var(--nes-light-gray);
--color-text-dark: var(--nes-cream);
/* Input colors */
--color-input-text: var(--nes-white);
--color-input-background: var(--nes-dark-gray);
--color-input-border: var(--nes-gray);
/* Status colors */
--color-success: var(--nes-green);
--color-success-text: var(--nes-white);
--color-warning: var(--nes-yellow);
--color-warning-text: var(--nes-black);
--color-error: var(--nes-red);
--color-error-text: var(--nes-white);
--color-info: var(--nes-light-blue);
--color-info-text: var(--nes-black);
/* Border */
--color-border: var(--nes-gray);
/* Link colors */
--color-link: var(--nes-sky);
--color-link-hover: var(--nes-white);
/* Code */
--color-code-bg: var(--nes-dark-gray);
--color-code-border: var(--nes-gray);
/* Shadows */
--color-shadow: rgba(0, 0, 0, 0.4);
}
/* Global styles */
body {
font-family: 'IBM Plex Mono', monospace !important;
background: linear-gradient(180deg, #0a0a28 0%, #1a1a40 30%, #1a1a40 70%, #0a0a28 100%) !important;
background-attachment: fixed !important;
}
/* Headings */
h1, h2, h3, h4, h5, h6,
.header-wrapper .header {
font-family: 'Press Start 2P', monospace !important;
letter-spacing: 0.5px !important;
}
h1 { font-size: 18px !important; color: var(--nes-light-green) !important; }
h2 { font-size: 14px !important; color: var(--nes-sky) !important; }
h3 { font-size: 12px !important; color: var(--nes-yellow) !important; }
/* Navbar */
.ui.secondary.menu.navbar,
.navbar {
background: var(--nes-black) !important;
border-bottom: 3px solid var(--nes-gray) !important;
}
.navbar .item {
font-family: 'IBM Plex Mono', monospace !important;
color: var(--nes-white) !important;
}
.navbar .item:hover {
background: var(--nes-dark-gray) !important;
color: var(--nes-sky) !important;
}
.navbar .item.active {
background: var(--nes-blue) !important;
color: var(--nes-white) !important;
}
/* Buttons */
.ui.button,
.button {
font-family: 'IBM Plex Mono', monospace !important;
font-weight: 500 !important;
border-radius: 4px !important;
transition: all 0.15s ease !important;
}
.ui.primary.button,
.ui.blue.button {
background: var(--nes-mid-blue) !important;
color: var(--nes-white) !important;
border: 2px solid var(--nes-light-blue) !important;
}
.ui.primary.button:hover,
.ui.blue.button:hover {
background: var(--nes-light-blue) !important;
color: var(--nes-black) !important;
}
.ui.green.button {
background: var(--nes-green) !important;
color: var(--nes-white) !important;
border: 2px solid var(--nes-light-green) !important;
}
.ui.green.button:hover {
background: var(--nes-light-green) !important;
color: var(--nes-black) !important;
}
.ui.red.button {
background: var(--nes-red) !important;
color: var(--nes-white) !important;
}
/* Segments and boxes */
.ui.segment,
.ui.segments,
.repository,
.box {
background: var(--nes-black) !important;
border: 2px solid var(--nes-gray) !important;
border-radius: 4px !important;
}
.ui.attached.header,
.box-header {
background: var(--nes-dark-gray) !important;
border-bottom: 2px solid var(--nes-gray) !important;
font-family: 'Press Start 2P', monospace !important;
font-size: 11px !important;
color: var(--nes-cream) !important;
}
/* Tables */
.ui.table {
background: var(--nes-black) !important;
border: 2px solid var(--nes-gray) !important;
}
.ui.table th {
background: var(--nes-blue) !important;
color: var(--nes-white) !important;
font-family: 'Press Start 2P', monospace !important;
font-size: 10px !important;
text-transform: uppercase !important;
border: 1px solid var(--nes-mid-blue) !important;
}
.ui.table td {
background: var(--nes-black) !important;
color: var(--nes-cream) !important;
border: 1px solid var(--nes-dark-gray) !important;
}
.ui.table tr:hover td {
background: var(--nes-dark-blue) !important;
}
/* Forms */
.ui.form input,
.ui.form textarea,
.ui.form select,
.ui.input input {
font-family: 'IBM Plex Mono', monospace !important;
background: var(--nes-dark-gray) !important;
color: var(--nes-white) !important;
border: 2px solid var(--nes-gray) !important;
border-radius: 4px !important;
}
.ui.form input:focus,
.ui.form textarea:focus,
.ui.input input:focus {
border-color: var(--nes-light-blue) !important;
background: var(--nes-dark-gray) !important;
}
/* Labels */
.ui.label {
font-family: 'IBM Plex Mono', monospace !important;
border-radius: 4px !important;
}
/* Dropdowns */
.ui.dropdown .menu {
background: var(--nes-dark-gray) !important;
border: 2px solid var(--nes-gray) !important;
}
.ui.dropdown .menu .item {
color: var(--nes-white) !important;
}
.ui.dropdown .menu .item:hover {
background: var(--nes-blue) !important;
}
/* Code and diffs */
.code-view,
.file-view,
.CodeMirror,
.highlight {
font-family: 'IBM Plex Mono', monospace !important;
background: var(--nes-black) !important;
}
.diff-file-box .diff-file-header {
background: var(--nes-dark-gray) !important;
}
/* Commit graph */
.repository .commit-list .commit {
border-bottom: 1px solid var(--nes-dark-gray) !important;
}
/* Issues and PRs */
.issue-list .item,
.pull-list .item {
border-bottom: 1px solid var(--nes-dark-gray) !important;
}
/* Footer */
.footer {
background: var(--nes-black) !important;
border-top: 3px solid var(--nes-gray) !important;
}
/* Cards */
.ui.card,
.ui.cards .card {
background: var(--nes-black) !important;
border: 2px solid var(--nes-gray) !important;
border-radius: 4px !important;
}
/* Messages */
.ui.message {
font-family: 'IBM Plex Mono', monospace !important;
border-radius: 4px !important;
}
.ui.positive.message,
.ui.success.message {
background: rgba(64, 160, 64, 0.2) !important;
border: 2px solid var(--nes-green) !important;
color: var(--nes-light-green) !important;
}
.ui.negative.message,
.ui.error.message {
background: rgba(208, 48, 48, 0.2) !important;
border: 2px solid var(--nes-red) !important;
color: var(--nes-red) !important;
}
.ui.warning.message {
background: rgba(224, 176, 64, 0.2) !important;
border: 2px solid var(--nes-yellow) !important;
color: var(--nes-yellow) !important;
}
.ui.info.message {
background: rgba(112, 160, 224, 0.2) !important;
border: 2px solid var(--nes-light-blue) !important;
color: var(--nes-light-blue) !important;
}
/* Scrollbars */
::-webkit-scrollbar {
width: 12px;
height: 12px;
}
::-webkit-scrollbar-track {
background: var(--nes-dark-gray);
}
::-webkit-scrollbar-thumb {
background: var(--nes-gray);
border-radius: 6px;
}
::-webkit-scrollbar-thumb:hover {
background: var(--nes-light-gray);
}
/* Selection */
::selection {
background: var(--nes-blue) !important;
color: var(--nes-white) !important;
}
/* Links */
a {
color: var(--nes-sky) !important;
}
a:hover {
color: var(--nes-white) !important;
}

View File

@@ -0,0 +1,346 @@
/* THROWBACK THEME FOR GITEA - Tecmo Super Bowl */
@import url('https://fonts.googleapis.com/css2?family=Press+Start+2P&family=IBM+Plex+Mono:wght@400;500;600&display=swap');
/* Theme metadata */
gitea-theme-meta-info {
--theme-display-name: "Throwback";
}
:root {
--is-dark-theme: true;
/* Throwback Color Palette */
--tb-navy: #0a1628;
--tb-navy-light: #162040;
--tb-navy-mid: #1e2d50;
--tb-slate: #2a3f5f;
--tb-steel: #5a7090;
--tb-white: #f0f0f0;
--tb-blue: #3366cc;
--tb-blue-light: #5588dd;
--tb-blue-bright: #88aaee;
--tb-red: #cc2244;
--tb-red-light: #dd4466;
--tb-green: #44aa44;
--tb-green-light: #66cc66;
--tb-yellow: #ddaa33;
/* Gitea color mappings */
--color-primary: var(--tb-blue);
--color-primary-light: var(--tb-blue-light);
--color-primary-dark: #2255aa;
--color-primary-alpha-10: rgba(51, 102, 204, 0.1);
--color-primary-alpha-20: rgba(51, 102, 204, 0.2);
--color-primary-alpha-40: rgba(51, 102, 204, 0.4);
--color-secondary: var(--tb-slate);
--color-secondary-light: var(--tb-steel);
--color-secondary-dark: var(--tb-navy-light);
/* Background colors */
--color-body: var(--tb-navy);
--color-box-body: var(--tb-navy);
--color-box-header: var(--tb-navy-light);
/* Text colors */
--color-text: var(--tb-white);
--color-text-light: var(--tb-steel);
--color-text-dark: var(--tb-white);
/* Input colors */
--color-input-text: var(--tb-white);
--color-input-background: var(--tb-navy-mid);
--color-input-border: var(--tb-slate);
/* Status colors */
--color-success: var(--tb-green);
--color-success-text: var(--tb-white);
--color-warning: var(--tb-yellow);
--color-warning-text: var(--tb-navy);
--color-error: var(--tb-red);
--color-error-text: var(--tb-white);
--color-info: var(--tb-blue-light);
--color-info-text: var(--tb-white);
/* Border */
--color-border: var(--tb-slate);
/* Link colors */
--color-link: var(--tb-blue-bright);
--color-link-hover: var(--tb-white);
/* Code */
--color-code-bg: var(--tb-navy-light);
--color-code-border: var(--tb-slate);
/* Shadows */
--color-shadow: rgba(0, 0, 0, 0.4);
}
/* Global styles */
body {
font-family: 'IBM Plex Mono', monospace !important;
background: var(--tb-navy) !important;
}
/* Headings */
h1, h2, h3, h4, h5, h6,
.header-wrapper .header {
font-family: 'Press Start 2P', monospace !important;
letter-spacing: 0.5px !important;
}
h1 { font-size: 18px !important; color: var(--tb-white) !important; }
h2 { font-size: 14px !important; color: var(--tb-blue-bright) !important; }
h3 { font-size: 12px !important; color: var(--tb-white) !important; }
/* Navbar */
.ui.secondary.menu.navbar,
.navbar {
background: var(--tb-navy-light) !important;
border-bottom: 3px solid var(--tb-slate) !important;
}
.navbar .item {
font-family: 'IBM Plex Mono', monospace !important;
color: var(--tb-white) !important;
}
.navbar .item:hover {
background: var(--tb-navy-mid) !important;
color: var(--tb-blue-bright) !important;
}
.navbar .item.active {
background: var(--tb-blue) !important;
color: var(--tb-white) !important;
}
/* Buttons */
.ui.button,
.button {
font-family: 'IBM Plex Mono', monospace !important;
font-weight: 500 !important;
border-radius: 4px !important;
transition: all 0.15s ease !important;
}
.ui.primary.button,
.ui.blue.button {
background: var(--tb-blue) !important;
color: var(--tb-white) !important;
border: 2px solid var(--tb-blue-light) !important;
}
.ui.primary.button:hover,
.ui.blue.button:hover {
background: var(--tb-blue-light) !important;
color: var(--tb-white) !important;
}
.ui.green.button {
background: var(--tb-green) !important;
color: var(--tb-white) !important;
border: 2px solid var(--tb-green-light) !important;
}
.ui.green.button:hover {
background: var(--tb-green-light) !important;
color: var(--tb-navy) !important;
}
.ui.red.button {
background: var(--tb-red) !important;
color: var(--tb-white) !important;
}
/* Segments and boxes */
.ui.segment,
.ui.segments,
.repository,
.box {
background: var(--tb-navy) !important;
border: 2px solid var(--tb-slate) !important;
border-radius: 4px !important;
}
.ui.attached.header,
.box-header {
background: var(--tb-navy-light) !important;
border-bottom: 2px solid var(--tb-slate) !important;
font-family: 'Press Start 2P', monospace !important;
font-size: 11px !important;
color: var(--tb-white) !important;
}
/* Tables */
.ui.table {
background: var(--tb-navy) !important;
border: 2px solid var(--tb-slate) !important;
}
.ui.table th {
background: var(--tb-blue) !important;
color: var(--tb-white) !important;
font-family: 'Press Start 2P', monospace !important;
font-size: 10px !important;
text-transform: uppercase !important;
border: 1px solid var(--tb-blue-light) !important;
}
.ui.table td {
background: var(--tb-navy) !important;
color: var(--tb-white) !important;
border: 1px solid var(--tb-navy-light) !important;
}
.ui.table tr:hover td {
background: rgba(51, 102, 204, 0.2) !important;
}
/* Forms */
.ui.form input,
.ui.form textarea,
.ui.form select,
.ui.input input {
font-family: 'IBM Plex Mono', monospace !important;
background: var(--tb-navy-mid) !important;
color: var(--tb-white) !important;
border: 2px solid var(--tb-slate) !important;
border-radius: 4px !important;
}
.ui.form input:focus,
.ui.form textarea:focus,
.ui.input input:focus {
border-color: var(--tb-blue-light) !important;
background: var(--tb-navy-mid) !important;
}
/* Labels */
.ui.label {
font-family: 'IBM Plex Mono', monospace !important;
border-radius: 4px !important;
}
/* Dropdowns */
.ui.dropdown .menu {
background: var(--tb-navy-light) !important;
border: 2px solid var(--tb-slate) !important;
}
.ui.dropdown .menu .item {
color: var(--tb-white) !important;
}
.ui.dropdown .menu .item:hover {
background: var(--tb-blue) !important;
}
/* Code and diffs */
.code-view,
.file-view,
.CodeMirror,
.highlight {
font-family: 'IBM Plex Mono', monospace !important;
background: var(--tb-navy) !important;
}
.diff-file-box .diff-file-header {
background: var(--tb-navy-light) !important;
}
/* Commit graph */
.repository .commit-list .commit {
border-bottom: 1px solid var(--tb-navy-light) !important;
}
/* Issues and PRs */
.issue-list .item,
.pull-list .item {
border-bottom: 1px solid var(--tb-navy-light) !important;
}
/* Footer */
.footer {
background: var(--tb-navy-light) !important;
border-top: 3px solid var(--tb-slate) !important;
}
/* Cards */
.ui.card,
.ui.cards .card {
background: var(--tb-navy) !important;
border: 2px solid var(--tb-slate) !important;
border-radius: 4px !important;
}
/* Messages */
.ui.message {
font-family: 'IBM Plex Mono', monospace !important;
border-radius: 4px !important;
}
.ui.positive.message,
.ui.success.message {
background: rgba(68, 170, 68, 0.2) !important;
border: 2px solid var(--tb-green) !important;
color: var(--tb-green-light) !important;
}
.ui.negative.message,
.ui.error.message {
background: rgba(204, 34, 68, 0.2) !important;
border: 2px solid var(--tb-red) !important;
color: var(--tb-red-light) !important;
}
.ui.warning.message {
background: rgba(221, 170, 51, 0.2) !important;
border: 2px solid var(--tb-yellow) !important;
color: var(--tb-yellow) !important;
}
.ui.info.message {
background: rgba(85, 136, 221, 0.2) !important;
border: 2px solid var(--tb-blue-light) !important;
color: var(--tb-blue-bright) !important;
}
/* Scrollbars */
::-webkit-scrollbar {
width: 12px;
height: 12px;
}
::-webkit-scrollbar-track {
background: var(--tb-navy-light);
}
::-webkit-scrollbar-thumb {
background: var(--tb-slate);
border-radius: 6px;
}
::-webkit-scrollbar-thumb:hover {
background: var(--tb-steel);
}
/* Selection */
::selection {
background: var(--tb-blue) !important;
color: var(--tb-white) !important;
}
/* Links */
a {
color: var(--tb-blue-bright) !important;
}
a:hover {
color: var(--tb-white) !important;
}

View File

@@ -4,7 +4,7 @@
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>HOMELAB COMMAND CENTER</title>
<link href="https://fonts.googleapis.com/css2?family=Press+Start+2P&display=swap" rel="stylesheet">
<link href="https://fonts.googleapis.com/css2?family=Press+Start+2P&family=IBM+Plex+Mono:wght@400;500;600&display=swap" rel="stylesheet">
<style>
* {
margin: 0;
@@ -12,53 +12,27 @@
box-sizing: border-box;
}
:root {
--tb-navy: #0a1628;
--tb-navy-light: #162040;
--tb-navy-mid: #1e2d50;
--tb-slate: #2a3f5f;
--tb-steel: #5a7090;
--tb-white: #f0f0f0;
--tb-blue: #3366cc;
--tb-blue-light: #5588dd;
--tb-blue-bright: #88aaee;
--tb-red: #cc2244;
--tb-red-light: #dd4466;
--tb-green: #44aa44;
}
body {
font-family: 'Press Start 2P', monospace;
background: #0a0a0a;
font-family: 'IBM Plex Mono', 'Courier New', monospace;
background: var(--tb-navy);
min-height: 100vh;
overflow-x: hidden;
position: relative;
}
/* CRT scanline effect */
body::before {
content: "";
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: repeating-linear-gradient(
0deg,
rgba(0, 0, 0, 0.15),
rgba(0, 0, 0, 0.15) 1px,
transparent 1px,
transparent 2px
);
pointer-events: none;
z-index: 1000;
}
/* Animated starfield background */
.stars {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: -1;
}
.star {
position: absolute;
background: #fff;
border-radius: 50%;
animation: twinkle 2s infinite;
}
@keyframes twinkle {
0%, 100% { opacity: 0.3; }
50% { opacity: 1; }
color: var(--tb-white);
}
.container {
@@ -70,36 +44,23 @@
header {
text-align: center;
padding: 40px 20px;
position: relative;
border-bottom: 4px solid var(--tb-red);
}
h1 {
font-family: 'Press Start 2P', monospace;
font-size: 2rem;
color: #00ff00;
text-shadow:
0 0 10px #00ff00,
0 0 20px #00ff00,
0 0 40px #00ff00,
4px 4px 0 #003300;
animation: glow 1.5s ease-in-out infinite alternate;
letter-spacing: 4px;
}
@keyframes glow {
from { text-shadow: 0 0 10px #00ff00, 0 0 20px #00ff00, 0 0 40px #00ff00, 4px 4px 0 #003300; }
to { text-shadow: 0 0 20px #00ff00, 0 0 40px #00ff00, 0 0 60px #00ff00, 4px 4px 0 #003300; }
color: var(--tb-white);
letter-spacing: 6px;
text-shadow: 3px 3px 0 var(--tb-navy-mid);
}
.subtitle {
color: #ff00ff;
font-size: 0.6rem;
font-family: 'Press Start 2P', monospace;
color: var(--tb-blue-bright);
font-size: 0.55rem;
margin-top: 15px;
text-shadow: 0 0 10px #ff00ff;
animation: blink 1s step-end infinite;
}
@keyframes blink {
50% { opacity: 0.7; }
letter-spacing: 2px;
}
.score-bar {
@@ -107,219 +68,141 @@
justify-content: center;
gap: 40px;
margin-top: 20px;
font-family: 'Press Start 2P', monospace;
font-size: 0.5rem;
}
.score-item {
color: #ffff00;
text-shadow: 0 0 5px #ffff00;
color: var(--tb-white);
background: var(--tb-blue);
padding: 6px 14px;
border: 2px solid var(--tb-blue-light);
}
.grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
gap: 25px;
padding: 20px;
gap: 20px;
padding: 20px 0;
margin-top: 20px;
}
.tile {
background: linear-gradient(135deg, #1a1a2e 0%, #16213e 100%);
border: 4px solid;
border-radius: 8px;
background: var(--tb-navy-light);
border: 3px solid var(--tb-slate);
padding: 25px;
position: relative;
overflow: hidden;
transition: all 0.3s ease;
transition: all 0.2s ease;
cursor: pointer;
text-decoration: none;
display: block;
}
.tile::before {
content: "";
position: absolute;
top: 0;
left: -100%;
width: 100%;
height: 100%;
background: linear-gradient(90deg, transparent, rgba(255,255,255,0.1), transparent);
transition: left 0.5s;
}
.tile:hover::before {
left: 100%;
}
.tile:hover {
transform: translateY(-5px) scale(1.02);
box-shadow: 0 10px 40px rgba(0, 0, 0, 0.5);
border-color: var(--tb-blue);
background: var(--tb-navy-mid);
}
/* Service-specific colors */
.tile.storage { border-color: #ff6b6b; }
.tile.storage .icon { color: #ff6b6b; text-shadow: 0 0 20px #ff6b6b; }
.tile.storage:hover { background: linear-gradient(135deg, #2a1a1a 0%, #1e1616 100%); }
.tile.database { border-color: #4ecdc4; }
.tile.database .icon { color: #4ecdc4; text-shadow: 0 0 20px #4ecdc4; }
.tile.database:hover { background: linear-gradient(135deg, #1a2a2a 0%, #162121 100%); }
.tile.git { border-color: #f7b731; }
.tile.git .icon { color: #f7b731; text-shadow: 0 0 20px #f7b731; }
.tile.git:hover { background: linear-gradient(135deg, #2a2a1a 0%, #212116 100%); }
.tile.ci { border-color: #a55eea; }
.tile.ci .icon { color: #a55eea; text-shadow: 0 0 20px #a55eea; }
.tile.ci:hover { background: linear-gradient(135deg, #2a1a2a 0%, #211621 100%); }
.tile.notebook { border-color: #26de81; }
.tile.notebook .icon { color: #26de81; text-shadow: 0 0 20px #26de81; }
.tile.notebook:hover { background: linear-gradient(135deg, #1a2a1a 0%, #162116 100%); }
.tile.research { border-color: #fd79a8; }
.tile.research .icon { color: #fd79a8; text-shadow: 0 0 20px #fd79a8; }
.tile.research:hover { background: linear-gradient(135deg, #2a1a22 0%, #21161a 100%); }
.tile.catalog { border-color: #00cec9; }
.tile.catalog .icon { color: #00cec9; text-shadow: 0 0 20px #00cec9; }
.tile.catalog:hover { background: linear-gradient(135deg, #1a2a2a 0%, #162121 100%); }
.tile.query { border-color: #6c5ce7; }
.tile.query .icon { color: #6c5ce7; text-shadow: 0 0 20px #6c5ce7; }
.tile.query:hover { background: linear-gradient(135deg, #1a1a2a 0%, #161621 100%); }
.tile.observability { border-color: #e17055; }
.tile.observability .icon { color: #e17055; text-shadow: 0 0 20px #e17055; }
.tile.observability:hover { background: linear-gradient(135deg, #2a1a1a 0%, #211616 100%); }
.tile.metrics { border-color: #fdcb6e; }
.tile.metrics .icon { color: #fdcb6e; text-shadow: 0 0 20px #fdcb6e; }
.tile.metrics:hover { background: linear-gradient(135deg, #2a2a1a 0%, #212116 100%); }
.tile.logs { border-color: #74b9ff; }
.tile.logs .icon { color: #74b9ff; text-shadow: 0 0 20px #74b9ff; }
.tile.logs:hover { background: linear-gradient(135deg, #1a1a2a 0%, #161621 100%); }
.tile.proxy { border-color: #55efc4; }
.tile.proxy .icon { color: #55efc4; text-shadow: 0 0 20px #55efc4; }
.tile.proxy:hover { background: linear-gradient(135deg, #1a2a2a 0%, #162121 100%); }
.icon {
/* All tile variants use the same base style */
.tile .icon {
font-size: 2.5rem;
margin-bottom: 15px;
display: block;
}
.tile.storage .icon,
.tile.database .icon { color: var(--tb-red); }
.tile.git .icon { color: var(--tb-blue-bright); }
.tile.ci .icon { color: var(--tb-blue-light); }
.tile.notebook .icon,
.tile.research .icon { color: var(--tb-white); }
.tile.catalog .icon { color: var(--tb-blue-bright); }
.tile.query .icon { color: var(--tb-blue-light); }
.tile.observability .icon { color: var(--tb-red-light); }
.tile.metrics .icon { color: var(--tb-blue-bright); }
.tile.logs .icon { color: var(--tb-blue-light); }
.tile.proxy .icon { color: var(--tb-white); }
.tile-title {
font-size: 0.8rem;
color: #fff;
font-family: 'Press Start 2P', monospace;
font-size: 0.7rem;
color: var(--tb-white);
margin-bottom: 10px;
text-shadow: 2px 2px 0 #000;
letter-spacing: 1px;
}
.tile-desc {
font-size: 0.5rem;
color: #888;
line-height: 1.8;
font-size: 0.75rem;
color: var(--tb-steel);
line-height: 1.6;
margin-bottom: 15px;
}
.tile-port {
font-size: 0.45rem;
color: #00ff00;
background: rgba(0, 255, 0, 0.1);
padding: 5px 10px;
border-radius: 4px;
font-size: 0.7rem;
color: var(--tb-blue-bright);
display: inline-block;
border: 1px solid #00ff00;
}
.status {
position: absolute;
top: 15px;
right: 15px;
width: 12px;
height: 12px;
background: #00ff00;
width: 10px;
height: 10px;
background: var(--tb-green);
border-radius: 50%;
animation: pulse 2s infinite;
box-shadow: 0 0 10px #00ff00;
}
@keyframes pulse {
0%, 100% { transform: scale(1); opacity: 1; }
50% { transform: scale(1.2); opacity: 0.7; }
}
.pixel-border {
.stripe {
position: absolute;
bottom: 0;
left: 0;
right: 0;
height: 4px;
background: repeating-linear-gradient(
90deg,
currentColor 0px,
currentColor 4px,
transparent 4px,
transparent 8px
);
opacity: 0.5;
height: 3px;
background: var(--tb-red);
}
footer {
text-align: center;
padding: 40px;
color: #444;
font-size: 0.4rem;
color: var(--tb-slate);
font-size: 0.7rem;
border-top: 3px solid var(--tb-slate);
margin-top: 20px;
}
footer span {
color: #ff0000;
animation: heart 1s infinite;
display: inline-block;
color: var(--tb-red);
}
@keyframes heart {
0%, 100% { transform: scale(1); }
50% { transform: scale(1.3); }
}
/* Power-up badges */
/* Badges */
.badge {
position: absolute;
top: -8px;
top: -1px;
left: 20px;
background: #ff0000;
color: #fff;
font-size: 0.4rem;
font-family: 'Press Start 2P', monospace;
color: var(--tb-white);
font-size: 0.35rem;
padding: 4px 8px;
border-radius: 4px;
text-transform: uppercase;
animation: bounce 0.5s ease infinite alternate;
letter-spacing: 1px;
}
@keyframes bounce {
from { transform: translateY(0); }
to { transform: translateY(-3px); }
}
.badge.new { background: #ff0000; }
.badge.gpu { background: #ff6b00; }
.badge.data { background: #0066ff; }
.badge.new { background: var(--tb-red); }
.badge.gpu { background: var(--tb-blue); }
.badge.data { background: var(--tb-blue); }
</style>
</head>
<body>
<div class="stars" id="stars"></div>
<div class="container">
<header>
<h1>HOMELAB</h1>
<p class="subtitle">[ PRESS START TO CONTINUE ]</p>
<p class="subtitle">COMMAND CENTER</p>
<div class="score-bar">
<span class="score-item">SERVICES: 13</span>
<span class="score-item">STATUS: ONLINE</span>
<span class="score-item">LEVEL: MAX</span>
</div>
</header>
@@ -331,7 +214,7 @@
<h2 class="tile-title">TRAEFIK</h2>
<p class="tile-desc">Cloud-native reverse proxy and load balancer</p>
<span class="tile-port"></span>
<div class="pixel-border" style="color: #55efc4;"></div>
<div class="stripe"></div>
</a>
<a data-subdomain="s3console" target="_blank" class="tile storage">
@@ -340,7 +223,7 @@
<h2 class="tile-title">RUSTFS</h2>
<p class="tile-desc">S3-compatible object storage for all your data needs</p>
<span class="tile-port"></span>
<div class="pixel-border" style="color: #ff6b6b;"></div>
<div class="stripe"></div>
</a>
<a data-subdomain="gitea" target="_blank" class="tile git">
@@ -349,7 +232,7 @@
<h2 class="tile-title">GITEA</h2>
<p class="tile-desc">Self-hosted Git service with packages & CI</p>
<span class="tile-port"></span>
<div class="pixel-border" style="color: #f7b731;"></div>
<div class="stripe"></div>
</a>
<a data-subdomain="ci" target="_blank" class="tile ci">
@@ -358,7 +241,7 @@
<h2 class="tile-title">WOODPECKER</h2>
<p class="tile-desc">Simple yet powerful CI/CD engine</p>
<span class="tile-port"></span>
<div class="pixel-border" style="color: #a55eea;"></div>
<div class="stripe"></div>
</a>
<a data-subdomain="notebooks" target="_blank" class="tile notebook">
@@ -368,7 +251,7 @@
<h2 class="tile-title">NOTEBOOKS</h2>
<p class="tile-desc">Marimo reactive notebooks with CUDA</p>
<span class="tile-port"></span>
<div class="pixel-border" style="color: #26de81;"></div>
<div class="stripe"></div>
</a>
<a data-subdomain="zotero" target="_blank" class="tile research">
@@ -378,7 +261,7 @@
<h2 class="tile-title">ZOTERO</h2>
<p class="tile-desc">Research reference manager in the cloud</p>
<span class="tile-port"></span>
<div class="pixel-border" style="color: #fd79a8;"></div>
<div class="stripe"></div>
</a>
<a data-subdomain="nessie" data-path="/api/v2/config" target="_blank" class="tile catalog">
@@ -388,7 +271,7 @@
<h2 class="tile-title">NESSIE</h2>
<p class="tile-desc">Git-like versioned data catalog for Iceberg</p>
<span class="tile-port"></span>
<div class="pixel-border" style="color: #00cec9;"></div>
<div class="stripe"></div>
</a>
<a data-subdomain="trino" target="_blank" class="tile query">
@@ -398,7 +281,7 @@
<h2 class="tile-title">TRINO</h2>
<p class="tile-desc">Distributed SQL query engine for the lakehouse</p>
<span class="tile-port"></span>
<div class="pixel-border" style="color: #6c5ce7;"></div>
<div class="stripe"></div>
</a>
<a data-subdomain="polaris" target="_blank" class="tile catalog">
@@ -408,7 +291,7 @@
<h2 class="tile-title">POLARIS</h2>
<p class="tile-desc">Apache Iceberg catalog with governance & Unity-style access</p>
<span class="tile-port"></span>
<div class="pixel-border" style="color: #00cec9;"></div>
<div class="stripe"></div>
</a>
<a data-subdomain="grafana" target="_blank" class="tile observability">
@@ -417,7 +300,7 @@
<h2 class="tile-title">GRAFANA</h2>
<p class="tile-desc">Observability dashboards for logs and traces</p>
<span class="tile-port"></span>
<div class="pixel-border" style="color: #e17055;"></div>
<div class="stripe"></div>
</a>
<a data-subdomain="jaeger" target="_blank" class="tile metrics">
@@ -426,7 +309,7 @@
<h2 class="tile-title">JAEGER</h2>
<p class="tile-desc">Distributed tracing for microservices</p>
<span class="tile-port"></span>
<div class="pixel-border" style="color: #fdcb6e;"></div>
<div class="stripe"></div>
</a>
<a data-subdomain="prometheus" target="_blank" class="tile logs">
@@ -435,7 +318,7 @@
<h2 class="tile-title">PROMETHEUS</h2>
<p class="tile-desc">Metrics collection and alerting</p>
<span class="tile-port"></span>
<div class="pixel-border" style="color: #74b9ff;"></div>
<div class="stripe"></div>
</a>
<a data-subdomain="loki" data-path="/ready" target="_blank" class="tile database">
@@ -444,7 +327,7 @@
<h2 class="tile-title">LOKI</h2>
<p class="tile-desc">Log aggregation system by Grafana</p>
<span class="tile-port"></span>
<div class="pixel-border" style="color: #4ecdc4;"></div>
<div class="stripe"></div>
</a>
</div>
@@ -454,9 +337,9 @@
</div>
<script>
// ── Domain resolution: derive base domain from current hostname ──
// dashboard.homelab.fhirworx.io → homelab.fhirworx.io
// homelab.fhirworx.io → homelab.fhirworx.io
// Domain resolution: derive base domain from current hostname
// dashboard.homelab.fhirworx.io -> homelab.fhirworx.io
// homelab.fhirworx.io -> homelab.fhirworx.io
(function() {
var base = location.hostname.replace(/^dashboard\./, '');
document.querySelectorAll('[data-subdomain]').forEach(function(el) {
@@ -468,28 +351,6 @@
if (port) port.textContent = fqdn;
});
})();
// Generate starfield
var starsContainer = document.getElementById('stars');
for (var i = 0; i < 100; i++) {
var star = document.createElement('div');
star.className = 'star';
star.style.left = Math.random() * 100 + '%';
star.style.top = Math.random() * 100 + '%';
star.style.width = Math.random() * 3 + 1 + 'px';
star.style.height = star.style.width;
star.style.animationDelay = Math.random() * 2 + 's';
starsContainer.appendChild(star);
}
// Add click sound effect (visual feedback)
document.querySelectorAll('.tile').forEach(function(tile) {
tile.addEventListener('click', function(e) {
this.style.transform = 'scale(0.95)';
var self = this;
setTimeout(function() { self.style.transform = ''; }, 100);
});
});
</script>
</body>
</html>

View File

@@ -1,4 +1,5 @@
/* NES SPORTS THEME - Universal Injectable CSS
/* THROWBACK THEME - Universal Injectable CSS
Tecmo Super Bowl inspired: deep navy, white text, patriotic red/blue
Injected via Traefik rewrite-body plugin into:
Grafana, Traefik Dashboard, RustFS/MinIO, Jaeger, Prometheus, Trino
Palette matches: Gitea, Woodpecker, Marimo, nginx dashboard themes */
@@ -6,28 +7,26 @@
@import url('https://fonts.googleapis.com/css2?family=Press+Start+2P&family=IBM+Plex+Mono:wght@400;500;600&display=swap');
/* ============================================
NES COLOR PALETTE
THROWBACK COLOR PALETTE
============================================ */
:root {
--nes-black: #0f0f0f;
--nes-dark-gray: #1a1a2e;
--nes-gray: #4a4a68;
--nes-light-gray: #9090a8;
--nes-white: #f0f0f0;
--tb-navy: #0a1628;
--tb-navy-light: #162040;
--tb-navy-mid: #1e2d50;
--tb-slate: #2a3f5f;
--tb-steel: #5a7090;
--tb-white: #f0f0f0;
--nes-dark-blue: #1a1a40;
--nes-blue: #2840a0;
--nes-mid-blue: #4060c0;
--nes-light-blue: #70a0e0;
--nes-sky: #90c0f0;
--tb-blue: #3366cc;
--tb-blue-light: #5588dd;
--tb-blue-bright: #88aaee;
--nes-red: #d03030;
--nes-orange: #e07020;
--nes-yellow: #e0b040;
--nes-green: #40a040;
--nes-light-green: #80d080;
--nes-cyan: #40b0c0;
--nes-cream: #e8d8c0;
--tb-red: #cc2244;
--tb-red-light: #dd4466;
--tb-green: #44aa44;
--tb-green-light: #66cc66;
--tb-yellow: #ddaa33;
}
/* ============================================
@@ -35,9 +34,8 @@
============================================ */
body, html {
font-family: 'IBM Plex Mono', 'Courier New', monospace !important;
background: linear-gradient(180deg, #0a0a28 0%, #1a1a40 30%, #1a1a40 70%, #0a0a28 100%) !important;
background-attachment: fixed !important;
color: var(--nes-white) !important;
background: var(--tb-navy) !important;
color: var(--tb-white) !important;
}
/* ============================================
@@ -51,19 +49,19 @@ h1, h2, h3, h4, h5, h6 {
h1 {
font-size: 16px !important;
color: var(--nes-light-green) !important;
color: var(--tb-white) !important;
}
h2 {
font-size: 13px !important;
color: var(--nes-sky) !important;
color: var(--tb-blue-bright) !important;
}
h3 {
font-size: 11px !important;
color: var(--nes-yellow) !important;
color: var(--tb-white) !important;
}
h4, h5, h6 {
font-size: 10px !important;
color: var(--nes-cream) !important;
color: var(--tb-white) !important;
}
/* ============================================
@@ -77,9 +75,9 @@ header,
[class*="topbar"],
[class*="top-bar"],
[class*="app-bar"] {
background: var(--nes-dark-gray) !important;
border-bottom: 3px solid var(--nes-gray) !important;
color: var(--nes-white) !important;
background: var(--tb-navy-light) !important;
border-bottom: 3px solid var(--tb-slate) !important;
color: var(--tb-white) !important;
}
/* ============================================
@@ -91,9 +89,9 @@ aside,
[class*="side-nav"],
[class*="drawer"],
[class*="menu-panel"] {
background: var(--nes-black) !important;
border-right: 3px solid var(--nes-gray) !important;
color: var(--nes-white) !important;
background: var(--tb-navy) !important;
border-right: 3px solid var(--tb-slate) !important;
color: var(--tb-white) !important;
}
/* ============================================
@@ -105,30 +103,30 @@ table {
}
th {
background: var(--nes-blue) !important;
color: var(--nes-white) !important;
background: var(--tb-blue) !important;
color: var(--tb-white) !important;
font-family: 'Press Start 2P', monospace !important;
font-size: 9px !important;
padding: 10px 12px !important;
text-transform: uppercase !important;
letter-spacing: 1px !important;
border: 1px solid var(--nes-mid-blue) !important;
border: 1px solid var(--tb-blue-light) !important;
}
td {
background: var(--nes-black) !important;
color: var(--nes-cream) !important;
background: var(--tb-navy) !important;
color: var(--tb-white) !important;
padding: 8px 12px !important;
border: 1px solid var(--nes-dark-gray) !important;
border: 1px solid var(--tb-navy-light) !important;
font-size: 13px !important;
}
tr:nth-child(even) td {
background: var(--nes-dark-gray) !important;
background: var(--tb-navy-light) !important;
}
tr:hover td {
background: rgba(40, 64, 160, 0.3) !important;
background: rgba(51, 102, 204, 0.2) !important;
}
/* ============================================
@@ -140,9 +138,9 @@ button,
[class*="button"] {
font-family: 'IBM Plex Mono', monospace !important;
font-weight: 500 !important;
background: var(--nes-dark-gray) !important;
color: var(--nes-white) !important;
border: 2px solid var(--nes-gray) !important;
background: var(--tb-navy-light) !important;
color: var(--tb-white) !important;
border: 2px solid var(--tb-slate) !important;
border-radius: 4px !important;
transition: all 0.15s ease !important;
}
@@ -151,8 +149,8 @@ button:hover,
[role="button"]:hover,
.btn:hover,
[class*="button"]:hover {
background: var(--nes-blue) !important;
border-color: var(--nes-light-blue) !important;
background: var(--tb-blue) !important;
border-color: var(--tb-blue-light) !important;
}
button:active,
@@ -165,14 +163,14 @@ button:active,
button[type="submit"],
[class*="primary"],
[class*="submit"] {
background: var(--nes-green) !important;
color: var(--nes-black) !important;
border: 2px solid var(--nes-light-green) !important;
background: var(--tb-blue) !important;
color: var(--tb-white) !important;
border: 2px solid var(--tb-blue-light) !important;
}
button[type="submit"]:hover,
[class*="primary"]:hover {
background: var(--nes-light-green) !important;
background: var(--tb-blue-light) !important;
}
/* ============================================
@@ -184,18 +182,18 @@ select,
[class*="input"],
[contenteditable="true"] {
font-family: 'IBM Plex Mono', monospace !important;
background: var(--nes-dark-gray) !important;
color: var(--nes-white) !important;
border: 2px solid var(--nes-gray) !important;
background: var(--tb-navy-mid) !important;
color: var(--tb-white) !important;
border: 2px solid var(--tb-slate) !important;
border-radius: 4px !important;
}
input:focus,
textarea:focus,
select:focus {
border-color: var(--nes-light-blue) !important;
border-color: var(--tb-blue-light) !important;
outline: none !important;
box-shadow: 0 0 0 2px rgba(112, 160, 224, 0.3) !important;
box-shadow: 0 0 0 2px rgba(85, 136, 221, 0.3) !important;
}
/* ============================================
@@ -206,15 +204,15 @@ pre, code {
}
code {
background: var(--nes-dark-gray) !important;
color: var(--nes-light-green) !important;
background: var(--tb-navy-light) !important;
color: var(--tb-blue-bright) !important;
padding: 2px 6px !important;
border-radius: 3px !important;
}
pre {
background: var(--nes-black) !important;
border: 2px solid var(--nes-gray) !important;
background: var(--tb-navy) !important;
border: 2px solid var(--tb-slate) !important;
border-radius: 4px !important;
padding: 12px !important;
}
@@ -228,18 +226,18 @@ pre code {
LINKS
============================================ */
a {
color: var(--nes-sky) !important;
color: var(--tb-blue-bright) !important;
text-decoration: none !important;
transition: color 0.15s ease !important;
}
a:hover {
color: var(--nes-white) !important;
color: var(--tb-white) !important;
text-decoration: underline !important;
}
a:visited {
color: var(--nes-light-blue) !important;
color: var(--tb-blue-light) !important;
}
/* ============================================
@@ -250,9 +248,9 @@ a:visited {
[class*="widget"],
[class*="Paper"],
[class*="container"]:not(body):not(html) {
background: var(--nes-black) !important;
border: 2px solid var(--nes-gray) !important;
color: var(--nes-white) !important;
background: var(--tb-navy) !important;
border: 2px solid var(--tb-slate) !important;
color: var(--tb-white) !important;
}
/* ============================================
@@ -266,16 +264,16 @@ a:visited {
[class*="Popover"],
[class*="Menu-list"],
[class*="MenuList"] {
background: var(--nes-dark-gray) !important;
border: 2px solid var(--nes-gray) !important;
color: var(--nes-white) !important;
background: var(--tb-navy-light) !important;
border: 2px solid var(--tb-slate) !important;
color: var(--tb-white) !important;
}
[role="menuitem"]:hover,
[role="option"]:hover,
[class*="MenuItem"]:hover {
background: var(--nes-blue) !important;
color: var(--nes-white) !important;
background: var(--tb-blue) !important;
color: var(--tb-white) !important;
}
/* ============================================
@@ -288,9 +286,9 @@ a:visited {
[class*="pill"] {
font-family: 'IBM Plex Mono', monospace !important;
font-size: 11px !important;
background: var(--nes-blue) !important;
color: var(--nes-white) !important;
border: 1px solid var(--nes-mid-blue) !important;
background: var(--tb-blue) !important;
color: var(--tb-white) !important;
border: 1px solid var(--tb-blue-light) !important;
}
/* Status indicators */
@@ -299,19 +297,19 @@ a:visited {
[class*="running"],
[class*="active"],
[class*="ok"] {
color: var(--nes-light-green) !important;
color: var(--tb-green-light) !important;
}
[class*="error"],
[class*="danger"],
[class*="failed"],
[class*="critical"] {
color: var(--nes-red) !important;
color: var(--tb-red) !important;
}
[class*="warning"],
[class*="pending"] {
color: var(--nes-yellow) !important;
color: var(--tb-yellow) !important;
}
/* ============================================
@@ -323,69 +321,35 @@ a:visited {
}
::-webkit-scrollbar-track {
background: var(--nes-dark-gray);
background: var(--tb-navy-light);
}
::-webkit-scrollbar-thumb {
background: var(--nes-gray);
background: var(--tb-slate);
border-radius: 6px;
border: 2px solid var(--nes-dark-gray);
border: 2px solid var(--tb-navy-light);
}
::-webkit-scrollbar-thumb:hover {
background: var(--nes-light-gray);
background: var(--tb-steel);
}
/* ============================================
TEXT SELECTION
============================================ */
::selection {
background: var(--nes-blue) !important;
color: var(--nes-white) !important;
background: var(--tb-blue) !important;
color: var(--tb-white) !important;
}
/* ============================================
FOCUS STYLES
============================================ */
:focus-visible {
outline: 2px solid var(--nes-light-blue) !important;
outline: 2px solid var(--tb-blue-light) !important;
outline-offset: 2px !important;
}
/* ============================================
CRT SCANLINE OVERLAY
============================================ */
body::after {
content: "";
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
pointer-events: none;
z-index: 9998;
background: repeating-linear-gradient(
0deg,
transparent,
transparent 2px,
rgba(0, 0, 0, 0.06) 2px,
rgba(0, 0, 0, 0.06) 4px
);
}
/* ============================================
POWER-ON ANIMATION
============================================ */
@keyframes power-on {
0% { opacity: 0; filter: brightness(2) saturate(0); }
30% { opacity: 1; filter: brightness(1.5) saturate(0.5); }
100% { opacity: 1; filter: brightness(1) saturate(1); }
}
body {
animation: power-on 0.8s ease-out;
}
/* ============================================
HIDE SERVICE LOGOS (universal)
@@ -428,15 +392,15 @@ img[alt*="Logo" i] {
.grafana-app [class*="NavBar"],
.grafana-app [class*="Topbar"],
.grafana-app [aria-label="Main menu"] {
background: var(--nes-dark-gray) !important;
border-bottom: 3px solid var(--nes-gray) !important;
background: var(--tb-navy-light) !important;
border-bottom: 3px solid var(--tb-slate) !important;
}
/* Sidenav */
.grafana-app [class*="sidemenu"],
.grafana-app [class*="NavBar"][class*="wrapper"] {
background: var(--nes-black) !important;
border-right: 3px solid var(--nes-gray) !important;
background: var(--tb-navy) !important;
border-right: 3px solid var(--tb-slate) !important;
}
/* Dashboard container */
@@ -449,20 +413,19 @@ img[alt*="Logo" i] {
.grafana-app .panel-container,
.grafana-app [class*="panel-content"],
.grafana-app [class*="PanelChrome"] {
background: var(--nes-black) !important;
border: 2px solid var(--nes-gray) !important;
background: var(--tb-navy) !important;
border: 2px solid var(--tb-slate) !important;
border-radius: 0 !important;
box-shadow: 4px 4px 0 rgba(0, 0, 0, 0.3) !important;
}
/* Panel headers */
.grafana-app .panel-title,
.grafana-app [class*="panel-header"],
.grafana-app [class*="PanelHeader"] {
background: var(--nes-dark-gray) !important;
background: var(--tb-navy-light) !important;
font-family: 'Press Start 2P', monospace !important;
font-size: 9px !important;
color: var(--nes-sky) !important;
color: var(--tb-white) !important;
}
/* Explore page */
@@ -474,21 +437,21 @@ img[alt*="Logo" i] {
/* Grafana query editor */
.grafana-app [class*="query-editor"],
.grafana-app [class*="QueryEditor"] {
background: var(--nes-dark-gray) !important;
border: 2px solid var(--nes-gray) !important;
background: var(--tb-navy-light) !important;
border: 2px solid var(--tb-slate) !important;
}
/* Alert list */
.grafana-app [class*="AlertList"],
.grafana-app [class*="alert-rule"] {
background: var(--nes-black) !important;
border: 1px solid var(--nes-gray) !important;
background: var(--tb-navy) !important;
border: 1px solid var(--tb-slate) !important;
}
/* Grafana page toolbar */
.grafana-app [class*="page-toolbar"],
.grafana-app [class*="PageToolbar"] {
background: var(--nes-dark-gray) !important;
background: var(--tb-navy-light) !important;
}
@@ -510,8 +473,8 @@ header img[alt*="Jaeger" i] {
.jaeger-ui [class*="TopNav"],
[class*="jaeger"] [class*="TopNav"],
[data-testid="TopNav"] {
background: var(--nes-dark-gray) !important;
border-bottom: 3px solid var(--nes-gray) !important;
background: var(--tb-navy-light) !important;
border-bottom: 3px solid var(--tb-slate) !important;
}
/* Trace page */
@@ -523,8 +486,8 @@ header img[alt*="Jaeger" i] {
/* Timeline viewer */
.TraceTimelineViewer,
[class*="TraceTimelineViewer"] {
background: var(--nes-black) !important;
border: 2px solid var(--nes-gray) !important;
background: var(--tb-navy) !important;
border: 2px solid var(--tb-slate) !important;
}
/* Span bars */
@@ -536,36 +499,36 @@ header img[alt*="Jaeger" i] {
.SearchForm,
[class*="SearchForm"],
[class*="search-form"] {
background: var(--nes-dark-gray) !important;
border: 2px solid var(--nes-gray) !important;
background: var(--tb-navy-light) !important;
border: 2px solid var(--tb-slate) !important;
padding: 12px !important;
}
/* Trace result list */
[class*="ResultItem"],
[class*="result-item"] {
background: var(--nes-black) !important;
border: 1px solid var(--nes-gray) !important;
background: var(--tb-navy) !important;
border: 1px solid var(--tb-slate) !important;
margin-bottom: 4px !important;
}
[class*="ResultItem"]:hover,
[class*="result-item"]:hover {
background: rgba(40, 64, 160, 0.3) !important;
background: rgba(51, 102, 204, 0.2) !important;
}
/* Span detail */
[class*="SpanDetail"],
[class*="AccordianKeyValues"] {
background: var(--nes-dark-gray) !important;
background: var(--tb-navy-light) !important;
}
/* Jaeger-specific selects */
.jaeger-ui select,
[class*="jaeger"] select {
background: var(--nes-dark-gray) !important;
color: var(--nes-white) !important;
border: 2px solid var(--nes-gray) !important;
background: var(--tb-navy-mid) !important;
color: var(--tb-white) !important;
border: 2px solid var(--tb-slate) !important;
}
@@ -584,23 +547,23 @@ nav.navbar svg,
/* Main navbar */
.prometheus-app nav.navbar,
nav.navbar {
background: var(--nes-dark-gray) !important;
border-bottom: 3px solid var(--nes-gray) !important;
background: var(--tb-navy-light) !important;
border-bottom: 3px solid var(--tb-slate) !important;
}
nav.navbar .navbar-brand {
font-family: 'Press Start 2P', monospace !important;
font-size: 10px !important;
color: var(--nes-light-green) !important;
color: var(--tb-white) !important;
}
/* Expression input */
.prometheus-app .expression-input,
[class*="expression-input"],
.cm-expression-input {
background: var(--nes-black) !important;
border: 3px solid var(--nes-gray) !important;
color: var(--nes-white) !important;
background: var(--tb-navy) !important;
border: 3px solid var(--tb-slate) !important;
color: var(--tb-white) !important;
font-family: 'IBM Plex Mono', monospace !important;
}
@@ -608,36 +571,35 @@ nav.navbar .navbar-brand {
.graph-container,
[class*="graph-container"],
.panel {
background: var(--nes-black) !important;
border: 2px solid var(--nes-gray) !important;
background: var(--tb-navy) !important;
border: 2px solid var(--tb-slate) !important;
border-radius: 0 !important;
box-shadow: 4px 4px 0 rgba(0, 0, 0, 0.3) !important;
}
/* Graph tab controls */
.graph-controls,
[class*="graph-controls"],
.nav-tabs {
background: var(--nes-dark-gray) !important;
border-bottom: 2px solid var(--nes-gray) !important;
background: var(--tb-navy-light) !important;
border-bottom: 2px solid var(--tb-slate) !important;
}
.nav-tabs .nav-link,
.nav-tabs a {
color: var(--nes-light-gray) !important;
color: var(--tb-steel) !important;
}
.nav-tabs .nav-link.active,
.nav-tabs .active a {
color: var(--nes-sky) !important;
border-bottom: 3px solid var(--nes-mid-blue) !important;
color: var(--tb-white) !important;
border-bottom: 3px solid var(--tb-blue) !important;
background: transparent !important;
}
/* Alert/Rule manager */
.rules-head,
[class*="rules"] th {
background: var(--nes-blue) !important;
background: var(--tb-blue) !important;
}
/* Prometheus targets page */
@@ -650,20 +612,20 @@ nav.navbar .navbar-brand {
/* Status badges */
.badge-success,
.label-success {
background: var(--nes-green) !important;
color: var(--nes-white) !important;
background: var(--tb-green) !important;
color: var(--tb-white) !important;
}
.badge-danger,
.label-danger {
background: var(--nes-red) !important;
color: var(--nes-white) !important;
background: var(--tb-red) !important;
color: var(--tb-white) !important;
}
.badge-warning,
.label-warning {
background: var(--nes-orange) !important;
color: var(--nes-white) !important;
background: var(--tb-yellow) !important;
color: var(--tb-navy) !important;
}
@@ -689,22 +651,22 @@ nav.navbar .navbar-brand {
#app nav,
#app [class*="nav-bar"],
#app .navbar {
background: var(--nes-dark-gray) !important;
border-bottom: 3px solid var(--nes-gray) !important;
background: var(--tb-navy-light) !important;
border-bottom: 3px solid var(--tb-slate) !important;
}
/* Sidebar */
#app [class*="sidebar"],
#app [class*="side-menu"],
#app aside {
background: var(--nes-black) !important;
border-right: 3px solid var(--nes-gray) !important;
background: var(--tb-navy) !important;
border-right: 3px solid var(--tb-slate) !important;
}
/* Sidebar menu items */
#app [class*="sidebar"] a,
#app [class*="side-menu"] a {
color: var(--nes-light-gray) !important;
color: var(--tb-steel) !important;
border-left: 3px solid transparent !important;
}
@@ -712,9 +674,9 @@ nav.navbar .navbar-brand {
#app [class*="side-menu"] a:hover,
#app [class*="sidebar"] a.active,
#app [class*="side-menu"] a.active {
color: var(--nes-sky) !important;
background: rgba(40, 64, 160, 0.2) !important;
border-left-color: var(--nes-mid-blue) !important;
color: var(--tb-white) !important;
background: rgba(51, 102, 204, 0.2) !important;
border-left-color: var(--tb-blue) !important;
}
/* Main content area */
@@ -729,34 +691,34 @@ nav.navbar .navbar-brand {
#app [class*="status"][class*="success"],
#app [class*="pill"][class*="green"],
#app [class*="positive"] {
background: var(--nes-green) !important;
color: var(--nes-white) !important;
background: var(--tb-green) !important;
color: var(--tb-white) !important;
}
#app [class*="status"][class*="negative"],
#app [class*="status"][class*="error"],
#app [class*="pill"][class*="red"],
#app [class*="negative"] {
background: var(--nes-red) !important;
color: var(--nes-white) !important;
background: var(--tb-red) !important;
color: var(--tb-white) !important;
}
#app [class*="status"][class*="warning"],
#app [class*="pill"][class*="orange"] {
background: var(--nes-orange) !important;
color: var(--nes-white) !important;
background: var(--tb-yellow) !important;
color: var(--tb-navy) !important;
}
/* Traefik table rows */
#app table tr:hover td {
background: rgba(40, 64, 160, 0.3) !important;
background: rgba(51, 102, 204, 0.2) !important;
}
/* Service detail panels */
#app [class*="detail"],
#app [class*="panel-block"] {
background: var(--nes-black) !important;
border: 2px solid var(--nes-gray) !important;
background: var(--tb-navy) !important;
border: 2px solid var(--tb-slate) !important;
}
@@ -783,8 +745,8 @@ nav.navbar .navbar-brand {
/* App bar */
#root .MuiAppBar-root,
#root [class*="MuiAppBar"] {
background: var(--nes-dark-gray) !important;
border-bottom: 3px solid var(--nes-gray) !important;
background: var(--tb-navy-light) !important;
border-bottom: 3px solid var(--tb-slate) !important;
box-shadow: none !important;
}
@@ -792,46 +754,45 @@ nav.navbar .navbar-brand {
#root .MuiDrawer-root,
#root .MuiDrawer-paper,
#root [class*="MuiDrawer"] {
background: var(--nes-black) !important;
border-right: 3px solid var(--nes-gray) !important;
background: var(--tb-navy) !important;
border-right: 3px solid var(--tb-slate) !important;
}
/* Drawer list items */
#root .MuiListItem-root,
#root .MuiListItemButton-root,
#root [class*="MuiListItem"] {
color: var(--nes-light-gray) !important;
color: var(--tb-steel) !important;
}
#root .MuiListItem-root:hover,
#root .MuiListItemButton-root:hover,
#root [class*="MuiListItem"]:hover {
background: rgba(40, 64, 160, 0.2) !important;
color: var(--nes-sky) !important;
background: rgba(51, 102, 204, 0.2) !important;
color: var(--tb-white) !important;
}
#root .Mui-selected,
#root [class*="Mui-selected"] {
background: rgba(40, 64, 160, 0.3) !important;
color: var(--nes-sky) !important;
background: rgba(51, 102, 204, 0.3) !important;
color: var(--tb-white) !important;
}
/* MUI Paper */
#root .MuiPaper-root,
#root [class*="MuiPaper"] {
background: var(--nes-black) !important;
color: var(--nes-white) !important;
border: 2px solid var(--nes-gray) !important;
box-shadow: 4px 4px 0 rgba(0, 0, 0, 0.3) !important;
background: var(--tb-navy) !important;
color: var(--tb-white) !important;
border: 2px solid var(--tb-slate) !important;
}
/* MUI Buttons */
#root .MuiButton-root,
#root [class*="MuiButton"] {
font-family: 'IBM Plex Mono', monospace !important;
background: var(--nes-dark-gray) !important;
color: var(--nes-white) !important;
border: 2px solid var(--nes-gray) !important;
background: var(--tb-navy-light) !important;
color: var(--tb-white) !important;
border: 2px solid var(--tb-slate) !important;
border-radius: 4px !important;
text-transform: none !important;
box-shadow: none !important;
@@ -839,33 +800,33 @@ nav.navbar .navbar-brand {
#root .MuiButton-root:hover,
#root [class*="MuiButton"]:hover {
background: var(--nes-blue) !important;
border-color: var(--nes-light-blue) !important;
background: var(--tb-blue) !important;
border-color: var(--tb-blue-light) !important;
}
#root .MuiButton-containedPrimary,
#root [class*="MuiButton-containedPrimary"] {
background: var(--nes-green) !important;
color: var(--nes-black) !important;
border: 2px solid var(--nes-light-green) !important;
background: var(--tb-blue) !important;
color: var(--tb-white) !important;
border: 2px solid var(--tb-blue-light) !important;
}
#root .MuiButton-containedPrimary:hover {
background: var(--nes-light-green) !important;
background: var(--tb-blue-light) !important;
}
/* MUI Table */
#root .MuiTableCell-root,
#root [class*="MuiTableCell"] {
border-color: var(--nes-gray) !important;
color: var(--nes-cream) !important;
border-color: var(--tb-slate) !important;
color: var(--tb-white) !important;
font-family: 'IBM Plex Mono', monospace !important;
}
#root .MuiTableCell-head,
#root [class*="MuiTableCell-head"] {
background: var(--nes-blue) !important;
color: var(--nes-white) !important;
background: var(--tb-blue) !important;
color: var(--tb-white) !important;
font-family: 'Press Start 2P', monospace !important;
font-size: 9px !important;
text-transform: uppercase !important;
@@ -873,7 +834,7 @@ nav.navbar .navbar-brand {
#root .MuiTableRow-root:hover,
#root [class*="MuiTableRow"]:hover {
background: rgba(40, 64, 160, 0.3) !important;
background: rgba(51, 102, 204, 0.2) !important;
}
/* MUI Inputs */
@@ -881,64 +842,64 @@ nav.navbar .navbar-brand {
#root .MuiOutlinedInput-root,
#root [class*="MuiInputBase"],
#root [class*="MuiOutlinedInput"] {
background: var(--nes-dark-gray) !important;
color: var(--nes-white) !important;
background: var(--tb-navy-mid) !important;
color: var(--tb-white) !important;
}
#root .MuiOutlinedInput-notchedOutline,
#root [class*="notchedOutline"] {
border-color: var(--nes-gray) !important;
border-color: var(--tb-slate) !important;
}
#root .MuiInputLabel-root,
#root [class*="MuiInputLabel"] {
color: var(--nes-light-gray) !important;
color: var(--tb-steel) !important;
}
/* MUI Dialogs */
#root .MuiDialog-paper,
#root [class*="MuiDialog-paper"] {
background: var(--nes-dark-gray) !important;
border: 3px solid var(--nes-gray) !important;
background: var(--tb-navy-light) !important;
border: 3px solid var(--tb-slate) !important;
}
/* MUI Tabs */
#root .MuiTab-root,
#root [class*="MuiTab"] {
color: var(--nes-light-gray) !important;
color: var(--tb-steel) !important;
font-family: 'IBM Plex Mono', monospace !important;
text-transform: none !important;
}
#root .MuiTab-root.Mui-selected,
#root [class*="MuiTab"][class*="Mui-selected"] {
color: var(--nes-sky) !important;
color: var(--tb-white) !important;
}
#root .MuiTabs-indicator,
#root [class*="MuiTabs-indicator"] {
background: var(--nes-mid-blue) !important;
background: var(--tb-blue) !important;
}
/* MUI Breadcrumbs */
#root .MuiBreadcrumbs-root,
#root [class*="MuiBreadcrumbs"] {
color: var(--nes-light-gray) !important;
color: var(--tb-steel) !important;
}
/* MUI Chips */
#root .MuiChip-root,
#root [class*="MuiChip"] {
background: var(--nes-blue) !important;
color: var(--nes-white) !important;
border: 1px solid var(--nes-mid-blue) !important;
background: var(--tb-blue) !important;
color: var(--tb-white) !important;
border: 1px solid var(--tb-blue-light) !important;
}
/* MUI Icons */
#root .MuiSvgIcon-root,
#root [class*="MuiSvgIcon"],
#root svg {
color: var(--nes-light-gray) !important;
color: var(--tb-steel) !important;
fill: currentColor !important;
}
@@ -951,9 +912,9 @@ nav.navbar .navbar-brand {
body > h2,
body > h3 {
font-family: 'Press Start 2P', monospace !important;
color: var(--nes-sky) !important;
color: var(--tb-white) !important;
padding: 10px 0 !important;
border-bottom: 3px solid var(--nes-gray) !important;
border-bottom: 3px solid var(--tb-slate) !important;
margin-bottom: 15px !important;
}
@@ -966,8 +927,8 @@ body > table {
/* Trino worker status */
[class*="query-list"],
[class*="query-detail"] {
background: var(--nes-black) !important;
border: 2px solid var(--nes-gray) !important;
background: var(--tb-navy) !important;
border: 2px solid var(--tb-slate) !important;
}
/* Progress bars used by various services */
@@ -975,15 +936,15 @@ progress,
[role="progressbar"],
[class*="progress-bar"],
[class*="ProgressBar"] {
background: var(--nes-dark-gray) !important;
border: 1px solid var(--nes-gray) !important;
background: var(--tb-navy-light) !important;
border: 1px solid var(--tb-slate) !important;
border-radius: 0 !important;
}
progress::-webkit-progress-bar {
background: var(--nes-dark-gray) !important;
background: var(--tb-navy-light) !important;
}
progress::-webkit-progress-value {
background: var(--nes-mid-blue) !important;
background: var(--tb-blue) !important;
}

View File

@@ -14,24 +14,24 @@
{{/* ── The Reef: one router + one backend per service ───────────── */}}
{{- $reef := dict
"dashboard" (dict "port" "80" "theme" false "extra_hosts" (list $domain))
"gitea" (dict "port" "3000" "theme" false)
"woodpecker-server" (dict "port" "8000" "theme" false "subdomain" "ci")
"notebooks" (dict "port" "2718" "theme" false)
"zotero" (dict "port" "8080" "theme" false)
"nessie" (dict "port" "19120" "theme" false)
"trino" (dict "port" "8080" "theme" true)
"polaris" (dict "port" "8181" "theme" false)
"grafana" (dict "port" "3000" "theme" true)
"prometheus" (dict "port" "9090" "theme" true)
"jaeger" (dict "port" "16686" "theme" true)
"loki" (dict "port" "3100" "theme" false)
"dashboard" (dict "port" "80" "theme" false "extra_hosts" (list $domain) "mw" "secure-headers")
"gitea" (dict "port" "3000" "theme" false "mw" "secure-headers,rate-limit")
"woodpecker-server" (dict "port" "8000" "theme" false "subdomain" "ci" "mw" "local-only,secure-headers,rate-limit")
"notebooks" (dict "port" "2718" "theme" false "mw" "local-only,secure-headers")
"zotero" (dict "port" "8080" "theme" false "mw" "secure-headers")
"nessie" (dict "port" "19120" "theme" false "mw" "local-only,infra-headers")
"trino" (dict "port" "8080" "theme" true "mw" "local-only,infra-headers")
"polaris" (dict "port" "8181" "theme" false "mw" "local-only,infra-headers")
"grafana" (dict "port" "3000" "theme" true "mw" "local-only,secure-headers")
"prometheus" (dict "port" "9090" "theme" true "mw" "local-only,infra-headers")
"jaeger" (dict "port" "16686" "theme" true "mw" "local-only,infra-headers")
"loki" (dict "port" "3100" "theme" false "mw" "local-only,infra-headers")
-}}
{{/* ── Multi-endpoint services ─────────────────────────────────── */}}
{{- $multi := dict
"rustfs-api" (dict "container" "rustfs" "port" "9000" "subdomain" "s3" "theme" false)
"rustfs-console" (dict "container" "rustfs" "port" "9001" "subdomain" "s3console" "theme" true)
"rustfs-api" (dict "container" "rustfs" "port" "9000" "subdomain" "s3" "theme" false "mw" "local-only,infra-headers")
"rustfs-console" (dict "container" "rustfs" "port" "9001" "subdomain" "s3console" "theme" true "mw" "local-only,infra-headers")
-}}
http:
@@ -46,13 +46,28 @@ http:
rateLimit:
average: 100
burst: 50
inject-nes-theme:
local-only:
ipAllowList:
sourceRange:
- "127.0.0.1/32"
- "10.0.0.0/8"
- "172.16.0.0/12"
- "192.168.0.0/16"
infra-headers:
headers:
frameDeny: true
browserXssFilter: true
contentTypeNosniff: true
referrerPolicy: "strict-origin-when-cross-origin"
customResponseHeaders:
X-Robots-Tag: "noindex, nofollow"
inject-throwback:
plugin:
rewrite-body:
lastModified: true
rewrites:
- regex: "</head>"
replacement: '<link rel="stylesheet" type="text/css" href="http://{{ $domain }}/nes-theme.css"></head>'
replacement: '<link rel="stylesheet" type="text/css" href="http://{{ $domain }}/throwback.css"></head>'
{{/* ── Generated routers ─────────────────────────────────────── */}}
routers:
@@ -62,9 +77,15 @@ http:
service: {{ $name }}
entryPoints:
- web
{{- if get $svc "theme" }}
{{- $mwList := list -}}
{{- if get $svc "theme" }}{{ $mwList = append $mwList "inject-throwback" }}{{ end -}}
{{- $extra := get $svc "mw" | default "" -}}
{{- if ne $extra "" }}{{ range split "," $extra }}{{ $mwList = append $mwList (trim .) }}{{ end }}{{ end -}}
{{- if $mwList }}
middlewares:
- inject-nes-theme
{{- range $mwList }}
- {{ . }}
{{- end }}
{{- end }}
{{- end }}
{{- range $name, $svc := $multi }}
@@ -73,9 +94,15 @@ http:
service: {{ $name }}
entryPoints:
- web
{{- if get $svc "theme" }}
{{- $mwList := list -}}
{{- if get $svc "theme" }}{{ $mwList = append $mwList "inject-throwback" }}{{ end -}}
{{- $extra := get $svc "mw" | default "" -}}
{{- if ne $extra "" }}{{ range split "," $extra }}{{ $mwList = append $mwList (trim .) }}{{ end }}{{ end -}}
{{- if $mwList }}
middlewares:
- inject-nes-theme
{{- range $mwList }}
- {{ . }}
{{- end }}
{{- end }}
{{- end }}
traefik-dashboard:
@@ -84,7 +111,9 @@ http:
entryPoints:
- web
middlewares:
- inject-nes-theme
- inject-throwback
- local-only
- infra-headers
{{/* ── Generated services ────────────────────────────────────── */}}
services:

View File

@@ -1,60 +1,57 @@
/* NES SPORTS THEME FOR WOODPECKER CI - RBI Baseball / Tecmo Super Bowl */
/* THROWBACK THEME FOR WOODPECKER CI - Tecmo Super Bowl */
@import url('https://fonts.googleapis.com/css2?family=Press+Start+2P&family=IBM+Plex+Mono:wght@400;500;600&display=swap');
:root {
/* NES Color Palette */
--nes-black: #0f0f0f;
--nes-dark-gray: #1a1a2e;
--nes-gray: #4a4a68;
--nes-light-gray: #9090a8;
--nes-white: #f0f0f0;
/* Throwback Color Palette */
--tb-navy: #0a1628;
--tb-navy-light: #162040;
--tb-navy-mid: #1e2d50;
--tb-slate: #2a3f5f;
--tb-steel: #5a7090;
--tb-white: #f0f0f0;
--nes-dark-blue: #1a1a40;
--nes-blue: #2840a0;
--nes-mid-blue: #4060c0;
--nes-light-blue: #70a0e0;
--nes-sky: #90c0f0;
--tb-blue: #3366cc;
--tb-blue-light: #5588dd;
--tb-blue-bright: #88aaee;
--nes-red: #d03030;
--nes-orange: #e07020;
--nes-yellow: #e0b040;
--nes-green: #40a040;
--nes-light-green: #80d080;
--nes-cyan: #40b0c0;
--nes-cream: #e8d8c0;
--tb-red: #cc2244;
--tb-red-light: #dd4466;
--tb-green: #44aa44;
--tb-green-light: #66cc66;
--tb-yellow: #ddaa33;
/* Woodpecker color overrides */
--color-wp-primary-100: var(--nes-sky);
--color-wp-primary-200: var(--nes-light-blue);
--color-wp-primary-300: var(--nes-mid-blue);
--color-wp-primary-400: var(--nes-blue);
--color-wp-primary-500: var(--nes-dark-blue);
--color-wp-primary-100: var(--tb-blue-bright);
--color-wp-primary-200: var(--tb-blue-light);
--color-wp-primary-300: var(--tb-blue);
--color-wp-primary-400: #2255aa;
--color-wp-primary-500: var(--tb-navy);
--color-wp-success-100: var(--nes-light-green);
--color-wp-success-200: var(--nes-green);
--color-wp-success-100: var(--tb-green-light);
--color-wp-success-200: var(--tb-green);
--color-wp-error-100: #f08080;
--color-wp-error-200: var(--nes-red);
--color-wp-error-100: var(--tb-red-light);
--color-wp-error-200: var(--tb-red);
--color-wp-warning-100: var(--nes-yellow);
--color-wp-warning-200: var(--nes-orange);
--color-wp-warning-100: var(--tb-yellow);
--color-wp-warning-200: #bb8822;
--color-wp-gray-100: var(--nes-white);
--color-wp-gray-200: var(--nes-light-gray);
--color-wp-gray-300: var(--nes-gray);
--color-wp-gray-400: var(--nes-dark-gray);
--color-wp-gray-500: var(--nes-dark-blue);
--color-wp-gray-600: var(--nes-black);
--color-wp-gray-100: var(--tb-white);
--color-wp-gray-200: var(--tb-steel);
--color-wp-gray-300: var(--tb-slate);
--color-wp-gray-400: var(--tb-navy-mid);
--color-wp-gray-500: var(--tb-navy-light);
--color-wp-gray-600: var(--tb-navy);
}
/* Global styles */
body,
html {
font-family: 'IBM Plex Mono', monospace !important;
background: linear-gradient(180deg, #0a0a28 0%, #1a1a40 30%, #1a1a40 70%, #0a0a28 100%) !important;
background-attachment: fixed !important;
color: var(--nes-white) !important;
background: var(--tb-navy) !important;
color: var(--tb-white) !important;
}
/* Headings */
@@ -64,9 +61,9 @@ h1, h2, h3, h4, h5, h6 {
line-height: 1.8 !important;
}
h1 { font-size: 18px !important; color: var(--nes-light-green) !important; }
h2 { font-size: 14px !important; color: var(--nes-sky) !important; }
h3 { font-size: 12px !important; color: var(--nes-yellow) !important; }
h1 { font-size: 18px !important; color: var(--tb-white) !important; }
h2 { font-size: 14px !important; color: var(--tb-blue-bright) !important; }
h3 { font-size: 12px !important; color: var(--tb-white) !important; }
/* Header/Navbar */
header,
@@ -74,16 +71,16 @@ nav,
.navbar,
[class*="header"],
[class*="Header"] {
background: var(--nes-black) !important;
border-bottom: 3px solid var(--nes-gray) !important;
background: var(--tb-navy-light) !important;
border-bottom: 3px solid var(--tb-slate) !important;
}
/* Sidebar */
aside,
[class*="sidebar"],
[class*="Sidebar"] {
background: var(--nes-black) !important;
border-right: 3px solid var(--nes-gray) !important;
background: var(--tb-navy) !important;
border-right: 3px solid var(--tb-slate) !important;
}
/* Main content area */
@@ -100,8 +97,8 @@ main,
[class*="Panel"],
[class*="box"],
[class*="Box"] {
background: var(--nes-black) !important;
border: 2px solid var(--nes-gray) !important;
background: var(--tb-navy) !important;
border: 2px solid var(--tb-slate) !important;
border-radius: 4px !important;
}
@@ -121,16 +118,16 @@ button,
[class*="primary"],
[class*="Primary"],
button[type="submit"] {
background: var(--nes-mid-blue) !important;
color: var(--nes-white) !important;
border: 2px solid var(--nes-light-blue) !important;
background: var(--tb-blue) !important;
color: var(--tb-white) !important;
border: 2px solid var(--tb-blue-light) !important;
}
[class*="primary"]:hover,
[class*="Primary"]:hover,
button[type="submit"]:hover {
background: var(--nes-light-blue) !important;
color: var(--nes-black) !important;
background: var(--tb-blue-light) !important;
color: var(--tb-white) !important;
}
/* Success/green buttons */
@@ -138,15 +135,15 @@ button[type="submit"]:hover {
[class*="Success"],
[class*="green"],
[class*="Green"] {
background: var(--nes-green) !important;
color: var(--nes-white) !important;
border: 2px solid var(--nes-light-green) !important;
background: var(--tb-green) !important;
color: var(--tb-white) !important;
border: 2px solid var(--tb-green-light) !important;
}
[class*="success"]:hover,
[class*="Success"]:hover {
background: var(--nes-light-green) !important;
color: var(--nes-black) !important;
background: var(--tb-green-light) !important;
color: var(--tb-navy) !important;
}
/* Danger/red buttons */
@@ -156,8 +153,8 @@ button[type="submit"]:hover {
[class*="Error"],
[class*="red"],
[class*="Red"] {
background: var(--nes-red) !important;
color: var(--nes-white) !important;
background: var(--tb-red) !important;
color: var(--tb-white) !important;
}
/* Forms */
@@ -165,9 +162,9 @@ input,
textarea,
select {
font-family: 'IBM Plex Mono', monospace !important;
background: var(--nes-dark-gray) !important;
color: var(--nes-white) !important;
border: 2px solid var(--nes-gray) !important;
background: var(--tb-navy-mid) !important;
color: var(--tb-white) !important;
border: 2px solid var(--tb-slate) !important;
border-radius: 4px !important;
padding: 8px 12px !important;
}
@@ -175,41 +172,41 @@ select {
input:focus,
textarea:focus,
select:focus {
border-color: var(--nes-light-blue) !important;
border-color: var(--tb-blue-light) !important;
outline: none !important;
box-shadow: 0 0 0 2px rgba(112, 160, 224, 0.3) !important;
box-shadow: 0 0 0 2px rgba(85, 136, 221, 0.3) !important;
}
/* Tables */
table {
background: var(--nes-black) !important;
border: 2px solid var(--nes-gray) !important;
background: var(--tb-navy) !important;
border: 2px solid var(--tb-slate) !important;
border-collapse: collapse !important;
}
th {
background: var(--nes-blue) !important;
color: var(--nes-white) !important;
background: var(--tb-blue) !important;
color: var(--tb-white) !important;
font-family: 'Press Start 2P', monospace !important;
font-size: 10px !important;
text-transform: uppercase !important;
padding: 10px 12px !important;
border: 1px solid var(--nes-mid-blue) !important;
border: 1px solid var(--tb-blue-light) !important;
}
td {
background: var(--nes-black) !important;
color: var(--nes-cream) !important;
background: var(--tb-navy) !important;
color: var(--tb-white) !important;
padding: 8px 12px !important;
border: 1px solid var(--nes-dark-gray) !important;
border: 1px solid var(--tb-navy-light) !important;
}
tr:nth-child(even) td {
background: var(--nes-dark-gray) !important;
background: var(--tb-navy-light) !important;
}
tr:hover td {
background: var(--nes-dark-blue) !important;
background: rgba(51, 102, 204, 0.2) !important;
}
/* Pipeline/build status colors */
@@ -217,14 +214,14 @@ tr:hover td {
[class*="Running"],
[class*="pending"],
[class*="Pending"] {
color: var(--nes-yellow) !important;
color: var(--tb-yellow) !important;
}
[class*="success"],
[class*="Success"],
[class*="passed"],
[class*="Passed"] {
color: var(--nes-light-green) !important;
color: var(--tb-green-light) !important;
}
[class*="failure"],
@@ -233,7 +230,7 @@ tr:hover td {
[class*="Failed"],
[class*="error"],
[class*="Error"] {
color: var(--nes-red) !important;
color: var(--tb-red) !important;
}
/* Build logs */
@@ -244,28 +241,28 @@ code,
[class*="console"],
[class*="Console"] {
font-family: 'IBM Plex Mono', monospace !important;
background: var(--nes-black) !important;
color: var(--nes-cream) !important;
border: 2px solid var(--nes-gray) !important;
background: var(--tb-navy) !important;
color: var(--tb-white) !important;
border: 2px solid var(--tb-slate) !important;
border-radius: 4px !important;
}
/* Inline code */
code {
background: var(--nes-dark-gray) !important;
color: var(--nes-light-green) !important;
background: var(--tb-navy-light) !important;
color: var(--tb-blue-bright) !important;
padding: 2px 6px !important;
border: none !important;
}
/* Links */
a {
color: var(--nes-sky) !important;
color: var(--tb-blue-bright) !important;
text-decoration: none !important;
}
a:hover {
color: var(--nes-white) !important;
color: var(--tb-white) !important;
text-decoration: underline !important;
}
@@ -276,8 +273,8 @@ a:hover {
[class*="Menu"],
[class*="popover"],
[class*="Popover"] {
background: var(--nes-dark-gray) !important;
border: 2px solid var(--nes-gray) !important;
background: var(--tb-navy-light) !important;
border: 2px solid var(--tb-slate) !important;
border-radius: 4px !important;
}
@@ -285,7 +282,7 @@ a:hover {
[class*="menu"] li:hover,
[role="menuitem"]:hover,
[role="option"]:hover {
background: var(--nes-blue) !important;
background: var(--tb-blue) !important;
}
/* Badges and tags */
@@ -305,9 +302,9 @@ a:hover {
[class*="tooltip"],
[class*="Tooltip"] {
font-family: 'IBM Plex Mono', monospace !important;
background: var(--nes-black) !important;
color: var(--nes-white) !important;
border: 2px solid var(--nes-gray) !important;
background: var(--tb-navy-light) !important;
color: var(--tb-white) !important;
border: 2px solid var(--tb-slate) !important;
border-radius: 4px !important;
}
@@ -315,17 +312,17 @@ a:hover {
progress,
[class*="progress"],
[class*="Progress"] {
background: var(--nes-dark-gray) !important;
border: 2px solid var(--nes-gray) !important;
background: var(--tb-navy-light) !important;
border: 2px solid var(--tb-slate) !important;
border-radius: 4px !important;
}
progress::-webkit-progress-bar {
background: var(--nes-dark-gray) !important;
background: var(--tb-navy-light) !important;
}
progress::-webkit-progress-value {
background: var(--nes-green) !important;
background: var(--tb-blue) !important;
}
/* Scrollbars */
@@ -335,22 +332,22 @@ progress::-webkit-progress-value {
}
::-webkit-scrollbar-track {
background: var(--nes-dark-gray);
background: var(--tb-navy-light);
}
::-webkit-scrollbar-thumb {
background: var(--nes-gray);
background: var(--tb-slate);
border-radius: 6px;
}
::-webkit-scrollbar-thumb:hover {
background: var(--nes-light-gray);
background: var(--tb-steel);
}
/* Selection */
::selection {
background: var(--nes-blue) !important;
color: var(--nes-white) !important;
background: var(--tb-blue) !important;
color: var(--tb-white) !important;
}
/* Icons - ensure visibility */
@@ -363,7 +360,7 @@ svg,
/* Avatar images */
img[class*="avatar"],
[class*="avatar"] img {
border: 2px solid var(--nes-gray) !important;
border: 2px solid var(--tb-slate) !important;
border-radius: 4px !important;
}
@@ -372,10 +369,10 @@ img[class*="avatar"],
[class*="Item"],
[class*="list-item"],
[class*="ListItem"] {
border-bottom: 1px solid var(--nes-dark-gray) !important;
border-bottom: 1px solid var(--tb-navy-light) !important;
}
[class*="item"]:hover,
[class*="Item"]:hover {
background: rgba(64, 96, 192, 0.1) !important;
background: rgba(51, 102, 204, 0.1) !important;
}