Files
retire-planner/HEADER-LANG-RIGHT-REPORT.md
T
krisf 043fa8a683 Header: right-align EN|FR toggle in .hdr-in (top-right of page)
One CSS line: .lang-toggle{order:99;margin-left:auto}. Spec's literal order:99 alone
failed its own <=2px right-edge acceptance (gap 45.6-467.6px, 18/24 cases — order
reorders, .hdr-in is left-packed; geo-literal-order99.json), so margin-left:auto on
the toggle absorbs the free space; pill/logo untouched (spec-forbidden zone respected).

Battery: A1 gap=0px + rightmost + nothing-past-left in 24/24 (320/360/390/1280 x EN/FR
x intro/quiz/results); A2 header height unchanged 56/66px; A3 320px no overflow/wrap,
FR logo ellipsis intact, elementFromPoint hit-tests lang buttons + Start over pass;
A4 FR/EN clicks switch lang+title+aria-pressed; A5 24 screenshots before/after; A6
engine 36 + i18n 48 + smoke 92 + ps-pension 70 green; A7 desktop pixdiff header-strip-
only, 0 px below header at every device, self-jitter bit-identical; A8 DEPLOY-OK,
origin marker=1 + byte-identical CSS, off-LAN troublebox 200+marker+rule OFFLAN-OK.
2026-10-09 13:32:40 -04:00

