158 lines
7.2 KiB
Markdown
158 lines
7.2 KiB
Markdown
# Dark Mode & Visual Polish Design Spec
|
|
|
|
> **For agentic workers:** REQUIRED SUB-SKILL: Use `superpowers:subagent-driven-development` (recommended) or `superpowers:executing-plans` to implement this plan task-by-task.
|
|
|
|
**Goal:** Replace the existing warm-paper light theme with a warm-dark "notebook/sketchbook at night" aesthetic — dark-only, no toggle, no system preference detection. Add paper grain texture, switch to dark terrain map tiles, and tighten typography.
|
|
|
|
**Architecture:** All changes are CSS and one Twig template update. Color tokens live in `tokens.css` (swap values, keep names). Grain texture is a pure-CSS SVG noise layer on `body::after`. Map tiles swap in `map.html.twig`. No new dependencies, no JS changes.
|
|
|
|
**Approach chosen:** B — color token swap + paper grain + typography refinements. Card/hero treatment (Approach C) deferred to a future visual polish pass.
|
|
|
|
**Tech Stack:** CSS custom properties, inline SVG data URI for grain, Stadia Maps tile CDN for dark terrain.
|
|
|
|
---
|
|
|
|
## Global Constraints
|
|
|
|
- Dark-only — no light mode, no `prefers-color-scheme` media query, no toggle
|
|
- All changes in `user/` — commit with `git -C user`
|
|
- No new npm/JS dependencies
|
|
- Existing token names (`--color-paper`, `--color-ink`, etc.) must not change — only values
|
|
- Teal accent `#1F6B5A` lightens to `#2A8C73` for dark-background contrast
|
|
- Map tile provider: Stadia Maps Alidade Smooth Dark (free tier; API key needed for production — see Task 2)
|
|
- `make test-ui` must pass after implementation (25/25 or pre-existing P2 exception)
|
|
|
|
---
|
|
|
|
## 1. Color System
|
|
|
|
Replace all values in `user/themes/intotheeast/css/tokens.css`. Token names are unchanged.
|
|
|
|
### Dark palette
|
|
|
|
| Token | Old value | New value | Role |
|
|
|---|---|---|---|
|
|
| `--color-paper` | `#F7F5F2` | `#1A1814` | Page background — warm near-black |
|
|
| `--color-canvas` | `#FFFFFF` | `#22201B` | Card surfaces, form backgrounds |
|
|
| `--color-ink` | `#17171A` | `#EDE8DF` | Primary text — warm cream |
|
|
| `--color-ink-2` | `#4A4850` | `#B8B0A4` | Body text — muted warm |
|
|
| `--color-ink-muted` | `#9896A0` | `#7A7268` | Labels, timestamps, captions |
|
|
| `--color-border` | `#E8E6E3` | `#2E2B25` | Standard dividers |
|
|
| `--color-border-soft` | `#F0EDEA` | `#252219` | Subtle dividers |
|
|
| `--color-accent` | `#1F6B5A` | `#2A8C73` | Teal — lightened for dark contrast |
|
|
| `--color-accent-hover` | `#185647` | `#236655` | Hover/pressed teal |
|
|
| `--color-accent-light` | `#EBF5F2` | `#1A2E29` | Pale teal tint backgrounds |
|
|
| `--color-accent-on` | `#FFFFFF` | `#FFFFFF` | Text on accent surfaces (unchanged) |
|
|
|
|
### Additional dark-only tokens (add to tokens.css)
|
|
|
|
```css
|
|
--color-surface-raised: #2A2720; /* elevated surfaces: tooltips, hover states */
|
|
--color-ink-inverse: #17171A; /* text on accent-colored buttons */
|
|
```
|
|
|
|
---
|
|
|
|
## 2. Paper Grain Texture
|
|
|
|
Add to `style.css`, in the `body` section:
|
|
|
|
```css
|
|
body::after {
|
|
content: '';
|
|
position: fixed;
|
|
inset: 0;
|
|
pointer-events: none;
|
|
z-index: 9998;
|
|
opacity: 0.035;
|
|
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='200' height='200'%3E%3Cfilter id='noise'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.65' numOctaves='3' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='200' height='200' filter='url(%23noise)' opacity='1'/%3E%3C/svg%3E");
|
|
background-repeat: repeat;
|
|
background-size: 200px 200px;
|
|
}
|
|
```
|
|
|
|
This overlays a fixed noise texture across the entire viewport. `pointer-events: none` ensures it never blocks clicks. `z-index: 9998` keeps it below any modals or dropdowns (which should use z-index 9999+). Opacity 3.5% — subtle enough to feel like paper texture without being distracting on photography.
|
|
|
|
---
|
|
|
|
## 3. Map Tiles — Stadia Alidade Smooth Dark
|
|
|
|
Replace the tile layer in `user/themes/intotheeast/templates/map.html.twig`.
|
|
|
|
**Old:**
|
|
```javascript
|
|
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
|
|
attribution: '© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
|
|
}).addTo(map);
|
|
```
|
|
|
|
**New:**
|
|
```javascript
|
|
L.tileLayer('https://tiles.stadiamaps.com/tiles/alidade_smooth_dark/{z}/{x}/{y}{r}.png', {
|
|
maxZoom: 20,
|
|
attribution: '© <a href="https://stadiamaps.com/">Stadia Maps</a> © <a href="https://openmaptiles.org/">OpenMapTiles</a> © <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
|
|
}).addTo(map);
|
|
```
|
|
|
|
**Production note:** Stadia Maps requires a free API key for production domains. Add the key as a query param when ready: `?api_key=YOUR_KEY`. During development on localhost no key is needed. Add a `<!-- TODO: add Stadia API key before launch -->` comment above the tile layer call so it's not forgotten.
|
|
|
|
Also update the mini-map tile layer in `dailies.html.twig` (same swap — same tile URL, same attribution).
|
|
|
|
---
|
|
|
|
## 4. Typography Refinements
|
|
|
|
Targeted improvements to `style.css` — not a full type system rewrite.
|
|
|
|
### 4a. Entry body readability
|
|
The entry body text (`--text-md` / 1.125rem) already uses `--leading-normal` (1.65) which is good. Increase the paragraph bottom margin slightly for breathing room:
|
|
|
|
```css
|
|
/* current */
|
|
.entry-body p { margin-bottom: 1.1em; ... }
|
|
|
|
/* new */
|
|
.entry-body p { margin-bottom: 1.4em; ... }
|
|
```
|
|
|
|
### 4b. Heading tracking
|
|
DM Serif Display at large sizes benefits from slightly tighter tracking. Find heading rules that currently have `letter-spacing: -0.01em` and tighten to `-0.02em`. Only apply to `h1` and `h2` — smaller headings keep current tracking.
|
|
|
|
### 4c. Login form dark surface
|
|
The login form currently hardcodes `background: #f0f0f0; color: #333` on the secondary button (line ~497 in style.css). Replace with tokens:
|
|
|
|
```css
|
|
/* current */
|
|
.login-form .button.secondary { background: #f0f0f0; color: #333; ... }
|
|
|
|
/* new */
|
|
.login-form .button.secondary { background: var(--color-canvas); color: var(--color-ink); ... }
|
|
```
|
|
|
|
### 4d. Stats numbers
|
|
On the stats page, numeric values should feel deliberate. Add `font-variant-numeric: tabular-nums` to the stat value elements so columns of numbers align cleanly.
|
|
|
|
---
|
|
|
|
## 5. Incidental dark-mode fixes
|
|
|
|
Some existing styles use hardcoded light colors that will look wrong in dark mode. Audit and fix these in `style.css`:
|
|
|
|
- Any `background: #fff` or `background: white` → `var(--color-canvas)`
|
|
- Any `color: #333` or similar hardcoded dark text → `var(--color-ink)` or `var(--color-ink-2)`
|
|
- Any `border: 1px solid #eee` or similar → `var(--color-border)`
|
|
- Focus outline: currently likely a light-mode color — ensure `outline-color` uses `var(--color-accent)`
|
|
|
|
Run a grep for literal hex values after implementation: `grep -n '#[0-9a-fA-F]\{3,6\}' user/themes/intotheeast/css/style.css` — every hit is a candidate to tokenize.
|
|
|
|
---
|
|
|
|
## Verification
|
|
|
|
After implementation:
|
|
1. `make test-ui` — all tests pass
|
|
2. Visual check at `http://localhost:8081/trips/japan-korea-2026/dailies` — warm dark background, cream text, teal accents visible, subtle grain
|
|
3. Visual check at `http://localhost:8081/trips/japan-korea-2026/map` — dark terrain tiles load, GPX polyline visible, entry pins visible
|
|
4. Check the post form at `/post` — form fields readable on dark canvas, no white-on-white or black-on-black surfaces
|
|
5. Run the hardcoded-hex grep and confirm any remaining literals are intentional
|