InteractionJuiceManipulator plays Transition Sequence assets on pointer interaction — press, release, hover — on any VisualElement, not just buttons.

Why a manipulator?

USS pseudo-states already give buttons basic feedback (.btn:active { scale: 0.95 }), but they have two hard limits:

  • :active is Button-only. The active pseudo-state is set by UI Toolkit's Clickable, which only Button has. A card, list row, or plain element never enters :active, and user code cannot set pseudo-states.
  • USS state transitions are single-keyframe. One end value per state, auto-reversing. Overshoot punches, shakes, flash-then-fade, loops — impossible in USS, natural in a sequence asset.

Simple hover feel stays cheaper in USS (:hover works on every element). Reach for the manipulator when you need press feedback on non-buttons or multi-step effects anywhere.

Slots

SlotFires onTypical asset
PressSlotpointer down (left/primary)scale to 0.94, fast ease-out, Persist
ReleaseSlotpointer up while pressed, or pointer leaving while pressed (cancel)scale back with overshoot, RestoreOnEnd
HoverEnterSlotpointer enter (mouse-only by default)scale to 1.05, Persist
HoverExitSlotpointer leavescale to 1.0, RestoreOnEnd

RestoreMode rule: hold-style slots (Press, HoverEnter) end in the held pose — their assets must use Persist, or the restore would snap the pose back. Return slots (Release, HoverExit) end at rest and should use RestoreOnEnd so the inline value is cleaned up and USS regains ownership.

Sequence nodes are USS-transition-driven, so preemption animates from the current value: releasing mid-press reverses smoothly, exactly like pseudo-state auto-reverse.

Profiles

InteractionJuiceProfileSO (Create → CupkekGames → Luna UI → Interaction Juice Profile) bundles the four slot assets plus options so one asset styles every button in the game:

  • Per-slot TransitionSequenceAsset + optional target selector
  • MouseOnlyHover (default on) — hover slots ignore touch pointers
  • SuppressUssClasses — USS classes added to the element on attach and removed on detach. Use this to silence a competing built-in USS effect: e.g. no-scale disables .btn's own hover/active scale so the profile owns scale outright.

The profile is pure data — all runtime state lives in the manipulator instance, so any number of elements can share one profile.

csharp
[SerializeField] private InteractionJuiceProfileSO _buttonJuice; button.AddManipulator(new InteractionJuiceManipulator(_buttonJuice));

Or without a profile, pass assets directly:

csharp
element.AddManipulator(new InteractionJuiceManipulator(pressAsset, releaseAsset, hoverInAsset, hoverOutAsset));

Targeting

By default all slots play on the manipulator's element. The manipulator exposes its TransitionSlotDriver for retargeting — CSS selector (downward), closest: prefix (upward), or a direct element:

csharp
var juice = new InteractionJuiceManipulator(profile); juice.Driver.SetTarget(InteractionJuiceManipulator.PressSlot, ".card__icon"); juice.Driver.SetTarget(InteractionJuiceManipulator.ReleaseSlot, "closest: .card-frame"); element.AddManipulator(juice);

Conflicts with USS transitions

If a sequence asset animates a property the element's USS still transitions (e.g. scale on .btn without suppression), the inline value wins while the sequence plays — hover stops responding mid-sequence — and restore hands control back to USS afterwards. Recoverable but janky. Either set the suppression class or target a child/wrapper instead.

One element has at most one active sequence player at a time: a hover effect and an attractor arrival aimed at the same element preempt each other by design. Target different elements (wrapper vs. content) when two systems must animate simultaneously.

Limitations

  • Disabled elements receive no pointer events in UI Toolkit, so a "denied shake" on disabled click can't be built with this manipulator.
  • Press responds to the primary button only.

Demo

The Progress Bar Juice showcase scene (Components/Juice/ProgressBarJuice) has three buttons wired with Subtle / Punchy / Bouncy profiles sharing hover assets, all suppressing no-scale. Its sibling scene Components/Juice/UICue demonstrates the declarative alternative — the UI Cue binder.

See also

Settings

Theme

Light

Contrast

Material

Dark

Dim

Material Dark

System

Sidebar(Light & Contrast only)

Light
Dark

Font Family

DM Sans

Wix

Inclusive Sans

AR One Sans

Direction

LTR
RTL