Skip to content

SliderV2 — Implementation Progress & Status

Branch: add-slider-v2 (both Application-Frontend and Creative-Engine repos) Last updated: 2026-03-10


Overall Status

PhaseStatus
Phase 1: Core engineCOMPLETE
Phase 2 Round 1: Styling controlsCOMPLETE
Phase 2 Round 2: Indicators (dots, counter, progress)COMPLETE
Phase 2 Polish: Feedback fixesCOMPLETE
Video in SliderCODE COMPLETE — awaiting BunnyCDN keys
Smart Video AutoplayCOMPLETE
Thumbnail NavigationCOMPLETE
Template PresetsCOMPLETE (4 templates, visual polish needed)
Phase 3: Bug fixes (A1-A13)COMPLETE
Phase 3: New features (B1-B3)COMPLETE
Phase 4: New transitions + config reorgCOMPLETE

Phase 1 — Core Engine (COMPLETE)

What was built:

  • CreativeSliderBlockV2.vue — full slider runtime component (~2080 lines) with multiple transition modes, navigation, autoplay, tease, feed support, and per-slide video
  • SliderV2Configuration.vue — config panel (~1500 lines) for all slider settings, organized into 4 tabs
  • Block registration in Application-Frontend: types (SliderV2Properties), defaults (sliderV2Defaults), and block registry
  • SliderV2Properties type definition with ~90+ typed properties covering layout, navigation, indicators, behavior, feed, and standard block properties

Key features:

  • Scroll-snap based navigation with native rubber-band feel on mobile
  • Fade and crossfade transitions with opacity-based slide switching
  • Sub-block architecture: each slide can contain text, graphic, button, and HTML blocks as children
  • Per-slide overrides: each slide entry in the slides array can override any sub-block property
  • Feed support via proxy URL with filter rules, custom params, sort key/order, and live override from flow operators
  • Flow operator support: ShowHide and TargetOpSelector recognize the sl2 prefix
  • Keyboard navigation (arrow keys) with focus management
  • Touch/swipe support for all JS-navigated modes (fade, slide, cube, carousel)
  • Slide click-through: each slide can have a URL that opens on click, with feed data interpolation
  • Lazy rendering via canShowSlide() — only renders slides within a 3-slide buffer in scroll/fade modes
  • Editing sync: listens for slider-editing-slide CustomEvent from the builder to preview specific slides and pause autoplay
  • Analytics: swipe events sent via debounced sendSwipeAnalytics(), first-event tracking on wrap click

Phase 2 — Styling & UI (COMPLETE)

Round 1: Styling Controls

