tricklekit
Live site, interactive catalog, and shadcn-installable registry — the canonical home for the project.
Quick install (any one component): npx shadcn@latest add https://tricklekit.dev/r/typewriter.json
Open source · v0.1 shipped · MIT
trickle
Pure-CSS text animations for React. Zero runtime, SSR-safe, copy-paste install via the shadcn registry. tricklekit.dev
What this is
trickle is forty-seven hand-tuned text-animation primitives for React, distributed shadcn-style. Every animation is a pure CSS keyframe — no framer-motion, no motion-one, no react-spring. The browser does the work; React orchestrates a couple of state changes when it has to.
42 of 47 are pure React Server Components — they ship literally zero client JavaScript. The other five (Typewriter, TypoCorrect, DecryptScramble, WordRotate, MorphSwap) need minimal client state for orchestration logic that genuinely cannot be expressed as a single keyframe. Median component weight: <1kb gzip. Animation runtime dependencies: zero.
SSR-safe by construction. The server renders the final HTML with animation classes applied, the browser starts the animation on first paint, no flash, no hydration mismatch.
Why it exists
Every popular React text-animation library makes you pay a tax to animate one word. Install framer-motion to fade in a heading and you've added 30kb of runtime JS to your bundle and forced every parent into a 'use client' boundary. The animation engine wants to live in your application's runtime, with refs into the live DOM, observing layout shifts, owning the render loop. For a library whose primary use case is "text appears nicely on a marketing page," that is profoundly the wrong tool for the job.
trickle is the argument that text animation should be CSS, and the components that prove it. The CSS Animations spec is twelve years old, GPU-accelerated, server-renderable, and declaratively complete for 80% of what marketing pages, dashboards, and hero sections actually need. The browser has been able to animate text natively since 2012 — the only reason we reach for animation libraries is because writing keyframes by hand is tedious, not because the platform is missing capability.
What I actually built
- A 47-component catalog organised by mechanism. Reveal animations (TextReveal, Typewriter, Shatter, Pixelate, Wireframe, Stamp, Compress, Tear, Mosaic, Halftone, Grain, Bounce, SpinIn, ScaleSlam, Shutter, others). Continuous loops (GradientShift, AuroraText, WordRotate, Wave, Wobble3D, Float, RainbowRoll, Flutter, PulseText, Spotlight, Magnetize, Plasma, MarqueeRibbon, Phase, NeonFlicker, CarouselFlip). Special-purpose effects (DecryptScramble, InkBleed, ConfettiText, GlitchSplit, UnderlineDraw, Echo, Reflect, Stretch, Scanline, StaticText).
- A shadcn-compatible registry I authored from scratch.
registry.jsonfeedsshadcn build, which generates a JSON manifest per component atpublic/r/<name>.json. Every endpoint resolves transitive dependencies (theTextRootshell, the per-component CSS-variable tokens, the keyframes block, the reduced-motion override) so that one CLI command installs a working component into any consuming repo. - A
TextRootorchestration shell. Components that need lifecycle awareness (mount-trigger, view-trigger, reduced-motion) compose auseTextRoothook. Pure-CSS components don't import it at all — they remain plain RSCs. The server renders withready=falseso the static fallback is visible without JS; the client hydrates and flips on the animation. No flash, no hydration mismatch. - Sub-character techniques without a JS runtime. Shatter breaks each letter into four
clip-pathshards that converge from random offsets. Pixelate resolves through a 6-step crossed-gradient mask that reads as a true 8-bit pixel grid. Wireframe drops in as a stroke outline, refines, and fills. CarouselFlip positions characters on an invisible 3D ring, spins three full revolutions, then unwraps and snaps into the spelled word. None of this usesrequestAnimationFrame; it's all keyframes and@property-typed CSS variables. - A docs site that is the spec. Next.js 15 App Router, Tailwind v4, dark-mode-first. The catalog is interactive — every strip has a tweak panel (per-component CSS-variable controls) and a replay button. There's an
/auditroute for single-strip QA renders, and an install-reference page with copy buttons for every shadcn command. - Reduced-motion as a first-class concern. Every keyframe ships with a
@media (prefers-reduced-motion: reduce)override that skips to the final visible state. The five JS-orchestrated components also exposeprefersReducedMotionfromuseTextRootso they can short-circuit too. - Build-and-validate tooling.
pnpm new <name>scaffolds a component plus a registry entry from a template.pnpm registry:buildregenerates the public manifests.pnpm registry:validateZod-checks every JSON in CI so a malformed entry can't reach the registry.
Design rubric
Every animation is judged against three axes before it ships.
Distinct motion signature
Could you name the component from the visual alone? If two animations look like the same gesture in different colors, one of them shouldn't exist. v0.1 cut four duplicates from the original draft (Drip, CurtainReveal, VerticalSlide, PaperFold) for failing this test.
Per-character expression where it matters
Generic entrance animations are easy mode. The interesting components express the concept of the animation through the character itself: Shatter breaks chars into clip-path shards, Pixelate resolves through a sub-character pixel grid, CarouselFlip positions chars on a rotating 3D ring, Wireframe draws stroke outlines per char before filling.
Fluidity
mask-image switching between gradient types mid-animation snaps discretely (browsers can't interpolate). All masked components keep mask-image constant on the class and animate only mask-size and mask-position. Same rule for clip-path polygons.
Why it's unusual
The React text-animation space is dominated by runtime-tax libraries: install 30kb of JavaScript to move a heading. trickle is the counter-argument, shipped as proof. It uses twelve-year-old CSS spec to do the work that Magic UI, framer-motion text components, and react-type-animation all do with a runtime. The result is faster (GPU-accelerated keyframes), lighter (median <1kb per component), and architecturally honest (server components stay server, no 'use client' tax).
Distribution is shadcn-CLI only by design. There is no installable npm package and no plan for one — the registry IS the distribution. You own the source the moment you run shadcn add. No version lock-in, no upgrade churn, customise freely.
Related perspective on building open-source kits with the shadcn registry pattern: HITL Kit · eval-kit.
How I describe the skill set
Modern CSS animation engineering (keyframes, the @property rule for typed custom properties, mask-composite, clip-path polygons, conic and radial gradients, backdrop-filter); SSR-safe React patterns and the discipline to keep 42 of 47 components as pure RSCs; shadcn registry CLI authoring (manifest schema, cssVars merge, registryDependencies, transitive resolution); Tailwind v4 @theme tokens; Next.js 15 App Router; TypeScript strict; design rubric authorship; accessibility (reduced-motion as a first-class build axis); and the willingness to delete four components from a draft because they failed the motion-signature test.
Most React text-animation libraries are runtime taxes. trickle is the argument that text animation should be CSS, and the 47 components that prove it. SSR-safe, <1kb median, installable component-by-component via the shadcn CLI. The registry is the distribution; the source is yours the moment you install it.