Guide
Working with CSS
Tokens, calc(), media queries, :hover, themes, and the variables Kinesis writes back.
Kinesis properties are CSS variables registered with @property and a type, such as <length> or <angle>. The browser computes their values the way it computes width, so anything valid in CSS works in them.
Tokens, units, and calc()
:root {
--space: 8px;
--brand: oklch(62% 0.2 265);
}
.layer {
--k-parallax: calc(var(--space) * 4); /* 32px */
--k-radius: 12vw;
}
.title {
font-size: 40px;
--k-parallax: 1em; /* 40px: this element's font size */
}
.chip {
--k-when: hover;
--k-background: var(--brand);
}
A value that doesn't match a property's type is ignored, like any invalid CSS.
Media queries and container queries
.hero__art { --k-parallax: 40px; }
@media (max-width: 700px) {
.hero__art { --k-parallax: 12px; }
}
@media (pointer: coarse) {
.card { --k-tilt: 0deg; }
}
Kinesis re-reads configuration after a window resize, so media queries apply on their own.
, , and classes
Kinesis picks up changes from classes, :hover, and :focus:
.card { --k-tilt: 4deg; }
.card:hover { --k-tilt: 12deg; }
.card.is-selected { --k-motion: bouncy; }
For hover effects, the hover reaction is usually better. It springs, and it doesn't flicker when the element moves away from the pointer.
Themes and stylesheets
Kinesis re-reads the page when a class or data-theme changes on <html> or <body>, and when a stylesheet is added. For changes it can't see, like :has() reacting to a distant element or a stylesheet edited through the CSSOM, call refresh():
import { refresh } from "@amineyarman/kinesis"
refresh()
What inherits
Interaction properties apply only to the element that declares them. --k-tilt on a card doesn't tilt its children.
Three context properties inherit, so you can set them once on a scope: --k-motion, --k-intensity, and --k-perspective.
Reaction progress
Every element with --k-when gets --k-progress, from 0 to 1, moving with the reaction's spring. Use it in any CSS property to animate what Kinesis has no output for:
.button {
--k-when: press;
box-shadow: 0 calc((1 - var(--k-progress)) * 6px) 0 #1a4fd6;
}
.route {
--k-when: scroll;
}
.route__dot {
offset-path: path("M 20 180 C 110 20, 290 20, 380 180");
offset-distance: calc(var(--k-progress) * 100%);
}
The dot reads its parent's progress. For scroll and view, measure an element that stays put. An element moved by its own progress measures a moving target.
--k-progress inherits, so children can use their parent's:
.card { --k-when: hover; }
.card .arrow { translate: calc(var(--k-progress) * 6px) 0; }
Pointer position
--k-track: pointer writes the pointer's position over the element to --k-pointer-x and --k-pointer-y, from 0 at the left and top to 1 at the right and bottom. With --k-progress, that makes a spotlight:
.spot-card {
--k-track: pointer;
--k-when: hover;
background:
radial-gradient(
260px circle at calc(var(--k-pointer-x) * 100%) calc(var(--k-pointer-y) * 100%),
rgb(120 160 255 / calc(var(--k-progress) * 0.35)),
transparent 65%
),
#0c1324;
}
Fast
Nothing runs while nothing moves.
Yours
Your transforms stay untouched.
Small
About 14 KB, gzipped.
Transforms and transitions
Kinesis writes translate, rotate, and scale, never transform, so your own transform keeps working. If you set translate, rotate, or scale yourself, Kinesis adds to your value. See How it works.
Don't put CSS transitions on the properties Kinesis animates. transition: all is the usual cause. The browser smooths every update a second time and the motion lags.
Debugging
initKinesis({ debug: true }) warns about inline elements that can't be transformed, conflicting transitions, unknown keywords, and 3D flattened by overflow or filter.
In DevTools, Kinesis properties appear in the Styles panel like any CSS, and the inline style shows what Kinesis writes.