scradar
Version:
CSS-first scroll interaction library with progress-based animations
445 lines (375 loc) • 12.5 kB
CSS
/**
* Scradar Predefined Animations
* CSS-first approach with customizable CSS variables
*/
/* Animation CSS Variables - Can be overridden */
:root {
--fade-distance: 30px;
--scale-start: 0.8;
--scale-amount: 0.1;
--parallax-slow: -20px;
--parallax-medium: -50px;
--parallax-fast: -100px;
--rotate-amount: 360deg;
--tilt-amount: 5deg;
--bounce-distance: -20px;
--transition-duration: 0.6s;
--transition-easing: ease;
}
/* Base scradar element properties for layout stability */
.scradar {
/* Ensure stable layout during animations */
contain: layout style paint;
/* Prevent layout shifts from transforms */
transform-style: preserve-3d;
backface-visibility: hidden;
}
/* Progress type variables - defined on scradar elements */
.scradar {
/* Priority order: peak > visibility > fill > cover > enter > exit */
--progress: var(--peak, var(--visibility, var(--fill, var(--cover, var(--enter, var(--exit, 0))))));
--progress-peak: var(--peak, 0);
--progress-visibility: var(--visibility, 0);
--progress-fill: var(--fill, 0);
--progress-cover: var(--cover, 0);
--progress-enter: var(--enter, 0);
--progress-exit: var(--exit, 0);
}
/* =============================================================================
FADE ANIMATIONS
============================================================================= */
.scradar__fade-in {
opacity: calc(var(--progress));
}
.scradar__fade-in--up {
opacity: calc(var(--progress));
transform: translateY(calc((1 - var(--progress)) * var(--fade-distance)));
}
.scradar__fade-in--down {
opacity: calc(var(--progress));
transform: translateY(calc((1 - var(--progress)) * var(--fade-distance) * -1));
}
.scradar__fade-in--left {
opacity: calc(var(--progress));
transform: translateX(calc((1 - var(--progress)) * var(--fade-distance) * -1));
}
.scradar__fade-in--right {
opacity: calc(var(--progress));
transform: translateX(calc((1 - var(--progress)) * var(--fade-distance)));
}
/* =============================================================================
SCALE ANIMATIONS
============================================================================= */
.scradar__scale-in {
opacity: calc(var(--progress));
transform: scale(calc(var(--scale-start) + var(--progress) * (1 - var(--scale-start))));
}
.scradar__scale-up {
transform: scale(calc(1 + var(--progress) * var(--scale-amount)));
}
/* =============================================================================
PARALLAX ANIMATIONS
============================================================================= */
.scradar__parallax--slow {
transform: translateY(calc(var(--progress) * var(--parallax-slow)));
}
.scradar__parallax--medium {
transform: translateY(calc(var(--progress) * var(--parallax-medium)));
}
.scradar__parallax--fast {
transform: translateY(calc(var(--progress) * var(--parallax-fast)));
}
/* =============================================================================
FILL-BASED ANIMATIONS
============================================================================= */
.scradar__progress-bar {
position: relative;
overflow: hidden;
}
.scradar__progress-bar::after {
content: '';
position: absolute;
top: 0;
left: 0;
height: 100%;
width: calc(var(--progress) * 100%);
background: currentColor;
}
.scradar__slide-reveal {
clip-path: inset(0 calc(100% - var(--progress) * 100%) 0 0);
}
/* =============================================================================
TEXT ANIMATIONS
============================================================================= */
.scradar__text-reveal {
background: linear-gradient(90deg,
currentColor calc(var(--progress) * 100% - 15px),
transparent calc(var(--progress) * 100% + 15px),
transparent 100%
);
background-clip: text;
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
}
.scradar__typewriter {
overflow: hidden;
white-space: nowrap;
border-right: 2px solid currentColor;
width: calc(var(--progress) * 100%);
}
/* =============================================================================
STAGGER ANIMATIONS
============================================================================= */
.scradar__stagger--fade-in .scradar__stagger-item {
opacity: 0;
transform: translateY(20px);
transition: opacity var(--transition-duration) var(--transition-easing),
transform var(--transition-duration) var(--transition-easing);
}
.scradar__stagger--fade-in[data-scradar-in="1"] .scradar__stagger-item:nth-child(1) {
opacity: 1;
transform: translateY(0);
transition-delay: 0.1s;
}
.scradar__stagger--fade-in[data-scradar-in="1"] .scradar__stagger-item:nth-child(2) {
opacity: 1;
transform: translateY(0);
transition-delay: 0.2s;
}
.scradar__stagger--fade-in[data-scradar-in="1"] .scradar__stagger-item:nth-child(3) {
opacity: 1;
transform: translateY(0);
transition-delay: 0.3s;
}
.scradar__stagger--fade-in[data-scradar-in="1"] .scradar__stagger-item:nth-child(4) {
opacity: 1;
transform: translateY(0);
transition-delay: 0.4s;
}
.scradar__stagger--fade-in[data-scradar-in="1"] .scradar__stagger-item:nth-child(5) {
opacity: 1;
transform: translateY(0);
transition-delay: 0.5s;
}
.scradar__stagger--fade-in[data-scradar-in="1"] .scradar__stagger-item:nth-child(6) {
opacity: 1;
transform: translateY(0);
transition-delay: 0.6s;
}
.scradar__stagger--fade-in[data-scradar-in="1"] .scradar__stagger-item:nth-child(7) {
opacity: 1;
transform: translateY(0);
transition-delay: 0.7s;
}
.scradar__stagger--fade-in[data-scradar-in="1"] .scradar__stagger-item:nth-child(8) {
opacity: 1;
transform: translateY(0);
transition-delay: 0.8s;
}
/* =============================================================================
ROTATION ANIMATIONS
============================================================================= */
.scradar__rotate {
transform: rotate(calc(var(--progress) * var(--rotate-amount)));
/* Ensure rotation doesn't affect layout calculations */
transform-origin: center center;
/* Preserve original dimensions during rotation */
width: fit-content;
height: fit-content;
/* Prevent layout shifts */
contain: layout style paint;
}
.scradar__tilt {
transform: rotate(calc(var(--progress) * var(--tilt-amount) - var(--tilt-amount) / 2));
/* Ensure rotation doesn't affect layout calculations */
transform-origin: center center;
/* Preserve original dimensions during rotation */
width: fit-content;
height: fit-content;
/* Prevent layout shifts */
contain: layout style paint;
}
/* =============================================================================
FILTER ANIMATIONS
============================================================================= */
.scradar__color-shift {
filter: hue-rotate(calc(var(--progress) * 180deg));
}
.scradar__blur-in {
filter: blur(calc((1 - var(--progress)) * 10px));
}
/* =============================================================================
PEAK-BASED ANIMATIONS (rise and fall)
============================================================================= */
.scradar__bounce {
transform: translateY(calc(var(--progress) * var(--bounce-distance)));
}
.scradar__pulse {
transform: scale(calc(1 + var(--progress) * var(--scale-amount)));
opacity: calc(0.7 + var(--progress) * 0.3);
}
.scradar__glow {
box-shadow: 0 0 calc(var(--progress) * 20px) calc(var(--progress) * 10px) rgba(79, 195, 247, calc(var(--progress) * 0.3));
}
/* =============================================================================
COMBINATION ANIMATIONS
============================================================================= */
.scradar__zoom-fade {
opacity: calc(var(--progress));
transform: scale(calc(var(--scale-start) + var(--progress) * (1 - var(--scale-start))))
translateY(calc((1 - var(--progress)) * var(--fade-distance)));
}
.scradar__slide-scale {
transform: translateX(calc((1 - var(--progress)) * var(--fade-distance)))
scale(calc(var(--scale-start) + var(--progress) * (1 - var(--scale-start))));
}
/* =============================================================================
TRIGGER-BASED ANIMATIONS (data-scradar-in attribute)
============================================================================= */
/* Trigger animations - when data-scradar-in="1" is set */
.scradar__trigger-fade-in[data-scradar-in="1"], .scradar[data-scradar-in="1"] .scradar__trigger-fade-in {
opacity: 1;
animation: scradar-fade-in var(--transition-duration) var(--transition-easing) forwards;
}
.scradar__trigger-fade-in--up[data-scradar-in="1"], .scradar[data-scradar-in="1"] .scradar__trigger-fade-in--up {
opacity: 1;
transform: translateY(0);
animation: scradar-fade-in-up var(--transition-duration) var(--transition-easing) forwards;
}
.scradar__trigger-scale-in[data-scradar-in="1"], .scradar[data-scradar-in="1"] .scradar__trigger-scale-in {
opacity: 1;
transform: scale(1);
animation: scradar-scale-in var(--transition-duration) var(--transition-easing) forwards;
}
.scradar__trigger-bounce[data-scradar-in="1"], .scradar[data-scradar-in="1"] .scradar__trigger-bounce {
animation: scradar-bounce var(--transition-duration) var(--transition-easing) forwards;
}
.scradar__trigger-pulse[data-scradar-in="1"], .scradar[data-scradar-in="1"] .scradar__trigger-pulse {
animation: scradar-pulse var(--transition-duration) var(--transition-easing) forwards;
}
.scradar__trigger-glow[data-scradar-in="1"], .scradar[data-scradar-in="1"] .scradar__trigger-glow {
animation: scradar-glow var(--transition-duration) var(--transition-easing) forwards;
}
/* Keyframe animations for trigger-based effects */
@keyframes scradar-fade-in {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
@keyframes scradar-fade-in-up {
from {
opacity: 0;
transform: translateY(var(--fade-distance));
}
to {
opacity: 1;
transform: translateY(0);
}
}
@keyframes scradar-scale-in {
from {
opacity: 0;
transform: scale(var(--scale-start));
}
to {
opacity: 1;
transform: scale(1);
}
}
@keyframes scradar-bounce {
0%, 20%, 53%, 80%, 100% {
transform: translateY(0);
}
40%, 43% {
transform: translateY(var(--bounce-distance));
}
70% {
transform: translateY(calc(var(--bounce-distance) * 0.6));
}
90% {
transform: translateY(calc(var(--bounce-distance) * 0.3));
}
}
@keyframes scradar-pulse {
0% {
transform: scale(1);
opacity: 0.7;
}
50% {
transform: scale(calc(1 + var(--scale-amount)));
opacity: 1;
}
100% {
transform: scale(1);
opacity: 0.7;
}
}
@keyframes scradar-glow {
0% {
box-shadow: 0 0 0 0 rgba(79, 195, 247, 0.3);
}
50% {
box-shadow: 0 0 20px 10px rgba(79, 195, 247, 0.3);
}
100% {
box-shadow: 0 0 0 0 rgba(79, 195, 247, 0.3);
}
}
/* =============================================================================
PROGRESS TYPE MODIFIERS
============================================================================= */
/* Use different progress types */
.scradar__use-visibility {
--progress: var(--progress-visibility);
}
.scradar__use-fill {
--progress: var(--progress-fill);
}
.scradar__use-peak {
--progress: var(--progress-peak);
}
.scradar__use-cover {
--progress: var(--progress-cover);
}
.scradar__use-enter {
--progress: var(--progress-enter);
}
.scradar__use-exit {
--progress: var(--progress-exit);
}
/* =============================================================================
UTILITY CLASSES
============================================================================= */
/* Custom timing functions */
.scradar__ease-out-back {
--transition-easing: cubic-bezier(0.34, 1.56, 0.64, 1);
}
.scradar__ease-out-elastic {
--transition-easing: cubic-bezier(0.68, -0.55, 0.265, 1.55);
}
.scradar__ease-out-bounce {
--transition-easing: cubic-bezier(0.68, -0.6, 0.32, 1.6);
}
/* Duration modifiers */
.scradar__fast {
--transition-duration: 0.3s;
}
.scradar__slow {
--transition-duration: 1.2s;
}
.scradar__very-slow {
--transition-duration: 2s;
}
/* Distance modifiers */
.scradar__small-distance {
--fade-distance: 15px;
}
.scradar__large-distance {
--fade-distance: 60px;
}
.scradar__huge-distance {
--fade-distance: 100px;
}