Apply LotusGuild design system convergence (aesthetic_diff.md)
CSS (style.css): - §1: Add unified naming aliases (--terminal-green, --bg-primary, etc.) - §2: Upgrade borders: modal 1px→3px double, btn/btn-sm/inputs 1px→2px - §3: Add [ ] bracket decorations to .btn classes; primary keeps > prefix; hover lift -1px→-2px; padding 6px 14px→5px 12px - §4: Fix glow definitions from 2-layer rgba to 3-layer solid stack - §5: Section headers now symmetric ╠═══ TITLE ═══╣ (was one-sided) - §6+§7: Modal border 3px double, corners ┌┐→╔╗, add glow shadow - §11: Nav active state now amber tint (was green); hover remains green - §15: Scanline opacity 0.13→0.15; flicker delay 45s→30s JS (app.js): - §18: Replace custom showToast() with lt.toast.* delegate wrapper Templates (base.html): - Load base.css and base.js (symlinked from web_template) - Add lt-boot overlay for boot sequence animation (§13) README: Remove completed pending convergence items Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
13
README.md
13
README.md
@@ -5,6 +5,19 @@
|
|||||||
Network monitoring dashboard for the LotusGuild Proxmox cluster.
|
Network monitoring dashboard for the LotusGuild Proxmox cluster.
|
||||||
Deployed on **LXC 157** (monitor-02 / 10.10.10.9), reachable at `gandalf.lotusguild.org`.
|
Deployed on **LXC 157** (monitor-02 / 10.10.10.9), reachable at `gandalf.lotusguild.org`.
|
||||||
|
|
||||||
|
**Design System**: [web_template](https://code.lotusguild.org/LotusGuild/web_template) — shared CSS, JS, and layout patterns for all LotusGuild apps
|
||||||
|
|
||||||
|
## Styling & Layout
|
||||||
|
|
||||||
|
GANDALF uses the **LotusGuild Terminal Design System**. For all styling, component, and layout documentation see:
|
||||||
|
|
||||||
|
- [`web_template/README.md`](https://code.lotusguild.org/LotusGuild/web_template/src/branch/main/README.md) — full component reference, CSS variables, JS API
|
||||||
|
- [`web_template/base.css`](https://code.lotusguild.org/LotusGuild/web_template/src/branch/main/base.css) — unified CSS (`.lt-*` classes)
|
||||||
|
- [`web_template/base.js`](https://code.lotusguild.org/LotusGuild/web_template/src/branch/main/base.js) — `window.lt` utilities (toast, modal, auto-refresh, fetch helpers)
|
||||||
|
- [`web_template/aesthetic_diff.md`](https://code.lotusguild.org/LotusGuild/web_template/src/branch/main/aesthetic_diff.md) — cross-app divergence analysis and convergence guide
|
||||||
|
- [`web_template/python/base.html`](https://code.lotusguild.org/LotusGuild/web_template/src/branch/main/python/base.html) — Jinja2 base template
|
||||||
|
- [`web_template/python/auth.py`](https://code.lotusguild.org/LotusGuild/web_template/src/branch/main/python/auth.py) — `@require_auth` decorator pattern
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Architecture
|
## Architecture
|
||||||
|
|||||||
@@ -1,18 +1,11 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
// ── Toast notifications ───────────────────────────────────────────────
|
// ── Toast notifications — delegates to lt.toast from base.js ─────────
|
||||||
function showToast(msg, type = 'success') {
|
function showToast(msg, type = 'success') {
|
||||||
let container = document.querySelector('.toast-container');
|
if (type === 'error') return lt.toast.error(msg);
|
||||||
if (!container) {
|
if (type === 'warning') return lt.toast.warning(msg);
|
||||||
container = document.createElement('div');
|
if (type === 'info') return lt.toast.info(msg);
|
||||||
container.className = 'toast-container';
|
return lt.toast.success(msg);
|
||||||
document.body.appendChild(container);
|
|
||||||
}
|
|
||||||
const toast = document.createElement('div');
|
|
||||||
toast.className = `toast toast-${type}`;
|
|
||||||
toast.textContent = msg;
|
|
||||||
container.appendChild(toast);
|
|
||||||
setTimeout(() => toast.remove(), 3500);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── Dashboard auto-refresh ────────────────────────────────────────────
|
// ── Dashboard auto-refresh ────────────────────────────────────────────
|
||||||
|
|||||||
1
static/base.css
Symbolic link
1
static/base.css
Symbolic link
@@ -0,0 +1 @@
|
|||||||
|
/root/code/web_template/base.css
|
||||||
1
static/base.js
Symbolic link
1
static/base.js
Symbolic link
@@ -0,0 +1 @@
|
|||||||
|
/root/code/web_template/base.js
|
||||||
@@ -35,11 +35,27 @@
|
|||||||
|
|
||||||
--font: 'Courier New','Consolas','Monaco','Menlo',monospace;
|
--font: 'Courier New','Consolas','Monaco','Menlo',monospace;
|
||||||
|
|
||||||
--glow: 0 0 5px #00ff41, 0 0 10px rgba(0,255,65,.4);
|
--glow: 0 0 5px #00ff41, 0 0 10px #00ff41, 0 0 15px #00ff41;
|
||||||
--glow-xl: 0 0 8px #00ff41, 0 0 20px rgba(0,255,65,.35);
|
--glow-xl: 0 0 8px #00ff41, 0 0 16px #00ff41, 0 0 24px #00ff41, 0 0 32px rgba(0,255,65,.5);
|
||||||
--glow-amber: 0 0 5px #ffb000, 0 0 10px rgba(255,176,0,.4);
|
--glow-amber: 0 0 5px #ffb000, 0 0 10px #ffb000, 0 0 15px #ffb000;
|
||||||
--glow-red: 0 0 5px #ff4444, 0 0 10px rgba(255,68,68,.4);
|
--glow-red: 0 0 5px #ff4444, 0 0 10px rgba(255,68,68,.4);
|
||||||
--glow-cyan: 0 0 5px #00ffff, 0 0 10px rgba(0,255,255,.35);
|
--glow-cyan: 0 0 5px #00ffff, 0 0 10px rgba(0,255,255,.35);
|
||||||
|
|
||||||
|
/* Unified naming aliases — matches base.css variable names */
|
||||||
|
--bg-primary: var(--bg);
|
||||||
|
--bg-secondary: var(--bg2);
|
||||||
|
--bg-tertiary: var(--bg3);
|
||||||
|
--terminal-green: var(--green);
|
||||||
|
--terminal-green-dim: var(--green-dim);
|
||||||
|
--terminal-amber: var(--amber);
|
||||||
|
--terminal-amber-dim: var(--amber-dim);
|
||||||
|
--terminal-cyan: var(--cyan);
|
||||||
|
--terminal-red: var(--red);
|
||||||
|
--text-primary: var(--text);
|
||||||
|
--text-secondary: var(--text-dim);
|
||||||
|
--border-color: var(--border);
|
||||||
|
--glow-green: var(--glow);
|
||||||
|
--font-mono: var(--font);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ── Reset ────────────────────────────────────────────────────────── */
|
/* ── Reset ────────────────────────────────────────────────────────── */
|
||||||
@@ -60,7 +76,7 @@ body {
|
|||||||
line-height: 1.5;
|
line-height: 1.5;
|
||||||
min-height: 100vh;
|
min-height: 100vh;
|
||||||
position: relative;
|
position: relative;
|
||||||
animation: flicker .25s ease-in-out 45s infinite;
|
animation: flicker .25s ease-in-out 30s infinite;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* CRT scanline overlay */
|
/* CRT scanline overlay */
|
||||||
@@ -70,7 +86,7 @@ body::before {
|
|||||||
inset: 0;
|
inset: 0;
|
||||||
background: repeating-linear-gradient(
|
background: repeating-linear-gradient(
|
||||||
0deg,
|
0deg,
|
||||||
rgba(0,0,0,.13) 0px, rgba(0,0,0,.13) 1px,
|
rgba(0,0,0,0.15) 0px, rgba(0,0,0,0.15) 1px,
|
||||||
transparent 1px, transparent 2px
|
transparent 1px, transparent 2px
|
||||||
);
|
);
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
@@ -157,13 +173,20 @@ a:hover { text-decoration: underline; text-shadow: var(--glow-amber); }
|
|||||||
}
|
}
|
||||||
.nav-link::before { content:'[ '; }
|
.nav-link::before { content:'[ '; }
|
||||||
.nav-link::after { content:' ]'; }
|
.nav-link::after { content:' ]'; }
|
||||||
.nav-link:hover, .nav-link.active {
|
.nav-link:hover {
|
||||||
color: var(--green);
|
color: var(--green);
|
||||||
border-color: var(--border);
|
border-color: var(--border);
|
||||||
background: var(--green-dim);
|
background: var(--green-dim);
|
||||||
text-shadow: var(--glow);
|
text-shadow: var(--glow);
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
}
|
}
|
||||||
|
.nav-link.active {
|
||||||
|
color: var(--amber);
|
||||||
|
border-color: var(--amber);
|
||||||
|
background: var(--amber-dim);
|
||||||
|
text-shadow: var(--glow-amber);
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
.header-right { display:flex; align-items:center; gap:10px; }
|
.header-right { display:flex; align-items:center; gap:10px; }
|
||||||
.header-user { font-size: .78em; color: var(--text-muted); }
|
.header-user { font-size: .78em; color: var(--text-muted); }
|
||||||
@@ -193,7 +216,8 @@ a:hover { text-decoration: underline; text-shadow: var(--glow-amber); }
|
|||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
letter-spacing: .1em;
|
letter-spacing: .1em;
|
||||||
}
|
}
|
||||||
.section-title::before { content:'╠══ '; color:var(--green); text-shadow:var(--glow); }
|
.section-title::before { content:'╠═══ '; color:var(--green); text-shadow:var(--glow); }
|
||||||
|
.section-title::after { content:' ═══╣'; color:var(--green); text-shadow:var(--glow); }
|
||||||
|
|
||||||
.section-badge {
|
.section-badge {
|
||||||
font-size: .72em;
|
font-size: .72em;
|
||||||
@@ -478,8 +502,8 @@ a:hover { text-decoration: underline; text-shadow: var(--glow-amber); }
|
|||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 5px;
|
gap: 5px;
|
||||||
padding: 6px 14px;
|
padding: 5px 12px;
|
||||||
border: 1px solid;
|
border: 2px solid;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
font-family: var(--font);
|
font-family: var(--font);
|
||||||
font-size: .8em;
|
font-size: .8em;
|
||||||
@@ -489,10 +513,13 @@ a:hover { text-decoration: underline; text-shadow: var(--glow-amber); }
|
|||||||
background: transparent;
|
background: transparent;
|
||||||
transition: all .15s;
|
transition: all .15s;
|
||||||
}
|
}
|
||||||
.btn:hover { transform: translateY(-1px); }
|
.btn::before { content: '[ '; }
|
||||||
|
.btn::after { content: ' ]'; }
|
||||||
|
.btn:hover { transform: translateY(-2px); }
|
||||||
|
|
||||||
.btn-primary { color:var(--green); border-color:var(--green); text-shadow:var(--glow); }
|
.btn-primary { color:var(--green); border-color:var(--green); text-shadow:var(--glow); }
|
||||||
.btn-primary::before { content:'> '; color:var(--amber); }
|
.btn-primary::before { content:'> '; color:var(--amber); }
|
||||||
|
.btn-primary::after { content:''; }
|
||||||
.btn-primary:hover { background:var(--green-dim); box-shadow:var(--glow); }
|
.btn-primary:hover { background:var(--green-dim); box-shadow:var(--glow); }
|
||||||
|
|
||||||
.btn-secondary { color:var(--text-dim); border-color:var(--border); }
|
.btn-secondary { color:var(--text-dim); border-color:var(--border); }
|
||||||
@@ -508,7 +535,7 @@ a:hover { text-decoration: underline; text-shadow: var(--glow-amber); }
|
|||||||
font-family: var(--font);
|
font-family: var(--font);
|
||||||
font-size: .7em;
|
font-size: .7em;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
border: 1px solid;
|
border: 2px solid;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
background: transparent;
|
background: transparent;
|
||||||
letter-spacing: .04em;
|
letter-spacing: .04em;
|
||||||
@@ -531,15 +558,15 @@ a:hover { text-decoration: underline; text-shadow: var(--glow-amber); }
|
|||||||
}
|
}
|
||||||
.modal {
|
.modal {
|
||||||
background: var(--bg2);
|
background: var(--bg2);
|
||||||
border: 1px solid var(--green);
|
border: 3px double var(--green);
|
||||||
box-shadow: 0 0 30px rgba(0,255,65,.18);
|
box-shadow: 0 0 30px rgba(0,255,65,.2), 0 8px 40px rgba(0,0,0,.8);
|
||||||
width: 480px;
|
width: 480px;
|
||||||
max-width: 95vw;
|
max-width: 95vw;
|
||||||
padding: 20px;
|
padding: 20px;
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
.modal::before { content:'┌'; position:absolute; top:-1px; left:-1px; color:var(--green); text-shadow:var(--glow); font-size:.9rem; line-height:1; }
|
.modal::before { content:'╔'; position:absolute; top:-1px; left:-1px; color:var(--green); text-shadow:var(--glow); font-size:.9rem; line-height:1; }
|
||||||
.modal::after { content:'┐'; position:absolute; top:-1px; right:-1px; color:var(--green); text-shadow:var(--glow); font-size:.9rem; line-height:1; }
|
.modal::after { content:'╗'; position:absolute; top:-1px; right:-1px; color:var(--green); text-shadow:var(--glow); font-size:.9rem; line-height:1; }
|
||||||
|
|
||||||
.modal-header {
|
.modal-header {
|
||||||
display: flex;
|
display: flex;
|
||||||
@@ -600,7 +627,7 @@ a:hover { text-decoration: underline; text-shadow: var(--glow-amber); }
|
|||||||
.form-group input,
|
.form-group input,
|
||||||
.form-group select {
|
.form-group select {
|
||||||
padding: 6px 9px;
|
padding: 6px 9px;
|
||||||
border: 1px solid var(--border);
|
border: 2px solid var(--border);
|
||||||
font-family: var(--font);
|
font-family: var(--font);
|
||||||
font-size: .8em;
|
font-size: .8em;
|
||||||
background: var(--bg3);
|
background: var(--bg3);
|
||||||
|
|||||||
@@ -4,9 +4,13 @@
|
|||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<title>{% block title %}GANDALF{% endblock %}</title>
|
<title>{% block title %}GANDALF{% endblock %}</title>
|
||||||
|
<link rel="stylesheet" href="{{ url_for('static', filename='base.css') }}">
|
||||||
<link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}">
|
<link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
<div id="lt-boot" class="lt-boot-overlay" data-app-name="GANDALF" style="display:none">
|
||||||
|
<pre id="lt-boot-text" class="lt-boot-text"></pre>
|
||||||
|
</div>
|
||||||
<header class="header">
|
<header class="header">
|
||||||
<div class="header-left">
|
<div class="header-left">
|
||||||
<div class="header-brand">
|
<div class="header-brand">
|
||||||
@@ -46,6 +50,7 @@
|
|||||||
ticket_web_url: "{{ config.get('ticket_api', {}).get('web_url', 'http://t.lotusguild.org/ticket/') }}"
|
ticket_web_url: "{{ config.get('ticket_api', {}).get('web_url', 'http://t.lotusguild.org/ticket/') }}"
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
<script src="{{ url_for('static', filename='base.js') }}"></script>
|
||||||
<script src="{{ url_for('static', filename='app.js') }}"></script>
|
<script src="{{ url_for('static', filename='app.js') }}"></script>
|
||||||
{% block scripts %}{% endblock %}
|
{% block scripts %}{% endblock %}
|
||||||
</body>
|
</body>
|
||||||
|
|||||||
Reference in New Issue
Block a user