Arrow styling:

  • Style options: Default SVG chevron / Custom image upload
  • Custom image modes: Single (right arrow mirrored via scaleX(-1) for left) / Separate (individual uploads for left and right)
  • Size: 16-80px (default 32px), configurable via slider
  • Color: any color with alpha (default #55555599), only shown for default SVG style
  • Background: any color with alpha (default rgba(255,255,255,0.7))
  • Border-radius: configurable (default 50% = circle)
  • Position: Inside / Outside / Free
    • Inside: offset from track edges by arrowOffset px
    • Outside: positioned outside track by -(arrowSize + arrowOffset) px
    • Free: full 4-sided position inputs (arrowFreeLeftPosition, arrowFreeRightPosition) with smart translateY(-50%) centering that detects whether top or bottom is active
  • Offset X and Offset Y controls for inside/outside modes
  • SVG scales with button size (60% of container)
  • Arrow z-index: 10 (above dots/thumbnails at z-index 2)

Dot styling:

  • 2 shapes: Dot (1:1 aspect ratio, round by default) and Line (active dot = 3x wider bar)
  • Shape change auto-sets border-radius: dot -> 50%, line -> 2px
  • BorderRadiusSection for custom rounding of both shapes
  • Color: inactive color + active color, both with alpha support
  • Size: 4-24px (default 8px)
  • Gap: 0-20px (default 6px)
  • Position: Overlay (absolute at bottom) / Above (absolute at -24px top) / Below (absolute at -24px bottom) / Free (full position input with z-index 3)
  • Active line dot border-radius clamped to min(dotBorderRadius, dotSize/2)px to prevent overflow

Layout:

  • Flex alignment section: horizontal + vertical alignment of the block itself
  • Size section: width and height with unit support
  • Position section: margin/offset for all 4 sides
  • Padding section: slide padding (inner padding within each slide), 4-sided with px/% units

Effects:

  • Edge fade: CSS mask gradient (5% transparent edges) — disabled for fade/cube/carousel modes
  • Box shadow: full shadow settings (x, y, blur, spread, color, inset)
  • Rotation: block rotation in degrees

Round 2: Indicators

Slide counter (1/N):

  • Toggle on/off
  • Placement: Overlay (positioned over the slider) or Per-Slide (inside each slide, top-right)
  • Color: configurable (default #ffffff)
  • Font size: 8-32px (default 14px)
  • Position (overlay mode): 4 corners (top-left, top-right, bottom-left, bottom-right) + free position
  • Text shadow for readability: 0 1px 3px rgba(0,0,0,0.3)

Progress bar:

  • Toggle on/off
  • Color: configurable (default #ffffff)
  • Height: 1-12px (default 3px)
  • Position: Top / Bottom / Free
  • Fill width: (activeIndex + 1) / totalSlides * 100% for slides, or videoProgress * 100% for video slides
  • Smooth width transition (300ms ease) for slides, no transition for video (real-time)
  • Background track: rgba(255,255,255,0.2)

Autoplay:

  • Toggle on/off
  • Interval: 1-60 seconds (default 5s, step 0.5s)
  • Loop video toggle (only visible when autoplay is on)
  • Loop toggle (independent of autoplay, always visible)

Polish (completed feedback fixes)

  • 4-tab config organization: Style | Navigation | Behavior | Slides
  • Numeric input ?? fallbacks instead of || to correctly handle zero values
  • Slide pagination click fix for navigating between slides in the editor
  • New slides clone the last slide via cloneDeep (preserving sub-block overrides)
  • Arrow SVG scales with button size (60% width/height)
  • All labels shortened and improved for clarity

Video in Slider (CODE COMPLETE)

Design: Per-slide video field. Each slide object can have optional videoStreamId, videoFileName, and videoUrl fields.

Video URL resolution (priority order):

  1. slide.videoUrl — direct URL
  2. slide.videoStreamId — BunnyCDN delivery URL: https://delivery-6.cavai.com/{streamId}/original
  3. Feed data videoUrl — from feed data, interpolated with feed variables

Engine: <video> element rendered per slide, absolute positioned over the slide content, object-fit: cover, muted, playsinline, z-index 1. When a video is playing on the active slide, all sub-blocks within that slide are hidden (v-if guard on video existence + active state).

Playback control:

  • Active slide auto-plays video (resets currentTime = 0 on activation)
  • Inactive slide video is paused via el.pause()
  • Video progress tracked via onVideoTimeUpdate — updates videoProgress for the progress bar
  • On onVideoEnded: if loopVideo is off, advances to next slide (treats as autoplay-driven advance)

Smart Video Autoplay interaction matrix:

AutoplayLoop videoBehavior
Off-Video loops indefinitely, manual navigation only
OnOffVideo plays to completion, then auto-advances to next slide
OnOnVideo loops indefinitely on its slide, autoplay timer skips it

Key implementation detail: The autoplay timer (setInterval) checks activeSlideHasVideo each tick — if the active slide has a video, the tick is skipped entirely. Video-driven advancement happens via the @ended event handler instead.


Thumbnail Navigation (COMPLETE)

Core features:

  • Scrollable thumbnail strip rendered as a row of <button> elements
  • Each thumbnail uses background-image from the slide's graphic block image
  • Active thumbnail has distinct border color and full opacity (inactive: 0.6 opacity)
  • Auto-scroll: active thumbnail scrolled into center of strip via container.scrollTo({ left, behavior: 'smooth' })
  • Click on thumbnail navigates to that slide

Source block detection:

  • Auto mode (default): iterates all graphic sub-blocks, returns first one with an actual image URL
  • Manual override: thumbnailSourceBlock can specify a specific graphic block name
  • Fallback: uses feed data imageUrl if no graphic block image found

Config options (16 total):

  • thumbnailAlignment: left / center / right (maps to flex-start / center / flex-end via justifyContent)
  • thumbnailSourceBlock: auto / specific graphic block name
  • thumbnailSize: 24-96px (default 48px) — sets both width and height
  • thumbnailGap: 0-16px (default 4px)
  • thumbnailFit: cover / contain (background-size on each thumbnail)
  • thumbnailMargin: 0-32px (default 0) — horizontal padding on the strip
  • thumbnailPadding: 0-16px (default 0) — inner padding within each thumbnail (uses background-origin: content-box)
  • thumbnailBorderRadius: configurable (default 4px)
  • thumbnailBgColor: background color behind the image (default transparent)
  • thumbnailBorderColor: border color (default transparent)
  • thumbnailActiveColor: border color for active thumbnail (default #ffffff)
  • thumbnailBorderWidth: 0-8px (default 2px)
  • thumbnailPosition: Overlay / Above / Below / Free (same position system as dots)

Scrollbar: Hidden via scrollbarWidth: none, -webkit-scrollbar: none, and msOverflowStyle: none


Template Presets (COMPLETE — needs visual polish)

Infrastructure:

  • SliderV2Template enum in types.ts with 4 values
  • Template functions in src/templates/sliderV2/ — each returns a partial SliderV2Properties object
  • updateSliderV2Template() in src/templates/index.ts — merges template data while preserving identity fields (order, parent, blockName, blockType, displayName, hidden), content fields (slides, feed*), and structural fields (allowedSubBlocks, etc.)
  • TemplateButton component in the Style tab of the config panel
  • templateModified tracking referenced in confirm dialog but not yet implemented
  • Purpose: E-commerce product browsing
  • Transition: Scroll (native snap)
  • Slide width: 80% (peek effect showing adjacent slides)
  • Gap: 8px between slides
  • Navigation: Arrows (inside, small 28px, white bg with 85% opacity), dots (below, small 7px, dark colors #444444)
  • Behavior: Loop on, tease on (0.5s delay, 0.8s duration, 1 iteration), no autoplay
  • Style: Light background (#F5F5F5), no border radius, 100% width x 90% height
  • Purpose: Photography/image showcase with thumbnails
  • Transition: Crossfade (overlapping opacity transition)
  • Slide width: 100%
  • Navigation: Arrows (inside, 30px, dark bg rgba(0,0,0,0.3), white arrows), slide counter (top-right, 12px, white), thumbnails (below, 44px, 4px gap, 2px padding, cover fit, white active border)
  • Behavior: Loop on, no autoplay, no tease
  • Style: Dark background (#1A1A1A), no border radius, 100% width x 90% height

3. Testimonial

  • Purpose: Quote/review carousel with autoplay
  • Transition: Fade (opacity with brief blackout delay)
  • Slide width: 100%
  • Navigation: Arrows (inside, small 24px, subtle bg rgba(255,255,255,0.1), white arrows), dots (below, line shape 20px, rgba(255,255,255,0.25) / #ffffffCC)
  • Behavior: Autoplay on (6s interval), loop on, no tease
  • Style: Dark blue-grey background (#2B2D42), 12px border radius, 90% width x 85% height, 12px slide padding all sides

4. Story

  • Purpose: Instagram-stories style full-screen content
  • Transition: Scroll (fast 250ms)
  • Slide width: 100%
  • Navigation: No arrows, no dots, no counter, no thumbnails. Only progress bar (top, 2px, white #ffffffCC)
  • Behavior: Autoplay on (4s interval), loop on, no tease
  • Style: Near-black background (#111111), no border radius, 100% width x 100% height

Phase 3 — Bug Fixes + Features (COMPLETE)

Bug Fixes (A1-A13)

IDIssueFix
A1Thumbnail alignmentjustifyContent maps left/center/right to flex-start/center/flex-end
A2Thumbnail graphic block selectionSmart auto-search through all graphic blocks + manual override via thumbnailSourceBlock
A3Cube scalingPerspective set to container size; compensating translateZ(-halfSize) so front face lands at Z=0 with scale=1.0
A4Cube loop backward directionProper forward/backward distance calculation using (target - active + total) % total vs (active - target + total) % total
A5Tease for all modesPer-mode implementations: scroll (scrollLeft wiggle), slide (translateX offset), fade (opacity flash to 0.3), carousel (viewport shift -5%), cube (rotation wobble 15deg)
A6Counter card paddingProper padding values on counter overlay
A7Overlay 50% centeringtranslateX(-50%) and translateY(-50%) applied in resolveOverlayPosition() when left/top are set
A8Thumbnail transparent colorsUse #ffffff00 instead of 'transparent' for consistent color picker behavior
A9Center slides -> "Peek"Renamed, uses scroll-snap-align: center + scroll-padding: 0 ${(100-slideWidth)/2}%
A10Timer dotsLine shape (HTML div width animation) + dot shape (SVG circle arc), rAF-based progress animation
A11Free-position z-indexz-index 3 for all free-positioned overlays via resolveOverlayPosition()
A12Feed section titleProper title for the feed configuration card
A13Scroll vs Slide distinctionScroll = native CSS scroll-snap; Slide = JS-driven translateX with CSS transition

New Features (B1-B3)

IDFeatureImplementation
B1Per-slide counterslideCounterPlacement: 'per-slide' renders {i+1}/{total} inside each slide (top-right, absolute positioned)
B2Slide editing preview syncBuilder dispatches slider-editing-slide CustomEvent with editingSlideIndex; engine pauses autoplay/tease and navigates to that slide
B33D Perspective CarouselNew carousel transition mode with depth-based positioning, scaling, rotation, and opacity

New Transition Modes (COMPLETE)

6 transition modes total, presented in a 3+3 grid in the config panel:

ModeTypeDescription
ScrollNative CSSCSS scroll-snap with rubber-band feel on mobile. flex container, scroll-snap-type: x mandatory, scroll-snap-align: start (or center for peek). Smooth scrollTo() for programmatic navigation.
SlideJS-driventrack-inner wrapper with translateX(calc(-index * (slideWidth% + gap px))). CSS transition: transform {duration}ms cubic-bezier(0.25, 0.1, 0.25, 1). Supports gap between slides.
FadeJS-drivenAll slides absolute positioned, opacity 0. Active slide opacity 1. Non-crossfade: transitionDelay on active slide (40% of duration) creates brief blackout between slides.
CrossfadeJS-drivenSame as fade but without delay — slides cross-fade simultaneously, no blackout gap. Full transitionDuration on both in and out.
Cube3D CSSCSS 3D cube rotation with perspective, preserve-3d, backfaceVisibility: hidden. Only 2 faces rendered at once (active + target).
Carousel3D perspectivePerspective-based card stack with depth layering. All slides rendered simultaneously with computed transform, opacity, and filter. Two layout modes: Stack and Ring.

Cube Transition — How It Works

Math:

  • Perspective set to containerSize (1x container width or height)
  • Scene pulled back by translateZ(-halfSize) so the active face (at translateZ(+halfSize)) lands at Z=0
  • This gives scale = perspective / (perspective - 0) = 1.0 — no magnification on the front face

Navigation flow:

  1. navigateCube(index) sets cubeTargetIndex, enables cubeAnimating (CSS transition)
  2. cubeRotation increments by +/-90 degrees depending on direction
  3. After transitionDuration ms, animation state resets: cubeAnimating = false, activeIndex = index, cubeRotation = 0
  4. Only 2 slides rendered via cubeFaceIndices: active + target

Face positioning:

  • Active face: transform: translateZ(halfSize px) — sits at scene origin
  • Target face: rotate{axis}(+-90deg) translateZ(halfSize px) — positioned on the adjacent cube face
  • Direction determined by shortest-path calculation: forwardDist = (target - active + total) % total vs backwardDist

Vertical mode:

  • Uses rotateX instead of rotateY
  • Sign flip (isVertical ? 1 : -1) for correct rotation direction

Background:

  • Cube scene element receives backgroundProperties from the block to prevent parent background from showing through during rotation (flash fix)

Two layout modes: Stack and Ring.

Stack Layout (default)

Linear depth layering with perspective transform.

Positioning:

  • Each slide gets width: slideWidth%, left: centerOffset% (where centerOffset = (100 - slideWidth) / 2)
  • Active slide: translateX(0) scale(1) rotateY(0deg), opacity 1, zIndex 10
  • Adjacent slides: offset by spacing * distance %, scaled by Math.pow(scale, distance). Distance-1 slides (directly beside active) are fully opaque; distance-2+ fade by Math.pow(opacity, distance)
  • Up to 5 visible slides (2 per side via carouselVisibleIndices), slides beyond maxVisible get opacity: 0
  • Z-index decreases with distance: 10 - distance * 2
  • Rotation applied per distance: rotSign * (isPrev ? rotation : -rotation) * distance

Config options:

  • Scale: 0.3-1.0 (default 0.8) — how much each depth level shrinks
  • Rotation: 0-30 degrees (default 5) — Y-axis tilt per depth level
  • Opacity: 0-1.0 (default 0.6) — how much each depth level fades
  • Blur: 0-10px (default 0) — Gaussian blur per depth level
  • Spacing: 10-80% (default 40) — horizontal offset between depth levels
  • Perspective: 200-3000px (default 1200) — only for stack layout

Ring Layout (NEW)

Circular distribution with JS-driven animation instead of CSS transitions.

Animation mechanism:

  • ringAngle state tracks continuous, unbounded rotation angle in degrees (not clamped to 360)
  • animateRing() method uses requestAnimationFrame with ease-out cubic interpolation
  • Animates from current ringAngle to target angle over transitionDuration ms
  • All ring slides have transition: 'none' — JS controls the animation

Math:

  • angleStep = (2 * PI) / totalSlides
  • Slide angle: (slideIndex * angleStep - ringAngle * π/180) — continuous, no wrapping
    • As ringAngle increases, slides rotate backward (counterclockwise appearance)
    • Using radians internally: ringAngle * π/180 converts degrees to radians
  • X position: sin(angle) * spacing — horizontal offset from center
  • Depth: cos(angle) — ranges from -1 (back of ring) to 1 (front)
  • Normalized depth: norm = (depth + 1) / 2 — maps to 0 (back) to 1 (front)

Visual properties interpolated from norm:

  • Scale: minScale + (1 - minScale) * norm — back slides small, front slides full size
  • Opacity: Front-half slides (depth >= 0) are fully opaque (1.0). Back-half slides (depth < 0) interpolate from minOpacity to 1.0. This prevents seeing through foreground slides.
  • Blur: Front-half slides (depth >= 0) have no blur. Back-half slides interpolate from maxBlur to 0.
  • Z-index: round(norm * 10) — proper layering
  • Tangential tilt: rotSign * -rotation * sin(angle) — side slides face inward

Shortest-path navigation:

  • Forward distance: (target - active + total) % total
  • Backward distance: (active - target + total) % total
  • Choose direction (forward/backward) based on which is shorter
  • Ring rotates continuously in that direction (no wrapping detection needed)

Rapid-click handling:

  • animateRing() starts from current ringAngle (which may be mid-animation)
  • Previous animation frame is cancelled before starting a new one
  • ringAnimationFrame ID tracked and cleaned up in beforeUnmount

When looping, slides that cross from one side to the other would normally animate through the center, creating a visual "jump" glitch. Solutions differ by layout:

Stack mode — departing approach:

  1. prepareCarouselTransition(newIndex) detects slides that change sides (oldIsPrev !== newIsPrev)
  2. Wrapping slides are added to carouselDeparting Map with their current direction and distance + 1
  3. These slides animate off-screen in their current direction (prevents empty edge during animation)
  4. After transitionDuration ms: slides added to carouselNoTransition Set (disables CSS transition), carouselDeparting cleared
  5. Next frame: carouselNoTransition cleared, slides snap to new positions without animation

Ring mode — no wrapping needed: Since ringAngle is continuous and unbounded, slides rotate naturally without wrapping artifacts. No prepareRingTransition logic required — the ring simply rotates to the shortest-path angle.

In carousel mode, the container background is hidden (backgroundProperties not applied to main container). Without this, the background color would show between the spaced-out slides, which looks wrong for carousel layouts. Instead, each slide gets backgroundProperties individually.

Autoplay System — How It Works

Timer:

  • setInterval with interval = autoplayInterval * 1000 + transitionDuration
  • The extra transitionDuration ensures the previous transition completes before the next one starts

Skip conditions (checked each tick):

  • isPaused — mouse hovering over track (mouseenter/mouseleave)
  • !inView — component not in viewport (from DataStore.inView)
  • autoplayStopped — user has manually navigated
  • activeSlideHasVideo — video slide controls its own timing

User interaction stops autoplay:

  • stopAutoplayOnInteraction() called from: scrollPrev(), scrollNext() (arrow clicks), scrollToSlide() (dot/thumbnail clicks, non-autoplay), onScroll() (manual scroll detection), onTouchEnd() (swipe)
  • Sets autoplayStopped = true, clears interval, stops timer animation

Scroll mode autoplay guard:

  • isAutoplayScrolling flag set to true before autoplay-triggered scrollTo()
  • onScroll() handler checks this flag — if true, does NOT call stopAutoplayOnInteraction()
  • Flag reset to false after scroll detection processes

Timer Dots — How It Works

Condition: showTimerDots = dots enabled AND autoplay enabled AND autoplayIndicatorStyle === 'timer'

When timer dots are active, regular dots are NOT shown (mutually exclusive via v-if).

Two shapes determined by dotShape:

  • Legacy mapping: circle/square -> 'dot', pill -> 'line'

Dot shape (SVG circle arc):

  • SVG viewBox="0 0 36 36" with r=15.9
  • Background circle: filled with dotColor
  • Progress arc: stroke-dasharray = circumference, stroke-dashoffset = circumference * (1 - progress)
  • Arc rotated -90deg to start from top (transform="rotate(-90 18 18)")
  • Only rendered on the active dot when autoplay is not stopped

Line shape (HTML div width animation):

  • Active timer dot: 3x wider than inactive (same as regular line dots)
  • Progress fill div: absolute positioned, width: (timerProgress * 100)%
  • Background color: dotActiveColor, border-radius: inherit
  • Only rendered on the active dot when autoplay is not stopped

Animation system:

  • requestAnimationFrame loop started by restartTimerAnimation()
  • Measures elapsed = performance.now() - timerStartTime
  • Progress = min(1, elapsed / (autoplayInterval * 1000))
  • Restarts per slide change via activeIndex watcher
  • Restarts on inView becoming true
  • Stopped on inView becoming false, on stopAutoplayOnInteraction()

On stop: Timer dots remain visible (showing which slide is active) but progress animation freezes. Active dot switches from dotColor (progress background) to dotActiveColor (highlight color) so the active slide is still clearly indicated. Regular dots do NOT appear as a fallback.

Tease System — How It Works

Trigger: Runs once on first inView change to true, controlled by teased boolean flag.

Configuration:

  • teaseConfig.delay: seconds before tease starts (default 0.5s)
  • teaseConfig.duration: duration of each tease cycle (default 0.8s)
  • teaseConfig.iterations: number of tease cycles (default 1)

Easing function (mapAdvancementRatio):

  • Complex component: ((1 - cos(2PI * ratio^0.7)) / 2)^2 — weighted 95%
  • Parabolic component: 4*ratio - 4*ratio^2 — weighted 5%
  • Creates a smooth wiggle-and-return effect

Per-mode implementations:

ModeTease behavior
ScrollTemporarily disables scroll-snap, wiggles track.scrollLeft/scrollTop by up to 20% of container width
SlideOffsets track-inner translateX/Y by -15% using inline style override
FadeFlashes next slide opacity to 0.3 briefly
CarouselShifts carousel viewport by -5% along primary axis
CubeWobbles cube scene rotation by 15 degrees

Cancellation: stopTease() called by scrollToSlide() — cancels teasingAnimationFrame, resets all inline styles. For cube mode, restores the base transform (translateZ(-hs)px rotateAxis(0deg)) instead of clearing to empty string (prevents Vue :style binding from not re-applying).

Peek Mode (Center Slides) — How It Works

  • Only active when centerSlides === true AND slideWidth < 100
  • Scroll mode only
  • Sets scroll-snap-align: 'center' on all slides
  • Sets scroll-padding: 0 ${(100 - slideWidth) / 2}% on track
  • Effect: first slide is flush-left, last slide is flush-right, middle slides are centered with adjacent slides peeking on both sides
  • Config UI: toggle with info text explaining the slideWidth requirement

Vertical Mode

All transitions support vertical via the isVertical computed property (direction === 'vertical').

Axis-dependent property swaps:

  • Transform axis: translateX -> translateY, rotateY -> rotateX
  • Container dimension: clientWidth -> clientHeight
  • Scroll position: scrollLeft -> scrollTop
  • Scroll-snap type: x mandatory -> y mandatory
  • Flex direction: row -> column
  • Arrow rotation: 90deg CSS rotation in vertical mode
  • Touch handling: dy becomes primary axis delta
  • Cube: rotateX with sign flip for correct direction

Config Panel Organization

4 tabs: Style | Nav (Navigation) | Behavior | Slides

Style tab:

  • Template selector (TemplateButton)
  • Block properties card: background (color/gradient/image), border-radius, border (width/style/color)
  • Layout card: flex alignment, size (width/height), position (margin), slide padding (4-sided)
  • Effects card: edge fade, box shadow, rotation

Navigation tab:

  • Arrows card (toggle-gated): style, custom image (single/separate), size, color, background, border-radius, position (inside/outside/free), offset X/Y, free position inputs
  • Dots card (toggle-gated): colors (inactive/active), size, gap, shape (dot/line), border-radius, position (overlay/above/below/free), autoplay indicator style (none/timer)
  • Slide counter card (toggle-gated): placement (overlay/per-slide), color, font size, position (4 corners + free)
  • Progress bar card (toggle-gated): color, height, position (top/bottom/free)
  • Thumbnails card (toggle-gated): alignment, source block, size, gap, fit, margin, padding, border-radius, bg color, border color, active color, border width, position

Behavior tab:

  • Behavior card: direction (horizontal/vertical), transition type (3+3 grid), transition duration (100-2000ms), carousel-specific options (layout, scale, rotation, opacity, blur, spacing, perspective)
  • Slide Layout card: slide width (%), gap (px), peek toggle
  • Autoplay card (toggle-gated): interval (1-60s), loop video toggle
  • Loop toggle (independent of autoplay)
  • Tease card (toggle-gated): delay, duration, iterations

Slides tab:

  • Feed card: URL, filter rules, custom params, sort key/order
  • Child creative redirect (link to master for editing)
  • Slide pagination dots (navigating between slides in the editor)
  • Slide editor: per-slide config via SlideSection component with add/remove slide controls
  • Maximum 25 slides

Recent Bug Fixes (Phase 4)

#FixDetails
1Transition layout padding3+3 grid via transitionOptionGroups computed, full-width OptionList, proper padding
2Autoplay waits for transitionInterval = autoplayInterval + transitionDuration (replaced duration cap approach)
3Timer dots line shapeHTML div with animated width (not SVG rect), inherits dot border-radius
4Loop independent of autoplayMoved loop toggle outside v-if="blockData.autoplay" so it's always visible
5Cube 3D mathScene translateZ(-halfSize), active face at translateZ(+halfSize) lands at Z=0, scale=1.0
6Carousel perspective configcarouselPerspective property (200-3000px, default 1200), only shown for stack layout
7Carousel slides use slideWidthInline width and left on each slide, overriding class-based 100% width
8Timer dots in scroll modeisAutoplayScrolling flag prevents onScroll() from killing autoplay during autoplay-triggered scrolls
9Click overrides teasestopTease() cancels animation + resets inline styles before any navigation
10Cube tease scale-up fixTease end handler restores translateZ(-hs) rotateAxis(0deg) instead of clearing to empty string
11Cube background flashCube scene element gets backgroundProperties to prevent parent from showing through
12Arrow z-indexIncreased from 2 to 10 (above dots/thumbnails at z-index 2)
13Default arrow free positionChanged from 4px to 2% for responsive defaults
14Button templates in sub-blocksNested lookup in updateButtonTemplate() — searches inside parent blocks for sub-blocks
15Carousel ghost effectRemoved opacity from carousel slide transitions (was causing mid-slide transparency)
16Carousel "jump to 1" bugWrapping detection + departing system: wrapping slides animate off-screen instead of snapping
17Carousel container background hiddenBackground not applied to main container in carousel mode (shows between slides otherwise)
18Carousel ring layoutNew carouselLayout: 'ring' option with circular 2D projection
19Carousel opacity removed from transitionsOnly transform and filter in CSS transition, no opacity — prevents mid-animation transparency
20Ring carousel JS-driven animationReplaces CSS transitions with requestAnimationFrame + ease-out cubic easing. Uses continuous ringAngle state instead of activeIndex for positioning. Prevents wrapping artifacts where slides would fade/disappear.
21Timer dot active color when stoppedWhen autoplay is stopped (user interacted), active timer dot now uses dotActiveColor instead of dotColor. Line-shape timer dots check autoplayStopped to determine which color to use.
22Carousel foreground slides fully opaqueRing: front-half slides (depth >= 0) get opacity: 1 and blur: 0. Stack: distance-1 slides (directly beside active) are fully opaque with no blur. Only back-half/distance-2+ slides fade. Prevents seeing through foreground slides.

Known Issues / TODO

Needs Testing

  • Video upload E2E (awaiting BunnyCDN env keys for full pipeline testing)
  • Cube transition in ad iframes (3D CSS transforms can be unstable in some iframe contexts)
  • Carousel ring layout with various slide counts (especially edge cases: 2 slides, 3 slides, large numbers)
  • Timer dots with all transition modes (verify animation restarts correctly for each)
  • Carousel departing system edge cases (rapid navigation, very fast transition durations)

Not Yet Implemented

  • templateModified tracking — the confirm dialog in handleTemplateSelect() references blockData.templateModified but no Vuex mutation ever sets it. Currently, template switching always applies immediately without a confirmation warning.
  • Easing functions for transitions — TransitionTiming type exists in the type system with easing options (ease, ease-in, ease-out, ease-in-out, linear) but these are not wired to the slider config. Currently hardcoded to ease for carousel, cubic-bezier(0.25, 0.1, 0.25, 1) for slide.
  • Thumbnails free mode: use alignment icons (like TextConfiguration) instead of text labels "Left" / "Center" / "Right"
  • Default thumbnail positioning to alignment-based (left bottom) instead of center
  • slideWidthBreakpoints — the runtime engine supports responsive breakpoints (effectiveSlideWidth computed checks a slideWidthBreakpoints object against container width), but the config panel has no UI to configure breakpoints

Config UI Improvements

  • Template visuals need polish — templates are functionally correct but could benefit from more distinctive visual differentiation
  • Consider adding more template presets beyond the current 4
  • Thumbnails free mode may have cropping issues at extreme positions

Key Files

Application-Frontend

FilePurpose
src/pages/Chatbots/components/BuilderVisuals/Blocks/data/types.tsSliderV2Properties type definition (~100 typed properties), SliderV2Template enum, OverlayPosition type
src/pages/Chatbots/components/BuilderVisuals/Blocks/data/defaults.tsDefault values for all properties (sliderV2Defaults object)
src/pages/Chatbots/components/BuilderVisuals/Configuration/configs/SliderV2Configuration.vueMain config panel (~1500 lines, 4 tabs, template selection, slide editor with pagination)
src/pages/Chatbots/components/BuilderVisuals/Configuration/components/SlideSection.vuePer-slide config (image overrides, video upload, link URL, sub-block overrides)
src/pages/Chatbots/components/BuilderVisuals/Configuration/components/SlideSectionsPagination.vueSlide pagination dots for navigating between slides in the editor
src/pages/Chatbots/components/BuilderVisuals/Configuration/components/OverlayPositionSection.vueShared position picker component (preset buttons + free mode with 4-sided inputs)
src/templates/sliderV2/index.tsBarrel exports for template functions
src/templates/sliderV2/productCarousel.tsProduct Carousel template preset
src/templates/sliderV2/imageGallery.tsImage Gallery template preset
src/templates/sliderV2/testimonial.tsTestimonial template preset
src/templates/sliderV2/story.tsStory template preset
src/templates/index.tsTemplate registry, updateSliderV2Template(), updateButtonTemplate() (with nested sub-block lookup)

Creative-Engine

FilePurpose
src/components/creative/CreativeSliderBlockV2/CreativeSliderBlockV2.vueRuntime component (~2080 lines): template, all computed styles, navigation methods, autoplay, tease, timer dots, carousel wrapping, cube navigation, feed loading, video control, touch handling, analytics

Architecture

Application-Frontend (Builder / Config)
=========================================

SliderV2Configuration.vue (config panel, ~1500 lines)
  |-- 4 tabs: Style | Navigation | Behavior | Slides
  |-- ~90 config options spread across tabs
  |-- Uses shared components:
  |     Card, OptionRow, OptionList, OptionButton, DelaySection,
  |     DurationSection, IterationsSection, ColorInput, ImageInput,
  |     InputField, BorderRadiusSection, BorderStyleSection,
  |     BackgroundStyleSection, BoxShadowSection, FlexAlignmentSection,
  |     SizeSection, PositionSection, PaddingSection, RotationSection,
  |     SliderEdgeFadeSection, OverlayPositionSection, TabNavigation,
  |     TemplateButton, SlideSection, SlideSectionsPagination,
  |     SliderFeedSection, DeleteButtonWithExpandingConfirmation
  |-- Vuex store: configurationLogic mixin for property updates
  |-- sliderHelpers mixin: getFuzzyIndex, pagination math
  |-- masterUrl mixin: link to master creative for child creatives
  |-- ScrollingAnimator class: eased scroll animation for slide editor

types.ts
  |-- SliderV2Properties (BlockBase & {...})
  |-- SliderV2Template enum
  |-- OverlayPosition type

defaults.ts
  |-- sliderV2Defaults: all default values
  |-- Registered in blockDefaults map

templates/
  |-- sliderV2/productCarousel.ts
  |-- sliderV2/imageGallery.ts
  |-- sliderV2/testimonial.ts
  |-- sliderV2/story.ts
  |-- index.ts: updateSliderV2Template(), registry


Creative-Engine (Runtime)
=========================================

CreativeSliderBlockV2.vue (runtime engine, ~2080 lines)
  |
  |-- Template Layer (HTML)
  |     |-- Arrows (left/right buttons, SVG or custom image)
  |     |-- Track container (event listeners: scroll, keydown, mouse, touch)
  |     |     |-- [Cube mode] cube-viewport > cube-scene > 2 cube-faces
  |     |     |-- [Carousel mode] carousel-viewport > all slides with computed transforms
  |     |     |-- [Slide mode] track-inner > all slides with translateX
  |     |     |-- [Scroll/Fade/Crossfade] slides directly in track
  |     |     |-- Each slide renders:
  |     |           |-- <video> element (if slide has video)
  |     |           |-- Sub-block components (text, graphic, button, HTML)
  |     |           |-- Per-slide counter (if enabled)
  |     |-- Dots (regular pagination dots)
  |     |-- Timer Dots (SVG arc or HTML progress bar)
  |     |-- Thumbnails (scrollable strip with background-image)
  |     |-- Slide Counter (overlay 1/N display)
  |     |-- Progress Bar (fill width)
  |
  |-- Computed Layer
  |     |-- Mode detection: isFadeMode, isSlideMode, isCubeMode, isCarouselMode
  |     |-- isJsNavigated: all modes except scroll
  |     |-- effectiveSlideWidth: supports responsive breakpoints
  |     |-- styles: generates ALL CSS as JavaScript objects (~500 lines)
  |     |     |-- Wrap, container, track, arrows, dots, timer dots,
  |     |         thumbnails, counter, progress bar, slide video,
  |     |         slide styles (per-mode: scroll/fade/slide/cube/carousel)
  |     |-- Carousel computeds: carouselVisibleIndices, cubeSceneStyle, trackInnerStyle
  |     |-- Timer computeds: showTimerDots, timerCircumference, timerDashOffset
  |
  |-- Methods Layer
  |     |-- Navigation: scrollToSlide, scrollPrev, scrollNext, navigateCube
  |     |-- Carousel: carouselSlideStyle, carouselRingSlideStyle,
  |     |             prepareCarouselTransition, prepareRingTransition
  |     |-- Cube: cubeFaceStyle, navigateCube
  |     |-- Autoplay: startAutoplay, stopAutoplay, stopAutoplayOnInteraction
  |     |-- Timer: restartTimerAnimation, stopTimerAnimation
  |     |-- Tease: tease (per-mode), stopTease
  |     |-- Touch: onTouchStart, onTouchMove, onTouchEnd
  |     |-- Video: slideVideoUrl, playSlideVideo, pauseSlideVideo,
  |     |          onVideoTimeUpdate, onVideoEnded
  |     |-- Thumbnails: getSlideThumbnail, scrollThumbnailIntoView
  |     |-- Feed: loadFeed (proxy URL construction, JSON fetch)
  |     |-- Analytics: sendSwipeAnalytics, sendFirstAnalyticsEvent
  |     |-- Helpers: resolveOverlayPosition, canShowSlide, slideClasses,
  |     |            dotClasses, timerDotClasses, slideInlineStyle
  |
  |-- Watchers
  |     |-- activeIndex: video control, thumbnail scroll, timer restart
  |     |-- autoplay/autoplayInterval: start/restart autoplay
  |     |-- inView: autoplay, tease, video, timer
  |     |-- feedOverridesFromDataStore: debounced feed reload
  |
  |-- Lifecycle
        |-- mounted: tease trigger, autoplay start, feed load, editing listener
        |-- beforeUnmount: cleanup (autoplay, timer, tease, editing listener)

Property Reference

Layout Properties

PropertyTypeDefaultDescription
slideWidthnumber100Percentage width of each slide (100 = full width)
gapnumber0Pixel gap between slides
slidePaddingobjectall 0pxInner padding within each slide (4-sided)
directionstring'horizontal''horizontal' or 'vertical'
centerSlidesbooleanfalsePeek mode — center slides with adjacent peek

Arrow Properties

PropertyTypeDefaultDescription
arrowsbooleantrueShow navigation arrows
arrowColorstring'#55555599'SVG arrow color
arrowStylestring'default''default' (SVG) or 'custom' (image)
arrowImageModestring'single''single' (mirrored) or 'separate'
arrowImageUrlstring''Custom arrow image URL (single mode)
arrowImageUrlLeftstring''Left arrow image (separate mode)
arrowImageUrlRightstring''Right arrow image (separate mode)
arrowSizenumber32Arrow button size in px
arrowBackgroundstring'rgba(255,255,255,0.7)'Arrow button background
arrowBorderRadiusstring'50%'Arrow button border radius
arrowPositionstring'inside''inside', 'outside', or 'free'
arrowOffsetnumber4X-axis offset in px (inside/outside)
arrowOffsetYnumber0Y-axis offset in px (inside/outside)
arrowFreeLeftPositionobjecttop:50%, left:2%Free position for left arrow
arrowFreeRightPositionobjecttop:50%, right:2%Free position for right arrow

Dot Properties

PropertyTypeDefaultDescription
dotsbooleanfalseShow pagination dots
dotColorstring'rgba(255,255,255,0.5)'Inactive dot color
dotActiveColorstring'#ffffff'Active dot color
dotSizenumber8Dot size in px
dotShapestring'dot''dot' or 'line'
dotGapnumber6Gap between dots in px
dotBorderRadiusstring'50%'Dot border radius
dotPositionOverlayPositionoverlayPosition (overlay/above/below/free)

Indicator Properties

PropertyTypeDefaultDescription
progressBarbooleanfalseShow progress bar
progressBarColorstring'#ffffff'Progress bar fill color
progressBarHeightnumber3Progress bar height in px
progressBarPositionOverlayPositionbottomPosition (top/bottom/free)
slideCounterbooleanfalseShow slide counter
slideCounterColorstring'#ffffff'Counter text color
slideCounterFontSizenumber14Counter font size in px
slideCounterPlacementstring'overlay''overlay' or 'per-slide'
slideCounterPositionOverlayPositiontop-rightPosition (4 corners + free)
autoplayIndicatorStylestring'none''none' or 'timer' (timer dots)

Behavior Properties

PropertyTypeDefaultDescription
transitionstring'scroll'Transition mode (scroll/fade/crossfade/slide/cube/carousel)
transitionDurationnumber400Transition duration in ms
autoplaybooleanfalseEnable autoplay
autoplayIntervalnumber5Seconds per slide
loopbooleanfalseLoop navigation
teasebooleantrueEnable tease animation
teaseConfigobjectTease settings
edgeFadebooleanfalseEdge gradient mask
PropertyTypeDefaultDescription
carouselLayoutstring'stack''stack' or 'ring'
carouselScalenumber0.8Depth scale factor (0.3-1.0)
carouselRotationnumber5Y-axis rotation per level (0-30 deg)
carouselOpacitynumber0.6Depth opacity factor (0-1.0)
carouselBlurnumber0Blur per depth level (0-10 px)
carouselSpacingnumber40Horizontal spacing (10-80%)
carouselPerspectivenumber1200CSS perspective (200-3000 px, stack only)

Feed Properties

PropertyTypeDefaultDescription
feedbooleanfalseEnable feed data
feedUrlstring''Feed URL
feedFilterRulesarray[]Filter rules (key/value/type)
feedCustomParamsarray[]Custom URL params (key/value)
feedSortKeystring''Sort key
feedSortDescendingbooleanfalseSort descending

Internal documentation