diff --git a/docs/reference/accessibility-audit.md b/docs/reference/accessibility-audit.md new file mode 100644 index 0000000..83c4d20 --- /dev/null +++ b/docs/reference/accessibility-audit.md @@ -0,0 +1,73 @@ +# Accessibility audit — C1 + S8 (T9.5, 2026-08-19) + +Approved Aug 14 2026 (C1): any component rebuilt ships accessible or it is not +done. This document records the audit at the end of wave 9 against the wave 0 +baseline, per CLAUDE.md's rules. Every number below is re-measured by a probe +on every run — the citations name which one. + +## The metrics + +| Metric | Wave 0 baseline | Now | Target | Verified by | +|---|---|---|---|---| +| `
` / `` with `onclick` | 12 / 2 | **0** | 0 | `helptip_check.py` (grep, comments stripped) | +| `.help-tip` unreachable by keyboard | 15 (18 by wave 6) — re-measured at T9.5 start: **20** | **0** | 0 | `helptip_check.py` (driven with real keys and taps) | +| `aria-live` regions | 0 | ≥1 per toast system and banner (login, both toasts, release banner, autosave indicator, list-import reports, field toast) | ≥1 each | `a11y_check.py`, `warning_check.py`, `creator_dialogs_check.py` | +| Text below 4.5:1 | present | none found on the audited surfaces | 0 | `a11y_check.py` (creator sweep), `frame_check.py` BL-013 note | +| `outline: none` without replacement | present | **0** (grep with replacement detection) | 0 | `helptip_check.py` | +| Native dialogs | 79 | **21** | 0 or documented | `creator_dialogs_check.py` prints the count; see the gap below | + +**The count went up before it went down, exactly as the task predicted:** the +wave 6 exit counted 18 unreachable help-tips; at the start of T9.5 there were +**20** (T6.x and wave 7/8 tasks reused the component as designed). All 20 are +buttons now — the fix is in the component (`help.js` upgrades every badge at +load and exposes `helpTipUpgrade()` for late renders), so a badge added +tomorrow is born reachable. + +## The documented gap — 21 native dialogs + +`admin.js` (6), `users.js` (10), `index.html` (5). These are the operator +consoles and the launcher — surfaces **no S1 task ever named** (S1's two +halves were the wizard, T5.8, and the creator, T7.9; both measure 0). They are +admin-only or low-frequency flows, every one a genuine confirm-before-destroy. +Logged as **BL-024** for conversion to the T7.9 dialog kit rather than done +here: converting three more pages inside the audit task is the drive-by +CLAUDE.md forbids. + +## The help-tip component (S8) + +- The badge is a `
+
Add to this project
+ `).join(''); modal.style.display = 'flex'; } diff --git a/html/work-package-suite-styles.css b/html/work-package-suite-styles.css index 16dfbfd..0a4d9a0 100644 --- a/html/work-package-suite-styles.css +++ b/html/work-package-suite-styles.css @@ -741,6 +741,13 @@ body { /* .field-error is declared once, in theme-light.css — the launcher's create form and T5.8's step validation use the same component. */ +/* The constraint-library entries (C1/T9.5): real buttons, block layout. */ +.constraint-option { display:block; width:100%; text-align:left; padding:0.75rem; + background:var(--bg); border:1px solid var(--border); border-radius:6px; + margin-bottom:0.5rem; cursor:pointer; font:inherit; color:inherit; transition:all .2s; } +.constraint-option:hover, .constraint-option:focus-visible { + border-color:var(--primary); background:var(--primary-light); } + /* NAVIGATION B6 / T7.8: sticky, the creator's pattern. On the Constraints and Sequence steps the proposal's beside-the-fields actions meant scrolling to save; the diff --git a/html/wp-creation-app.js b/html/wp-creation-app.js index 34a356e..c9797df 100644 --- a/html/wp-creation-app.js +++ b/html/wp-creation-app.js @@ -3409,7 +3409,8 @@ function renderDashboard(){ `; // status + discipline breakdown chips (status chips also filter the board) - const statusChip=(label,count,cls,status)=>`${esc(label)}: ${count}`; + // C1/T9.5: the chip filters the board, so it is a button. + const statusChip=(label,count,cls,status)=>``; const statusChips=STATUS_ORDER.filter(s=>byStatus[s]).map(s=>statusChip(s,byStatus[s],'',s)).join('') + (byStatus['Issue']?statusChip('On Hold',byStatus['Issue'],'chip-red','Issue'):''); const discChips=Object.keys(byDisc).map(d=>`${esc(d)}: ${byDisc[d]}`).join('')||''; @@ -3633,7 +3634,9 @@ async function clearMyComments(){ const d=cmtLoad(); const mine=d.comments.filte if(!mine){ toast('No comments to clear.', 'alert'); return; } if(!(await wpConfirmDialog({title:'Clear my comments', message:`Delete your ${mine} comment(s)?`, okLabel:'Delete them'}))) return; d.comments=d.comments.filter(c=>c.clientId!==d.clientId); cmtSave(d); renderComments(); refreshCommentBadges(); } -function cmtInit(){ const d=cmtLoad(); cmtSave(d); const a=document.getElementById('cmt-author'); if(a)a.value=d.author||''; cmtUpdateCurStep(); renderComments(); refreshCommentBadges(); } +function cmtInit(){ + const ov=document.getElementById('cmt-overlay'); + if(ov && !ov._wired){ ov._wired=true; ov.addEventListener('click', toggleComments); } const d=cmtLoad(); cmtSave(d); const a=document.getElementById('cmt-author'); if(a)a.value=d.author||''; cmtUpdateCurStep(); renderComments(); refreshCommentBadges(); } // ── STATUS PILLS ───────────────────────────────────────────────────────────── document.querySelectorAll('#status-group .radio-pill').forEach(p=>p.addEventListener('click',e=>{ diff --git a/html/wp-creation-index.html b/html/wp-creation-index.html index ebfa066..791f85e 100644 --- a/html/wp-creation-index.html +++ b/html/wp-creation-index.html @@ -591,7 +591,9 @@ -
+ +