@its-thepoe/design-motion-principles 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/SKILL.md +334 -0
- package/audit-checklist.md +137 -0
- package/package.json +18 -0
- package/references/accessibility.md +52 -0
- package/references/common-mistakes.md +158 -0
- package/references/emil-kowalski.md +355 -0
- package/references/jakub-krehel.md +317 -0
- package/references/jhey-tompkins.md +367 -0
- package/references/performance.md +82 -0
- package/references/philosophy.md +108 -0
- package/references/technical-principles.md +527 -0
|
@@ -0,0 +1,317 @@
|
|
|
1
|
+
# Jakub Krehel's Animation Principles
|
|
2
|
+
|
|
3
|
+
Jakub Krehel is a design engineer known for his work at jakub.kr. His approach emphasizes **subtle production polish**—animations that enhance the experience invisibly, designed for real client work where users will interact repeatedly.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Core Philosophy: Invisible Enhancement
|
|
8
|
+
|
|
9
|
+
> "The best animation is that which goes unnoticed."
|
|
10
|
+
|
|
11
|
+
Jakub's work embodies **refinement for production use**. His animations are:
|
|
12
|
+
|
|
13
|
+
- **Barely noticeable** — If users consciously notice the animation, it's probably too much
|
|
14
|
+
- **Production-ready** — Designed for real client work, not demos
|
|
15
|
+
- **Contextually appropriate** — Adapts to light mode, varied backgrounds, real content
|
|
16
|
+
- **Subtle over flashy** — The goal is to make interfaces feel smooth and responsive, not impressive
|
|
17
|
+
|
|
18
|
+
**The best compliment**: "This feels really nice" — not "cool animation!"
|
|
19
|
+
|
|
20
|
+
**The test**: If you remove the animation, do users feel something is missing? Good. If users comment "nice animation!" every time they see it? Too prominent.
|
|
21
|
+
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
## When to Apply Jakub's Mindset
|
|
25
|
+
|
|
26
|
+
- Production applications and client work
|
|
27
|
+
- Professional/enterprise interfaces
|
|
28
|
+
- When users will interact repeatedly (animations must not get tiresome)
|
|
29
|
+
- When accessibility and performance are critical
|
|
30
|
+
- When you need polish without distraction
|
|
31
|
+
|
|
32
|
+
---
|
|
33
|
+
|
|
34
|
+
## Enter Animation Recipe
|
|
35
|
+
|
|
36
|
+
A standard enter animation combines three properties:
|
|
37
|
+
|
|
38
|
+
```jsx
|
|
39
|
+
initial={{ opacity: 0, translateY: 8, filter: "blur(4px)" }}
|
|
40
|
+
animate={{ opacity: 1, translateY: 0, filter: "blur(0px)" }}
|
|
41
|
+
transition={{ type: "spring", duration: 0.45, bounce: 0 }}
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
| Property | From | To | Purpose |
|
|
45
|
+
|----------|------|-----|---------|
|
|
46
|
+
| Opacity | 0 | 1 | Fade in |
|
|
47
|
+
| TranslateY | 8px | 0 | Subtle upward movement |
|
|
48
|
+
| Blur | 4px | 0px | "Materializing" effect |
|
|
49
|
+
|
|
50
|
+
**Why blur?** It creates a "materializing" effect that feels more physical than opacity alone. The element appears to come into focus, not just fade in.
|
|
51
|
+
|
|
52
|
+
For full container slides, use `translateY: "calc(-100% - 4px)"` instead of fixed pixels.
|
|
53
|
+
|
|
54
|
+
---
|
|
55
|
+
|
|
56
|
+
## Exit Animation Subtlety
|
|
57
|
+
|
|
58
|
+
**Key Insight**: Exit animations should be subtler than enter animations.
|
|
59
|
+
|
|
60
|
+
When a component exits, it doesn't need the same amount of movement or attention as when entering. The user's focus is moving to what comes next, not what's leaving.
|
|
61
|
+
|
|
62
|
+
```jsx
|
|
63
|
+
// Instead of full exit movement:
|
|
64
|
+
exit={{ translateY: "calc(-100% - 4px)" }}
|
|
65
|
+
|
|
66
|
+
// Use a subtle fixed value:
|
|
67
|
+
exit={{ translateY: "-12px", opacity: 0, filter: "blur(4px)" }}
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
**Why this works**: Exits become softer, less jarring, and don't compete for attention.
|
|
71
|
+
|
|
72
|
+
**When NOT to use subtle exits**:
|
|
73
|
+
- When the exit itself is meaningful (user-initiated dismissal)
|
|
74
|
+
- When you need to emphasize something leaving (error clearing, item deletion)
|
|
75
|
+
- Full-page transitions where directional continuity matters
|
|
76
|
+
|
|
77
|
+
---
|
|
78
|
+
|
|
79
|
+
## Spring Animations
|
|
80
|
+
|
|
81
|
+
Prefer spring animations over linear/ease for more natural-feeling motion:
|
|
82
|
+
|
|
83
|
+
```jsx
|
|
84
|
+
transition={{ type: "spring", duration: 0.45, bounce: 0 }}
|
|
85
|
+
transition={{ type: "spring", duration: 0.55, bounce: 0.1 }}
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
**Why `bounce: 0`?** It gives smooth deceleration without overshoot—professional and refined.
|
|
89
|
+
|
|
90
|
+
**Reserve bounce > 0** for playful contexts only.
|
|
91
|
+
|
|
92
|
+
---
|
|
93
|
+
|
|
94
|
+
## Shadows Instead of Borders
|
|
95
|
+
|
|
96
|
+
In light mode, prefer subtle multi-layer box-shadows over solid borders:
|
|
97
|
+
|
|
98
|
+
```css
|
|
99
|
+
.card {
|
|
100
|
+
box-shadow:
|
|
101
|
+
0px 0px 0px 1px rgba(0, 0, 0, 0.06),
|
|
102
|
+
0px 1px 2px -1px rgba(0, 0, 0, 0.06),
|
|
103
|
+
0px 2px 4px 0px rgba(0, 0, 0, 0.04);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/* Slightly darker on hover */
|
|
107
|
+
.card:hover {
|
|
108
|
+
box-shadow:
|
|
109
|
+
0px 0px 0px 1px rgba(0, 0, 0, 0.08),
|
|
110
|
+
0px 1px 2px -1px rgba(0, 0, 0, 0.08),
|
|
111
|
+
0px 2px 4px 0px rgba(0, 0, 0, 0.06);
|
|
112
|
+
}
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
**Why shadows over borders?**
|
|
116
|
+
- Shadows adapt to any background (images, gradients, varied colors) via transparency
|
|
117
|
+
- Borders are solid colors that may clash with dynamic backgrounds
|
|
118
|
+
- Multi-layer shadows create depth; single borders feel flat
|
|
119
|
+
- Shadows can be transitioned smoothly
|
|
120
|
+
|
|
121
|
+
**When borders are fine**:
|
|
122
|
+
- Dark mode (shadows less visible anyway)
|
|
123
|
+
- When you need hard edges intentionally
|
|
124
|
+
- Simple interfaces where depth isn't needed
|
|
125
|
+
|
|
126
|
+
---
|
|
127
|
+
|
|
128
|
+
## Gradients & Color Spaces
|
|
129
|
+
|
|
130
|
+
Use `oklch` for gradients to avoid muddy midpoints:
|
|
131
|
+
|
|
132
|
+
```css
|
|
133
|
+
.element {
|
|
134
|
+
background: linear-gradient(in oklch, blue, red);
|
|
135
|
+
}
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
**Why oklch?** It interpolates through perceptually uniform color space, avoiding the gray/muddy zone that sRGB hits when blending complementary colors.
|
|
139
|
+
|
|
140
|
+
**Color hints** control where the blend midpoint appears (different from color stops).
|
|
141
|
+
|
|
142
|
+
Layer gradients with `background-blend-mode` for unique effects.
|
|
143
|
+
|
|
144
|
+
---
|
|
145
|
+
|
|
146
|
+
## Blur as a Signal
|
|
147
|
+
|
|
148
|
+
Blur (via `filter: blur()`) combined with opacity and translate creates a "materializing" effect. Use blur to signal:
|
|
149
|
+
|
|
150
|
+
- **Entering focus**: blur → sharp
|
|
151
|
+
- **Losing relevance**: sharp → blur
|
|
152
|
+
- **State transitions**: blur during, sharp after
|
|
153
|
+
|
|
154
|
+
---
|
|
155
|
+
|
|
156
|
+
## Optical Alignment
|
|
157
|
+
|
|
158
|
+
> "Sometimes it's necessary to break out of geometric alignment to make things feel visually balanced."
|
|
159
|
+
|
|
160
|
+
### Buttons with Icons
|
|
161
|
+
Reduce padding on the icon side so content appears centered:
|
|
162
|
+
```
|
|
163
|
+
[ Icon Text ] ← Geometric (mathematically centered, feels off)
|
|
164
|
+
[ Icon Text ] ← Optical (visually centered, feels right)
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
### Play Button Icons
|
|
168
|
+
The triangle points right, creating visual weight on the left. Shift it slightly right to appear centered.
|
|
169
|
+
|
|
170
|
+
### The Rule
|
|
171
|
+
If it looks wrong despite being mathematically correct, trust your eyes and adjust.
|
|
172
|
+
|
|
173
|
+
---
|
|
174
|
+
|
|
175
|
+
## Icon & State Animations
|
|
176
|
+
|
|
177
|
+
When icons change contextually (copy → check, loading → done), animate:
|
|
178
|
+
- Opacity
|
|
179
|
+
- Scale
|
|
180
|
+
- Blur
|
|
181
|
+
|
|
182
|
+
```jsx
|
|
183
|
+
<AnimatePresence mode="wait">
|
|
184
|
+
{isCopied ? (
|
|
185
|
+
<motion.div
|
|
186
|
+
initial={{ opacity: 0, scale: 0.8, filter: "blur(4px)" }}
|
|
187
|
+
animate={{ opacity: 1, scale: 1, filter: "blur(0px)" }}
|
|
188
|
+
exit={{ opacity: 0, scale: 0.8, filter: "blur(4px)" }}
|
|
189
|
+
>
|
|
190
|
+
<CheckIcon />
|
|
191
|
+
</motion.div>
|
|
192
|
+
) : (
|
|
193
|
+
<motion.div ...>
|
|
194
|
+
<CopyIcon />
|
|
195
|
+
</motion.div>
|
|
196
|
+
)}
|
|
197
|
+
</AnimatePresence>
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
**Why animate icon swaps?** Instant swaps feel jarring and can be missed. Animated transitions:
|
|
201
|
+
- Draw attention to the state change
|
|
202
|
+
- Feel responsive and polished
|
|
203
|
+
- Give the user confidence their action registered
|
|
204
|
+
|
|
205
|
+
---
|
|
206
|
+
|
|
207
|
+
## Shared Layout Animations
|
|
208
|
+
|
|
209
|
+
### FLIP Technique via layoutId
|
|
210
|
+
|
|
211
|
+
Motion's `layoutId` prop enables smooth transitions between completely different components:
|
|
212
|
+
|
|
213
|
+
```jsx
|
|
214
|
+
// In one location:
|
|
215
|
+
<motion.div layoutId="card" className="small-card" />
|
|
216
|
+
|
|
217
|
+
// In another location:
|
|
218
|
+
<motion.div layoutId="card" className="large-card" />
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
Motion automatically animates between them using the FLIP technique (First, Last, Inverse, Play).
|
|
222
|
+
|
|
223
|
+
### Best Practices
|
|
224
|
+
- Keep elements with `layoutId` **outside** of `AnimatePresence` to avoid conflicts
|
|
225
|
+
- If inside `AnimatePresence`, initial/exit animations trigger during layout animation (looks bad)
|
|
226
|
+
- Multiple elements can animate if each has a unique `layoutId`
|
|
227
|
+
- Works for different heights, widths, positions, and component types
|
|
228
|
+
|
|
229
|
+
---
|
|
230
|
+
|
|
231
|
+
## will-change Performance
|
|
232
|
+
|
|
233
|
+
A hint to the browser: "I'm about to animate these properties, please prepare."
|
|
234
|
+
|
|
235
|
+
```css
|
|
236
|
+
/* Good - specific properties that will animate */
|
|
237
|
+
.animated-button {
|
|
238
|
+
will-change: transform, opacity;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
/* Bad - too broad, wastes resources */
|
|
242
|
+
* { will-change: auto; }
|
|
243
|
+
.element { will-change: all; }
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
**Properties that benefit from will-change**:
|
|
247
|
+
- transform
|
|
248
|
+
- opacity
|
|
249
|
+
- filter (blur, brightness)
|
|
250
|
+
- clip-path
|
|
251
|
+
- mask
|
|
252
|
+
|
|
253
|
+
**Why it matters**: Without the hint, the browser promotes elements to GPU layers only when animation starts, causing first-frame stutter.
|
|
254
|
+
|
|
255
|
+
**When NOT to use**:
|
|
256
|
+
- On elements that won't animate
|
|
257
|
+
- On too many elements (each GPU layer uses memory)
|
|
258
|
+
- As a "fix" for janky animations (find the real cause)
|
|
259
|
+
|
|
260
|
+
---
|
|
261
|
+
|
|
262
|
+
## Gradient Animation Performance
|
|
263
|
+
|
|
264
|
+
**Cheap to animate (GPU-accelerated)**:
|
|
265
|
+
- background-position
|
|
266
|
+
- background-size
|
|
267
|
+
- opacity
|
|
268
|
+
|
|
269
|
+
**Expensive to animate**:
|
|
270
|
+
- Color stops
|
|
271
|
+
- Adding/removing gradient layers
|
|
272
|
+
- Switching gradient types
|
|
273
|
+
|
|
274
|
+
**Tip**: Animate a pseudo-element overlay or use CSS variables that transition indirectly.
|
|
275
|
+
|
|
276
|
+
---
|
|
277
|
+
|
|
278
|
+
## Common Mistakes (Jakub's Perspective)
|
|
279
|
+
|
|
280
|
+
- **Making enter and exit animations equally prominent** — Exits should be subtler
|
|
281
|
+
- **Using solid borders when shadows would adapt better** — Especially on varied backgrounds
|
|
282
|
+
- **Forgetting optical alignment** — Buttons with icons, play buttons, asymmetric shapes
|
|
283
|
+
- **Over-animating** — If users notice the animation itself, it's too much
|
|
284
|
+
- **Using the same animation everywhere** — Context should drive timing and easing choices
|
|
285
|
+
- **Ignoring hover state transitions** — Even small transitions (150-200ms) feel more polished than instant changes
|
|
286
|
+
|
|
287
|
+
---
|
|
288
|
+
|
|
289
|
+
## Jakub's Technique Index
|
|
290
|
+
|
|
291
|
+
From jakub.kr:
|
|
292
|
+
|
|
293
|
+
| Technique | Key Insight |
|
|
294
|
+
|-----------|-------------|
|
|
295
|
+
| Enter Animation | Opacity + translateY + blur creates "materializing" effect |
|
|
296
|
+
| Exit Animations | Subtler than enters, don't compete for attention |
|
|
297
|
+
| Shadows vs Borders | Shadows adapt to varied backgrounds via transparency |
|
|
298
|
+
| Gradients | Use oklch color space for smooth blending |
|
|
299
|
+
| Optical Alignment | Trust your eyes over mathematical centering |
|
|
300
|
+
| Shared Layout | layoutId enables FLIP-based smooth transitions |
|
|
301
|
+
| will-change | Specific properties only, not everywhere |
|
|
302
|
+
| Icon Animations | Animate swaps with opacity + scale + blur |
|
|
303
|
+
| Spring Animations | bounce: 0 for professional, bounce > 0 for playful |
|
|
304
|
+
| Motion Gestures | Micro-interactions for tactile feedback |
|
|
305
|
+
|
|
306
|
+
---
|
|
307
|
+
|
|
308
|
+
## Jakub vs. Emil vs. Jhey
|
|
309
|
+
|
|
310
|
+
| Aspect | Jakub | Emil | Jhey |
|
|
311
|
+
|--------|-------|------|------|
|
|
312
|
+
| **Focus** | Subtle polish | Restraint & speed | Playful experimentation |
|
|
313
|
+
| **Key question** | "Is this subtle enough?" | "Should this animate?" | "What could this become?" |
|
|
314
|
+
| **Signature technique** | Blur + opacity + translateY | Frequency-based decisions | CSS custom properties |
|
|
315
|
+
| **Ideal context** | Production polish | High-frequency tools | Learning & exploration |
|
|
316
|
+
|
|
317
|
+
**When to use Jakub**: You've decided something should animate (passed Emil's gate) and need to make it production-ready and polished.
|
|
@@ -0,0 +1,367 @@
|
|
|
1
|
+
# Jhey Tompkins' Animation Principles
|
|
2
|
+
|
|
3
|
+
Jhey Tompkins (@jh3yy) is a design engineer known for pushing the boundaries of CSS and creative coding. His approach emphasizes **playful experimentation**—learning through building whimsical projects where the joy of creation drives skill development.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Core Philosophy: Learn Through Play
|
|
8
|
+
|
|
9
|
+
> "I went from 'I want to learn X, so how do I fit it into Y' to 'I want to make Y, can I learn X to do it?'"
|
|
10
|
+
|
|
11
|
+
**The motivation should be making something cool—learning is a happy side effect.**
|
|
12
|
+
|
|
13
|
+
### Core Beliefs
|
|
14
|
+
|
|
15
|
+
- **No idea is a bad idea** — Document every spark, however weird (toadstools, Peter Griffin blinds, bread array slice/splice cartoon)
|
|
16
|
+
- **Don't ask "Why?" or "Is this practical?"** — Make what brings you joy first
|
|
17
|
+
- **"Useless" demos teach real skills** — CSS art teaches clip-path mastery, border-radius tricks, stacking contexts
|
|
18
|
+
- **Lateral learning** — Building diverse demos trains you to switch contexts and rise to challenges
|
|
19
|
+
- **You'll never have time to make everything** — And that's okay. The act of documenting ideas matters.
|
|
20
|
+
|
|
21
|
+
### The Playfulness Philosophy
|
|
22
|
+
|
|
23
|
+
Playfulness in code supercharges learning. The fact you're learning new skills is a bonus, not the goal. Work on ideas that spark joy for you.
|
|
24
|
+
|
|
25
|
+
**Keep notebooks everywhere** — including by your bed. Creative sparks happen at random times.
|
|
26
|
+
|
|
27
|
+
---
|
|
28
|
+
|
|
29
|
+
## When to Apply Jhey's Mindset
|
|
30
|
+
|
|
31
|
+
- Learning new techniques
|
|
32
|
+
- Personal projects and experiments
|
|
33
|
+
- When you're stuck in a creative rut
|
|
34
|
+
- Building your portfolio or demos
|
|
35
|
+
- Exploring what's possible with new CSS features
|
|
36
|
+
- When tutorials aren't clicking—try building something weird instead
|
|
37
|
+
|
|
38
|
+
---
|
|
39
|
+
|
|
40
|
+
## The Golden Rule
|
|
41
|
+
|
|
42
|
+
> "The best animation is that which goes unnoticed."
|
|
43
|
+
|
|
44
|
+
Jhey references this as a saying that has always stuck with him. Even in playful contexts, effective motion:
|
|
45
|
+
- Enhances the experience without demanding attention
|
|
46
|
+
- Feels natural and expected
|
|
47
|
+
- Serves a functional purpose
|
|
48
|
+
- Doesn't fatigue users on repeated interactions
|
|
49
|
+
|
|
50
|
+
---
|
|
51
|
+
|
|
52
|
+
## Easing & Timing
|
|
53
|
+
|
|
54
|
+
### Duration Impacts Naturalness
|
|
55
|
+
|
|
56
|
+
> "Duration is all about timing, and timing has a big impact on the movement's naturalness."
|
|
57
|
+
|
|
58
|
+
### Easing Selection Guidelines
|
|
59
|
+
|
|
60
|
+
Each easing curve communicates something to the viewer. **Context matters more than rules.**
|
|
61
|
+
|
|
62
|
+
| Easing | Feel | Good For |
|
|
63
|
+
|--------|------|----------|
|
|
64
|
+
| `ease-out` | Fast start, gentle stop | Elements entering view (arriving) |
|
|
65
|
+
| `ease-in` | Gentle start, fast exit | Elements leaving view (departing) |
|
|
66
|
+
| `ease-in-out` | Gentle both ends | Elements changing state while visible |
|
|
67
|
+
| `linear` | Constant speed | Continuous loops, progress indicators |
|
|
68
|
+
| `spring` | Natural deceleration | Interactive elements, professional UI |
|
|
69
|
+
|
|
70
|
+
### The Context Rule
|
|
71
|
+
|
|
72
|
+
> "You wouldn't use 'Elastic' for a bank's website, but it might work perfectly for an energetic site for children."
|
|
73
|
+
|
|
74
|
+
Brand personality should drive easing choices:
|
|
75
|
+
- Playful brand → bouncy, elastic easing
|
|
76
|
+
- Professional brand → subtle springs or ease-out
|
|
77
|
+
|
|
78
|
+
**When NOT to use bouncy/elastic easing**:
|
|
79
|
+
- Professional/enterprise applications
|
|
80
|
+
- Frequently repeated interactions (gets tiresome)
|
|
81
|
+
- Error states or serious UI
|
|
82
|
+
- When users need to complete tasks quickly
|
|
83
|
+
|
|
84
|
+
---
|
|
85
|
+
|
|
86
|
+
## The linear() Function
|
|
87
|
+
|
|
88
|
+
CSS `linear()` enables bounce, elastic, and spring effects in pure CSS:
|
|
89
|
+
|
|
90
|
+
```css
|
|
91
|
+
:root {
|
|
92
|
+
--bounce-easing: linear(
|
|
93
|
+
0, 0.004, 0.016, 0.035, 0.063, 0.098, 0.141 13.6%, 0.25, 0.391, 0.563, 0.765,
|
|
94
|
+
1, 0.891 40.9%, 0.848, 0.813, 0.785, 0.766, 0.754, 0.75, 0.754, 0.766, 0.785,
|
|
95
|
+
0.813, 0.848, 0.891 68.2%, 1 72.7%, 0.973, 0.953, 0.941, 0.938, 0.941, 0.953,
|
|
96
|
+
0.973, 1, 0.988, 0.984, 0.988, 1
|
|
97
|
+
);
|
|
98
|
+
}
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
**Tool**: Use Jake Archibald's linear() generator: https://linear-easing-generator.netlify.app/
|
|
102
|
+
|
|
103
|
+
---
|
|
104
|
+
|
|
105
|
+
## Animation Fill Mode
|
|
106
|
+
|
|
107
|
+
Use `animation-fill-mode` to prevent jarring visual resets:
|
|
108
|
+
|
|
109
|
+
| Mode | Behavior |
|
|
110
|
+
|------|----------|
|
|
111
|
+
| `forwards` | Retains animation styling after completion |
|
|
112
|
+
| `backwards` | Retains style from first keyframe before animation starts |
|
|
113
|
+
| `both` | Retains styling in both directions |
|
|
114
|
+
|
|
115
|
+
**Critical for**: Fade-in sequences with delays. Without `backwards`, elements flash at full opacity before their delayed animation starts, then pop to invisible, then fade in.
|
|
116
|
+
|
|
117
|
+
---
|
|
118
|
+
|
|
119
|
+
## Stagger Techniques
|
|
120
|
+
|
|
121
|
+
`animation-delay` only applies once (not per iteration). Approaches:
|
|
122
|
+
|
|
123
|
+
### 1. Different delays with finite iterations
|
|
124
|
+
Works for one-time sequences.
|
|
125
|
+
|
|
126
|
+
### 2. Pad keyframes to create stagger
|
|
127
|
+
```css
|
|
128
|
+
@keyframes spin {
|
|
129
|
+
0%, 50% { transform: rotate(0deg); }
|
|
130
|
+
100% { transform: rotate(360deg); }
|
|
131
|
+
}
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
### 3. Negative delays for "already in progress" effects
|
|
135
|
+
```css
|
|
136
|
+
.element {
|
|
137
|
+
animation-delay: calc(var(--index) * -0.2s);
|
|
138
|
+
}
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
This makes animations appear mid-flight from the start—useful for staggered continuous animations.
|
|
142
|
+
|
|
143
|
+
---
|
|
144
|
+
|
|
145
|
+
## CSS Custom Properties & @property
|
|
146
|
+
|
|
147
|
+
### Type Specification Unlocks Animation
|
|
148
|
+
|
|
149
|
+
The `@property` rule lets you declare types for CSS variables, enabling smooth interpolation:
|
|
150
|
+
|
|
151
|
+
```css
|
|
152
|
+
@property --hue {
|
|
153
|
+
initial-value: 0;
|
|
154
|
+
inherits: false;
|
|
155
|
+
syntax: '<number>';
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
@keyframes rainbow {
|
|
159
|
+
to { --hue: 360; }
|
|
160
|
+
}
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
**Available types**: length, number, percentage, color, angle, time, integer, transform-list
|
|
164
|
+
|
|
165
|
+
**Why this matters**: Without `@property`, CSS sees custom properties as strings. Strings can't interpolate—they just swap. With a declared type, the browser knows how to smoothly transition between values.
|
|
166
|
+
|
|
167
|
+
### Decompose Complex Transforms
|
|
168
|
+
|
|
169
|
+
Instead of animating a monolithic transform, split into typed properties for curved paths:
|
|
170
|
+
|
|
171
|
+
```css
|
|
172
|
+
@property --x { syntax: '<percentage>'; initial-value: 0%; inherits: false; }
|
|
173
|
+
@property --y { syntax: '<percentage>'; initial-value: 0%; inherits: false; }
|
|
174
|
+
|
|
175
|
+
.ball {
|
|
176
|
+
transform: translateX(var(--x)) translateY(var(--y));
|
|
177
|
+
animation: throw 1s;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
@keyframes throw {
|
|
181
|
+
0% { --x: -500%; }
|
|
182
|
+
50% { --y: -250%; }
|
|
183
|
+
100% { --x: 500%; }
|
|
184
|
+
}
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
This creates curved motion paths impossible with standard transform animation—the ball arcs through space rather than moving in straight lines.
|
|
188
|
+
|
|
189
|
+
### Scoped Variables for Dynamic Behavior
|
|
190
|
+
|
|
191
|
+
CSS custom properties respect scope:
|
|
192
|
+
|
|
193
|
+
```css
|
|
194
|
+
.item { --delay: 0; animation-delay: calc(var(--delay) * 100ms); }
|
|
195
|
+
.item:nth-child(1) { --delay: 0; }
|
|
196
|
+
.item:nth-child(2) { --delay: 1; }
|
|
197
|
+
.item:nth-child(3) { --delay: 2; }
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
Use scoped variables to create varied behavior from a single animation definition.
|
|
201
|
+
|
|
202
|
+
---
|
|
203
|
+
|
|
204
|
+
## 3D CSS
|
|
205
|
+
|
|
206
|
+
### Think in Cuboids
|
|
207
|
+
|
|
208
|
+
> "Think in cubes instead of boxes"
|
|
209
|
+
|
|
210
|
+
Complex 3D scenes are assemblies of cube-shaped elements (like LEGO). Decompose any 3D object into cuboids.
|
|
211
|
+
|
|
212
|
+
### Essential Setup
|
|
213
|
+
|
|
214
|
+
```css
|
|
215
|
+
.scene {
|
|
216
|
+
transform-style: preserve-3d;
|
|
217
|
+
perspective: 1000px;
|
|
218
|
+
}
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
### Responsive 3D
|
|
222
|
+
|
|
223
|
+
Use CSS variables for dimensions and `vmin` units:
|
|
224
|
+
|
|
225
|
+
```css
|
|
226
|
+
.cube {
|
|
227
|
+
--size: 10vmin;
|
|
228
|
+
width: var(--size);
|
|
229
|
+
height: var(--size);
|
|
230
|
+
}
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
---
|
|
234
|
+
|
|
235
|
+
## Scroll-Driven Animations
|
|
236
|
+
|
|
237
|
+
### The Core Problem
|
|
238
|
+
|
|
239
|
+
Scroll-driven animations are tied to scroll **speed**. If users scroll slowly, animations play slowly. This feels wrong for most UI—you want animations to trigger at a scroll position, not be controlled by scroll speed.
|
|
240
|
+
|
|
241
|
+
### Duration Control Pattern
|
|
242
|
+
|
|
243
|
+
Use two coordinated animations:
|
|
244
|
+
|
|
245
|
+
1. **Trigger animation**: Scroll-driven, toggles a custom property when element enters view
|
|
246
|
+
2. **Main animation**: Traditional duration-based, activated via Style Query
|
|
247
|
+
|
|
248
|
+
This severs the connection between scroll speed and animation timing—the animation runs over a fixed duration once triggered.
|
|
249
|
+
|
|
250
|
+
### Progressive Enhancement
|
|
251
|
+
|
|
252
|
+
Always provide fallbacks:
|
|
253
|
+
|
|
254
|
+
```javascript
|
|
255
|
+
if (!CSS.supports('animation-timeline', 'scroll()')) {
|
|
256
|
+
// Use IntersectionObserver instead
|
|
257
|
+
}
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
---
|
|
261
|
+
|
|
262
|
+
## CSS Art & Illustrations
|
|
263
|
+
|
|
264
|
+
### Why CSS Art Matters
|
|
265
|
+
|
|
266
|
+
CSS art teaches real skills that transfer to production work:
|
|
267
|
+
- clip-path mastery
|
|
268
|
+
- Border-radius tricks
|
|
269
|
+
- Stacking contexts
|
|
270
|
+
- Complex gradients
|
|
271
|
+
- Pseudo-element layering
|
|
272
|
+
|
|
273
|
+
### Advice for Complex Illustrations
|
|
274
|
+
|
|
275
|
+
- Break down into simple shapes
|
|
276
|
+
- Use pseudo-elements liberally (`:before`, `:after`)
|
|
277
|
+
- Layer with z-index carefully
|
|
278
|
+
- Use CSS variables for repeated values
|
|
279
|
+
- Don't be afraid to use many elements
|
|
280
|
+
|
|
281
|
+
---
|
|
282
|
+
|
|
283
|
+
## Motion Paths
|
|
284
|
+
|
|
285
|
+
### Responsive Motion Paths
|
|
286
|
+
|
|
287
|
+
```css
|
|
288
|
+
.element {
|
|
289
|
+
offset-path: path('M 0 0 Q 50 100 100 0');
|
|
290
|
+
animation: move 2s ease-in-out infinite alternate;
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
@keyframes move {
|
|
294
|
+
to { offset-distance: 100%; }
|
|
295
|
+
}
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
For responsive paths, calculate path coordinates based on container size using CSS variables or JavaScript.
|
|
299
|
+
|
|
300
|
+
---
|
|
301
|
+
|
|
302
|
+
## Common Mistakes (Jhey's Perspective)
|
|
303
|
+
|
|
304
|
+
- **Filtering ideas based on "usefulness" too early** — Make first, judge later
|
|
305
|
+
- **Not documenting random creative sparks** — Keep notebooks everywhere, including by your bed
|
|
306
|
+
- **Thinking CSS art is useless** — It teaches real skills (clip-path, layering, complex shapes)
|
|
307
|
+
- **Focusing on "How do I learn X?" instead of "How do I make Y?"** — Let ideas drive learning
|
|
308
|
+
- **Following tutorials without experimenting** — Tutorials teach techniques; experimentation teaches problem-solving
|
|
309
|
+
- **Giving up when something doesn't work** — The struggle is where learning happens
|
|
310
|
+
|
|
311
|
+
---
|
|
312
|
+
|
|
313
|
+
## When to Experiment vs. Ship
|
|
314
|
+
|
|
315
|
+
| Situation | Approach |
|
|
316
|
+
|-----------|----------|
|
|
317
|
+
| Learning new CSS feature | Build something weird and fun |
|
|
318
|
+
| Portfolio piece | Push boundaries, show creativity |
|
|
319
|
+
| Personal project | Follow your joy |
|
|
320
|
+
| Client work | Apply Jakub's production polish instead |
|
|
321
|
+
| High-frequency tool | Apply Emil's restraint instead |
|
|
322
|
+
|
|
323
|
+
The playful approach is for **learning and exploration**. For production, switch to Jakub or Emil's mindset.
|
|
324
|
+
|
|
325
|
+
---
|
|
326
|
+
|
|
327
|
+
## Jhey's Technique Index
|
|
328
|
+
|
|
329
|
+
| Technique | Key Insight |
|
|
330
|
+
|-----------|-------------|
|
|
331
|
+
| linear() function | Pure CSS bounce/elastic/spring effects |
|
|
332
|
+
| @property | Type specification enables CSS variable animation |
|
|
333
|
+
| animation-fill-mode | Prevent flash before delayed animations |
|
|
334
|
+
| Negative delays | "Already in progress" stagger effects |
|
|
335
|
+
| 3D CSS | Think in cubes, use preserve-3d |
|
|
336
|
+
| Scroll-driven | Decouple scroll speed from animation duration |
|
|
337
|
+
| CSS art | "Useless" demos teach real skills |
|
|
338
|
+
| Scoped variables | Varied behavior from single animation |
|
|
339
|
+
|
|
340
|
+
---
|
|
341
|
+
|
|
342
|
+
## Jhey's Article Index
|
|
343
|
+
|
|
344
|
+
From Smashing Magazine, CSS-Tricks, and more:
|
|
345
|
+
|
|
346
|
+
| Article | Topic |
|
|
347
|
+
|---------|-------|
|
|
348
|
+
| Playfulness In Code | Philosophy of learning through fun |
|
|
349
|
+
| The Path To Awesome CSS Easing With linear() | Custom easing in pure CSS |
|
|
350
|
+
| Exploring @property | Animating CSS custom properties |
|
|
351
|
+
| CSS in 3D: Thinking in Cubes | 3D CSS fundamentals |
|
|
352
|
+
| Advice for Complex CSS Illustrations | CSS art techniques |
|
|
353
|
+
| Scroll-Driven Animation with Duration | Decoupling scroll from timing |
|
|
354
|
+
| The Power of Scope with CSS Custom Properties | Dynamic behavior patterns |
|
|
355
|
+
|
|
356
|
+
---
|
|
357
|
+
|
|
358
|
+
## Jhey vs. Emil vs. Jakub
|
|
359
|
+
|
|
360
|
+
| Aspect | Jhey | Emil | Jakub |
|
|
361
|
+
|--------|------|------|-------|
|
|
362
|
+
| **Focus** | Playful experimentation | Restraint & speed | Subtle polish |
|
|
363
|
+
| **Key question** | "What could this become?" | "Should this animate?" | "Is this subtle enough?" |
|
|
364
|
+
| **Signature technique** | CSS custom properties | Frequency-based decisions | Blur + opacity + translateY |
|
|
365
|
+
| **Ideal context** | Learning & exploration | High-frequency tools | Production polish |
|
|
366
|
+
|
|
367
|
+
**When to use Jhey**: You're learning something new, exploring what's possible, or building something for the joy of it. The skills transfer to production work later.
|