Umbrella WordPress plugin for accessibility Features and Experiments, each labelled with a Track describing whether it is aimed at Core adoption or shipped as a standalone practical tool.
Every module answers two questions:
Bucket — shape/maturity:
- Features — practical, stable tools. Safe to rely on.
- Experiments — architecturally hard changes gathering real-world data before Core commits.
Track — intent to reach WordPress Core:
- Core-track — this module exists to inform a Core proposal.
- Practical — shipped as a stable tool, no Core promise implied.
Either bucket can live on either track. Credits are optional metadata any module can attach when adopted from a community source.
| Module | Bucket | Track | Adopted from |
|---|---|---|---|
| Media Library: disable infinite scroll by default | Feature | Practical | WordPress core |
| Heading-order validation | Experiment | TBD | — |
| Block Validation Framework | Experiment | Core-track | validation-api (Troy Chaplin) |
| Core block accessibility rules | Feature | Practical | validation-api-core-blocks (Troy Chaplin) |
| Validation settings | Feature | Practical | validation-api-settings (Troy Chaplin) |
Three of the first-party modules compose into a full block-editor validation subsystem:
- Block Validation Framework — a plugin API (
validation_api_register_block_check(),validation_api_register_meta_check(),validation_api_register_editor_check()), anaccessibility-lab/validationdata store, real-time debounced validation, publish locking, a Validation sidebar, and REST introspection atGET /wp-validation/v1/checks. - Core block accessibility rules — WCAG-oriented checks for
core/image,core/button,core/table,core/heading,core/gallery, plus a required post/page title editor check. - Validation settings — a single Validation admin page listing every registered check in a DataViews table, with search, sorting, pagination, and filters for check type, registering plugin, and severity. Admins set each check's severity to Error, Warning, or Disabled, and reset overridden checks back to their registered default. Third-party plugins that register checks appear in that table automatically, with zero admin-menu code.
Third parties integrate by calling the same validation_api_register_* functions and hooking the editor.validateBlock / editor.validateMeta / editor.validateEditor JS filters. See docs/features/validation-api.md for the full integration guide, including the filter signatures and worked PHP + JS examples for each scope.
Severity overrides are stored in the validation_api_settings option, keyed by check id (scope, namespace, target, and check name — so the same check name registered against two block types is configured independently). Checks registered with 'configurable' => false are omitted from the table and cannot be overridden.
The settings screen is built on @wordpress/dataviews. WordPress registers no wp-dataviews script or style handle, so the package, its stylesheet, and the @wordpress/theme design tokens it depends on all ship inside the plugin bundle — build/validation-settings.js is roughly 520 KB, loaded on that one admin screen only.
Import it from the package root, not the @wordpress/dataviews/wp subpath. The subpath inlines its own copy of @wordpress/components, which alone accounted for two thirds of a 1.9 MB bundle; the root entry lets wp-components, wp-compose and wp-private-apis resolve to the copies WordPress already loads. That does mean the screen depends on core's private-apis allowlist continuing to include @wordpress/dataviews, so src/validation-settings/index.tsx loads the app inside a try/catch and renders a message instead of a blank page if that ever stops being true.
npm install
composer install # future — no PHP deps yet
npm run build # multi-entry build to build/*.jsThen symlink the folder into wp-content/plugins/ (or point wp-env at it) and activate.
add_action( 'accessibility_lab_register_modules', function ( \AccessibilityLab\Registry $registry ) {
$registry->register( new My_A11y_Module() );
} );Your class extends AccessibilityLab\Abstracts\Abstract_Module and declares:
bucket()—Bucket::FEATUREorBucket::EXPERIMENTtrack()—Track::CORE_TRACKorTrack::PRACTICAL(defaults to Practical)- optional
credits()— attach aCreditsobject when adopting from an existing plugin
add_action( 'init', function () {
if ( ! function_exists( 'validation_api_register_block_check' ) ) {
return; // Framework module not active.
}
// Name your plugin once. Every check under this namespace is credited to
// it on the settings page.
validation_api_register_namespace( 'my-plugin', [
'title' => 'My Plugin',
] );
validation_api_register_block_check( 'my-plugin/my-block', [
'namespace' => 'my-plugin',
'name' => 'has_title',
'title' => 'Block title required',
'level' => 'error',
'description' => 'This block must have a title.',
'error_msg' => 'Title is required.',
'warning_msg' => 'Consider adding a title.',
] );
} );And the matching JS filter in your editor bundle:
import { addFilter } from '@wordpress/hooks';
addFilter(
'editor.validateBlock',
'my-plugin/title-check',
( isValid, blockName, attributes, checkName ) => {
if ( blockName !== 'my-plugin/my-block' ) return isValid;
if ( checkName === 'has_title' ) return !! attributes.title?.trim();
return isValid;
}
);That's the block-attribute case. docs/features/validation-api.md covers the rest: the full argument reference, the signatures for all three filters, and worked examples for inner block structure, post meta, and editor-scope checks.
accessibility-lab.php Plugin bootstrap
uninstall.php Removes options and calls each module's on_uninstall()
includes/
autoload.php PSR-4 autoloader for AccessibilityLab\*
Plugin.php Singleton bootstrapper
Bucket.php FEATURE | EXPERIMENT constants
Track.php CORE_TRACK | PRACTICAL constants
Credits.php Optional attribution value object
Registry.php Module registry + settings persistence
Abstracts/Abstract_Module.php
Admin/Settings_Page.php Registers Settings → Accessibility Lab
REST/Modules_Controller.php /accessibility-lab/v1/modules
Modules/
Features/
Media_Library_Infinite_Scroll_Optout.php
Core_Block_Validation_Rules.php
CoreBlockRules/... (per-block PHP registration in one file per group)
Validation_Settings.php
ValidationSettings/
Admin_Pages.php Registers the Validation admin page
Level_Override.php Hooks validation_api_check_level
Rest_Controller.php /accessibility-lab/v1/validation-settings
Experiments/
Heading_Order.php
Block_Validation_Framework.php
BlockValidation/
Check_Registry.php In-memory check store
Check_Key.php Stable check id / override key
Global_Functions.php validation_api_register_* globals
Rest_Controller.php /wp-validation/v1/checks
src/
settings/index.tsx Main lab settings React app
editor/framework/ Validation runtime (store, sidebar, publish lock)
editor/core-block-rules/ Per-block JS validators
validation-settings/ DataViews admin app for severity overrides
webpack.config.js Multi-entry build config
The block validation subsystem is a port of Troy Chaplin's validation plugins:
- validation-api — framework
- validation-api-core-blocks — core block rules
- validation-api-settings — admin UI
Additional rules (alt-length warning, alt/caption match, non-descriptive alt patterns, gallery inheritance, required post/page title) are adapted from block-accessibility-checks.