Skip to content

Instantly share code, notes, and snippets.

@zzap
Last active September 28, 2026 14:08
Show Gist options
  • Select an option

  • Save zzap/e45702d27c0f8593355d85d95f7ac202 to your computer and use it in GitHub Desktop.

Select an option

Save zzap/e45702d27c0f8593355d85d95f7ac202 to your computer and use it in GitHub Desktop.
AI fixes accessibility problem
// This is Copilot's response to a following prompt:
//
// "This is the code from a WordPress plugin I have no control over.
// Write code that I can use in my plugin to make this form accessible."
//
// Code can be found here: https://github.com/zzap/WordPress-Gems-for-Devs-Accessibility-with-HTML-API/blob/main/wp-content/plugins/wpgems-inaccessible-blocks/src/blocks/form/save.js
//
import { useBlockProps } from '@wordpress/block-editor';
import { useInstanceId } from '@wordpress/compose';
export default function save() {
const instanceId = useInstanceId( save );
const nameId = `wpgems-form-name-${ instanceId }`;
const emailId = `wpgems-form-email-${ instanceId }`;
const optionsId = `wpgems-form-options-${ instanceId }`;
return (
<div {...useBlockProps.save()}>
<form>
<p>
<label htmlFor={ nameId }>Name</label>
<input
id={ nameId }
name="name"
type="text"
placeholder="Enter your name"
autoComplete="name"
/>
</p>
<p>
<label htmlFor={ emailId }>Email</label>
<input
id={ emailId }
name="email"
type="email"
autoComplete="email"
/>
</p>
<div>
<label htmlFor={ optionsId }>Select an option:</label>
<select id={ optionsId } name="options">
<option value="1">Option 1</option>
<option value="2">Option 2</option>
</select>
</div>
<button type="submit">Submit</button>
</form>
</div>
);
}
/**
* This is Copilot's second attempt to modify plugin's output, the prompt:
*
* "How is that modifying the plugin's output?
* I need to use this other plugin with an inaccessible form, but I need to modify its output
* without modifying its files."
*/
/**
* Codepilot says:
* "If the form is already on the page and you only need a frontend accessibility patch, use JS instead.
*
* Why this works:
* - You’re not editing the third-party plugin files.
* - You’re overriding/repairing the rendered output in a WordPress-safe hook.
* - This preserves the plugin while fixing the accessibility issue."
*/
document.addEventListener( 'DOMContentLoaded', () => {
document.querySelectorAll( '.wp-block-wpgems-inaccessible-blocks-form form' ).forEach( ( form, formIndex ) => {
const nameInput = form.querySelector( 'input[name="name"]' );
const emailInput = form.querySelector( 'input[name="email"]' );
const select = form.querySelector( 'select[name="options"]' );
if ( nameInput ) {
nameInput.id = `name-${ formIndex }`;
const label = document.createElement( 'label' );
label.setAttribute( 'for', nameInput.id );
label.textContent = 'Name';
nameInput.before( label );
}
if ( emailInput ) {
emailInput.id = `email-${ formIndex }`;
const label = document.createElement( 'label' );
label.setAttribute( 'for', emailInput.id );
label.textContent = 'Email';
emailInput.before( label );
}
if ( select ) {
select.id = `options-${ formIndex }`;
const label = document.createElement( 'label' );
label.setAttribute( 'for', select.id );
label.textContent = 'Select an option:';
select.before( label );
}
} );
} );
<?php
/**
* This is Copilot's second attempt to modify plugin's output, the prompt:
*
* "How is that modifying the plugin's output?
* I need to use this other plugin with an inaccessible form, but I need to modify its output
* without modifying its files."
*/
/**
* Codepilot says:
* "Best approach: filter the block output in WordPress.
* If the plugin is a block and the HTML later gets re-rendered server-side, this is the cleanest non-invasive fix."
*/
/**
* Plugin Name: My Accessible Form Patcher
*/
add_filter( 'render_block', function( $block_content, $block ) {
if ( empty( $block['blockName'] ) || 'wpgems-inaccessible-blocks/form' !== $block['blockName'] ) {
return $block_content;
}
$dom = new DOMDocument();
libxml_use_internal_errors( true );
$dom->loadHTML( '<?xml encoding="utf-8" ?>' . $block_content, LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD );
$form = $dom->getElementsByTagName( 'form' )->item( 0 );
if ( ! $form ) {
return $block_content;
}
$fields = $form->getElementsByTagName( 'input' );
$index = 0;
foreach ( $fields as $field ) {
$id = 'form-field-' . $index++;
if ( 'name' === $field->getAttribute( 'name' ) ) {
$field->setAttribute( 'id', $id );
$field->setAttribute( 'autocomplete', 'name' );
}
if ( 'email' === $field->getAttribute( 'name' ) ) {
$field->setAttribute( 'id', $id );
$field->setAttribute( 'autocomplete', 'email' );
}
}
$select = $dom->getElementsByTagName( 'select' )->item( 0 );
if ( $select ) {
$select->setAttribute( 'id', 'form-options-' . wp_unique_id() );
}
$labels = $dom->getElementsByTagName( 'label' );
foreach ( $labels as $label ) {
$label->setAttribute( 'for', $label->getAttribute( 'for' ) ?: 'form-field-0' );
}
return $dom->saveHTML();
}, 10, 2 );
<?php
/**
* This is Copilot's third attempt to modify plugin's output, with the following prompt:
*
* "Why not using some of WordPress' API for this? For example, the HTML API?"
*/
/**
* Codepilot says:
* Because this is not a “database / WP API” problem; it is a “final rendered HTML” problem.
*
* The JS in save.js is the block’s saved markup. If you cannot change that plugin’s code,
* the idiomatic WordPress way is to mutate the rendered HTML after it is generated, not to edit the third-party source.
*
* Use the WordPress HTML API, especially WP_HTML_Tag_Processor, inside a render_block filter.
* It is safer and cleaner than regex or DOMDocument for WordPress HTML.
*
* Why this is the right approach:
* - It avoids modifying the third-party plugin files.
* - It uses WordPress-native HTML parsing instead of string surgery.
* - It preserves accessibility without breaking the block’s HTML structure.
*
* Note:
* - save.js itself is still the author’s source of truth for the block markup.
* - If the plugin later changes its block or markup, this filter should be adjusted.
* - For true <label for="..."> associations, you’d ideally add real label elements, but aria-label is a valid fallback when you cannot alter the original markup cleanly.
*
*/
add_filter( 'render_block', function( $block_content, $block ) {
if ( ( $block['blockName'] ?? '' ) !== 'wpgems-inaccessible-blocks/form' ) {
return $block_content;
}
$processor = new WP_HTML_Tag_Processor( $block_content );
while ( $processor->next_tag() ) {
$tag = $processor->get_tag();
$name = $processor->get_attribute( 'name' );
if ( 'input' === $tag && 'name' === $name ) {
$processor->set_attribute( 'id', 'wpgems-name' );
$processor->set_attribute( 'autocomplete', 'name' );
$processor->set_attribute( 'aria-label', 'Name' );
}
if ( 'input' === $tag && 'email' === $name ) {
$processor->set_attribute( 'id', 'wpgems-email' );
$processor->set_attribute( 'autocomplete', 'email' );
$processor->set_attribute( 'aria-label', 'Email' );
}
if ( 'select' === $tag && 'options' === $name ) {
$processor->set_attribute( 'id', 'wpgems-options' );
$processor->set_attribute( 'aria-label', 'Select an option' );
}
}
return $processor->get_updated_html();
}, 10, 2 );
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment