Release 1.3.0 — retire Epsilon, add theme.json, clear the ground for blocks - #354
Merged
Conversation
theme.json (v2, matching the WordPress 6.4 floor) becomes the source of truth
for the palette, type scale and layout widths. The customizer stays where a
site owner changes colours: each colour theme mod overrides the corresponding
--wp--preset--color-- custom property, so blocks and the classic front end
resolve to one value instead of two palettes disagreeing.
All 31 Epsilon_Control_Toggle usages become core checkbox controls and the one
Epsilon_Control_Slider becomes Shapely_Control_Range, a ~45-line control that
renders a range input with its value alongside -- core's plain 'range' type
shows no number, which is unhelpful for an opacity setting.
Epsilon_Section_Pro becomes Shapely_Section_Link.
The class_exists( 'Epsilon_*' ) guards and their else branches are collapsed to
a single registration each. Worth recording why the else branches could not
simply be kept: they were never complete. Two of them registered 6 controls
where the Epsilon branch registered 11, and one had no else at all -- so had
Epsilon ever failed to load, these 11 controls would have silently vanished:
post_author, post_category, post_date, title_above_post, title_in_header,
project_author, project_category, project_date, project_title_in_header,
title_above_project
The Epsilon branch was the complete one, so the core registrations are
generated from it instead.
Counts before and after, as the check that nothing was dropped:
add_setting 52 -> 52
add_control 74 -> 52 (22 duplicate registrations from the else
branches removed: 10 single-control blocks
plus 2 blocks of 6)
Control ids registered: no change, none lost.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Removes inc/libraries/ entirely -- 144 files, 1.3 MB, which was 52% of the
shipped theme. PHP files drop from 88 to 50.
What it was actually providing, and what replaces it:
Epsilon_Control_Toggle (x31) core checkbox controls
Epsilon_Control_Slider (x1) Shapely_Control_Range (~45 lines)
Epsilon_Section_Pro Shapely_Section_Link
Epsilon_Section_Recommended_Actions Shapely_Section_Link -> welcome screen
Epsilon_Welcome_Screen (850 lines) Shapely_Welcome
Epsilon_Notify_System nothing; see below
Shapely_Notify_System no longer extends Epsilon_Notify_System. Nothing was
inherited: every self:: call resolves to a method defined in the child, the two
that looked inherited were already overridden because the parent hardcoded
ABSPATH . 'wp-content/plugins/', and no parent-only method is referenced
anywhere in the theme.
Two contracts are preserved deliberately, because other code depends on them:
- The demo import still posts `shapely_companion_import_content` with a
`welcome_nonce` and the same `import` values. That endpoint lives in the
Shapely Companion plugin, so changing the payload would break the import
against every released version of it.
- The welcome screen is still themes.php?page=shapely-welcome, and the
customizer section ids are unchanged, so existing deep links resolve.
Two deliberate behaviour changes:
- The old AJAX endpoint read a class name and a method name from POST and
called $class::$method(). It was whitelisted to one class, but dispatching
arbitrary static calls from request data is not a pattern worth carrying
forward. Dismissals now use one handler that does one thing, with a nonce
and a capability check.
- The customizer's Recommended Actions section was a second copy of the
welcome screen's list. It now links to that screen rather than keeping two
implementations that can drift apart.
Plugin installation is handed to core's wp.updates rather than reimplemented.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Migration. No theme mod was renamed in 1.3.0 -- all 52 customizer settings keep their names, so saved values are read by the same code that wrote them and need no mapping. What does need carrying is the state the retired framework owned: Epsilon stored dismissed actions as an id => false map in shapely_actions_left, while the new welcome screen keeps a flat list, so without this every action a site owner had already dealt with would reappear after the update. The legacy options are read but not deleted -- they are inert, and leaving them means a downgrade to 1.2.x still finds its own state. An established site is also marked as having seen the welcome notice, since on a site that has been running for years that notice would be the one visible sign of an update meant to change nothing. Block alignments. The theme has declared add_theme_support( 'align-wide' ) for years without ever shipping CSS for the classes it enables, so choosing "Wide width" or "Full width" on a block did nothing at all. Post content sits inside a fixed-width Bootstrap .container, so the breakout uses viewport-relative margins. Bootstrap 3 also only makes images responsive via .img-responsive, which block markup never carries, so a wide image overflowed its column. All additive: content not using those alignments renders identically. Asset handles. bootstrap, flexslider, owl.carousel and owl.carousel.theme are now shapely-prefixed. These are the same latent fault as the font-awesome collision that silently blanked every icon on any site running Elementor: WordPress keeps only the first registration of a handle, so a plugin claiming 'bootstrap' first would have suppressed the theme's stylesheet entirely. jquery-masonry is deliberately unchanged -- that is a WordPress core handle and using it is correct. Child themes dequeuing the old names need updating; this is the one intentional compatibility break in 1.3.0 and the reason it lands in a major. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Editor parity: the heading scale moves into theme.json alongside the palette, so
assets/css/editor-style.css shrinks to only what theme.json has no vocabulary
for -- the flat 24px block rhythm, the mobile heading drop, and the blockquote.
Two robustness fixes found while verifying on a live install, both pre-existing:
- shapely_customize_register() assigned ->transport directly onto the result
of get_setting(). Those are core settings, but a plugin may remove any of
them, and get_setting() then returns null -- a fatal on PHP 8 rather than a
notice.
- Shapely::setup_recommended_actions() used Shapely_Notify_System without
requiring it. load_class() only runs for admin and customizer requests, so
any other caller hit an undefined class.
CLAUDE.md is rewritten where it described Epsilon, and now records the demo
import contract with the companion plugin, which is the one thing here that
cannot change without breaking something outside this repo.
Verified by upgrading the live demo from 1.2.21:
theme mods 8 before, 8 after -- none lost, none changed, including
shapely_sticky_header_transparency (the former slider)
body markup 99.95% identical across 4 page types; every difference is
an asset handle gaining its shapely- prefix
customizer 204 settings, 196 controls, 29 sections register clean
front end 5/5 pages pass, 0 blank icons, 0 broken images, 0 JS errors
One visible change, deliberate and worth stating. WordPress stops emitting
classic-theme-styles once a theme ships theme.json, so core Button blocks lose
the generic grey pill core gave them and take the theme's own button style
instead. Blocks are styled, not unstyled -- but a site using core Button blocks
will see them change from grey pills to Shapely buttons.
zip 228 files, 1.87 MB -> 116 files, 1.57 MB
php 88 files -> 51 files
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The groundwork release. Nothing a visitor sees should change; everything a
future block-editor and FSE rewrite depends on should be in place by the end
of it.
Epsilon is gone
inc/libraries/deleted — 144 files, 1.3 MB, 52% of the shipped theme. Whatit was actually providing, and what replaces it:
Epsilon_Control_Toggle×31checkboxcontrolsEpsilon_Control_Slider×1Shapely_Control_Range(~45 lines)Epsilon_Section_ProShapely_Section_LinkEpsilon_Section_Recommended_ActionsShapely_Section_Link→ welcome screenEpsilon_Welcome_Screen(850 lines)Shapely_WelcomeEpsilon_Notify_SystemShapely_Notify_Systemno longer extends it. Nothing was inherited: everyself::call resolves to a method defined in the child, the two that lookedinherited were already overridden because the parent hardcoded
ABSPATH . 'wp-content/plugins/', and no parent-only method is referencedanywhere in the theme.
A latent bug this surfaced
Every Epsilon control sat behind
class_exists()with a core fallback in theelse. Those fallbacks were never complete — two registered 6 controls wherethe Epsilon branch registered 11, and one had no
elseat all. Had Epsilon everfailed to load, these 11 controls would have silently vanished:
So the core registrations are generated from the Epsilon branch, which was the
complete one. Counts as the check that nothing was dropped:
theme.json
v2, matching the WordPress 6.4 floor. Owns the palette, type scale, heading
sizes and layout widths. The customizer stays authoritative: each colour theme
mod overrides the matching
--wp--preset--color--*property, so blocks and theclassic front end resolve to one value instead of two palettes disagreeing.
Things that were quietly broken
add_theme_support( 'align-wide' )since 2018 without ever shipping the CSSthose classes need.
responsive via
.img-responsive, which block markup never carries.bootstrap,flexslider,owl.carouselandowl.carousel.themeare now theme-prefixed — the same fault that blankedevery icon on Elementor sites in 1.2.20.
jquery-masonryis left alone; thatis a core handle.
->transporton a possibly-null setting, andsetup_recommended_actions()using a class it never required.
Security
The old welcome-screen AJAX endpoint took a class name and a method name from
POST and called
$class::$method(). It was whitelisted to one class, but thatpattern is not worth reimplementing — dismissals now run through one handler
that does one thing, with a nonce and a capability check.
Migration
No theme mod was renamed. All 52 settings keep their names, so saved values
are read by the same code that wrote them. What needed carrying is the state
Epsilon owned: dismissed actions move from
shapely_actions_left(anid => falsemap) to a flat list, or every action a site owner had already dealtwith would reappear. Legacy options are read but not deleted, so a downgrade to
1.2.x still finds its own state. Established sites are marked as having seen the
onboarding notice — on a site running for years that notice would be the one
visible sign of an update meant to change nothing.
Verified by upgrading the live demo from 1.2.21
One visible change, stated plainly
WordPress stops emitting
classic-theme-stylesonce a theme shipstheme.json.Core Button blocks therefore lose the generic grey pill core gave them and take
the theme's own button style instead. They are styled, not unstyled — but a
site using core Button blocks will see them change from grey pills to Shapely
buttons. The demo has zero Button blocks, so no impact there.
Compatibility
Child themes dequeuing
bootstrap,flexslider,owl.carouselorowl.carousel.themeneed the new handles. This is the one intentional break,and the reason it lands in a major. Documented in the readme upgrade notice.