Editor components updates in WordPress 7.1

40px default size for form controls

Starting in WordPress 7.1, @wordpress/components form controls use a 40px default height unconditionally. The opt-in __next40pxDefaultSize prop is no longer needed and has no runtime effect when passed.

This completes the rollout that followed the soft deprecation in WordPress 6.8. The prop was introduced in WordPress 6.7 so plugins could opt in early. Since 6.8, components that had not opted in logged a console warning.

What changed

  • Affected components now render at 40px by default without the prop.
  • Passing __next40pxDefaultSize is ignored at runtime.
  • Passing __next40pxDefaultSize={ false } no longer opts out to the previous 36px height.
  • On BorderBoxControl, BorderControl, FontSizePicker, and ToggleGroupControl, the size prop is also deprecated and has no effect.

What to do

Remove __next40pxDefaultSize from your component usage. No replacement prop is needed.

If you were passing size="__unstable-large" on the components listed only to get 40px height, remove that as well.

Affected components

For links to the code changes, see tracking issue #65751.

@wordpress/components

BorderBoxControl, BorderControl, BoxControl, ComboboxControl, CustomSelectControl, FontSizePicker, FormFileUpload, FormTokenField, FocalPointPicker, InputControl, NumberControl, QueryControls, Radio, RangeControl, SearchControl, SelectControl, TextControl, ToggleGroupControl, TreeSelect, UnitControl

@wordpress/block-editor

FontAppearanceControl, FontFamilyControl, LetterSpacingControl, LineHeightControl

Not included

This rollout covers form controls only. Button still uses the opt-in prop and is unchanged.


Changes for consumers styling with Emotion

A long-running migrationMigration Moving the code, database and media files for a website site from one server to another. Most typically done when changing hosting companies. has kickstarted in the @wordpress/components package, with the goal of refactoring all Emotion-based styles to SCSS modules.

Most consumers should not need to change anything, but if you do use Emotion to style your components, there are two migration details for code that relied on Emotion-specific behavior:

  • View still accepts the legacy css prop for type compatibility, but it is now a no-op. Use style for inline styles or className for CSSCSS Cascading Style Sheets.-based styling.
  • When using cx() with Emotion css() fragments, compose source-order-dependent fragments into a single css() call before passing them to cx(). Passing separate fragments can change override order now that View no longer renders through Emotion.

Example:

const classes = cx(
	css(
		baseStyles,
		condition && overrideStyles
	),
	className
);

This keeps shorthand/longhand overrides and nested-selector overrides in one generated class, preserving the intended cascade order.

The affected components are:

  • Divider
  • Surface
  • Truncate
  • View
  • Flex
  • Spacer

The list is expected to grow as the migration continues. Follow #66806 for more details.


Remove Navigation

