Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion .stylelintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
"assets/css/fontawesome6/**",
"assets/css/flexslider.css",
"assets/js/**",
"inc/libraries/**",
"layouts/**"
],
"rules": {
Expand Down
12 changes: 6 additions & 6 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,12 +56,12 @@ The version lives in **four** places, all currently in sync at 1.2.20: `style.cs
Nearly all theme behavior is driven by `get_theme_mod()` reads scattered across templates. The chain:

1. `inc/customizer.php` (~1500 lines) registers the `shapely_main_options` panel and its sections/controls.
2. `inc/libraries/epsilon-framework/` — a **vendored** third-party customizer framework (Macho Themes) supplying `Epsilon_Control_Toggle`, `Epsilon_Control_Slider`, `Epsilon_Section_Pro`, `Epsilon_Section_Recommended_Actions`. `Shapely::init_epsilon()` passes a whitelist of controls/sections to load. Classes resolve through `class-epsilon-autoloader.php`, which maps `Foo_Bar` → `class-foo-bar.php` across a fixed directory list.
2. `inc/custom-controls/` — the theme's own customizer controls and sections: `Shapely_Custom_Label`, `Shapely_Logo_Dimensions`, `Shapely_Control_Range` (a range input with a value readout), and `Shapely_Section_Link` (a section rendered as a single outbound button). Everything else uses core control types. The vendored Epsilon framework that used to supply these was removed in 1.3.0.
3. `shapely_get_theme_options()` in `inc/extras.php` builds a CSS string from those theme mods. `shapely_enqueue_theme_options_css()` buffers it and hands it to `wp_add_inline_style( 'shapely-style', … )`, so it rides along with the main stylesheet rather than being echoed straight into `wp_head`.

So **adding a color/typography option means two edits**: a control in `inc/customizer.php` *and* a selector block in `shapely_get_theme_options()`. Sanitizers (`shapely_sanitize_checkbox`, `shapely_sanitize_layout`, …) live at the bottom of `customizer.php`.

The Epsilon JS bundles (`assets/js/epsilon-framework-*.js`) are webpack output from TypeScript/Vue sources in `inc/libraries/epsilon-framework/assets/vendors/` — do not hand-edit the bundles; rebuild with `npm run build` **inside `inc/libraries/epsilon-framework/`** (its own webpack setup, unrelated to the theme's `npm run build`).
`theme.json` (v2, matching the WordPress 6.4 floor) is the source of truth for the palette, type scale, heading sizes and layout widths — it is what the block editor reads. The customizer is still where a site owner changes colours: `shapely_enqueue_theme_options_css()` emits each colour theme mod as an override of the matching `--wp--preset--color--*` property, so blocks and the classic front end resolve to one value rather than two palettes disagreeing. **Adding a colour option now means three edits**: a control in `inc/customizer.php`, a selector block in `shapely_get_theme_options()`, and a preset in `theme.json` plus its entry in the `$presets` map.

### Layout resolution

Expand All @@ -80,7 +80,7 @@ Blog listings (`index.php`, `archive.php`) dispatch on the `blog_layout_view` th

- **WooCommerce**: theme support in `functions.php`, `woocommerce.php` as the wrapper, `woocommerce/product-searchform.php` as the only template override. The `shop-sidebar` widget area registers only when `shapely_is_woocommerce_activated()`. `woocommerce/` is excluded from phpcs.
- **Jetpack**: `inc/jetpack.php` plus `archive-jetpack-portfolio.php` / `single-jetpack-portfolio.php` for the portfolio CPT (masonry is enqueued only when that CPT exists).
- **Welcome screen / recommended plugins**: `inc/libraries/welcome-screen/` + `inc/class-shapely-notify-system.php` drive the "Import Demo Content" flow and the recommended-plugins list defined in `Shapely::$recommended_plugins`.
- **Welcome screen / recommended plugins**: `inc/admin/class-shapely-welcome.php` + `inc/class-shapely-notify-system.php` drive the "Import Demo Content" flow and the recommended-plugins list defined in `Shapely::$recommended_plugins`. The import itself is **not** in the theme: the button posts `shapely_companion_import_content` with a `welcome_nonce` to a handler in the shapely-companion plugin, so that action name, its `import` values and the nonce name are a fixed contract — changing any of them breaks the import against every released version of the plugin. Plugin installation is handed to core's `wp.updates`.

### Frontend JS

Expand All @@ -96,14 +96,14 @@ Every optional third-party plugin (FlexSlider, OwlCarousel, imagesLoaded, YTPlay
- Font Awesome 6 splits families: brand glyphs (`fa-github`, `fa-x-twitter`, …) need `fa-brands`, everything else uses `fa`/`fa-solid`. A brand icon rendered with plain `fa` shows a blank box. FA4 `-o` outline suffixes no longer exist. The bundled build is 6.4.2, so icons added later (e.g. `fa-bluesky`, 6.6) are unavailable.
- PHP follows WordPress-Core via `phpcs.ruleset.xml` (plus `PHPCompatibility`; `node_modules/` and `woocommerce/` excluded). Newer files open with an `ABSPATH` guard.
- JS follows `.jshintrc`: `es3`, single quotes, mandatory curly braces, `eqeqeq`. New localized objects must be added to its `globals` whitelist or jshint fails.
- Text domain is `shapely` (`epsilon-framework` is also allowed by checktextdomain). Translations live in `languages/`.
- Text domain is `shapely`, and it is the only one allowed by `npm run i18n:check`. Translations live in `languages/`.
- Supported floor: WordPress 6.4 / PHP 7.4, tested to WordPress 6.8 / PHP 8.4 (stated in `style.css`, `readme.txt`, and `functions.php`).

## Repo quirks worth knowing

- `README.md` documents a git-submodule workflow and `setup.sh` / `setup.bat` — **none of that exists**: `.gitmodules` is empty, no submodules are registered, and the Epsilon framework is vendored directly in `inc/libraries/`.
- `README.md` documents a git-submodule workflow and `setup.sh` / `setup.bat` — **none of that exists**: `.gitmodules` is empty and no submodules are registered.
- `.gitignore` used to list `Gruntfile.js`, `.jshintrc`, `.travis.yml`, `phpcs.ruleset.xml` and `package-lock.json`. The first three are gone; the last two are now deliberately tracked (`npm ci` needs the lockfile, and the phpcs ruleset is real config).
- The zip builder in `tools/build-zip.mjs` is deny-by-default: a file ships unless `EXCLUDE` matches it. The old Grunt copy task was allow-everything-then-subtract, which is how `CLAUDE.md` and a nested `package.json` ended up inside released zips.
- The vendored `inc/libraries/` code is third-party but **not** a submodule, so it is edited in place when it blocks a PHP/WordPress upgrade. Two such patches exist: optional-before-required parameters in `class-epsilon-control-section-repeater.php`, and `get_page_by_title()` (deprecated in WP 6.2) in `class-epsilon-welcome-screen.php`.
- `inc/class-shapely-migrations.php` runs one-time upgrade steps, keyed on the `shapely_migrated_version` option. No theme mod has ever been renamed, so it does not map settings — it carries the state the retired Epsilon framework owned (`shapely_actions_left` → `shapely_dismissed_actions`) and suppresses the onboarding notice on sites that were already established.
- `Shapely_Notify_System` overrides the vendored `check_plugin_is_installed()`/`check_plugin_is_active()` because the parent hardcodes `ABSPATH . 'wp-content/plugins/'` and breaks on relocated content directories.
- `layouts/content-sidebar.css` and `layouts/sidebar-content.css` are leftovers from Underscores and are not enqueued anywhere.
75 changes: 17 additions & 58 deletions assets/css/editor-style.css
Original file line number Diff line number Diff line change
@@ -1,88 +1,47 @@
/*
* Block editor styles.
*
* Mirrors the front-end typography from style.css so that what is written in
* the editor resembles what the visitor sees. Values are copied from the
* corresponding front-end rules rather than reinvented -- when style.css
* changes, this file needs the same change.
* Deliberately short. theme.json now carries the palette, the type scale, the
* heading sizes and the link and button treatments, and WordPress applies those
* to the editor canvas on its own -- so anything expressible there has been
* removed from here rather than maintained in two places that can drift.
*
* Scoped by the editor itself: WordPress rewrites these selectors to apply
* inside the editor canvas only, so plain element selectors are correct here
* and must not be prefixed by hand.
* What is left is the handful of front-end rules from style.css that theme.json
* has no vocabulary for.
*/

.editor-styles-wrapper {
font-family: Raleway, 'Helvetica Neue', Helvetica, Arial, sans-serif;
font-size: 14px;
font-weight: 400;
line-height: 24px;
color: #8c979e;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}

/*
* theme.json sets the body font size, but not the reading rhythm the front end
* uses: style.css gives every block-level element a flat 24px bottom margin
* rather than the editor's default spacing.
*/
.editor-styles-wrapper p,
.editor-styles-wrapper ul,
.editor-styles-wrapper ol,
.editor-styles-wrapper table,
.editor-styles-wrapper blockquote {
margin-top: 0;
margin-bottom: 24px;
}

.editor-styles-wrapper blockquote,
.editor-styles-wrapper h1,
.editor-styles-wrapper h2,
.editor-styles-wrapper h3,
.editor-styles-wrapper h4,
.editor-styles-wrapper h5,
.editor-styles-wrapper h6 {
font-family: inherit;
font-weight: 400;
color: #0e1015;
margin-top: 0;
margin-bottom: 24px;
}

.editor-styles-wrapper h1 {
font-size: 60px;
}

.editor-styles-wrapper h2 {
font-size: 45px;
}

.editor-styles-wrapper h3 {
font-size: 30px;
}

.editor-styles-wrapper h4 {
font-size: 25px;
}

.editor-styles-wrapper h5 {
font-size: 22px;
line-height: 28px;
}

.editor-styles-wrapper h6 {
font-size: 18px;
line-height: 25px;
}

/* The front end drops heading sizes sharply on small screens; match it so the
editor's mobile preview is not wildly larger than the real thing. */
/*
* Headings drop sharply on narrow screens on the front end. Without this the
* editor's mobile preview shows a 60px h1 where the visitor sees 32px.
*/
@media all and (max-width: 767px) {

.editor-styles-wrapper h1 {
font-size: 32px;
}
}

.editor-styles-wrapper a {
color: #745cf9;
font-weight: 600;
}

/* style.css blockquote: no rule, larger type, its own colour. */
.editor-styles-wrapper blockquote {
overflow: hidden;
padding: 32px 0;
Expand Down
139 changes: 139 additions & 0 deletions assets/css/welcome.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
/*
* Shapely welcome screen.
*
* Deliberately plain: this is a WordPress admin page, so it borrows core's
* type and colour rather than introducing a second visual language inside
* wp-admin. Replaces welcome-screen/css/welcome.css from the Epsilon bundle.
*/

.shapely-welcome__title {
margin-bottom: 0.2em;
}

.shapely-welcome__intro {
max-width: 60em;
margin-top: 0;
color: #50575e;
font-size: 14px;
}

.shapely-welcome__tabs {
margin-bottom: 1.5em;
}

.shapely-welcome__count {
display: inline-block;
min-width: 1.6em;
padding: 0 0.4em;
border-radius: 9px;
background: #d63638;
color: #fff;
font-size: 11px;
line-height: 1.6;
text-align: center;
}

/* ---- card grid ---- */

.shapely-cards {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
gap: 20px;
max-width: 1100px;
}

.shapely-card {
padding: 20px;
border: 1px solid #dcdcde;
background: #fff;
border-radius: 4px;
}

.shapely-card h3 {
margin-top: 0;
}

.shapely-card p:last-child {
margin-bottom: 0;
}

/* ---- recommended actions ---- */

.shapely-actions {
max-width: 800px;
margin: 0;
padding: 0;
list-style: none;
}

.shapely-action {
position: relative;
margin: 0 0 16px;
padding: 20px 48px 20px 20px;
border: 1px solid #dcdcde;
background: #fff;
border-radius: 4px;
}

.shapely-action h3 {
margin-top: 0;
}

.shapely-action__dismiss {
position: absolute;
top: 12px;
right: 12px;
padding: 0;
border: 0;
background: none;
color: #a7aaad;
cursor: pointer;
line-height: 1;
}

.shapely-action__dismiss:hover,
.shapely-action__dismiss:focus {
color: #d63638;
}

.shapely-action__done {
color: #008a20;
font-weight: 600;
}

.shapely-action__error {
color: #d63638;
}

.shapely-empty {
max-width: 800px;
padding: 20px;
border: 1px solid #dcdcde;
background: #fff;
border-radius: 4px;
}

/* ---- import options ---- */

.import-content-container {
display: none;
margin-top: 12px;
padding-top: 12px;
border-top: 1px solid #f0f0f1;
}

.import-content-container h4 {
margin: 12px 0 6px;
}

.checkbox-group {
display: flex;
flex-wrap: wrap;
gap: 6px 20px;
}

.checkbox-group label {
display: inline-flex;
align-items: center;
gap: 6px;
}
Loading
Loading