WP Manifestindependent plugin directory
manifest / editor / rotate-everything

Rotate Everything

Rotate any block by a free angle from the editor sidebar — server-rendered CSS transform, no JavaScript on the front end.

★ 0stars

Install

Download source zip No release zip yet

This is the repository archive. It installs, but the folder name will carry the branch suffix and updates will not flow.

Install straight from GitHub with WP-CLI:

wp plugin install https://github.com/fyrins/rotate-everything/archive/refs/heads/main.zip
No built-in updater. New versions won't appear under Dashboard → Updates. Install new releases yourself, or let Git Updater track the repository.

Rotate any block by a free angle from the editor sidebar. Server-rendered CSS transform, no JavaScript on the front end.

Plugin directory page: https://wordpress.org/plugins/rotate-everything/

  • Requires WordPress 6.2 or later
  • Requires PHP 7.4 or later
  • License GPL-2.0-or-later

What it does

Adds a Rotation panel to the block inspector. The angle is stored as a block attribute, previewed live in the editor, and turned into a transform: rotate() declaration when the page is rendered.

By default the panel appears on core/image and on nothing else. Rotating running text hurts legibility, so opening the list up is a decision the site owner makes explicitly through a filter.

Design decisions

These are the constraints the plugin was built under. They explain most of the code, so they are worth reading before changing any of it.

Nothing is written to the saved markup

The rotation attribute is declared without a source, which means it lives in the block comment rather than in the block's HTML:

<!-- wp:image {"id":42,"rotation":-8} -->
<figure class="wp-block-image size-full"><img src="…" alt="" class="wp-image-42"/></figure>
<!-- /wp:image -->

Block validation reads the HTML, not the comment, so the attribute is invisible to it. The consequence is the one that matters: deactivating the plugin invalidates nothing. The markup in the database is still exactly what core/image's save function produces, so the editor compares identical strings and every block stays valid.

Writing the transform into the saved markup, as an earlier prototype of this plugin did through blocks.getSaveElement, breaks that property in both directions. It creates a second source of truth alongside render_block, which risks applying the rotation twice, and it puts a style attribute in the database that the regenerated markup no longer produces once the plugin is gone, invalidating every affected block at deactivation.

For the same reason there is no blocks.getSaveContent.extraProps filter and no blocks.getSaveElement filter in this codebase, and there should not be one.

The transform is applied on the server, through the Tag Processor

render_block hands us the rendered HTML of one block. We open it with WP_HTML_Tag_Processor, move to the target element, and write two attributes. No DOMDocument, which would reserialise markup it does not fully understand, and no regular expression over HTML.

The Tag Processor can only read and write attributes, which is all this needs.

Where the transform lands, and why

On the block's outermost rendering element: the first tag that is not `