mirror of
https://codeberg.org/PostERG/xamxam.git
synced 2026-05-06 19:19:19 +02:00
a11y(jury-fieldset): fix WCAG 3.3.2, 4.1.2, 2.1.1 + audit 1.4.4/1.4.12
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
This commit is contained in:
36
TODO.md
36
TODO.md
@@ -7,28 +7,34 @@ Pending tasks have been split into topic files under [`todo/`](todo/README.md):
|
|||||||
| [todo/01-css-semantic-refactor.md](todo/01-css-semantic-refactor.md) | CSS class audit, semantic HTML (public + admin), inline style extraction, favicon |
|
| [todo/01-css-semantic-refactor.md](todo/01-css-semantic-refactor.md) | CSS class audit, semantic HTML (public + admin), inline style extraction, favicon |
|
||||||
| [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)
|
## Recently completed (this session)
|
||||||
|
|
||||||
- [x] `admin.css` — added `[aria-current="page"]` rule for admin nav links (`border-bottom: 2px solid currentColor; padding-bottom: 1px`) fixing WCAG 1.4.1 (active nav link had no non-colour indicator)
|
- [x] `admin.css` - added `[aria-current="page"]` rule for admin nav links (`border-bottom: 2px solid currentColor; padding-bottom: 1px`) fixing WCAG 1.4.1 (active nav link had no non-colour indicator)
|
||||||
- [x] `admin.css` — fixed undefined `--admin-purple` variable in pagination hover; replaced with `--accent-primary` (same `#9557b5` value)
|
- [x] `admin.css` - fixed undefined `--admin-purple` variable in pagination hover; replaced with `--accent-primary` (same `#9557b5` value)
|
||||||
- [x] `todo/01-css-semantic-refactor.md` — audited all CSS/HTML refactor tasks; marked ~15 items as already-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` selector, `login.php`/`edit.php` inline styles, `admin-alert` replacement, form partial hints)
|
- [x] `todo/01-css-semantic-refactor.md` - audited all CSS/HTML refactor tasks; marked ~15 items as already-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` selector, `login.php`/`edit.php` inline styles, `admin-alert` replacement, form partial hints)
|
||||||
- [x] `todo/04-accessibility.md` — marked WCAG 1.4.1 admin nav and `--admin-purple` audit items as completed
|
- [x] `todo/04-accessibility.md` - marked WCAG 1.4.1 admin nav and `--admin-purple` audit items as completed
|
||||||
|
|
||||||
- [x] `admin/index.php` — server-side pagination (25/page); `Database::getThesesListCount()` added; `getThesesList()` extended with `$limit`/`$offset`; `access_type` JOIN added to query (was missing); result-count meta line added; `.pagination-wrap` + `.pagination-btn` + `.pagination-info` styles added to `admin.css`
|
- [x] `admin/index.php` - server-side pagination (25/page); `Database::getThesesListCount()` added; `getThesesList()` extended with `$limit`/`$offset`; `access_type` JOIN added to query (was missing); result-count meta line added; `.pagination-wrap` + `.pagination-btn` + `.pagination-info` styles added to `admin.css`
|
||||||
|
|
||||||
- [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] `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] `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] `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] `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] `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] `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] `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
|
- [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
|
||||||
|
|
||||||
- [x] `admin-submit-wrap` → `admin-form-footer` rename: updated all 6 admin templates (`add.php`, `edit.php`, `login.php`, `account.php`, `import.php`, `pages-edit.php`) and all 8 CSS selectors in `admin.css` (`.admin-form > div:not(…)` exclusion guards, `.admin-login-box` overrides). Closes `todo/01-css-semantic-refactor.md` submit-wrap task.
|
- [x] `admin-submit-wrap` → `admin-form-footer` rename: updated all 6 admin templates (`add.php`, `edit.php`, `login.php`, `account.php`, `import.php`, `pages-edit.php`) and all 8 CSS selectors in `admin.css` (`.admin-form > div:not(...)` exclusion guards, `.admin-login-box` overrides). Closes `todo/01-css-semantic-refactor.md` submit-wrap task.
|
||||||
- [x] Marked `status-badge.php` partial and WCAG 1.3.1 status-badge items as already-done in `todo/02-php-components.md` and `todo/04-accessibility.md` (partial + CSS were fully implemented; TODO had not been updated)
|
- [x] Marked `status-badge.php` partial and WCAG 1.3.1 status-badge items as already-done in `todo/02-php-components.md` and `todo/04-accessibility.md` (partial + CSS were fully implemented; TODO had not been updated)
|
||||||
|
|
||||||
- [x] `public/index.php` — WCAG 2.4.4: home page cards now append `<span class="sr-only">, YEAR</span>` to each card's `<p>` link text so screen readers get unique link names when two theses share the same title
|
- [x] `public/index.php` — WCAG 2.4.4: home page cards now append `<span class="sr-only">, YEAR</span>` to each card’s `<p>` link text so screen readers get unique link names when two theses share the same title
|
||||||
- [x] `todo/04-accessibility.md` — WCAG 2.4.3: marked back-link focus-order item as already done (`tfe-back-link` is already the first DOM element in `.tfe-left`, before `<h1>`)
|
- [x] `todo/04-accessibility.md` — WCAG 2.4.3: marked back-link focus-order item as already done (`tfe-back-link` is already the first DOM element in `.tfe-left`, before `<h1>`)
|
||||||
|
|
||||||
|
- [x] `jury-fieldset.php` — WCAG 3.3.2: replaced bare `<label>Lecteur·ices :</label>` with `<fieldset class="admin-jury-lecteurs"><legend>Lecteur·ices</legend>` so the lecteur group has a proper programmatic label
|
||||||
|
- [x] `jury-fieldset.php` — WCAG 4.1.2: all "Externe" checkboxes (promoteur + each lecteur row, static + dynamically added via JS) now carry `aria-label="[Role] — externe"` providing group context without visible redundancy
|
||||||
|
- [x] `jury-fieldset.php` — WCAG 2.1.1: jury remove buttons verified to have descriptive `aria-label="Supprimer le lecteur·ice N"` on all rows (static + dynamic)
|
||||||
|
- [x] `admin.css` — added `.admin-body fieldset fieldset.admin-jury-lecteurs` rule: strips border/background on the nested lecteur fieldset so it renders as a visual sub-group, not a double-bordered card
|
||||||
|
- [x] `todo/04-accessibility.md` — WCAG 1.4.4 + 1.4.12 audited and marked done: all font-sizes are `rem`; no `overflow:hidden` on essential text content
|
||||||
|
|||||||
@@ -747,6 +747,26 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* ── Jury fieldset ──────────────────────────────────────────────────────── */
|
/* ── Jury fieldset ──────────────────────────────────────────────────────── */
|
||||||
|
|
||||||
|
/* Nested lecteur·ices fieldset sits inside the outer jury fieldset:
|
||||||
|
strip heavy border/background so it reads as a sub-group, not a card. */
|
||||||
|
.admin-body fieldset fieldset.admin-jury-lecteurs {
|
||||||
|
border: none;
|
||||||
|
padding: 0;
|
||||||
|
margin: 0.75rem 0 0;
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.admin-body fieldset fieldset.admin-jury-lecteurs > legend {
|
||||||
|
font-size: 0.78rem;
|
||||||
|
font-weight: 600;
|
||||||
|
letter-spacing: 0.03em;
|
||||||
|
text-transform: uppercase;
|
||||||
|
color: var(--text-tertiary);
|
||||||
|
padding: 0;
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
.admin-jury-row {
|
.admin-jury-row {
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 0.75rem;
|
gap: 0.75rem;
|
||||||
|
|||||||
@@ -48,56 +48,64 @@ $juryIdx = max(count($juryLecteurs), 1);
|
|||||||
value="<?= htmlspecialchars($juryPromoteur ?? '') ?>" placeholder="Nom">
|
value="<?= htmlspecialchars($juryPromoteur ?? '') ?>" placeholder="Nom">
|
||||||
<label class="admin-checkbox-label admin-jury-ext">
|
<label class="admin-checkbox-label admin-jury-ext">
|
||||||
<input type="checkbox" name="jury_promoteur_ext" value="1"
|
<input type="checkbox" name="jury_promoteur_ext" value="1"
|
||||||
<?= $juryPromoteurExt ? 'checked' : '' ?>> Externe
|
<?= $juryPromoteurExt ? 'checked' : '' ?>
|
||||||
|
aria-label="Promoteur·ice — externe"> Externe
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Lecteur·ices (dynamic list) -->
|
<!-- Lecteur·ices (dynamic list) -->
|
||||||
<div>
|
<fieldset class="admin-jury-lecteurs">
|
||||||
<label>Lecteur·ices :</label>
|
<legend>Lecteur·ices</legend>
|
||||||
<div>
|
<div id="jury-lecteurs-list" class="admin-jury-list">
|
||||||
<div id="jury-lecteurs-list" class="admin-jury-list">
|
<?php if (empty($juryLecteurs)): ?>
|
||||||
<?php if (empty($juryLecteurs)): ?>
|
<div class="admin-jury-entry">
|
||||||
|
<input type="text" name="jury_lecteurs[]" placeholder="Nom"
|
||||||
|
id="jury_lecteur_0" aria-label="Lecteur·ice 1 — nom">
|
||||||
|
<label class="admin-checkbox-label admin-jury-ext">
|
||||||
|
<input type="checkbox" name="jury_lecteurs_ext[0]" value="1"
|
||||||
|
aria-label="Lecteur·ice 1 — externe"> Externe
|
||||||
|
</label>
|
||||||
|
<button type="button" class="admin-btn-remove" onclick="removeJuryRow(this)"
|
||||||
|
aria-label="Supprimer le lecteur·ice 1"><span aria-hidden="true">✕</span></button>
|
||||||
|
</div>
|
||||||
|
<?php else: ?>
|
||||||
|
<?php foreach ($juryLecteurs as $li => $lm): ?>
|
||||||
|
<?php $lNum = $li + 1; ?>
|
||||||
<div class="admin-jury-entry">
|
<div class="admin-jury-entry">
|
||||||
<input type="text" name="jury_lecteurs[]" placeholder="Nom">
|
<input type="text" name="jury_lecteurs[]"
|
||||||
|
value="<?= htmlspecialchars($lm['name']) ?>" placeholder="Nom"
|
||||||
|
id="jury_lecteur_<?= $li ?>" aria-label="Lecteur·ice <?= $lNum ?> — nom">
|
||||||
<label class="admin-checkbox-label admin-jury-ext">
|
<label class="admin-checkbox-label admin-jury-ext">
|
||||||
<input type="checkbox" name="jury_lecteurs_ext[0]" value="1"> Externe
|
<input type="checkbox" name="jury_lecteurs_ext[<?= $li ?>]" value="1"
|
||||||
|
<?= $lm['is_external'] ? 'checked' : '' ?>
|
||||||
|
aria-label="Lecteur·ice <?= $lNum ?> — externe"> Externe
|
||||||
</label>
|
</label>
|
||||||
<button type="button" class="admin-btn-remove" onclick="removeJuryRow(this)"
|
<button type="button" class="admin-btn-remove" onclick="removeJuryRow(this)"
|
||||||
aria-label="Supprimer ce lecteur"><span aria-hidden="true">✕</span></button>
|
aria-label="Supprimer le lecteur·ice <?= $lNum ?>"><span aria-hidden="true">✕</span></button>
|
||||||
</div>
|
</div>
|
||||||
<?php else: ?>
|
<?php endforeach; ?>
|
||||||
<?php foreach ($juryLecteurs as $li => $lm): ?>
|
<?php endif; ?>
|
||||||
<div class="admin-jury-entry">
|
|
||||||
<input type="text" name="jury_lecteurs[]"
|
|
||||||
value="<?= htmlspecialchars($lm['name']) ?>" placeholder="Nom">
|
|
||||||
<label class="admin-checkbox-label admin-jury-ext">
|
|
||||||
<input type="checkbox" name="jury_lecteurs_ext[<?= $li ?>]" value="1"
|
|
||||||
<?= $lm['is_external'] ? 'checked' : '' ?>> Externe
|
|
||||||
</label>
|
|
||||||
<button type="button" class="admin-btn-remove" onclick="removeJuryRow(this)"
|
|
||||||
aria-label="Supprimer ce lecteur"><span aria-hidden="true">✕</span></button>
|
|
||||||
</div>
|
|
||||||
<?php endforeach; ?>
|
|
||||||
<?php endif; ?>
|
|
||||||
</div>
|
|
||||||
<button type="button" class="admin-btn-secondary admin-add-jury-btn"
|
|
||||||
onclick="addJuryRow()">+ Ajouter un·e lecteur·ice</button>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<button type="button" class="admin-btn-secondary admin-add-jury-btn"
|
||||||
|
onclick="addJuryRow()">+ Ajouter un·e lecteur·ice</button>
|
||||||
|
</fieldset>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
<script>
|
<script>
|
||||||
var juryIdx = <?= $juryIdx ?>;
|
var juryIdx = <?= $juryIdx ?>;
|
||||||
function addJuryRow() {
|
function addJuryRow() {
|
||||||
var list = document.getElementById('jury-lecteurs-list');
|
var list = document.getElementById('jury-lecteurs-list');
|
||||||
|
var n = list.querySelectorAll('.admin-jury-entry').length + 1;
|
||||||
var div = document.createElement('div');
|
var div = document.createElement('div');
|
||||||
div.className = 'admin-jury-entry';
|
div.className = 'admin-jury-entry';
|
||||||
div.innerHTML = '<input type="text" name="jury_lecteurs[]" placeholder="Nom">'
|
div.innerHTML = '<input type="text" name="jury_lecteurs[]" placeholder="Nom"'
|
||||||
|
+ ' aria-label="Lecteur\u00b7ice ' + n + ' \u2014 nom">'
|
||||||
+ '<label class="admin-checkbox-label admin-jury-ext">'
|
+ '<label class="admin-checkbox-label admin-jury-ext">'
|
||||||
+ '<input type="checkbox" name="jury_lecteurs_ext[' + juryIdx + ']" value="1"> Externe'
|
+ '<input type="checkbox" name="jury_lecteurs_ext[' + juryIdx + ']" value="1"'
|
||||||
|
+ ' aria-label="Lecteur\u00b7ice ' + n + ' \u2014 externe"> Externe'
|
||||||
+ '</label>'
|
+ '</label>'
|
||||||
+ '<button type="button" class="admin-btn-remove" onclick="removeJuryRow(this)" aria-label="Supprimer ce lecteur"><span aria-hidden="true">✕</span></button>';
|
+ '<button type="button" class="admin-btn-remove" onclick="removeJuryRow(this)"'
|
||||||
|
+ ' aria-label="Supprimer le lecteur\u00b7ice ' + n + '"><span aria-hidden="true">\u2715</span></button>';
|
||||||
list.appendChild(div);
|
list.appendChild(div);
|
||||||
juryIdx++;
|
juryIdx++;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,15 +22,15 @@
|
|||||||
|
|
||||||
## 1.4.4 Resize text
|
## 1.4.4 Resize text
|
||||||
|
|
||||||
- [ ] **Verify no text is set in `px`** — ensure `width: 14px; height: 14px` on checkboxes and similar elements do not prevent text scaling
|
- [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
|
## 1.4.12 Text spacing
|
||||||
|
|
||||||
- [ ] **No text-spacing override test done** — verify WCAG 1.4.12 bookmarklet does not cause content clipping (especially `overflow: hidden` on `.card__media` and tight `aspect-ratio: 4/3`)
|
- [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
|
## 2.1.1 Keyboard
|
||||||
|
|
||||||
- [ ] **Jury "✕" remove buttons in `add.php`/`edit.php`** — add `aria-label` for keyboard discoverability
|
- [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
|
## 2.4.3 Focus order
|
||||||
|
|
||||||
@@ -62,17 +62,17 @@
|
|||||||
|
|
||||||
## 3.3.2 Labels or instructions
|
## 3.3.2 Labels or instructions
|
||||||
|
|
||||||
- [ ] **Admin jury "Lecteur·ices" label has no `for` attribute** — wrap lecteur rows in `<fieldset>/<legend>` or use `aria-describedby`
|
- [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 fieldset’s border/padding so it renders as a sub-group
|
||||||
|
|
||||||
## 4.1.2 Name, role, value
|
## 4.1.2 Name, role, value
|
||||||
|
|
||||||
- [ ] **Custom "Externe" checkbox for jury members has no group context** — add `aria-label="[Nom du promoteur] est externe"` dynamically via JS, or `aria-describedby` pointing to the adjacent name input
|
- [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
|
||||||
|
|
||||||
- [ ] **`<video>` elements on `tfe.php` have no captions** — add `<track kind="captions">` slot in template; document caption requirement in admin upload form
|
- [ ] **`<video>` elements on `tfe.php` have no captions** — add `<track kind="captions">` slot in template; document caption requirement in admin upload form
|
||||||
|
|
||||||
- [ ] **Admin `<select>` for visibility/access in `edit.php` uses truncated option text** — use full description in option text (or `title` attribute); keep truncated text only for visual display
|
- [ ] **Admin `<select>` for visibility/access in `edit.php` uses truncated option text** — use full description in option text (or `title` attribute); keep truncated text only for visual display
|
||||||
|
|
||||||
- [ ] **Bulk publish/unpublish JS does not announce result to screen readers** — add `role="alert"` to error messages and `role="status"` to success messages across all admin pages
|
- [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
|
## 5 - Motion & user preferences
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user