Remove aesthetic_diff.md — convergence complete across all three apps

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-14 21:49:18 -04:00
parent 997450aaf1
commit d7eb20cb3c

View File

@@ -1,437 +0,0 @@
# LotusGuild Aesthetic Diff — Convergence Guide
Cross-app analysis of every divergence between Tinker Tickets (PHP),
PULSE (Node.js), and GANDALF (Flask) that prevents a unified aesthetic.
Each section lists the current state in each app, then the **target state**
from the unified design system (`web_template/base.css`).
---
## 1. CSS Custom Property Names
The biggest source of divergence. Gandalf uses entirely different variable names.
| Property | Tinker Tickets | PULSE | GANDALF | **Target** |
|---|---|---|---|---|
| Main background | `--bg-primary: #0a0a0a` | `--bg-primary: #0a0a0a` | `--bg: #0a0a0a` | `--bg-primary` |
| Surface | `--bg-secondary: #1a1a1a` | `--bg-secondary: #1a1a1a` | `--bg2: #1a1a1a` | `--bg-secondary` |
| Raised surface | `--bg-tertiary: #2a2a2a` | `--bg-tertiary: #2a2a2a` | `--bg3: #2a2a2a` | `--bg-tertiary` |
| Primary green | `--terminal-green: #00ff41` | `--terminal-green: #00ff41` | `--green: #00ff41` | `--terminal-green` |
| Green tint | `--terminal-green-dim` | `--terminal-green-dim` | `--green-dim: rgba(0,255,65,.15)` | `--terminal-green-dim` |
| Amber | `--terminal-amber: #ffb000` | `--terminal-amber: #ffb000` | `--amber: #ffb000` | `--terminal-amber` |
| Amber tint | `--terminal-amber-dim` | `--terminal-amber-dim` | `--amber-dim` | `--terminal-amber-dim` |
| Cyan | `--terminal-cyan: #00ffff` | `--terminal-cyan: #00ffff` | `--cyan: #00ffff` | `--terminal-cyan` |
| Red | `--terminal-red: #ff4444` | `--terminal-red: #ff4444` | `--red: #ff4444` | `--terminal-red` |
| Body text | `--text-primary` | `--text-primary` | `--text` | `--text-primary` |
| Dim text | `--text-secondary` | `--text-secondary` | `--text-dim` | `--text-secondary` |
| Muted text | `--text-muted: #00bb33` | `--text-muted: #008822` | `--text-muted: #00bb33` | `--text-muted: #00bb33` |
| Border | `--border-color` | `--border-color` | `--border: rgba(0,255,65,.35)` | `--border-color` |
| Glow (green) | `--glow-green` | `--glow-green` | `--glow` | `--glow-green` |
| Glow (amber) | `--glow-amber` | `--glow-amber` | `--glow-amber` | `--glow-amber` ✓ |
| Font | `--font-mono` | `--font-mono` | `--font` | `--font-mono` |
### Fix for GANDALF (`style.css`)
Add these aliases at the top of `:root` (keeps existing rules working):
```css
:root {
/* Aliases to match unified naming */
--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);
--text-muted: #00bb33; /* override GANDALF's #008822 — too dark */
}
```
Then migrate GANDALF's new code to use unified names. Remove aliases on next major refactor.
---
## 2. Border Width & Style
| Context | Tinker Tickets | PULSE | GANDALF | **Target** |
|---|---|---|---|---|
| Standard card / panel | `2px solid` | `2px solid` | `1px solid` | `2px solid` |
| Modal | `3px double` | `3px double` | `1px solid` | `3px double` |
| Table outer | `2px solid` | — | none | `2px solid` |
| Input fields | `2px solid` | `2px solid` | `1px solid` | `2px solid` |
| Button | `2px solid` | `2px solid` | `1px solid` (`.btn-sm`) | `2px solid` |
**Change required in GANDALF:** Increase `.modal`, `.host-card`, `.data-table`, `.form-group input/select`, `.btn-sm` border widths from `1px` to `2px`. The `1px` borders make elements look fragile compared to the other apps.
```css
/* GANDALF style.css — search & replace */
.modal { border: 1px solid border: 3px double }
.host-card { border: 1px solid border: 2px solid }
.form-group input,
.form-group select { border: 1px solid border: 2px solid }
.btn-sm { border: 1px solid border: 2px solid }
```
---
## 3. Button Pattern
| Aspect | Tinker Tickets | PULSE | GANDALF | **Target** |
|---|---|---|---|---|
| Bracket decoration | `[ text ]` via `::before`/`::after` | `[ text ]` via `::before`/`::after` | `> text` (primary only), no brackets on most buttons | `[ text ]` via `::before`/`::after` |
| Hover transform | `translateY(-2px)` | `translateY(-2px)` | none | `translateY(-2px)` |
| Hover animation | `pulse-glow-box 1.5s infinite` | none | none | none (lift is sufficient) |
| Padding (standard) | `10px 20px` | `12px 24px` | `6px 14px` (`.btn`) | `10px 20px` |
| Padding (small) | `5px 10px` | `6px 12px` | `2px 8px` | `5px 10px` |
| Font size | `0.9rem` | `1em` | `0.72em0.8em` | `0.9rem` |
| Text transform | uppercase | uppercase | uppercase | uppercase |
**Change required in GANDALF:** Add `::before`/`::after` bracket decorations to all `.btn`, `.btn-primary`, `.btn-secondary`, `.btn-danger` classes. Add `translateY(-2px)` hover transform. Increase padding.
```css
/* GANDALF — add to existing .btn rules */
.btn::before { content: '[ '; }
.btn::after { content: ' ]'; }
.btn:hover { transform: translateY(-2px); }
/* Adjust .btn-primary::before to '> ' instead of '[ ' for visual differentiation */
.btn-primary::before { content: '> '; }
```
---
## 4. Glow Definition Consistency
| App | Green glow | Amber glow |
|---|---|---|
| Tinker Tickets | `0 0 5px #00ff41, 0 0 10px #00ff41, 0 0 15px #00ff41` (3-layer solid) | `0 0 5px #ffb000, 0 0 10px #ffb000, 0 0 15px #ffb000` |
| PULSE | Identical to Tinker Tickets | Identical |
| GANDALF | `0 0 5px #00ff41, 0 0 10px rgba(0,255,65,.4)` (2-layer, rgba 2nd) | `0 0 5px #ffb000, 0 0 10px rgba(255,176,0,.4)` |
GANDALF's 2-layer glow is slightly softer. Both look fine, but they appear different on the same screen if pages are compared side-by-side.
**Change required in GANDALF:** Update glow definitions to match the 3-layer solid stack:
```css
/* GANDALF style.css */
:root {
--glow: 0 0 5px #00ff41, 0 0 10px #00ff41, 0 0 15px #00ff41;
--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 #ffb000, 0 0 15px #ffb000;
}
```
---
## 5. Section Header Pattern
| App | Syntax | Example |
|---|---|---|
| Tinker Tickets | `╠═══ TITLE ═══╣` | Symmetric, double-bar bookends |
| PULSE | `═══ TITLE ═══` (via h3::before/after) | No `╠` character |
| GANDALF | `╠══ TITLE` (one-sided) | Only left bookend, no right |
**Change required in PULSE and GANDALF:** Standardise to `╠═══ TITLE ═══╣`.
PULSE (`index.html`, all `h3::before`/`h3::after`):
```css
/* PULSE: update h3 pseudo-elements */
h3::before { content: '╠═══ '; color: var(--terminal-green); }
h3::after { content: ' ═══╣'; color: var(--terminal-green); }
```
GANDALF (`style.css`, `.section-title`):
```css
/* GANDALF: add right bookend */
.section-title::after { content: ' ═══╣'; color: var(--green); }
```
---
## 6. Modal Border Style
| App | Border | Box shadow |
|---|---|---|
| Tinker Tickets | `3px double var(--terminal-green)` | none |
| PULSE | `3px double var(--terminal-green)` | `0 0 30px rgba(0,255,65,.3)` |
| GANDALF | `1px solid var(--green)` | `0 0 30px rgba(0,255,65,.18)` |
**Change required in GANDALF:** Upgrade to `3px double` and add PULSE-style glow:
```css
.modal {
border: 3px double var(--green);
box-shadow: 0 0 30px rgba(0, 255, 65, 0.2), 0 8px 40px rgba(0,0,0,0.8);
}
```
---
## 7. Modal Corner Characters
| App | Top corners | Notes |
|---|---|---|
| Tinker Tickets | `╔ ╗` (double-line) | Matches `3px double` border |
| PULSE | `╔ ╗` (double-line) | Matches `3px double` border |
| GANDALF | `┌ ┐` (single-line) | Doesn't match — should be `╔ ╗` for modals |
**Change required in GANDALF:**
```css
.modal::before { content: '╔'; }
.modal::after { content: '╗'; }
```
---
## 8. Toast Position & Animation
| Aspect | Tinker Tickets | PULSE | GANDALF | **Target** |
|---|---|---|---|---|
| Position | `bottom: 1rem; right: 1rem` | `top: 80px; right: 20px` | `bottom: 20px; right: 20px` | `bottom: 1rem; right: 1rem` |
| Slide direction | from bottom | from top | from right | from right (`translateX(30px)`) |
| Animation duration | `0.3s ease` | `0.3s ease-out` | `0.15s ease` | `0.2s ease-out` |
| Auto-dismiss | 3500ms | 3000ms | 3500ms | `3500ms` |
| Icon format | `>> ` prefix | `✓/✗/` prefix (inline style) | `>> ` prefix | `[✓]` `[✗]` `[!]` `[i]` in `.lt-toast-icon` |
| Queue system | Yes (serialised) | No (stacks) | No (stacks) | Yes (serialised) |
**Change required in PULSE:** Move toast position to `bottom: 20px; right: 20px`.
Replace inline-style `showTerminalNotification()` with `lt.toast.*` calls. The icon format in `base.js` uses bracketed symbols: `[✓]`, `[✗]`, `[!]`, `[i]` — matches the terminal aesthetic better than bare unicode symbols.
**Change required in GANDALF:** Already close — update animation to `slide-in-right` instead of `slide-in` (which slides from left in GANDALF's current implementation). Replace `showToast()` calls with `lt.toast.*` calls.
---
## 9. Table Cell Borders
| App | Approach |
|---|---|
| Tinker Tickets | `border: 1px solid var(--border-color)` on every `<td>` — full grid |
| PULSE | Minimal table usage; when present, `border-bottom` only |
| GANDALF | `border-collapse: collapse` + `border-bottom: 1px solid rgba(0,255,65,.08)` row-only |
Both approaches are valid for different use cases. The design system provides both:
- `.lt-table` → full-grid borders (Tinker Tickets style, simple data)
- `.lt-data-table` → row-only borders (GANDALF style, dense data)
**Action:** Migrate existing tables to the appropriate class. No visual breakage, just choose the right variant per context.
---
## 10. Form Label Colour
| App | Label colour |
|---|---|
| Tinker Tickets | `color: var(--terminal-green)` |
| PULSE | `color: var(--terminal-green)` |
| GANDALF | `color: var(--amber)` (amber labels) |
GANDALF's amber labels create a better visual hierarchy (labels stand out from field values). The unified design system adopts **amber labels** for all apps.
**Change required in Tinker Tickets and PULSE:**
```css
/* Tinker Tickets: assets/css/dashboard.css + ticket.css */
label, .filter-group h4 { color: var(--terminal-amber); text-shadow: var(--glow-amber); }
/* PULSE: index.html inline CSS */
label { color: var(--terminal-amber); }
```
---
## 11. Nav Link Active State
| Aspect | Tinker Tickets | PULSE (tabs) | GANDALF | **Target** |
|---|---|---|---|---|
| Active colour | amber | amber | amber | amber ✓ |
| Active background | `rgba(0,255,65,.08)` | `rgba(0,255,65,.2)` | `rgba(0,255,65,.07)` | `rgba(255,176,0,.15)` (amber tint) |
| Active border | green | amber | `border-color: var(--border)` (invisible) | amber |
| `[ ]` brackets | on `.btn` but not nav | on `.tab` | on `.nav-link` | on nav links |
**Change required in Tinker Tickets:** Add `[ ]` bracket decoration to nav links to match GANDALF. Currently Tinker Tickets has plain nav links without brackets.
**Change required in GANDALF:** Use amber tint background on active state instead of green tint:
```css
.nav-link.active {
background: var(--amber-dim); /* was: var(--green-dim) */
border-color: var(--amber);
}
```
---
## 12. `text-muted` Colour Value
| App | Value | Contrast on #0a0a0a |
|---|---|---|
| Tinker Tickets | `#00bb33` | ~4.8:1 |
| PULSE | `#008822` | ~2.9:1 ✗ WCAG AA fail |
| GANDALF | `#00bb33` | ~4.8:1 |
**Change required in PULSE:** Update `--text-muted` from `#008822` to `#00bb33` to pass WCAG AA contrast.
```css
/* PULSE index.html :root */
--text-muted: #00bb33; /* was: #008822 */
```
---
## 13. Boot Sequence — Presence & Format
| App | Boot sequence | App name in banner |
|---|---|---|
| Tinker Tickets | Yes — `showBootSequence()` in `DashboardView.php` | "TINKER TICKETS TERMINAL v1.0" |
| PULSE | Yes — `showBootSequence()` in `index.html` | "PULSE ORCHESTRATION TERMINAL v1.0" |
| GANDALF | **No** | — |
**Change required in GANDALF:** Add boot sequence overlay to `base.html`.
```html
<!-- Add to base.html <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>
```
Plus add `lt.boot.run('GANDALF');` in `app.js` or inline at end of `base.html`.
---
## 14. Status Badge Format
| App | Format | Example |
|---|---|---|
| Tinker Tickets | `[● Open]``::before`/`::after` brackets | Brackets are pseudo-elements |
| PULSE | `[● Online]` — same pattern | Same |
| GANDALF | `.chip::before { content: '['; }` + `.chip::after { content: ']'; }` | Same pattern — different class names |
The pattern is consistent. The only issue is **class names** differ:
| Component | Tinker Tickets | PULSE | GANDALF |
|---|---|---|---|
| Full status badge | `.status-Open`, `.status-Closed` | `.status.online`, `.status.failed` | `.chip-critical`, `.chip-ok` |
| Small badge | — | `.badge` | `.badge`, `.chip` |
**Standardise to** `.lt-status-*` (full badge) + `.lt-chip-*` (compact) + `.lt-badge-*` (inline label) going forward. Existing class names can remain as app-internal aliases.
---
## 15. Scanline Effect Differences
All three apps have the same scanline `body::before` and data-stream `body::after`. Minor differences:
| Aspect | Tinker Tickets | PULSE | GANDALF |
|---|---|---|---|
| Scanline opacity | `rgba(0,0,0,0.15)` | `rgba(0,0,0,0.15)` | `rgba(0,0,0,.13)` |
| Flicker delay | `30s` | `30s` | `45s` |
| Data stream position | `bottom:10px; right:10px` | `bottom:10px; right:10px` | `bottom:10px; right:14px` |
These are minor. **Standardise to:** opacity `0.15`, flicker delay `30s`, position `bottom:10px; right:14px`.
---
## 16. Hover Transform on Cards / Items
| App | Card hover | List item hover |
|---|---|---|
| Tinker Tickets | `translateY(-2px)` + glow | `translateY(-2px)` |
| PULSE | `translateY(-2px)` | `translateX(3px)` |
| GANDALF | `border-color` change only, no transform | `border-left-width` expansion |
**Standardise to:** Cards use `translateY(-2px)`. List/row items use `border-left-width` expansion (GANDALF approach, less disorienting for dense lists).
---
## 17. CSS Architecture — Inline vs. External
| App | CSS location |
|---|---|
| Tinker Tickets | External: `assets/css/dashboard.css` + `ticket.css` |
| PULSE | **All inline** in `index.html` (`<style>` tag, ~800 lines) |
| GANDALF | External: `static/style.css` |
**Change required in PULSE:** Extract the `<style>` block from `index.html` into `public/style.css`. This enables:
- Browser caching
- Cache-busting with `?v=YYYYMMDD`
- Editor syntax highlighting
- Easier diff when updating to unified base.css
---
## 18. JavaScript Toast Implementation
| Aspect | Tinker Tickets | PULSE | GANDALF |
|---|---|---|---|
| Function name | `showToast(msg, type, duration)` | `showTerminalNotification(msg, type)` | `showToast(msg, type)` |
| Uses CSS classes | Yes (`.terminal-toast`) | No (inline styles) | Yes (`.toast`) |
| Queue system | Yes | No | No |
| Plays audio | No | Yes (Web Audio) | No |
**Standardise all three** to use `lt.toast.*` from `base.js`. The function is already a superset of all three implementations.
Required changes:
- Tinker Tickets: Replace `showToast()` in `toast.js` with `lt.toast.*` calls
- PULSE: Replace `showTerminalNotification()` in `index.html` with `lt.toast.*` calls
- GANDALF: Replace `showToast()` in `app.js` with `lt.toast.*` calls
---
## 19. CSRF Implementation
| App | Method | Token location |
|---|---|---|
| Tinker Tickets | PHP `CsrfMiddleware::getToken()``X-CSRF-Token` header | `window.CSRF_TOKEN` via inline script |
| PULSE | No CSRF tokens (session cookies + API key auth) | — |
| GANDALF | No CSRF tokens (Authelia `SameSite=Strict`) | — |
No breaking changes needed. `lt.csrf.headers()` gracefully returns `{}` when `window.CSRF_TOKEN` is not set, so PULSE and GANDALF are unaffected.
---
## 20. Responsive Design Coverage
| App | Mobile-responsive? | Notes |
|---|---|---|
| Tinker Tickets | Yes — extensive breakpoints, card view below 1400px | Most complete |
| PULSE | Partial — `auto-fit` grid, 90vw modals | No nav changes on mobile |
| GANDALF | Partial — grid wraps, no explicit breakpoints documented | Nav becomes scrollable |
**Change required in PULSE and GANDALF:** Add mobile nav handling (hamburger / collapsible), hide `.lt-nav` below 768px, ensure tables are horizontally scrollable on mobile.
---
## Priority Order for Convergence
| Priority | Item | Effort | Impact |
|---|---|---|---|
| 🔴 High | **GANDALF: CSS variable renaming** (§1) | Medium | Eliminates all naming confusion |
| 🔴 High | **PULSE: Extract inline CSS to file** (§17) | Low | Enables sharing base.css |
| 🔴 High | **All: Standardise toast to `lt.toast`** (§18) | Low | Consistent UX across apps |
| 🟡 Medium | **GANDALF: Border widths 1px→2px** (§2) | Low | Visual parity |
| 🟡 Medium | **GANDALF: Modal → `3px double`** (§6) | Low | Visual parity |
| 🟡 Medium | **PULSE: Fix `--text-muted` contrast** (§12) | Trivial | Accessibility |
| 🟡 Medium | **All: Section headers → `╠═══ X ═══╣`** (§5) | Low | Consistent branding |
| 🟡 Medium | **GANDALF: Add boot sequence** (§13) | Low | Brand consistency |
| 🟢 Low | **GANDALF: Button `[ ]` brackets + hover lift** (§3) | Low | Visual consistency |
| 🟢 Low | **TT + PULSE: Amber form labels** (§10) | Low | Better hierarchy |
| 🟢 Low | **Scanline exact values** (§15) | Trivial | Micro-consistency |
| 🟢 Low | **PULSE: Toast position bottom-right** (§8) | Trivial | UX consistency |
| 🟢 Low | **GANDALF: Glow 2-layer → 3-layer** (§4) | Trivial | Visual consistency |
---
## What Does NOT Need to Change
These differences are intentional or platform-specific and should be preserved:
- **GANDALF's compact font sizes** (`.7em` for table headers) — appropriate for dense network data
- **PULSE's tab-based navigation** — appropriate for single-page app structure
- **Tinker Tickets' full table cell borders** — appropriate for ticket data with many columns
- **PULSE's WebSocket real-time updates** — architecture difference, not aesthetic
- **GANDALF's collapse state stored in sessionStorage** vs. Tinker Tickets using CSS class toggle — both work
- **Status colour values for tickets vs. network events** — apps use different semantic colours for their domain (ticket statuses vs. UP/DOWN states)
- **Priority colour scale** — only Tinker Tickets uses P1P5; the other apps don't need it