[Glitch] Patch over a11y issues caused by "simple" ruby gems

Port 5553448678a8a9f4ef970948b6fb7f013e9a4238 to glitch-soc

Signed-off-by: Claire <claire.github-309c@sitedethib.com>
This commit is contained in:
diondiondion 2026-06-08 15:30:10 +02:00 committed by Claire
parent b0f609bfb7
commit 4eeb1b0118

View File

@ -181,6 +181,8 @@ async function loaded() {
truncateRuleHints();
applyRailsA11yPatches();
const reactComponents = document.querySelectorAll('[data-component]');
if (reactComponents.length > 0) {
@ -515,6 +517,88 @@ on('click', '.rules-list button', ({ target }) => {
}
});
/**
* Patch accessibility issues caused by Ruby Gems that
* don't produce accessible markup (simple-forms & simple-navigation)
*/
function applyRailsA11yPatches() {
/**
* Mark current navigation item with aria-current
*/
const activeNavLink = document.querySelector(
'.simple-navigation-active-leaf a.selected',
);
activeNavLink?.setAttribute('aria-current', 'page');
/**
* Hides the asterisk added to labels of required form fields
* from assistive tech. (Those fields already have the `required` attribute)
*/
document
.querySelectorAll<HTMLElement>('.simple_form label.required abbr')
.forEach((element) => {
element.setAttribute('aria-hidden', 'true');
});
/**
* Associate form field hints with their inputs via aria-describedby
*/
document
.querySelectorAll<HTMLDivElement>('.simple_form .field_with_hint')
.forEach((field) => {
const inputs = field.querySelectorAll<
HTMLInputElement | HTMLTextAreaElement
>("input[type='text'], input[type='checkbox'], textarea");
const hint = field.querySelector<HTMLDivElement>('.hint');
// Bail out if there are more than one input as
// the association can't be safely made.
if (inputs.length !== 1 || !inputs[0] || !hint) {
return;
}
const input = inputs[0];
const inputId = input.getAttribute('id');
const hintId = `${inputId}_hint`;
input.setAttribute('aria-describedby', hintId);
hint.setAttribute('id', hintId);
});
/**
* Add fieldset-like group labels ("legends") to the date-of-birth selector
* and groups of radio buttons
*/
const groups = document.querySelectorAll<HTMLDivElement>(
'.simple_form .date_of_birth, .simple_form .input.with_label.radio_buttons',
);
groups.forEach((groupWrapper) => {
// This is the element serving as the label of the group.
const groupLabel = groupWrapper.querySelector<HTMLLabelElement>('label');
const labelWithId =
groupWrapper.querySelector<HTMLLabelElement>('label[for]');
const groupHint = groupWrapper.querySelector<HTMLDivElement>('.hint');
// We need a unique ID to generate the aria associations. If `groupLabel`
// doesn't have one, we just take the first label with a `for` attribute
// that we can find, which is fine because we'll modify it before use.
const inputId =
groupLabel?.getAttribute('for') ?? labelWithId?.getAttribute('for');
const labelId = `${inputId}_label`;
const hintId = `${inputId}_hint`;
groupLabel?.setAttribute('id', labelId);
groupHint?.setAttribute('id', hintId);
groupWrapper.setAttribute('role', 'group');
groupWrapper.setAttribute('aria-labelledby', labelId);
if (groupHint) {
groupWrapper.setAttribute('aria-describedby', hintId);
}
});
}
function main() {
ready(loaded).catch((error: unknown) => {
console.error(error);