Fifteen more patterns, and somewhere to find them. The library had two visible groups while the headers and footers sat under core's categories where nobody looks. There are now six Osmium groups in the inserter.
Read full notes8 changes
Footers, now five. A signup band, a centered stack for a personal site, and a dark closing band that follows whichever palette is active rather than turning white.
Openers, a new group of five. The newsletter band moves here, joined by a text and image split, a centered statement with one clear next step, a single sentence for an essayist, and one that leads with whatever you published last so the top of the page changes on its own.
Five more sections. A numbers row, a three-step explainer, a section with the heading beside the text so a long passage keeps its measure, a callout note for inside an article, and a keep-reading block for the end of one.
Every pattern that pulls posts carries an empty state, so a new site never renders a heading with nothing under it.
Install: Download osmium-0.2.0.zip below, then Appearance → Themes → Add New → Upload Theme. Requires WordPress 7.0 and PHP 7.4. Upgrading from 0.1.0 is a straight replace; nothing you have built changes.
New in this release: Headers, now five. Alongside centered and minimal: one with a call to action for a site that sells, one with a notice strip for an announcement, and one with search in the open for a site with a deep archive.
The theme now ships: | | | |---|---| | Templates | 11 | | Patterns | 42, in six groups | | Style variations | 4, plus 3 section styles | | Theme CSS | 8.0 KB | | Theme JavaScript | none |
Checked, not assumed: Every pattern and template validates in the block editor. PHPCS is clean. Colour contrast passes in all four palettes, measured by script. axe-core reports zero WCAG 2.2 AA violations across every template and every new pattern, at desktop and mobile, on a fresh install and a populated one.
Known limits
Tested on WordPress 7.0 and 7.1. The 7.2 beta opens on 20 October and this will be retested then.
The Front Page template takes precedence over a static front page you set yourself. To use your own page, edit or delete that template in Appearance → Editor → Templates.
Not submitted to the WordPress.org theme directory yet.
First public release. Early, but complete enough to run a real site, and everything in it has been checked rather than assumed.
Read full notes13 changes
On activation you get a designed front page straight away, filled with your own posts. Nothing is created in your database and no settings are changed.
No JavaScript ships with the theme. No post types, no blocks, no shortcodes, no settings page. Everything you build keeps working if you switch themes.
Feedback and bug reports are welcome in the issues.
Install: Download osmium-0.1.0.zip below, then in your WordPress admin go to Appearance → Themes → Add New → Upload Theme. Requires WordPress 7.0 and PHP 7.4.
What is in it
11 templates: front page, blog home, index, single, single with sidebar, page, page without a title, archive, author, search, and 404
27 patterns, including a review kit of seven: verdict, pros and cons, spec table, comparison table, best-for cards, product call to action, and a deal strip
Four page-creation patterns: homepage, about, newsletter landing, and tools
Four style variations: Editorial by default, plus Paper, Signal, and Midnight for dark
Three section styles for tinted, dark, and card sections, so patterns stay core blocks
Two bundled fonts, Valley Sans and Hedvig Letters Serif, self-hosted and served from your own domain under the SIL Open Font License
Checked, not assumed: | | | |---|---| | Theme Check | 0 required, warning, or recommended issues | | PHPCS, WordPress standards | clean across 27 files | | axe-core, WCAG 2.2 AA | 0 violations on every template, desktop and mobile, on a fresh install and a populated one | | Colour contrast | every pair in all four palettes measured by script | | Theme CSS | 8.0 KB |
Known limits
Tested on WordPress 7.0 and 7.1. WordPress 7.2 beta opens on 20 October and this will be retested then.
The Front Page template takes precedence over a static front page you set yourself. To use your own page, edit or delete that template in Appearance → Editor → Templates.
Not submitted to the WordPress.org theme directory yet.
Unused CSS status on the Optimization tab: queued, processing, ready, stale, failed, and skipped results; original and generated sizes; reduction percentage; build duration; and failure details.
Manual status refresh, per-URL and per-result force regeneration, and full regeneration in bounded background batches. Known eligible URLs and the homepage are queued; other pages rebuild when visited.
Counts across all stored reports, with the latest 50 results shown in the table. Current savings exclude stale results and missing generated files.
Fixed
Signed CSS generator requests were blocked by page-cache and commerce query rules. The authenticated build parameter is now excluded from the policy context while other request protections remain intact. Build responses remain private and are never cached.
Generator tokens no longer become part of report URLs or CSS reuse keys.
HTTP errors and responses without completed CSS reports now fail the background job so its retry policy applies. Successful generation purges the public page cache so visitors receive the new CSS.
CSS reuse now accounts for the URL, full markup, and CSS revisions, preventing mismatches involving IDs, attribute values, and DOM relationships. Forced builds bypass existing results.
Regeneration controls check saved rollout, exclusions, safe mode, and optimization ownership, and avoid duplicating active jobs. Disabled or newly excluded jobs record a skipped result.
Consistent padding and spacing across status cards, statistics, reports, and regeneration controls.
Clearer help text across optimization and cache settings.
WordPress.org builds omit the self-hosted Update URI header. FluentCart URL handling uses WordPress parsing helpers, and the early Redis drop-in documents its filesystem fallback.
A block_type_metadata filter now sets version to ACF_BLOCKS_VERSION for blocks whose block.json lives inside this plugin, matching what the plugin's direct wp_enqueue_style() calls have always passed. Blocks that other plugins register…
Read full notes18 changes
A block_type_metadata filter now sets version to ACF_BLOCKS_VERSION for blocks whose block.json lives inside this plugin, matching what the plugin's direct wp_enqueue_style() calls have always passed. Blocks that other plugins register under the acf/ namespace are left alone. Asset URLs now carry the plugin version and change with every release.
Sites running GT Performance saw this compounded: its assetVersion filter rewrites ver when it equals the core version, turning ?ver=7.1 into a stable hash. It was doing its job — hiding the core version — but the versioning underneath was already wrong, and the hash made the staleness harder to spot.
What's Changed
Fixed
Block stylesheets were never cache-busted by a plugin release. Core's register_block_style_handle() reads $metadata['version'] from block.json and falls back to false, which makes wp_register_style() stamp the WordPress version onto the URL. Every one of this plugin's block.json-registered handles was therefore versioned by core, not by the plugin: on this site url-preview.min.css shipped as ?ver=7.1 for WordPress 7.1, and the files are served public, max-age=31536000. The query string is the only thing that can retire a year-long cached copy, so a CSS fix in a plugin release stayed invisible to every reader who had already loaded the page until WordPress itself updated. The 2.11.2 contrast fix was landing this way — correct on disk and at the CDN, still broken in the browser of anyone who had visited before.
Commits
2.11.3: version block.json assets with the plugin, not with WP core (a91797b)
2.11.2: fix white-on-white URL Preview button in dark mode (e40be8e)
Interface fixes. The WordPress 7 data panel no longer appears for modules you have switched off, where it offered a create form that could not work. Several admin headings now match the module names they belong to. No settings, data, or…
Read full notes9 changes
= 1.6.0 = = 1.6.1 = Interface fixes. The WordPress 7 data panel no longer appears for modules you have switched off, where it offered a create form that could not work. Several admin headings now match the module names they belong to. No settings, data, or hooks change.
What's Changed
Fixed The WordPress 7 data panel no longer appears for a module that is switched off. On a site with Redirect Manager disabled, its page still rendered two empty tables and a working "Add redirect" form, even though the module refuses every write while disabled, so the form could only produce an error.
Changed The panel's "WordPress 7 workspace" heading is gone. Its tables already carry their own headings, and naming a section after the WordPress version labelled the implementation rather than what you are looking at.
Fixed The Performance & Cleanup settings page was headed "Miscellaneous (Bloat Control)", which matched neither the module card you clicked nor any other name in the interface.
Changed The dashboard box holding the AI opt-in is now called "AI explanations", after the single setting it contains, instead of advertising four platform features that are not configurable there.
Fixed Link Management's JSON preset filter note now matches the format used by every other filter note in the plugin.
Commits
Ship 1.6.1 (933f1be)
Stop the WordPress 7 panel labelling itself, and hide it when the module is off (b063842)
Security release. Fixes an Abilities API permission flaw that let any user who could edit a post reach administrator-only operations, moves redirect and task data into a private folder, and stops header/footer snippets being mangled for…
Read full notes28 changes
= 1.5.0 = = 1.6.0 = Security release. Fixes an Abilities API permission flaw that let any user who could edit a post reach administrator-only operations, moves redirect and task data into a private folder, and stops header/footer snippets being mangled for logged-out visitors. Also replaces DOMDocument with the WordPress HTML API in three content filters, so pages using Vue or Alpine are processed correctly instead of skipped. Existing settings, hooks, admin URLs, and data files are migrated automatically.
What's Changed
Security Abilities API operations now use a permission callback per ability and reject undeclared input properties. A shared callback previously widened to edit_post whenever the request carried a post_id, so any user who could edit one post could toggle modules, create redirects, create tasks, and trigger scans.
Security Redirects, the bounded 404 log, and Task Manager projects moved to a private folder with a random name under wp-content/functionalities/. Existing files are migrated automatically. Apache, IIS, and directory-listing rules are written alongside them, and a new Site Health check confirms over HTTP that the folder really is unreachable.
Fixed Header and footer snippets are no longer re-filtered against the visitor's capability at output time. Anonymous visitors were receiving mangled code — && became && and comparison operators were eaten as tags — while the logged-in administrator saw the snippet work. Filtering now happens once, at save time, against the author's capability.
Fixed A JSON exception preset served from a URL is fetched at most once per cache window instead of on every page load. The cache clears whenever the module settings change, a post or page is edited, or the theme changes, and the last good list is kept when a fetch fails.
Fixed The bulk nofollow tool pages through posts with an ID cursor and now finishes on sites with more than 100 matches. It previously returned the same first batch on every run.
Improved Link Management, Block Cleanup, and Schema use the WordPress HTML API instead of DOMDocument. Attributes are edited in place, so Vue, Alpine, and mustache syntax survive untouched and the JS-framework skip guard added in 1.4.3 and 1.4.4 is gone. Content that used to be skipped is now processed correctly.
Improved Redirect hits and 404 aggregates are buffered and written in batches rather than rewriting the whole JSON file under an exclusive lock on every request.
Improved Redirects run at parse_request, before WordPress queries the database for a page it is about to discard. WordPress's own entry points are never redirected.
Improved The Content Integrity column on the posts list reads a result cached at save time instead of rendering and parsing every row on every page load.
Improved The SVG icon library is stored without autoloading, so full SVG markup no longer loads on every request.
Improved The service worker skips wp-admin, the login page, REST responses, cross-origin requests, and anything marked no-store or private; caps the runtime cache; and precaches URLs individually so one stale entry cannot stop it installing. The manifest now includes an id.
Improved Login Security adds per-username throttling, an IP allowlist, an unlock button on the lockout log, and a warning when every recent lockout shares one address, which is the signature of a site behind a CDN.
Improved Prism.js is bundled with the plugin instead of being loaded from a third-party CDN.
Improved Performance & Cleanup makes the revision limit configurable, and disabling Heartbeat now applies to the frontend only unless the new admin option is also enabled, so autosave and post locking keep working.
Improved Content Integrity and Assumption Detection gained the filters their documentation promised, and the module documentation now lists hook names that exist. Nineteen documented hooks were never fired.
Fixed Settings export no longer redacts the GA4 measurement ID as if it were custom code.
Fixed Core icons get the same definition-ID prefixing as custom icons, so two gradient icons on one page no longer collide.
Fixed Saving PWA settings flushes rewrite rules once instead of twice.
Fixed Disabling feeds falls back to a message only when a redirect is genuinely impossible, making the documented message filter reachable.
Changed The translation template is generated from the source. It was a one-string placeholder.
Changedsrc/ and docs/ are excluded from the distribution, and build.sh now uses the same exclude list as the release workflow so a local build and a tagged release cannot drift.
Changed Tested up to WordPress 7.1.
Commits
Readme: test against WordPress 7.1, correct stale claims, link the docs site (bbcd38d)
Fix CI: ship the Prism assets and make the POT check reproducible (c199bbd)
Migrate legacy data even when the file modules are disabled (e1c467d)
Ship 1.6.0: Abilities permission fix, private data storage, HTML API filters (c5cdccd)
Core Forms 4.9.0 improves submission reliability, spam protection, payment validation, and the WordPress admin experience.
Read full notes7 changes
Requires WordPress 6.4+ and PHP 8.1+. Tested with WordPress 7.1.
Validation: 336 unit tests, PHPStan, JavaScript lint, generated-asset checks, package integrity and PHP syntax checks, plus local browser verification.
A correctness release. It repairs paths that silently corrupted or discarded work, gives the plugin a test suite and CI for the first time, and makes the upgrade itself verifiable.
Read full notes16 changes
Back up your database before upgrading — the schema change is one-way. Requires PHP 8.1. Flush your page cache and CDN afterwards: generated CSS and JS filenames now carry a content hash.
Breaking
PHP 8.1 is the minimum. WordPress will not offer this to a site below it, and activation stops with a notice instead of a white screen.
The CSS minifier no longer collapses whitespace around :..menu :hover stays a descendant selector instead of silently becoming .menu:hover. If a stylesheet unknowingly depended on the collapsed form, that rule changes what it matches — this fixed eight blocks on our own site whose typography had quietly stopped applying.
Minified JavaScript changes shape. A line comment no longer swallows the rest of the file, and newlines survive so semicolon-free code is not concatenated. Blocks whose JS silently did nothing start working.
Generated asset filenames carry a content hash. Every cache and CDN misses once. The old unhashed name is still written as a copy for this major version.
Utility-class output is switched off once during the upgrade, with a notice. The scanner never emitted anything for page blocks, so this is a zero-visual-change upgrade; turning it back on is your decision.
PHP execution in the block preview requires administrator access. It previously ran for anyone who could edit the post.
The AI panel requires manage_options. Restore with add_filter( 'gt_pb_ai_capability', fn() => 'edit_posts' );.
The undocumented terminal endpoint is removed.
Uninstalling deletes options, transients and stored AI keys. The library is dropped only if you tick the new setting.
Library usage counts change on block themes, where the invalidation hooks were never registered.
Correctness: Editing a library block reaches the visitor — in file-output mode the generated file was written once and never again. The builder stops blanking core and third-party blocks on every keystroke. Renaming a section persists. A failed save says so instead of showing a green notice over a discarded session. Inline SVG backgrounds survive the CSS sanitiser. A block detached from the library stops rendering the library version over your own copy.
Recovery: Library blocks keep a revision history with one-click restore. Document-level undo covers add, delete, duplicate, reorder and import. Cmd+Backspace and Cmd+D stop acting on the whole section from inside a code pane. Undo after switching sections no longer pastes the previous section's code.
Security: The PHP-execution checksum is keyed with the site salt, so it cannot be recomputed by whoever wrote the row; existing blocks keep working and are re-keyed in the background. The licence and update channel verifies TLS certificates and rejects package URLs that are not HTTPS on the licence host.
Free, with licensed updates: Every feature works without a licence key; nothing is gated. A licence buys automatic updates and support. Security releases reach every install regardless, through a separate channel — see SECURITY.md.
Going back: Reinstall v2.8.1. The database stays at schema 1.2, which is expected and harmless. PHP-enabled blocks strip their tags until re-saved. Detail in README.md under "Going back from 3.0.0".
Run the test site on pretty permalinks — on plain permalinks the library screen's REST calls fail. Known, fixed for final.
Install: Download the ZIP below and install it through Plugins → Add New → Upload Plugin, or:
Before you start: define( 'GT_PB_DISABLE_SECURITY_CHANNEL', true ); // stops the RC being swapped mid-test define( 'GT_PB_ALLOW_PHP', true ); // only if testing PHP blocks
What to exercise, in order
Upgrade a site that has PHP-enabled blocks and confirm they still render. The checksum scheme changed and old values are rewritten in the background; this is the highest-risk path.
A page mixing page blocks with core blocks. Open the visual builder, type, save, reload — core and third-party blocks must survive, and the page must save twice in a row.
A block using External File output. Edit its CSS and confirm the change reaches the front end. This never worked before 3.0.
Detach a linked block in the block editor, edit the copy, and check the front end shows your version — fixed in rc.3.
New since rc.2
A block detached from the library kept rendering the library version. Fixed on both the editor and the server, so content already saved in that state is healed too.
tags and description are writable and round-trip over REST; both get fields on the edit screen.
Display Conditions are always visible, not only with a hook position set. Linked blocks get a respectConditions toggle, off by default.
New gt_pb_block_rendered filter, and a hooks reference in README.md.
CI syntax-checks assets/js — there is no bundler, so those files ship exactly as committed.
Going back: Reinstall v2.8.1. The database stays at schema 1.2, which is expected and harmless. PHP-enabled blocks strip their tags until re-saved. Full detail in README.md under "Going back from 3.0.0".
Reporting: Include the version from the Plugins screen (should read 3.0.0-rc.3), single vs multisite, and whether you upgraded or installed fresh. Security issues: see SECURITY.md.
Privilege escalation in the block preview. PHP in a Page Block no longer runs for users who can merely edit the post. The builder's preview endpoint is reachable by anyone with edit_post — an Author, or a Contributor on their own draft — and it executed the section's PHP after deriving the content checksum from the very content it was about to run, so the check was satisfied by definition. On any site that had turned PHP blocks on, that left the site-wide constant standing alone as the only gate. Running PHP in a preview now requires administrator access, and everyone else previews with the tags stripped and a note saying so rather than silently different output.
The update channel verifies certificates again. It was requesting with sslverify off, and the server's reply supplies the package URL WordPress downloads and installs a plugin from, so anything able to answer as the licence server could have installed arbitrary code. Certificates are now verified, the request goes through wp_safe_remote_post(), and any package, url or homepage pointing somewhere other than the licence server's own host is discarded rather than followed. A host with a genuinely broken CA bundle can opt out per-site with GT_PB_LICENSE_INSECURE. The changelog the update screen renders is escaped before display.
The plugin declares what it needs.Requires PHP: 8.1, Requires at least: 6.0, a licence and a text-domain path. It previously declared none, so WordPress offered the update to sites that would fatal on it, and the update payload separately claimed PHP 7.4 while the code has needed 8.1 since 2.7. A site below 8.1 now gets an admin notice naming the versions instead of a white screen. Update URI is set, so the plugin can only ever be updated from its own source.
The licence screen is reachable. It is registered under the Page Blocks menu, but the Plugins row action and both admin notices linked to options-general.php, a screen that does not exist.
Also: the preview endpoint checks the post type, matching the builder; and the GPLv2 text the header declares now ships with the plugin.
Correctness release. Everything here is a defect a site could hit without opting into anything, or a claim the shipped documents made that was not true.
Read full notes19 changes
Fixed
The managed Cloudflare Cache Rule instructed the edge to cache responses the origin marks no-store, private. cloudflare.edge_ttl now defaults to 0, so the rule compiles as respect_origin. A positive lifetime still compiles as override_origin, but the expression is then narrowed to requests with no query string, because overriding the origin cannot be made safe for the unbounded set of query parameters the origin refuses.
Trashing, unpublishing, or renaming a post never cleared its cached page. The save_post handler returns early for posts that are not publicly viewable, and a status change reaches it with the new status already applied, so a withdrawn page kept returning 200 for the rest of its stale window.
Saving settings orphaned the whole cache. generation is part of the cache key and is bumped on every save; nothing deleted the now-unreachable entries.
The capture pipeline ran when no page-cache drop-in was installed, which is the state directly after activation.
A full purge deleted the .htaccess and index.html that keep the cache directory unreadable from the web.
The private-fragments AJAX endpoint was registered even when the feature was off.
Removed
The WordPress revision limit control. It filtered wp_revisions_to_keep unconditionally at 5 on every activation, whether or not its own module was enabled, discarding revision history irreversibly on the next save.
The X-GT-Performance-Bypass request header. Its reason code claimed a signature that nothing ever computed or verified, so any client could force a full uncached render on every request.
The "Remove unused CSS" setting. The engine flattens native CSS nesting, drops @import stylesheets, prunes escaped utility class names, and runs during the visitor request; the damage was silent and cached. Define GTPERF_UNUSED_CSS in wp-config.php to run it anyway. It returns as a supported feature once generation moves out of the request and the differential safety net lands.
Multisite activation. One compiled config and one cache root are shared across a network, so the last subsite to save decided every other subsite's cache behavior.
Added
LICENSE, and a Third-party libraries section disclosing the three bundled MIT libraries.
Update URI: false, so nothing claiming the unclaimed gt-performance directory slug can push a package to existing installs.
A golden-file HTML regression fixture, and a Plugin Check job in CI that runs against the built ZIP rather than the working tree.
Upgrade Notice entries, including the one 1.0.4 shipped without.
Changed
Documentation no longer describes WordPress.org as the update authority. The plugin is not listed in the directory yet.
Adds a hover-and-click copy button to the Branded URL column and the plugin's own icon in the admin menu, plus an expanded readme and FAQ. No database changes.
Read full notes6 changes
= 1.8.0 = = 1.8.1 = Adds a hover-and-click copy button to the Branded URL column and the plugin's own icon in the admin menu, plus an expanded readme and FAQ. No database changes.
What's Changed
New a copy button on the Branded URL column. It appears when you hover the row or focus it with the keyboard, copies the full branded link, and confirms with an inline Copied badge that clears itself.
New the plugin's own icon now appears in the admin menu, replacing the generic WordPress link dashicon. It is painted as a mask filled with the menu's own text colour, so it matches the icons beside it in every state and every admin colour scheme, staying light on dark sidebars and dark on light ones.
Updated refreshed the WordPress.org plugin icon.
Commits
Release 1.8.1: copy button, brand menu icon, expanded readme (ead0ff0)