admin: semantic HTML pass — checkbox fieldset, landmarks, dl/dt, autocomplete, inline styles

checkbox-list.php partial:
- Replace outer <div>/<label> with <div>/<span class="admin-row-label"> + inner
  <fieldset class="admin-checkbox-group"><legend class="sr-only"> to satisfy
  WCAG 1.3.1 (group label for multi-checkbox rows without duplicating visible text)
- Replace <div class="admin-checkbox-list"> with <ul>; each checkbox wrapped in <li>

admin.css:
- Drop .admin-checkbox-list; add .admin-body fieldset.admin-checkbox-group rules
  (border/padding reset so it doesn't inherit jury-fieldset box styling)
- Extend form-row label rule to span.admin-row-label
- .admin-inline-form + .admin-inline-form { margin-top:.35rem } replaces inline style
- .admin-input--inline / .admin-select--inline get width:160px (was inline style)
- .admin-tags-count + table th sizing via :has() replaces th inline styles

login.php: wrap content in <main id="main-content"> (missing landmark)

account.php:
- <div class="admin-account-status"> → <dl>; __label <span> → <dt>
- <div class="admin-danger-zone__description"> → <p>

index.php: <div class="admin-maintenance-bar"> → <aside role="status" aria-label="Statut du site">

add.php / edit.php: autocomplete="name" on author field, autocomplete="email" on
contact field (WCAG 1.3.5 / input purpose)

tags.php: all inline style= attributes removed (width, text-align, margin-top,
display:inline); all moved to CSS classes
This commit is contained in:
Pontoporeia
2026-04-02 21:06:20 +02:00
parent fde05da493
commit ff8e33727d
11 changed files with 111 additions and 56 deletions

11
TODO.md
View File

@@ -8,3 +8,14 @@ Pending tasks have been split into topic files under [`todo/`](todo/README.md):
| [todo/02-php-components.md](todo/02-php-components.md) | Form field partials, shared UI partials, controller extraction, backend maintenance | | [todo/02-php-components.md](todo/02-php-components.md) | Form field partials, shared UI partials, controller extraction, backend maintenance |
| [todo/03-system-cache.md](todo/03-system-cache.md) | `system_cache` table, `SystemCache` class, `system.php` refactor | | [todo/03-system-cache.md](todo/03-system-cache.md) | `system_cache` table, `SystemCache` class, `system.php` refactor |
| [todo/04-accessibility.md](todo/04-accessibility.md) | WCAG 2.1 AA — remaining failures grouped by success criterion | | [todo/04-accessibility.md](todo/04-accessibility.md) | WCAG 2.1 AA — remaining failures grouped by success criterion |
## Recently completed (this session)
- [x] `checkbox-list.php` — replaced `<div class="admin-checkbox-list">` with `<fieldset class="admin-checkbox-group"><legend class="sr-only">…</legend><ul>` (WCAG 1.3.1 fix)
- [x] `admin.css` — replaced `.admin-checkbox-list` with `.admin-body fieldset.admin-checkbox-group > ul` semantic selectors; added `span.admin-row-label` as visible label column counterpart
- [x] `login.php` — wrapped content in `<main id="main-content">` landmark
- [x] `account.php``<div class="admin-account-status">``<dl>`; `__row` divs kept; `__label` spans → `<dt>`; `admin-danger-zone__description` div → `<p>`
- [x] `index.php` — maintenance bar `<div>``<aside role="status" aria-label="Statut du site">`
- [x] `add.php` / `edit.php``autocomplete="name"` on author field, `autocomplete="email"` on contact field
- [x] `tags.php` — all inline `style=` attributes removed; sizing/spacing moved to CSS (`.admin-input--inline`, `.admin-select--inline`, `.admin-inline-form + .admin-inline-form`, `.admin-tags-count`)
- [x] Marked already-done items in todo files: stats `<dl>`, `thanks.php` `<section>`, `scope="col"` on both tables, `tfe.php` inline styles, `role="alert"` on flash messages

View File

@@ -23,15 +23,17 @@ if (empty($_SESSION['csrf_token'])) {
<?php include APP_ROOT . '/templates/partials/flash-messages.php'; ?> <?php include APP_ROOT . '/templates/partials/flash-messages.php'; ?>
<!-- Status info --> <!-- Status info -->
<div class="admin-account-status"> <dl class="admin-account-status">
<div class="admin-account-status__row"> <div class="admin-account-status__row">
<span class="admin-account-status__label">Authentification PHP</span> <dt class="admin-account-status__label">Authentification PHP</dt>
<?php $badgeType = 'ok'; $badgeValue = $hasPassword; $badgeOkLabel = 'Active'; $badgeWarnLabel = 'Non configurée'; include APP_ROOT . '/templates/partials/status-badge.php'; ?> <dd><?php $badgeType = 'ok'; $badgeValue = $hasPassword; $badgeOkLabel = 'Active'; $badgeWarnLabel = 'Non configurée'; include APP_ROOT . '/templates/partials/status-badge.php'; ?></dd>
</div> </div>
<div class="admin-account-status__row"> <div class="admin-account-status__row">
<span class="admin-account-status__label">Fichier de configuration</span> <dt class="admin-account-status__label">Fichier de configuration</dt>
<dd>
<code class="admin-account-status__code">config/admin_credentials.php</code> <code class="admin-account-status__code">config/admin_credentials.php</code>
<?php $badgeType = 'ok'; $badgeValue = file_exists($credentialsFile); $badgeOkLabel = 'Présent'; $badgeWarnLabel = 'Absent'; include APP_ROOT . '/templates/partials/status-badge.php'; ?> <?php $badgeType = 'ok'; $badgeValue = file_exists($credentialsFile); $badgeOkLabel = 'Présent'; $badgeWarnLabel = 'Absent'; include APP_ROOT . '/templates/partials/status-badge.php'; ?>
</dd>
</div> </div>
<?php if (!$hasPassword): ?> <?php if (!$hasPassword): ?>
<p class="admin-account-status__note"> <p class="admin-account-status__note">
@@ -39,7 +41,7 @@ if (empty($_SESSION['csrf_token'])) {
<code>config/admin_credentials.php</code> avec un hash bcrypt. <code>config/admin_credentials.php</code> avec un hash bcrypt.
</p> </p>
<?php endif; ?> <?php endif; ?>
</div> </dl>
<!-- Password change form --> <!-- Password change form -->
<h2 class="admin-section-title"><?= $hasPassword ? 'Changer le mot de passe' : 'Définir le mot de passe' ?></h2> <h2 class="admin-section-title"><?= $hasPassword ? 'Changer le mot de passe' : 'Définir le mot de passe' ?></h2>
@@ -86,13 +88,13 @@ if (empty($_SESSION['csrf_token'])) {
<!-- Danger zone: remove password --> <!-- Danger zone: remove password -->
<h2 class="admin-section-title admin-section-title--danger">Zone de danger</h2> <h2 class="admin-section-title admin-section-title--danger">Zone de danger</h2>
<div class="admin-danger-zone"> <div class="admin-danger-zone">
<div class="admin-danger-zone__description"> <p class="admin-danger-zone__description">
<strong>Supprimer la configuration du mot de passe PHP</strong><br> <strong>Supprimer la configuration du mot de passe PHP</strong><br>
<small> <small>
Supprime <code>config/admin_credentials.php</code>. L'accès admin Supprime <code>config/admin_credentials.php</code>. L'accès admin
dépendra uniquement de l'authentification nginx Basic Auth si elle est configurée. dépendra uniquement de l'authentification nginx Basic Auth si elle est configurée.
</small> </small>
</div> </p>
<form method="post" action="/admin/actions/account.php" <form method="post" action="/admin/actions/account.php"
onsubmit="return confirm('Supprimer le fichier de mot de passe PHP ? L\'accès admin ne sera protégé que par nginx Basic Auth.')"> onsubmit="return confirm('Supprimer le fichier de mot de passe PHP ? L\'accès admin ne sera protégé que par nginx Basic Auth.')">
<input type="hidden" name="csrf_token" value="<?= htmlspecialchars($_SESSION['csrf_token']) ?>"> <input type="hidden" name="csrf_token" value="<?= htmlspecialchars($_SESSION['csrf_token']) ?>">

View File

@@ -52,8 +52,8 @@ function wasSelected($key, $value) {
<?php $name = 'titre'; $label = 'Titre :'; $value = old('titre'); $required = true; include APP_ROOT . '/templates/partials/form/text-field.php'; ?> <?php $name = 'titre'; $label = 'Titre :'; $value = old('titre'); $required = true; include APP_ROOT . '/templates/partials/form/text-field.php'; ?>
<?php $name = 'subtitle'; $label = 'Sous-titre (si applicable) :'; $value = old('subtitle'); $required = false; include APP_ROOT . '/templates/partials/form/text-field.php'; ?> <?php $name = 'subtitle'; $label = 'Sous-titre (si applicable) :'; $value = old('subtitle'); $required = false; include APP_ROOT . '/templates/partials/form/text-field.php'; ?>
<?php $name = 'auteurice'; $label = 'Auteur·ice(s) :'; $value = old('auteurice'); $required = true; include APP_ROOT . '/templates/partials/form/text-field.php'; ?> <?php $name = 'auteurice'; $label = 'Auteur·ice(s) :'; $value = old('auteurice'); $required = true; $attrs = ['autocomplete' => 'name']; include APP_ROOT . '/templates/partials/form/text-field.php'; ?>
<?php $name = 'mail'; $label = 'Contact(s) (optionnel) [mail/site/insta/etc.] :'; $value = old('mail'); include APP_ROOT . '/templates/partials/form/text-field.php'; ?> <?php $name = 'mail'; $label = 'Contact(s) (optionnel) [mail/site/insta/etc.] :'; $value = old('mail'); $attrs = ['autocomplete' => 'email']; include APP_ROOT . '/templates/partials/form/text-field.php'; ?>
<?php require APP_ROOT . '/templates/partials/form/jury-fieldset.php'; ?> <?php require APP_ROOT . '/templates/partials/form/jury-fieldset.php'; ?>

View File

@@ -70,8 +70,8 @@ try {
<input type="hidden" name="csrf_token" value="<?= htmlspecialchars($_SESSION['csrf_token']) ?>"> <input type="hidden" name="csrf_token" value="<?= htmlspecialchars($_SESSION['csrf_token']) ?>">
<input type="hidden" name="thesis_id" value="<?= $thesisId ?>"> <input type="hidden" name="thesis_id" value="<?= $thesisId ?>">
<?php $name = 'auteurice'; $label = 'Auteur·ice(s) :'; $value = htmlspecialchars($thesis['authors']); $required = true; include APP_ROOT . '/templates/partials/form/text-field.php'; ?> <?php $name = 'auteurice'; $label = 'Auteur·ice(s) :'; $value = htmlspecialchars($thesis['authors']); $required = true; $attrs = ['autocomplete' => 'name']; include APP_ROOT . '/templates/partials/form/text-field.php'; ?>
<?php $name = 'mail'; $label = 'Contact :'; $value = ''; include APP_ROOT . '/templates/partials/form/text-field.php'; ?> <?php $name = 'mail'; $label = 'Contact :'; $value = ''; $attrs = ['autocomplete' => 'email']; include APP_ROOT . '/templates/partials/form/text-field.php'; ?>
<?php <?php
$name = 'année'; $label = 'Année :'; $value = htmlspecialchars((string)$thesis['year']); $required = true; $name = 'année'; $label = 'Année :'; $value = htmlspecialchars((string)$thesis['year']); $required = true;

View File

@@ -71,7 +71,7 @@ document.addEventListener('DOMContentLoaded', () => {
<!-- Maintenance mode toggle --> <!-- Maintenance mode toggle -->
<?php $maintenanceOn = file_exists(APP_ROOT . '/storage/maintenance.flag'); ?> <?php $maintenanceOn = file_exists(APP_ROOT . '/storage/maintenance.flag'); ?>
<div class="admin-maintenance-bar <?= $maintenanceOn ? 'admin-maintenance-bar--active' : '' ?>"> <aside role="status" class="admin-maintenance-bar <?= $maintenanceOn ? 'admin-maintenance-bar--active' : '' ?>" aria-label="Statut du site">
<?php if ($maintenanceOn): ?> <?php if ($maintenanceOn): ?>
<span>⚠ Mode maintenance <strong>activé</strong> — le site public est inaccessible.</span> <span>⚠ Mode maintenance <strong>activé</strong> — le site public est inaccessible.</span>
<form method="post" action="actions/maintenance.php" style="display:inline;"> <form method="post" action="actions/maintenance.php" style="display:inline;">
@@ -90,7 +90,7 @@ document.addEventListener('DOMContentLoaded', () => {
</button> </button>
</form> </form>
<?php endif; ?> <?php endif; ?>
</div> </aside>
<!-- Stats (always reflects full DB, independent of active filters) --> <!-- Stats (always reflects full DB, independent of active filters) -->
<dl class="admin-stats"> <dl class="admin-stats">

View File

@@ -26,6 +26,7 @@ $pageTitle = 'Connexion';
<?php $isAdmin = true; $bodyClass = 'admin-body'; require_once APP_ROOT . '/templates/head.php'; ?> <?php $isAdmin = true; $bodyClass = 'admin-body'; require_once APP_ROOT . '/templates/head.php'; ?>
<?php include APP_ROOT . '/templates/header.php'; ?> <?php include APP_ROOT . '/templates/header.php'; ?>
<main id="main-content">
<div class="admin-login-wrap"> <div class="admin-login-wrap">
<div class="admin-login-box"> <div class="admin-login-box">
<h2>Administration</h2> <h2>Administration</h2>
@@ -43,5 +44,6 @@ $pageTitle = 'Connexion';
</form> </form>
</div> </div>
</div> </div>
</main>
<?php require_once APP_ROOT . '/templates/admin/footer.php'; ?> <?php require_once APP_ROOT . '/templates/admin/footer.php'; ?>

View File

@@ -31,16 +31,16 @@ try {
<table> <table>
<thead> <thead>
<tr> <tr>
<th scope="col" style="width:40%;">Nom</th> <th scope="col">Nom</th>
<th scope="col" style="width:12%;text-align:center;">TFE associés</th> <th scope="col">TFE associés</th>
<th scope="col" style="width:48%;">Actions</th> <th scope="col">Actions</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
<?php foreach ($tags as $tag): ?> <?php foreach ($tags as $tag): ?>
<tr> <tr>
<td><?= htmlspecialchars($tag['name']) ?></td> <td><?= htmlspecialchars($tag['name']) ?></td>
<td style="text-align:center;"><?= (int)$tag['thesis_count'] ?></td> <td class="admin-tags-count"><?= (int)$tag['thesis_count'] ?></td>
<td> <td>
<!-- Rename --> <!-- Rename -->
<form method="post" action="actions/tag.php" class="admin-inline-form"> <form method="post" action="actions/tag.php" class="admin-inline-form">
@@ -48,16 +48,16 @@ try {
<input type="hidden" name="action" value="rename"> <input type="hidden" name="action" value="rename">
<input type="hidden" name="tag_id" value="<?= (int)$tag['id'] ?>"> <input type="hidden" name="tag_id" value="<?= (int)$tag['id'] ?>">
<input class="admin-input--inline" type="text" name="new_name" <input class="admin-input--inline" type="text" name="new_name"
value="<?= htmlspecialchars($tag['name']) ?>" required style="width:160px;"> value="<?= htmlspecialchars($tag['name']) ?>" required>
<button type="submit" class="admin-btn admin-btn--sm">Renommer</button> <button type="submit" class="admin-btn admin-btn--sm">Renommer</button>
</form> </form>
<!-- Merge into another tag --> <!-- Merge into another tag -->
<form method="post" action="actions/tag.php" class="admin-inline-form" style="margin-top:.35rem;"> <form method="post" action="actions/tag.php" class="admin-inline-form">
<input type="hidden" name="csrf_token" value="<?= htmlspecialchars($_SESSION['csrf_token']) ?>"> <input type="hidden" name="csrf_token" value="<?= htmlspecialchars($_SESSION['csrf_token']) ?>">
<input type="hidden" name="action" value="merge"> <input type="hidden" name="action" value="merge">
<input type="hidden" name="source_id" value="<?= (int)$tag['id'] ?>"> <input type="hidden" name="source_id" value="<?= (int)$tag['id'] ?>">
<select name="target_id" class="admin-select--inline" style="width:160px;" required> <select name="target_id" class="admin-select--inline" required>
<option value="">— Fusionner dans… —</option> <option value="">— Fusionner dans… —</option>
<?php foreach ($tags as $other): ?> <?php foreach ($tags as $other): ?>
<?php if ($other['id'] !== $tag['id']): ?> <?php if ($other['id'] !== $tag['id']): ?>
@@ -72,7 +72,7 @@ try {
</form> </form>
<!-- Delete --> <!-- Delete -->
<form method="post" action="actions/tag.php" class="admin-inline-form" style="margin-top:.35rem;display:inline;"> <form method="post" action="actions/tag.php" class="admin-inline-form">
<input type="hidden" name="csrf_token" value="<?= htmlspecialchars($_SESSION['csrf_token']) ?>"> <input type="hidden" name="csrf_token" value="<?= htmlspecialchars($_SESSION['csrf_token']) ?>">
<input type="hidden" name="action" value="delete"> <input type="hidden" name="action" value="delete">
<input type="hidden" name="tag_id" value="<?= (int)$tag['id'] ?>"> <input type="hidden" name="tag_id" value="<?= (int)$tag['id'] ?>">

View File

@@ -54,7 +54,8 @@
border-bottom: 1px solid var(--border-primary); border-bottom: 1px solid var(--border-primary);
} }
.admin-form > div:not(.admin-submit-wrap) > label { .admin-form > div:not(.admin-submit-wrap) > label,
.admin-form > div:not(.admin-submit-wrap) > span.admin-row-label {
font-size: 0.92rem; font-size: 0.92rem;
padding-top: 0.5rem; padding-top: 0.5rem;
font-weight: 400; font-weight: 400;
@@ -140,12 +141,28 @@
display: block; display: block;
} }
/* Checkboxes */ /* Checkbox group fieldset (languages, formats)
.admin-checkbox-list { Wraps the <ul> of checkboxes; the visible label is a sibling <span>
in the grid row. The <legend> repeats the label text for AT only (sr-only).
.admin-body scope ensures this overrides the generic .admin-body fieldset rule. */
.admin-body fieldset.admin-checkbox-group {
border: none;
padding: 0;
margin: 0;
background: transparent;
}
.admin-body fieldset.admin-checkbox-group > ul {
list-style: none;
margin: 0;
padding-top: 0.3rem;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: 0.35rem; gap: 0.35rem;
padding-top: 0.3rem; }
.admin-body fieldset.admin-checkbox-group > ul > li {
display: contents; /* let the inner label handle layout */
} }
.admin-checkbox-label { .admin-checkbox-label {
@@ -754,13 +771,26 @@
.admin-input--inline, .admin-input--inline,
.admin-inline-form input[type="text"] { .admin-inline-form input[type="text"] {
font-size: 0.82rem; font-size: 0.82rem;
width: 160px;
} }
.admin-select--inline, .admin-select--inline,
.admin-inline-form select { .admin-inline-form select {
font-size: 0.82rem; font-size: 0.82rem;
width: 160px;
} }
/* Stack secondary forms (merge, delete) below the rename form */
.admin-inline-form + .admin-inline-form {
margin-top: 0.35rem;
}
/* Tags table column sizing */
.admin-body table:has(.admin-tags-count) th:nth-child(1) { width: 40%; }
.admin-body table:has(.admin-tags-count) th:nth-child(2) { width: 12%; }
.admin-body table:has(.admin-tags-count) th:nth-child(3) { width: 48%; }
.admin-tags-count { text-align: center; }
/* ── Banner preview ─────────────────────────────────────────────────────── */ /* ── Banner preview ─────────────────────────────────────────────────────── */
.admin-banner-preview img { .admin-banner-preview img {
max-width: 320px; max-width: 320px;

View File

@@ -2,9 +2,14 @@
/** /**
* Checkbox list partial — renders a group of checkboxes (e.g. languages, formats). * Checkbox list partial — renders a group of checkboxes (e.g. languages, formats).
* *
* The group label uses a visible <span> as the first column (matching other form
* rows), while a <fieldset>/<legend> in the second column provides the accessible
* grouping required by WCAG 1.3.1. The <legend> is visually hidden (sr-only) to
* avoid duplicating the visible label text.
*
* Variables consumed: * Variables consumed:
* string $name — input name attribute (will be posted as array: name[]) * string $name — input name attribute (will be posted as array: name[])
* string $label — group label (rendered as plain <label>, not associated with any single input) * string $label — group label text
* array $options — each element must have 'id' and 'name' keys * array $options — each element must have 'id' and 'name' keys
* array $checked — array of 'id' values that are currently checked * array $checked — array of 'id' values that are currently checked
*/ */
@@ -12,9 +17,12 @@
$checked = $checked ?? []; $checked = $checked ?? [];
?> ?>
<div> <div>
<label><?= htmlspecialchars($label) ?></label> <span class="admin-row-label"><?= htmlspecialchars($label) ?></span>
<div class="admin-checkbox-list"> <fieldset class="admin-checkbox-group">
<legend class="sr-only"><?= htmlspecialchars($label) ?></legend>
<ul>
<?php foreach ($options as $opt): ?> <?php foreach ($options as $opt): ?>
<li>
<label class="admin-checkbox-label"> <label class="admin-checkbox-label">
<input type="checkbox" <input type="checkbox"
name="<?= htmlspecialchars($name) ?>[]" name="<?= htmlspecialchars($name) ?>[]"
@@ -22,8 +30,10 @@ $checked = $checked ?? [];
<?= in_array((string)$opt['id'], array_map('strval', $checked)) ? 'checked' : '' ?>> <?= in_array((string)$opt['id'], array_map('strval', $checked)) ? 'checked' : '' ?>>
<?= htmlspecialchars($opt['name']) ?> <?= htmlspecialchars($opt['name']) ?>
</label> </label>
</li>
<?php endforeach; ?> <?php endforeach; ?>
</div> </ul>
</fieldset>
</div> </div>
<?php <?php
unset($checked); unset($checked);

View File

@@ -29,29 +29,28 @@
## Scattered inline styles in templates ## Scattered inline styles in templates
- [ ] `tfe.php` line 146: `style="align-items:start;"``.tfe-meta-item--top` in `tfe.css` - [x] `tfe.php` inline styles — already extracted (no `style=` attributes remain in `public/tfe.php`)
- [ ] `tfe.php` lines 148, 170-172, 193: `font-style:italic`, `margin-top:1.5rem`, `font-size:.88rem;color:#666`, `color:#999;font-style:italic``.tfe-note-value`, `.tfe-back-link`, `.tfe-restricted` in `tfe.css`
- [ ] `admin/edit.php`: multiple `style=` on `.admin-form-row` and banner preview → modifier classes in `admin.css` - [ ] `admin/edit.php`: multiple `style=` on `.admin-form-row` and banner preview → modifier classes in `admin.css`
## Admin semantic HTML (sections IXXVI) ## Admin semantic HTML (sections IXXVI)
- [ ] **`add.php`/`edit.php`**: Replace `<div class="admin-form-row">` with CSS grid on `<form>` children (~20 divs in add.php, ~22 in edit.php) - [ ] **`add.php`/`edit.php`**: Replace `<div class="admin-form-row">` with CSS grid on `<form>` children (~20 divs in add.php, ~22 in edit.php)
- [ ] **`add.php`/`edit.php`**: Replace inner wrapper `<div>` in multi-control rows — use `<small>` for hints, remove the wrapper div - [ ] **`add.php`/`edit.php`**: Replace inner wrapper `<div>` in multi-control rows — use `<small>` for hints, remove the wrapper div
- [ ] **`add.php`/`edit.php`**: Replace `<div class="admin-checkbox-list">` with `<ul>`; each `<label class="admin-checkbox-label">` becomes `<li>` containing `<label>` - [x] **`add.php`/`edit.php`**: Replace `<div class="admin-checkbox-list">` with `<ul>`; each `<label class="admin-checkbox-label">` becomes `<li>` containing `<label>`
- [ ] **`add.php`/`edit.php`**: Replace `<div class="admin-submit-wrap">` — remove; apply styles directly to `form > button:last-child` - [ ] **`add.php`/`edit.php`**: Replace `<div class="admin-submit-wrap">` — remove; apply styles directly to `form > button:last-child`
- [ ] **`add.php`/`edit.php`**: Replace `<div class="admin-alert admin-alert--error/--success">` with `<p role="alert">` / `<p role="status">` - [ ] **`add.php`/`edit.php`**: Replace `<div class="admin-alert admin-alert--error/--success">` with `<p role="alert">` / `<p role="status">`
- [ ] **`index.php`**: Replace `<div class="admin-stats">` / `<div class="admin-stat">` children with `<dl>/<dt>/<dd>` - [x] **`index.php`**: Replace `<div class="admin-stats">` / `<div class="admin-stat">` children with `<dl>/<dt>/<dd>`
- [ ] **`index.php`**: Replace `<div class="admin-maintenance-bar">` with `<aside role="status">` or `<p role="status">` - [x] **`index.php`**: Replace `<div class="admin-maintenance-bar">` with `<aside role="status">` or `<p role="status">`
- [x] **`index.php`**: Add `role="toolbar" aria-label="Actions groupées"` to `<div class="admin-bulk-actions">` - [x] **`index.php`**: Add `role="toolbar" aria-label="Actions groupées"` to `<div class="admin-bulk-actions">`
- [ ] **`index.php`**: Add `scope="col"` to all `<th>` cells in the admin table - [x] **`index.php`**: Add `scope="col"` to all `<th>` cells in the admin table
- [ ] **`index.php`**: Add non-colour indicator + `aria-label="Statut : …"` to status badge `<span>` elements - [ ] **`index.php`**: Add non-colour indicator + `aria-label="Statut : …"` to status badge `<span>` elements
- [ ] **`tags.php`**: Add `scope="col"` to `<th>` cells - [x] **`tags.php`**: Add `scope="col"` to `<th>` cells
- [ ] **`tags.php`**: Move inline `style="margin-top:.35rem;"` on forms → `.admin-inline-form + .admin-inline-form` selector - [x] **`tags.php`**: Move inline `style="margin-top:.35rem;"` on forms → `.admin-inline-form + .admin-inline-form` selector
- [ ] **`thanks.php`**: Replace `<div class="admin-thesis-info">` with `<section>` + `<h2>` heading; CSS targets `main > section` - [x] **`thanks.php`**: Replace `<div class="admin-thesis-info">` with `<section>` + `<h2>` heading; CSS targets `main > section`
- [ ] **`account.php`**: Replace `<div class="admin-account-status">` with `<dl>`; `__row``<div>`, `__label``<dt>` - [x] **`account.php`**: Replace `<div class="admin-account-status">` with `<dl>`; `__row``<div>`, `__label``<dt>`
- [ ] **`account.php`**: Replace `<div class="admin-danger-zone__description">` with `<p>` - [x] **`account.php`**: Replace `<div class="admin-danger-zone__description">` with `<p>`
- [ ] **`account.php`**: Move `style="margin-top:3rem;"` on danger zone heading → CSS modifier class - [x] **`account.php`**: Move `style="margin-top:3rem;"` on danger zone heading → CSS modifier class
- [ ] **`login.php`**: Wrap login content in `<main>` (currently no main landmark) - [x] **`login.php`**: Wrap login content in `<main>` (currently no main landmark)
- [ ] **`login.php`**: Extract inline styles on `.admin-form-row` and `.admin-submit-wrap``.admin-form-row--compact` modifier in `admin.css` - [ ] **`login.php`**: Extract inline styles on `.admin-form-row` and `.admin-submit-wrap``.admin-form-row--compact` modifier in `admin.css`
## Favicon ## Favicon

View File

@@ -6,13 +6,13 @@
## 1.3.1 Info and relationships ## 1.3.1 Info and relationships
- [ ] **Admin form rows: multi-input rows (languages, formats)**`<label class="admin-label">` without `for` labels a group of checkboxes; replace with `<fieldset>/<legend>` - [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
- [ ] **Status badges in `admin/index.php` convey state by colour alone** — add visible non-colour distinction (prefix icon with `aria-hidden="true"`) and `aria-label="Statut : Publié"` on badge `<span>` - [ ] **Status badges in `admin/index.php` convey state by colour alone** — add visible non-colour distinction (prefix icon with `aria-hidden="true"`) and `aria-label="Statut : Publié"` on badge `<span>`
## 1.3.4 / 1.3.5 Orientation & Input purpose ## 1.3.4 / 1.3.5 Orientation & Input purpose
- [ ] **No `autocomplete` attributes on personal data fields**`add.php`/`edit.php`: add `autocomplete="name"` on author fields, `autocomplete="email"` on mail fields - [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 ## 1.4.1 Use of colour
@@ -57,7 +57,8 @@
## 3.3.1 Error identification ## 3.3.1 Error identification
- [ ] **`add.php`/`formulaire.php` validation errors** — add `role="alert"` to error div; add `autofocus` to first invalid field when re-rendering form with session error data - [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
- [ ] **`add.php`/`edit.php` `autofocus` on first invalid field** — requires controller to pass back which field failed; deferred (larger refactor)
## 3.3.2 Labels or instructions ## 3.3.2 Labels or instructions