Keyframes
Overview
Keyframes in Styleframe provide a powerful way to create CSS animations with full type safety and auto-complete. You can define animation sequences that smoothly transition between different states, creating engaging motion effects while maintaining the benefits of Styleframe's TypeScript support.
Why use keyframes?
Keyframes help you:
- Create smooth animations: Build fluid motion effects that enhance user experience and guide attention.
- Maintain design consistency: Use variables and tokens to ensure consistent timing and easing across animations.
- Write maintainable code: Leverage type safety and organized structure to prevent common animation mistakes.
- Generate optimized output: Let Styleframe handle the generation of clean, efficient CSS animations.
Defining Keyframes
You define keyframes using the keyframes() function from your styleframe instance:
import { styleframe, css } from 'styleframe';
const s = styleframe();
const { keyframes, selector, ref } = s;
const fadeInKeyframes = keyframes('fade-in', {
'0%': {
opacity: 0,
transform: 'translateY(20px)',
},
'100%': {
opacity: 1,
transform: 'translateY(0)',
},
});
selector('.animated-element', {
animation: css`${ref(fadeInKeyframes)} 0.3s ease-out`,
});
export default s;
@keyframes fade-in {
0% {
opacity: 0;
transform: translateY(20px);
}
100% {
opacity: 1;
transform: translateY(0);
}
}
.animated-element {
animation: fade-in 0.3s ease-out;
}
Each keyframe takes an animation name and a keyframes object with percentage-based stops defining the animation sequence.
Using Variables with Keyframes
For better maintainability and consistency, combine keyframes with variables:
import { styleframe, css } from 'styleframe';
const s = styleframe();
const { variable, ref, keyframes, selector } = s;
const animationDurationFast = variable('animation-duration--fast', '0.2s');
const animationDurationNormal = variable('animation-duration--normal', '0.3s');
const transformTranslateUp = variable('transform--translate-up', 'translateY(-10px)');
const colorPrimary = variable('color--primary', '#006cff');
const colorPrimaryHover = variable('color--primary-hover', '#0056cc');
const buttonHoverKeyframes = keyframes('button-hover', {
'0%': {
transform: 'translateY(0)',
backgroundColor: ref(colorPrimary),
},
'100%': {
transform: ref(transformTranslateUp),
backgroundColor: ref(colorPrimaryHover),
},
});
selector('.interactive-button', {
backgroundColor: ref(colorPrimary),
transition: css`all ${ref(animationDurationNormal)} ease`,
'&:hover': {
animation: css`${ref(buttonHoverKeyframes)} ${ref(animationDurationFast)} ease-out`,
},
});
export default s;
:root {
--animation-duration--fast: 0.2s;
--animation-duration--normal: 0.3s;
--transform--translate-up: translateY(-10px);
--color--primary: #006cff;
--color--primary-hover: #0056cc;
}
@keyframes button-hover {
0% {
transform: translateY(0);
background-color: var(--color--primary);
}
100% {
transform: var(--transform--translate-up);
background-color: var(--color--primary-hover);
}
}
.interactive-button {
background-color: var(--color--primary);
transition: all var(--animation-duration--normal) ease;
&:hover {
animation: button-hover var(--animation-duration--fast) ease-out;
}
}
Responsive Keyframes
You can create different keyframe animations for different screen sizes using media queries:
import { styleframe, css } from 'styleframe';
const s = styleframe();
const { keyframes, selector, media, ref } = s;
const slideInMobileKeyframes = keyframes('slide-in-mobile', {
'0%': {
transform: 'translateX(-100%)',
opacity: 0,
},
'100%': {
transform: 'translateX(0)',
opacity: 1,
},
});
const slideInDesktopKeyframes = keyframes('slide-in-desktop', {
'0%': {
transform: 'translateY(-50px) scale(0.9)',
opacity: 0,
},
'100%': {
transform: 'translateY(0) scale(1)',
opacity: 1,
},
});
selector('.modal', {
animation: css`${ref(slideInMobileKeyframes)} 0.3s ease-out`,
});
media('(min-width: 768px)', ({ selector }) => {
selector('.modal', {
animation: css`${ref(slideInDesktopKeyframes)} 0.4s ease-out`,
});
});
export default s;
@keyframes slide-in-mobile {
0% {
transform: translateX(-100%);
opacity: 0;
}
100% {
transform: translateX(0);
opacity: 1;
}
}
@keyframes slide-in-desktop {
0% {
transform: translateY(-50px) scale(0.9);
opacity: 0;
}
100% {
transform: translateY(0) scale(1);
opacity: 1;
}
}
.modal {
animation: slide-in-mobile 0.3s ease-out;
}
@media (min-width: 768px) {
.modal {
animation: slide-in-desktop 0.4s ease-out;
}
}
Examples
Animation Utilities
Create reusable animation patterns by combining keyframes with utility selectors:
import { styleframe, css } from 'styleframe';
const s = styleframe();
const { keyframes, selector, ref } = s;
const pulseKeyframes = keyframes('pulse', {
'0%': {
transform: 'scale(1)',
opacity: 1,
},
'50%': {
transform: 'scale(1.05)',
opacity: 0.8,
},
'100%': {
transform: 'scale(1)',
opacity: 1,
},
});
const spinKeyframes = keyframes('spin', {
'0%': {
transform: 'rotate(0deg)',
},
'100%': {
transform: 'rotate(360deg)',
},
});
const shakeKeyframes = keyframes('shake', {
'0%, 100%': {
transform: 'translateX(0)',
},
'10%, 30%, 50%, 70%, 90%': {
transform: 'translateX(-10px)',
},
'20%, 40%, 60%, 80%': {
transform: 'translateX(10px)',
},
});
selector('.animate-pulse', {
animation: css`${ref(pulseKeyframes)} 2s cubic-bezier(0.4, 0, 0.6, 1) infinite`,
});
selector('.animate-spin', {
animation: css`${ref(spinKeyframes)} 1s linear infinite`,
});
selector('.animate-shake', {
animation: css`${ref(shakeKeyframes)} 0.82s cubic-bezier(0.36, 0.07, 0.19, 0.97)`,
});
export default s;
@keyframes pulse {
0% {
transform: scale(1);
opacity: 1;
}
50% {
transform: scale(1.05);
opacity: 0.8;
}
100% {
transform: scale(1);
opacity: 1;
}
}
@keyframes spin {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
@keyframes shake {
0%, 100% {
transform: translateX(0);
}
10%, 30%, 50%, 70%, 90% {
transform: translateX(-10px);
}
20%, 40%, 60%, 80% {
transform: translateX(10px);
}
}
.animate-pulse {
animation: pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;
}
.animate-spin {
animation: spin 1s linear infinite;
}
.animate-shake {
animation: shake 0.82s cubic-bezier(0.36, 0.07, 0.19, 0.97);
}
Animation States and Controls
You can create animations that respond to different states or user interactions:
import { styleframe, css } from 'styleframe';
const s = styleframe();
const { keyframes, selector, ref } = s;
const expandWidthKeyframes = keyframes('expand-width', {
'0%': {
width: '0%',
},
'100%': {
width: '100%',
},
});
selector('.progress-bar', {
width: '0%',
height: '4px',
backgroundColor: '#006cff',
transition: 'width 0.3s ease',
'&.active': {
animation: css`${ref(expandWidthKeyframes)} 2s ease-out forwards`,
},
});
export default s;
@keyframes expand-width {
0% {
width: 0%;
}
100% {
width: 100%;
}
}
.progress-bar {
width: 0%;
height: 4px;
background-color: #006cff;
transition: width 0.3s ease;
&.active {
animation: expand-width 2s ease-out forwards;
}
}
import { styleframe, css } from 'styleframe';
const s = styleframe();
const { keyframes, selector, ref } = s;
const fadeInUpKeyframes = keyframes('fade-in-up', {
'0%': {
opacity: 0,
transform: 'translateY(30px)',
},
'100%': {
opacity: 1,
transform: 'translateY(0)',
},
});
selector('.notification', {
opacity: 0,
transform: 'translateY(30px)',
'&.show': {
animation: css`${ref(fadeInUpKeyframes)} 0.4s ease-out forwards`,
},
'&.hide': {
animation: css`${ref(fadeInUpKeyframes)} 0.4s ease-out reverse`,
},
});
export default s;
@keyframes fade-in-up {
0% {
opacity: 0;
transform: translateY(30px);
}
100% {
opacity: 1;
transform: translateY(0);
}
}
.notification {
opacity: 0;
transform: translateY(30px);
&.show {
animation: fade-in-up 0.4s ease-out forwards;
}
&.hide {
animation: fade-in-up 0.4s ease-out reverse;
}
}
Complex Keyframe Sequences
You can create sophisticated animations with multiple keyframe stops:
import { styleframe, css } from 'styleframe';
const s = styleframe();
const { keyframes, selector, ref } = s;
const bounceKeyframes = keyframes('bounce', {
'0%': {
transform: 'translateY(0)',
animationTimingFunction: 'ease-out',
},
'25%': {
transform: 'translateY(-20px)',
animationTimingFunction: 'ease-in',
},
'50%': {
transform: 'translateY(0)',
animationTimingFunction: 'ease-out',
},
'75%': {
transform: 'translateY(-10px)',
animationTimingFunction: 'ease-in',
},
'100%': {
transform: 'translateY(0)',
animationTimingFunction: 'ease-out',
},
});
selector('.bouncing-button', {
animation: css`${ref(bounceKeyframes)} 0.6s infinite`,
});
export default s;
@keyframes bounce {
0% {
transform: translateY(0);
animation-timing-function: ease-out;
}
25% {
transform: translateY(-20px);
animation-timing-function: ease-in;
}
50% {
transform: translateY(0);
animation-timing-function: ease-out;
}
75% {
transform: translateY(-10px);
animation-timing-function: ease-in;
}
100% {
transform: translateY(0);
animation-timing-function: ease-out;
}
}
.bouncing-button {
animation: bounce 0.6s infinite;
}
Best Practices
- Use meaningful animation names that describe the motion effect or purpose.
- Define animation variables for durations, easing functions, and common transform values.
- Consider performance by animating only transform and opacity properties when possible.
- Respect user preferences by providing reduced motion alternatives for users who prefer less animation.
- Keep animations purposeful and avoid overusing motion effects that might distract from content.
FAQ
prefers-reduced-motion: reduce to provide alternative, less animated experiences.Media Queries
Styleframe media queries enable responsive design with type-safe breakpoint definitions. Create adaptive layouts that work seamlessly across all device sizes.
Interpolation
Styleframe interpolation provides a powerful CSS template literal utility that enables dynamic value insertion with full type safety. Create flexible, maintainable CSS values that adapt to your design system.