0
# Animations
1
2
Create CSS animations with keyframes and integrate with theme tokens. The `keyframes` function generates CSS animations that can be used across styled components and CSS classes.
3
4
## Capabilities
5
6
### Keyframes Function
7
8
Creates CSS keyframe animations that can be referenced in styles.
9
10
```typescript { .api }
11
/**
12
* Creates CSS keyframe animations
13
* @param styles - Object defining animation keyframes with percentages or keywords
14
* @returns Keyframes object with name property and toString method
15
*/
16
function keyframes(styles: KeyframesObject): KeyframesResult;
17
18
interface KeyframesObject {
19
/** Keyframe percentages (0%, 50%, 100%) or keywords (from, to) */
20
[percentage: string]: StyleObject;
21
}
22
23
interface KeyframesResult {
24
/** Returns the animation name for use in CSS */
25
(): string;
26
/** CSS animation name */
27
name: string;
28
/** String representation of the animation name */
29
toString(): string;
30
}
31
```
32
33
**Usage Examples:**
34
35
```typescript
36
import { keyframes, styled } from "@stitches/react";
37
38
// Basic fade in animation
39
const fadeIn = keyframes({
40
'0%': { opacity: 0 },
41
'100%': { opacity: 1 }
42
});
43
44
// Complex animation with multiple keyframes
45
const slideAndScale = keyframes({
46
'0%': {
47
transform: 'translateX(-100px) scale(0.8)',
48
opacity: 0
49
},
50
'50%': {
51
transform: 'translateX(0) scale(1.1)',
52
opacity: 0.8
53
},
54
'100%': {
55
transform: 'translateX(0) scale(1)',
56
opacity: 1
57
}
58
});
59
60
// Use in styled components
61
const AnimatedBox = styled('div', {
62
animation: `${fadeIn} 0.3s ease-out`
63
});
64
65
const ComplexAnimatedBox = styled('div', {
66
animation: `${slideAndScale} 0.6s ease-out`
67
});
68
```
69
70
### Animation with Variants
71
72
Combine animations with variant systems for conditional animations.
73
74
**Usage Examples:**
75
76
```typescript
77
const bounce = keyframes({
78
'0%, 20%, 53%, 80%, 100%': {
79
transform: 'translate3d(0, 0, 0)'
80
},
81
'40%, 43%': {
82
transform: 'translate3d(0, -30px, 0)'
83
},
84
'70%': {
85
transform: 'translate3d(0, -15px, 0)'
86
},
87
'90%': {
88
transform: 'translate3d(0, -4px, 0)'
89
}
90
});
91
92
const pulse = keyframes({
93
'0%': {
94
transform: 'scale(1)'
95
},
96
'50%': {
97
transform: 'scale(1.05)'
98
},
99
'100%': {
100
transform: 'scale(1)'
101
}
102
});
103
104
const AnimatedButton = styled('button', {
105
padding: '12px 24px',
106
border: 'none',
107
borderRadius: '4px',
108
cursor: 'pointer',
109
110
variants: {
111
animation: {
112
none: {},
113
bounce: {
114
animation: `${bounce} 1s ease-in-out`
115
},
116
pulse: {
117
animation: `${pulse} 2s infinite`
118
},
119
fadeIn: {
120
animation: `${fadeIn} 0.3s ease-out`
121
}
122
},
123
loading: {
124
true: {
125
animation: `${pulse} 1s infinite`
126
}
127
}
128
}
129
});
130
131
// Usage with animation variants
132
<AnimatedButton animation="bounce">Bouncy Button</AnimatedButton>
133
<AnimatedButton loading={true}>Loading Button</AnimatedButton>
134
```
135
136
### Theme Token Integration
137
138
Use theme tokens within keyframe animations.
139
140
**Usage Examples:**
141
142
```typescript
143
import { createTheme, keyframes, styled } from "@stitches/react";
144
145
const theme = createTheme({
146
colors: {
147
primary: 'blue',
148
secondary: 'lightblue'
149
},
150
space: {
151
sm: '8px',
152
md: '16px',
153
lg: '24px'
154
}
155
});
156
157
// Animations using theme tokens
158
const colorShift = keyframes({
159
'0%': {
160
backgroundColor: '$colors$primary',
161
transform: 'translateY(0)'
162
},
163
'50%': {
164
backgroundColor: '$colors$secondary',
165
transform: 'translateY(-$space$md)'
166
},
167
'100%': {
168
backgroundColor: '$colors$primary',
169
transform: 'translateY(0)'
170
}
171
});
172
173
const ThemeAnimatedBox = styled('div', {
174
width: '100px',
175
height: '100px',
176
animation: `${colorShift} 2s infinite ease-in-out`
177
});
178
```
179
180
### Complex Animations
181
182
Create sophisticated multi-step animations with precise timing.
183
184
**Usage Examples:**
185
186
```typescript
187
// Loading spinner animation
188
const spin = keyframes({
189
'0%': { transform: 'rotate(0deg)' },
190
'100%': { transform: 'rotate(360deg)' }
191
});
192
193
// Morphing animation
194
const morphing = keyframes({
195
'0%': {
196
borderRadius: '50%',
197
transform: 'scale(1) rotate(0deg)'
198
},
199
'25%': {
200
borderRadius: '25%',
201
transform: 'scale(1.2) rotate(90deg)'
202
},
203
'50%': {
204
borderRadius: '0%',
205
transform: 'scale(1.4) rotate(180deg)'
206
},
207
'75%': {
208
borderRadius: '25%',
209
transform: 'scale(1.2) rotate(270deg)'
210
},
211
'100%': {
212
borderRadius: '50%',
213
transform: 'scale(1) rotate(360deg)'
214
}
215
});
216
217
// Typing animation
218
const typing = keyframes({
219
'from': { width: '0' },
220
'to': { width: '100%' }
221
});
222
223
const blink = keyframes({
224
'0%, 50%': { borderColor: 'transparent' },
225
'51%, 100%': { borderColor: 'currentColor' }
226
});
227
228
const Spinner = styled('div', {
229
width: '20px',
230
height: '20px',
231
border: '2px solid transparent',
232
borderTop: '2px solid currentColor',
233
borderRadius: '50%',
234
animation: `${spin} 1s linear infinite`
235
});
236
237
const MorphingBox = styled('div', {
238
width: '50px',
239
height: '50px',
240
backgroundColor: 'blue',
241
animation: `${morphing} 3s infinite ease-in-out`
242
});
243
244
const TypingText = styled('div', {
245
overflow: 'hidden',
246
borderRight: '2px solid currentColor',
247
whiteSpace: 'nowrap',
248
animation: `
249
${typing} 3s steps(40, end),
250
${blink} 0.75s step-end infinite
251
`
252
});
253
```
254
255
### Animation States and Controls
256
257
Control animations based on component state and user interactions.
258
259
**Usage Examples:**
260
261
```typescript
262
const slideInLeft = keyframes({
263
from: { transform: 'translateX(-100%)' },
264
to: { transform: 'translateX(0)' }
265
});
266
267
const slideInRight = keyframes({
268
from: { transform: 'translateX(100%)' },
269
to: { transform: 'translateX(0)' }
270
});
271
272
const scaleUp = keyframes({
273
from: { transform: 'scale(0.5)', opacity: 0 },
274
to: { transform: 'scale(1)', opacity: 1 }
275
});
276
277
const InteractiveCard = styled('div', {
278
padding: '20px',
279
backgroundColor: 'white',
280
borderRadius: '8px',
281
boxShadow: '0 2px 4px rgba(0,0,0,0.1)',
282
transition: 'transform 0.2s ease',
283
284
variants: {
285
entrance: {
286
slideLeft: {
287
animation: `${slideInLeft} 0.5s ease-out`
288
},
289
slideRight: {
290
animation: `${slideInRight} 0.5s ease-out`
291
},
292
scale: {
293
animation: `${scaleUp} 0.3s ease-out`
294
}
295
},
296
hover: {
297
true: {
298
'&:hover': {
299
transform: 'translateY(-4px)'
300
}
301
}
302
}
303
}
304
});
305
306
// Usage with different entrance animations
307
<InteractiveCard entrance="slideLeft" hover>
308
Slides in from left
309
</InteractiveCard>
310
```
311
312
### Animation Composition
313
314
Combine multiple animations for complex effects.
315
316
**Usage Examples:**
317
318
```typescript
319
const float = keyframes({
320
'0%, 100%': { transform: 'translateY(0px)' },
321
'50%': { transform: 'translateY(-10px)' }
322
});
323
324
const glow = keyframes({
325
'0%, 100%': { boxShadow: '0 0 5px rgba(59, 130, 246, 0.5)' },
326
'50%': { boxShadow: '0 0 20px rgba(59, 130, 246, 0.8)' }
327
});
328
329
const rotate = keyframes({
330
from: { transform: 'rotate(0deg)' },
331
to: { transform: 'rotate(360deg)' }
332
});
333
334
// Combine multiple animations
335
const FloatingGlowBox = styled('div', {
336
width: '100px',
337
height: '100px',
338
backgroundColor: 'blue',
339
borderRadius: '8px',
340
animation: `
341
${float} 3s ease-in-out infinite,
342
${glow} 2s ease-in-out infinite,
343
${rotate} 10s linear infinite
344
`
345
});
346
347
// Sequential animations with delays
348
const SequentialBox = styled('div', {
349
animation: `
350
${fadeIn} 0.5s ease-out,
351
${scaleUp} 0.3s ease-out 0.5s both,
352
${float} 2s ease-in-out 0.8s infinite
353
`
354
});
355
```
356
357
### Performance Optimized Animations
358
359
Create animations optimized for performance using transform and opacity.
360
361
**Usage Examples:**
362
363
```typescript
364
// GPU-accelerated animations
365
const smoothSlide = keyframes({
366
from: {
367
transform: 'translate3d(-100%, 0, 0)',
368
opacity: 0
369
},
370
to: {
371
transform: 'translate3d(0, 0, 0)',
372
opacity: 1
373
}
374
});
375
376
const smoothScale = keyframes({
377
from: {
378
transform: 'scale3d(0.8, 0.8, 1)',
379
opacity: 0
380
},
381
to: {
382
transform: 'scale3d(1, 1, 1)',
383
opacity: 1
384
}
385
});
386
387
const PerformantBox = styled('div', {
388
// Force hardware acceleration
389
willChange: 'transform, opacity',
390
backfaceVisibility: 'hidden',
391
perspective: '1000px',
392
393
variants: {
394
animation: {
395
slide: {
396
animation: `${smoothSlide} 0.3s ease-out`
397
},
398
scale: {
399
animation: `${smoothScale} 0.2s ease-out`
400
}
401
}
402
}
403
});
404
```
405
406
### Animation Utilities
407
408
Helper patterns for common animation scenarios.
409
410
**Usage Examples:**
411
412
```typescript
413
// Staggered animations
414
const staggeredFade = keyframes({
415
from: { opacity: 0, transform: 'translateY(20px)' },
416
to: { opacity: 1, transform: 'translateY(0)' }
417
});
418
419
const StaggeredList = styled('ul', {
420
'& li': {
421
animation: `${staggeredFade} 0.5s ease-out both`,
422
423
'&:nth-child(1)': { animationDelay: '0s' },
424
'&:nth-child(2)': { animationDelay: '0.1s' },
425
'&:nth-child(3)': { animationDelay: '0.2s' },
426
'&:nth-child(4)': { animationDelay: '0.3s' },
427
'&:nth-child(5)': { animationDelay: '0.4s' }
428
}
429
});
430
431
// Infinite loading states
432
const shimmer = keyframes({
433
'0%': { transform: 'translateX(-100%)' },
434
'100%': { transform: 'translateX(100%)' }
435
});
436
437
const LoadingSkeleton = styled('div', {
438
position: 'relative',
439
backgroundColor: '#f0f0f0',
440
overflow: 'hidden',
441
442
'&::after': {
443
content: '""',
444
position: 'absolute',
445
top: 0,
446
right: 0,
447
bottom: 0,
448
left: 0,
449
background: 'linear-gradient(90deg, transparent, rgba(255,255,255,0.8), transparent)',
450
animation: `${shimmer} 1.5s infinite`
451
}
452
});
453
```