Stagger entry is the "DOM-order bounce-in on screen entry" pattern every game-style UI reaches for. Luna has no separate stagger system — it's just a UIViewTransitionEntry whose Target is an ElementSelector matching multiple elements with a non-zero StepMs between them. The same machinery handles single-target animations and multi-target stagger passes.

The convention

Mark descendants you want to bounce-in with the enter-stagger USS class:

xml
<ui:VisualElement class="screen-root"> <ui:Label class="enter-stagger" text="Welcome" /> <ui:VisualElement class="enter-stagger menu-buttons"> <ui:Button class="enter-stagger" text="Continue" /> <ui:Button class="enter-stagger" text="New Game" /> <ui:Button class="enter-stagger" text="Settings" /> </ui:VisualElement> </ui:VisualElement>

enter-stagger is convention, not a hard-coded selector — any USS class works. The class only means something because your transition entry's Target selector names it.

Author on UIViewComponent — designer-driven, no code

On any view backed by a UIViewComponent, add one entry to the inspector _transitions list:

FieldValue
AnimationA TransitionSequenceAsset containing the bounce-in sequence (opacity 0→1 / scale 0.85→1, or any preset you like)
TargetClass Name = enter-stagger
TriggerFadeInStart
StepMs80 (delay between successive matches)

When the view fades in, every descendant matching the class plays the animation in DOM order, 80 ms apart. No code at the call site. Re-fires on every push — OnFadeInStart triggers it.

Equivalent in code

UIViewTransitionEntry is just a serializable POCO — same in code:

csharp
myUIView.AddTransition(new UIViewTransitionEntry { Animation = bounceAsset, Target = new ElementSelector { ClassName = "enter-stagger" }, Trigger = FadeEventTrigger.FadeInStart, StepMs = 80L, });

Per-element timing in code

When you need ordering or per-element delays that DOM order can't express — e.g. a "3rd → 2nd → 1st" podium reveal where the DOM is positional rather than ranked — play your bounce TransitionSequenceAsset directly on each element with a scheduled delay:

csharp
[SerializeField] TransitionSequenceAsset _entryBounce; void PlayPodiumReveal(VisualElement body) { PlayOn(body.Q<VisualElement>("Podium3"), 0L); PlayOn(body.Q<VisualElement>("Podium2"), 100L); PlayOn(body.Q<VisualElement>("Podium1"), 200L); } void PlayOn(VisualElement target, long delayMs) { if (target == null) return; var player = _entryBounce.CreatePlayer(); if (delayMs <= 0) player.Play(target); else target.schedule.Execute(() => player.Play(target)).ExecuteLater(delayMs); }

If DOM order happens to match the desired reveal order, the inspector path (multi-match Target + StepMs) is simpler — only reach for explicit code timing when DOM and reveal disagree.

The canonical bounce preset

Build a TransitionSequenceAsset once and drop it in every screen's transition entry. The Luna samples ship one; the equivalent code is:

csharp
new TransitionSequence() .Together(g => g .Opacity(1f, 0.32f, EasingMode.EaseOutCubic) .Scale(1f, 0.36f, EasingMode.EaseOutBack)) .ClearInlineStyles(StyleProperty.Opacity, StyleProperty.Scale) .Build();

For matched elements, set inline initial state at the start of the sequence (opacity = 0, scale = 0.85) — or rely on a USS rule scoped to .enter-stagger that does the same.

Translate is deliberately not animated. Some marked elements carry USS-declared translates for centering (translate: -50% 0), and setting inline translate at entry overrides those, snapping the element off-anchor for the duration. Scale + opacity produces a sufficient grow-in feel without the conflict.

Public API

csharp
namespace CupkekGames.Luna { [Serializable] public class UIViewTransitionEntry { public TransitionSequenceAsset Animation; public ElementSelector Target; // default = no filters (the view root) public FadeEventTrigger Trigger; // default = FadeInStart public long StepMs; // default = 0 } [Serializable] public class ElementSelector { public enum Scope { Self, Children, Descendants } public enum ElementType { Any, Label, Button, TextField, Toggle, Slider, ScrollView, Image } public Scope ScopeValue { get; set; } // default = Self public ElementType ElementTypeValue { get; set; } // default = Any public string Name { get; set; } // exact name match; empty = no filter public string ClassName { get; set; } // USS class match; empty = no filter } }

See also

  • Transition Animation — the builder used to author the bounce asset
  • UIViewComponent — author transition entries in the inspector
  • Source: Runtime/Scripts/UIView/UIViewTransitionEntry.cs, Runtime/Scripts/ElementSelector/ElementSelector.cs

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