@opendirectory.dev/skills 0.1.58 → 0.1.59
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/package.json +1 -1
- package/registry.json +10 -0
- package/skills/graphic-gif/README.md +99 -0
- package/skills/graphic-gif/SKILL.md +313 -0
- package/skills/graphic-gif/evals/evals.json +30 -0
- package/skills/graphic-gif/references/animation-library.md +446 -0
- package/skills/graphic-gif/references/style-presets.md +194 -0
- package/skills/graphic-gif/scripts/capture-and-encode.mjs +201 -0
- package/skills/graphic-gif/scripts/export-gif.sh +274 -0
|
@@ -0,0 +1,446 @@
|
|
|
1
|
+
# Animation Library — graphic-gif
|
|
2
|
+
|
|
3
|
+
6 animation types for CSS-animated GIFs. All specs for 800×800px canvas (1vw = 8px).
|
|
4
|
+
|
|
5
|
+
Read this file before generating HTML in Step 3. Pick one animation type per GIF.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## 1. fade-in
|
|
10
|
+
|
|
11
|
+
Content fades in from transparent, with subtle upward drift.
|
|
12
|
+
|
|
13
|
+
**Best for:** Quotes, announcements, single-stat reveals, brand messages.
|
|
14
|
+
|
|
15
|
+
**Key technique:** `opacity: 0 → 1` + `translateY(12px → 0)` with smooth ease-out.
|
|
16
|
+
|
|
17
|
+
```css
|
|
18
|
+
@keyframes fadeIn {
|
|
19
|
+
from {
|
|
20
|
+
opacity: 0;
|
|
21
|
+
transform: translateY(12px);
|
|
22
|
+
}
|
|
23
|
+
to {
|
|
24
|
+
opacity: 1;
|
|
25
|
+
transform: translateY(0);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
.fade-item {
|
|
30
|
+
animation: fadeIn 0.8s cubic-bezier(0.22, 1, 0.36, 1) forwards;
|
|
31
|
+
opacity: 0; /* start hidden */
|
|
32
|
+
}
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
**For staggered multi-element fade:**
|
|
36
|
+
Do NOT use `animation-delay`. Instead, structure the animation duration to reveal elements sequentially. Web Animations API will seek through the full timeline — each element needs its own named animation with different start points baked into its `@keyframes` percentages.
|
|
37
|
+
|
|
38
|
+
Example — 3 elements staggered within a 3s animation:
|
|
39
|
+
```css
|
|
40
|
+
/* Element 1: reveals at 0–25% (0–750ms) */
|
|
41
|
+
@keyframes fadeEl1 {
|
|
42
|
+
0% { opacity: 0; transform: translateY(12px); }
|
|
43
|
+
25% { opacity: 1; transform: translateY(0); }
|
|
44
|
+
100% { opacity: 1; transform: translateY(0); }
|
|
45
|
+
}
|
|
46
|
+
/* Element 2: reveals at 25–50% (750–1500ms) */
|
|
47
|
+
@keyframes fadeEl2 {
|
|
48
|
+
0% { opacity: 0; transform: translateY(12px); }
|
|
49
|
+
25% { opacity: 0; transform: translateY(12px); }
|
|
50
|
+
50% { opacity: 1; transform: translateY(0); }
|
|
51
|
+
100% { opacity: 1; transform: translateY(0); }
|
|
52
|
+
}
|
|
53
|
+
/* Element 3: reveals at 50–75% (1500–2250ms) */
|
|
54
|
+
@keyframes fadeEl3 {
|
|
55
|
+
0% { opacity: 0; transform: translateY(12px); }
|
|
56
|
+
50% { opacity: 0; transform: translateY(12px); }
|
|
57
|
+
75% { opacity: 1; transform: translateY(0); }
|
|
58
|
+
100% { opacity: 1; transform: translateY(0); }
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
.el1 { animation: fadeEl1 3s cubic-bezier(0.22, 1, 0.36, 1) forwards; }
|
|
62
|
+
.el2 { animation: fadeEl2 3s cubic-bezier(0.22, 1, 0.36, 1) forwards; }
|
|
63
|
+
.el3 { animation: fadeEl3 3s cubic-bezier(0.22, 1, 0.36, 1) forwards; }
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
**HTML structure:**
|
|
67
|
+
```html
|
|
68
|
+
<div class="canvas">
|
|
69
|
+
<div class="content">
|
|
70
|
+
<p class="el1 fade-item">Main message here</p>
|
|
71
|
+
<p class="el2 fade-item">Supporting line here</p>
|
|
72
|
+
<p class="el3 fade-item">Third element here</p>
|
|
73
|
+
</div>
|
|
74
|
+
</div>
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
---
|
|
78
|
+
|
|
79
|
+
## 2. slide-in
|
|
80
|
+
|
|
81
|
+
Elements slide in from one edge with a spring overshoot effect.
|
|
82
|
+
|
|
83
|
+
**Best for:** Headlines, key stats, before/after comparisons, list reveals.
|
|
84
|
+
|
|
85
|
+
**Directions:** left (default), right, top, bottom.
|
|
86
|
+
|
|
87
|
+
**Key technique:** `translateX/Y` + spring cubic-bezier `(0.34, 1.56, 0.64, 1)` for overshoot.
|
|
88
|
+
|
|
89
|
+
```css
|
|
90
|
+
/* Slide from left */
|
|
91
|
+
@keyframes slideInLeft {
|
|
92
|
+
from { opacity: 0; transform: translateX(-60px); }
|
|
93
|
+
to { opacity: 1; transform: translateX(0); }
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/* Slide from right */
|
|
97
|
+
@keyframes slideInRight {
|
|
98
|
+
from { opacity: 0; transform: translateX(60px); }
|
|
99
|
+
to { opacity: 1; transform: translateX(0); }
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/* Slide from bottom */
|
|
103
|
+
@keyframes slideInUp {
|
|
104
|
+
from { opacity: 0; transform: translateY(40px); }
|
|
105
|
+
to { opacity: 1; transform: translateY(0); }
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
.slide-item {
|
|
109
|
+
animation: slideInLeft 0.6s cubic-bezier(0.34, 1.56, 0.64, 1) forwards;
|
|
110
|
+
opacity: 0;
|
|
111
|
+
}
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
**For sequential slide-in of multiple elements (bake stagger into keyframes):**
|
|
115
|
+
```css
|
|
116
|
+
@keyframes slideEl1 {
|
|
117
|
+
0% { opacity: 0; transform: translateX(-60px); }
|
|
118
|
+
20% { opacity: 1; transform: translateX(0); }
|
|
119
|
+
100% { opacity: 1; transform: translateX(0); }
|
|
120
|
+
}
|
|
121
|
+
@keyframes slideEl2 {
|
|
122
|
+
0% { opacity: 0; transform: translateX(-60px); }
|
|
123
|
+
20% { opacity: 0; transform: translateX(-60px); }
|
|
124
|
+
40% { opacity: 1; transform: translateX(0); }
|
|
125
|
+
100% { opacity: 1; transform: translateX(0); }
|
|
126
|
+
}
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
---
|
|
130
|
+
|
|
131
|
+
## 3. typewriter
|
|
132
|
+
|
|
133
|
+
Text types out character by character. The most effective animation for revealing stats, insights, or hooks.
|
|
134
|
+
|
|
135
|
+
**Best for:** Single sentences, stat reveals, developer-audience content, hooks with tension.
|
|
136
|
+
|
|
137
|
+
**Key technique:** `steps(N, end)` with `overflow: hidden` and `width: 0 → 100%`. N = exact character count of the text string.
|
|
138
|
+
|
|
139
|
+
**Critical:** N MUST equal the exact character count of the text. Wrong N = wrong reveal speed.
|
|
140
|
+
|
|
141
|
+
```css
|
|
142
|
+
.typewriter-text {
|
|
143
|
+
display: inline-block;
|
|
144
|
+
overflow: hidden;
|
|
145
|
+
white-space: nowrap;
|
|
146
|
+
border-right: 2px solid var(--accent);
|
|
147
|
+
animation:
|
|
148
|
+
typing 2.5s steps(N, end) forwards,
|
|
149
|
+
blink 0.75s step-end infinite;
|
|
150
|
+
width: 0;
|
|
151
|
+
max-width: 100%;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
@keyframes typing {
|
|
155
|
+
from { width: 0; }
|
|
156
|
+
to { width: 100%; }
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
@keyframes blink {
|
|
160
|
+
from, to { border-color: transparent; }
|
|
161
|
+
50% { border-color: var(--accent); }
|
|
162
|
+
}
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
**Where N = character count:**
|
|
166
|
+
- Count spaces, punctuation, and numbers as characters
|
|
167
|
+
- "73% of buyers" → N = 14
|
|
168
|
+
- "Hello, World!" → N = 13
|
|
169
|
+
|
|
170
|
+
**For multi-line typewriter (sequential lines):**
|
|
171
|
+
Each line needs its own keyframe with percentage-baked timing:
|
|
172
|
+
```css
|
|
173
|
+
/* Line 1: types from 0% to 40% of total duration */
|
|
174
|
+
@keyframes typeLine1 {
|
|
175
|
+
0% { width: 0; }
|
|
176
|
+
40% { width: 100%; }
|
|
177
|
+
100% { width: 100%; }
|
|
178
|
+
}
|
|
179
|
+
/* Line 2: appears at 40%, types from 40% to 80% */
|
|
180
|
+
@keyframes typeLine2 {
|
|
181
|
+
0% { width: 0; opacity: 0; }
|
|
182
|
+
40% { width: 0; opacity: 0; }
|
|
183
|
+
40.1%{ width: 0; opacity: 1; }
|
|
184
|
+
80% { width: 100%; opacity: 1; }
|
|
185
|
+
100% { width: 100%; opacity: 1; }
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
.line1 { animation: typeLine1 3s steps(N1, end) forwards; }
|
|
189
|
+
.line2 { animation: typeLine2 3s steps(N2, end) forwards; }
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
**HTML structure:**
|
|
193
|
+
```html
|
|
194
|
+
<div class="canvas">
|
|
195
|
+
<div class="typewriter-wrapper">
|
|
196
|
+
<span class="typewriter-text">Your text here exactly</span>
|
|
197
|
+
</div>
|
|
198
|
+
</div>
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
---
|
|
202
|
+
|
|
203
|
+
## 4. counter
|
|
204
|
+
|
|
205
|
+
Numbers count up from 0 to a target value. Native CSS, no JavaScript.
|
|
206
|
+
|
|
207
|
+
**Best for:** Stats, metrics, growth numbers, percentages. High-impact for data-driven content.
|
|
208
|
+
|
|
209
|
+
**Key technique:** `@property --num` CSS custom property with integer interpolation. `counter-reset: num var(--num)` renders the integer as text via `::after { content: counter(num) }`.
|
|
210
|
+
|
|
211
|
+
**Critical:** Requires Chromium rendering (Playwright uses Chromium → works for GIF export).
|
|
212
|
+
|
|
213
|
+
```css
|
|
214
|
+
@property --num {
|
|
215
|
+
syntax: '<integer>';
|
|
216
|
+
inherits: false;
|
|
217
|
+
initial-value: 0;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
.counter {
|
|
221
|
+
animation: countUp var(--duration, 2s) linear forwards;
|
|
222
|
+
counter-reset: num var(--num);
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
.counter::after {
|
|
226
|
+
content: counter(num);
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
@keyframes countUp {
|
|
230
|
+
from { --num: 0; }
|
|
231
|
+
to { --num: var(--target); }
|
|
232
|
+
}
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
**Usage (set target per element):**
|
|
236
|
+
```html
|
|
237
|
+
<!-- Counts 0 → 73 over 2 seconds -->
|
|
238
|
+
<div class="counter" style="--target: 73; --duration: 2s;"></div>
|
|
239
|
+
|
|
240
|
+
<!-- Counts 0 → 500 over 3 seconds -->
|
|
241
|
+
<div class="counter" style="--target: 500; --duration: 3s;"></div>
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
**For counter with suffix (%, K, etc.) — use ::before/::after wrappers:**
|
|
245
|
+
```html
|
|
246
|
+
<div class="stat-block">
|
|
247
|
+
<span class="counter" style="--target: 73; --duration: 2s;"></span>
|
|
248
|
+
<span class="suffix">%</span>
|
|
249
|
+
</div>
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
**For multiple counters with staggered start (bake into keyframes):**
|
|
253
|
+
```css
|
|
254
|
+
/* Counter 1: counts during 0–60% of animation */
|
|
255
|
+
@keyframes count1 {
|
|
256
|
+
0% { --num: 0; }
|
|
257
|
+
60% { --num: 73; }
|
|
258
|
+
100% { --num: 73; }
|
|
259
|
+
}
|
|
260
|
+
/* Counter 2: counts during 40–100% of animation */
|
|
261
|
+
@keyframes count2 {
|
|
262
|
+
0% { --num: 0; }
|
|
263
|
+
40% { --num: 0; }
|
|
264
|
+
100% { --num: 250; }
|
|
265
|
+
}
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
---
|
|
269
|
+
|
|
270
|
+
## 5. pulse
|
|
271
|
+
|
|
272
|
+
Pulsing / breathing scale or glow effect. Infinite loop by nature.
|
|
273
|
+
|
|
274
|
+
**Best for:** CTAs, icons, emphasis elements, "live" indicators, attention draws.
|
|
275
|
+
|
|
276
|
+
**Key technique:** `scale(1) → scale(1.06) → scale(1)` with `ease-in-out` and `animation-iteration-count: infinite`.
|
|
277
|
+
|
|
278
|
+
**Note:** Since this animation loops in CSS (`infinite`), the GIF frame capture simply captures `duration * fps` frames — the animation is already looping at the CSS level.
|
|
279
|
+
|
|
280
|
+
```css
|
|
281
|
+
/* Scale pulse */
|
|
282
|
+
@keyframes pulse {
|
|
283
|
+
0%, 100% { transform: scale(1); opacity: 1; }
|
|
284
|
+
50% { transform: scale(1.06); opacity: 0.85; }
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
/* Glow pulse (for accent elements on dark backgrounds) */
|
|
288
|
+
@keyframes glowPulse {
|
|
289
|
+
0%, 100% {
|
|
290
|
+
box-shadow: 0 0 0 0 rgba(var(--accent-rgb), 0.4);
|
|
291
|
+
transform: scale(1);
|
|
292
|
+
}
|
|
293
|
+
50% {
|
|
294
|
+
box-shadow: 0 0 20px 8px rgba(var(--accent-rgb), 0.15);
|
|
295
|
+
transform: scale(1.04);
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
.pulse-item {
|
|
300
|
+
animation: pulse 1.5s ease-in-out infinite;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
.cta-button {
|
|
304
|
+
animation: glowPulse 2s ease-in-out infinite;
|
|
305
|
+
/* define --accent-rgb as R,G,B values: e.g. 250,204,21 for #FACC15 */
|
|
306
|
+
}
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
**Combining pulse with static content:**
|
|
310
|
+
```css
|
|
311
|
+
/* Background stays still — only the CTA button pulses */
|
|
312
|
+
.static-content { /* no animation */ }
|
|
313
|
+
.cta-button { animation: pulse 2s ease-in-out infinite; }
|
|
314
|
+
```
|
|
315
|
+
|
|
316
|
+
**HTML structure:**
|
|
317
|
+
```html
|
|
318
|
+
<div class="canvas">
|
|
319
|
+
<div class="static-content">
|
|
320
|
+
<h1>Ready to reduce churn?</h1>
|
|
321
|
+
<p>See how DataPulse works</p>
|
|
322
|
+
</div>
|
|
323
|
+
<button class="cta-button pulse-item">Book a demo →</button>
|
|
324
|
+
</div>
|
|
325
|
+
```
|
|
326
|
+
|
|
327
|
+
---
|
|
328
|
+
|
|
329
|
+
## 6. loop-scroll
|
|
330
|
+
|
|
331
|
+
Infinite scrolling ticker / marquee. Content scrolls continuously in one direction.
|
|
332
|
+
|
|
333
|
+
**Best for:** Feature lists, social proof, product attributes, news-style tickers, repeated keyword emphasis.
|
|
334
|
+
|
|
335
|
+
**Key technique:** Content is duplicated (`[items] [items]`). `translateX(0 → -50%)` with `linear` timing and `infinite` iteration — the second copy seamlessly takes over when the first exits.
|
|
336
|
+
|
|
337
|
+
**Critical:** Content MUST be duplicated exactly once. If 4 items, the HTML contains all 8 items (4 original + 4 copy). The GIF frame count covers one full scroll cycle = smooth loop.
|
|
338
|
+
|
|
339
|
+
```css
|
|
340
|
+
.ticker-container {
|
|
341
|
+
width: 800px;
|
|
342
|
+
overflow: hidden;
|
|
343
|
+
/* Add a fade mask for polished edge effect */
|
|
344
|
+
-webkit-mask-image: linear-gradient(
|
|
345
|
+
to right,
|
|
346
|
+
transparent 0%,
|
|
347
|
+
black 8%,
|
|
348
|
+
black 92%,
|
|
349
|
+
transparent 100%
|
|
350
|
+
);
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
.ticker-track {
|
|
354
|
+
display: flex;
|
|
355
|
+
align-items: center;
|
|
356
|
+
white-space: nowrap;
|
|
357
|
+
/* Duration = number of original items × base speed */
|
|
358
|
+
animation: scrollLeft linear infinite;
|
|
359
|
+
animation-duration: calc(var(--item-count) * 1.5s);
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
@keyframes scrollLeft {
|
|
363
|
+
from { transform: translateX(0); }
|
|
364
|
+
to { transform: translateX(-50%); }
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
.ticker-item {
|
|
368
|
+
padding: 0 clamp(1.5rem, 4vw, 2.5rem);
|
|
369
|
+
font-size: clamp(1rem, 2vw, 1.4rem);
|
|
370
|
+
font-weight: 600;
|
|
371
|
+
flex-shrink: 0;
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
.ticker-sep {
|
|
375
|
+
color: var(--accent);
|
|
376
|
+
padding: 0 0.5rem;
|
|
377
|
+
flex-shrink: 0;
|
|
378
|
+
}
|
|
379
|
+
```
|
|
380
|
+
|
|
381
|
+
**HTML structure (items duplicated):**
|
|
382
|
+
```html
|
|
383
|
+
<div class="canvas">
|
|
384
|
+
<div class="ticker-container">
|
|
385
|
+
<div class="ticker-track" style="--item-count: 4;">
|
|
386
|
+
<!-- Original set -->
|
|
387
|
+
<span class="ticker-item">Reduce Churn</span>
|
|
388
|
+
<span class="ticker-sep">·</span>
|
|
389
|
+
<span class="ticker-item">Increase NRR</span>
|
|
390
|
+
<span class="ticker-sep">·</span>
|
|
391
|
+
<span class="ticker-item">Boost LTV</span>
|
|
392
|
+
<span class="ticker-sep">·</span>
|
|
393
|
+
<span class="ticker-item">Drive Expansion</span>
|
|
394
|
+
<span class="ticker-sep">·</span>
|
|
395
|
+
<!-- Duplicate set (exact copy) -->
|
|
396
|
+
<span class="ticker-item">Reduce Churn</span>
|
|
397
|
+
<span class="ticker-sep">·</span>
|
|
398
|
+
<span class="ticker-item">Increase NRR</span>
|
|
399
|
+
<span class="ticker-sep">·</span>
|
|
400
|
+
<span class="ticker-item">Boost LTV</span>
|
|
401
|
+
<span class="ticker-sep">·</span>
|
|
402
|
+
<span class="ticker-item">Drive Expansion</span>
|
|
403
|
+
<span class="ticker-sep">·</span>
|
|
404
|
+
</div>
|
|
405
|
+
</div>
|
|
406
|
+
</div>
|
|
407
|
+
```
|
|
408
|
+
|
|
409
|
+
**For vertical scroll (top to bottom):**
|
|
410
|
+
```css
|
|
411
|
+
@keyframes scrollUp {
|
|
412
|
+
from { transform: translateY(0); }
|
|
413
|
+
to { transform: translateY(-50%); }
|
|
414
|
+
}
|
|
415
|
+
.ticker-track-vertical {
|
|
416
|
+
display: flex;
|
|
417
|
+
flex-direction: column;
|
|
418
|
+
animation: scrollUp linear infinite;
|
|
419
|
+
}
|
|
420
|
+
```
|
|
421
|
+
|
|
422
|
+
---
|
|
423
|
+
|
|
424
|
+
## Choosing the Right Animation
|
|
425
|
+
|
|
426
|
+
| Use case | Best animation |
|
|
427
|
+
|---|---|
|
|
428
|
+
| "Show a stat with impact" | counter or fade-in |
|
|
429
|
+
| "Reveal a quote or insight" | typewriter or fade-in |
|
|
430
|
+
| "Social media hook" | typewriter (terminal style) |
|
|
431
|
+
| "CTA / button emphasis" | pulse |
|
|
432
|
+
| "List of features/benefits" | loop-scroll |
|
|
433
|
+
| "Headline announcement" | slide-in |
|
|
434
|
+
| "Multiple stats together" | counter (multiple) |
|
|
435
|
+
| "Developer/tech audience" | typewriter (terminal style) |
|
|
436
|
+
|
|
437
|
+
## File Size Guidance by Animation Type
|
|
438
|
+
|
|
439
|
+
| Animation | Typical size | Why |
|
|
440
|
+
|---|---|---|
|
|
441
|
+
| fade-in (1 element) | 100–400KB | Few color changes between frames |
|
|
442
|
+
| slide-in | 200–600KB | More color variation as element moves |
|
|
443
|
+
| typewriter (terminal) | 150–500KB | Dark bg + monochrome text = few colors |
|
|
444
|
+
| counter | 100–350KB | Static background, changing number only |
|
|
445
|
+
| pulse | 200–600KB | Repetitive frames compress well |
|
|
446
|
+
| loop-scroll | 400KB–1.5MB | Many unique positions = more frames |
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
# Style Presets — graphic-gif
|
|
2
|
+
|
|
3
|
+
4 GIF-appropriate style presets. These are a subset of the full 9-preset library — chosen because they use simple, flat palettes that produce smaller GIF files and smoother loops.
|
|
4
|
+
|
|
5
|
+
**Why only 4?** GIF format is limited to 256 colors per frame. Styles with complex gradients, photographic backgrounds, or multiple gradient stops create color-banding artifacts and larger files. The 4 presets below use solid backgrounds and single accent colors.
|
|
6
|
+
|
|
7
|
+
**Rule:** Pick the preset that matches the tone and audience. Read all 4 before choosing.
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## 1. clean-slate
|
|
12
|
+
|
|
13
|
+
**Character:** Professional. Clean. High-trust. No-noise.
|
|
14
|
+
|
|
15
|
+
```css
|
|
16
|
+
:root {
|
|
17
|
+
/* Colors */
|
|
18
|
+
--bg: #FFFFFF;
|
|
19
|
+
--bg-elevated: #F8FAFC;
|
|
20
|
+
--text: #0F172A;
|
|
21
|
+
--text-muted: #64748B;
|
|
22
|
+
--accent: #0F172A;
|
|
23
|
+
--accent-light: #F1F5F9;
|
|
24
|
+
--divider: #E2E8F0;
|
|
25
|
+
|
|
26
|
+
/* Typography */
|
|
27
|
+
--font-display: 'Plus Jakarta Sans', sans-serif;
|
|
28
|
+
--font-body: 'Plus Jakarta Sans', sans-serif;
|
|
29
|
+
|
|
30
|
+
/* Font CDN */
|
|
31
|
+
/* <link href="https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@400;500;600;700;800&display=swap" rel="stylesheet"> */
|
|
32
|
+
}
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
**Design direction:**
|
|
36
|
+
- Background: white. Period. No gradients.
|
|
37
|
+
- Accent: near-black `#0F172A` — used sparingly (underlines, stats, borders)
|
|
38
|
+
- Typography: weight contrast is the design — 800 display vs 400 body
|
|
39
|
+
- Signature element: a thin 2px `--accent` underline or left border on key elements
|
|
40
|
+
|
|
41
|
+
**Use for:** Stat counters, professional quote reveals, LinkedIn-style social content. Any audience that expects polish without flair.
|
|
42
|
+
|
|
43
|
+
**File size:** Small. White bg + black text = ~16 colors per frame.
|
|
44
|
+
|
|
45
|
+
---
|
|
46
|
+
|
|
47
|
+
## 2. terminal
|
|
48
|
+
|
|
49
|
+
**Character:** Developer. Precise. Monochrome matrix. Mechanical.
|
|
50
|
+
|
|
51
|
+
```css
|
|
52
|
+
:root {
|
|
53
|
+
/* Colors */
|
|
54
|
+
--bg: #0D1117;
|
|
55
|
+
--bg-elevated: #161B22;
|
|
56
|
+
--text: #C9D1D9;
|
|
57
|
+
--text-muted: #8B949E;
|
|
58
|
+
--accent: #00FF41;
|
|
59
|
+
--accent-light: #1A2332;
|
|
60
|
+
--divider: #21262D;
|
|
61
|
+
|
|
62
|
+
/* Typography */
|
|
63
|
+
--font-display: 'JetBrains Mono', monospace;
|
|
64
|
+
--font-body: 'JetBrains Mono', monospace;
|
|
65
|
+
|
|
66
|
+
/* Font CDN */
|
|
67
|
+
/* <link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500;700&display=swap" rel="stylesheet"> */
|
|
68
|
+
}
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
**Design direction:**
|
|
72
|
+
- Background: near-black `#0D1117` — GitHub dark mode reference
|
|
73
|
+
- Accent: matrix green `#00FF41` — for cursor, numbers, key terms
|
|
74
|
+
- Typography: monospace everywhere — even display headings
|
|
75
|
+
- Signature element: blinking cursor `|` using `animation: blink step-end infinite`
|
|
76
|
+
- Optional: scan-line overlay (`repeating-linear-gradient` with 1px transparent lines at 2px intervals, opacity 0.03)
|
|
77
|
+
|
|
78
|
+
**Scan-line recipe:**
|
|
79
|
+
```css
|
|
80
|
+
.canvas::after {
|
|
81
|
+
content: '';
|
|
82
|
+
position: absolute;
|
|
83
|
+
inset: 0;
|
|
84
|
+
background: repeating-linear-gradient(
|
|
85
|
+
to bottom,
|
|
86
|
+
transparent,
|
|
87
|
+
transparent 1px,
|
|
88
|
+
rgba(0, 0, 0, 0.03) 1px,
|
|
89
|
+
rgba(0, 0, 0, 0.03) 2px
|
|
90
|
+
);
|
|
91
|
+
pointer-events: none;
|
|
92
|
+
}
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
**Use for:** Typewriter effects (most effective), code reveals, developer stats, startup/tech metrics, anything aimed at builders.
|
|
96
|
+
|
|
97
|
+
**File size:** Very small. Dark bg + single-color text = ~8–12 colors per frame.
|
|
98
|
+
|
|
99
|
+
---
|
|
100
|
+
|
|
101
|
+
## 3. electric-burst
|
|
102
|
+
|
|
103
|
+
**Character:** Bold. Kinetic. Electric. High-contrast impact.
|
|
104
|
+
|
|
105
|
+
```css
|
|
106
|
+
:root {
|
|
107
|
+
/* Colors */
|
|
108
|
+
--bg: #09090B;
|
|
109
|
+
--bg-elevated: #18181B;
|
|
110
|
+
--text: #FAFAFA;
|
|
111
|
+
--text-muted: #A1A1AA;
|
|
112
|
+
--accent: #FACC15;
|
|
113
|
+
--accent-light: #1C1917;
|
|
114
|
+
--divider: #27272A;
|
|
115
|
+
|
|
116
|
+
/* Typography */
|
|
117
|
+
--font-display: 'Space Grotesk', sans-serif;
|
|
118
|
+
--font-body: 'DM Sans', sans-serif;
|
|
119
|
+
|
|
120
|
+
/* Font CDN */
|
|
121
|
+
/* <link href="https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@600;700&family=DM+Sans:wght@400;500&display=swap" rel="stylesheet"> */
|
|
122
|
+
}
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
**Design direction:**
|
|
126
|
+
- Background: near-black `#09090B` — darker than terminal, no green tint
|
|
127
|
+
- Accent: electric yellow `#FACC15` — for numbers, key words, CTA text
|
|
128
|
+
- Typography: `Space Grotesk` is chunky and geometric at large sizes
|
|
129
|
+
- Signature element: large accent-colored number or stat as the hero element, with `--text-muted` label below
|
|
130
|
+
|
|
131
|
+
**Accent usage rule:** Yellow only on the most important element per canvas (one stat, one CTA word, one key number). Everything else in white `#FAFAFA`. Yellow everywhere = no yellow.
|
|
132
|
+
|
|
133
|
+
**Use for:** Bold stat reveals, CTA buttons (pulse animation), product metric GIFs, high-energy social content.
|
|
134
|
+
|
|
135
|
+
**File size:** Small-medium. Dark bg + white text + 1 yellow element = ~20–30 colors per frame.
|
|
136
|
+
|
|
137
|
+
---
|
|
138
|
+
|
|
139
|
+
## 4. brutalist
|
|
140
|
+
|
|
141
|
+
**Character:** Raw. Confrontational. Typographic. Zero decoration.
|
|
142
|
+
|
|
143
|
+
```css
|
|
144
|
+
:root {
|
|
145
|
+
/* Colors */
|
|
146
|
+
--bg: #FFFFFF;
|
|
147
|
+
--bg-elevated: #F5F5F5;
|
|
148
|
+
--text: #000000;
|
|
149
|
+
--text-muted: #666666;
|
|
150
|
+
--accent: #FF0000;
|
|
151
|
+
--accent-light: #FFF5F5;
|
|
152
|
+
--divider: #000000;
|
|
153
|
+
|
|
154
|
+
/* Typography */
|
|
155
|
+
--font-display: 'Space Mono', monospace;
|
|
156
|
+
--font-body: 'Space Mono', monospace;
|
|
157
|
+
|
|
158
|
+
/* Font CDN */
|
|
159
|
+
/* <link href="https://fonts.googleapis.com/css2?family=Space+Mono:wght@400;700&display=swap" rel="stylesheet"> */
|
|
160
|
+
}
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
**Design direction:**
|
|
164
|
+
- Background: white. Nothing soft.
|
|
165
|
+
- Accent: pure red `#FF0000` — used for borders, tickers, 1–2 key words
|
|
166
|
+
- Typography: monospace everywhere — no humanist curves, no refinement
|
|
167
|
+
- Signature element: a thick `4px solid #000000` border or underline. Or a red separator.
|
|
168
|
+
- Layout: asymmetric, left-heavy. Text at extreme sizes. Oversized numbers.
|
|
169
|
+
|
|
170
|
+
**For loop-scroll tickers:** Red `--accent` separator dots (`·`) between items. Track background `--bg` white. Item font `Space Mono` 700.
|
|
171
|
+
|
|
172
|
+
**For slide-in:** Slide from left with `translateX(-100vw)` overshoot. Hard cubic-bezier: `cubic-bezier(0.87, 0, 0.13, 1)` (no spring — mechanical snap).
|
|
173
|
+
|
|
174
|
+
**Use for:** Loop-scroll tickers, bold feature lists, confrontational stat reveals, design-forward agencies, brands that want to stand out.
|
|
175
|
+
|
|
176
|
+
**File size:** Very small. White + black + occasional red = ~8–16 colors per frame.
|
|
177
|
+
|
|
178
|
+
---
|
|
179
|
+
|
|
180
|
+
## Styles to AVOID for GIFs
|
|
181
|
+
|
|
182
|
+
These styles from the full 9-preset library produce poor GIF output:
|
|
183
|
+
|
|
184
|
+
| Style | Problem |
|
|
185
|
+
|---|---|
|
|
186
|
+
| midnight-editorial | Complex gradient overlays = hundreds of colors = banding + large files |
|
|
187
|
+
| matt-gray | Multiple grey shades with subtle transitions = poor quantization |
|
|
188
|
+
| product-minimal | Gradient accents and backgrounds = too many colors |
|
|
189
|
+
| mint-pixel-corporate | Mint gradient + purple tones = complex palette |
|
|
190
|
+
| warm-earth | Warm texture/noise patterns = photographic complexity |
|
|
191
|
+
| magazine-red | Multiple brand colors + photographic intent = GIF banding |
|
|
192
|
+
| soft-cloud | Pastel gradients = smooth color transitions that GIF destroys |
|
|
193
|
+
|
|
194
|
+
**The rule:** If the style uses any `linear-gradient` as a background (not just borders), avoid it for GIFs. Flat solid colors only.
|