Skip to content
zedkit
@zedtech/zedkit-ui

Animate

One typed attribute and wrapper API for identity-token motion, with a tiny SSR-safe runtime.

alpha@zedtech/zedkit-ui

Wraps: (none)

Invoice
Appears on scroll, presses down.
View props table
Who is it for?
Developers adding appear, scroll, press, longpress, hover or focus motion to any kit element.
When should I use it?
When a card, section or control should move on appear, scroll progress, press, longpress, hover or focus.
When should I pick something else?
For pinned scene crossfades use `ScrollScenes`. For section snap/smooth-scroll use `SectionScroll`. For first-paint class utilities see `Animate entrances and hover feedback with CSS utilities`. For section-shared parallax layers use `ParallaxSection` from `@zedtech/zedkit-ui/motion`.

Use it

Attribute on a card
import { AnimateObserver, Card } from "@zedtech/zedkit-ui";

<AnimateObserver />
<Card data-animate="fade-up" data-animate-delay="100" data-animate-press="scale-down">
  Invoice
</Card>

Wrapper with several triggers

Hold to pulse. Scroll to shift.

import { Animate } from "@zedtech/zedkit-ui";

<Animate on={{ appear: "fade-up", press: "scale-down", longpress: "pulse", scroll: "parallax" }}>
  Band
</Animate>

Stagger children

Write the launch email

Review the product photos

Publish the sale banner

import { Animate, Stack, Text } from "@zedtech/zedkit-ui";

<Animate on={{ appear: "fade-up" }} stagger>
  <Stack>
    <Text>One</Text>
    <Text>Two</Text>
  </Stack>
</Animate>

Kit not installed yet?

This ships on npm only; there is no copy-source brick for it.

You get
Every component from one import, with the theme tokens included. Updates arrive with your package manager.
You need
  • React 19 and Tailwind CSS 4, for example a Next.js app created with the Tailwind option.
  1. Install

    Terminal
    pnpm add @zedtech/zedkit-ui @fontsource-variable/inter @fontsource-variable/space-grotesk
  2. Load the styles

    Tailwind does not scan node_modules, so the @source line points it at the kit's class names. The path is relative to this file.

    src/app/globals.cssReplace the contents
    @import "tailwindcss";
    @import "@zedtech/zedkit-ui/styles.css";
    @source "../../node_modules/@zedtech/zedkit-ui/src/**/*.{ts,tsx}";
    @source not "../../node_modules/@zedtech/zedkit-ui/src/**/*.test.*";
    
  3. Pick a look

    Set data-identity="glass" on <html> so the finished app matches the site. Fresh app: replace src/app/layout.tsx. Existing app: merge, and keep ThemeProvider and other providers.

    src/app/layout.tsxReplace the contents
    import "./globals.css";
    import "./fonts";
    
    export default function RootLayout({ children }: { children: React.ReactNode }) {
      return (
        <html lang="en" data-identity="glass">
          <body>{children}</body>
        </html>
      );
    }
    
  4. Load the fonts

    The Glass look uses Inter and Space Grotesk; these imports self-host them.

    src/app/fonts.tsCreate the file
    import "@fontsource-variable/inter";
    import "@fontsource-variable/space-grotesk";
    
  5. Import and render

    src/app/page.tsxReplace the contents
    import { AnimateObserver, Card } from "@zedtech/zedkit-ui";
    
    export default function Page() {
      return (
        <main className="px-page-gutter py-section-sm">
          <AnimateObserver />
          <Card data-animate="fade-up" data-animate-delay="100" data-animate-press="scale-down">
            Invoice
          </Card>
        </main>
      );
    }
    

Package version 2.0.1.

Props

Prop

Type

Show inherited HTML attributes

Standard attributes from the underlying element. See MDN.

Prop

Type

Accessibility

prefers-reduced-motion: reduce leaves content visible and in place. Hover is gated to hover-capable pointers. Focus uses :focus-visible. Press ignores disabled. Longpress is decorative, never the only action. Appear does not set aria-hidden.

Troubleshooting

Nothing appears to move — mount `<AnimateObserver />` once near the app root (or call `observeAnimate()`), then check reduced motion and `data-motion` on `<html>`. Content stays visible without JS by design. Scroll parallax needs `animation-timeline: view()` or the runtime fallback. In-app reduce flashes on load — render `<MotionScript />` in `<head>` (same pattern as `IdentityScript`) so `data-motion="reduce"` is set before paint; `useMotionPreference()` is `"reduce"` on the server.

Triggers and tokens

One element can carry several triggers. Identities retune the same tokens (--zk-reveal-duration, --zk-reveal-distance, --zk-reveal-ease, --zk-stagger-step, --zk-lift-*, --zk-press-scale, --zk-longpress-ms).

TriggerAttributeWhen
appeardata-animate / data-animate-appearEnters the viewport once
scrolldata-animate-scrollProgress through the viewport (view() timeline, rAF fallback)
pressdata-animate-press:active (not disabled)
longpressdata-animate-longpressPointer held --zk-longpress-ms
hoverdata-animate-hover:hover on hover-capable pointers
focusdata-animate-focus:focus-visible

Mount <AnimateObserver /> once near the app root so appear, the scroll fallback, and longpress run. Press, hover and focus are CSS-only.

Reduced motion leaves everything visible and still. The attribute grammar and trigger semantics live in packages/ui/docs/animate-spec.md.

On this page