Skip to content

Conversation

@prosdev
Copy link
Contributor

@prosdev prosdev commented Dec 30, 2025

Phase 2: Presentation Layer - Modal & Inline Plugins

Closes #40 - Modal Plugin
Closes #41 - Inline Plugin
Closes #42 - Testing - Modal, Inline, and Integration
Closes #43 - Documentation
Closes #44 - Playground Examples

What's New

This release completes the presentation layer with two powerful new rendering plugins and built-in form support.

Modal Plugin (#40)

  • Rich content modals with announcements, promotions, and interactive content
  • Built-in forms with email capture, surveys, and feedback
  • Size variants (small, medium, large, extra-large)
  • Hero images for visual impact
  • Responsive design with mobile fullscreen
  • Keyboard navigation (focus trap, Escape, Tab)
  • ARIA attributes and accessibility
  • Smooth animations
  • Form states (success, error, loading)
  • API: show(), remove(), isShowing(), showFormState(), resetForm(), getFormData()

Inline Plugin (#41)

  • Embed content anywhere using CSS selectors
  • 5 insertion methods: replace, append, prepend, before, after
  • Dismissal with localStorage persistence
  • Smooth entrance animations
  • No layout disruption
  • API: show(), remove(), isShowing()

Forms (Built into Modal)

  • Field types: text, email, url, tel, number, textarea, select, checkbox, radio
  • Validation: required, email, URL, pattern, custom, min/max length
  • Real-time feedback (validates on blur)
  • Submission handling via experiences:modal:form:submit event
  • Success/error states with built-in UI
  • Pure functions for easy testing

Theming

All plugins now support CSS variable theming:

  • Modal: --xp-modal-* variables
  • Forms: --xp-form-* variables
  • Banner: --xp-banner-* variables (refactored)
  • Inline: --xp-inline-* variables

See Theming Guide for full reference.

Bundle Size

  • Core SDK: 13.4 KB gzipped
  • All Plugins: ~26 KB gzipped total
  • Smaller than competitors (Pathfora: ~47 KB)

Testing (#42)

432 tests passing (unit, integration, browser)

  • Modal Plugin: 56 tests (keyboard, focus trap, forms, accessibility)
  • Inline Plugin: 24 tests (insertion, dismissal, persistence)
  • Form Validation: 35 tests (all field types, edge cases)
  • Integration: 10 tests (plugin interactions)
  • Exit Intent: 21 tests (timing, sensitivity)
  • All display conditions: 100+ tests

Coverage: 100% for modal, inline, and form validation logic

Documentation (#43)

Complete documentation added:

  • Modal Plugin Reference (docs/pages/reference/plugins/modal.mdx)
  • Inline Plugin Reference (docs/pages/reference/plugins/inline.mdx)
  • Theming Guide (docs/pages/guides/theming.mdx)
  • Updated plugin navigation and overview
  • Updated main README and package READMEs

Playground Examples (#44)

Interactive demos added:

  • Layout Gallery Hub: Browse banner, modal, and inline examples
  • Exit Intent Email Capture: Exit intent + modal with form
  • Feature Discovery Journey: Scroll depth + inline tips + modal tour
  • Time-Delayed Promotions: Time delay + modal with hero image
  • Promotions & Announcements: Banner examples
  • Navigation system with breadcrumbs and tabs
  • Developer event logs for debugging

Breaking Changes

None. This is a minor release with backward compatibility.

Next Steps

  • Merge to main
  • Automated publish to npm (v0.2.0)
  • Update playground to use published packages
  • Phase 3: Chrome DevTools extension

All Phase 2 tasks complete

- Implement modal plugin with centered overlay layout
- Add focus trap and keyboard navigation (Tab, Escape)
- Include ARIA attributes for screen readers
- Support multi-button layouts with primary/secondary variants
- Add custom className and style props for customization
- Auto-register in core runtime
- Comprehensive test coverage (31 tests)

Features:
- Dismissable (close button, backdrop click, Escape key)
- Configurable zIndex, backdrop dismiss, and dismissable options
- HTML sanitization for XSS prevention
- Event emission (shown, dismissed, action, trigger)
- Focus management (trap focus, return on close)
- Button variants (primary, secondary) with hover states

Also fixed:
- Update all display condition plugins to use 'sdk:destroy' event
- Add ExperienceButton type (replaces BannerButton)
- Update modal trigger state in Context interface
…testing

Modal Enhancements:
- Add size variants (sm/md/lg/fullscreen/auto)
- Add mobile fullscreen behavior (auto-enables for lg on <640px)
- Add hero image support with lazy loading and responsive sizing
- Add animations (fade, slide-up, none) with configurable duration
- Add position options (center, bottom)
- Update types to support new features

Testing Infrastructure:
- Set up Vitest Browser Mode with Playwright for real browser testing
- Add 5 browser tests for mobile viewport behavior
- Integrate happy-dom for faster unit tests (2x speed improvement)
- Update modal tests to use happy-dom environment
- Add vitest.browser.config.ts for browser-specific test configuration
- Exclude browser tests from regular unit test runs

CI/CD:
- Add browser tests to GitHub Actions workflow
- Add Playwright browser caching for faster CI runs
- Install Chromium with system dependencies in CI

Test Coverage:
- Modal plugin: 50 tests total (45 unit + 5 browser)
- All tests passing (359 unit + 5 browser = 364 total)
- Mobile auto-fullscreen tested in real Chromium browser

Dependencies:
- Add @vitest/browser@4.0.16
- Add @vitest/browser-playwright@4.0.16
- Add playwright@1.57.0
- Add happy-dom@20.0.11

Relates to #40
Add comprehensive form support to modal plugin:

Form Types & Validation:
- Add FormConfig, FormField, FormState, ValidationResult types
- Implement pure validation functions (validateField, validateForm)
- Support email, url, tel, number, text, textarea field types
- Custom validation with regex patterns and functions
- 35 validation tests passing

Form Rendering:
- Pure rendering functions (renderForm, renderFormField, renderSubmitButton)
- Tailwind-inspired CSS styles (form-styles.ts)
- Success/error state rendering
- Accessibility: ARIA attributes, labels, error messages
- Responsive design with focus states

Modal Integration:
- Detect content.form and render form instead of buttons
- Backward compatible with existing button-based modals
- Forms easily extractable to separate plugin later

Tests: 80 total (45 modal + 35 validation)
Convert hardcoded CSS values to CSS variables for better theming:

Modal Plugin:
- Create modal-styles.ts with CSS variable helpers
- Refactor modal.ts inline styles to use CSS variables
- Variables for colors, spacing, typography, shadows
- Support for custom hover states via CSS variables

Form Styles:
- Refactor form-styles.ts to use CSS variables
- All form elements (inputs, labels, buttons, states) themeable
- Consistent naming convention: --xp-form-*, --xp-input-*, etc.

Banner Plugin:
- Convert banner CSS <style> tag to use CSS variables
- Support for light/dark mode via CSS variables
- Variables for all colors, spacing, typography
- Responsive variables (mobile padding, etc.)

Benefits:
- Users can theme without !important
- Clean CSS variable API
- Dark mode support
- Better design system integration
- No breaking changes (defaults match previous styles)

Bundle Size:
- Plugins: 56.35 KB -> 62.11 KB uncompressed (+5.76 KB)
- Gzipped: 12.7 KB -> 13.7 KB (+1 KB)

Tests: 125 passing (45 banner + 45 modal + 35 validation)
Add complete form event handling and state management:

Event Listeners:
- input: Track form data changes, emit change events
- blur: Validate field on blur, show/clear errors
- submit: Validate entire form, emit submit event

Form State Management:
- Track form data by experienceId
- Show inline validation errors
- Disable submit button during submission
- Update aria-invalid and error messages

API Methods:
- showFormState(id, 'success'|'error'): Replace form with state UI
- resetForm(id): Clear all form data and errors
- getFormData(id): Get current form data

Events Emitted:
- experiences:modal:form:change: Field value changed
- experiences:modal:form:validation: Field/form validated
- experiences:modal:form:submit: Form submitted (validation passed)
- experiences:modal:form:state: Form state changed

Validation:
- Uses pure validateField/validateForm functions
- Real-time feedback on blur
- All fields validated on submit
- Custom validation function support

Tests (11 new):
- Render form with fields and labels
- Input change events and data tracking
- Field validation on blur
- Clear errors when field becomes valid
- Full form validation on submit
- Submit event with valid data
- Disable submit button during submission
- Get/reset form data
- Show success/error states

Tests: 405 passing (56 modal, 35 form validation, 11 form integration)
Add inline plugin supporting 5 insertion methods: replace, append, prepend, before, after.

Implementation:
- types.ts: InlineContent, InlinePlugin, InsertionPosition types
- insertion.ts: Pure DOM manipulation functions
- inline.ts: Plugin implementation with CSS variables
- Auto-register in core runtime

Features:
- CSS selector targeting
- Dismissal with localStorage persistence
- Error events when selector not found
- Multi-instance support
- Custom className and style props
- Auto-loads storage plugin if needed

Events:
- experiences:shown, experiences:dismissed
- experiences:inline:error
- trigger:inline

Bundle: 54 KB -> 57.6 KB (+3.6 KB)
- 24 tests covering all inline plugin functionality
- Insertion methods: replace, append, prepend, before, after
- Dismissal with persistence
- Custom styling (className, style props)
- Multi-instance support
- Error handling (selector not found)
- HTML sanitization
- Cleanup on destroy
- Event emission (shown, dismissed, error)

Fixed:
- Use sdk:destroy event instead of destroy
- Add config parameter to plugin function signature
- Remove unused InlineContent import

All 429 tests passing
- Created modal plugin docs with form examples
- Created inline plugin docs with insertion methods
- Updated plugins overview and navigation
- Updated core README with new features
- Updated plugins README with all plugins
- Added CSS variables documentation
- Added comprehensive examples for all layouts
- Hide existing modal before showing new one
- Only one modal visible at a time
- Add test for modal replacement behavior
- Fix linter errors (use for...of instead of forEach)

All 427 tests passing
- Register listeners for each trigger type individually
- Workaround for sdk-kit emitter not passing event name to wildcard handlers
- Prevents 't.replace is not a function' error
- Supports: exitIntent, scrollDepth, timeDelay, pageVisits, modal, inline

Note: sdk-kit's emitter passes only data to wildcard handlers, not the event name.
This is a known limitation. For now, we register each trigger explicitly.

All 427 tests passing
- Add getter properties for modal, inline, banner on singleton
- Allows script tag users to access window.experiences.modal.show()
- Fixes playground getting stuck at 'Loading Experience SDK...'

Now window.experiences.modal, .inline, .banner are available
- Make ExperienceRuntime.sdk public (not readonly) for Proxy access
- Add default export for IIFE build to expose singleton correctly
- Update singleton Proxy to dynamically access defaultInstance.sdk
- Remove manual window assignment (handled by IIFE build)
- Fixes playground integration where experiences.modal was undefined

This enables script tag users to access plugin APIs via global:
experiences.modal.show(), experiences.inline.show(), etc.
- Prevent duplicate inline experiences from being shown
- Fix scroll depth threshold evaluation to match specific percentages
- Remove trigger:inline emission that caused infinite event loops
- Add scrollDepth.reset() method to clear triggered thresholds
- Expand HTML sanitizer to allow div, ul, li tags for rich content
- Add smooth fade-in animations to inline experiences
- Add display conditions evaluation for trigger-based experiences
- Add inline to Experience type union
- Add tests for new functionality (432 tests passing)
- Update main README with v0.2.0 features and examples
- Update packages/core/README.md (already accurate)
- Fix duplicate header in packages/plugins/README.md
- Update changeset test counts (432 tests, 56 modal tests)
- Add script tag and ESM usage examples
- Update roadmap and project status
@prosdev prosdev merged commit 91e8c94 into main Dec 30, 2025
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

2 participants