Skip to content
zedkit
@zedtech/zedkit-ui

Motion

Token-driven entrance, stagger, hover and marquee utilities that switch off under reduced motion.

alpha@zedtech/zedkit-ui

Wraps: tailwindcss, @utility, @theme

Install
One package, one CSS import.
Compose
Kit components, tokens only.
Ship
Same code in light and dark.

Children of data-stagger enter one after another.

Reduced motion shows everything at once.

Who is it for?
Developers adding calm entrance and hover motion to kit pages.
When should I use it?
For first-paint entrances, staggered lists, card lift and button press feedback.
When should I pick something else?
For gesture or layout animation (drag, shared-element transitions) — use a motion library instead.

Use it

Entrance with stagger
import { Card } from "@zedtech/zedkit-ui";

<Card className="animate-fade-up stagger-1">First</Card>
<Card className="animate-fade-up stagger-2">Second</Card>

Hover feedback

Website relaunch
Lifts on hover (devices that hover).
<Card className="hover-lift">Lifts on hover (devices that hover)</Card>
<Button className="hover-press">Presses down on click</Button>

Staggered children

Write the launch email

Review the product photos

Publish the sale banner

// Children enter one after another; set "pending" first to hide them
// until they scroll into view, then switch to "in".
<Stack data-stagger="in">
  <Text>One</Text>
  <Text>Two</Text>
</Stack>

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}";
    
  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 { Card } from "@zedtech/zedkit-ui";
    
    export default function Page() {
      return (
        <main className="px-page-gutter py-section-sm">
          <Card className="animate-fade-up stagger-1">First</Card>
          <Card className="animate-fade-up stagger-2">Second</Card>
        </main>
      );
    }
    

Package version 2.0.1.

Accessibility

Every utility and attribute is switched off under prefers-reduced-motion: reduce, with content left visible and in place. Hover lift only applies on devices that hover, so it never sticks after a tap.

Troubleshooting

Nothing animates — check the OS reduced-motion setting first. Content stays hidden — `data-reveal="pending"` and `data-stagger="pending"` hide content until your code sets them to `"in"`. Stagger has no delay — `stagger-*` delays an animation class (`animate-fade-up stagger-2`), not a transition. Parallax travel follows `--zk-motion-intensity` (default 1, `0` = no shift) times `--zk-motion-mobile` (`0.5` under 40rem); override on a subtree (`.hero { --zk-motion-intensity: 0.3 }`), not as an identity token. Derived amplitude is `min(calc(var(--zk-reveal-distance) * 8), var(--zk-parallax-max-shift, 120px))` (x-axis cap `12vw`). Sticky offset is `--zk-pin-top` (`calc(var(--zk-header-height) + 1rem)`).

Tokens that drive motion

Every utility reads CSS custom properties with a fallback, so a page or a future identity can change how things move without touching a component.

TokenDefaultUsed by
--zk-reveal-duration300msanimate-fade-up, animate-fade-in, animate-pop-in, data-reveal, data-stagger, data-animate
--zk-reveal-distance0.75remupward travel of fade-up and reveal
--zk-reveal-easecubic-bezier(0.22, 1, 0.36, 1)entrance easing
--zk-stagger-step70msstagger-*, data-reveal-delay, data-stagger children
--zk-lift-duration200mshover-lift, hover-press
--zk-lift-translate0 -2pxhover-lift offset
--zk-press-translate0 1pxhover-press offset
--zk-marquee-duration40smarquee-track loop
globals.css
:root {
  --zk-reveal-duration: 400ms; /* slower entrances across the app */
  --zk-stagger-step: 90ms;
}

Reveal on scroll

Prefer data-animate / <Animate> (see Reveal and react with motion on any element): the kit observer writes data-animate-state. The older data-reveal / data-stagger attributes still style pending → in if you drive them yourself.

Write the launch email

Review the product photos

Publish the sale banner

reveal-on-scroll.tsx
import { Animate, Stack, Text } from "@zedtech/zedkit-ui";

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

Marquee

marquee-track scrolls its children in a loop. Render the content twice in the track so the -50% translate wraps seamlessly, and keep the duplicate out of the accessibility tree with aria-hidden.

On this page