React Scrim
A covering layer for React whose state is data attributes, so every animation stays in your stylesheet.
Installation
npm install react-scrimThe problem
A covering layer is trivial to draw and awkward to drive. The markup is one fixed element, but the moment it has to cover during a route change the timing leaks into JavaScript: a duration in a constant, an easing duplicated from the stylesheet, a state machine deciding when to fade. The animation stops living where you write animations.
This library keeps the layer and gives up everything else. It renders one element and writes its state onto it as data attributes. What that state looks like is CSS you already know how to write.
<Scrim until={appReady} open={isEnteringSection} variant={section}>
<SplashContent />
</Scrim>The attributes
Every state the component knows about is on the element, so the stylesheet can react to all of it. data-scrim-open while it covers, data-scrim-instant when the state must apply without animating, data-scrim-ready once until has resolved, and data-scrim-variant mirroring whatever string you passed.
<div data-scrim data-scrim-open data-scrim-instant data-scrim-ready data-scrim-variant="/react-scrim">The closed state is the one you write. Nothing in the package decides that a scrim slides rather than fades, or from which edge.
[data-scrim] {
transition-property: translate;
transition-duration: 650ms;
}
[data-scrim]:not([data-scrim-open]) {
translate: 0 100%;
}Variants
variant is a free string, so one scrim can look different depending on where it is covering for. Give a variant its own closed state and it gets its own entrance and its own exit.
[data-scrim-variant="/settings"]:not([data-scrim-open]) {
translate: -100% 0;
}Opening applies the incoming variant unanimated for one frame before it opens. Without that a transition would start from wherever the previous variant left the element, and a variant that fades leaves nothing for a variant that slides to animate from.
A note on timing
The library never reads, stores or parses a duration, which is what keeps the stylesheet authoritative. When something else has to wait for the scrim — holding a route until it covers, most often — declare the value in CSS and read it back from there.
:root {
--scrim-duration: 650ms;
}
[data-scrim] {
transition-duration: var(--scrim-duration);
}It also means prefers-reduced-motion is yours to honour in the stylesheet, and everything downstream follows without a second switch in JavaScript.