116 lines
6.7 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# HEADER-LANG-RIGHT REPORT — EN|FR toggle right-aligned in `.hdr-in` (top-right of page)
**Run: 2026-10-09, DSH.** Spec: `/home/ubuntu/projects/.hermes/plans/2026-10-09_header-lang-toggle-rightalign.md` (v1).
Baseline: `main @ 4c8fcad` (PS-pension stage B + Hermes post-deploy verification), tree clean, tests green.
Result: **SHIPPED** — deployed, origin + off-LAN verified, committed and pushed.
---
## 1. The change
One added line in `site/style.css` (top level, directly after the existing `.lang-toggle` rule at line 27):
```css
.lang-toggle{order:99;margin-left:auto} /* right-align the EN|FR toggle inside .hdr-in ... */
```
No DOM, JS, or other-CSS changes. `git diff site/` = 1 insertion, 0 deletions.
### Deviation from the spec's literal rule (measured, justified)
The spec preferred `.lang-toggle{order:99}` alone. Applied literally, it **failed the spec's own
acceptance 1** in 18/24 cases: `order` reorders flex items but `.hdr-in` is left-packed (no
`justify-content`), so the toggle became the rightmost element yet sat flush against *Start over*,
`order:99` alone → gap (toggle.right → hdrIn.right−padR) 188.1–467.6px on desktop, 45.6–160px on mobile
(full matrix in `audit/lang-right/geo-literal-order99.json` — re-measured post-hoc against a site copy
carrying the literal rule; the `after/` dir is the shipped rule).
The spec's DOM-move fallback fails the same ≤2px criterion for the same reason (a last child packs
against the previous item). The spec's "order misbehaves" escape clause covers this:
added `margin-left:auto` on the toggle itself — the auto margin absorbs the free space so the toggle
sits flush at the header's right padding edge. The spec only forbade `margin-left:auto` on the
**pill/logo** ("leave the other elements left-packed exactly as today") — untouched, verified:
all non-header-strip pixels on desktop are bit-identical (section 4).
Resulting visual order = exactly the spec's drawing: `[logo] [pill] [Start over] ………… [EN|FR]` desktop;
`[logo] [Start over] …… [EN|FR]` at ≤700px.
## 2. Geometry — acceptance 1/2/3 (24 cases: 320/360/390/1280 × EN/FR × intro/quiz/results)
Measured with `getBoundingClientRect` via Playwright (`audit/lang_right_capture.py`); `gap` =
`(hdrInRect.right − paddingRight) − toggleRect.right`, spec tolerance ≤2px.
| case group | gap BEFORE | gap AFTER | rightmost AFTER | other elements past toggle-left AFTER |
|---|---|---|---|---|
| desk 1280×900 (intro/quiz/results × EN/FR) | 733.7–807.4px | **0.0px** | ✓ 6/6 | none (6/6) |
| iphone14 390 | 95–160px | **0.0px** | ✓ 6/6 | none |
| pixel 360 | 65–130px | **0.0px** | ✓ 6/6 | none |
| se 320 | 25–112.5px | **0.0px** | ✓ 6/6 | none |
- **A1** ✓ every case: gap 0px (≤2px), toggle is the rightmost visible `.hdr-in` child, no other
element's rect extends past the toggle's left edge. Computed `order`: toggle 99, all siblings 0.
- **A2** ✓ header height **unchanged**: 56px at 320/360/390 (≤56 required) and 66px at 1280, identical
before and after (the `.hdr-in{flex-wrap:nowrap;padding:5px 10px}` mobile rule keeps holding).
- **A3** ✓ at 320px: `scrollWidth == clientWidth == 320` (no overflow), zero wrap at every width
(center-Y row test), `elementFromPoint` center hit-tests pass for `#lang-en`, `#lang-fr` and
`#btn-restart` on every screen where they are shown (restart is `hidden` on intro by design).
Logo continuity: EN logo fits 320px (124.6px wide, not truncating — identical to before); FR
"Planificateur de retraite" truncates on quiz/results at exactly 102.1px with
`text-overflow:ellipsis` (`ellipsisPossible: true`), same as before.
## 3. Toggle behavior — acceptance 4 ✓
In-browser click test at 390px (`toggle-click` block in both geo JSONs, before and after identical):
- FR click → `documentElement.lang === 'fr'`, title → “Planificateur de retraite — la retraite
canadienne, modélisée comme il faut”, `#lang-en` aria-pressed=false, `#lang-fr` true.
- EN click → back: lang `en`, original title, aria-pressed true/false. `frOk: true, backOk: true`.
## 4. Screenshots + desktop regression — acceptance 5 & 7 ✓
24 screenshots per version (`{se,pixel,iphone14,desk}-{en,fr}-{intro,quiz,results}.png`) in
`audit/lang-right/before/` and `audit/lang-right/after/`; self-jitter pairs in `before2/`/`after2/`.
PNGs are gitignored (ps-pension precedent); files on disk for Hermes re-verification; geo JSONs are committed.
Jitter-controlled pixdiff (`audit/lang_right_pixdiff.py`, PIL `ImageChops`):
- **Self-jitter**: before vs before2 and after vs after2, 1280×900 intro/results EN+FR:
**0 px, bit-identical** both times → capture pipeline adds no noise; cross-version diffs are trustworthy.
- **Cross-version before↔after**: every diff bbox sits inside the header strip
(desk: bbox y 10–56, header 66px; **0 changed px below y=66**). Mobile re-checked with
DPR-scaled masks (56 CSS-px header → 114/157/171 device-px at dsf 2/2.75/3):
**0 px below the header strip at every device** — page body pixel-identical below the header on all
24 before/after pairs.
## 5. Tests — acceptance 6 ✓ (green at preflight and after the change)
| suite | result |
|---|---|
| `node tests/engine.test.js` | **36 pass, 0 fail** |
| `node tests/i18n.test.js` | **I18N-OK (48)** |
| `node tests/ui.smoke.js` | **SMOKE-OK (92)** — spec said 56; the suite has grown since spec time (PS rounds), gate is “green”, met |
| `node tests/ps-pension.test.js` | **70 pass, 0 fail** |
## 6. Deploy — acceptance 8 ✓
- `bash scripts/deploy.sh` → **DEPLOY-OK**
- Origin (`http://192.168.0.123:8103/`): marker count **1**; new rule present **1×**; served
`style.css` **byte-identical** to local.
- Off-LAN (Midhurst troublebox, `audit/lang_right_offlan.py` → `audit/lang-right-geo-check.json`):
**200** + marker ✓ + served CSS carries `lang-toggle{order:99;margin-left:auto}` ✓ → **OFFLAN-OK**
(2026-10-09T17:31:08Z).
## 7. Files
- `site/style.css` — the one-line fix (the only site change).
- `audit/lang_right_capture.py` — capture + acceptance 1–4 harness (A3 gates: ellipsis-clean truncation
when present; hidden `#btn-restart` on intro is not a hit-test failure).
- `audit/lang_right_pixdiff.py` — header-strip-masked pixdiff + self-jitter.
- `audit/lang_right_offlan.py` — off-LAN troublebox check.
- `audit/lang-right/{before,before2,after,after2}/` — screenshots (on disk; PNGs gitignored),
`geo-before.json` / `geo-after.json` (full 24-case measurements + toggle-click test),
`geo-before-order99.json` (literal-spec-rule evidence).
- `audit/lang-right-geo-check.json` — off-LAN proof.
## 8. Commit
`main`: `site/style.css` one-liner + audit scripts/JSONs, pushed to `origin/main`.
HEADER_LANG_DONE