docs(nerv-theme): new guide covering palette, geometry, FX, configuration, performance, and accessibility
This commit is contained in:
@@ -0,0 +1,274 @@
|
||||
# NERV Theme (Tactical Console Aesthetic)
|
||||
|
||||
[Top](../README.md) | [Shaders & Window](guide_shaders_and_window.md) | [Architecture](guide_architecture.md)
|
||||
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
The **NERV Theme** is a selectable high-density visual variant inspired by the NERV tactical consoles from the *Neon Genesis Evangelion* anime. It uses sharp geometry, a near-black palette, CRT-style scanlines, status flickering, and alert animations to produce a "tactical command center" aesthetic that emphasizes information density and operational urgency.
|
||||
|
||||
This guide covers:
|
||||
|
||||
1. **Design Philosophy** — Why NERV looks the way it does
|
||||
2. **Visual Components** — Palette, geometry, typography, effects
|
||||
3. **Implementation** — The `theme_nerv.py` and `theme_nerv_fx.py` modules
|
||||
4. **Configuration** — `config.toml` keys
|
||||
5. **Performance** — Cost of FX rendering
|
||||
6. **Testing** — Visual verification
|
||||
|
||||
---
|
||||
|
||||
## Design Philosophy
|
||||
|
||||
The NERV theme is built around three principles:
|
||||
|
||||
1. **Black Void Background** — The dominant color is near-black (0,0,0 to 8,8,8 range). This reduces visual noise and lets accent colors pop.
|
||||
2. **Zero-Rounding Geometry** — No rounded corners on panels, buttons, frames, or separators. Sharp rectangles convey "instrumentation," not "consumer app."
|
||||
3. **Operational Urgency** — Status indicators flicker (subtle, low frequency). Errors trigger red border pulses. This is intentional: it makes operational state changes immediately visible to the operator.
|
||||
|
||||
The theme is **opt-in**, not the default. Most users will use the standard dark or light themes. NERV is for users who want maximum information density and a "tactical" feel.
|
||||
|
||||
---
|
||||
|
||||
## Visual Components
|
||||
|
||||
### Color Palette
|
||||
|
||||
| Role | Hex | Usage |
|
||||
|---|---|---|
|
||||
| **Background** | `#000000` | Primary window background |
|
||||
| **Panel** | `#0A0A0A` | Panel and group backgrounds |
|
||||
| **Text Primary** | `#E0E0E0` | Body text, labels |
|
||||
| **Text Secondary** | `#A0A0A0` | Subdued text, hints |
|
||||
| **Accent (NERV Orange)** | `#FF6B00` | Primary action color, active state |
|
||||
| **Accent (NERV Red)** | `#E50000` | Error, blocked, abort state |
|
||||
| **Accent (NERV Green)** | `#00C800` | Success, completed state |
|
||||
| **Accent (NERV Blue)** | `#0080FF` | Information, in-progress state |
|
||||
| **Border** | `#1A1A1A` | Panel and window borders |
|
||||
| **Border Active** | `#FF6B00` | Active focus indicator |
|
||||
|
||||
The accent colors are the iconic NERV orange, red, green, and blue — visible against the black void without being saturated to the point of eye strain.
|
||||
|
||||
### Geometry
|
||||
|
||||
- **Window corners**: Square (0px rounding)
|
||||
- **Panel corners**: Square
|
||||
- **Button corners**: Square
|
||||
- **Frame rounding**: Disabled (`ImGuiStyle.FrameRounding = 0`)
|
||||
- **Window rounding**: Disabled (`ImGuiStyle.WindowRounding = 0`)
|
||||
- **Child rounding**: Disabled
|
||||
|
||||
This produces a "military hardware" feel that contrasts with consumer-app aesthetics.
|
||||
|
||||
### Typography
|
||||
|
||||
- **Primary font**: `Inter` (already used by other themes)
|
||||
- **Monospace font**: `Maple Mono` (for code, telemetry, logs)
|
||||
- **Font sizes**: Slightly smaller than the default theme to maximize information density
|
||||
- **Font weights**: Medium for body, Bold for headers
|
||||
|
||||
### Status Flickering
|
||||
|
||||
The active tier indicator in the MMA Dashboard, and other "operational status" elements, flicker at a low frequency (default: 0.5 Hz, configurable). The flicker is implemented as a sine wave on the alpha channel:
|
||||
|
||||
```python
|
||||
alpha = 0.7 + 0.3 * math.sin(time.monotonic() * 2 * math.pi * 0.5)
|
||||
```
|
||||
|
||||
This creates a subtle "tactical display" effect without being distracting.
|
||||
|
||||
### CRT Scanlines
|
||||
|
||||
A repeating horizontal line overlay applied via the `custom_background` callback. The scanlines are drawn at 2-pixel intervals, with the scanline alpha at ~30% by default (configurable).
|
||||
|
||||
**Implementation**: Uses the FBO pipeline described in [guide_shaders_and_window.md#1-shader-injection-strategy](guide_shaders_and_window.md#1-shader-injection-strategy). The scanline pass is a fragment shader that modulates output color based on the screen-space Y coordinate.
|
||||
|
||||
**Performance cost**: ~1-2ms per frame on a 1920×1080 display with default settings.
|
||||
|
||||
### Alert Animations
|
||||
|
||||
When MMA state transitions to `"blocked"` or `"error"`, a red border pulse animation runs for ~1.5 seconds:
|
||||
|
||||
1. At t=0, the border alpha spikes to 1.0.
|
||||
2. Over 1.5 seconds, the alpha decays exponentially to 0.1.
|
||||
3. The border color is the NERV red (`#E50000`).
|
||||
|
||||
This makes operational failures immediately visible without requiring the user to look at a specific panel.
|
||||
|
||||
---
|
||||
|
||||
## Implementation
|
||||
|
||||
### `src/theme_nerv.py` — Base Theme
|
||||
|
||||
Defines the color palette, geometry overrides, and font selection. The application function applies these to the active ImGui style.
|
||||
|
||||
```python
|
||||
def apply_nerv_theme() -> None:
|
||||
style = imgui.get_style()
|
||||
style.colors[imgui.Col_.window_bg] = (0.0, 0.0, 0.0, 1.0)
|
||||
style.colors[imgui.Col_.text] = (0.88, 0.88, 0.88, 1.0)
|
||||
# ... (all other colors per the palette table)
|
||||
style.window_rounding = 0.0
|
||||
style.frame_rounding = 0.0
|
||||
style.child_rounding = 0.0
|
||||
# ... (geometry overrides)
|
||||
# Apply fonts
|
||||
```
|
||||
|
||||
### `src/theme_nerv_fx.py` — Visual Effects
|
||||
|
||||
Implements the per-frame effects that go beyond static styling:
|
||||
|
||||
- **Status flickering**: Sine-wave alpha modulation for active-state indicators.
|
||||
- **CRT scanlines**: Custom background shader (delegated to `bg_shader.py`).
|
||||
- **Alert animations**: Time-based border pulse on state transitions.
|
||||
|
||||
```python
|
||||
def render_nerv_fx(fx_state: dict) -> None:
|
||||
"""Per-frame NERV FX. Called from the main render loop when NERV is active."""
|
||||
if not fx_state.get("fx_enabled", True):
|
||||
return
|
||||
render_scanlines(fx_state.get("scanline_alpha", 0.3))
|
||||
render_status_flicker(fx_state.get("flicker_rate_hz", 0.5))
|
||||
render_alert_pulse(fx_state.get("alert_state"))
|
||||
```
|
||||
|
||||
### `src/bg_shader.py` — Background Shader
|
||||
|
||||
The custom background shader (used for scanlines) is in `src/bg_shader.py`. It uses the FBO pipeline described in the Shaders & Window guide.
|
||||
|
||||
### Activation
|
||||
|
||||
The NERV theme is activated via the GUI's theme picker:
|
||||
|
||||
```
|
||||
User Menu → Theme → NERV
|
||||
```
|
||||
|
||||
Or programmatically:
|
||||
|
||||
```python
|
||||
from src.theme_nerv import apply_nerv_theme
|
||||
apply_nerv_theme()
|
||||
```
|
||||
|
||||
When activated, the active color set, geometry style, and font are swapped. The FX layer is enabled/disabled via the `[nerv].fx_enabled` config flag (default: enabled).
|
||||
|
||||
---
|
||||
|
||||
## Configuration
|
||||
|
||||
`config.toml`:
|
||||
|
||||
```toml
|
||||
[nerv]
|
||||
fx_enabled = true # Master toggle for all FX
|
||||
scanline_alpha = 0.3 # 0.0 (none) to 1.0 (opaque)
|
||||
flicker_rate_hz = 0.5 # Flicker frequency for active-state indicators
|
||||
alert_pulse_duration_seconds = 1.5 # How long the red border pulse runs
|
||||
alert_pulse_color = "#E50000" # Hex color for the alert pulse
|
||||
```
|
||||
|
||||
| Key | Default | Description |
|
||||
|---|---|---|
|
||||
| `fx_enabled` | `true` | Master toggle. When `false`, no scanlines, no flickering, no alerts. |
|
||||
| `scanline_alpha` | `0.3` | Alpha of the scanline overlay. `0.0` disables scanlines; `1.0` makes them solid (usually too aggressive). |
|
||||
| `flicker_rate_hz` | `0.5` | Frequency of status flickering. `0.0` disables. Values > `2.0` become seizure-inducing. |
|
||||
| `alert_pulse_duration_seconds` | `1.5` | How long the red border pulse runs on errors. `0.0` disables. |
|
||||
| `alert_pulse_color` | `"#E50000"` | Hex color of the alert pulse. |
|
||||
|
||||
### Per-Project Override
|
||||
|
||||
Projects can override the NERV settings in `manual_slop.toml` under a `[theme_nerv]` section (with the same keys). The project override wins over the global `config.toml`.
|
||||
|
||||
---
|
||||
|
||||
## Performance
|
||||
|
||||
| Effect | Cost (1920×1080) | Cost (4K) | Notes |
|
||||
|---|---|---|---|
|
||||
| Scanlines (FBO) | 1-2ms/frame | 4-8ms/frame | Scales with resolution. |
|
||||
| Status flicker | <0.1ms/frame | <0.1ms/frame | Per-element alpha calc, trivial. |
|
||||
| Alert pulse | <0.1ms/frame | <0.1ms/frame | Per-frame interpolation, trivial. |
|
||||
|
||||
**Total NERV overhead**: ~1-2ms/frame on a 1920×1080 display with default settings. For a 60 FPS target (16.67ms/frame), this leaves 14+ ms for the rest of the GUI.
|
||||
|
||||
**Lower-end GPU**: Reduce `scanline_alpha` (cheaper blending) or disable FX entirely (`fx_enabled = false`). The base NERV theme (colors, geometry, fonts) has no measurable GPU cost.
|
||||
|
||||
**Latency impact**: None. The FX layer is purely visual; it doesn't block the main render thread.
|
||||
|
||||
---
|
||||
|
||||
## Interaction with Other Subsystems
|
||||
|
||||
### MMA Dashboard
|
||||
|
||||
The NERV theme enhances the MMA Dashboard with:
|
||||
- Color-coded tier indicators (using the NERV accent colors)
|
||||
- Status flickering on the active tier
|
||||
- Red border pulse on "blocked" / "error" states
|
||||
|
||||
### Diagnostics Panel
|
||||
|
||||
The FPS / CPU / Input Lag display uses the NERV palette. Performance warnings (FPS < 30, CPU > 80%) flash in NERV red.
|
||||
|
||||
### Approval Modal
|
||||
|
||||
When the Execution Clutch suspends on a destructive action, the approval modal uses the NERV orange for the "Approve" button and the standard gray for "Reject". This makes the destructive action visually distinct.
|
||||
|
||||
---
|
||||
|
||||
## Testing
|
||||
|
||||
### Unit Tests
|
||||
|
||||
- `tests/test_theme_nerv.py` — `apply_nerv_theme()` correctly sets all colors, geometry, and fonts.
|
||||
- `tests/test_theme_nerv_fx.py` — FX rendering functions don't raise, produce expected output dimensions.
|
||||
- `tests/test_theme_nerv_alert.py` — Alert pulse animation runs for the configured duration and decays correctly.
|
||||
|
||||
### Visual Verification
|
||||
|
||||
The NERV theme is primarily a visual feature; functional tests are limited. The `[nerv]` config keys are read at theme application time; a misconfigured key defaults gracefully.
|
||||
|
||||
For visual verification in `live_gui` tests:
|
||||
|
||||
```python
|
||||
def test_nerv_theme_active(live_gui):
|
||||
# Activate NERV theme
|
||||
client.activate_theme("nerv")
|
||||
|
||||
# Verify the active theme
|
||||
status = client.get_ui_performance() # Or a theme-specific endpoint
|
||||
assert "theme" in status
|
||||
assert status["theme"] == "nerv"
|
||||
```
|
||||
|
||||
The visual appearance (colors, scanlines) is best verified manually by a human reviewer.
|
||||
|
||||
---
|
||||
|
||||
## Limitations
|
||||
|
||||
1. **Scanlines Are Static**: The current implementation uses a fixed scanline pattern. Animated scanlines (slow vertical scroll) are not implemented.
|
||||
|
||||
2. **No Color Customization in GUI**: The NERV palette is hardcoded. To customize colors, edit `src/theme_nerv.py` directly (no GUI editor for NERV-specific colors).
|
||||
|
||||
3. **No Theme Mixing**: The NERV theme is a single selectable theme. It cannot be mixed with other themes (e.g., "NERV colors but default rounding").
|
||||
|
||||
4. **Performance Cost on Low-End GPUs**: The FBO scanline pass may be too expensive on integrated graphics or older GPUs. The `fx_enabled` flag is the workaround.
|
||||
|
||||
5. **Accessibility**: The black void palette with high-contrast accents is **not** WCAG AA compliant for text contrast in all combinations. The active state color (NERV orange) on black meets AA, but secondary text (gray on black) is borderline. Users with low vision may need a different theme.
|
||||
|
||||
---
|
||||
|
||||
## Future Work
|
||||
|
||||
- **CRT Curvature**: A more advanced shader could apply screen-space curvature to mimic CRT monitors. Currently planned but not implemented.
|
||||
- **Bloom Effect**: For high-saturation accent colors, a subtle bloom could make the NERV orange "glow." Adds GPU cost.
|
||||
- **Animated Scanlines**: Slow vertical scroll of the scanline pattern for a more "active" CRT feel.
|
||||
- **Custom Palette Editor**: Allow the user to override individual NERV colors via the GUI.
|
||||
- **Hybrid Themes**: Allow partial NERV adoption (e.g., NERV colors but default rounding).
|
||||
|
||||
See [guide_shaders_and_window.md](guide_shaders_and_window.md) for the underlying shader and window frame infrastructure.
|
||||
Reference in New Issue
Block a user