# P6-03 — Accessibility and visual evidence triage This is a contextual review of the current automated evidence, not a conformance certificate or a human screen-reader result. The project target remains WCAG 2.2 AA as defined in [Phase 3](../../phase-3/ACCESSIBILITY_PLAN.md). The [M01–M13 operator packet](../../phase-5/p5-15/p5-15-02/MANUAL_ACCEPTANCE.md) remains **NOT VERIFIED**; the owner's postponed reader session is not a waiver. No axe rule, assertion or result is hidden to improve acceptance. ## Evidence provenance and counting The fresh focused Settings run (`.artifacts/phase6/current/ui/semantics.json`, started 2026-10-11 01:03:18.498 UTC) passed **4/4** browser cases with no skips, expected failures or retries. Its four detailed all-enabled axe samples have **0 violation-node occurrences** and **12 incomplete-node occurrences**: `color-contrast` 8, `aria-prohibited-attr` 2 and `aria-hidden-focus` 2. They cover EN/light at 1920×900, NB/dark at 360×900, EN/dark at 1280×900 and NB/light at 390×900. Each sample records zero application-chrome horizontal overflow. The fresh company/language feedback run (`feedback.json`, started 2026-10-11 01:02:06.277 UTC) passed **4/4** native failure/retry/readback cases. Its dialogue snapshots show the translated error within the still-open company form, retained edits and reachable Save/Cancel actions. This report has no detailed axe JSON attachments, so it contributes no axe node count. The first fresh ordinary browser run (`main.json`, started 2026-10-11 01:09:52.804 UTC, 30.1 minutes) completed **153 passed / 4 failed**, with no skips or retries. Its four failures are the retained named-controls tests' obsolete expectation that the company-save error remains `aria-hidden="true"`; the bounded correction now presents that error inside the active dialog. The parent retains that first failure and is running fresh corrected focused/full regressions. **This first run is partial whole-suite evidence, not a full regression pass.** Its successful audit tests provide 49 all-enabled samples and 251 retained-rule samples; they can be triaged without declaring the failed suite accepted. First main source-report SHA-256: `8f513e36724e5b0c35b8d0c236b827faa21091bd59d37342eb11f723f6655d75`. This identifies the first partial run even after the parent's later report archive/rerun. | First-run lane | Scans | Violation-node occurrences | Incomplete-node occurrences | |---|---:|---:|---:| | Detailed all-enabled audit, including explicitly enabled target-size | 49 | 11: landmark-unique 5; heading-order 2; aria-command-name 4 | 342: color-contrast 302; aria-prohibited-attr 28; aria-hidden-focus 9; aria-valid-attr-value 2; target-size 1 | | Retained-rule continuity attachments | 251 | 0 | 1,881: color-contrast 1,621; aria-prohibited-attr 201; aria-hidden-focus 37; aria-valid-attr-value 18; th-has-data-cells 4 | | First-run combined occurrences, distinct schemas counted once | 300 | 11 | 2,223 | The missing 16 retained-rule scans are later checkpoints in the four failed tests (four per variant); the corrected full run must restore and verify those cases. The four new focused Settings cases explain 49 detailed samples compared with the previous 45. Historical Phase 5 totals (312 scans / 39 violation-node occurrences / 2,367 incomplete-node occurrences) provide provenance only. A node may recur across themes, widths and states; repeated occurrences are not independent defects. Retained-rule and all-enabled attachment schemas must be counted separately, and a partial or differently sized suite is not a like-for-like improvement measurement. Final acceptance uses the actual later completed regression report, not this first run's subset. ## Focused Settings incomplete checks | Rule and current occurrences | Concrete target and observed context | Disposition and required acceptance | |---|---|---| | `color-contrast` — 4 | `.MuiBadge-invisible`, unread count `0`, 12px text. Axe explicitly reports short text content and cannot calculate a ratio. | Incomplete; do not turn the reported ratio `0` into a measured contrast failure or a pass. Inspect a settled visible non-zero notification badge in both themes, alongside the hidden zero state. Record actual colours/ratio and understandable unread meaning. M02/M11. | | `color-contrast` — 4 | Focused `#settings-tab-2` on desktop or `.MuiSelect-select` on phone. Axe says another element overlaps the background. | Incomplete. Inspect the settled selected/focused control after transitions, including keyboard focus visibility and 3:1 essential state/focus contrast. Visible text needs its applicable 4.5:1 ratio. Closing menu/backdrop sampling does not certify the control. M07/M11. | | `aria-prohibited-attr` — 2 | Desktop `.MuiChip-root`, `data-testid="admin-version-badge"`, a generic `div` with `aria-label="Application version e2e-verification, commit e2e1234"`. | Incomplete with a concrete semantic concern: axe identifies that this generic role does not reliably support the label. Check actual reader speech for displayed version/commit metadata and long Bokmål wrapping before selecting an appropriate semantic element or removing redundant naming. This bounded Phase 6 review did not change the shell without reader/native evidence of actual missing meaning. M01/M09. | | `aria-hidden-focus` — 2 | Phone `#menu-`, a closing MUI section-selector menu with `aria-hidden="true"`; its selected option still has `tabindex="0"`. | Incomplete, not waived. The check asks whether the option is tabbable **in the current state**. Exercise Tab/Shift+Tab during and after closure, Escape and restoration to the selector; verify hidden options leave keyboard/reader traversal when settled. Preserve MUI's modal isolation. M07/M09/M12. | The active Notifications/Varsler panel is `#settings-panel-2`, named from `#settings-tab-2` with a translated `aria-label` fallback for the mobile selector layout. Current native role/name assertions passed at all four widths/themes/locales. Inactive tab controls reference panels 0/1/3 that remain unmounted, as before: only the active panel exists. This is distinct from a missing **active** label reference. Preserve inactive-panel and Rules-draft lifecycle ownership; verify arrow/activation and spoken selection/panel behaviour in M07 rather than remounting inactive content solely to eliminate an ID inventory warning. ## Sidebar child-name investigation — P6-DC05 Local tooling reviewed: Playwright 1.62.1, as installed in this checkout. An earlier failed performance run's error-context display was noticed to contain expanded child `button` entries without quoted names, followed by their visible text. Its exact generated error-context file was overwritten when the runner reused its output directory; the first failed JSON report and log remain retained. This signal cannot retrospectively prove a missing accessible name, a transition defect or a resolved defect. Current positive evidence is more specific: the focused 1920px Settings **default** `body.ariaSnapshot()` contains `button "Discover jobs"`, `button "Companies"`, `button "Compare offers"`, `button "Trash"`, `button "Profile"` and `button "Connected accounts"`; the badge contributes to `button "Reminders 2"`. The same detailed all-enabled scan has zero violations and passes `aria-command-name` for 17 nodes and `button-name` for 13. The corresponding children in `AppShell.tsx` use visible `ListItemText` for their names; top-level buttons additionally have explicit translated labels. Absence of a redundant explicit `aria-label` on text-bearing controls is not itself a naming defect. The installed Playwright implementation explains why these two snapshot presentations can differ. `node_modules/playwright/lib/index.js` captures error contexts with `page.ariaSnapshot({ mode: "ai" })`. In `playwright-core/lib/coreBundle.js`, `distillAriaSnapshot` selects `aiPlugins` for that mode; its `removeRedundantNames` plugin sets a node's displayed name to an empty string when referenced child content already supplies that information. Default snapshot normalisation uses only string merging and generic-node unwrapping. Thus an unquoted AI-mode snapshot `button` with named descendant content can be deliberate output deduplication. The potential-unnamed-lines inventory is a triage heuristic, not a browser accessible-name assertion. A local isolated [static-markup demonstration](evidence/snapshot-name-demonstration.json), Chromium 151.0.7922.34 / Playwright 1.62.1, reproduced this mechanism without loading the application or making network requests: Chromium CDP exposes `Discover jobs`, `Job Applications` and `Reminders 2` as the three button names, and the exact default role/name lookup finds `Discover jobs`. The default snapshot quotes all three names; AI mode omits the child-derived first/third names while preserving their paragraph/text descendants and the explicit middle label. This is tooling evidence only, not a reconstruction of the overwritten application context. The portable file records the original raw artifact path, write timestamp and SHA-256 `d590c70d4d8b7101e7facf9fa659ba2fd82c86e097526e825de9faf294318608` and retains its original payload. The first fresh full run's default `cv-1440x900-en-light` snapshot also quotes all expanded child names; its name checks pass (`aria-command-name` 17, `button-name` 80 across the page). The deliberate keyboard-open EN 390px drawer sample names the children, focuses Dashboard and has zero axe violations. However, the distinct `shell-390x844-nb-dark-emulated-touch-navigation` sample has **four actual serious `aria-command-name` violation nodes**: Discover jobs, Companies, Compare offers and Trash child controls lack reader-visible text at that captured moment. Its default snapshot shows empty Job search/Career/Insights lists, and geometry still reports focus on the outside `Åpne navigasjon` launcher. This actual axe result is independent of AI snapshot formatting and remains an unresolved drawer transition/visibility/focus investigation. The sample does not establish a persistent settled name failure, and another positive sample cannot dismiss it. No shell labels or navigation semantics were changed for the unconfirmed failed-context formatting signal. The mandatory next acceptance is a settled NB drawer name/AX check together with focus timing after emulated and actual touch, followed by M01/M09/M12 reader/device traversal. Capture the DOM visibility/clip state and a default/Chromium AX name at the failing instant and after transition; an actual settled failure justifies a bounded repair. Do not globally silence the name rule, weaken focus checks or assume this state passed because the enclosing collection test passed. Reader acceptance of expanded/collapsed/mobile navigation and badge speech remains mandatory. ## First-run remaining violation triage | Rule and occurrences | Exact current context | Disposition and next work | |---|---|---| | `aria-command-name` — 4 | NB 390×844 emulated-touch drawer sample; four Jobs child controls, as detailed above. | Genuine automated serious name findings in the sampled state. Transition/visibility/focus cause is unresolved; settled native and operator checks are mandatory. No accessibility GO from the passing collection test. | | `heading-order` — 1 | `jobs-390x900-en-light-add-dialog-keyboard`: Add Job dialog H2, then `
Step 1 of 6: Add job
`; its next subheading is also H6. | Confirmed retained dialog hierarchy gap affecting heading navigation. Add-job wizard/dialog migration belongs to Phase 7; retain as a required semantic correction with the wizard workflow/keyboard contracts, not a renderer/global typography rewrite. M03/M09. | | `heading-order` — 1 | `workspace-768x900-en-light-keyboard-section`: Match panel's Analysis/CV Match H3 sections followed by H6 Strategy snapshot. | Confirmed contextual hierarchy gap in retained existing AI strategy entry. A Phase 7 semantics correction must preserve this existing grounded action and the withheld P5-13 boundary. M05/M09; no model qualification or new review UI. | | `landmark-unique` — 5 | Desktop, keyboard section selector, preview end and phone portrait/landscape CV captures: `#app-main-content` is related to `
` inside `iframe[title="CV preview page 1"]`. | Repeated moderate best-practice cross-frame finding. Parent main and separately titled preview document need actual reader navigation/distinguishability review (M06/M09). Keep white print pages, page geometry and renderer ownership; this finding authorises no pagination change. It is retained, not dismissed as compliant. | ## First-run broader incomplete triage | Category | Concrete additional contexts | Required disposition | |---|---|---| | Prohibited ARIA labels | Generic `span[aria-label="Undo"/"Redo"]` (NB Angre/Gjør om) wrapping disabled editor actions; MUI DataGrid `span[aria-label="Sort"]`; generic desktop version Chip. | Inspect whether the actual child button/header supplies the intended name and disabled explanation, and check tooltip/reader behaviour. Remove or semantically correct only redundant wrapper naming after proving consumers retain names/help. M06/M08/M09. | | Unresolved contrast | Short zero badges, outlined input/tab backgrounds, overlaid modal/selector content and partially obscured table/editor text. | Retain exact targets/check reasons. Measure settled text/non-text/focus ratios and genuine disabled/hidden exceptions; do not equate `contrastRatio: 0` in an incomplete overlap calculation with a measured failure. M10/M11. | | Hidden focus | Modal-hidden `#root` background controls; a hidden/closing command dialog after opening Add Job; closing Settings selector menus. | Background `aria-hidden` alone is expected modal isolation, but tabbable hidden content or lost/escaped focus is not. Complete native Tab/Shift+Tab/Escape/restoration at each state and actual reader isolation. M01/M02/M05/M07/M09. | | Invalid attribute value | Quick Search/Hurtigsøk dialog has explicit translated `aria-label` but generated `aria-labelledby` points to absent IDs (`_r_1o_` / `_r_1q_`) in two detailed states; retained scans repeat this category. | Missing generated label references are concrete semantic debt even with an explicit name fallback. Verify actual dialog name/speech and fix the redundant invalid reference in its bounded Phase 7 dialog migration; preserve command keyboard and result-announcement contracts. M02/M09. | | Target size | Admin Users `data-rowindex="2"` action gridcell has overflow; axe cannot determine size and asks whether its negative-tabindex gridcell is itself a target, reporting a 4.2px spacing candidate. | Assess actual actionable buttons versus grid navigation cells, visible clipping and target/spacing at each width. The applicable 24px AA criterion and exceptions need real target measurements; do not mistake a negative-tabindex cell for a proven undersized button or declare it safe. M08/M12. | | Header/data associations | Four retained `th-has-data-cells` incomplete occurrences in empty/action table contexts. | Read table headers/actions/empty states using the actual data and reader table navigation. Verify a header represents real data or has a deliberately justified action column; no blanket rule disabling. M03/M04/M08/M09. | ## Agent visual observations — focused runs These images were actually inspected with the local image viewer on 2026-10-11. They are automated synthetic-fixture captures. Observation of a visible focus ring or wrapped label does not prove its measured contrast, spoken output, physical hit area or software-keyboard behaviour. The eventual full-run manifest will separately identify the exact copied images and hashes; inspecting an earlier focused image does not mark a later image inspected automatically. | Focused screenshot | Actual observation | Remaining limitation or friction | |---|---|---| | `settings-360x900-nb-dark-named-notification-panel` | The two-row shell fits the 360px capture. The Varsler selector has a visible focus outline. The notification heading, description, checkbox and explanatory text wrap inside the content surface; Save and Open Reminders remain visible. Surrounding dark chrome uses distinct surface/border boundaries. | The save action retains title-style capitalisation (`Lagre Varslingsinnstillinger`), an existing copy-style variation for Phase 7 review. Focus contrast and actual touch/reader operation remain unqualified. | | `settings-1920x900-en-light-named-notification-panel` | The selected Notifications tab has a clear rectangular focus outline; its white content surface is separated from the pale page. The save action is the dominant filled action and Open Reminders is outlined. Wide-screen content stays bounded rather than stretching form lines across the viewport. | Consider broader Settings migration/copy consistency only in its approved Phase 7 scope. The image cannot resolve axe's overlapped-background calculation or speak the tab/panel relationship. | | `company-nb-failed-save` | At phone width the translated error is directly below the dialogue title, the edited name with `æøå` remains visible, every form label fits, and Save is focused and available above Cancel. The error is persistent within the form. | The retained full-height mobile dialogue has substantial space between fields and footer. Native date input placeholders show `dd/mm/yyyy` in this English-host Chromium fixture; distinguish browser-controlled date chrome from translated application labels and test a Norwegian browser/device in M07/M12. No data-format or native-input rewrite is justified by this capture. | | `language-nb-failed-account-save` | The chosen `Norsk Bokmål` remains visible with a focus outline. The bottom error says the language changed on this device but failed to save to the account; `PRØV IGJEN` wraps into two readable lines at 360px. The toast fits the viewport and the General section fields wrap within their surfaces. | The snackbar overlays the lowest visible pagination content at this instant. Verify keyboard traversal/scroll and unobscured focus while it remains open, then actual speech and physical keyboard occlusion; the screenshot alone cannot close those gates. | ## Agent visual observations — first main capture set All **14 exact copied images** in the first-run [visual manifest](evidence/visual-manifest.json) were separately opened in the local image viewer on 2026-10-11, and their file hashes were verified before setting `inspectedByAgent: true`. That manifest retains the **153 passed / 4 failed** whole-run status and source hash. These inspections concern those exact first-run files; a subsequent collector that replaces an image needs a new inspection. Human acceptance remains NOT VERIFIED. | Actual copied captures | Observed design/UX result | Remaining debt or evidence limit | |---|---|---| | EN desktop and NB phone company failures | Error stays inside the labelled dialog above retained edits. EN desktop uses a two-column form with a clearly focused primary Save; NB phone fits each field and keeps Save/Cancel at the footer. | Native date chrome reflects the English-host browser; mobile full-height spacing is a retained presentation variation. Actual reader announcements, keyboard occlusion and 24px target measurements remain manual requirements. | | NB language failure; EN wide and NB phone Notifications | The translated device-versus-account save explanation and wrapped Retry are readable. Settings uses bounded desktop content and a named phone section selector; primary Save is distinct from the secondary destination action. | The language toast overlaps the lowest visible pagination content at this moment; verify unobscured focused content during its lifetime. Focused-ring images do not measure contrast or reader speech. | | Authentication at 320px | Sign-in title, credential fields, Remember me, password recovery and primary sign-in action fit inside the capture; the focused username field has a visible outline. Disabled provider actions remain readable as disabled choices. | Large rounded authentication surface is retained Phase 7 presentation, not a new Phase 6 migration. Provider integration, actual 200/400% operator tasks and disabled text contrast remain independently qualified. | | Jobs at 320px NB/dark; Board at 390px EN/light | Table/Board state and result count are visible. Search/filter labels wrap inside the toolbar; Jobs uses phone cards and the long synthetic company/job text wraps. Board shows a whole first-column card and a glimpse of the next column, reinforcing its horizontal workspace. | Only the currently visible content is inspected; lower card actions still require scrolling/native workflows. Board column scrolling is legitimate two-dimensional content, not evidence of application-chrome overflow. Physical drag and software keyboard remain open. | | Dashboard 1440px EN/light | Page purpose, onboarding progress, four labelled metrics and their population/time captions have distinct hierarchy. Today and pipeline surfaces begin below the metrics; contextual actions remain visible. | Onboarding occupies substantial vertical space while incomplete, consistent with its purpose. The image does not certify dashboard timing/reliability, source correctness or future Phase 8 drag/resize. | | Analytics 390px EN/light | Period/population explanation wraps; selected six-month control is distinct and the 24-month alternative wraps onto a second line. Activity report labels its date range, recorded-response fraction, line styles and exact-values continuation. | Only one chart is visible. Chart values/provenance are verified by fixtures/native tests separately; charts' exact-values disclosure, focus and non-colour meaning require reader/keyboard checks. | | Job workspace 320px EN/light | Long title/company wrap; status and edit action stay visible. Progress and section tabs scroll within their own area; selected Timeline and Activity History are visible with a focus outline. | At the shown scroll position the previous Application tab is partly off the left edge. This is a tab-strip scroll state, not hidden evidence of a removed section; verify every tab can be deliberately revealed/reached, including spoken state. | | Career Profile 390px NB/dark | Profile/import surfaces fit, revised avatar initials are dark against the existing pale primary surface, long synthetic account identity wraps and source-workspace content remains visible. | This **first-run baseline** shows the floating `Område i karrierearbeidsområdet` selector label clipped at the top of its rounded navigation surface. It triggered the bounded P6-03 correction below; its later native RED/GREEN and separately inspected before/after images confirm the repair. The original baseline image remains retained and is not relabelled as repaired. | | CV 1440px EN/light; mobile physical-preview capture | Desktop editor and preview remain separate, with source-versus-CV guidance prominent. White print page is bounded by preview chrome. Phone Edit/Preview controls and PDF action fit; Preview has a visible focus ring and the page is fit to width with an explicit percentage and page count. | Small document text at fit-width is expected print-canvas scaling; zoom/document access and actual reader traversal remain necessary. Editable long CV name scroll/clipping is distinct from route-chrome reflow. These images do not qualify production fonts/photos or paginate by appearance; physical measurements/fidelity proofs remain authoritative. | ## Bounded P6-03 mobile Career label repair — verified native RED/GREEN The first-run image and two new native cases confirmed a real clipped field label, so this bounded correction was completed in Phase 6. `CareerProfilePage.tsx` changes only the mobile FormControl from padding `1.25` to margin `1.25` with automatic width, and removes the InputLabel's duplicate left margin. That moves the absolute floating label inside the existing clipped Paper while keeping the Select's usable inset. Paper overflow, desktop tabs, label/Select association, section handlers, default routes and profile/CV save ownership remain unchanged. No translation, framework or renderer changes accompany it. The separately retained [label proof](evidence/career-selector-label-proof.json) records three actual runs, report hashes, exact geometry and four before/after image hashes. It does not overwrite the first 14-image baseline manifest: | Native run | Actual result | Meaning | |---|---|---| | RED, 2026-10-11 08:38:01.973 UTC | **0 passed / 2 failed**, no skips/retries. Both NB/dark 320px and EN/light 390px report `labelTop=200` versus clipping `navTop=208`. | Ordinary assertions first reproduce 8px of vertical clipping. Initial test did not attach all remaining bounds; the proof retains only its actually reported values. | | First post-CSS attempt, 08:42:52.719 UTC | Both label geometry checks pass (`210 ≥ 208`, bottom `225.09375 ≤ 272`), then **2 tests fail** because a newly written ancillary assertion incorrectly expects `section=overview`. | Existing approved navigation removes the default query and reaches `/career`. Parent corrected the test to the exact existing URL and selected Overview text; no route-handler change and no masked failure. This failed attempt is retained. | | Final focused GREEN, 08:44:30.468 UTC | **2/2 passed**, no skips/retries, 39.4 seconds. Both variants retain `labelTop=210`, `labelBottom=225.09375`, `navTop=208`, `navBottom=272`; application-chrome overflow is 0px. Keyboard selection reaches `/career` with Overview selected. | Finite native proof of the specific label repair and preserved existing selector/default-route behaviour. The parent final whole-suite regression remains independently required. | All four exact RED/GREEN screenshots were opened with the local image viewer and hash-verified before marking inspection. The original RED images visibly crop the upper part of the floating label; the GREEN images show the entire EN `Career workspace section` and NB `Område i karrierearbeidsområdet` label above the Select outline inside the same surface. Surrounding profile/import controls retain their layout and translated names. This closes the specific automated/agent-visual clipping finding; it does not close human reader, touch, zoom/forced-colour or production font/photo acceptance. ## Remaining operator dependencies | Area | Current evidence and limit | Still required | |---|---|---| | Landmarks, headings, names and reading order | Native/DOM assertions can verify exact current relationships. They do not prove speech, browse-mode order or understanding. | M01–M09 with actual NVDA/Narrator/browser versions and exact observed speech; names, validation/help relationships, live announcements and restored focus. | | Modal/transition focus | Axe hidden-focus or overlapping-background results require state-specific triage. A static early snapshot cannot decide settled focus. | Opening, nesting, Escape, Tab/Shift+Tab and trigger restoration, including the mobile drawer and selector. M01/M02/M05–M09. | | CV editor/preview | White document pages and separately named preview documents preserve print output. A parent/iframe landmark warning is not permission to alter pagination geometry. | M06/M09 actual reader traversal between editor and preview, source/override distinction, section order, Save/Discard and long-document access; retain fidelity proofs separately. | | Reflow, zoom and visibility | Finite browser widths, browser-engine zoom and text-spacing assertions support only their recorded Chromium cases. | M10 actual task execution, text-only zoom and supported other browsers; M11 forced colours, contrast and reduced motion; focus not obscured by sticky chrome. | | Physical touch and mobile AT | Emulated phone geometry/pointer input and screenshots cannot qualify real keyboards, gestures, drag or touch targets. | M12 physical portrait/landscape/software keyboard and actual VoiceOver/TalkBack; 24px AA criterion/spacing exceptions and 44px touch aim recorded separately. | | Production font/photo output | Local synthetic fixtures cannot establish production Linux font availability or real photo rendering. | Existing production font/photo qualification and F04/release gates. Production remains NO-GO. | | Withheld AI capability | No qualification or AI review activation is included in these scans. | M13 applies only after P5-13's independent model/grounding/human qualification; its UI remains deferred. | Automated and agent visual observations will be recorded separately from operator acceptance. No PRES group is promoted to PASS by this triage alone, and no manual field is filled from an expected result, axe status or screenshot.