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
Yes, keyframes support all CSS properties that can be animated, including transforms, colors, dimensions, and more.
Use media queries with prefers-reduced-motion: reduce
to provide alternative, less animated experiences.
Absolutely! Once defined, a keyframe animation can be referenced in any selector within the same styleframe instance.
Transitions animate between two states (like hover effects), while keyframes allow you to define complex, multi-step animations with precise control over timing and intermediate states.
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.