fix(a11y): move pages-edit EasyMDE scripts to head/footer, add h1 to home, fix stale TODO items

- pages-edit.php: EasyMDE CDN JS URL moved to $extraJs (rendered by footer.php before </body>);
  inline EasyMDE init block moved to $extraJsInline, emitted by footer.php via new
  `<?php if (!empty($extraJsInline))` guard - fixes invalid <script> floating in <body> (WCAG 4.1.1)
- pages-edit.php: add <small> keyboard-trap hint below the editor textarea:
  'Appuyez sur Échap pour quitter l'éditeur au clavier.' (WCAG 2.1.2)
- templates/admin/footer.php: extend to support $extraJsInline (raw inline script string)
- index.php: add <h1 class="sr-only">Mémoires de l'ERG</h1> inside <main> so the page has
  a document heading (WCAG 2.4.6; h2 columns in search.php already had a sr-only h1)
- TODO.md: mark completed items as [x]: skip links (2.4.1), focus-visible / outline:none
  removal (2.4.7), search.php h1 + index.php h1 (2.4.6), pages-edit.php invalid HTML (4.1.1),
  EasyMDE keyboard trap hint (2.1.2)
This commit is contained in:
Pontoporeia
2026-03-31 15:28:47 +02:00
parent 59ae2151d0
commit 986945a347
4 changed files with 29 additions and 22 deletions

19
TODO.md
View File

@@ -1077,14 +1077,14 @@ Current state: **zero ARIA attributes, zero skip links, zero focus-visible style
#### 2.1.2 No keyboard trap
- [ ] **EasyMDE editor in `pages-edit.php`** - CodeMirror-based editors are known keyboard
- [x] **EasyMDE editor in `pages-edit.php`** - CodeMirror-based editors are known keyboard
traps; Tab inside the editor inserts a tab character rather than moving focus out. EasyMDE
provides an escape route (Escape key exits the editor). Verify this works and document
it with a visible hint below the editor (`<small>Appuyez sur Échap pour quitter l'éditeur</small>`).
#### 2.4.1 Bypass blocks - skip link
- [ ] **No skip-to-main-content link exists on any page** - every page loads with focus on
- [x] **No skip-to-main-content link exists on any page** - every page loads with focus on
the browser chrome, then Tab cycles through the nav and search bar before reaching `<main>`.
On the home page that means tabbing through 4 nav links before reaching 24 thesis cards.
Add `<a href="#main-content" class="skip-link">Aller au contenu principal</a>` as the
@@ -1133,14 +1133,14 @@ Current state: **zero ARIA attributes, zero skip links, zero focus-visible style
a label/metadata, not a heading. This is flagged in the semantic audit but it is also
directly a WCAG 2.4.6 failure (heading does not describe the topic of the page).
- [ ] **`search.php` répertoire index: `<h2>` headings inside columns are correct** - "Années",
- [x] **`search.php` répertoire index: `<h2>` headings inside columns are correct** - "Années",
"Catégories", "Étudiantes", "Mots-clés" as `<h2>` under a page with no `<h1>` is a skip.
Add an `<h1>` for the page (visually hidden if needed): `<h1 class="sr-only">Répertoire</h1>`.
Same for `index.php` which has no heading at all.
`index.php` now has `<h1 class="sr-only">Mémoires de l'ERG</h1>` inside `<main>`.
#### 2.4.7 Focus visible
- [ ] **No `:focus-visible` style defined anywhere in the public CSS** - `common.css`,
- [x] **No `:focus-visible` style defined anywhere in the public CSS** - `common.css`,
`main.css`, `search.css`, `tfe.css`, and `apropos.css` contain zero `:focus` or
`:focus-visible` rules. `modern-normalize` does not add any either. The browser's default
focus ring is the only indicator, and it is suppressed by `outline: none` on
@@ -1155,7 +1155,7 @@ Current state: **zero ARIA attributes, zero skip links, zero focus-visible style
in `common.css`. This single rule covers every `<a>`, `<button>`, `<input>`, `<select>`,
`<textarea>` on public pages. For admin: same using `var(--admin-purple)`.
- [ ] **`outline: none` on `.site-search__input`** - this is an explicit suppression of the
- [x] **`outline: none` on `.site-search__input`** - this is an explicit suppression of the
browser focus ring with no replacement. Remove `outline: none` once the global
`:focus-visible` rule above is in place. Same for `outline: none` on `.admin-input`,
`.admin-select`, `.admin-textarea`, and `.search-filter-select`.
@@ -1225,9 +1225,10 @@ Current state: **zero ARIA attributes, zero skip links, zero focus-visible style
#### 4.1.1 Parsing
- [ ] **`pages-edit.php` has a `<link>` element inside `<body>`** - invalid HTML. Confirmed in
the semantic audit (section XV). Browsers tolerate it but validators flag it and some AT
may misinterpret the document structure.
- [x] **`pages-edit.php` has a `<link>` element inside `<body>`** - invalid HTML. Confirmed in
the semantic audit (section XV). The EasyMDE CSS was already moved to `$extraCss` (rendered
in `<head>`). The inline `<script>` init block is now moved to `$extraJsInline` (rendered
by `footer.php` before `</body>`). CDN JS URL uses `$extraJs`. All scripts/styles valid.
#### 4.1.2 Name, role, value

