+ )
+} )
+
+export default ModalTour
diff --git a/src/editor/components/modal-tour/tour-steps.js b/src/editor/components/modal-tour/tour-steps.js
new file mode 100644
index 0000000..bb5acc8
--- /dev/null
+++ b/src/editor/components/modal-tour/tour-steps.js
@@ -0,0 +1,6 @@
+// Import all tour files from the tours directory
+import { tours } from './tours/index.js'
+
+export const TOUR_STEPS = {
+ ...tours,
+}
diff --git a/src/editor/components/modal-tour/tours/README.md b/src/editor/components/modal-tour/tours/README.md
new file mode 100644
index 0000000..4f2d414
--- /dev/null
+++ b/src/editor/components/modal-tour/tours/README.md
@@ -0,0 +1,163 @@
+# Guided Modal Tour Documentation
+
+> For creating new Tours, refer to `src/editor/components/guided-modal-tour/README.MD`
+
+This directory contains individual tour configurations for the Interactions guided
+modal tour system. Each tour is defined in its own JavaScript file and
+automatically imported into the main tour system.
+
+## How It Works
+
+The tour system uses `require.context()` to automatically discover and import
+all `.js` files in this directory. Each tour's ID (`tourId`) is derived from its
+filename in kebab-case (e.g., `interaction-library.js` becomes `interaction-library`).
+Each tour file should export a named export corresponding to the tour's purpose.
+
+## Tour Structure
+
+Each tour file should export an object with the following structure:
+
+```javascript
+import { createInterpolateElement } from '@wordpress/element'
+
+export const tourName = {
+ // Tour-level properties
+ hasConfetti: false,
+ condition: () => { /* condition logic */ },
+ initialize: () => { /* optional initialization */ },
+ steps: [
+ // Array of step objects
+ ]
+}
+```
+
+## Tour-Level Properties
+
+### `steps` (array)
+The array of step objects that define the tour flow.
+
+### `hasConfetti` (boolean)
+If `true`, confetti is shown on the last step. Default is `true`.
+
+### `condition` (function)
+A function that returns:
+- `true` - Show the tour (even if it's already been completed)
+- `false` - Do not show the tour
+- `null` - Show the tour only once (default behavior)
+
+### `initialize` (function, optional)
+A function called when the tour starts. Useful for setting up initial state or content.
+
+## Step Properties
+
+Each step in a tour is an object with the following possible properties:
+
+### `title` (string)
+The title text displayed at the top of the modal.
+
+### `description` (string|ReactNode)
+The main content or instructions for the step.
+
+### `help` (string|ReactNode, optional)
+If provided, a help text will be shown below the description.
+
+### `size` (string, optional)
+The size of the modal. Can be:
+- `'small'` (default)
+- `'medium'`
+- `'large'`
+
+### `anchor` (string, optional)
+A CSS selector for the element to which the modal should be anchored. If not provided, modal is centered.
+
+### `position` (string, optional)
+The position of the modal relative to the anchor. Can be:
+- `'left'`
+- `'right'`
+- `'top'`
+- `'bottom'`
+- `'center'` (default)
+
+### `offsetX` (number, optional)
+X-axis offset in pixels for fine-tuning the modal's position relative to the anchor.
+
+### `offsetY` (number, optional)
+Y-axis offset in pixels for fine-tuning the modal's position relative to the anchor.
+
+### `ctaLabel` (string, optional)
+If provided, a call-to-action button will be shown with this label.
+
+### `ctaOnClick` (function, optional)
+Function to call when the CTA button is clicked. The tour will move to the next step after this is called.
+
+### `showNext` (boolean, optional)
+If `true`, a "Next" button is shown. Default is `true`.
+
+### `nextEventTarget` (string, optional)
+A CSS selector for an element. If provided, the tour will wait for the specified event on this element before moving to the next step.
+
+### `nextEvent` (string, optional)
+The event name to listen for on `nextEventTarget` (e.g., 'click'). Default is 'click'.
+
+### `glowTarget` (string, optional)
+A CSS selector for an element to highlight/glow during this step.
+
+### `preStep` (function, optional)
+Function called before the step is displayed. Useful for setup or preparation.
+
+### `postStep` (function, optional)
+Function called after the step is completed. Useful for cleanup or triggering actions.
+
+### `skipIf` (function, optional)
+Function that returns `true` if this step should be skipped. Useful for conditional steps.
+
+## Example Tour
+
+```javascript
+import { __ } from '@wordpress/i18n'
+import { createInterpolateElement } from '@wordpress/element'
+
+export const exampleTour = {
+ hasConfetti: false,
+ condition: () => {
+ // Only show if there's a specific URL parameter
+ return window?.location?.search?.includes('tour=example') ? true : null
+ },
+ steps: [
+ {
+ title: 'Welcome',
+ description: 'This is the first step.',
+ size: 'medium',
+ anchor: '.my-element',
+ position: 'bottom',
+ offsetX: 10,
+ offsetY: 0,
+ ctaLabel: 'Get Started',
+ ctaOnClick: () => { console.log('CTA clicked!') },
+ showNext: false,
+ nextEventTarget: '.my-button',
+ nextEvent: 'click',
+ glowTarget: '.my-element',
+ },
+ {
+ title: 'Second Step',
+ description: 'This is the second step.',
+ help: createInterpolateElement(
+ 'Click the Continue button to proceed.',
+ { strong: }
+ ),
+ anchor: '.another-element',
+ position: 'right',
+ nextEventTarget: '.continue-button',
+ glowTarget: '.another-element',
+ }
+ ]
+}
+```
+
+## Creating New Tours
+
+1. Create a new `.js` file in this directory
+2. Import the necessary dependencies (`__`, `createInterpolateElement`)
+3. Export a named export with your tour configuration
+4. The tour will be automatically discovered and included in the tour system
diff --git a/src/editor/components/modal-tour/tours/editor.js b/src/editor/components/modal-tour/tours/editor.js
new file mode 100644
index 0000000..6d0947b
--- /dev/null
+++ b/src/editor/components/modal-tour/tours/editor.js
@@ -0,0 +1,20 @@
+import { __ } from '@wordpress/i18n'
+import { createInterpolateElement } from '@wordpress/element'
+
+export const editor = {
+ hasConfetti: false,
+ steps: [
+ {
+ title: '👋 ' + __( 'Welcome to Interactions', 'interactions' ),
+ description: __( 'Transform your WordPress site with animations and dynamic interactions that bring your content to life. Let’s get started by exploring the Interaction Library.', 'interactions' ),
+ help: createInterpolateElement( __( 'Click the Interactions button to continue.', 'interactions' ), {
+ strong: ,
+ } ),
+ anchor: '.interact-insert-library-button',
+ position: 'bottom',
+ nextEventTarget: '.interact-insert-library-button',
+ glowTarget: '.interact-insert-library-button',
+ showNext: false,
+ },
+ ],
+}
diff --git a/src/editor/components/modal-tour/tours/index.js b/src/editor/components/modal-tour/tours/index.js
new file mode 100644
index 0000000..e47c7ab
--- /dev/null
+++ b/src/editor/components/modal-tour/tours/index.js
@@ -0,0 +1,34 @@
+// This file automatically imports all tour files from the tours directory
+// and exports them as a single object for use in TOUR_STEPS
+
+// Dynamically import all tour files from the tours directory
+const tourContext = require.context( './', false, /\.js$/ )
+const tours = {}
+
+// Import all tour files and populate the tours object using the filename as the key
+tourContext.keys().forEach( fileName => {
+ // Skip this index.js file itself
+ if ( fileName === './index.js' ) {
+ return
+ }
+
+ // Import the tour module
+ const tourModule = tourContext( fileName )
+
+ // Use the filename (without extension) as the key
+ const tourName = fileName.replace( './', '' ).replace( '.js', '' )
+
+ // Prefer default export, fallback to first named export if available
+ if ( tourModule.default ) {
+ tours[ tourName ] = tourModule.default
+ } else {
+ // If no default export, use the first named export (if any)
+ const namedExports = Object.keys( tourModule ).filter( name => name !== 'default' )
+ if ( namedExports.length > 0 ) {
+ tours[ tourName ] = tourModule[ namedExports[ 0 ] ]
+ }
+ }
+} )
+
+// Export all tours as a single object
+export { tours }
diff --git a/src/editor/components/modal-tour/tours/interaction-library.js b/src/editor/components/modal-tour/tours/interaction-library.js
new file mode 100644
index 0000000..9c18f9b
--- /dev/null
+++ b/src/editor/components/modal-tour/tours/interaction-library.js
@@ -0,0 +1,51 @@
+import { __ } from '@wordpress/i18n'
+import { createInterpolateElement } from '@wordpress/element'
+
+export const interactionLibrary = {
+ steps: [
+ {
+ title: '👋 ' + __( 'Interaction Library', 'interactions' ),
+ description: __( 'The Interaction Library contains curated presets for both simple and advanced animations that you can insert in just one click.', 'interactions' ),
+ offsetX: '-400px',
+ },
+ {
+ title: __( 'Filtering by Categories', 'interactions' ),
+ description: __( 'Quickly find the type of interaction you need by filtering presets by category or using the search bar above.', 'interactions' ),
+ help: createInterpolateElement( __( 'Select the Button category from the sidebar filter.', 'interactions' ), {
+ strong: ,
+ } ),
+ anchor: '.interact-interaction-library__select__category--button',
+ position: 'right',
+ nextEventTarget: '.interact-interaction-library__select__category--button',
+ glowTarget: '.interact-interaction-library__select__category--button',
+ showNext: false,
+ },
+ {
+ title: __( 'Insert or Customize', 'interactions' ),
+ description: __( 'You can insert an interaction as-is, or go deeper by customizing its settings.', 'interactions' ),
+ help: createInterpolateElement( __( 'Hover over the interaction and click Customize on a preset to continue.', 'interactions' ), {
+ strong: ,
+ } ),
+ anchor: '.interact-interaction-library__select__preset-card',
+ position: 'bottom',
+ nextEventTarget: '.interact-interaction-library__select__preset-card',
+ glowTarget: '.interact-interaction-library__select__preset-card',
+ offsetY: '-100px',
+ showNext: false,
+ },
+ {
+ title: __( 'Customize Section', 'interactions' ),
+ description: __( 'Each interaction can be tailored to your needs. Adjust interaction type, triggers, and animation styles with intuitive controls.', 'interactions' ),
+ help: createInterpolateElement( __( 'Tweak a few settings and click Insert.', 'interactions' ), {
+ strong: ,
+ } ),
+ anchor: '.interact-interaction-library__configure-middle',
+ position: 'left',
+ nextEventTarget: '.interact-interaction-library__configure__apply-button__button',
+ glowTarget: '.interact-interaction-library__configure__apply-button__button',
+ offsetY: '200px',
+ showNext: false,
+ },
+ ],
+}
+
diff --git a/src/editor/components/modal-tour/tours/sidebar.js b/src/editor/components/modal-tour/tours/sidebar.js
new file mode 100644
index 0000000..96f1406
--- /dev/null
+++ b/src/editor/components/modal-tour/tours/sidebar.js
@@ -0,0 +1,65 @@
+import { __ } from '@wordpress/i18n'
+import { createInterpolateElement } from '@wordpress/element'
+import { select, dispatch } from '@wordpress/data'
+
+export const sidebar = {
+ steps: [
+ {
+ title: __( 'Inspector Panel', 'interactions' ),
+ description: __( 'You can create your own interaction in the inspector or further adjust the interaction inserted from the library.', 'interactions' ),
+ help: __( 'Explore the inspector settings to see how it works.', 'interactions' ),
+ anchor: '.interact-interaction-card',
+ position: 'left',
+ glowTarget: '.interact-sidebar',
+ },
+ {
+ title: __( 'Previewing Interaction', 'interactions' ),
+ description: __( 'Once you’re satisfied, preview your page to see interactions in action. Remember, you can always come back and edit later.', 'interactions' ),
+ help: createInterpolateElement( __( 'Click Preview to view the result, then save your changes.', 'interactions' ), {
+ strong: ,
+ } ),
+ size: 'medium',
+ anchor: '.interact-timeline__preview-button',
+ position: 'left',
+ nextEventTarget: '.interact-timeline__preview-button',
+ glowTarget: '.interact-timeline__preview-button',
+ preStep: () => {
+ // Scroll to the preview button before moving to the next step.
+ document.querySelector( '.interact-timeline__preview-button' )?.scrollIntoView( {
+ behavior: 'smooth',
+ block: 'center',
+ } )
+ },
+ },
+ {
+ title: __( 'Apply to Existing Elements', 'interactions' ),
+ description: __( 'Interactions aren’t limited to new content. You can also add animations to elements you’ve already created.', 'interactions' ),
+ help: createInterpolateElement( __( 'Select an existing block and click the Interactions logo to open the library.', 'interactions' ), {
+ strong: ,
+ } ),
+ size: 'medium',
+ anchor: '.interact-block-toolbar-button',
+ position: 'bottom',
+ nextEventTarget: '.interact-block-toolbar-button',
+ glowTarget: '.interact-block-toolbar-button',
+ preStep: () => {
+ // Select the last block in the editor
+ const blocks = select( 'core/block-editor' ).getBlocks()
+
+ // Recursively find the last innermost block
+ const getLastInnermostBlock = block => {
+ if ( block.innerBlocks && block.innerBlocks.length ) {
+ return getLastInnermostBlock( block.innerBlocks[ block.innerBlocks.length - 1 ] )
+ }
+ return block
+ }
+
+ if ( blocks.length ) {
+ const lastBlock = blocks[ blocks.length - 1 ]
+ const innermostBlock = getLastInnermostBlock( lastBlock )
+ dispatch( 'core/block-editor' ).selectBlock( innermostBlock.clientId )
+ }
+ },
+ },
+ ],
+}
diff --git a/src/editor/components/timeline/index.js b/src/editor/components/timeline/index.js
index 0ccb454..c9740a8 100644
--- a/src/editor/components/timeline/index.js
+++ b/src/editor/components/timeline/index.js
@@ -1110,6 +1110,7 @@ const Timeline = props => {
<>
{ ! isPreviewing && (