@whykusanagi/corrupted-theme 0.1.1 → 0.1.3
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/CHANGELOG.md +253 -0
- package/README.md +97 -7
- package/docs/CAPABILITIES.md +209 -0
- package/docs/CHARACTER_LEVEL_CORRUPTION.md +264 -0
- package/docs/COMPONENTS_REFERENCE.md +295 -8
- package/docs/CORRUPTION_PHRASES.md +529 -0
- package/docs/FUTURE_WORK.md +189 -0
- package/docs/IMPLEMENTATION_VALIDATION.md +401 -0
- package/docs/LLM_PROVIDERS.md +345 -0
- package/docs/PERSONALITY.md +128 -0
- package/docs/ROADMAP.md +266 -0
- package/docs/ROUTING.md +324 -0
- package/docs/STYLE_GUIDE.md +605 -0
- package/docs/brand/BRAND_OVERVIEW.md +413 -0
- package/docs/brand/COLOR_SYSTEM.md +583 -0
- package/docs/brand/DESIGN_TOKENS.md +1009 -0
- package/docs/brand/TRANSLATION_FAILURE_AESTHETIC.md +525 -0
- package/docs/brand/TYPOGRAPHY.md +624 -0
- package/docs/components/ANIMATION_GUIDELINES.md +901 -0
- package/docs/components/COMPONENT_LIBRARY.md +1061 -0
- package/docs/components/GLASSMORPHISM.md +602 -0
- package/docs/components/INTERACTIVE_STATES.md +766 -0
- package/docs/governance/CONTRIBUTION_GUIDELINES.md +593 -0
- package/docs/governance/DESIGN_SYSTEM_GOVERNANCE.md +451 -0
- package/docs/governance/VERSION_MANAGEMENT.md +447 -0
- package/docs/governance/VERSION_REFERENCES.md +229 -0
- package/docs/platforms/CLI_IMPLEMENTATION.md +1025 -0
- package/docs/platforms/COMPONENT_MAPPING.md +579 -0
- package/docs/platforms/NPM_PACKAGE.md +854 -0
- package/docs/platforms/WEB_IMPLEMENTATION.md +1221 -0
- package/docs/standards/ACCESSIBILITY.md +715 -0
- package/docs/standards/ANTI_PATTERNS.md +554 -0
- package/docs/standards/SPACING_SYSTEM.md +549 -0
- package/examples/assets/celeste-avatar.png +0 -0
- package/examples/button.html +22 -10
- package/examples/card.html +22 -9
- package/examples/extensions-showcase.html +716 -0
- package/examples/form.html +22 -9
- package/examples/index.html +619 -396
- package/examples/layout.html +22 -8
- package/examples/nikke-team-builder.html +23 -9
- package/examples/showcase-complete.html +884 -28
- package/examples/showcase.html +21 -8
- package/package.json +14 -5
- package/src/css/components.css +676 -0
- package/src/css/extensions.css +933 -0
- package/src/css/theme.css +6 -74
- package/src/css/typography.css +5 -0
- package/src/lib/character-corruption.js +563 -0
- package/src/lib/components.js +283 -0
- package/src/lib/countdown-widget.js +609 -0
- package/src/lib/gallery.js +481 -0
|
@@ -0,0 +1,901 @@
|
|
|
1
|
+
# Animation Guidelines
|
|
2
|
+
|
|
3
|
+
> **Celeste Brand System** | Component Documentation
|
|
4
|
+
> **Document**: Animation Guidelines
|
|
5
|
+
> **Version**: 1.0.0
|
|
6
|
+
> **Last Updated**: 2025-12-13
|
|
7
|
+
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
## Table of Contents
|
|
11
|
+
|
|
12
|
+
1. [Overview](#overview)
|
|
13
|
+
2. [Core Principles](#core-principles)
|
|
14
|
+
3. [Timing System](#timing-system)
|
|
15
|
+
4. [Easing Functions](#easing-functions)
|
|
16
|
+
5. [Animation Types](#animation-types)
|
|
17
|
+
6. [Corruption Animations](#corruption-animations)
|
|
18
|
+
7. [CLI Terminal Animations](#cli-terminal-animations)
|
|
19
|
+
8. [Performance Guidelines](#performance-guidelines)
|
|
20
|
+
9. [Accessibility](#accessibility)
|
|
21
|
+
10. [Implementation Examples](#implementation-examples)
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
## Overview
|
|
26
|
+
|
|
27
|
+
Animation brings the Celeste brand to life while maintaining the **translation-failure corruption aesthetic**. All animations must balance premium polish with glitching imperfection, creating an authentic corrupted AI experience.
|
|
28
|
+
|
|
29
|
+
### Animation Philosophy
|
|
30
|
+
|
|
31
|
+
- **Purposeful**: Every animation serves a functional or branding purpose
|
|
32
|
+
- **Performant**: 60fps minimum, GPU-accelerated when possible
|
|
33
|
+
- **Accessible**: Respects `prefers-reduced-motion` user preference
|
|
34
|
+
- **Corrupted**: Subtle glitches enhance brand identity without disrupting UX
|
|
35
|
+
- **Consistent**: Same timing/easing across platforms (web + CLI)
|
|
36
|
+
|
|
37
|
+
---
|
|
38
|
+
|
|
39
|
+
## Core Principles
|
|
40
|
+
|
|
41
|
+
### 1. Speed Hierarchy
|
|
42
|
+
|
|
43
|
+
Animations must feel **fast but not rushed**, with three distinct speed tiers:
|
|
44
|
+
|
|
45
|
+
| Tier | Duration | Use Case | Example |
|
|
46
|
+
|------|----------|----------|---------|
|
|
47
|
+
| **Micro** | 150ms | Immediate feedback | Button hover, focus ring appearance |
|
|
48
|
+
| **Standard** | 300ms | Primary transitions | Card expansion, modal open/close |
|
|
49
|
+
| **Slow** | 500ms | Complex changes | Page transitions, layout shifts |
|
|
50
|
+
|
|
51
|
+
### 2. Natural Motion
|
|
52
|
+
|
|
53
|
+
- **Ease-in-out** is the default easing (feels natural)
|
|
54
|
+
- **Avoid linear** easing (feels robotic except for loaders)
|
|
55
|
+
- **Custom curves** for brand-specific animations (corruption glitch, bounce)
|
|
56
|
+
|
|
57
|
+
### 3. Layered Timing
|
|
58
|
+
|
|
59
|
+
When animating multiple properties, **stagger timing** to create depth:
|
|
60
|
+
|
|
61
|
+
```css
|
|
62
|
+
/* Good: Staggered properties feel polished */
|
|
63
|
+
.card {
|
|
64
|
+
transition:
|
|
65
|
+
transform 0.3s cubic-bezier(0.34, 1.56, 0.64, 1), /* Bounce first */
|
|
66
|
+
box-shadow 0.3s ease-out 0.05s, /* Shadow follows */
|
|
67
|
+
border-color 0.3s ease-out 0.1s; /* Border last */
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/* Bad: Simultaneous properties feel flat */
|
|
71
|
+
.card {
|
|
72
|
+
transition: all 0.3s ease; /* Don't use 'all' */
|
|
73
|
+
}
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
### 4. Directional Consistency
|
|
77
|
+
|
|
78
|
+
- **Entrances**: Fade in + slide from direction of origin (300ms)
|
|
79
|
+
- **Exits**: Fade out + slide toward destination (200ms, 33% faster)
|
|
80
|
+
- **Exits are faster** than entrances (user already made decision)
|
|
81
|
+
|
|
82
|
+
---
|
|
83
|
+
|
|
84
|
+
## Timing System
|
|
85
|
+
|
|
86
|
+
### Duration Scale
|
|
87
|
+
|
|
88
|
+
All durations are based on **multiples of 50ms** for consistency:
|
|
89
|
+
|
|
90
|
+
```css
|
|
91
|
+
/* CSS Custom Properties */
|
|
92
|
+
:root {
|
|
93
|
+
/* Micro-interactions */
|
|
94
|
+
--duration-instant: 100ms; /* Hover color changes */
|
|
95
|
+
--duration-fast: 150ms; /* Focus indicators, tooltips */
|
|
96
|
+
--duration-quick: 200ms; /* Dropdown open, badge appear */
|
|
97
|
+
|
|
98
|
+
/* Standard transitions */
|
|
99
|
+
--duration-normal: 300ms; /* Modal open, card expand */
|
|
100
|
+
--duration-moderate: 400ms; /* Sidebar slide, toast enter */
|
|
101
|
+
|
|
102
|
+
/* Complex transitions */
|
|
103
|
+
--duration-slow: 500ms; /* Page transitions */
|
|
104
|
+
--duration-slower: 700ms; /* Full layout shifts */
|
|
105
|
+
--duration-slowest: 1000ms; /* Loading screens only */
|
|
106
|
+
}
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
### When to Use Each Duration
|
|
110
|
+
|
|
111
|
+
#### 100ms (Instant)
|
|
112
|
+
- Color changes on hover
|
|
113
|
+
- Background tint changes
|
|
114
|
+
- Icon color shifts
|
|
115
|
+
|
|
116
|
+
#### 150ms (Fast) ⭐ Default for micro-interactions
|
|
117
|
+
- Button hover states
|
|
118
|
+
- Focus ring appearance
|
|
119
|
+
- Small scale changes (1.0 → 1.05)
|
|
120
|
+
- Border color changes
|
|
121
|
+
|
|
122
|
+
#### 200ms (Quick)
|
|
123
|
+
- Dropdown menu open
|
|
124
|
+
- Tooltip appearance
|
|
125
|
+
- Badge/chip animations
|
|
126
|
+
- Small element exits
|
|
127
|
+
|
|
128
|
+
#### 300ms (Normal) ⭐ Default for standard transitions
|
|
129
|
+
- Modal/dialog open
|
|
130
|
+
- Card expansion
|
|
131
|
+
- Accordion open/close
|
|
132
|
+
- Tab switching
|
|
133
|
+
- Form field validation feedback
|
|
134
|
+
|
|
135
|
+
#### 400ms (Moderate)
|
|
136
|
+
- Sidebar slide in/out
|
|
137
|
+
- Toast notification enter
|
|
138
|
+
- Complex hover effects (multiple properties)
|
|
139
|
+
- Menu navigation transitions
|
|
140
|
+
|
|
141
|
+
#### 500ms (Slow)
|
|
142
|
+
- Page route transitions
|
|
143
|
+
- Full-screen overlay enter
|
|
144
|
+
- Large layout shifts
|
|
145
|
+
- Dashboard section transitions
|
|
146
|
+
|
|
147
|
+
#### 700ms+ (Slower)
|
|
148
|
+
- **Use sparingly** - Reserved for:
|
|
149
|
+
- Initial page load animations
|
|
150
|
+
- Loading screen transitions
|
|
151
|
+
- Full application state changes
|
|
152
|
+
|
|
153
|
+
---
|
|
154
|
+
|
|
155
|
+
## Easing Functions
|
|
156
|
+
|
|
157
|
+
### Standard Easing Curves
|
|
158
|
+
|
|
159
|
+
```css
|
|
160
|
+
:root {
|
|
161
|
+
/* Default easing (natural motion) */
|
|
162
|
+
--ease-default: ease; /* Browser default */
|
|
163
|
+
--ease-in-out: cubic-bezier(0.42, 0, 0.58, 1); /* Symmetric */
|
|
164
|
+
|
|
165
|
+
/* Directional easing */
|
|
166
|
+
--ease-in: cubic-bezier(0.42, 0, 1, 1); /* Starts slow */
|
|
167
|
+
--ease-out: cubic-bezier(0, 0, 0.58, 1); /* Ends slow */
|
|
168
|
+
|
|
169
|
+
/* Custom easing (brand-specific) */
|
|
170
|
+
--ease-bounce: cubic-bezier(0.34, 1.56, 0.64, 1); /* Overshoot bounce */
|
|
171
|
+
--ease-snappy: cubic-bezier(0.16, 1, 0.3, 1); /* Fast start, smooth end */
|
|
172
|
+
--ease-glitch: cubic-bezier(0.68, -0.55, 0.27, 1.55); /* Corrupted motion */
|
|
173
|
+
--ease-smooth: cubic-bezier(0.25, 0.1, 0.25, 1); /* Material Design */
|
|
174
|
+
}
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
### Easing Selection Guide
|
|
178
|
+
|
|
179
|
+
| Easing | Use Case | Visual Effect |
|
|
180
|
+
|--------|----------|---------------|
|
|
181
|
+
| `ease` | **Default** - Most transitions | Natural acceleration/deceleration |
|
|
182
|
+
| `ease-in-out` | Symmetric motion | Smooth start and end |
|
|
183
|
+
| `ease-out` | **Recommended for entrances** | Elements settle into place |
|
|
184
|
+
| `ease-in` | **Recommended for exits** | Elements accelerate away |
|
|
185
|
+
| `cubic-bezier(0.34, 1.56, 0.64, 1)` | **Bounce effect** (brand signature) | Playful overshoot (buttons, cards) |
|
|
186
|
+
| `cubic-bezier(0.68, -0.55, 0.27, 1.55)` | **Glitch effect** (corruption) | Erratic, corrupted motion |
|
|
187
|
+
| `linear` | Loading spinners, progress bars | Constant speed (no acceleration) |
|
|
188
|
+
|
|
189
|
+
### When to Use Custom Easing
|
|
190
|
+
|
|
191
|
+
**Bounce (`--ease-bounce`)** - Signature Celeste animation:
|
|
192
|
+
- Button clicks (scale 1.0 → 0.98 → 1.0)
|
|
193
|
+
- Card hover interactions
|
|
194
|
+
- Badge/chip appearances
|
|
195
|
+
- Success state confirmations
|
|
196
|
+
|
|
197
|
+
**Glitch (`--ease-glitch`)** - Corruption aesthetic:
|
|
198
|
+
- Text scramble/corruption effects
|
|
199
|
+
- Error state transitions
|
|
200
|
+
- Eye flicker animations
|
|
201
|
+
- Translation-failure text changes
|
|
202
|
+
|
|
203
|
+
---
|
|
204
|
+
|
|
205
|
+
## Animation Types
|
|
206
|
+
|
|
207
|
+
### 1. Transitions (Default)
|
|
208
|
+
|
|
209
|
+
Use CSS `transition` for simple state changes:
|
|
210
|
+
|
|
211
|
+
```css
|
|
212
|
+
.btn {
|
|
213
|
+
/* Properties to animate | duration | easing | delay */
|
|
214
|
+
transition:
|
|
215
|
+
background-color 0.15s ease-out,
|
|
216
|
+
border-color 0.15s ease-out,
|
|
217
|
+
transform 0.15s cubic-bezier(0.34, 1.56, 0.64, 1),
|
|
218
|
+
box-shadow 0.2s ease-out 0.05s; /* Delayed shadow */
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
.btn:hover {
|
|
222
|
+
background-color: var(--color-accent-light);
|
|
223
|
+
border-color: rgba(217, 79, 144, 0.5);
|
|
224
|
+
transform: scale(1.05);
|
|
225
|
+
box-shadow: 0 0 20px rgba(217, 79, 144, 0.4);
|
|
226
|
+
}
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
### 2. Keyframe Animations
|
|
230
|
+
|
|
231
|
+
Use `@keyframes` for complex, looping, or multi-step animations:
|
|
232
|
+
|
|
233
|
+
```css
|
|
234
|
+
/* Spinner (loading indicator) */
|
|
235
|
+
@keyframes spin {
|
|
236
|
+
from { transform: rotate(0deg); }
|
|
237
|
+
to { transform: rotate(360deg); }
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
.spinner {
|
|
241
|
+
animation: spin 1s linear infinite; /* Linear for constant rotation */
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
/* Pulse (attention-grabbing) */
|
|
245
|
+
@keyframes pulse {
|
|
246
|
+
0%, 100% { opacity: 1; }
|
|
247
|
+
50% { opacity: 0.5; }
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
.loading-badge {
|
|
251
|
+
animation: pulse 1.5s ease-in-out infinite;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
/* Bounce in (entrance animation) */
|
|
255
|
+
@keyframes bounceIn {
|
|
256
|
+
0% {
|
|
257
|
+
opacity: 0;
|
|
258
|
+
transform: scale(0.3) translateY(-20px);
|
|
259
|
+
}
|
|
260
|
+
50% {
|
|
261
|
+
transform: scale(1.05); /* Overshoot */
|
|
262
|
+
}
|
|
263
|
+
70% {
|
|
264
|
+
transform: scale(0.95); /* Settle */
|
|
265
|
+
}
|
|
266
|
+
100% {
|
|
267
|
+
opacity: 1;
|
|
268
|
+
transform: scale(1) translateY(0);
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
.card-enter {
|
|
273
|
+
animation: bounceIn 0.5s cubic-bezier(0.34, 1.56, 0.64, 1);
|
|
274
|
+
}
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
### 3. Transform Animations
|
|
278
|
+
|
|
279
|
+
**CRITICAL**: Only animate GPU-accelerated properties for 60fps performance:
|
|
280
|
+
|
|
281
|
+
✅ **Animate these** (GPU-accelerated):
|
|
282
|
+
- `transform: translate()` - Move elements
|
|
283
|
+
- `transform: scale()` - Size changes
|
|
284
|
+
- `transform: rotate()` - Rotation
|
|
285
|
+
- `opacity` - Fade in/out
|
|
286
|
+
|
|
287
|
+
❌ **Never animate these** (CPU-bound, causes reflow):
|
|
288
|
+
- `width`, `height` - Use `scale()` instead
|
|
289
|
+
- `top`, `left`, `margin` - Use `translate()` instead
|
|
290
|
+
- `padding` - Pre-render different states
|
|
291
|
+
- `background-position` - Use `transform` on pseudo-element
|
|
292
|
+
|
|
293
|
+
```css
|
|
294
|
+
/* Good: GPU-accelerated */
|
|
295
|
+
.card {
|
|
296
|
+
transition: transform 0.3s ease-out;
|
|
297
|
+
}
|
|
298
|
+
.card:hover {
|
|
299
|
+
transform: scale(1.02) translateY(-4px);
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
/* Bad: Causes reflow */
|
|
303
|
+
.card {
|
|
304
|
+
transition: width 0.3s ease-out, margin-top 0.3s ease-out;
|
|
305
|
+
}
|
|
306
|
+
.card:hover {
|
|
307
|
+
width: 320px;
|
|
308
|
+
margin-top: -4px;
|
|
309
|
+
}
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
---
|
|
313
|
+
|
|
314
|
+
## Corruption Animations
|
|
315
|
+
|
|
316
|
+
### Philosophy
|
|
317
|
+
|
|
318
|
+
Corruption animations are the **signature Celeste brand element**. They must:
|
|
319
|
+
- Be **subtle** (25-40% intensity) to maintain readability
|
|
320
|
+
- **Enhance, not disrupt** the user experience
|
|
321
|
+
- Follow the **translation-failure aesthetic** (character-level mixing, NOT leet speak)
|
|
322
|
+
|
|
323
|
+
### Intensity Guidelines
|
|
324
|
+
|
|
325
|
+
| Intensity | Corruption % | Use Case |
|
|
326
|
+
|-----------|--------------|----------|
|
|
327
|
+
| **Minimal** | 15-20% | Body text, critical information |
|
|
328
|
+
| **Low** | 25-30% | Headers, non-critical labels |
|
|
329
|
+
| **Medium** | 35-40% | Decorative text, brand elements |
|
|
330
|
+
| **High** | 45-50% | Easter eggs, loading screens |
|
|
331
|
+
| **Extreme** | 50%+ | ⚠️ **Never use** - unreadable |
|
|
332
|
+
|
|
333
|
+
### Text Corruption Animation
|
|
334
|
+
|
|
335
|
+
Character-level corruption with Japanese character mixing:
|
|
336
|
+
|
|
337
|
+
```css
|
|
338
|
+
@keyframes textCorrupt {
|
|
339
|
+
0%, 100% {
|
|
340
|
+
opacity: 1;
|
|
341
|
+
}
|
|
342
|
+
10% {
|
|
343
|
+
opacity: 0.8;
|
|
344
|
+
transform: translateX(1px);
|
|
345
|
+
}
|
|
346
|
+
20% {
|
|
347
|
+
opacity: 1;
|
|
348
|
+
transform: translateX(-1px);
|
|
349
|
+
}
|
|
350
|
+
30% {
|
|
351
|
+
opacity: 0.9;
|
|
352
|
+
transform: translateX(0);
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
.corrupted-text {
|
|
357
|
+
animation: textCorrupt 3s ease-in-out infinite;
|
|
358
|
+
/* JavaScript handles character replacement */
|
|
359
|
+
}
|
|
360
|
+
```
|
|
361
|
+
|
|
362
|
+
**JavaScript Implementation** (character-level mixing):
|
|
363
|
+
|
|
364
|
+
```javascript
|
|
365
|
+
function corruptText(text, intensity = 0.3) {
|
|
366
|
+
const japaneseChars = ['ア', 'イ', 'ウ', '使', '統', '計', 'ー', 'ル'];
|
|
367
|
+
|
|
368
|
+
return text.split('').map((char, i) => {
|
|
369
|
+
if (Math.random() < intensity && /[a-zA-Z]/.test(char)) {
|
|
370
|
+
return japaneseChars[Math.floor(Math.random() * japaneseChars.length)];
|
|
371
|
+
}
|
|
372
|
+
return char;
|
|
373
|
+
}).join('');
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
// Example: "USAGE ANALYTICS" → "US使AGE ANア統LYTICS" (30% corruption)
|
|
377
|
+
```
|
|
378
|
+
|
|
379
|
+
### Flicker Animation (Eyes)
|
|
380
|
+
|
|
381
|
+
The signature "eye flicker" animation from CLI dashboard:
|
|
382
|
+
|
|
383
|
+
```css
|
|
384
|
+
@keyframes flicker {
|
|
385
|
+
0%, 100% { opacity: 1; }
|
|
386
|
+
45% { opacity: 1; }
|
|
387
|
+
50% { opacity: 0.3; } /* Brief blink */
|
|
388
|
+
55% { opacity: 1; }
|
|
389
|
+
60% { opacity: 0.5; } /* Double blink */
|
|
390
|
+
65% { opacity: 1; }
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
.eye-icon {
|
|
394
|
+
animation: flicker 4s ease-in-out infinite;
|
|
395
|
+
animation-delay: calc(var(--flicker-seed) * 1s); /* Randomize timing */
|
|
396
|
+
}
|
|
397
|
+
```
|
|
398
|
+
|
|
399
|
+
### Glitch Effect (Advanced)
|
|
400
|
+
|
|
401
|
+
Multi-layer glitch effect for error states or brand moments:
|
|
402
|
+
|
|
403
|
+
```css
|
|
404
|
+
@keyframes glitch {
|
|
405
|
+
0% {
|
|
406
|
+
transform: translate(0);
|
|
407
|
+
opacity: 1;
|
|
408
|
+
}
|
|
409
|
+
20% {
|
|
410
|
+
transform: translate(-2px, 2px);
|
|
411
|
+
opacity: 0.8;
|
|
412
|
+
}
|
|
413
|
+
40% {
|
|
414
|
+
transform: translate(2px, -2px);
|
|
415
|
+
opacity: 0.9;
|
|
416
|
+
}
|
|
417
|
+
60% {
|
|
418
|
+
transform: translate(-1px, -1px);
|
|
419
|
+
opacity: 0.85;
|
|
420
|
+
}
|
|
421
|
+
80% {
|
|
422
|
+
transform: translate(1px, 1px);
|
|
423
|
+
opacity: 0.95;
|
|
424
|
+
}
|
|
425
|
+
100% {
|
|
426
|
+
transform: translate(0);
|
|
427
|
+
opacity: 1;
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
.glitch-text {
|
|
432
|
+
position: relative;
|
|
433
|
+
animation: glitch 0.3s cubic-bezier(0.68, -0.55, 0.27, 1.55);
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
/* Optional: RGB split effect */
|
|
437
|
+
.glitch-text::before,
|
|
438
|
+
.glitch-text::after {
|
|
439
|
+
content: attr(data-text);
|
|
440
|
+
position: absolute;
|
|
441
|
+
top: 0;
|
|
442
|
+
left: 0;
|
|
443
|
+
opacity: 0.8;
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
.glitch-text::before {
|
|
447
|
+
animation: glitch 0.3s cubic-bezier(0.68, -0.55, 0.27, 1.55);
|
|
448
|
+
color: #d94f90; /* Pink channel */
|
|
449
|
+
z-index: -1;
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
.glitch-text::after {
|
|
453
|
+
animation: glitch 0.3s cubic-bezier(0.68, -0.55, 0.27, 1.55) 0.05s;
|
|
454
|
+
color: #8b5cf6; /* Purple channel */
|
|
455
|
+
z-index: -2;
|
|
456
|
+
}
|
|
457
|
+
```
|
|
458
|
+
|
|
459
|
+
### Loading Spinner (Corrupted)
|
|
460
|
+
|
|
461
|
+
```css
|
|
462
|
+
@keyframes spinCorrupt {
|
|
463
|
+
0% { transform: rotate(0deg); }
|
|
464
|
+
25% { transform: rotate(90deg) scale(1.1); } /* Stutter */
|
|
465
|
+
26% { transform: rotate(85deg) scale(1.0); } /* Correction */
|
|
466
|
+
50% { transform: rotate(180deg); }
|
|
467
|
+
75% { transform: rotate(270deg) scale(0.9); } /* Stutter */
|
|
468
|
+
76% { transform: rotate(275deg) scale(1.0); } /* Correction */
|
|
469
|
+
100% { transform: rotate(360deg); }
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
.spinner-corrupted {
|
|
473
|
+
animation: spinCorrupt 1.2s cubic-bezier(0.68, -0.55, 0.27, 1.55) infinite;
|
|
474
|
+
}
|
|
475
|
+
```
|
|
476
|
+
|
|
477
|
+
---
|
|
478
|
+
|
|
479
|
+
## CLI Terminal Animations
|
|
480
|
+
|
|
481
|
+
### Terminal Constraints
|
|
482
|
+
|
|
483
|
+
- **Refresh Rate**: Most terminals refresh at 60Hz (16.67ms per frame)
|
|
484
|
+
- **Character-Based**: Can't use sub-character positioning
|
|
485
|
+
- **Color Limitations**: 256-color palette (not full RGB)
|
|
486
|
+
- **No Blur/Shadow**: CSS effects unavailable
|
|
487
|
+
|
|
488
|
+
### Animation Techniques
|
|
489
|
+
|
|
490
|
+
#### 1. Frame-Based Animation (Spinner)
|
|
491
|
+
|
|
492
|
+
```go
|
|
493
|
+
// Rotating spinner frames
|
|
494
|
+
var spinnerFrames = []string{"⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"}
|
|
495
|
+
|
|
496
|
+
func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
|
497
|
+
switch msg := msg.(type) {
|
|
498
|
+
case tickMsg:
|
|
499
|
+
m.spinnerFrame = (m.spinnerFrame + 1) % len(spinnerFrames)
|
|
500
|
+
return m, tick(150 * time.Millisecond) // 150ms per frame
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
```
|
|
504
|
+
|
|
505
|
+
#### 2. Progress Bar Animation
|
|
506
|
+
|
|
507
|
+
```go
|
|
508
|
+
// Block character progress (smooth with unicode)
|
|
509
|
+
var progressChars = []string{"█", "▓", "▒", "░"}
|
|
510
|
+
|
|
511
|
+
func renderProgress(percent float64) string {
|
|
512
|
+
filled := int(percent * 40) // 40 char width
|
|
513
|
+
bar := strings.Repeat("█", filled)
|
|
514
|
+
bar += strings.Repeat("░", 40-filled)
|
|
515
|
+
return bar
|
|
516
|
+
}
|
|
517
|
+
```
|
|
518
|
+
|
|
519
|
+
#### 3. Text Fade (ANSI Color Gradients)
|
|
520
|
+
|
|
521
|
+
```go
|
|
522
|
+
// Fade text by reducing color intensity
|
|
523
|
+
var fadeColors = []string{
|
|
524
|
+
"\033[38;5;205m", // Full pink (100%)
|
|
525
|
+
"\033[38;5;204m", // Lighter (80%)
|
|
526
|
+
"\033[38;5;203m", // Lighter (60%)
|
|
527
|
+
"\033[38;5;202m", // Lighter (40%)
|
|
528
|
+
"\033[38;5;201m", // Lighter (20%)
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
func fadeText(text string, frame int) string {
|
|
532
|
+
color := fadeColors[frame % len(fadeColors)]
|
|
533
|
+
return color + text + "\033[0m"
|
|
534
|
+
}
|
|
535
|
+
```
|
|
536
|
+
|
|
537
|
+
#### 4. Corruption Intensity Animation
|
|
538
|
+
|
|
539
|
+
```go
|
|
540
|
+
// Gradually increase corruption intensity
|
|
541
|
+
func animateCorruption(text string, tick int) string {
|
|
542
|
+
intensity := 0.15 + (float64(tick % 100) / 100.0 * 0.25) // 15-40%
|
|
543
|
+
return CorruptTextJapanese(text, intensity)
|
|
544
|
+
}
|
|
545
|
+
```
|
|
546
|
+
|
|
547
|
+
### CLI Animation Timing
|
|
548
|
+
|
|
549
|
+
Match web timing where possible for cross-platform consistency:
|
|
550
|
+
|
|
551
|
+
| Animation | Web | CLI | Implementation |
|
|
552
|
+
|-----------|-----|-----|----------------|
|
|
553
|
+
| **Spinner** | 150ms/frame | 150ms/frame | Frame-based rotation |
|
|
554
|
+
| **Progress bar** | 300ms/update | 300ms/update | Block character fill |
|
|
555
|
+
| **Text fade** | 150ms transition | 150ms/step | ANSI color change |
|
|
556
|
+
| **Corruption flicker** | 3s cycle | 3s cycle | Character replacement |
|
|
557
|
+
|
|
558
|
+
---
|
|
559
|
+
|
|
560
|
+
## Performance Guidelines
|
|
561
|
+
|
|
562
|
+
### Rule 1: Animate Only GPU Properties
|
|
563
|
+
|
|
564
|
+
✅ **Safe** (60fps on all devices):
|
|
565
|
+
- `transform` (translate, rotate, scale)
|
|
566
|
+
- `opacity`
|
|
567
|
+
|
|
568
|
+
⚠️ **Caution** (may drop frames on mobile):
|
|
569
|
+
- `backdrop-filter` (blur) - use sparingly
|
|
570
|
+
- `box-shadow` - pre-render or use pseudo-elements
|
|
571
|
+
|
|
572
|
+
❌ **Never** (causes jank):
|
|
573
|
+
- `width`, `height`, `top`, `left`, `margin`, `padding`
|
|
574
|
+
- `background-position` (use transform on ::before instead)
|
|
575
|
+
|
|
576
|
+
### Rule 2: Limit Concurrent Animations
|
|
577
|
+
|
|
578
|
+
- **Maximum 3-5 animated elements** visible at once
|
|
579
|
+
- **Pause animations** for off-screen elements (`Intersection Observer`)
|
|
580
|
+
- **Reduce motion** on low-powered devices
|
|
581
|
+
|
|
582
|
+
```javascript
|
|
583
|
+
// Pause animations for off-screen elements
|
|
584
|
+
const observer = new IntersectionObserver((entries) => {
|
|
585
|
+
entries.forEach(entry => {
|
|
586
|
+
if (entry.isIntersecting) {
|
|
587
|
+
entry.target.style.animationPlayState = 'running';
|
|
588
|
+
} else {
|
|
589
|
+
entry.target.style.animationPlayState = 'paused';
|
|
590
|
+
}
|
|
591
|
+
});
|
|
592
|
+
});
|
|
593
|
+
|
|
594
|
+
document.querySelectorAll('.animated').forEach(el => observer.observe(el));
|
|
595
|
+
```
|
|
596
|
+
|
|
597
|
+
### Rule 3: Use `will-change` Sparingly
|
|
598
|
+
|
|
599
|
+
```css
|
|
600
|
+
/* Good: Apply will-change only during animation */
|
|
601
|
+
.card:hover {
|
|
602
|
+
will-change: transform; /* Browser optimizes during hover */
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
.card {
|
|
606
|
+
will-change: auto; /* Remove optimization when done */
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
/* Bad: Always on (wastes memory) */
|
|
610
|
+
.card {
|
|
611
|
+
will-change: transform, opacity, box-shadow; /* Too many properties */
|
|
612
|
+
}
|
|
613
|
+
```
|
|
614
|
+
|
|
615
|
+
---
|
|
616
|
+
|
|
617
|
+
## Accessibility
|
|
618
|
+
|
|
619
|
+
### 1. Respect `prefers-reduced-motion`
|
|
620
|
+
|
|
621
|
+
**CRITICAL**: All animations must respect user preference:
|
|
622
|
+
|
|
623
|
+
```css
|
|
624
|
+
/* Default: Full animations */
|
|
625
|
+
.card {
|
|
626
|
+
transition: transform 0.3s cubic-bezier(0.34, 1.56, 0.64, 1);
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
/* Reduced motion: Instant transitions */
|
|
630
|
+
@media (prefers-reduced-motion: reduce) {
|
|
631
|
+
.card {
|
|
632
|
+
transition: transform 0.01s linear; /* Near-instant */
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
/* Disable decorative animations entirely */
|
|
636
|
+
.corrupted-text,
|
|
637
|
+
.flicker,
|
|
638
|
+
.glitch-effect {
|
|
639
|
+
animation: none;
|
|
640
|
+
}
|
|
641
|
+
}
|
|
642
|
+
```
|
|
643
|
+
|
|
644
|
+
### 2. Never Animate Critical Content
|
|
645
|
+
|
|
646
|
+
- **Error messages**: Appear instantly (no delay/animation)
|
|
647
|
+
- **Form validation**: Immediate feedback (<100ms)
|
|
648
|
+
- **Focus indicators**: Instant appearance (accessibility requirement)
|
|
649
|
+
- **Loading states**: Show within 100ms (perceived performance)
|
|
650
|
+
|
|
651
|
+
### 3. Provide Skip Options
|
|
652
|
+
|
|
653
|
+
For long animations (>1s), provide skip button:
|
|
654
|
+
|
|
655
|
+
```html
|
|
656
|
+
<div class="loading-screen">
|
|
657
|
+
<div class="animation">...</div>
|
|
658
|
+
<button class="skip-btn">Skip animation</button>
|
|
659
|
+
</div>
|
|
660
|
+
```
|
|
661
|
+
|
|
662
|
+
---
|
|
663
|
+
|
|
664
|
+
## Implementation Examples
|
|
665
|
+
|
|
666
|
+
### Example 1: Animated Button (Complete)
|
|
667
|
+
|
|
668
|
+
```css
|
|
669
|
+
.btn {
|
|
670
|
+
/* Base styles */
|
|
671
|
+
background: rgba(217, 79, 144, 0.2);
|
|
672
|
+
border: 1px solid rgba(217, 79, 144, 0.3);
|
|
673
|
+
color: #ffffff;
|
|
674
|
+
padding: 0.75rem 1.5rem;
|
|
675
|
+
border-radius: 8px;
|
|
676
|
+
cursor: pointer;
|
|
677
|
+
|
|
678
|
+
/* Animations */
|
|
679
|
+
transition:
|
|
680
|
+
background-color 0.15s ease-out,
|
|
681
|
+
border-color 0.15s ease-out,
|
|
682
|
+
transform 0.15s cubic-bezier(0.34, 1.56, 0.64, 1),
|
|
683
|
+
box-shadow 0.2s ease-out 0.05s;
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
.btn:hover {
|
|
687
|
+
background: rgba(217, 79, 144, 0.3);
|
|
688
|
+
border-color: rgba(217, 79, 144, 0.5);
|
|
689
|
+
transform: scale(1.05);
|
|
690
|
+
box-shadow: 0 0 20px rgba(217, 79, 144, 0.4);
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
.btn:active {
|
|
694
|
+
transform: scale(0.98);
|
|
695
|
+
transition-duration: 0.1s; /* Faster on click */
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
/* Accessibility */
|
|
699
|
+
@media (prefers-reduced-motion: reduce) {
|
|
700
|
+
.btn {
|
|
701
|
+
transition: background-color 0.01s linear, border-color 0.01s linear;
|
|
702
|
+
}
|
|
703
|
+
}
|
|
704
|
+
```
|
|
705
|
+
|
|
706
|
+
### Example 2: Modal Enter/Exit
|
|
707
|
+
|
|
708
|
+
```css
|
|
709
|
+
/* Modal backdrop */
|
|
710
|
+
.modal-backdrop {
|
|
711
|
+
opacity: 0;
|
|
712
|
+
animation: fadeIn 0.3s ease-out forwards;
|
|
713
|
+
}
|
|
714
|
+
|
|
715
|
+
@keyframes fadeIn {
|
|
716
|
+
to { opacity: 1; }
|
|
717
|
+
}
|
|
718
|
+
|
|
719
|
+
/* Modal content */
|
|
720
|
+
.modal {
|
|
721
|
+
opacity: 0;
|
|
722
|
+
transform: scale(0.9) translateY(-20px);
|
|
723
|
+
animation: modalEnter 0.4s cubic-bezier(0.34, 1.56, 0.64, 1) forwards;
|
|
724
|
+
}
|
|
725
|
+
|
|
726
|
+
@keyframes modalEnter {
|
|
727
|
+
to {
|
|
728
|
+
opacity: 1;
|
|
729
|
+
transform: scale(1) translateY(0);
|
|
730
|
+
}
|
|
731
|
+
}
|
|
732
|
+
|
|
733
|
+
/* Exit animation (triggered by JS class) */
|
|
734
|
+
.modal.exiting {
|
|
735
|
+
animation: modalExit 0.2s ease-in forwards;
|
|
736
|
+
}
|
|
737
|
+
|
|
738
|
+
@keyframes modalExit {
|
|
739
|
+
to {
|
|
740
|
+
opacity: 0;
|
|
741
|
+
transform: scale(0.95) translateY(10px);
|
|
742
|
+
}
|
|
743
|
+
}
|
|
744
|
+
```
|
|
745
|
+
|
|
746
|
+
### Example 3: Corrupted Loading Spinner (CLI + Web)
|
|
747
|
+
|
|
748
|
+
**Web**:
|
|
749
|
+
```css
|
|
750
|
+
.spinner-corrupted {
|
|
751
|
+
width: 40px;
|
|
752
|
+
height: 40px;
|
|
753
|
+
border: 3px solid rgba(217, 79, 144, 0.2);
|
|
754
|
+
border-top-color: #d94f90;
|
|
755
|
+
border-radius: 50%;
|
|
756
|
+
animation: spinCorrupt 1.2s cubic-bezier(0.68, -0.55, 0.27, 1.55) infinite;
|
|
757
|
+
}
|
|
758
|
+
|
|
759
|
+
@keyframes spinCorrupt {
|
|
760
|
+
0% { transform: rotate(0deg); }
|
|
761
|
+
25% { transform: rotate(90deg) scale(1.1); }
|
|
762
|
+
26% { transform: rotate(85deg); }
|
|
763
|
+
50% { transform: rotate(180deg); }
|
|
764
|
+
75% { transform: rotate(270deg) scale(0.9); }
|
|
765
|
+
76% { transform: rotate(275deg); }
|
|
766
|
+
100% { transform: rotate(360deg); }
|
|
767
|
+
}
|
|
768
|
+
```
|
|
769
|
+
|
|
770
|
+
**CLI (Go)**:
|
|
771
|
+
```go
|
|
772
|
+
var spinnerFrames = []string{
|
|
773
|
+
"⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏",
|
|
774
|
+
}
|
|
775
|
+
|
|
776
|
+
func (m Model) View() string {
|
|
777
|
+
frame := spinnerFrames[m.tick % len(spinnerFrames)]
|
|
778
|
+
color := lipgloss.Color("#d94f90")
|
|
779
|
+
|
|
780
|
+
// Add random stutter (corruption)
|
|
781
|
+
if m.tick % 4 == 0 && rand.Float64() < 0.15 {
|
|
782
|
+
frame = spinnerFrames[(m.tick-1) % len(spinnerFrames)] // Repeat frame
|
|
783
|
+
}
|
|
784
|
+
|
|
785
|
+
return lipgloss.NewStyle().Foreground(color).Render(frame)
|
|
786
|
+
}
|
|
787
|
+
```
|
|
788
|
+
|
|
789
|
+
---
|
|
790
|
+
|
|
791
|
+
## Anti-Patterns
|
|
792
|
+
|
|
793
|
+
### ❌ Don't: Use `transition: all`
|
|
794
|
+
|
|
795
|
+
```css
|
|
796
|
+
/* Bad: Animates everything (slow, unintentional animations) */
|
|
797
|
+
.card {
|
|
798
|
+
transition: all 0.3s ease;
|
|
799
|
+
}
|
|
800
|
+
|
|
801
|
+
/* Good: Specify properties explicitly */
|
|
802
|
+
.card {
|
|
803
|
+
transition: transform 0.3s ease, opacity 0.3s ease;
|
|
804
|
+
}
|
|
805
|
+
```
|
|
806
|
+
|
|
807
|
+
### ❌ Don't: Animate Layout Properties
|
|
808
|
+
|
|
809
|
+
```css
|
|
810
|
+
/* Bad: Causes reflow */
|
|
811
|
+
.card:hover {
|
|
812
|
+
width: 320px;
|
|
813
|
+
margin-top: -10px;
|
|
814
|
+
}
|
|
815
|
+
|
|
816
|
+
/* Good: Use transform */
|
|
817
|
+
.card:hover {
|
|
818
|
+
transform: scale(1.1) translateY(-10px);
|
|
819
|
+
}
|
|
820
|
+
```
|
|
821
|
+
|
|
822
|
+
### ❌ Don't: Over-Animate
|
|
823
|
+
|
|
824
|
+
```css
|
|
825
|
+
/* Bad: Too many concurrent animations (overwhelming) */
|
|
826
|
+
.card {
|
|
827
|
+
animation: pulse 1s infinite, shake 0.5s infinite, glow 2s infinite;
|
|
828
|
+
}
|
|
829
|
+
|
|
830
|
+
/* Good: One primary animation */
|
|
831
|
+
.card {
|
|
832
|
+
animation: pulse 2s ease-in-out infinite;
|
|
833
|
+
}
|
|
834
|
+
```
|
|
835
|
+
|
|
836
|
+
### ❌ Don't: Ignore Reduced Motion
|
|
837
|
+
|
|
838
|
+
```css
|
|
839
|
+
/* Bad: Forces animation on all users */
|
|
840
|
+
.element {
|
|
841
|
+
animation: spin 1s infinite; /* Can cause nausea */
|
|
842
|
+
}
|
|
843
|
+
|
|
844
|
+
/* Good: Respects user preference */
|
|
845
|
+
@media (prefers-reduced-motion: no-preference) {
|
|
846
|
+
.element {
|
|
847
|
+
animation: spin 1s infinite;
|
|
848
|
+
}
|
|
849
|
+
}
|
|
850
|
+
```
|
|
851
|
+
|
|
852
|
+
---
|
|
853
|
+
|
|
854
|
+
## Quick Reference
|
|
855
|
+
|
|
856
|
+
### Animation Decision Tree
|
|
857
|
+
|
|
858
|
+
```
|
|
859
|
+
Need animation?
|
|
860
|
+
├─ Immediate feedback (<100ms)? → Use transition with 100-150ms
|
|
861
|
+
├─ State change? → Use transition with 150-300ms
|
|
862
|
+
├─ Complex multi-step? → Use @keyframes
|
|
863
|
+
├─ Loading indicator? → Use linear animation (constant speed)
|
|
864
|
+
├─ Decorative/branding? → Use cubic-bezier with reduced-motion fallback
|
|
865
|
+
└─ Layout change? → Use transform (never width/height/margin)
|
|
866
|
+
```
|
|
867
|
+
|
|
868
|
+
### Common Durations Cheat Sheet
|
|
869
|
+
|
|
870
|
+
```css
|
|
871
|
+
/* Copy-paste ready */
|
|
872
|
+
--fast: 150ms; /* Hover, focus */
|
|
873
|
+
--normal: 300ms; /* Modal, card */
|
|
874
|
+
--slow: 500ms; /* Page transition */
|
|
875
|
+
```
|
|
876
|
+
|
|
877
|
+
### Common Easings Cheat Sheet
|
|
878
|
+
|
|
879
|
+
```css
|
|
880
|
+
/* Copy-paste ready */
|
|
881
|
+
--ease-default: ease; /* General use */
|
|
882
|
+
--ease-bounce: cubic-bezier(0.34, 1.56, 0.64, 1); /* Buttons, cards */
|
|
883
|
+
--ease-glitch: cubic-bezier(0.68, -0.55, 0.27, 1.55); /* Corruption */
|
|
884
|
+
```
|
|
885
|
+
|
|
886
|
+
---
|
|
887
|
+
|
|
888
|
+
## Related Documentation
|
|
889
|
+
|
|
890
|
+
- [INTERACTIVE_STATES.md](./INTERACTIVE_STATES.md) - State-specific animation timings
|
|
891
|
+
- [GLASSMORPHISM.md](./GLASSMORPHISM.md) - Performance notes for backdrop-filter
|
|
892
|
+
- [COLOR_SYSTEM.md](../brand/COLOR_SYSTEM.md) - Color values for animations
|
|
893
|
+
- [TYPOGRAPHY.md](../brand/TYPOGRAPHY.md) - Text animation considerations
|
|
894
|
+
- [TRANSLATION_FAILURE_AESTHETIC.md](../brand/TRANSLATION_FAILURE_AESTHETIC.md) - Corruption guidelines
|
|
895
|
+
|
|
896
|
+
---
|
|
897
|
+
|
|
898
|
+
**Last Updated**: 2025-12-13
|
|
899
|
+
**Version**: 1.0.0
|
|
900
|
+
**Maintainer**: Celeste Brand System
|
|
901
|
+
**Status**: ✅ Phase 2 Complete
|