View File

@@ -29,6 +29,16 @@ try {
$pageTitle = "Éditer : " . htmlspecialchars($page['title']);
$extraCss = ['https://cdn.jsdelivr.net/npm/easymde/dist/easymde.min.css'];
$extraJs = ['https://cdn.jsdelivr.net/npm/easymde/dist/easymde.min.js'];
$extraJsInline = <<<'JS'
var easyMDE = new EasyMDE({
element: document.getElementById('content'),
spellChecker: false,
status: ['lines', 'words'],
minHeight: '400px',
toolbarTips: true
});
JS;
?>
<?php require_once APP_ROOT . '/templates/admin/head.php'; ?>
@@ -41,8 +51,11 @@ $extraCss = ['https://cdn.jsdelivr.net/npm/easymde/dist/easymde.min.css'];
<div class="admin-form-row" style="align-items:start;">
<label class="admin-label" for="content">Contenu (Markdown) :</label>
<div>
<textarea class="admin-textarea" id="content" name="content"
rows="20"><?= htmlspecialchars($page['content'] ?? '') ?></textarea>
<small class="admin-hint">Appuyez sur <kbd>Échap</kbd> pour quitter l'éditeur au clavier.</small>
</div>
</div>
<div class="admin-submit-wrap">
@@ -51,15 +64,4 @@ $extraCss = ['https://cdn.jsdelivr.net/npm/easymde/dist/easymde.min.css'];
</div>
</form>
</main>
<script src="https://cdn.jsdelivr.net/npm/easymde/dist/easymde.min.js"></script>
<script>
var easyMDE = new EasyMDE({
element: document.getElementById('content'),
spellChecker: false,
status: ['lines', 'words'],
minHeight: '400px',
toolbarTips: true
});
</script>
<?php require_once APP_ROOT . '/templates/admin/footer.php'; ?>

View File

@@ -80,6 +80,7 @@ $extraCss = ['assets/main.css'];
<?php endif; ?>
<main class="home-main" id="main-content">
<h1 class="sr-only">Mémoires de l'ERG</h1>
<ul class="cards-container">
<?php foreach ($itemsToLoad as $item): ?>
<li class="card">

View File

@@ -1,5 +1,8 @@
<?php foreach ($extraJs ?? [] as $js): ?>
<script src="<?= htmlspecialchars($js) ?>"></script>
<?php endforeach; ?>
<?php if (!empty($extraJsInline)): ?>
<script><?= $extraJsInline ?></script>
<?php endif; ?>
</body>
</html>