zedkit
@zedtech/zedkit-ui

Animate entrances and hover feedback with CSS utilities

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

alpha

At a glance

  • Who: Developers adding calm entrance and hover motion to kit pages.
  • What: Token-driven entrance, stagger, hover and marquee utilities that switch off under reduced motion.
  • Use when: For first-paint entrances, staggered lists, card lift and button press feedback.
  • Avoid when: For gesture or layout animation (drag, shared-element transitions) — use a motion library instead.
  • See also: Theme an admin app (dark/light included), Group content in a card section

Preview

@zedtech/zedkit-ui/styles.css (utilities animate-fade-up, animate-fade-in, animate-pop-in, stagger-*, hover-lift, hover-press, marquee-track; attributes data-reveal, data-stagger)

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.

Install

pnpm add @zedtech/zedkit-ui

Latest: 0.4.0 (via changesets).

Example

Usage
import "@zedtech/zedkit-ui/styles.css"; // motion utilities ship with the theme
<Card className="animate-fade-up stagger-1">First</Card>
<Card className="animate-fade-up stagger-2">Second</Card>

Wraps: tailwindcss (v4 @utility and @theme)

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
--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

data-reveal and data-stagger are plain attributes: the kit styles them, your code decides when content is visible. Render "pending" (hidden), then switch to "in" once the element enters the viewport.

reveal-on-scroll.tsx
"use client";
import { useEffect, useId, useState } from "react";
import { Stack, Text } from "@zedtech/zedkit-ui";

export function RevealList() {
  const id = useId();
  const [state, setState] = useState<"pending" | "in">("pending");
  useEffect(() => {
    const node = document.getElementById(id);
    if (!node) return;
    const observer = new IntersectionObserver(([entry]) => {
      if (entry?.isIntersecting) {
        setState("in");
        observer.disconnect();
      }
    });
    observer.observe(node);
    return () => observer.disconnect();
  }, [id]);
  return (
    <Stack id={id} data-stagger={state}>
      <Text>One</Text>
      <Text>Two</Text>
    </Stack>
  );
}

Variants: data-reveal-variant="fade" or "pop" on a data-reveal element; data-reveal-delay="1" to "6" offsets it by that many stagger steps.

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