P4-03 — Builder preview keyboard boundary
Implementation and local checkpoint validation complete on 2026-09-12, from ebd4a3e24541dabd7ff3b732c07fa972ab843f96 on codex/ui-redesign-phase4. Approved scope: P4-03, coordinated with archived audit A02/F14. No concurrent D01 work was present in the clean starting tree. No save/revision refactor, renderer replacement, route/layout redesign, P4-04 or B07. F04 remains open. The commit containing this record is the implementation checkpoint (git log -- docs/redesign/phase-4/p4-03).
Reconciliation before implementation
The actual CvBuilderEditor owns preview fitting, measurement, pagination, mobile panes, settings history and saves. Its overflowing page container has neither a name nor an explicit keyboard tab stop. Visible titled page iframes and the hidden measurement iframe are separate from that container. Keep their document rendering/links and the existing P4-01 focus CSS; do not add focusability to every Box or replace the preview engine.
Phase 3's future independent editor panes, manual zoom and broader content architecture are not added by this ticket. Existing compact presentation changes at 900px, while the MUI grid changes at 1024px; preserve and test both boundaries rather than silently unifying them. Prior records retain the CV overflow warning, not a requirement to repair document pagination/print layout in this keyboard slice. Phase 2's F15 shorthand for preview focus is superseded by Phase 1/3's corrected F14/A02 attribution.
Change-impact mapping before application changes
| PRES contracts | Retained behaviour → P4-03 change | Existing automated protection | New automated / visual evidence required |
|---|---|---|---|
| PRES-C06, PRES-E08 | Server-rendered pages, fit and mobile Edit/Preview → named keyboard-scrollable preview | cv-builder-deep-link; redesign-preservation; semantic-foundations | Real one/multipage keyboard entry, scroll, exit; EN/NB, themes, phone/tablet/desktop transitions; visible shared focus |
| PRES-C07, PRES-C10, PRES-C11 | CV-only overrides, local undo/redo, autosave/draft and AI-review lifetime → unchanged through preview focus/resize | cv-builder-deep-link; ai-section-assistant; canonical/sibling browser isolation | Edit/Preview/resize/undo/redo retains current CV settings; native API persistence and canonical/sibling isolation; inherited AI apply/reject tests |
| PRES-C08, PRES-C09, PRES-C12 | Template settings, document metrics/pagination, shared preview/PDF → rendering retained | cvBuilder; CvBuilder backend; long searchable PDF and public PDF browser; B03 admission | Native loaded pages and preview retry; full existing render/PDF suite; no template/printed-paper change |
| PRES-E09 | Account-owned preview/settings and private-tree lifetime → unchanged | session-boundary; account-isolation; backend ownership/session tests | Full inherited security/browser checks; no new preview network path |
These scoped rows extend the 57-group preservation matrix and Phase 3 impact matrix. They do not replace any contract or claim exhaustive visual coverage. Tests were added before the corresponding application changes; executed results follow below.
Reproduction and implementation
The initial native Chromium regression against P4-02 loaded a real one-page CV and failed because there was no Live preview region. After adding that boundary, a second regression narrowed a focused desktop preview to 390px and reproduced its disappearance: the stored mobile preference still selected Edit. These match the archived A02 requirement for a named scroll container and focus preservation across viewport transitions. The before logs are retained locally as .artifacts/p4-03-before.log and p4-03-transition-before.log.
Only job-tracker-ui/src/views/CvBuilderEditor.tsx changes application behaviour:
- The existing scroll Box becomes a named
region, using the existing translatedcvEditorLivePreviewstring, with one keyboard tab stop. Native Page Down and Ctrl+Home/End operate on this existing scroll container. No custom key handler, focus trap or preview component is added. - Focus in either existing pane records that pane in
mobilePane. A focused preview stays visible when the viewport narrows; a focused editor field stays in Edit. This is a deliberate presentation-preference correction. Existing Edit/Preview buttons and component instances remain, without touching settings, undo stacks, save queues or resource identity. - P4-01's existing
[tabindex]:focus-visiblerule supplies the 3px semantic outline. No new colour, spacing, theme override or visual component system is added. Both iframe types, sandboxing, document HTML, page measurement/cropping, fit arithmetic and PDF paths remain unchanged.
Retained implementations: CvBuilderEditor, CvCustomizeTab, native iframes, MUI theme, translations, server renderer and all editor persistence handlers. Refactored/replaced implementations: none. Added capability: explicit keyboard access to the existing preview region and focus-aware reuse of the existing responsive preference; no new CV product feature.
Changed-file inventory: the editor file above; new job-tracker-ui/e2e/builder-preview-keyboard.spec.ts; this p4-03 record, scan evidence and screenshots; successor links in docs/redesign/phase-2/TRACEABILITY.md and docs/redesign/phase-4/README.md; current status/deferred work in docs/roadmap/todo.md. No existing test cases or production files outside the editor are changed.
Behavioural regression coverage
job-tracker-ui/e2e/builder-preview-keyboard.spec.ts adds four real-API workflows: light/English, dark/Bokmål, light/Bokmål and dark/English. Every workflow creates a disposable non-admin account, a canonical profile, a one-page CV and a longer CV. Browser requests cannot leave localhost. Authentication, B02 context, source/variant persistence and rendering use the real local API; one preview POST is deliberately delayed and failed with 503 to test recovery. No model/provider or production request is made.
Each workflow verifies:
- Real loaded one-page and multipage documents; named region reached through Tab, visible inherited focus and measured outline contrast of at least 3:1 against the adjacent application surface.
- Page Down changes the preview scroll position; Ctrl+End reaches the bottom and Ctrl+Home returns to the top. Shift+Tab leaves the region, Tab returns, and forward traversal through existing page iframes exits. Titled iframes are retained in the tab sequence.
- CV-only headline edit/autosave; keyboard Edit/Preview and preview entry at 390, 768 and 899px, then populated scans at 900, 1024 and 1440px. The same editor field/value survives these transitions.
- Resizing while the preview owns focus retains visible preview focus; resizing while the editor owns focus retains that visible field. Local Undo/Redo still works after the full sequence.
- A held preview update retains the previous document. Failure keeps the focused region and old content; Retry renders the current settings. Subsequent Redo/save succeeds. Native API reads prove the canonical profile and sibling CV are unchanged.
Existing Jest tests already protect variant loading/denial, preview retry, dirty save, Undo/Redo, AI review and related state. No redundant DOM-attribute-only Jest case was added: browser behaviour is the new missing evidence. Frontend count remains 345, while browser actual passes increase by four from 39 to 43. The existing P2-D01 expected failure remains unchanged and is not counted as a successful frozen-package workflow.
The first forward-traversal test pressed a fixed number of Tab keys and could re-enter after successfully exiting. The corrected test observes each step and stops on exit; recorded traversal includes page 1, page 2, then the outer body for the synthetic no-link document. No iframe was removed from keyboard access to make that test pass.
Visual and accessibility evidence
Screenshots cover seven states for all four theme/language combinations, with a 1440×900 desktop and 390/768/899×844 narrow viewports. The browser also exercises 900 and 1024px. This preserves the distinct 900px pane and 1024px grid boundaries. Reduced motion is active. Captures wait for a paint cycle; no screenshot-only application styling is applied.
| Combination | Representative screenshots |
|---|---|
| Light / English | One page, last page, phone, loading, error |
| Dark / Bokmål | One page, last page, phone, tablet, error |
| Light / Bokmål | One page, phone, near breakpoint, error |
| Dark / English | One page, phone, near breakpoint, error |
The scope is keyboard access to the document viewport. It is not certification of rendered margins, page breaks, theme/template fidelity or all print content. Printed paper remains white in both application themes. Existing small scaled text on phone, long editor tabs, fixed-shell occlusion during page scrolling and overflow warnings remain visible in evidence; the region's focus sides remain visible. No overflow clipping workaround or page-height/layout change was introduced.
Manual Narrator/NVDA, mobile screen reader, real touch/software keyboard, 200%/400% browser zoom, 320px reflow and forced-colors testing remain unverified. DOM naming, real Chromium keyboard traversal and axe do not substitute for that evidence. Page iframe links/rendered content retain their previous behaviour; the synthetic focus-exit fixture is not an exhaustive screen-reader/link traversal of every template.
SCAN_RESULTS.json records the general-run totals and the final focused run's nodes, incomplete-rule counts and forward Tab traversals. The general run has 74 scans (38 P4-01, 20 P4-02 and 16 P4-03), with the existing command list finding in four scans, mobile workspace scrollable-region-focusable in two and light Tooltip color-contrast in two. The final focused rerun has 16 scans: 14 without violations and the two explicit light Tooltip findings. No new preview-region violation is reported. This focused rerun repeats the four added cases; it does not add another four cases to the baseline count. All 28 committed screenshots were refreshed during that final run and representative rendered states were inspected in both modes/languages and at desktop/phone/tablet widths.
Existing defects intentionally deferred
- P4-03-D01 — shared Tooltip contrast: a full loaded scan exposed the existing light Redo tooltip at 3.89:1 (white on composite
#81808a). Its MUI/default colour path andtheme.tsare unchanged from P4-02. Full findings are retained; the test narrowly recognizes only acolor-contrastfinding targeting.MuiTooltip-tooltipin the final post-Redo scan. Every preview rule and other contrast target remains blocking. Repair in a separately scoped shared-theme follow-up; this is not an all-accessibility pass. - Existing CV rendering accuracy debt: Connor reiterated that margins and theme/template output remain incorrect. The minimal one-page fixture also retains the existing horizontal-overflow warning. The future CV rendering work needs concrete expected margin/page/template output and preview/PDF comparison across preserved settings (PRES-C06/C08/C09/C12). This ticket does not fix or claim correctness for that renderer. The earlier statement that the preview will be overhauled is a direction for a dedicated later scope, not authorization to fold it into P4-03 or P4-04.
- Existing preview state announcements: updating/unavailable chips and retry remain the current implementation. Their visual recovery and retained focus are tested; a comprehensive preview announcement design/manual AT audit is not added here.
- P4-02-D01–D04, P2-D01, stale-record concurrency and F04 remain open. The audit's D01 and redesign P2-D01 are different issues; neither is addressed by pane focus.
Validation and handoff
The inherited baseline is 916 backend, 345 frontend, 39 browser actual passes plus P2-D01 expected failure, rate-limit 9, ingress 6, PDF admission 1 and Compose/configuration 3. The final application change precedes all complete suite/build checks below. A subsequent capture-only test adjustment scrolls the existing loading/error label into view for its screenshot; it does not change application behaviour or remove assertions.
| Gate | P4-02 baseline | P4-03 final result |
|---|---|---|
| Backend Release xUnit | 916 pass | 916 pass, 0 failed/skipped |
| Frontend Jest | 345 pass / 73 suites | 345 pass / 73 suites; no cases removed |
| General Chromium | 39 actual pass + P2-D01 expected failure | 43 actual pass + unchanged P2-D01 expected failure; +4 cases |
| Focused preview Chromium | No dedicated fixture | 4 pass; final capture rerun, 3.2 minutes |
| Standalone TypeScript / affected E2E type check / lint | pass | pass; no TS2571 recurrence |
| Frontend production / API Release build | pass | pass; API warnings-as-errors, 0 warnings/errors |
| Account/IP rate-limit HTTP checks | 9 pass | 9 pass |
| Production-mode ingress HTTP checks | 6 pass | 6 pass |
| Shared PDF admission HTTP probe | 1 pass | 1 pass |
| Effective Compose / adversarial configuration | 3 pass | 3 pass |
Reproduction commands, from repository root unless changed below:
$env:DOTNET_ROOT='C:/Users/Connor/.dotnet'
& C:/Users/Connor/.dotnet/dotnet.exe test JobTrackerApi.Tests/JobTrackerApi.Tests.csproj --configuration Release --no-restore --logger 'trx;LogFileName=p4-03.trx' --results-directory .artifacts/redesign-p4-03 -- xUnit.parallelizeTestCollections=false xUnit.maxParallelThreads=1
& C:/Users/Connor/.dotnet/dotnet.exe build JobTrackerApi/JobTrackerApi.csproj --configuration Release --no-restore -warnaserror
python scripts/test-ingress.py
Set-Location job-tracker-ui
npm test -- --watchAll=false --runInBand
npm run typecheck
node node_modules/typescript/bin/tsc --project ../.artifacts/p4-03-e2e-tsconfig.json
npm run lint
$env:NEXT_PUBLIC_API_BASE_URL='http://localhost:5302/api'
$env:NEXT_TELEMETRY_DISABLED='1'
npm run build
$env:DOTNET_HOST_PATH='C:/Users/Connor/.dotnet/dotnet.exe'
$env:PLAYWRIGHT_STATIC_EXPORT='true'
$env:PLAYWRIGHT_PREBUILT_EXPORT='true'
$env:P4_03_CAPTURE='true'
$env:PLAYWRIGHT_JSON_OUTPUT_FILE='../.artifacts/p4-03-browser-results.json'
node node_modules/@playwright/test/cli.js test --reporter=list,json
$env:PLAYWRIGHT_JSON_OUTPUT_FILE='../.artifacts/p4-03-final-focused.json'
node node_modules/@playwright/test/cli.js test e2e/builder-preview-keyboard.spec.ts --reporter=list,json
node node_modules/@playwright/test/cli.js test --config=playwright.rate-limits.config.ts
node node_modules/@playwright/test/cli.js test --config=playwright.ingress.config.ts
node node_modules/@playwright/test/cli.js test --config=playwright.pdf.config.ts
Set-Location ..
python scripts/supply-chain.py scan-secrets
git diff --check
git diff --cached --check
The ignored E2E type config extends the existing frontend tsconfig, includes only e2e/builder-preview-keyboard.spec.ts, disables incremental caching and resolves frontend Node types. No package, dependency, CI, infrastructure, backend, schema or theme change was needed. Logs, isolated SQLite data and authenticated browser traces remain ignored; committed screenshots contain only synthetic data.
The complete general run took 6.7 minutes. Its console reports “44 passed” because the unchanged expected failure is an expected outcome. JSON distinguishes 43 passed → passed plus one failed → failed, with no unexpected result. Actual passes comprise 23 pre-P4-01 + 12 P4-01 + 4 P4-02 + 4 P4-03. The full backend suite includes B01–B05 guards; the independent HTTP probes additionally exercise B02/B04 principal/session boundaries, B06 forwarded trust and B03 shared resource admission. None of this is evidence of deployed F04 closure.
Final repository review: the staged tracked-secret scan passed; git diff --check and git diff --cached --check passed; 117 current/root Markdown documents were checked with zero missing local link targets (historical archive snapshots excluded). The staged non-document diff contains only the existing editor and its new browser fixture. No test skip, expected-failure rename, package/CI modification or unrelated application change is included.
The scoped acceptance criteria are satisfied locally: the existing preview has a named keyboard boundary, native scrolling and exit work, focused panes survive resize, and the existing edit/save/Undo/Redo and ownership regressions remain healthy. The retained tooltip defect and manual/rendering/production gaps above are not claimed closed. Rollback is limited to the three editor attribute/handler changes; there is no data migration or setting-format change.
Recommended next ticket only: P4-04 — page layout and typography primitives, under its separate approved consumer scope. No P4-04 implementation, broader CV rendering work, B07 or production deployment is included in this checkpoint.