Vanilla Breeze

data-trigger

Controls when effects activate. Separates effect behavior from activation timing.

Overview

The data-trigger attribute controls when an effect activates. It separates the visual effect from its activation moment, allowing the same effect to be triggered by scroll, hover, click, or a timed delay.

If no data-trigger is set, CSS-only effects activate immediately and JS effects run on load.

scroll

Activates when the element scrolls into the viewport (10% visible threshold). Uses IntersectionObserver and fires once.

hover

Activates on pointer enter, deactivates on pointer leave. For CSS-only effects, the :hover pseudo-class drives the animation directly.

click

Toggles the effect on click. Adds [data-effect-active] on first click, removes it on second.

time:n

Activates after a delay of n milliseconds. Uses setTimeout.

view-progress, view-progress:range

Drives the effect's animation continuously from the element's view progress through the scrollport, via CSS animation-timeline: view(). Unlike scroll (which fires once via IntersectionObserver), view-progress ties the animation directly to scroll position — stop scrolling mid-reveal and the animation pauses at that exact frame.

Range variants

The range controls which slice of the element's scrollport journey drives the animation. Default is entry 0% cover 50% — animation reaches its end keyframe by the time the element is halfway covered.

Variantanimation-rangeWhen the animation plays
view-progressentry 0% cover 50%Default. Starts as element enters viewport, finishes when half-covered.
view-progress:entryentry 0% entry 100%Only while entering the viewport (bottom edge crossing).
view-progress:exitexit 0% exit 100%Only while leaving the viewport (top edge crossing). Pair with exit-slot effects (fade-out, collapse).
view-progress:covercover 0% cover 100%Across the entire time the element is in view.
view-progress:containcontain 0% contain 100%Only while element is fully visible (smaller than the scrollport).

scroll-progress (root scroller)

data-trigger="scroll-progress" ties the animation to the root scroller's total progress (0% at top, 100% at bottom of the page) rather than the element's view progress. Useful for whole-page indicators and parallax decoration.

Browser support and fallback

  • Chromium 115+ (July 2023): stable.
  • Safari 26+: partial (view() supported, scroll() more limited).
  • Firefox: behind layout.css.scroll-driven-animations.enabled.

The rules are wrapped in @supports (animation-timeline: view()). Browsers without support see the element's idle (hidden) state — no broken layout. For a one-shot fallback, add scroll as a peer trigger: data-trigger="scroll view-progress" — the existing IntersectionObserver trigger fires in older browsers; newer browsers run the timeline-driven version (CSS specificity favors the timeline rule).

prefers-reduced-motion: reduce (and the project-wide :root[data-motion-reduced] opt-in) suppress the timeline binding entirely and snap the element to its final state — no scroll-linked motion at all.

intersect, intersect:once, intersect:toggle

Generalised IntersectionObserver trigger. intersect and intersect:once fire once on first viewport entry (same behaviour as scroll). intersect:toggle re-activates on every viewport entry and deactivates on every exit — useful for decoration effects that should follow the viewport.

media:(query)

Activates while a matchMedia query matches, deactivates on mismatch. Reacts to media-query change events so a decoration switches on at a breakpoint, in dark mode, or when reduced-motion lifts.

Note: the query must not contain whitespace — data-trigger is split on whitespace into multiple triggers.

event:name

Listens for a custom DOM event by name on the element. Pairs with VB.activate(el) so app code can drive effect timing without touching data-effect-active directly.

vt (View Transition bridge)

Defers activation until VB.swap()'s view transition commits the new DOM. Bridges the data-transition system into the effect lifecycle, so an entrance animation runs in sync with the cross-fade instead of before it.

No trigger (immediate)

When data-trigger is omitted, CSS-only effects activate immediately via their CSS selectors. JS effects run their activate() function on initialization.

Trigger independence

An effect doesn't know what triggered it. The same fade-in slide-up effect behaves identically whether activated by scroll, hover, click, or a timer.

Custom triggers

Register custom triggers via the VB.trigger() API. Custom triggers work identically to built-in ones.

Attribute reference

TriggerImplementationNotes
scrollIntersectionObserverFires once at 10% visible, then disconnects. Alias of intersect:once.
hoverCSS :hover + JS eventsCSS-only effects use :hover. JS effects use pointer events.
clickaddEventListener('click')Toggles [data-effect-active].
time:nsetTimeoutn is delay in milliseconds.
intersect:once|toggleIntersectionObserveronce (default) fires on first entry; toggle activates/deactivates on every enter/leave.
view-progress[:entry|:exit|:cover|:contain]animation-timeline: view()Continuous, scroll-linked. Drives the animation from the element's view progress through the scrollport.
scroll-progressanimation-timeline: scroll()Continuous, bound to root-scroller progress (0% top, 100% bottom).
media:(query)matchMediaActivates while query matches; deactivates on mismatch. No whitespace in the query.
event:nameaddEventListenerActivates when the named DOM event fires on the element.
vtvb:vt-update-doneFires after VB.swap()'s view transition commits. Pairs with data-transition="effect:class".