Starting in WordPress 7.1, the deprecated Navigation component and its subcomponents are removed from @wordpress/components (#78529).

The component has been deprecated since WordPress 6.8. Use the Navigator component instead.


Remove __experimentalApplyValueToSides

Starting in WordPress 7.1, the __experimentalApplyValueToSides utility is removed from @wordpress/components (#78528).

The utility has been deprecated since WordPress 6.8. BoxControl itself is unaffected.


Co-authored by @0mirka00 and @mciampini.

Props to @aduth for review.

#7-1, #dev-notes, #dev-notes-7-1

Editable blocks inside the Custom HTML block

In WordPress 7.1, the Custom HTMLHTML HyperText Markup Language. The semantic scripting language primarily used for outputting content in web browsers. blockBlock Block is the abstract term used to describe units of markup that, composed together, form the content or layout of a webpage using the WordPress editor. The idea combines concepts of what in the past may have achieved with shortcodes, custom HTML, and embed discovery into a single consistent API and user experience. supports interleaving static HTML with regular, editable blocks (79115):

<!-- wp:html -->
<div class="banner"><h1>Static heading</h1><!-- wp:paragraph -->
<p>Editable paragraph</p>
<!-- /wp:paragraph --><footer>Static footer</footer></div>
<!-- /wp:html -->

In the editor, the static markup renders inert while the inner blocks are editable in place — but locked: they can’t be moved, removed, or have siblings added. The surrounding structure stays intact. The full markup remains accessible in the “Edit HTML” modal, and serialization round-trips unchanged, so existing content is unaffected.

This also makes block markup a friendlier target for AI tools: a model can generate one Custom HTML block mixing arbitrary markup with editable slots — no custom block, no build step — and the output is immediately safe to edit.

Registering variations as “higher level blocks”

Block variations now accept an innerContent field (79659): an array of static HTML fragments where each null marks the position of the corresponding innerBlocks entry. This lets you ship a fixed markup shell with editable slots as its own inserter item — no custom block needed:

wp.blocks.registerBlockVariation( 'core/html', {
  name: 'testimonial-card',
  title: 'Testimonial Card',
  icon: 'format-quote',
  innerContent: [ '<div class="testimonial-card">', null, '</div>' ],
  innerBlocks: [
    [ 'core/paragraph', { content: 'An inspiring quote.' } ],
  ],
} );

Inserting the variation produces a Custom HTML block with the preset structure and an editable paragraph inside it. Unlike a pattern, the user can edit only the designated slots, not the structure.

innerContent only applies to core/html variations; it is ignored elsewhere.

#7-1, #blocks, #dev-notes, #dev-notes-7-1

Media Library infinite scrolling is now enabled by default, with a per-user opt-out

Background

The grid view of the Media Library (including the Media Modal) has supported infinite scrolling for a long time, where attachments load automatically as you scroll instead of behind a Load more button. That behavior was controlled by the media_library_infinite_scrolling filterFilter Filters are one of the two types of Hooks https://codex.wordpress.org/Plugin_API/Hooks. They provide a way for functions to modify data of other functions. They are the counterpart to Actions. Unlike Actions, filters are meant to work in an isolated manner, and should never have side effects such as affecting global variables and output., which defaulted to false since WordPress 5.8 due to accessibilityAccessibility Accessibility (commonly shortened to a11y) refers to the design of products, devices, services, or environments for people with disabilities. The concept of accessible design ensures both “direct access” (i.e. unassisted) and “indirect access” meaning compatibility with a person’s assistive technology (for example, computer screen readers). (https://en.wikipedia.org/wiki/Accessibility), performance, and usability concerns (#50105 / r50829 / #40330). As a result, infinite scrolling was effectively off for everyone unless a pluginPlugin A plugin is a piece of software containing a group of functions that can be added to a WordPress website. They can extend functionality or add new features to your WordPress websites. WordPress plugins are written in the PHP programming language and integrate seamlessly with WordPress. These can be free in the WordPress.org Plugin Directory https://wordpress.org/plugins/ or can be cost-based plugin from a third-party. or theme opted in via the filter.

The Load more button has long been a friction point for users managing large media libraries, and the smoother infinite-scroll experience was hidden behind code that most sites never enabled.

What changed

In WordPress 7.1 (#65564):

  1. Infinite scrolling is now enabled by default. The media_library_infinite_scrolling filter now defaults to true, so the grid view auto-loads attachments on scroll out of the box. This applies to both the Grid view of the Media Library, and the Media Modal.
  2. Users can opt out individually. A new Infinite Scrolling personal option appears on the profile screen (Users > Profile), with a checkbox labeled “Disable infinite scrolling in the Media Library grid view” (unchecked by default). The option is only shown to users who have the upload_files capabilitycapability capability is permission to perform one or more types of task. Checking if a user has a capability is performed by the current_user_can function. Each user of a WordPress site might have some permissions but not others, depending on their role. For example, users who have the Author role usually have permission to edit their own posts (the “edit_posts” capability), but not permission to edit other users’ posts (the “edit_others_posts” capability)., since the attachment grid is unreachable without it.

Precedence

The effective setting is resolved in this order, from highest priority to lowest:

  1. The media_library_infinite_scrolling filter: a hooked filter callback always wins.
  2. The user’s opt-out preference: applies when no filter is hooked.
  3. The default (true): applies when neither of the above is set.

In other words: filter > user preference > default.

Developer-facing details

The filter’s default changed

If your plugin or theme relies on the previous behavior (infinite scrolling off unless explicitly enabled), be aware that the default is now true. To force the previous behavior for all users regardless of their preference, you can use the filter, as it takes precedence over everything:

// Force infinite scrolling OFF for all users (restores to the behavior that was default between 5.8 and 7.1).

add_filter( 'media_library_infinite_scrolling', '__return_false' );

// Force infinite scrolling ON for all users, ignoring per-user opt-out.

add_filter( 'media_library_infinite_scrolling', '__return_true' );

Because the filter runs after the per-user preference is read, adding a callback overrides any individual user’s choice.

The new user option

The preference is stored as a user option under the metaMeta Meta is a term that refers to the inside workings of a group. For us, this is the team that works on internal WordPress sites like WordCamp Central and Make WordPress. key infinite_scrolling, as the string 'true' or 'false', consistent with how existing options such as syntax_highlighting and rich_editing are persisted. It is also wired consistently through the profile form (user-edit.php), the save handler (edit_user()), and persistence in wp_insert_user() and _get_additional_user_keys().

You can read a user’s preference programmatically:

// The user setting is 'false' if the user has disabled infinite scrolling, 'true' (or empty) otherwise.

$infinite_scrolling_disabled = 'false' === get_user_option( 'infinite_scrolling', $user_id );

Note the direction of the stored value: 'false' means the user has disabled infinite scrolling. wp_enqueue_media() reads this option to determine the pre-filter default.

Backwards compatibility

  • The media_library_infinite_scrolling filter is unchanged in signature; only its default value changed (from false to true). Existing callbacks continue to work exactly as before and still take precedence.
  • Sites that had already opted in via __return_true see no change.
  • Sites that never touched the filter now get infinite scrolling by default; users who prefer the Load more button can opt out from their profile.

Props to @youknowriad, @khokansardar, @wildworks, @davidbaumwald, @joedolson, @sabernhardt for reviewing.

#7-1, #dev-notes, #dev-notes-7-1, #media, #media-library, #media-grid

Text Shadow Support in Global Styles

WordPress 7.1 introduces the ability to define a text-shadow value in Global Styles through theme.json. Themes can now set a text shadow globally, on specific blocks, and on elements such as links, without a pluginPlugin A plugin is a piece of software containing a group of functions that can be added to a WordPress website. They can extend functionality or add new features to your WordPress websites. WordPress plugins are written in the PHP programming language and integrate seamlessly with WordPress. These can be free in the WordPress.org Plugin Directory https://wordpress.org/plugins/ or can be cost-based plugin from a third-party. or custom stylesheet.

GitHubGitHub GitHub is a website that offers online implementation of git repositories that can easily be shared, copied and modified by other developers. Public repositories are free to host, private repositories require a paid subscription. GitHub introduced the concept of the ‘pull request’ where code changes done in branches by contributors can be reviewed and discussed before being merged by the repository owner. https://github.com/ issue: #47904 | PR: #73320

This is the first step of the text shadow feature. It covers theme.json styling only. A user interface, presets, and per-blockBlock Block is the abstract term used to describe units of markup that, composed together, form the content or layout of a webpage using the WordPress editor. The idea combines concepts of what in the past may have achieved with shortcodes, custom HTML, and embed discovery into a single consistent API and user experience.-instance controls arrive in the next release. See the “What is not included yet” section below.

Background

The CSSCSS Cascading Style Sheets. text-shadow property has been a frequently requested typography feature (see #47904). Until now, applying a text shadow through the block system was not possible. Theme authors had to add their own CSS to style text shadows, which kept the value outside of theme.json and Global Styles.

Fully implementing text shadow raises questions that still need discussion, such as the right control for editing shadows and whether shadows should be stored as presets. To make progress without waiting on those decisions, this release adds the smallest useful piece: the ability to declare a text-shadow value directly in theme.json.

What changed

A textShadow property is now recognized under styles.typography in theme.json. The value maps directly to the CSS text-shadow property, so any valid text-shadow value works, including multiple comma-separated shadows.

The property is supported in the same places as other typography styles:

  • Global typography (styles.typography)
  • Per-block styles (styles.blocks.<block>.typography)
  • Element styles, including states such as :hover (styles.elements.<element>)

There is no block inspector control and no Global Styles interface for this in this release. The value is set in theme.json only.

How to use it

Set textShadow under styles.typography to apply a shadow to all text. The example below applies a global shadow, overrides it for the Paragraph block, and removes the shadow from links on hover.

{
	"$schema": "https://schemas.wp.org/trunk/theme.json",
	"version": 3,
	"styles": {
		"typography": {
			"textShadow": "1px 1px 2px red, 0 0 1em blue, 0 0 0.2em blue"
		},
		"blocks": {
			"core/paragraph": {
				"typography": {
					"textShadow": "1px 1px 2px red, 0 0 1em red, 0 0 0.2em red"
				}
			}
		},
		"elements": {
			"link": {
				":hover": {
					"typography": {
						"textShadow": "none"
					}
				}
			}
		}
	}
}

Block-level values override the global value, following the same cascade as other typography styles.

Editor behavior

When a global text shadow is set, the shadow is removed from the empty rich text placeholder (for example the “Type / to choose a block” prompt). Without this reset, the placeholder text can become hard to read. The reset applies to the placeholder only. Actual content still renders with the configured shadow in both the editor and on the front end.

What is not included yet

This release covers theme.json styling only. The following are planned for the next release in #79584:

  • A text shadow control in the block inspector, so a shadow can be set on an individual block instance.
  • A Global Styles interface for browsing, creating, and editing text shadow presets.
  • Text shadow presets in theme.json (settings.typography.textShadow, textShadowPresets, and defaultTextShadowPresets), each output as a var(--wp--preset--text-shadow--{slug}) custom property.
  • A new supports.typography.textShadow block support, enabled first on the Paragraph and Heading blocks.

Until then, text shadow can be configured through theme.json styles as shown above.

Backwards compatibility

These are additive changes. No existing blocks or themes are affected, and no action is required. Themes that do not set textShadow behave exactly as before.

Further Reading

Props to @wildworks for reviewing this post.

#dev-notes, #dev-notes-7-1

Client-Side Media Processing in WordPress 7.1

WordPress 7.1 ships client-side media processing – a capabilitycapability capability is permission to perform one or more types of task. Checking if a user has a capability is performed by the current_user_can function. Each user of a WordPress site might have some permissions but not others, depending on their role. For example, users who have the Author role usually have permission to edit their own posts (the “edit_posts” capability), but not permission to edit other users’ posts (the “edit_others_posts” capability). that handles image compression, resizing, format conversion, rotation, and thumbnail generation directly in the user’s browser using WebAssembly, rather than on the server. The feature is enabled by default in supporting browsers.

This post outlines what’s changing, how it works, and what pluginPlugin A plugin is a piece of software containing a group of functions that can be added to a WordPress website. They can extend functionality or add new features to your WordPress websites. WordPress plugins are written in the PHP programming language and integrate seamlessly with WordPress. These can be free in the WordPress.org Plugin Directory https://wordpress.org/plugins/ or can be cost-based plugin from a third-party. and theme developers need to know.

What is client-side media processing?

Traditionally, when a user uploads an image in the blockBlock Block is the abstract term used to describe units of markup that, composed together, form the content or layout of a webpage using the WordPress editor. The idea combines concepts of what in the past may have achieved with shortcodes, custom HTML, and embed discovery into a single consistent API and user experience. editor, the file is sent to the server where PHPPHP The web scripting language in which WordPress is primarily architected. WordPress requires PHP 7.4 or higher (using GD or Imagick) generates thumbnails (various image sizes for the front end), applies format conversions, handles EXIF rotation, and scales large images. This approach is limited by PHP memory constraints, server CPU availability, and the capabilitiescapability capability is permission to perform one or more types of task. Checking if a user has a capability is performed by the current_user_can function. Each user of a WordPress site might have some permissions but not others, depending on their role. For example, users who have the Author role usually have permission to edit their own posts (the “edit_posts” capability), but not permission to edit other users’ posts (the “edit_others_posts” capability). of the server’s installed image library.

Client-side media processing moves this work to the browser. Images are processed using wasm-vips, a WebAssembly compilation of the high-performance libvips image processing library. The processed images – including all thumbnails – are then uploaded to the server, which stores them. After all client-side operations complete, a finalize step applies the wp_generate_attachment_metadata filterFilter Filters are one of the two types of Hooks https://codex.wordpress.org/Plugin_API/Hooks. They provide a way for functions to modify data of other functions. They are the counterpart to Actions. Unlike Actions, filters are meant to work in an isolated manner, and should never have side effects such as affecting global variables and output. with context 'update' so plugins see the full sub-sizes metadata. This mirrors how the server already handlesimage uploads, where sub-sizes trigger the same 'update' pass.

Key benefits

  • Consistent, high-quality output with modern image support. All users get the same libvips powered processing regardless of whether the server has GD or Imagick, and regardless of which version is installed.
  • Faster downloads for visitors. libvips produces better-compressed output than GD or Imagick (JPEGs are reduced ~15% with MozJPEG like encoding), so the generated images served to site visitors are smaller and load faster.
  • No more PHP memory limit failures. Large image processing that would exceed PHP’s memory limit now succeeds because it runs in the browser’s memory space.
  • Reduced server load. Image processing is offloaded to the user’s device, freeing server CPU and memory for other tasks.
  • iPhone photos just work. HEIC images can be decoded in the browser and converted to JPEG before upload, even on hosts without server-side HEIC support. Note: HEIC decode relies on platform codecs and is supported in Chromium browsers (Chrome, Edge, Brave) on macOS and on Windows with HEVC support, and in Safari on macOS. The full WASM pipeline (everything beyond HEIC) is Chromium-only – see Browser compatibility and fallback below.
  • AVIF without server-side AVIF support. Hosts whose PHP image editor doesn’t support AVIF can still accept AVIF uploads when client-side processing is active. The MIME-type check is bypassed for client-decoded uploads – see the security FAQ below for details.
  • Animated GIFs become efficient video. Opaque animated GIFs can be converted in the browser to a companion MP4/WebM video that plays exactly like the original GIF, dramatically cutting the bytes visitors download, with no loss of the autoplay-loopLoop The Loop is PHP code used by WordPress to display posts. Using The Loop, WordPress processes each post to be displayed on the current page, and formats it according to how it matches specified criteria within The Loop tags. Any HTML or PHP code in the Loop will be processed on each post. https://codex.wordpress.org/The_Loop GIF feel.
  • More resilient uploads. Sub-size uploads are independent requests, so a networknetwork (versus site, blog) hiccup mid-upload doesn’t lose the entire batch. Failed requests are retried automatically with exponential backoff, so transient network errors recover without user intervention. Uploads are paused if you go offline and resume when you come back online.

What’s included

  • Browser-based image processing – Compression, resizing, cropping, format conversion (JPEG, PNG, WebP, AVIF, GIF), EXIF rotation, and progressive/interlaced encoding via WebAssembly in a Web Worker.
  • Thumbnail generation in the browser – All registered image sub-sizes are generated client-side and uploaded individually via a new sideload REST APIREST API The REST API is an acronym for the RESTful Application Program Interface (API) that uses HTTP requests to GET, PUT, POST and DELETE data. It is how the front end of an application (think “phone app” or “website”) can communicate with the data store (think “database” or “file system”) https://developer.wordpress.org/rest-api/ endpoint. Sizes that share dimensions with built-in sizes (e.g. Twenty Eleven’s large matches medium_large) are deduplicated to a single physical file registered under all matching size names.
  • HEIC/HEIF support – iPhone photos (image/heicimage/heif) are decoded in the browser and uploaded as web-ready JPEG. The original HEIC is kept as a companion file (source_image) and removed when the attachment is deleted.
  • AVIF end-to-end uploads – AVIF can be decoded client-side, no longer requiring server-side AVIF support. High-bit-depth AVIF sources (10- or 12-bit, common for HDR photos) keep their bit depth in generated sub-sizes.
  • Gain Map HDR support – UltraHDR JPEGs embed a gain map alongside a standard SDR base image: a new, backwards-compatible way of adding HDR data to SDR images that is supported by Google (UltraHDR), Apple (Adaptive HDR), and Adobe (Camera Raw, Lightroom, and Photoshop). These files are detected on upload and preserved end-to-end: the original uploads unmodified, and every generated sub-size keeps its gain map, so thumbnails stay HDR. Format conversion via image_editor_output_format is intentionally skipped for these files, since converting to a different codec would strip the gain map.
  • Automatic format conversion – The existing image_editor_output_format filter is respected client-side, enabling automatic conversion (e.g., JPEG to WebP) during processing.
  • Animated GIF to video conversion – Opaque animated GIFs are converted in the browser to an MP4 (or WebM) using the native WebCodecs APIs and the mediabunny library. The GIF stays a single image/gif attachment; the converted video and a first-frame poster are sideloaded as companion files (media_details.animated_video / animated_video_poster). In the editor the block is optionally switched to a “GIF” variation of the Video block that autoplays, loops, and is muted – playing just like the original GIF – and the front end renders a native <video>. The swap is fully reversible via the block transform menu, transparent GIFs are left as images, and browsers without WebCodecs video encoding (e.g. Firefox) upload the original GIF unchanged.
  • A cross-origin-isolated editor – To run the WASM pipeline, the editor needs SharedArrayBuffer, which browsers only expose to cross-origin-isolated documents. WordPress enables this with Document-Isolation-Policy: isolate-and-credentialless on block editor screens for Chromium 137+. Beyond media processing, this means SharedArrayBuffer and high-resolution timers are now available to any code running in the editor, so plugins can build their own multithreaded or WASM-backed features there. Because DIP is per-document, it provides this isolation without imposing the page-wide constraints of COOP/COEP. See Cross-origin isolation impact below for what extenders should watch for.
  • Server-side hook compatibility – wp_generate_attachment_metadata fires the same way as for a server-side upload: once with context 'create' during the initial upload and again with 'update' after POST /wp/v2/media/{id}/finalize runs. Plugins that hook into it (watermarking, CDN sync, etc.) continue to work, the same way they already handle the deferred-subsize pass on big-image uploads.
  • Upload progress feedback – A snackbar in the editor tracks batch upload progress, with a spinner while uploads run and a brief checkmark on completion. It works on both the client-side and server-side upload paths, and announces start and completion via wp.a11y.speak() for screen reader users.
  • Smart fallback – Browsers that don’t support the required features automatically fall back to server-side processing with no user-facing change.
  • Image quality filters honored – The standard wp_editor_set_quality and jpeg_quality filters flow through to client-side sub-size generation via a size-aware image_quality field in the upload response, so existing quality-tuning code works unchanged.
  • Server-side import of external images – The Image block’s “Upload to Media Library” action and the pre-publish “External media” panel now send the image URLURL A specific web address of a website or web page on the Internet, such as a website’s URL www.wordpress.org to the server, which downloads and sideloads it – avoiding browser CORS failures entirely (the old client-side fetch also could not work in the cross-origin-isolated editor).

Technical overview

  • @wordpress/upload-media – Manages the upload queue, concurrency (max 5 uploads, max 2 image processing operations), and orchestrates the pipeline.
  • @wordpress/vips – Wraps wasm-vips in a Web Worker for non-blocking image processing. The WASM bundle is loaded lazily on first use and bundles vips.wasm and vips-heif.wasm (the latter is needed for AVIF decoding).
  • @wordpress/media-utils – Handles HTTPHTTP HTTP is an acronym for Hyper Text Transfer Protocol. HTTP is the underlying protocol used by the World Wide Web and this protocol defines how messages are formatted and transmitted, and what actions Web servers and browsers should take in response to various commands. transport to the WordPress REST API.
  • @wordpress/video-conversion – Wraps the mediabunny library in a Web Worker (mirroring the @wordpress/vips pattern) to convert animated GIFs to MP4/WebM off the main thread, gated on WebCodecs ImageDecoder/VideoEncoder availability and run with a concurrency limit of 1.

On the PHP side:

  • wp_is_client_side_media_processing_enabled() – Feature gate, filterable via wp_client_side_media_processing_enabled.
  • Cross-origin isolation – wp_start_cross_origin_isolation_output_buffer() sends Document-Isolation-Policy on load-post.phpload-post-new.phpload-site-editor.php, and load-widgets.php for Chromium 137+. Only active when client side media is enabled.
  • REST API extensions – New generate_sub_sizes and convert_format parameters, sideload endpoint (POST /wp/v2/media/{id}/sideload), finalize endpoint (POST /wp/v2/media/{id}/finalize), replace_file flag for HEIC companion uploads, and new response fields (exif_orientationmissing_image_sizesfilenamefilesize).

For the full architecture deep-dive, see the client-side media processing architecture documentation.

What plugin developers need to know

Disabling client-side processing

If your plugin needs to disable client-side media processing, use the wp_client_side_media_processing_enabled filter:

add_filter( 'wp_client_side_media_processing_enabled', '__return_false' );

Server-side hooksHooks In WordPress theme and development, hooks are functions that can be applied to an action or a Filter in WordPress. Actions are functions performed when a certain event occurs in WordPress. Filters allow you to modify certain functions. Arguments used to hook both filters and actions look the same. still fire

A common concern: if client-side processing bypasses server-side image generation, do plugins that hook into wp_generate_attachment_metadata stop working? No – the filter fires the same way it does during a server-side upload, just with the work shifted around. WordPress fires it once with context 'create' during the initial upload (before the sub-sizes are created), and again with 'update' after the finalize endpoint runs (once all client-side sub-size sideloads are complete). Plugins for watermarking, CDN sync, custom metadata processing, and similar use cases continue to work without modification – write them idempotently so they handle both passes correctly. This double-fire pattern matches how WordPress already handles big-image uploads on the server, where sub-size generation is deferred and triggers a second 'update' pass.

If finalize fails, the error is logged but the upload still succeeds – the call is best-effort so a plugin failure can’t block the user’s upload.

Existing filters still work

Client-side processing reads settings from the server and respects:

  • big_image_size_threshold – Maximum image dimension before scaling.
  • image_editor_output_format – Automatic format conversion.
  • image_save_progressive – Progressive/interlaced encoding.
  • wp_image_maybe_exif_rotate – EXIF rotation.
  • wp_editor_set_quality (and jpeg_quality for JPEG output) – Encode quality, resolved per registered size.

There is no client_side_supported_mime_types filter; the supported set (image/jpegimage/pngimage/gifimage/webpimage/avif) is fixed at CLIENT_SIDE_SUPPORTED_MIME_TYPES.

Controlling image quality

Client-side encoding honors the same PHP filters that control server-side quality: wp_editor_set_quality and, for JPEG output, jpeg_quality. The server resolves the filters per registered size and reports the result in the upload response’s size-aware image_quality field, which the client applies during sub-size resize and transcode. The same code that tunes server-side quality works unchanged:

/*
 * Size-aware: drop JPEG thumbnails (300px wide or less) to quality 60,
 * leave larger sizes untouched.
 */
add_filter(
	'wp_editor_set_quality',
	function ( $quality, $mime_type, $size ) {
		if ( 'image/jpeg' === $mime_type && isset( $size['width'] ) && $size['width'] <= 300 ) {
			return 60;
		}
		return $quality;
	},
	10,
	3
);

When the server doesn’t report the field, the client falls back to a default of 0.82.

Cross-origin isolation impact

When client-side media is active, WordPress sends Document-Isolation-Policy: isolate-and-credentialless on block editor screens for Chromium 137+. Since DIP is per-document, it doesn’t impose the page-wide constraints of COEP/COOP. Notable behavior:

  • External scripts loaded across origins automatically get a crossorigin="anonymous" attribute via the server-side wp_add_crossorigin_attributes() output buffer and a client-side MutationObserver. <img> is excluded so external image previews aren’t affected.
  • DIP is skipped on adminadmin (and super admin) pages with an action other than edit, which keeps third-party page builders that rely on same-origin iframeiframe iFrame is an acronym for an inline frame. An iFrame is used inside a webpage to load another HTML document and render it. This HTML document may also contain JavaScript and/or CSS which is loaded at the time when iframe tag is parsed by the user’s browser. access functional.
  • External images are imported server-side. “Upload to Media Library” and the pre-publish “External media” panel POST the image URL to the media endpoint (a new url parameter) and the server downloads and sideloads it. Plugins importing remote media should do the same rather than fetch()ing image bytes in the browser – a cross-origin fetch is subject to CORS and fails in a credentialless isolated document.

Content Security Policy (CSP)

If your plugin sets a Content Security Policy, ensure the worker-src directive includes blob::

Content-Security-Policy: worker-src 'self' blob:;

Without this, the WASM processing worker cannot be created and processing falls back to server-side.

Server specific hooks don’t fire

Because a server side editor is not used, wp_image_editors, image_memory_limit and image_make_intermediate_size never fire. A complete accounting of media hooks before and after this change is available in the handbook.

What theme developers need to know

Client-side media processing is transparent to themes. Existing filters (big_image_size_thresholdimage_editor_output_format, etc.) continue to work without modification. Image sizes registered via add_image_size() are automatically generated client-side, and sizes that share dimensions with built-in sizes are deduplicated to a single physical file.

Browser compatibility and fallback

Client-side processing depends on Document-Isolation-Policy to enable SharedArrayBuffer, which is currently only available in Chromium-based browsers.

BrowserMinimum VersionStatus
Chrome137+Full support via Document-Isolation-Policy
Edge137+Full support via Document-Isolation-Policy
Firefox*Not supported (no Document-Isolation-Policy) – falls back to server-side
Safari*Not supported (no Document-Isolation-Policy) – falls back to server-side. In-browser HEIC decode still works, since it does not require Document-Isolation-Policy.

Chrome and Edge have supported Document-Isolation-Policy since version 137 (released in mid-2025). As of this post’s publication, current stable Chrome and Edge are well past that, so the overwhelming majority of Chromium users already meet the requirement. Chrome on Android supports the feature from version 146. Document-Isolation-Policy is not yet tracked on caniuse; the most reliable place to check current and future browser support is the Chrome Platform Status entry.

  • On unsupported browsers WordPress falls back to server-side processing automatically. Users see no difference in behavior. A plugin is available to enable the client-side media feature in Firefox/Safari using COEP/COOP headers. These are not used by default because they create compatibility issues with embeds and other third party resources.

Feature detection and limitations

Beyond browser support, the client checks several runtime conditions before activating the WASM pipeline. Failing any check causes a transparent fallback to server-side processing – there is no user-facing change.

CheckThresholdWhy
Device memory> 2 GBWASM image processing can OOM on very low-memory devices.
CPU cores≥ 2WASM image processing benefits from at least one coreCore Core is the set of software required to run WordPress. The Core Development Team builds WordPress. for the worker plus one for the UIUI User interface thread.
Networknot 2g/slow-2g, no Save-Data headerThe ~13 MB worker download is gated to faster connections; 3g is allowed.
CSP blob: workersmust succeedThe worker is created from a blob URL; strict worker-src policies block it.

Frequently asked questions

Isn’t this just a bandwidth optimization?

Not exactly. The client uploads the original plus every sub-size, so total bytes over the wire actually go up during uploads compared to the server-side path (which receives only the original). Bandwidth is saved when serving the images since encoding is better and modern images are always supported. For uploads the real win is server CPU and memory relief: hosts no longer pay the GD/Imagick cost of generating sub-sizes on upload, which is one of the most common causes of PHP timeouts and memory-limit failures on shared hosting. See “Key benefits” above.

Doesn’t the “never trust the client” rule apply here?

Client-side processing is a performance optimization, not a trust boundary. The server still validates every uploaded file – MIME type, dimensions, capability checks, sanitization – and runs the same wp_generate_attachment_metadata filter chain. If the browser can’t or won’t process the file, WordPress falls back to server-side processing transparently.

What happens if the browser can’t process the image?

Server-side processing runs as before. The fallback is automatic and transparent to the user – no UI change, no error. The exact gating (browser features, device memory, CPU cores, network class, CSP) is described in “Feature detection and limitations” above.

Will my plugin’s wp_generate_attachment_metadata hooks still run?

Yes. The filter fires the same way as during a server-side upload: once with context 'create' during the initial upload, and again with 'update' after the finalize endpoint runs (once all client-side sub-size sideloads complete). Watermarking, CDN sync, custom metadata processing, and similar plugins should keep working without modification, but should be checked to make sure they handle both passes. See “Server-side hooks still fire” above.

Does this change the format my users upload?

Only if image_editor_output_format says so – the existing filter is honored client-side. There are two new behaviors. HEIC inputs are converted to JPEG before upload and the original is kept as a companion file. And opaque animated GIFs are converted to a companion video (see below). AVIF inputs upload as AVIF, even on hosts whose server-side image editor lacks AVIF support. HDR images using gain maps upload unmodified, so their HDR gain maps survive – including in every generated sub-size.

What happens to animated GIFs?

Opaque animated GIFs are converted in the browser to a companion MP4/WebM video, and the editor offers a transform to convert the block to a “GIF” variation of the Video block that autoplays, loops, and is muted – so it behaves exactly like the original GIF while downloading far less data. Important details for extenders:

  • The attachment is still a GIF. It stays a single image/gif attachment in the media library; the video and a first-frame poster are companion files recorded in media_details.animated_video / animated_video_poster, removed automatically when the GIF is deleted.
  • The front end is a real video block. The swap is a block switch in the editor, not a render-time filter, so the published markup is a native <video autoplay loop muted playsinline poster> – nothing GIF-specific to filter.
  • It’s reversible, transparent GIFs are left as images, and only standalone Image blocks are converted (GIFs inside a Gallery, Media & Text, or Cover are untouched).
  • Browser support. Conversion needs WebCodecs video encoding (ImageDecoder + VideoEncoder). Browsers without it – notably Firefox – upload the original GIF unchanged, with no error. There is no separate opt-out filter; disabling client-side media processing also disables GIF conversion.

Why aren’t Firefox and Safari supported?

They don’t ship Document-Isolation-Policy, which is what enables SharedArrayBuffer (required for the WASM pipeline). Users on those browsers get the existing server-side path – no regressionregression A software bug that breaks or degrades something that previously worked. Regressions are often treated as critical bugs or blockers. Recent regressions may be given higher priorities. A "3.6 regression" would be a bug in 3.6 that worked as intended in 3.5.. The HEIC canvas fallback still works in Safari for HEIC inputs. A plugin is available (wordpress.orgWordPress.org The community site where WordPress code is created and shared by the users. This is where you can download the source code for WordPress core, plugins and themes as well as the central location for community conversations and organization. https://wordpress.org/ version coming soon, available now on GitHub) to enable the client-side media feature in Firefox/Safari using COEP/COOP headers.

Testing and feedback

We encourage plugin and theme developers to test client-side media processing with their products. In particular:

  • Verify that uploads work with your plugin’s custom image sizes and format settings – including sizes that share dimensions with built-in sizes.
  • Test HEIC uploads if you target sites with iPhone-using authors.
  • Test AVIF uploads on hosts whose image editor lacks AVIF support.
  • Test gain-mapped HDR photos (UltraHDR JPEGs) if your plugin transforms images – sub-sizes remain UltraHDR JPEGs with their gain maps, and format conversion is intentionally skipped for them.
  • Test animated GIF uploads – confirm the block converts to a looping muted video, the round-trip back to a GIF works, and any plugin that post-processes attachments handles the companion video/poster correctly.
  • Check that cross-origin isolation doesn’t break any external resources or embeds your plugin loads in the editor.
  • Test with wp_client_side_media_processing_enabled returning false to ensure your fallback path works.

Please report any issues on the Gutenberg GitHub repository. Related tracking issues:

For detailed developer documentation, see:

Props to @swissspidy, @andrewserong, and the many other contributors who worked on this feature. Thanks to @wildworks and @andrewserong for reviewing this post.

#7-1, #dev-notes, #dev-notes-7-1

Consistent navigation in WordPress 7.1 with persistent toolbar

WordPress 7.1 makes navigation more consistent across the whole adminadmin (and super admin) interface, including the editor, where the difference is most noticeable.

In the editor, the top-left “W” logo / site icon served as the back button, and clicking it took you out of the editor. But a “W” logo / site icon doesn’t read as a back button, and using it for navigation was a frequent source of confusion. Three changes are implemented to fix this situation:

  • the toolbar now appears in the editor as it does everywhere else (except in the Distraction Free mode),
  • the “W” logo / site icon is replaced with a dedicated back button (a chevron), and
  • the site icon, when set, is shown in the toolbar.

The result is a navigation model where each icon means one thing everywhere: the “W” logo always opens the About page, the site icon (when set) always opens the site menu in the toolbar, and the chevron always goes back to the previous screen. The site title also stays visible throughout the editor, including on the editing canvas. See the following images:

BeforeAfter

or with site icon:

BeforeAfter

What it means for users

The toolbar will be shown in Post and Site Editors by default, as part of the persistent navigation layer. If you’d rather work without it, turn on the Distraction Free mode, where the toolbar is hidden as it is today in that mode.

What it means for pluginPlugin A plugin is a piece of software containing a group of functions that can be added to a WordPress website. They can extend functionality or add new features to your WordPress websites. WordPress plugins are written in the PHP programming language and integrate seamlessly with WordPress. These can be free in the WordPress.org Plugin Directory https://wordpress.org/plugins/ or can be cost-based plugin from a third-party. developers

Before WordPress 7.1, the toolbar could already appear in the Post Editor when the fullscreen mode is turned off. The Site Editor has no such mode, so a persistent toolbar in the Site Editor is a new behavior. If your plugin adds a node in the toolbar, it’s worth double-checking if it still works correctly in the Site Editor.

If you’d prefer not to show your plugin’s toolbar node in the editor at all, you can filterFilter Filters are one of the two types of Hooks https://codex.wordpress.org/Plugin_API/Hooks. They provide a way for functions to modify data of other functions. They are the counterpart to Actions. Unlike Actions, filters are meant to work in an isolated manner, and should never have side effects such as affecting global variables and output. it out on editor screens, e.g. by checking $screen->is_block_editor() as follows:

add_action(
    'admin_bar_menu',
    function ( WP_Admin_Bar $wp_admin_bar ) {
        $screen = function_exists( 'get_current_screen' ) ? get_current_screen() : null;

              // use this check to hide the node in the Site Editor
              if ( $screen && 'site-editor' === $screen->id ) {
                      return;
              }
              // ... or use this check to hide the node in any block editor
              if ( $screen && $screen->is_block_editor() ) {
                      return;
              }


        $wp_admin_bar->add_node( ... );
    },
    100
);

Additional resources

  • TracTrac An open source project by Edgewall Software that serves as a bug tracker and project management tool for WordPress. tickets: #65091, #65088
  • Iteration issue

Props to @tyxla, @mayanktripathi32, @joen, @lucasmdo, @mamaduka, and @annezazu for reviewing this post.

#7-1, #editor, #dev-notes, #dev-notes-7-1

The Classic block stays in the inserter for WordPress 7.1

In an earlier post, I announced that the Classic blockBlock Block is the abstract term used to describe units of markup that, composed together, form the content or layout of a webpage using the WordPress editor. The idea combines concepts of what in the past may have achieved with shortcodes, custom HTML, and embed discovery into a single consistent API and user experience. (core/freeform) would be hidden from the inserter by default starting in WordPress 7.1, accompanied by a new filterFilter Filters are one of the two types of Hooks https://codex.wordpress.org/Plugin_API/Hooks. They provide a way for functions to modify data of other functions. They are the counterpart to Actions. Unlike Actions, filters are meant to work in an isolated manner, and should never have side effects such as affecting global variables and output. and a companion pluginPlugin A plugin is a piece of software containing a group of functions that can be added to a WordPress website. They can extend functionality or add new features to your WordPress websites. WordPress plugins are written in the PHP programming language and integrate seamlessly with WordPress. These can be free in the WordPress.org Plugin Directory https://wordpress.org/plugins/ or can be cost-based plugin from a third-party..

I’ve decided to revert this change. The Classic block will continue to appear in the inserter in WordPress 7.1, exactly as it does today. There is no change in behavior for users or developers, and no migrationMigration Moving the code, database and media files for a website site from one server to another. Most typically done when changing hosting companies. is required.

What this means

  • The Classic block remains available in the inserter by default. You can insert new Classic blocks through the inserter, block library, and slash commands as before.
  • The wp_classic_block_supports_inserter filter has been removed. Because this change never shipped in a stable WordPress release, the filter has no backward-compatibility footprint; there is nothing to migrate away from.
  • The block-level deprecation/migration notice has been removed. The Classic block editing experience returns to what it was previously, including the “Convert to blocks” toolbar action.
  • The Enable Classic Block plugin will be closed. With the default behavior restored, the plugin no longer serves a purpose. If you installed it, you can safely deactivate and remove it; no action is otherwise needed.

Why it is being reverted

After discussing this with a number of people and gathering feedback from different places, it became clear that this approach had things largely backward. It’s one step that makes the experience worse with no direct gain, and it doesn’t really get us any closer to transparently not loading TinyMCE. One of the takeaways is that the Classic block should become obsolete by choice, not by force. I believe time will be better spent to make the alternative genuinely better, while also smoothly, losslessly migrating content, so that users move off Classic block because they want to, not because the door has been removed.

Where the effort goes next

Much of the groundwork from this effort remains valuable, and the intention is to keep pursuing it from a user-first angle:

  • Understanding more in-depth why users still rely on Classic and bridging those gaps
  • Make “Convert to Blocks” flawless – it still has a bunch of flaws and inconsistencies
  • Work on better and more intuitive conversion/migration mechanisms, including mass migration
  • Improve TinyMCE asset registration and allow it to be disabled under various circumstances.
  • Build a mechanism for declaring proper explicit dependency on TinyMCE and work with plugins to utilize it.
  • Continue exploring ways to load TinyMCE on demand / asynchronously, among other performance improvements
  • Not loading TinyMCE on the block editor if the Classic Block is disabled from the block manager

Thank you to everyone who shared feedback and helped course-correct here. This work continues, pointed more squarely at what’s best for users.


Props to @mamaduka for reviewing this post.

#7-1, #dev-notes, #dev-notes-7-1

Hiding the Classic block from the inserter in WordPress 7.1

Note: this decision was reverted. You can read more about it in the new dev note.

We’ve just merged a change that will be part of WordPress 7.1 that hides the Classic blockBlock Block is the abstract term used to describe units of markup that, composed together, form the content or layout of a webpage using the WordPress editor. The idea combines concepts of what in the past may have achieved with shortcodes, custom HTML, and embed discovery into a single consistent API and user experience. from the block inserter by default. The Classic block stays registered, every existing Classic block keeps working and remains editable, and a new filterFilter Filters are one of the two types of Hooks https://codex.wordpress.org/Plugin_API/Hooks. They provide a way for functions to modify data of other functions. They are the counterpart to Actions. Unlike Actions, filters are meant to work in an isolated manner, and should never have side effects such as affecting global variables and output. lets anyone bring it back into the inserter. This post explains what changes, why, and how to opt back in if needed.

What’s changing

Starting in WordPress 7.1, the Classic block (core/freeform) no longer appears in the block inserter (#11712, Trac #65166, originally #77911 in GutenbergGutenberg The Gutenberg project is the new Editor Interface for WordPress. The editor improves the process and experience of creating new content, making writing rich content much simpler. It uses ‘blocks’ to add richness rather than shortcodes, custom HTML etc. https://wordpress.org/gutenberg/). In practice, this means you can’t add a new Classic block from the inserter, the block library, or slash commands.

Nothing else about the block changes:

  • The Classic block remains registered.
  • All existing Classic blocks (including any <!-- wp:freeform --> content) continue to render and stay fully editable, exactly as before.
  • The Classic editor and the underlying TinyMCE experience are untouched. If a post type doesn’t use the block editor, nothing here applies to it.

This is purely about steering new content away from the legacy Classic block, not about removing anything you already have.

To be clear: the Classic editor is not affected at all by this change. This is strictly about the Classic block inside the block editor. If you use the Classic editor (for example, via the Classic Editor pluginPlugin A plugin is a piece of software containing a group of functions that can be added to a WordPress website. They can extend functionality or add new features to your WordPress websites. WordPress plugins are written in the PHP programming language and integrate seamlessly with WordPress. These can be free in the WordPress.org Plugin Directory https://wordpress.org/plugins/ or can be cost-based plugin from a third-party. or on post types that don’t use the block editor), your experience stays exactly the same.

Why we’re doing this

The Classic block has been the bridge from the pre-block era into the block editor, and it has served that role well. But it’s also the one block in CoreCore Core is the set of software required to run WordPress. The Core Development Team builds WordPress. that doesn’t behave like a block:

  • Architectural consistency. Every other Core block is a node in the block tree. The Classic block is the lone exception, opaque HTMLHTML HyperText Markup Language. The semantic scripting language primarily used for outputting content in web browsers. rendered through a separate editor embedded inside the block editor. Keeping it as a default inserter option works against the block-first model on which the editor is built.
  • Reducing the inflow. The migrationMigration Moving the code, database and media files for a website site from one server to another. Most typically done when changing hosting companies. path away from Classic content (Convert to Blocks) has existed for years, and Classic usage keeps shrinking. Hiding the block from the inserter stops new Classic content from being created, so that set keeps getting smaller rather than growing.
  • Maintenance leverage. Many block-library improvements have to special-case the Classic block. Each special handling may be small on its own, but cumulatively, this may slow down work that benefits every other block.

The broader, longer-term goal, which will be covered separately as it matures, is to make the Classic block fully opt-in and eventually to lay the groundwork for loading TinyMCE only when it’s actually needed. WordPress 7.1 is just the first user-facing step on that path. None of the later steps are happening in 7.1, and each will get its own discussion and dev notedev note Each important change in WordPress Core is documented in a developers note, (usually called dev note). Good dev notes generally include a description of the change, the decision that led to this change, and a description of how developers are supposed to work with that change. Dev notes are published on Make/Core blog during the beta phase of WordPress release cycle. Publishing dev notes is particularly important when plugin/theme authors and WordPress developers need to be aware of those changes.In general, all dev notes are compiled into a Field Guide at the beginning of the release candidate phase..

Opting back in

If you (or your users) still want the Classic block available in the inserter, there’s a dedicated filter: wp_classic_block_supports_inserter.

Return true to show it everywhere:

add_filter( 'wp_classic_block_supports_inserter', '__return_true' );

The filter also receives the post being edited, so you can make the decision conditional, for example, per post type:

add_filter(
	'wp_classic_block_supports_inserter',
	function ( $supports_inserter, $post ) {
		return 'page' === $post->post_type ? true : $supports_inserter;
	},
	10,
	2
);

If you’d rather not write code, there’s a small plugin that does exactly this, Enable Classic Block, which flips the filter on for you. The plugin has already been submitted for approval to the WordPress Plugin Directory.

Backward compatibility

This change is opt-out by design and doesn’t break anything:

  • No content is modified or migrated. Existing Classic blocks are left exactly as they are.
  • The block, its edit behavior, and the Convert to Blocks action all continue to work.
  • The core/freeform block remains registered, so any code that relies on it being present keeps functioning.
  • Restoring the previous behavior is a one-line filter (or one tiny plugin) away.

What’s next

Alongside this change, we’re investing in the surrounding experience so that moving away from the Classic block is smoother for everyone:

  • A deprecation/migration notice (experimental). There’s an experiment in Gutenberg that surfaces a notice inside existing Classic blocks, with one-click actions to convert the content to blocks or to a Custom HTML block. We’re exploring this as a gentle way to highlight that the Classic block is being phased out and to make the migration path more discoverable. It’s behind an experiment flag for now while we refine it for a WordPress release.
  • Improving everything around it. In parallel, we’re improving and fixing the pieces that live by the Classic block: the Custom HTML block, the Convert to Blocks path, freeform handling and conversion, and related compatibility layers. The goal is that by the time Classic content needs to move, the tools to move it are solid.

These, alongside other planned next steps, can be tracked in the dedicated tracking issue.

We’d love your feedback

This is an early step in a longer effort, and we want to get it right. If you maintain plugins or custom integrations, run large sites, or have workflows that depend on the Classic block, we’d really like to hear from you, especially around migration and bulk-conversion needs.


Props to @desrosj, @mamaduka, @mukesh27, @westonruter, @wildworks, and @yuliyan for the contributions, feedback, and code reviews.

Props to @mamaduka and @yuliyan for reviewing this post.

#7-1, #dev-notes, #dev-notes-7-1

Accessibility Improvements in WordPress 7.0

WordPress 7.0 continues to polish accessibilityAccessibility Accessibility (commonly shortened to a11y) refers to the design of products, devices, services, or environments for people with disabilities. The concept of accessible design ensures both “direct access” (i.e. unassisted) and “indirect access” meaning compatibility with a person’s assistive technology (for example, computer screen readers). (https://en.wikipedia.org/wiki/Accessibility) across WordPress CoreCore Core is the set of software required to run WordPress. The Core Development Team builds WordPress. and GutenbergGutenberg The Gutenberg project is the new Editor Interface for WordPress. The editor improves the process and experience of creating new content, making writing rich content much simpler. It uses ‘blocks’ to add richness rather than shortcodes, custom HTML etc. https://wordpress.org/gutenberg/, advancing the goals to meet accessibility standards. WordPress 7.0 includes fixes across the platform, improving media management, usability for voice control, and improvements to color contrast with the new adminadmin (and super admin) color scheme. The editor ships with new blocks and improvements to editor navigation and interaction.

Core

Improvements to WordPress Core include 24 accessibility enhancements and bugbug A bug is an error or unexpected result. Performance improvements, code optimization, and are considered enhancements, not defects. After feature freeze, only bugs are dealt with, with regressions (adverse changes from the previous version) being the highest priority. fixes. Major changes include enhancements to the media library for voice control users and the import of alternative text from image metadata, improvements to control semantics, and fixes to color contrast.

Media

Significant changes to media will improve both the editor and user experience. In WordPress 7.0, using the media library with voice control technology is now possible. Alternative text embedded in photo metaMeta Meta is a term that refers to the inside workings of a group. For us, this is the team that works on internal WordPress sites like WordCamp Central and Make WordPress. data will be imported and automatically set as the image text alternative when available.

  • #23562 – Using Speech Recognition Software with the Add Media Panel
  • #55535 – Pre-populate Image Alt Text field with IPTC Photo Metadata Standard Alt Text
  • #63895 – Accessibility: Alt Text Metadata is not imported but Description is
  • #63984 – Assess if the tabpanels in the media modals should receive focus
  • #64374 – Alt text helper text can be more educational and visual indicator of opening in new tab
  • #63980 – Set featured imageFeatured image A featured image is the main image used on your blog archive page and is pulled when the post or page is shared on social media. The image can be used to display in widget areas on your site or in a summary list of posts. button incorrectly coded as link and missing required ARIA attributes

Admin

Improvements to predictability and verbosity for screen reader users have been made across the admin to provide users with a more consistent and stable interface.

  • #23432 – Review usage of target="_blank" in the admin
  • #33002 – List table: avoid redundant Edit links and reduce noise for screen readers
  • #43084 – dashboard confuses published posts count with all posts
  • #64065 – Dragging theme/pluginPlugin A plugin is a piece of software containing a group of functions that can be added to a WordPress website. They can extend functionality or add new features to your WordPress websites. WordPress plugins are written in the PHP programming language and integrate seamlessly with WordPress. These can be free in the WordPress.org Plugin Directory https://wordpress.org/plugins/ or can be cost-based plugin from a third-party. ZIP outside file input field, downloads file instead of uploading.
  • #64375 – Set word-break property in screen reader only css.
  • #64313 – Color Contrast raises errors in automated tests for WordPress Dashboard
  • #64382 – Post search input “close” (×) button should use cursor: pointer
  • #64811 – Zero comment notification in admin toolbar has insufficient color contrast

Themes

Numerous improvements to theme template functions and core themes.

  • #62835 – Remove title attributes from author link functions
  • #62982 – Twenty Twenty-Five: The Written by pattern on single posts has too low color contrast in some variations
  • #64064 – Twenty Ten: remove auto-focus script from 404 template
  • #64594BlockBlock Block is the abstract term used to describe units of markup that, composed together, form the content or layout of a webpage using the WordPress editor. The idea combines concepts of what in the past may have achieved with shortcodes, custom HTML, and embed discovery into a single consistent API and user experience. Support: Allow serialization skipping for ariaLabel
  • #64361 – Leverage HTMLHTML HyperText Markup Language. The semantic scripting language primarily used for outputting content in web browsers. APIAPI An API or Application Programming Interface is a software intermediary that allows programs to interact with each other and share data in limited, clearly defined ways. to implement block template skip link

Miscellaneous

Improvements in the classic editor, code editing, the CustomizerCustomizer Tool built into WordPress core that hooks into most modern themes. You can use it to preview and modify many of your site’s appearance settings., and login and registration.

  • #63981TaxonomyTaxonomy A taxonomy is a way to group things together. In WordPress, some common taxonomies are category, link, tag, or post format. https://codex.wordpress.org/Taxonomies#Default_Taxonomies. meta box tabs not programmatically identified
  • #42822 – CodeMirror: HTML attributes values hints not fully operable with a keyboard
  • #60726 – The WordPress core password reset needs to pre-populate the username to meet WCAGWCAG WCAG is an acronym for Web Content Accessibility Guidelines. These guidelines are helping make sure the internet is accessible to all people no matter how they would need to access the internet (screen-reader, keyboard only, etc) https://www.w3.org/TR/WCAG21/. 2.2
  • #63861 – Explore removing wpmu activation styles
  • #64013 – Color contrast below WCAG standards for newly-added items in customizer menus

Gutenberg

Changes within Gutenberg include 16 accessibility fixes and enhancements, including the addition of new interactive blocks that have undergone accessibility reviews. Numerous fundamental components have had accessibility improvements to ensure that interfaces across the editor are more consistent and understandable. 

While there are relatively few accessibility fixes and enhancements in the editor for WordPress 7.0, there are many new interfaces that have undergone accessibility review, per the WordPress commitment to meeting WCAG 2.2 at level AA for all new and updated code. These include the Visual Revisions inspector, Gallery lightboxes, and the new Connectors interface.

Bug fixes: 

  • #75165 – RangeControl: support forced-colors mode
  • #66735 – Resize meta box pane without ResizableBox
  • #74387 – Use 12px as minimum font size for warning on fit text (see also #73730)
  • #74205 – add ariaKeyShortcut and shortcutFormats exports
  • #73674 – Fix block toolbar icon CSSCSS Cascading Style Sheets. when using show icon label preference
  • #73245 – Make DataViews table checkbox permanently visible
  • #72997 – DataViews: Add grid keyboard navigation
  • #70787 – Button: update font-weight to 500 
  • #75689 – DataForm: Fix focus loss and refactor Card layout
  • #75271 – Accordion block: Add list view support.
  • #75407 – Gallery: Add list view block support
  • #73823 – Add Heading level variations

New Features:

  • #62906 – Gallery: Add lightbox support
  • #16484 – Add an Icons block
  • #75833 – Add Connectors screen and API
  • #74742 – Add visual revisionsRevisions The WordPress revisions system stores a record of each saved draft or published update. The revision system allows you to see what changes were made in each revision by dragging a slider (or using the Next/Previous buttons). The display indicates what has changed in each revision.

Reviewed by @amykamala, @sabernhardt

#7-0, #accessibility, #dev-notes, #dev-notes-7-0

Removing title attributes in author link functions

WordPress 7.0 removes—or facilitates removing—title attributes from links relating to post authors.

Author’s Website link (from the user profile)

get_the_author_link() and the_author_link() have a new $use_title_attr parameter, which can be set to false to remove the “Visit Author’s website” tooltip. By default, these functions continue to include a title attribute.

<?php
// either
the_author_link();
// or
echo get_the_author_link();

Default output is the same in 7.0 as in 6.9:
<a href="https://author.example.com" title="Visit Author&#8217;s website" rel="author external">Author</a>

<?php
// either
the_author_link( false );
// or
echo get_the_author_link( false );

Output in 7.0:
<a href="https://author.example.com" rel="author external">Author</a>

Author’s posts archive link

The “Posts by Authortitle attribute is removed from the link by default. However, the title text is still available for use within the the_author_posts_link hook, along with the author’s display name.

<?php
// either
the_author_posts_link();
// or
echo get_the_author_posts_link();

Output in 6.9:
<a href="https://example.org/author/author/" title="Posts by Author" rel="author">Author</a>

Output in 7.0:
<a href="https://example.org/author/author/" rel="author">Author</a>

Editing the posts link text

To replace the author name with the “Posts by Author” text, use multiple arguments in the the_author_posts_link filterFilter Filters are one of the two types of Hooks https://codex.wordpress.org/Plugin_API/Hooks. They provide a way for functions to modify data of other functions. They are the counterpart to Actions. Unlike Actions, filters are meant to work in an isolated manner, and should never have side effects such as affecting global variables and output..

<?php
/**
 * Edits text for the link to the author page of the author of the current post.
 *
 * Add "Posts by" before the author's display name (or after the name in some translations):
 * `<a href="https://example.org/author/author/" rel="author">Posts by Author</a>`
 *
 * @param string $link   HTML link.
 * @param string $author Author's display name. Default empty string.
 * @param string $title  Text originally used for a title attribute. Default empty string.
 */
function wpdocs_author_posts_link( $link, $author = '', $title = '' ) {
	// In WordPress versions prior to 7.0, $author and $title would be empty.
	if ( '' !== $title && '' !== $author ) {
		$link = str_replace(
			'>' . $author . '</a>',
			'>' . esc_html( $title ) . '</a>', 
			$link
		);
	}

	return $link;
}
add_filter( 'the_author_posts_link', 'wpdocs_author_posts_link', 10, 3 );

Authors list HTMLHTML HyperText Markup Language. The semantic scripting language primarily used for outputting content in web browsers.

wp_list_authors() simply removes the “Posts by Author” tooltips.

<?php
wp_list_authors(
	array(
		'html' => true // This is true by default.
	)
);

Output in 6.9:
<li><a href="https://example.org/author/author/" title="Posts by Author">Author</a></li><li><a href="https://example.org/author/editor/" title="Posts by Editor">Editor</a></li>

Output in 7.0:
<li><a href="https://example.org/author/author/">Author</a></li><li><a href="https://example.org/author/editor/">Editor</a></li>

For more information, refer to #62835.


Props to @amykamala and @audrasjb for review.

#7-0, #dev-notes, #dev-notes-7-0