Skip to content

Release 1.3.0 — retire Epsilon, add theme.json, clear the ground for blocks - #354

Merged
puikinsh merged 4 commits into
masterfrom
release/1.3.0
Aug 12, 2026
Merged

Release 1.3.0 — retire Epsilon, add theme.json, clear the ground for blocks#354
puikinsh merged 4 commits into
masterfrom
release/1.3.0

Conversation

@puikinsh

Copy link
Copy Markdown
Member

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.

1.2.21 1.3.0
files in zip 228 116
download 1.87 MB 1.57 MB
PHP files 88 51
Theme Check INFO items ~17 3

Epsilon is gone

inc/libraries/ deleted — 144 files, 1.3 MB, 52% of the shipped theme. What
it was actually providing, and what replaces it:

was now
Epsilon_Control_Toggle ×31 core checkbox controls
Epsilon_Control_Slider ×1 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 it. 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.

A latent bug this surfaced

Every Epsilon control sat behind class_exists() with a core fallback in the
else. Those fallbacks were never complete — two registered 6 controls where
the Epsilon branch registered 11, and one had no else at all. 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

So the core registrations are generated from the Epsilon branch, which was the
complete one. Counts as the check that nothing was dropped:

add_setting  52 -> 52
add_control  74 -> 52   (22 duplicate registrations from else branches removed)

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 the
classic front end resolve to one value instead of two palettes disagreeing.

Things that were quietly broken

  • "Wide width" and "Full width" did nothing. The theme has declared
    add_theme_support( 'align-wide' ) since 2018 without ever shipping the CSS
    those classes need.
  • Images in post content overflowed. Bootstrap 3 only makes images
    responsive via .img-responsive, which block markup never carries.
  • Generic asset handles. bootstrap, flexslider, owl.carousel and
    owl.carousel.theme are now theme-prefixed — the same fault that blanked
    every icon on Elementor sites in 1.2.20. jquery-masonry is left alone; that
    is a core handle.
  • ->transport on a possibly-null setting, and setup_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 that
pattern 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 (an
id => false map) to a flat list, or every action a site owner had already dealt
with 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

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 single 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, stated plainly

WordPress stops emitting classic-theme-styles once a theme ships theme.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.carousel or
owl.carousel.theme need the new handles. This is the one intentional break,
and the reason it lands in a major. Documented in the readme upgrade notice.

puikinsh and others added 4 commits August 12, 2026 14:57
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>
@puikinsh
puikinsh merged commit 9330880 into master Aug 12, 2026
10 checks passed
@puikinsh
puikinsh deleted the release/1.3.0 branch August 12, 2026 12:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant