Skip to content

Default motion for the remaining components + unify the two animation paths #20

Description

@KealanAU

Filed in May when the premise was that neither animation path worked reliably on native. That's no longer true — Vy UI animates today. This is now the next piece of work, not a hole in what's shipped: give the components that still have no motion a default, and collapse the two systems into one.

What animates today

  • Overlays — Dialog, AlertDialog, Sheet, DropdownMenu, via the shared vyui-fade-* / vyui-zoom-* / vyui-slide-* keyframes in packages/core/src/components/Presence/presence.css, driven by Presence-toggled ui-entering / ui-leaving classes. Runs on native and web.
  • Kit surfaces — Modal, Drawer, Tray, Toast, Progress carry their own animation/transition in the component + theme files.
  • Gesture settles — Draggable, SwipeAction, ToastSwipe and Swiper's snap loop animate through main-thread element.animate() (shared/composables/useAnimate.ts).

What doesn't

  • Accordion, Collapsible, Popover, Combobox, Tabs, Checkbox indicator, RadioGroup indicator — appear and disappear instantly. Each exposes reka-ui-style forceMount so a consumer can wrap it in their own transition, but there's no built-in motion.
  • List reorder / TransitionGroup FLIP — unsupported. No synchronous bounding-rect from the background thread.

The work

  • Default motion for the components above. Mostly a matter of extending the presence.css vocabulary — the classes and lifecycle already exist.
  • One system, not two. Overlays animate via CSS keyframe classes; gestures via useAnimate() / bespoke MT loops. Durations and easings are defined twice with nothing shared between them. Pick the canonical path (or a shared token layer over both) before adding more.
  • useAnimate() into the Presence mount/unmount lifecycle — portalled enter/leave is CSS-only today.
  • FLIP stays parked until there's a way to read layout synchronously.

Platform notes (learned on device, keep in mind when picking the path)

  • CSS keyframes are the resilient path. Class/keyframe-driven animation survives a background-thread style flush; main-thread inline-style animation gets wiped by any BG re-render. transition: height between two concrete px values also tweens on native (measure via @layoutchange, then px→px — MT setStyleProperty('height') is a no-op).
  • Lynx Web's animate() ignores WAAPI fill and easing (mapTimingOptions reads only fillMode/timingFunction/iterationCount). Every call site writes the end state inline first and passes both key spellings. lynx-family/lynx#2329 is the PR that made animate() available at all.
  • Never use rAF-tick counts as a timeout — lost animation events wedged Presence on web until a wall-clock watchdog replaced them (feat: web parity for gesture surfaces #161). requestAnimationFrame itself is normalised in shared/utils/delayFrames.ts.

Dropping upstream-blocked — nothing here waits on Lynx anymore.

Related

Metadata

Metadata

Assignees

No one assigned

    Labels

    native-correctnessBroken or partially-working on Lynx nativewebLynx Web target / cross-platform parity

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions