diff --git a/README.md b/README.md index 89f930a..38f5c13 100644 --- a/README.md +++ b/README.md @@ -39,7 +39,7 @@ Two words do a lot of work in this subsystem, so to be precise: **validation** n Three of the first-party modules compose into a full block-editor validation subsystem: 1. **Block Validation Framework** — a plugin API (`validation_api_register_block_check()`, `validation_api_register_meta_check()`, `validation_api_register_editor_check()`), an `accessibility-lab/validation` data store, real-time debounced validation, publish locking, a Validation sidebar, and REST introspection at `GET /wp-validation/v1/checks`. -2. **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. +2. **Core block accessibility rules** — WCAG-oriented checks for `core/image`, `core/video`, `core/button`, `core/table`, `core/heading`, `core/gallery`, plus a required post/page title editor check. 3. **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](docs/features/validation-api.md)** for the full integration guide, including the filter signatures and worked PHP + JS examples for each scope. diff --git a/docs/features/validation-api.md b/docs/features/validation-api.md index 6919064..d83a1f5 100644 --- a/docs/features/validation-api.md +++ b/docs/features/validation-api.md @@ -46,7 +46,7 @@ The plugin declares a starting severity; a site administrator can override it pe The subsystem is three separately toggleable modules under **Settings → Accessibility Lab**: 1. **Block Validation Framework** — the registration API, the `accessibility-lab/validation` data store, the editor runtime, the Validation sidebar, publish locking, and REST introspection. Everything else depends on it. -2. **Core block accessibility rules** — WCAG checks for `core/image`, `core/button`, `core/table`, `core/heading` and `core/gallery`, plus required post and page titles. An optional consumer of the framework, and a working reference. +2. **Core block accessibility rules** — WCAG checks for `core/image`, `core/video`, `core/button`, `core/table`, `core/heading` and `core/gallery`, plus required post and page titles. An optional consumer of the framework, and a working reference. 3. **Validation settings** — the admin screen for adjusting severities. Third-party checks need only the framework. If it's disabled, the `validation_api_register_*` functions don't exist, which is why every example below guards with `function_exists()`. diff --git a/includes/Modules/Features/Core_Block_Validation_Rules.php b/includes/Modules/Features/Core_Block_Validation_Rules.php index bb23b35..0fee887 100644 --- a/includes/Modules/Features/Core_Block_Validation_Rules.php +++ b/includes/Modules/Features/Core_Block_Validation_Rules.php @@ -71,7 +71,7 @@ public function name(): string { * Returns the description for the core block accessibility rules module. */ public function description(): string { - return __( 'WCAG-oriented validation checks for the image, button, table, heading, and gallery core blocks, plus required post/page titles. Requires the Block Validation Framework module.', 'accessibility-lab' ); + return __( 'WCAG-oriented validation checks for the image, video, button, table, heading, and gallery core blocks, plus required post/page titles. Requires the Block Validation Framework module.', 'accessibility-lab' ); } /** @@ -141,6 +141,7 @@ public function register_checks(): void { ); $this->register_image_checks(); + $this->register_video_checks(); $this->register_button_checks(); $this->register_table_checks(); $this->register_heading_checks(); @@ -253,6 +254,24 @@ private function register_button_checks(): void { ); } + /** + * Registers video block accessibility checks. + */ + private function register_video_checks(): void { + validation_api_register_block_check( + 'core/video', + array( + 'namespace' => self::NS, + 'name' => 'check_video_tracks', + 'title' => __( 'Video captions or subtitles recommended', 'accessibility-lab' ), + 'level' => 'warning', + 'description' => __( 'Videos should provide captions or subtitles for deaf or hard-of-hearing users (WCAG 1.2.2).', 'accessibility-lab' ), + 'error_msg' => __( 'This video is missing captions or subtitle tracks.', 'accessibility-lab' ), + 'warning_msg' => __( 'Consider adding captions or subtitles to this video.', 'accessibility-lab' ), + ) + ); + } + /** * Registers table block accessibility checks. */ diff --git a/readme.txt b/readme.txt index 366d001..2f46671 100644 --- a/readme.txt +++ b/readme.txt @@ -24,7 +24,7 @@ Included modules: * Media Library: add view options — in-modal popover for infinite scroll, thumbnail density, always-show file names, and items-per-page. Per-user preferences. * Block Validation Framework — third-party plugins can register real-time validation checks for blocks, post meta, and editor-level document concerns. -* Core block accessibility rules — WCAG-oriented validation for the image, button, table, heading, and gallery core blocks, plus required post/page titles. +* Core block accessibility rules — WCAG-oriented validation for the image, video, button, table, heading, and gallery core blocks, plus required post/page titles. * Validation settings — a Validation admin page listing every registered validation check in a filterable, searchable table, where each check's severity can be set to Error, Warning, or Disabled. == Installation == diff --git a/src/editor/core-block-rules/index.ts b/src/editor/core-block-rules/index.ts index aab7fcb..a66a698 100644 --- a/src/editor/core-block-rules/index.ts +++ b/src/editor/core-block-rules/index.ts @@ -10,4 +10,5 @@ import './button'; import './table'; import './heading'; import './gallery'; +import './video'; import './post-title'; diff --git a/src/editor/core-block-rules/video.ts b/src/editor/core-block-rules/video.ts new file mode 100644 index 0000000..bdb1afa --- /dev/null +++ b/src/editor/core-block-rules/video.ts @@ -0,0 +1,72 @@ +/** + * core/video validation logic. + */ + +import { addFilter } from '@wordpress/hooks'; + +type Track = { + src?: string; + kind?: string; + label?: string; + srcLang?: string; +}; + +type VideoAttributes = { + id?: number; + src?: string; + autoplay?: boolean; + controls?: boolean; + loop?: boolean; + muted?: boolean; + tracks?: Track[]; +}; + +addFilter( + 'editor.validateBlock', + 'accessibility-lab-core-blocks/video', + ( + isValid: boolean, + blockType: string, + attributes: VideoAttributes, + checkName: string + ) => { + if ( blockType !== 'core/video' ) { + return isValid; + } + + const hasVideo = Boolean( attributes.src || attributes.id ); + + switch ( checkName ) { + case 'check_video_tracks': { + // If no video file is attached yet, do not trigger unconfigured warning. + if ( ! hasVideo ) { + return true; + } + // Muted, looping, autoplaying video without controls is a + // background/decorative clip with no audio to caption. + const isBackground = + attributes.autoplay && + attributes.muted && + attributes.loop && + attributes.controls === false; + if ( isBackground ) { + return true; + } + const tracks = Array.isArray( attributes.tracks ) + ? attributes.tracks + : []; + return tracks.some( + ( track ) => + Boolean( track?.src ) && + // A without `kind` defaults to subtitles. + [ 'captions', 'subtitles', undefined ].includes( + track?.kind + ) + ); + } + + default: + return isValid; + } + } +);