diff --git a/CHANGELOG.md b/CHANGELOG.md
index 30c3aac..b7dc5a4 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -11,14 +11,23 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Added
- Added a Style Switcher block with selectable theme style variations and configurable icon display behaviour.
+- Added a Button Icon selector panel for core Button blocks, including left/right positioning and up/down icon options.
+- Added a Back to Top option as a `core/button` variation so users inherit native Button styling controls and icon compatibility.
+- Added smooth scrolling support for Back to Top button clicks and internal anchor links using vanilla JavaScript.
### Changed
+- Changed Back to Top implementation from a standalone custom block to a `core/button` variation.
+- Changed Back to Top frontend targeting to use a dedicated wrapper class (`is-back-to-top`) for reliable JS and CSS behaviour.
+- Changed Back to Top visibility to always display (removed scroll-threshold hide/show behaviour).
### Deprecated
### Removed
+- Removed the standalone Back to Top block source in favour of variation-based implementation.
### Fixed
+- Fixed editor runtime errors from invalid React component handling in Back to Top editor integration.
+- Fixed strict mode error in Back to Top animation loop by replacing `arguments.callee` with a named animation step.
### Security
diff --git a/README.md b/README.md
index 957d31b..ad277aa 100644
--- a/README.md
+++ b/README.md
@@ -112,6 +112,16 @@ composer run phpcs
---
+## Back to Top variation (how to use)
+
+1. In the editor, insert a Button block.
+2. Select the Back to Top variation from block styles/variations.
+3. Publish or update the page.
+
+When clicked on the frontend, the button smoothly scrolls to the top of the page.
+
+---
+
## AI workflows
| Folder | Purpose |
diff --git a/build/css/back-to-top.asset.php b/build/css/back-to-top.asset.php
new file mode 100644
index 0000000..941e541
--- /dev/null
+++ b/build/css/back-to-top.asset.php
@@ -0,0 +1 @@
+ array(), 'version' => 'd4bf1d0478307697fd97');
diff --git a/build/css/button-icon.asset.php b/build/css/button-icon.asset.php
new file mode 100644
index 0000000..141d624
--- /dev/null
+++ b/build/css/button-icon.asset.php
@@ -0,0 +1 @@
+ array(), 'version' => 'ee8a3fb648b3838658c7');
diff --git a/build/css/style-back-to-top-rtl.css b/build/css/style-back-to-top-rtl.css
new file mode 100644
index 0000000..694df9a
--- /dev/null
+++ b/build/css/style-back-to-top-rtl.css
@@ -0,0 +1 @@
+.wp-block-button.is-back-to-top[data-back-to-top-mode=fixed]{bottom:2rem;position:fixed;left:2rem;z-index:40}@media(max-width:640px){.wp-block-button.is-back-to-top[data-back-to-top-mode=fixed]{bottom:1rem;left:1rem}}.wp-block-button.is-back-to-top .wp-block-button__link{transition:all .2s ease-in-out}.wp-block-button.is-back-to-top .wp-block-button__link:hover{box-shadow:0 4px 6px -1px rgba(0,0,0,.1),0 2px 4px -2px rgba(0,0,0,.1);transform:translateY(-2px)}.wp-block-button.is-back-to-top .wp-block-button__link:active{transform:translateY(0)}@media(prefers-reduced-motion:reduce){.wp-block-button.is-back-to-top .wp-block-button__link{transition:none}.wp-block-button.is-back-to-top .wp-block-button__link:hover{transform:none}}
diff --git a/build/css/style-back-to-top.css b/build/css/style-back-to-top.css
new file mode 100644
index 0000000..35ecbab
--- /dev/null
+++ b/build/css/style-back-to-top.css
@@ -0,0 +1 @@
+.wp-block-button.is-back-to-top[data-back-to-top-mode=fixed]{bottom:2rem;position:fixed;right:2rem;z-index:40}@media(max-width:640px){.wp-block-button.is-back-to-top[data-back-to-top-mode=fixed]{bottom:1rem;right:1rem}}.wp-block-button.is-back-to-top .wp-block-button__link{transition:all .2s ease-in-out}.wp-block-button.is-back-to-top .wp-block-button__link:hover{box-shadow:0 4px 6px -1px rgba(0,0,0,.1),0 2px 4px -2px rgba(0,0,0,.1);transform:translateY(-2px)}.wp-block-button.is-back-to-top .wp-block-button__link:active{transform:translateY(0)}@media(prefers-reduced-motion:reduce){.wp-block-button.is-back-to-top .wp-block-button__link{transition:none}.wp-block-button.is-back-to-top .wp-block-button__link:hover{transform:none}}
diff --git a/build/css/style-button-icon-rtl.css b/build/css/style-button-icon-rtl.css
new file mode 100644
index 0000000..ad02bd4
--- /dev/null
+++ b/build/css/style-button-icon-rtl.css
@@ -0,0 +1 @@
+.wp-block-button.has-ls-button-icon .wp-block-button__link{align-items:center;display:inline-flex;position:relative}.wp-block-button.has-ls-button-icon .wp-block-button__link:after,.wp-block-button.has-ls-button-icon.has-ls-button-icon-position-left .wp-block-button__link:before{color:var(--ls-button-fill-icon-colour,currentColor);display:grid;font-size:var(--ls-button-fill-icon-font-size);font-weight:700;inset-block:var(--ls-button-fill-icon-block-inset);line-height:1;place-items:center;position:absolute;transition:color .5s cubic-bezier(.4,0,.2,1);width:var(--ls-button-fill-icon-well-size)}.wp-block-button.has-ls-button-icon .wp-block-button__link:after{content:"→";inset-inline-end:var(--ls-button-fill-icon-inline-end)}.wp-block-button.has-ls-button-icon.has-ls-button-icon-position-left .wp-block-button__link:before{content:"→";inset-inline-start:var(--ls-button-fill-icon-inline-end)}.wp-block-button.has-ls-button-icon.has-ls-button-icon-position-left .wp-block-button__link:after{content:none}.wp-block-button.has-ls-button-icon-arrow-right .wp-block-button__link:after,.wp-block-button.has-ls-button-icon-arrow-right.has-ls-button-icon-position-left .wp-block-button__link:before{content:"→"!important}.wp-block-button.has-ls-button-icon-arrow-left .wp-block-button__link:after,.wp-block-button.has-ls-button-icon-arrow-left.has-ls-button-icon-position-left .wp-block-button__link:before{content:"←"!important}.wp-block-button.has-ls-button-icon-chevron-right .wp-block-button__link:after,.wp-block-button.has-ls-button-icon-chevron-right.has-ls-button-icon-position-left .wp-block-button__link:before{content:"›"!important}.wp-block-button.has-ls-button-icon-arrow-up .wp-block-button__link:after,.wp-block-button.has-ls-button-icon-arrow-up.has-ls-button-icon-position-left .wp-block-button__link:before{content:"↑"!important}.wp-block-button.has-ls-button-icon-arrow-down .wp-block-button__link:after,.wp-block-button.has-ls-button-icon-arrow-down.has-ls-button-icon-position-left .wp-block-button__link:before{content:"↓"!important}.wp-block-button.has-ls-button-icon-external .wp-block-button__link:after,.wp-block-button.has-ls-button-icon-external.has-ls-button-icon-position-left .wp-block-button__link:before{content:"↗"!important}
diff --git a/build/css/style-button-icon.css b/build/css/style-button-icon.css
new file mode 100644
index 0000000..ad02bd4
--- /dev/null
+++ b/build/css/style-button-icon.css
@@ -0,0 +1 @@
+.wp-block-button.has-ls-button-icon .wp-block-button__link{align-items:center;display:inline-flex;position:relative}.wp-block-button.has-ls-button-icon .wp-block-button__link:after,.wp-block-button.has-ls-button-icon.has-ls-button-icon-position-left .wp-block-button__link:before{color:var(--ls-button-fill-icon-colour,currentColor);display:grid;font-size:var(--ls-button-fill-icon-font-size);font-weight:700;inset-block:var(--ls-button-fill-icon-block-inset);line-height:1;place-items:center;position:absolute;transition:color .5s cubic-bezier(.4,0,.2,1);width:var(--ls-button-fill-icon-well-size)}.wp-block-button.has-ls-button-icon .wp-block-button__link:after{content:"→";inset-inline-end:var(--ls-button-fill-icon-inline-end)}.wp-block-button.has-ls-button-icon.has-ls-button-icon-position-left .wp-block-button__link:before{content:"→";inset-inline-start:var(--ls-button-fill-icon-inline-end)}.wp-block-button.has-ls-button-icon.has-ls-button-icon-position-left .wp-block-button__link:after{content:none}.wp-block-button.has-ls-button-icon-arrow-right .wp-block-button__link:after,.wp-block-button.has-ls-button-icon-arrow-right.has-ls-button-icon-position-left .wp-block-button__link:before{content:"→"!important}.wp-block-button.has-ls-button-icon-arrow-left .wp-block-button__link:after,.wp-block-button.has-ls-button-icon-arrow-left.has-ls-button-icon-position-left .wp-block-button__link:before{content:"←"!important}.wp-block-button.has-ls-button-icon-chevron-right .wp-block-button__link:after,.wp-block-button.has-ls-button-icon-chevron-right.has-ls-button-icon-position-left .wp-block-button__link:before{content:"›"!important}.wp-block-button.has-ls-button-icon-arrow-up .wp-block-button__link:after,.wp-block-button.has-ls-button-icon-arrow-up.has-ls-button-icon-position-left .wp-block-button__link:before{content:"↑"!important}.wp-block-button.has-ls-button-icon-arrow-down .wp-block-button__link:after,.wp-block-button.has-ls-button-icon-arrow-down.has-ls-button-icon-position-left .wp-block-button__link:before{content:"↓"!important}.wp-block-button.has-ls-button-icon-external .wp-block-button__link:after,.wp-block-button.has-ls-button-icon-external.has-ls-button-icon-position-left .wp-block-button__link:before{content:"↗"!important}
diff --git a/build/js/back-to-top-view.asset.php b/build/js/back-to-top-view.asset.php
new file mode 100644
index 0000000..e436f46
--- /dev/null
+++ b/build/js/back-to-top-view.asset.php
@@ -0,0 +1 @@
+ array(), 'version' => '84a35a129b8cf8105805');
diff --git a/build/js/back-to-top-view.js b/build/js/back-to-top-view.js
new file mode 100644
index 0000000..a5110dd
--- /dev/null
+++ b/build/js/back-to-top-view.js
@@ -0,0 +1 @@
+(()=>{"use strict";!function(){const t=window.matchMedia("(prefers-reduced-motion: reduce)").matches,e=(e,n=600)=>{if(t)return void("number"==typeof e?window.scrollTo(0,e):e instanceof HTMLElement&&e.scrollIntoView());const o=window.scrollY,r=("number"==typeof e?e:e.getBoundingClientRect().top+o-(()=>{const t=document.querySelector('header[sticky="true"], [data-sticky="true"], .is-sticky');if(!t)return 0;const e=t.getBoundingClientRect();return Math.max(0,e.height+20)})())-o,c=performance.now(),i=t=>{const e=t-c,a=Math.min(e/n,1),s=o+r*(t=>t<.5?4*t*t*t:(t-1)*(2*t-2)*(2*t-2)+1)(a);window.scrollTo(0,s),a<1&&requestAnimationFrame(i)};requestAnimationFrame(i)};var n;n=()=>{document.addEventListener("click",t=>{const n=t.target.closest('a[href*="#"]');if(!n||n.hostname!==window.location.hostname||n.pathname!==window.location.pathname)return;const o=n.getAttribute("href"),r=o.substring(o.indexOf("#"));if("#"===r||""===r)return;const c=document.querySelector(r);c&&(t.preventDefault(),e(c),window.history.pushState(null,"",r))}),document.querySelectorAll(".wp-block-button.is-back-to-top").forEach(n=>{const o=n.querySelector(".wp-block-button__link");o&&o.addEventListener("click",n=>{n.preventDefault(),e(0,t?0:600)})})},"loading"===document.readyState||"interactive"===document.readyState?document.addEventListener("DOMContentLoaded",n):n()}()})();
\ No newline at end of file
diff --git a/build/js/back-to-top.asset.php b/build/js/back-to-top.asset.php
new file mode 100644
index 0000000..e1920d0
--- /dev/null
+++ b/build/js/back-to-top.asset.php
@@ -0,0 +1 @@
+ array('wp-blocks', 'wp-hooks', 'wp-i18n'), 'version' => '9d676336fdab1bdceaa8');
diff --git a/build/js/back-to-top.js b/build/js/back-to-top.js
new file mode 100644
index 0000000..ebf3889
--- /dev/null
+++ b/build/js/back-to-top.js
@@ -0,0 +1 @@
+(()=>{"use strict";const o=window.wp.hooks,t=window.wp.blocks,e=window.wp.i18n;(0,t.registerBlockVariation)("core/button",{name:"back-to-top",title:(0,e.__)("Back to Top","ls-plugin"),icon:"arrow-up",description:(0,e.__)("A button that scrolls to the top of the page with smooth animation.","ls-plugin"),attributes:{text:(0,e.__)("Back to Top","ls-plugin"),isBackToTop:!0,backToTopPositionMode:"scroll",backToTopScrollThreshold:50},isActive:o=>!0===o.isBackToTop}),(0,o.addFilter)("blocks.registerBlockType","ls-plugin/add-back-to-top-attributes",o=>"core/button"!==o.name?o:{...o,attributes:{...o.attributes,isBackToTop:{type:"boolean",default:!1},backToTopPositionMode:{type:"string",default:"scroll"},backToTopScrollThreshold:{type:"number",default:50}}}),(0,o.addFilter)("blocks.getSaveContent.extraProps","ls-plugin/back-to-top-save-props",(o,t,e)=>{if("core/button"!==t.name||!e.isBackToTop)return o;const a=[o.className,"is-back-to-top"].filter(Boolean).join(" ");return{...o,className:a,"data-back-to-top-mode":e.backToTopPositionMode||"scroll"}})})();
\ No newline at end of file
diff --git a/build/js/button-icon.asset.php b/build/js/button-icon.asset.php
new file mode 100644
index 0000000..ed79d89
--- /dev/null
+++ b/build/js/button-icon.asset.php
@@ -0,0 +1 @@
+ array('react-jsx-runtime', 'wp-block-editor', 'wp-components', 'wp-compose', 'wp-element', 'wp-hooks', 'wp-i18n'), 'version' => 'd7bb9b5a3d9658d15028');
diff --git a/build/js/button-icon.js b/build/js/button-icon.js
new file mode 100644
index 0000000..d7c589d
--- /dev/null
+++ b/build/js/button-icon.js
@@ -0,0 +1 @@
+(()=>{"use strict";const n=window.wp.hooks,t=window.wp.compose,o=window.wp.blockEditor,l=window.wp.components,e=window.wp.element,i=window.wp.i18n,s=window.ReactJSXRuntime;function r(){return(0,s.jsx)(l.SVG,{width:"20",height:"20",viewBox:"0 0 24 24",fill:"none",children:(0,s.jsx)(l.Path,{fill:"currentColor",d:"M4 11h12.17l-3.58-3.59L14 6l6 6-6 6-1.41-1.41L16.17 13H4v-2z"})})}function c(){return(0,s.jsx)(l.SVG,{width:"20",height:"20",viewBox:"0 0 24 24",fill:"none",children:(0,s.jsx)(l.Path,{fill:"currentColor",d:"M20 11H7.83l3.58-3.59L10 6l-6 6 6 6 1.41-1.41L7.83 13H20v-2z"})})}function u(){return(0,s.jsx)(l.SVG,{width:"20",height:"20",viewBox:"0 0 24 24",fill:"none",children:(0,s.jsx)(l.Path,{fill:"currentColor",d:"m10 6 6 6-6 6-1.41-1.41L13.17 12 8.59 7.41 10 6z"})})}function a(){return(0,s.jsx)(l.SVG,{width:"20",height:"20",viewBox:"0 0 24 24",fill:"none",children:(0,s.jsx)(l.Path,{fill:"currentColor",d:"M11 20V7.83l-3.59 3.58L6 10l6-6 6 6-1.41 1.41L13 7.83V20h-2z"})})}function h(){return(0,s.jsx)(l.SVG,{width:"20",height:"20",viewBox:"0 0 24 24",fill:"none",children:(0,s.jsx)(l.Path,{fill:"currentColor",d:"M11 4v12.17l-3.59-3.58L6 14l6 6 6-6-1.41-1.41L13 16.17V4h-2z"})})}function d(){return(0,s.jsx)(l.SVG,{width:"20",height:"20",viewBox:"0 0 24 24",fill:"none",children:(0,s.jsx)(l.Path,{fill:"currentColor",d:"M14 3h7v7h-2V6.41l-9.29 9.3-1.42-1.42 9.3-9.29H14V3zM5 5h6v2H7v10h10v-4h2v6H5V5z"})})}const w=[{label:(0,i.__)("Arrow Right","ls-plugin"),value:"arrow-right",char:"→",icon:(0,s.jsx)(r,{})},{label:(0,i.__)("Arrow Left","ls-plugin"),value:"arrow-left",char:"←",icon:(0,s.jsx)(c,{})},{label:(0,i.__)("Chevron Right","ls-plugin"),value:"chevron-right",char:"›",icon:(0,s.jsx)(u,{})},{label:(0,i.__)("Arrow Up","ls-plugin"),value:"arrow-up",char:"↑",icon:(0,s.jsx)(a,{})},{label:(0,i.__)("Arrow Down","ls-plugin"),value:"arrow-down",char:"↓",icon:(0,s.jsx)(h,{})},{label:(0,i.__)("External Link","ls-plugin"),value:"external",char:"↗",icon:(0,s.jsx)(d,{})}];function x(...n){return n.filter(Boolean).join(" ")}(0,n.addFilter)("blocks.registerBlockType","ls-plugin/button-icons/add-attributes",function(n){return"core/button"!==n.name?n:{...n,attributes:{...n.attributes,lsButtonIcon:{type:"string"},lsButtonIconPositionLeft:{type:"boolean",default:!1}}}}),(0,n.addFilter)("editor.BlockEdit","ls-plugin/button-icons/add-inspector-controls",function(n){return t=>{if("core/button"!==t.name)return(0,s.jsx)(n,{...t});const{attributes:r,setAttributes:c}=t,{lsButtonIcon:u,lsButtonIconPositionLeft:a}=r;return(0,s.jsxs)(e.Fragment,{children:[(0,s.jsx)(n,{...t}),(0,s.jsx)(o.InspectorControls,{children:(0,s.jsxs)(l.PanelBody,{title:(0,i.__)("Button Icon","ls-plugin"),className:"ls-plugin-button-icon-picker",initialOpen:!0,children:[(0,s.jsx)(l.PanelRow,{children:(0,s.jsx)(l.__experimentalGrid,{columns:"4",gap:"8px",children:w.map(n=>(0,s.jsx)(l.Button,{label:n.label,isPressed:u===n.value,onClick:()=>c({lsButtonIcon:u===n.value?void 0:n.value}),children:n.icon},n.value))})}),u&&(0,s.jsx)(l.PanelRow,{children:(0,s.jsx)(l.ToggleControl,{label:(0,i.__)("Show icon on left","ls-plugin"),checked:a,onChange:()=>c({lsButtonIconPositionLeft:!a})})})]})})]})}});const p=(0,t.createHigherOrderComponent)(n=>t=>{const{name:o,attributes:l}=t;if("core/button"!==o||!l?.lsButtonIcon)return(0,s.jsx)(n,{...t});const e=x(t.className,"has-ls-button-icon",l.lsButtonIcon?`has-ls-button-icon-${l.lsButtonIcon}`:"",l.lsButtonIconPositionLeft?"has-ls-button-icon-position-left":"");return(0,s.jsx)(n,{...t,className:e})},"addClasses");(0,n.addFilter)("editor.BlockListBlock","ls-plugin/button-icons/add-classes",p),(0,n.addFilter)("blocks.getSaveContent.extraProps","ls-plugin/button-icons/add-save-props",function(n,t,o){return"core/button"===t.name&&o?.lsButtonIcon?{...n,className:x(n.className,"has-ls-button-icon",o.lsButtonIcon?`has-ls-button-icon-${o.lsButtonIcon}`:"",o.lsButtonIconPositionLeft?"has-ls-button-icon-position-left":"")}:n})})();
\ No newline at end of file
diff --git a/ls-plugin.php b/ls-plugin.php
index 66f6994..38207bf 100644
--- a/ls-plugin.php
+++ b/ls-plugin.php
@@ -50,3 +50,121 @@ function ls_plugin_init() {
$style_switcher->register_hooks();
}
add_action( 'plugins_loaded', 'ls_plugin_init' );
+
+/**
+ * Enqueues Button Icon editor assets.
+ *
+ * @return void
+ */
+function ls_plugin_enqueue_button_icon_editor_assets() {
+ $asset_path = LS_PLUGIN_PLUGIN_DIR . 'build/js/button-icon.asset.php';
+
+ if ( ! file_exists( $asset_path ) ) {
+ return;
+ }
+
+ $asset = include $asset_path;
+
+ wp_enqueue_script(
+ 'ls-plugin-button-icon',
+ LS_PLUGIN_PLUGIN_URL . 'build/js/button-icon.js',
+ $asset['dependencies'] ?? array(),
+ $asset['version'] ?? LS_PLUGIN_VERSION,
+ true
+ );
+}
+add_action( 'enqueue_block_editor_assets', 'ls_plugin_enqueue_button_icon_editor_assets' );
+
+/**
+ * Enqueues Button Icon shared styles for front end and editor.
+ *
+ * @return void
+ */
+function ls_plugin_enqueue_button_icon_styles() {
+ $asset_path = LS_PLUGIN_PLUGIN_DIR . 'build/css/button-icon.asset.php';
+
+ if ( ! file_exists( $asset_path ) ) {
+ return;
+ }
+
+ $asset = include $asset_path;
+
+ wp_enqueue_style(
+ 'ls-plugin-button-icon',
+ LS_PLUGIN_PLUGIN_URL . 'build/css/style-button-icon.css',
+ $asset['dependencies'] ?? array(),
+ $asset['version'] ?? LS_PLUGIN_VERSION
+ );
+}
+add_action( 'enqueue_block_assets', 'ls_plugin_enqueue_button_icon_styles' );
+
+/**
+ * Enqueues Back to Top variation editor assets.
+ *
+ * @return void
+ */
+function ls_plugin_enqueue_back_to_top_editor_assets() {
+ $asset_path = LS_PLUGIN_PLUGIN_DIR . 'build/js/back-to-top.asset.php';
+
+ if ( ! file_exists( $asset_path ) ) {
+ return;
+ }
+
+ $asset = include $asset_path;
+
+ wp_enqueue_script(
+ 'ls-plugin-back-to-top',
+ LS_PLUGIN_PLUGIN_URL . 'build/js/back-to-top.js',
+ $asset['dependencies'] ?? array(),
+ $asset['version'] ?? LS_PLUGIN_VERSION,
+ true
+ );
+}
+add_action( 'enqueue_block_editor_assets', 'ls_plugin_enqueue_back_to_top_editor_assets' );
+
+/**
+ * Enqueues Back to Top view script for smooth scrolling on front end.
+ *
+ * @return void
+ */
+function ls_plugin_enqueue_back_to_top_view_script() {
+ $asset_path = LS_PLUGIN_PLUGIN_DIR . 'build/js/back-to-top-view.asset.php';
+
+ if ( ! file_exists( $asset_path ) ) {
+ return;
+ }
+
+ $asset = include $asset_path;
+
+ wp_enqueue_script(
+ 'ls-plugin-back-to-top-view',
+ LS_PLUGIN_PLUGIN_URL . 'build/js/back-to-top-view.js',
+ $asset['dependencies'] ?? array(),
+ $asset['version'] ?? LS_PLUGIN_VERSION,
+ true
+ );
+}
+add_action( 'wp_enqueue_scripts', 'ls_plugin_enqueue_back_to_top_view_script' );
+
+/**
+ * Enqueues Back to Top shared styles for front end and editor.
+ *
+ * @return void
+ */
+function ls_plugin_enqueue_back_to_top_styles() {
+ $asset_path = LS_PLUGIN_PLUGIN_DIR . 'build/css/back-to-top.asset.php';
+
+ if ( ! file_exists( $asset_path ) ) {
+ return;
+ }
+
+ $asset = include $asset_path;
+
+ wp_enqueue_style(
+ 'ls-plugin-back-to-top',
+ LS_PLUGIN_PLUGIN_URL . 'build/css/style-back-to-top.css',
+ $asset['dependencies'] ?? array(),
+ $asset['version'] ?? LS_PLUGIN_VERSION
+ );
+}
+add_action( 'enqueue_block_assets', 'ls_plugin_enqueue_back_to_top_styles' );
diff --git a/src/plugins/back-to-top/index.js b/src/plugins/back-to-top/index.js
new file mode 100644
index 0000000..53cdcef
--- /dev/null
+++ b/src/plugins/back-to-top/index.js
@@ -0,0 +1,88 @@
+/**
+ * Back to Top Variation for core/button
+ * Registers a "Back to Top" variation of the core Button block with smooth scrolling.
+ */
+
+import { addFilter } from '@wordpress/hooks';
+import { registerBlockVariation } from '@wordpress/blocks';
+import { __ } from '@wordpress/i18n';
+
+/**
+ * Register Back to Top as a core/button variation
+ */
+registerBlockVariation( 'core/button', {
+ name: 'back-to-top',
+ title: __( 'Back to Top', 'ls-plugin' ),
+ icon: 'arrow-up',
+ description: __(
+ 'A button that scrolls to the top of the page with smooth animation.',
+ 'ls-plugin'
+ ),
+ attributes: {
+ text: __( 'Back to Top', 'ls-plugin' ),
+ isBackToTop: true,
+ backToTopPositionMode: 'scroll',
+ backToTopScrollThreshold: 50,
+ },
+ isActive: ( blockAttributes ) => blockAttributes.isBackToTop === true,
+} );
+
+/**
+ * Add back-to-top attributes to core/button
+ */
+addFilter(
+ 'blocks.registerBlockType',
+ 'ls-plugin/add-back-to-top-attributes',
+ ( settings ) => {
+ if ( settings.name !== 'core/button' ) {
+ return settings;
+ }
+
+ return {
+ ...settings,
+ attributes: {
+ ...settings.attributes,
+ isBackToTop: {
+ type: 'boolean',
+ default: false,
+ },
+ backToTopPositionMode: {
+ type: 'string',
+ default: 'scroll',
+ },
+ backToTopScrollThreshold: {
+ type: 'number',
+ default: 50,
+ },
+ },
+ };
+ }
+);
+
+/**
+ * Add back-to-top inspector controls and editor classes
+ * We don't need to override BlockEdit - the attributes filter handles everything
+ */
+
+/**
+ * Add back-to-top data attributes to saved button markup
+ */
+addFilter(
+ 'blocks.getSaveContent.extraProps',
+ 'ls-plugin/back-to-top-save-props',
+ ( extraProps, blockType, attributes ) => {
+ if ( blockType.name !== 'core/button' || ! attributes.isBackToTop ) {
+ return extraProps;
+ }
+
+ const classes = [ extraProps.className, 'is-back-to-top' ]
+ .filter( Boolean )
+ .join( ' ' );
+
+ return {
+ ...extraProps,
+ className: classes,
+ 'data-back-to-top-mode': attributes.backToTopPositionMode || 'scroll',
+ };
+ }
+);
diff --git a/src/plugins/back-to-top/style.css b/src/plugins/back-to-top/style.css
new file mode 100644
index 0000000..3cd0049
--- /dev/null
+++ b/src/plugins/back-to-top/style.css
@@ -0,0 +1,41 @@
+/**
+ * Back to Top Button Styles
+ * Minimal styling for Back to Top variation of core/button
+ * Handles positioning modes and visibility transitions
+ */
+/* Back to Top button specific positioning */
+.wp-block-button.is-back-to-top {
+ /* Fixed mode (sticky to footer) */
+ /* Smooth transitions for hover state */
+}
+.wp-block-button.is-back-to-top[data-back-to-top-mode=fixed] {
+ position: fixed;
+ bottom: 2rem;
+ right: 2rem;
+ z-index: 40;
+}
+@media (max-width: 640px) {
+ .wp-block-button.is-back-to-top[data-back-to-top-mode=fixed] {
+ bottom: 1rem;
+ right: 1rem;
+ }
+}
+.wp-block-button.is-back-to-top .wp-block-button__link {
+ transition: all 0.2s ease-in-out;
+}
+.wp-block-button.is-back-to-top .wp-block-button__link:hover {
+ transform: translateY(-2px);
+ box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -2px rgba(0, 0, 0, 0.1);
+}
+.wp-block-button.is-back-to-top .wp-block-button__link:active {
+ transform: translateY(0);
+}
+@media (prefers-reduced-motion: reduce) {
+ .wp-block-button.is-back-to-top .wp-block-button__link {
+ transition: none;
+ }
+ .wp-block-button.is-back-to-top .wp-block-button__link:hover {
+ transform: none;
+ }
+}
+/*# sourceMappingURL=style.css.map */
\ No newline at end of file
diff --git a/src/plugins/back-to-top/style.css.map b/src/plugins/back-to-top/style.css.map
new file mode 100644
index 0000000..9d3620c
--- /dev/null
+++ b/src/plugins/back-to-top/style.css.map
@@ -0,0 +1 @@
+{"version":3,"sources":["style.scss","style.css"],"names":[],"mappings":"AAAA;;;;EAAA;AAMA,4CAAA;AACA;EACC,kCAAA;EAaA,uCAAA;ACZD;ADAC;EACC,eAAA;EACA,YAAA;EACA,WAAA;EACA,WAAA;ACEF;ADAE;EAND;IAOE,YAAA;IACA,WAAA;ECGD;AACF;ADCC;EACC,gCAAA;ACCF;ADCE;EACC,2BAAA;EACA,gFAAA;ACCH;ADGE;EACC,wBAAA;ACDH;ADIE;EAbD;IAcE,gBAAA;ECDD;EDGC;IACC,eAAA;ECDF;AACF","file":"style.css"}
\ No newline at end of file
diff --git a/src/plugins/back-to-top/style.scss b/src/plugins/back-to-top/style.scss
new file mode 100644
index 0000000..c751b07
--- /dev/null
+++ b/src/plugins/back-to-top/style.scss
@@ -0,0 +1,44 @@
+/**
+ * Back to Top Button Styles
+ * Minimal styling for Back to Top variation of core/button
+ * Handles positioning modes and visibility transitions
+ */
+
+/* Back to Top button specific positioning */
+.wp-block-button.is-back-to-top {
+ /* Fixed mode (sticky to footer) */
+ &[data-back-to-top-mode="fixed"] {
+ position: fixed;
+ bottom: 2rem;
+ right: 2rem;
+ z-index: 40;
+
+ @media ( max-width: 640px ) {
+ bottom: 1rem;
+ right: 1rem;
+ }
+ }
+
+ /* Smooth transitions for hover state */
+ .wp-block-button__link {
+ transition: all 0.2s ease-in-out;
+
+ &:hover {
+ transform: translateY( -2px );
+ box-shadow: 0 4px 6px -1px rgba( 0, 0, 0, 0.1 ),
+ 0 2px 4px -2px rgba( 0, 0, 0, 0.1 );
+ }
+
+ &:active {
+ transform: translateY( 0 );
+ }
+
+ @media ( prefers-reduced-motion: reduce ) {
+ transition: none;
+
+ &:hover {
+ transform: none;
+ }
+ }
+ }
+}
diff --git a/src/plugins/back-to-top/view.js b/src/plugins/back-to-top/view.js
new file mode 100644
index 0000000..17548c7
--- /dev/null
+++ b/src/plugins/back-to-top/view.js
@@ -0,0 +1,123 @@
+/**
+ * Smooth scroll utility with back-to-top support and anchor link smooth scrolling.
+ * Works with core/button Back to Top variation.
+ * Accessible, performant, and supports prefers-reduced-motion.
+ */
+
+( function () {
+ // Check if the browser prefers reduced motion
+ const prefersReducedMotion = window.matchMedia(
+ '(prefers-reduced-motion: reduce)'
+ ).matches;
+
+
+ // Calculate offset for sticky headers
+ const getStickyHeaderOffset = () => {
+ const header = document.querySelector( 'header[sticky="true"], [data-sticky="true"], .is-sticky' );
+ if ( ! header ) return 0;
+
+ const rect = header.getBoundingClientRect();
+ return Math.max( 0, rect.height + 20 ); // Add 20px padding
+ };
+
+ // Smooth scroll to target element or position
+ const smoothScrollTo = ( target, duration = 600 ) => {
+ if ( prefersReducedMotion ) {
+ // For users who prefer reduced motion, use instant scroll
+ if ( typeof target === 'number' ) {
+ window.scrollTo( 0, target );
+ } else if ( target instanceof HTMLElement ) {
+ target.scrollIntoView();
+ }
+ return;
+ }
+
+ const startY = window.scrollY;
+ const endY =
+ typeof target === 'number'
+ ? target
+ : target.getBoundingClientRect().top + startY - getStickyHeaderOffset();
+ const distance = endY - startY;
+ const startTime = performance.now();
+
+ const easeInOutCubic = ( progress ) => {
+ return progress < 0.5
+ ? 4 * progress * progress * progress
+ : ( progress - 1 ) * ( 2 * progress - 2 ) * ( 2 * progress - 2 ) + 1;
+ };
+
+ const step = ( currentTime ) => {
+ const elapsed = currentTime - startTime;
+ const progress = Math.min( elapsed / duration, 1 );
+ const currentY = startY + distance * easeInOutCubic( progress );
+
+ window.scrollTo( 0, currentY );
+
+ if ( progress < 1 ) {
+ requestAnimationFrame( step );
+ }
+ };
+
+ requestAnimationFrame( step );
+ };
+
+ // Initialize smooth scrolling for anchor links
+ const initAnchorLinks = () => {
+ document.addEventListener( 'click', ( e ) => {
+ const link = e.target.closest( 'a[href*="#"]' );
+ if ( ! link || link.hostname !== window.location.hostname || link.pathname !== window.location.pathname ) return;
+
+ const href = link.getAttribute( 'href' );
+ const hash = href.substring( href.indexOf( '#' ) );
+
+ // Skip if it's just a hash or empty
+ if ( hash === '#' || hash === '' ) return;
+
+ // Check if the target exists
+ const target = document.querySelector( hash );
+ if ( ! target ) return;
+
+ e.preventDefault();
+ smoothScrollTo( target );
+
+ // Update URL without triggering scroll
+ window.history.pushState( null, '', hash );
+ } );
+ };
+
+ // Initialize back-to-top functionality
+ const initBackToTop = () => {
+ const wrappers = document.querySelectorAll(
+ '.wp-block-button.is-back-to-top'
+ );
+
+ wrappers.forEach( ( wrapper ) => {
+ // Find the inner link or button
+ const link = wrapper.querySelector( '.wp-block-button__link' );
+ if ( ! link ) return;
+
+ link.addEventListener( 'click', ( e ) => {
+ e.preventDefault();
+ smoothScrollTo( 0, prefersReducedMotion ? 0 : 600 );
+ } );
+ } );
+ };
+
+ // DOM ready check
+ const ready = ( callback ) => {
+ if (
+ document.readyState === 'loading' ||
+ document.readyState === 'interactive'
+ ) {
+ document.addEventListener( 'DOMContentLoaded', callback );
+ } else {
+ callback();
+ }
+ };
+
+ // Initialize on DOM ready
+ ready( () => {
+ initAnchorLinks();
+ initBackToTop();
+ } );
+} )();
diff --git a/src/plugins/button-icon/icons.js b/src/plugins/button-icon/icons.js
new file mode 100644
index 0000000..b0cab69
--- /dev/null
+++ b/src/plugins/button-icon/icons.js
@@ -0,0 +1,109 @@
+import { Path, SVG } from '@wordpress/components';
+import { __ } from '@wordpress/i18n';
+
+function ArrowRightIcon() {
+ return (
+
+ );
+}
+
+function ArrowLeftIcon() {
+ return (
+
+ );
+}
+
+function ChevronRightIcon() {
+ return (
+
+ );
+}
+
+function ArrowUpIcon() {
+ return (
+
+ );
+}
+
+function ArrowDownIcon() {
+ return (
+
+ );
+}
+
+function ExternalIcon() {
+ return (
+
+ );
+}
+
+export const BUTTON_ICONS = [
+ {
+ label: __( 'Arrow Right', 'ls-plugin' ),
+ value: 'arrow-right',
+ char: '→',
+ icon: ,
+ },
+ {
+ label: __( 'Arrow Left', 'ls-plugin' ),
+ value: 'arrow-left',
+ char: '←',
+ icon: ,
+ },
+ {
+ label: __( 'Chevron Right', 'ls-plugin' ),
+ value: 'chevron-right',
+ char: '›',
+ icon: ,
+ },
+ {
+ label: __( 'Arrow Up', 'ls-plugin' ),
+ value: 'arrow-up',
+ char: '↑',
+ icon: ,
+ },
+ {
+ label: __( 'Arrow Down', 'ls-plugin' ),
+ value: 'arrow-down',
+ char: '↓',
+ icon: ,
+ },
+ {
+ label: __( 'External Link', 'ls-plugin' ),
+ value: 'external',
+ char: '↗',
+ icon: ,
+ },
+];
+
+export function getButtonIconChar( iconSlug ) {
+ const icon = BUTTON_ICONS.find( ( item ) => item.value === iconSlug );
+ return icon ? icon.char : '';
+}
diff --git a/src/plugins/button-icon/index.js b/src/plugins/button-icon/index.js
new file mode 100644
index 0000000..e066269
--- /dev/null
+++ b/src/plugins/button-icon/index.js
@@ -0,0 +1,159 @@
+import { addFilter } from '@wordpress/hooks';
+import { createHigherOrderComponent } from '@wordpress/compose';
+import { InspectorControls } from '@wordpress/block-editor';
+import { Button, PanelBody, PanelRow, ToggleControl, __experimentalGrid as Grid } from '@wordpress/components';
+import { Fragment } from '@wordpress/element';
+import { __ } from '@wordpress/i18n';
+
+import { BUTTON_ICONS } from './icons.js';
+
+function joinClasses( ...classNames ) {
+ return classNames.filter( Boolean ).join( ' ' );
+}
+
+function addAttributes( settings ) {
+ if ( 'core/button' !== settings.name ) {
+ return settings;
+ }
+
+ return {
+ ...settings,
+ attributes: {
+ ...settings.attributes,
+ lsButtonIcon: {
+ type: 'string',
+ },
+ lsButtonIconPositionLeft: {
+ type: 'boolean',
+ default: false,
+ },
+ },
+ };
+}
+
+addFilter(
+ 'blocks.registerBlockType',
+ 'ls-plugin/button-icons/add-attributes',
+ addAttributes
+);
+
+function addInspectorControls( BlockEdit ) {
+ return ( props ) => {
+ if ( 'core/button' !== props.name ) {
+ return ;
+ }
+
+ const { attributes, setAttributes } = props;
+ const { lsButtonIcon, lsButtonIconPositionLeft } = attributes;
+
+ return (
+
+
+
+
+
+
+ { BUTTON_ICONS.map( ( icon ) => (
+
+ ) ) }
+
+
+ { lsButtonIcon && (
+
+
+ setAttributes( {
+ lsButtonIconPositionLeft:
+ ! lsButtonIconPositionLeft,
+ } )
+ }
+ />
+
+ ) }
+
+
+
+ );
+ };
+}
+
+addFilter(
+ 'editor.BlockEdit',
+ 'ls-plugin/button-icons/add-inspector-controls',
+ addInspectorControls
+);
+
+const addClasses = createHigherOrderComponent( ( BlockListBlock ) => {
+ return ( props ) => {
+ const { name, attributes } = props;
+
+ if ( 'core/button' !== name || ! attributes?.lsButtonIcon ) {
+ return ;
+ }
+
+ const classes = joinClasses(
+ props.className,
+ 'has-ls-button-icon',
+ attributes.lsButtonIcon
+ ? `has-ls-button-icon-${ attributes.lsButtonIcon }`
+ : '',
+ attributes.lsButtonIconPositionLeft
+ ? 'has-ls-button-icon-position-left'
+ : ''
+ );
+
+ return ;
+ };
+}, 'addClasses' );
+
+addFilter(
+ 'editor.BlockListBlock',
+ 'ls-plugin/button-icons/add-classes',
+ addClasses
+);
+
+function addSaveProps( extraProps, blockType, attributes ) {
+ if ( 'core/button' !== blockType.name || ! attributes?.lsButtonIcon ) {
+ return extraProps;
+ }
+
+ return {
+ ...extraProps,
+ className: joinClasses(
+ extraProps.className,
+ 'has-ls-button-icon',
+ attributes.lsButtonIcon
+ ? `has-ls-button-icon-${ attributes.lsButtonIcon }`
+ : '',
+ attributes.lsButtonIconPositionLeft
+ ? 'has-ls-button-icon-position-left'
+ : ''
+ ),
+ };
+}
+
+addFilter(
+ 'blocks.getSaveContent.extraProps',
+ 'ls-plugin/button-icons/add-save-props',
+ addSaveProps
+);
diff --git a/src/plugins/button-icon/style.css b/src/plugins/button-icon/style.css
new file mode 100644
index 0000000..7917eb2
--- /dev/null
+++ b/src/plugins/button-icon/style.css
@@ -0,0 +1,65 @@
+@charset "UTF-8";
+.wp-block-button.has-ls-button-icon .wp-block-button__link {
+ display: inline-flex;
+ align-items: center;
+ position: relative;
+}
+
+.wp-block-button.has-ls-button-icon .wp-block-button__link::after,
+.wp-block-button.has-ls-button-icon.has-ls-button-icon-position-left .wp-block-button__link::before {
+ color: var(--ls-button-fill-icon-colour, currentColor);
+ display: grid;
+ font-size: var(--ls-button-fill-icon-font-size);
+ font-weight: 700;
+ inset-block: var(--ls-button-fill-icon-block-inset);
+ line-height: 1;
+ place-items: center;
+ position: absolute;
+ transition: color 0.5s cubic-bezier(0.4, 0, 0.2, 1);
+ width: var(--ls-button-fill-icon-well-size);
+}
+
+.wp-block-button.has-ls-button-icon .wp-block-button__link::after {
+ content: "→";
+ inset-inline-end: var(--ls-button-fill-icon-inline-end);
+}
+
+.wp-block-button.has-ls-button-icon.has-ls-button-icon-position-left .wp-block-button__link::before {
+ content: "→";
+ inset-inline-start: var(--ls-button-fill-icon-inline-end);
+}
+
+.wp-block-button.has-ls-button-icon.has-ls-button-icon-position-left .wp-block-button__link::after {
+ content: none;
+}
+
+.wp-block-button.has-ls-button-icon-arrow-right .wp-block-button__link::after,
+.wp-block-button.has-ls-button-icon-arrow-right.has-ls-button-icon-position-left .wp-block-button__link::before {
+ content: "→" !important;
+}
+
+.wp-block-button.has-ls-button-icon-arrow-left .wp-block-button__link::after,
+.wp-block-button.has-ls-button-icon-arrow-left.has-ls-button-icon-position-left .wp-block-button__link::before {
+ content: "←" !important;
+}
+
+.wp-block-button.has-ls-button-icon-chevron-right .wp-block-button__link::after,
+.wp-block-button.has-ls-button-icon-chevron-right.has-ls-button-icon-position-left .wp-block-button__link::before {
+ content: "›" !important;
+}
+
+.wp-block-button.has-ls-button-icon-arrow-up .wp-block-button__link::after,
+.wp-block-button.has-ls-button-icon-arrow-up.has-ls-button-icon-position-left .wp-block-button__link::before {
+ content: "↑" !important;
+}
+
+.wp-block-button.has-ls-button-icon-arrow-down .wp-block-button__link::after,
+.wp-block-button.has-ls-button-icon-arrow-down.has-ls-button-icon-position-left .wp-block-button__link::before {
+ content: "↓" !important;
+}
+
+.wp-block-button.has-ls-button-icon-external .wp-block-button__link::after,
+.wp-block-button.has-ls-button-icon-external.has-ls-button-icon-position-left .wp-block-button__link::before {
+ content: "↗" !important;
+}
+/*# sourceMappingURL=style.css.map */
\ No newline at end of file
diff --git a/src/plugins/button-icon/style.css.map b/src/plugins/button-icon/style.css.map
new file mode 100644
index 0000000..ce250e9
--- /dev/null
+++ b/src/plugins/button-icon/style.css.map
@@ -0,0 +1 @@
+{"version":3,"sources":["style.css","style.scss"],"names":[],"mappings":"AAAA,gBAAgB;ACAhB;EACC,oBAAA;EACA,mBAAA;EACA,kBAAA;ADED;;ACCA;;EAEC,sDAAA;EACA,aAAA;EACA,+CAAA;EACA,gBAAA;EACA,mDAAA;EACA,cAAA;EACA,mBAAA;EACA,kBAAA;EACA,mDAAA;EACA,2CAAA;ADED;;ACCA;EACC,YAAA;EACA,uDAAA;ADED;;ACCA;EACC,YAAA;EACA,yDAAA;ADED;;ACCA;EACC,aAAA;ADED;;ACCA;;EAEC,uBAAA;ADED;;ACCA;;EAEC,uBAAA;ADED;;ACCA;;EAEC,uBAAA;ADED;;ACCA;;EAEC,uBAAA;ADED;;ACCA;;EAEC,uBAAA;ADED;;ACCA;;EAEC,uBAAA;ADED","file":"style.css"}
\ No newline at end of file
diff --git a/src/plugins/button-icon/style.scss b/src/plugins/button-icon/style.scss
new file mode 100644
index 0000000..1223f93
--- /dev/null
+++ b/src/plugins/button-icon/style.scss
@@ -0,0 +1,63 @@
+.wp-block-button.has-ls-button-icon .wp-block-button__link {
+ display: inline-flex;
+ align-items: center;
+ position: relative;
+}
+
+.wp-block-button.has-ls-button-icon .wp-block-button__link::after,
+.wp-block-button.has-ls-button-icon.has-ls-button-icon-position-left .wp-block-button__link::before {
+ color: var(--ls-button-fill-icon-colour, currentColor);
+ display: grid;
+ font-size: var(--ls-button-fill-icon-font-size);
+ font-weight: 700;
+ inset-block: var(--ls-button-fill-icon-block-inset);
+ line-height: 1;
+ place-items: center;
+ position: absolute;
+ transition: color .5s cubic-bezier(.4, 0, .2, 1);
+ width: var(--ls-button-fill-icon-well-size);
+}
+
+.wp-block-button.has-ls-button-icon .wp-block-button__link::after {
+ content: "\2192";
+ inset-inline-end: var(--ls-button-fill-icon-inline-end);
+}
+
+.wp-block-button.has-ls-button-icon.has-ls-button-icon-position-left .wp-block-button__link::before {
+ content: "\2192";
+ inset-inline-start: var(--ls-button-fill-icon-inline-end);
+}
+
+.wp-block-button.has-ls-button-icon.has-ls-button-icon-position-left .wp-block-button__link::after {
+ content: none;
+}
+
+.wp-block-button.has-ls-button-icon-arrow-right .wp-block-button__link::after,
+.wp-block-button.has-ls-button-icon-arrow-right.has-ls-button-icon-position-left .wp-block-button__link::before {
+ content: "→" !important;
+}
+
+.wp-block-button.has-ls-button-icon-arrow-left .wp-block-button__link::after,
+.wp-block-button.has-ls-button-icon-arrow-left.has-ls-button-icon-position-left .wp-block-button__link::before {
+ content: "←" !important;
+}
+
+.wp-block-button.has-ls-button-icon-chevron-right .wp-block-button__link::after,
+.wp-block-button.has-ls-button-icon-chevron-right.has-ls-button-icon-position-left .wp-block-button__link::before {
+ content: "›" !important;
+}
+
+.wp-block-button.has-ls-button-icon-arrow-up .wp-block-button__link::after,
+.wp-block-button.has-ls-button-icon-arrow-up.has-ls-button-icon-position-left .wp-block-button__link::before {
+ content: "↑" !important;
+}
+
+.wp-block-button.has-ls-button-icon-arrow-down .wp-block-button__link::after,
+.wp-block-button.has-ls-button-icon-arrow-down.has-ls-button-icon-position-left .wp-block-button__link::before {
+ content: "↓" !important;
+}
+
+.wp-block-button.has-ls-button-icon-external .wp-block-button__link::after,
+.wp-block-button.has-ls-button-icon-external.has-ls-button-icon-position-left .wp-block-button__link::before {
+ content: "↗" !important;
+}
diff --git a/webpack.config.cjs b/webpack.config.cjs
index c60621c..4bfcd00 100644
--- a/webpack.config.cjs
+++ b/webpack.config.cjs
@@ -6,6 +6,11 @@ module.exports = {
...defaultConfig,
entry: () => ( {
...( typeof defaultConfig.entry === 'function' ? defaultConfig.entry() : defaultConfig.entry ),
+ 'js/button-icon': path.resolve( process.cwd(), 'src/plugins/button-icon', 'index.js' ),
+ 'css/button-icon': path.resolve( process.cwd(), 'src/plugins/button-icon', 'style.scss' ),
+ 'js/back-to-top': path.resolve( process.cwd(), 'src/plugins/back-to-top', 'index.js' ),
+ 'js/back-to-top-view': path.resolve( process.cwd(), 'src/plugins/back-to-top', 'view.js' ),
+ 'css/back-to-top': path.resolve( process.cwd(), 'src/plugins/back-to-top', 'style.scss' ),
'js/style-switcher': path.resolve( process.cwd(), 'src/js', 'style-switcher.js' ),
} ),
output: {