3.3.2 Labels or instructions
- Replace bare <label>Lecteur·ices :</label> (no 'for', no associated control)
with <fieldset class="admin-jury-lecteurs"><legend>Lecteur·ices</legend>
giving AT a proper programmatic label for the entire lecteur group
4.1.2 Name, role, value — Externe checkboxes lacked group context
- Add aria-label="Promoteur·ice — externe" on the promoteur Externe checkbox
- Add aria-label="Lecteur·ice N — nom" on every lecteur name input
- Add aria-label="Lecteur·ice N — externe" on every lecteur Externe checkbox
- All three attributes added to both PHP-rendered rows and the addJuryRow() JS
that builds new rows dynamically
2.1.1 Keyboard — remove buttons already had aria-label; verified and updated
label text to "Supprimer le lecteur·ice N" (consistent with new numbering)
CSS (admin.css)
- Add .admin-body fieldset fieldset.admin-jury-lecteurs rule: removes
border/padding/background from the nested fieldset so it reads as a
sub-group inside the outer jury fieldset, not a double-bordered card
Audit (no code change)
- WCAG 1.4.4: all font-size values use rem — no px text sizing anywhere
- WCAG 1.4.12: only overflow:hidden on media containers and .sr-only utility;
no essential text content is clipped by text-spacing overrides
- WCAG 4.1.2 bulk JS: result is a redirect to flash-messages.php which already
emits role="alert"/role="status" — no additional JS announcement needed
Add <span class="sr-only">, YEAR</span> to each thesis card <p> in
public/index.php. Screen readers now read "Author – Title, 2024" instead
of bare "Author – Title", so two theses sharing the same title produce
distinct accessible names (WCAG 2.4.4 Link Purpose — In Context).
Also audit and close WCAG 2.4.3: the tfe.php back link (<a class="tfe-back-link">
← Retour</a>) is already the first child of <header class="tfe-left">
in DOM order, preceding <h1 class="tfe-title">. No code change needed;
TODO item marked done.
- Updated 6 admin templates: add.php, edit.php, login.php, account.php,
import.php, pages-edit.php — replaced <div class="admin-submit-wrap">
with <div class="admin-form-footer">
- Updated 8 CSS selectors in admin.css:
- .admin-form-footer { margin-top/padding-top } (was .admin-submit-wrap)
- .admin-form > div:not(.admin-form-footer) grid exclusion guard (×3)
- .admin-login-box .admin-form > div:not(.admin-form-footer) overrides (×2)
- .admin-login-box .admin-form-footer compact spacing override
- No visual change; purely a semantic rename to a descriptive class name
- Also marked status-badge.php partial and WCAG 1.3.1 badge tasks as
already-done in todo/02-php-components.md and todo/04-accessibility.md
(partial + CSS were fully implemented but todo had not been updated)
WCAG 1.4.1 — Active nav link had no non-colour indicator in the admin panel.
Public nav already had border-bottom via common.css; admin nav had nothing.
admin.css:
- Add `[aria-current="page"]` rule on admin nav links:
border-bottom: 2px solid currentColor; padding-bottom: 1px
This gives a visible underline as a non-colour signal for the active page.
- Fix `--admin-purple` undefined CSS variable in pagination button hover.
The variable was referenced but never defined in variables.css (which was
refactored to use --accent-primary / --accent-secondary). Replaced both
border-color and color usages with var(--accent-primary) (#9557b5 — same
value), restoring the intended purple hover tint on pagination buttons.
todo/01-css-semantic-refactor.md:
- Audited ~15 pending CSS/HTML tasks; all were already implemented.
Marked as done: .admin-main, .admin-page-title, .admin-form-row,
.admin-label, .admin-input/select/textarea, .admin-table, .admin-fieldset,
tfe.css class replacements, search.css h2 selector, admin-alert replacement,
login.php/edit.php inline style removal, form partial hints (<small>).
todo/04-accessibility.md:
- Marked WCAG 1.4.1 admin nav and --admin-purple audit items as completed.
- Add Database::getThesesListCount(array $filters) — runs the same WHERE
clauses as getThesesList() but with COUNT(DISTINCT t.id); used to compute
total pages without loading all rows.
- Extend Database::getThesesList() with $limit/$offset parameters; when
$limit > 0 appends LIMIT/OFFSET and re-binds positional params individually
to avoid the PDO mixed-style restriction.
- Fix getThesesList() SELECT: add LEFT JOIN access_types + at.name as
access_type — the column was referenced in the template but never fetched.
- Wire admin/index.php: read ?page=, compute $totalPages/$offset, pass
$perPage=25 + $offset to getThesesList(); include pagination.php partial
below the table with filter-preserving $baseParams.
- Add result-count line (<p class="admin-list-meta">) showing "X–Y sur Z TFE"
when multiple pages exist.
- Add .admin-body .pagination-wrap / .pagination-btn / .pagination-info styles
to admin.css (scoped to .admin-body to avoid colliding with public pages).
The file had accumulated severe corruption in its lower half (garbled
selector text, variable names spliced into property values, orphaned
declarations, broken nesting) alongside hardcoded hex colours throughout.
Rewrote the entire file cleanly:
- Every colour is now a var() referencing a token defined in variables.css:
--accent-primary/secondary/foreground, --accent-blue/green/yellow/red,
--bg-secondary/tertiary, --border-primary, --text-primary/secondary/tertiary,
--error, --warning, --success, --accent-muted.
- Zero raw hex values remain in admin.css.
- Removed the corrupted/dead CSS from the bottom half and reconstructed
all selectors from what the templates actually use (audited via grep).
- Fixed structural issues: broken border shorthand, nested rules that
were not valid CSS, orphaned declaration blocks.
- New/restored rules: .admin-maintenance-bar (was corrupted),
.status-access variants (was corrupted), .admin-section-title--danger,
.admin-danger-zone, .admin-account-status (all reconstructed cleanly).
- .admin-btn--warning and .admin-btn--danger now use var(--accent-yellow)
and var(--accent-red) instead of hardcoded dark hex values.
- .admin-btn-remove hover now uses var(--error) instead of #e55.
- .admin-btn-unpublish now uses var(--bg-secondary)/var(--text-tertiary)
instead of hardcoded grey hex values.
- select option background colours removed (browser chrome, not styleable
cross-platform).
Templates: replace 4 inline var(--admin-text-muted) with var(--text-secondary)
in index.php, thanks.php, import.php.
Add system-fragment.php — a thin authenticated endpoint that returns only
the tab-panel HTML (toolbar + meta + log/nginx-config output) for a given
?tab=&n= combination. No page shell, no status section, no DB queries.
system.php changes:
- Tab <a> elements gain data-tab= attributes used by JS to identify the
target without parsing hrefs.
- Tab panel content wrapped in <div id=sys-tab-panel data-tab= data-n=>
which JS uses as both the swap target and its own state store.
- JS rewritten: tab clicks and lines-select changes call loadPanel()
which fetch()es system-fragment.php, swaps innerHTML, updates active
tab ARIA attributes, and pushes state via history.pushState.
- Browser back/forward handled via popstate listener.
- bindPanelControls() re-wires the lines-select and copy-to-clipboard
button after every innerHTML swap (event delegation not feasible here
because log-output is replaced wholesale).
- fetch() failure falls back to window.location.href (full page load).
- Tabs without JS still work: <a href> links go to system.php?tab=…
as before.
system-fragment.php:
- Requires AdminAuth::isAuthenticated(); returns 403 on failure.
- Validates tab and n params against the same whitelist as system.php.
- All helper functions namespaced with frag_ prefix to avoid redeclaration
if PHP ever includes both files in the same process.
- Renders identical HTML to the corresponding section in system.php.
system.css:
- #sys-tab-panel gets min-height:8rem and position:relative to prevent
layout jump during fetch.
- .sys-panel-loading: opacity 0.4 + pointer-events:none + subtle
diagonal-stripe ::after overlay with shimmer animation.
Status (services, PHP env, disk) is now always visible above the log/config
tab bar rather than being one of the tab targets:
- Status section rendered unconditionally above <nav class="sys-tabs">.
- Services grid, PHP info grid and disk bar grouped inside a collapsible
<section> with a header row containing the cache-freshness badge and a
toggle button (▲ Réduire / ▼ Développer).
- Collapse state persisted in localStorage so the preference survives
page reloads (e.g. when switching log tabs).
- Tab bar now only contains the three log tabs + nginx config; the 'Statut'
tab is removed. Legacy ?tab=status URLs fall through to nginx_access.
- PHP/disk sub-sections laid out in a 2-col grid inside the status panel;
responsive single-col below 700px.
- system.css: new .sys-status-section / .sys-status-header /
.sys-status-toggle / .sys-status-meta rules added.
- aria-current="page" added to active tab links.
- todo/03-system-cache.md: all items marked done; notes added explaining
why log caching was deliberately omitted.