Files
xamxam/todo/04-accessibility.md
Pontoporeia ca8081575c Add prefers-color-scheme dark mode for public pages
Scope: variables.css, search.css, todo/04-accessibility.md

- variables.css: add @media (prefers-color-scheme: dark) block scoped to
  body:not(.admin-body); overrides all semantic tokens with dark equivalents:
  --bg-* (#111→#333 range), --text-* (#eee/aaa/777),
  --border-* (#333/#444), --accent-primary lightened to #b87fd4
  (4.5:1 contrast on #111 background), --accent-secondary stays #9557b5,
  --accent-foreground flipped to #111111 for dark buttons,
  --accent-muted adjusted to rgba(184,127,212,0.15),
  status colours muted for dark (success #4db886, error #e05555,
  warning #d4a830); new --search-error-{bg,border,color} tokens added
  to :root (light: #fff0f0/#c00) and overridden in dark (#2a1515/#e05555)

- search.css: replace three hardcoded hex values in .search-error rule
  with var(--search-error-bg/border/color) so dark mode applies cleanly

- Admin pages are entirely unaffected: .admin-body body class is excluded
  from the dark-mode selector; system.css already has its own dark palette
2026-04-06 15:33:08 +02:00

80 lines
6.6 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.
# Accessibility Audit (WCAG 2.1 AA)
## 1.1.1 Non-text content (alt text)
- [x] **Admin `<nav>` "✕ Réinitialiser" and "✕" remove buttons** — wrap `✕` in `<span aria-hidden="true">✕</span>`; add `aria-label="Supprimer ce membre du jury"` on jury remove buttons in `add.php`/`edit.php`
## 1.3.1 Info and relationships
- [x] **Admin form rows: multi-input rows (languages, formats)**`checkbox-list.php` partial now wraps checkboxes in `<fieldset class="admin-checkbox-group">` with a `<legend class="sr-only">` for AT grouping
- [x] **Status badges in `admin/index.php` convey state by colour alone**`status-badge.php` partial emits `<span aria-label="Statut : Publié"><span aria-hidden="true">● </span>Publié</span>` (circle symbol is non-colour indicator); both publish and access badges implemented
## 1.3.4 / 1.3.5 Orientation & Input purpose
- [x] **No `autocomplete` attributes on personal data fields**`add.php`/`edit.php`: `autocomplete="name"` on author fields, `autocomplete="email"` on mail fields (via `$attrs` in `text-field.php`)
## 1.4.1 Use of colour
- [x] **Active nav link has no non-colour indicator** — admin nav: `border-bottom: 2px solid currentColor` added for `[aria-current="page"]` in `admin.css`; public nav already had `border-bottom` in `common.css`
- [x] **Admin purple `#9557b5` as text colour**`--admin-purple` was an undefined variable referenced only in pagination hover; replaced with `--accent-primary` (same value, #9557b5). The variable is only used for `border-color` and `color` on `:hover` state of pagination buttons (not body text), so no contrast violation in practice. Pagination buttons remain small-text; hover state is non-essential information so this is acceptable.
## 1.4.4 Resize text
- [x] **Verify no text is set in `px`** — audited all CSS files; every `font-size` uses `rem` or `em`; no `px` font-size found anywhere. No action needed.
## 1.4.12 Text spacing
- [x] **No text-spacing override test done** — audited all `overflow: hidden` instances: `.sr-only` (visually hidden utility, 1×1px — not text content), `.home-body figure` / `aside figure` / `.card` (media containers, not text). `.card__gradient-title` clamps decorative gradient text — not essential content (same info is in the `<p>` link). No WCAG 1.4.12 failure found.
## 2.1.1 Keyboard
- [x] **Jury "✕" remove buttons in `add.php`/`edit.php`**`aria-label="Supprimer le lecteur·ice N"` already present on all remove buttons in `jury-fieldset.php` (both static and dynamically added rows)
## 2.4.3 Focus order
- [x] **On `tfe.php` the `← Retour` back link is at the bottom of the left column in DOM** — already fixed; `<a class="tfe-back-link">← Retour</a>` is the first child of `<header class="tfe-left">`, which precedes `<h1 class="tfe-title">` in DOM order
## 2.4.4 Link purpose
- [x] **Home page cards: if two theses share the same title, identical link texts exist**`public/index.php` card `<p>` now appends `<span class="sr-only">, YEAR</span>` when `$item['year']` is set, giving screen-reader users a unique link name per thesis
## 2.5.3 Label in name
- [x] **`<a class="clear-filter">✕ Réinitialiser</a>`** — wrap `✕` in `<span aria-hidden="true">`
- [x] **Admin jury remove buttons `✕`**`aria-label="Supprimer ce lecteur"` replaces the symbol
## 2.5.5 Target size
- [x] **Pagination buttons are `2rem` (32px)** — increase to `min-height: 2.75rem; min-width: 2.75rem` (44px)
- [x] **Admin `.admin-btn-sm` (~28px)** — increase to minimum 32px with padding
- [x] **Admin bulk action buttons and jury remove `✕` buttons (~28px)** — increase target size
## 3.1.1 Language of page
- [x] **All public pages have `<html lang="fr">`** — verified: `templates/head.php` line 2 has `<html lang="fr">`; all pages share this template. No action needed.
## 3.3.1 Error identification
- [x] **`add.php`/`edit.php` validation errors** — `flash-messages.php` already emits `<p role="alert" data-type="error">` for errors and `<p role="status">` for success
- [x] **`add.php`/`edit.php` `autofocus` on first invalid field** — `App::flashAutofocus(fieldName)` stores the failing field in `$_SESSION['_flash_autofocus']`; action handlers map exception messages to field names; `add.php` consumes via `withAutofocus()` helper + injects into `$attrs`; `edit.php` uses inline ternary; partials support boolean `true` in `$attrs` to emit bare attribute names
## 3.3.2 Labels or instructions
- [x] **Admin jury "Lecteur·ices" label has no `for` attribute** — replaced plain `<label>Lecteur·ices :</label>` with `<fieldset class="admin-jury-lecteurs"><legend>Lecteur·ices</legend>` in `jury-fieldset.php`; CSS rule strips the nested fieldsets border/padding so it renders as a sub-group
## 4.1.2 Name, role, value
- [x] **Custom "Externe" checkbox for jury members has no group context** — all jury "Externe" checkboxes now carry explicit `aria-label` (e.g. `"Promoteur·ice — externe"`, `"Lecteur·ice N — externe"`); both static PHP-rendered rows and dynamically added rows via `addJuryRow()` receive the label
- [x] **`<video>` elements on `tfe.php` have no captions** — `<track kind="captions">` now emitted for each MP4 when a `.vtt` sidecar has been uploaded alongside it; N-th VTT file is paired with N-th video in document order. `formulaire.php` accepts `.vtt` uploads (MIME `text/vtt`, `file_type = 'caption'`); `media.php` serves VTT with correct `Content-Type`; admin `add.php` file-field hint documents the `.vtt` convention.
- [x] **Admin `<select>` for visibility/access in `edit.php` uses truncated option text** — removed `mb_strimwidth` call; option text now uses full description (`name — description`) so screen-reader accessible name is complete and unambiguous
- [x] **Bulk publish/unpublish JS does not announce result to screen readers** — action result is a full-page redirect to a flash message rendered by `flash-messages.php` which already emits `role="alert"` (error) / `role="status"` (success); no additional JS announcement needed
## 5 - Motion & user preferences
- [x] **`prefers-color-scheme` not respected** — `variables.css` now includes a `@media (prefers-color-scheme: dark)` block scoped to `body:not(.admin-body)`; overrides all semantic tokens (`--bg-*`, `--text-*`, `--border-*`, `--accent-*`, status colours) with dark equivalents; `--accent-primary` lightened to `#b87fd4` for 4.5:1 contrast on dark backgrounds; `search.css` `.search-error` hardcoded `#fff0f0`/`#c00` replaced with `--search-error-bg`/`--search-error-border`/`--search-error-color` variables also overridden in dark mode; admin pages unaffected (`.admin-body` preserves light-mode values)