Skip to main content
The core — every entrance, exit, interaction, presence class, motion profile and stagger — works in all modern browsers that support CSS animations, custom properties and cascade layers. Newer features degrade rather than break. Each one sits behind an @supports guard, and no guard can leave an element invisible: the hidden state is only ever declared inside the block that also says how it comes back.

The full table

“not yet” is true at the time of writing and is the entry most likely to change. Check caniuse before relying on it.

Safari, specifically

Three things worth knowing if Safari is a first-class target.
Safari has no animation-timeline. Every tm-scroll-* class is inside @supports (animation-timeline: view()), so in Safari not one declaration applies and the section is ordinary, fully visible content.This is the strongest argument for the CSS approach over an IntersectionObserver: an observer-based reveal has to hide the content first and then hope its JavaScript runs.
transition-behavior: allow-discrete shipped in Safari 17.4; @starting-style shipped in 17.5. TailMotion guards both on the former, because the two shipped together in Chrome and Firefox.In Safari 17.4 exactly, native exits animate and a panel mounted already-open appears instead of animating in. Nothing breaks; one frame is missing.
::backdrop only started inheriting custom properties from its originating element in Chrome 122, Firefox 123 and Safari 17.4. Older engines resolve var() there against nothing.Every fallback in the tm-native-dialog backdrop rules is therefore a literal matching the dialog’s own unscaled default, so the dialog and its backdrop stay in step either way. Set --tm-backdrop-duration to pin it explicitly.
Two Safari-specific performance decisions are already baked in:
  • tm-view-morph skips its live blur on touch-first devices (@media (hover: none) and (pointer: coarse)), because Safari rasterizes it even on hidden panels.
  • No blur in the library exceeds 8px. Large blur radii are expensive everywhere and worst in Safari.

How the guards are verified

fails the build if any rule sets opacity: 0 or visibility: hidden outside an @supports guard, without a closed-state selector, and outside the small allowlist of two-face recipes where the counterpart is always visible. That is what makes “no guard can leave an element invisible” a checked property rather than a promise.

Cascade layers

TailMotion’s rules live in native @layer base and @layer utilities.
This only matters where both fight over the same propertyopacity-0 on a tm-presence-* panel is the realistic case. Let TailMotion own opacity, translate, scale and visibility on an element it is animating, and the question never comes up.

Not supported

@supports itself, CSS custom properties, and cascade layers are hard requirements. In practice that means Chrome 99+, Safari 15.4+ and Firefox 97+. Below that, the stylesheet parses but the @layer blocks are discarded and no class applies — content stays visible and unstyled by TailMotion, which is the correct failure mode.

React Native

Not supported. See Support.