Initial commit: 79+ design patterns, LLM prompts, and assets

This commit is contained in:
Neo committed 2026-06-20 17:06:07 +05:00
commit 2b42a76d6d
112 files changed
+13652

No files matched your search

+320
View File
@@ -0,0 +1,320 @@
---
name: motion/animated-gradient
title: Animated Gradient Background
description: Smoothly shifting color gradients for backgrounds, buttons, and text. Creates premium, living feel.
tags: [motion, gradient, animation, background, glassmorphism]
source: stripe.com, raycast.com, resend.com, linear.app
---
# Animated Gradient Background
## Что это
Анимированные градиентные фоны, которые плавно перетекают друг в друга. Один из ключевых паттернов Stripe.com и современных SaaS-лендингов. Создаёт ощущение "живого" дизайна.
## Типы анимированных градиентов
### 1. Stripe-Style (shifting color flow)
```css
.gradient-bg {
background: linear-gradient(
135deg,
#6366f1,
#8b5cf6,
#ec4899,
#f43f5e,
#6366f1
);
background-size: 400% 400%;
animation: gradient-shift 15s ease infinite;
}
@keyframes gradient-shift {
0% { background-position: 0% 50%; }
25% { background-position: 100% 50%; }
50% { background-position: 100% 100%; }
75% { background-position: 0% 100%; }
100% { background-position: 0% 50%; }
}
```
### 2. Radial Pulse (расширяющийся круг)
```css
.gradient-pulse {
background: radial-gradient(
circle at 30% 30%,
rgba(99, 102, 241, 0.15),
transparent 50%
);
animation: pulse 8s ease-in-out infinite;
}
@keyframes pulse {
0%, 100% {
transform: scale(1);
opacity: 0.5;
}
50% {
transform: scale(1.2);
opacity: 0.8;
}
}
```
### 3. Mesh Gradient (несколько источников)
```css
.mesh-gradient {
background:
radial-gradient(ellipse at 20% 80%, rgba(99, 102, 241, 0.15), transparent 50%),
radial-gradient(ellipse at 80% 20%, rgba(236, 72, 153, 0.15), transparent 50%),
radial-gradient(ellipse at 50% 50%, rgba(139, 92, 246, 0.1), transparent 50%);
background-size: 100% 100%;
animation: mesh-move 20s ease-in-out infinite alternate;
}
@keyframes mesh-move {
0% {
background-position: 0% 0%, 100% 100%, 50% 50%;
}
100% {
background-position: 100% 100%, 0% 0%, 60% 40%;
}
}
```
### 4. Gradient Border (подвижная рамка)
```css
.gradient-border {
position: relative;
border-radius: 20px;
padding: 2px; /* border width */
background: linear-gradient(90deg, #6366f1, #ec4899, #f43f5e, #6366f1);
background-size: 300% 100%;
animation: border-gradient 4s linear infinite;
}
.gradient-border::before {
content: '';
position: absolute;
inset: 0;
border-radius: 18px;
background: #0a0a0a;
z-index: -1;
}
@keyframes border-gradient {
0% { background-position: 0% 50%; }
100% { background-position: 100% 50%; }
}
```
## Glassmorphism Card (Stripe style)
```css
.glass-card {
position: relative;
background: rgba(255, 255, 255, 0.03);
backdrop-filter: blur(20px);
-webkit-backdrop-filter: blur(20px);
border: 1px solid rgba(255, 255, 255, 0.08);
border-radius: 20px;
overflow: hidden;
}
.glass-card::before {
content: '';
position: absolute;
inset: 0;
background: linear-gradient(
135deg,
rgba(255, 255, 255, 0.05) 0%,
transparent 50%
);
pointer-events: none;
}
```
## Hero Gradient Background (Stripe.com style)
```html
<section class="hero-gradient">
<div class="gradient-orb orb-1"></div>
<div class="gradient-orb orb-2"></div>
<div class="gradient-orb orb-3"></div>
<div class="hero-content">
<h1>Your text here</h1>
</div>
</section>
```
```css
.hero-gradient {
position: relative;
min-height: 60vh;
overflow: hidden;
background: #0a0a0a;
}
.gradient-orb {
position: absolute;
border-radius: 50%;
filter: blur(80px);
opacity: 0.4;
animation: orb-float 20s ease-in-out infinite;
}
.orb-1 {
width: 600px;
height: 600px;
background: radial-gradient(circle, #6366f1, transparent 70%);
top: -20%;
left: -10%;
animation-delay: 0s;
}
.orb-2 {
width: 500px;
height: 500px;
background: radial-gradient(circle, #ec4899, transparent 70%);
bottom: -20%;
right: -5%;
animation-delay: -5s;
}
.orb-3 {
width: 400px;
height: 400px;
background: radial-gradient(circle, #10b981, transparent 70%);
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
animation-delay: -10s;
}
@keyframes orb-float {
0%, 100% {
transform: translate(0, 0) scale(1);
}
25% {
transform: translate(50px, -30px) scale(1.1);
}
50% {
transform: translate(-30px, 50px) scale(0.95);
}
75% {
transform: translate(30px, 30px) scale(1.05);
}
}
```
## Animated Button Gradient
```css
.btn-gradient-animated {
position: relative;
padding: 14px 28px;
border: none;
border-radius: 12px;
font-weight: 600;
color: white;
cursor: pointer;
overflow: hidden;
transition: transform 0.3s ease, box-shadow 0.3s ease;
}
.btn-gradient-animated::before {
content: '';
position: absolute;
inset: 0;
background: linear-gradient(
120deg,
#6366f1,
#8b5cf6,
#ec4899,
#f43f5e,
#6366f1
);
background-size: 300% 100%;
animation: btn-gradient 6s ease infinite;
}
.btn-gradient-animated:hover {
transform: translateY(-2px);
box-shadow: 0 8px 30px rgba(99, 102, 241, 0.3);
}
@keyframes btn-gradient {
0% { background-position: 0% 50%; }
50% { background-position: 100% 50%; }
100% { background-position: 0% 50%; }
}
```
## Color Palettes
### Indie/SaaS (Stripe/Linear style)
```css
--grad-indigo: #6366f1;
--grad-purple: #8b5cf6;
--grad-pink: #ec4899;
--grad-rose: #f43f5e;
--grad-cyan: #06b6d4;
```
### Warm (Resend style)
```css
--grad-orange: #f97316;
--grad-yellow: #eab308;
--grad-red: #ef4444;
--grad-pink: #ec4899;
```
### Cool (Raycast style)
```css
--grad-blue: #3b82f6;
--grad-cyan: #06b6d4;
--grad-teal: #14b8a6;
--grad-green: #10b981;
```
## Anti-patterns
| ❌ Плохо | ✅ Хорошо |
|----------|-----------|
| `animation: 3s` — слишком быстро (мозг не успевает) | `animation: 15–30s` — медленное плавное движение |
| Яркие цвета `#ff0000` | Приглушённые `rgba(99,102,241,0.15)` |
| Без `filter: blur()` на орбах | `blur(80–120px)` для мягких переходов |
| Без `prefers-reduced-motion` | Уважать настройки пользователя |
| Градиент на тексте без fallback | Всегда `-webkit-background-clip` |
## Performance
```css
/* ✅ Good — CSS animation (GPU accelerated) */
.gradient-bg {
animation: gradient-shift 15s ease infinite;
will-change: background-position;
}
/* ❌ Bad — JS animation (main thread) */
element.animate([
{ backgroundPosition: '0% 50%' },
{ backgroundPosition: '100% 50%' }
], { duration: 15000, iterations: Infinity });
```
## CV Service Validation
```bash
# Проверить визуальную гармонию градиентов
curl -s -X POST http://localhost:8001/design/analyze \
-H "Content-Type: application/json" \
-d '{"image_base64": "...", "full_analysis": false}' | python3 -c "
import sys, json
d = json.load(sys.stdin)
color = d['dimensions'].get('color_harmony', {})
print(f\"Color Harmony: {color.get('score', 'N/A')}/10\")
print(f\" Palette: {color.get('palette', 'N/A')}\")
"
```
Ожидается: `color_harmony` > 6.
+145
View File
@@ -0,0 +1,145 @@
---
name: motion/hover-micro-interaction
title: Hover Micro-interactions
description: Subtle animations on hover: scale, shadow, border glow, color shift.
tags: [motion, hover, micro-interaction, subtle, premium]
source: linear.app, raycast.com, stripe.com, resend.com
---
# Hover Micro-interactions
## Что это
Микровзаимодействия при наведении: плавные изменения масштаба, тени, свечения бордера, цветового сдвига. Создают ощущение живого интерфейса.
## Типы hover-эффектов
### 1. Card Lift + Shadow
```css
.card {
transition: transform 0.3s ease, box-shadow 0.3s ease;
}
.card:hover {
transform: translateY(-4px);
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.3);
}
```
### 2. Border Glow
```css
.card {
border: 1px solid var(--color-border);
transition: border-color 0.3s ease;
}
.card:hover {
border-color: var(--color-accent);
box-shadow: 0 0 20px rgba(99, 102, 241, 0.2);
}
```
### 3. Image Zoom
```css
.image-wrapper {
overflow: hidden;
border-radius: 16px;
}
.image-wrapper img {
transition: transform 0.5s ease;
}
.image-wrapper:hover img {
transform: scale(1.05);
}
```
### 4. Text Color Shift
```css
.link {
color: var(--color-text-secondary);
position: relative;
transition: color 0.3s ease;
}
.link:hover {
color: var(--color-accent);
}
.link::after {
content: '';
position: absolute;
bottom: -2px;
left: 0;
width: 0;
height: 2px;
background: var(--color-accent);
transition: width 0.3s ease;
}
.link:hover::after {
width: 100%;
}
```
### 5. Icon Rotation/Swing
```css
.icon {
transition: transform 0.3s ease;
}
.icon:hover {
transform: rotate(15deg) scale(1.1);
}
```
## Magnetic Hover (Enhanced)
```css
.hover-magnetic {
transition: transform 0.2s ease;
}
.hover-magnetic:hover {
transform: scale(1.02);
}
```
```javascript
document.querySelectorAll('.hover-magnetic').forEach(el => {
el.addEventListener('mousemove', (e) => {
const rect = el.getBoundingClientRect();
const x = (e.clientX - rect.left) / rect.width - 0.5;
const y = (e.clientY - rect.top) / rect.height - 0.5;
el.style.transform = `perspective(500px) rotateY(${x * 10}deg) rotateX(${-y * 10}deg) scale(1.02)`;
});
el.addEventListener('mouseleave', () => {
el.style.transform = 'perspective(500px) rotateY(0) rotateX(0) scale(1)';
});
});
```
## Anti-patterns
| ❌ Плохо | ✅ Хорошо |
|----------|-----------|
| `transform: scale(1.2)` — слишком резко | `transform: scale(1.02–1.05)` |
| Без `transition` | `transition: all 0.3s ease` |
| Без `will-change` на тяжёлых | `will-change: transform` |
| Hover на touch-устройствах | `@media (hover: hover)` |
## Mobile Detection
```css
@media (hover: none) {
.hover-effect:hover {
transform: none;
}
}
```
## CV Service Validation
```bash
curl -s -X POST http://localhost:8001/design/analyze \
-H "Content-Type: application/json" \
-d '{"image_base64": "...", "full_analysis": false}' | python3 -c "
import sys, json
d = json.load(sys.stdin)
motion = d['dimensions'].get('motion_quality', {})
print(f\"Motion Quality: {motion.get('score', 'N/A')}/10\")
"
```
Ожидается: `motion_quality` > 6.
+151
View File
@@ -0,0 +1,151 @@
---
name: motion/magnetic-button
title: Magnetic Button Effect
description: Buttons that follow cursor within radius. Creates premium tactile feel.
tags: [motion, magnetic, button, micro-interaction, premium]
source: raycast.com, linear.app, brad.co, amppz.com
---
# Magnetic Button Effect
## Что это
Кнопки и элементы, которые притягиваются к курсору мыши в пределах радиуса. Создаёт ощущение тактильности и премиальности.
## Параметры
| Параметр | Значение |
|----------|----------|
| **Radius** | 60–100px |
| **Strength** | 0.1–0.3 |
| **Easing** | `cubic-bezier(0.25, 0.1, 0.25, 1)` |
| **Duration** | 0.3s |
## Реализация
```html
<button class="magnetic-btn">Hover me</button>
```
```css
.magnetic-btn {
padding: 16px 32px;
background: var(--color-accent);
color: white;
border: none;
border-radius: 12px;
font-size: 1rem;
font-weight: 600;
cursor: pointer;
position: relative;
transition: transform 0.3s cubic-bezier(0.25, 0.1, 0.25, 1);
}
```
```javascript
class MagneticButton {
constructor(element, strength = 0.2, radius = 80) {
this.el = element;
this.strength = strength;
this.radius = radius;
this.rect = element.getBoundingClientRect();
this.isHovering = false;
this.animationId = null;
this.pos = { x: 0, y: 0 };
this.target = { x: 0, y: 0 };
this.init();
}
init() {
this.el.addEventListener('mouseenter', (e) => {
this.isHovering = true;
this.rect = this.el.getBoundingClientRect();
});
this.el.addEventListener('mouseleave', () => {
this.isHovering = false;
this.target = { x: 0, y: 0 };
this.animate();
});
this.el.addEventListener('mousemove', (e) => {
const x = e.clientX - this.rect.left - this.rect.width / 2;
const y = e.clientY - this.rect.top - this.rect.height / 2;
this.target = { x: x * this.strength, y: y * this.strength };
});
this.animate();
}
animate() {
if (this.isHovering) {
this.pos.x += (this.target.x - this.pos.x) * 0.1;
this.pos.y += (this.target.y - this.pos.y) * 0.1;
} else {
this.pos.x *= 0.9;
this.pos.y *= 0.9;
}
this.el.style.transform = `translate(${this.pos.x}px, ${this.pos.y}px)`;
this.animationId = requestAnimationFrame(() => this.animate());
}
destroy() {
cancelAnimationFrame(this.animationId);
}
}
// Usage
document.querySelectorAll('.magnetic-btn').forEach(el => {
new MagneticButton(el);
});
```
## Варианты силы притяжения
```javascript
// Слабое (для текста)
new MagneticButton(el, 0.1, 60);
// Среднее (для кнопок)
new MagneticButton(el, 0.2, 80);
// Сильное (для крупных элементов)
new MagneticButton(el, 0.3, 100);
```
## Anti-patterns
| ❌ Плохо | ✅ Хорошо |
|----------|-----------|
| `strength: 0.5` — слишком резко | `strength: 0.1–0.3` |
| Без `mouseleave` сброса | Всегда сбрасывать в 0,0 |
| Без `requestAnimationFrame` | Использовать RAF |
| На мобильных без mouse | Проверять `matchMedia('(pointer: fine)')` |
## Mobile Fallback
```javascript
if (window.matchMedia('(pointer: fine)').matches) {
document.querySelectorAll('.magnetic-btn').forEach(el => {
new MagneticButton(el);
});
}
// На мобильных — просто hover-эффект
```
## CV Service Validation
```bash
# Проверить micro-interactions
curl -s -X POST http://localhost:8001/design/analyze \
-H "Content-Type: application/json" \
-d '{"image_base64": "...", "full_analysis": false}' | python3 -c "
import sys, json
d = json.load(sys.stdin)
motion = d['dimensions'].get('motion_quality', {})
print(f\"Motion Quality: {motion.get('score', 'N/A')}/10\")
"
```
Ожидается: `motion_quality` > 6.
+119
View File
@@ -0,0 +1,119 @@
---
name: motion/page-transitions
title: Page Transitions & Scroll Animations
description: Smooth page transitions, section reveals, and scroll-triggered animations.
tags: [motion, page-transition, scroll, reveal, animation]
source: linear.app, raycast.com, brad.co, amppz.com
---
# Page Transitions & Scroll Animations
## Что это
Анимации при переходе между страницами и появление секций при скролле.
## 1. Section Reveal on Scroll
```javascript
const sections = document.querySelectorAll('.reveal-section');
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
entry.target.classList.add('revealed');
observer.unobserve(entry.target);
}
});
}, { threshold: 0.1, rootMargin: '0px 0px -10% 0px' });
sections.forEach(section => observer.observe(section));
```
```css
.reveal-section {
opacity: 0;
transform: translateY(40px);
transition: opacity 0.8s ease, transform 0.8s ease;
}
.reveal-section.revealed {
opacity: 1;
transform: translateY(0);
}
```
## 2. Staggered Children Reveal
```javascript
const sections = document.querySelectorAll('.stagger-section');
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
entry.target.querySelectorAll('.stagger-child').forEach((child, i) => {
setTimeout(() => {
child.classList.add('revealed');
}, i * 100);
});
observer.unobserve(entry.target);
}
});
}, { threshold: 0.1 });
sections.forEach(section => observer.observe(section));
```
```css
.stagger-child {
opacity: 0;
transform: translateY(30px);
transition: all 0.6s ease;
}
.stagger-child.revealed {
opacity: 1;
transform: translateY(0);
}
```
## 3. Page Transition (SPA style)
```css
.page {
position: fixed;
inset: 0;
opacity: 0;
transform: scale(1.05);
transition: all 0.4s ease;
pointer-events: none;
z-index: 0;
}
.page.active {
opacity: 1;
transform: scale(1);
pointer-events: all;
z-index: 1;
}
.page.exit {
opacity: 0;
transform: scale(0.95);
}
```
## 4. Smooth Scroll Snap
```css
.snap-scroll {
scroll-snap-type: y mandatory;
overflow-y: auto;
height: 100vh;
}
.snap-section {
scroll-snap-align: start;
min-height: 100vh;
}
```
## Anti-patterns
| ❌ Плохо | ✅ Хорошо |
|----------|-----------|
| `transition: 0.1s` — слишком быстро | `transition: 0.6–1s` |
| Без `IntersectionObserver` | Использовать observer |
| Без `prefers-reduced-motion` | Уважать настройки |
| Без `will-change` | `will-change: transform, opacity` |
+142
View File
@@ -0,0 +1,142 @@
---
name: motion/parallax-scroll
title: Parallax Depth Effect
description: Multi-layer scrolling with different speeds. Creates 3D depth perception.
tags: [motion, parallax, depth, scroll, 3d, premium]
source: apple.com, linear.app, amppz.com, brad.co
---
# Parallax Depth Effect
## Что это
Многослойный скролл с разной скоростью — создаёт ощущение глубины и 3D-пространства.
## Параметры
| Параметр | Значение |
|----------|----------|
| **Foreground speed** | 1x (обычный скролл) |
| **Midground speed** | 0.5x–0.7x |
| **Background speed** | 0.1x–0.3x |
## CSS-реализация (простая)
```css
.parallax-section {
position: relative;
height: 100vh;
overflow: hidden;
}
.parallax-layer {
position: absolute;
width: 100%;
height: 120%;
top: -10%;
}
.parallax-layer.background {
z-index: 1;
}
.parallax-layer.midground {
z-index: 2;
transform: translateY(10%);
}
.parallax-layer.foreground {
z-index: 3;
transform: translateY(20%);
}
```
## GSAP ScrollTrigger (рекомендуется)
```javascript
import gsap from 'gsap';
import { ScrollTrigger } from 'gsap/ScrollTrigger';
gsap.registerPlugin(ScrollTrigger);
// Background slow
gsap.to('.parallax-bg', {
yPercent: 30,
ease: 'none',
scrollTrigger: {
trigger: '.parallax-section',
start: 'top bottom',
end: 'bottom top',
scrub: true
}
});
// Midground medium
gsap.to('.parallax-mid', {
yPercent: 50,
ease: 'none',
scrollTrigger: {
trigger: '.parallax-section',
start: 'top bottom',
end: 'bottom top',
scrub: true
}
});
// Foreground fast
gsap.to('.parallax-fg', {
yPercent: 80,
ease: 'none',
scrollTrigger: {
trigger: '.parallax-section',
start: 'top bottom',
end: 'bottom top',
scrub: true
}
});
```
## Intersection Observer (лёгкий вариант)
```javascript
const layers = document.querySelectorAll('.parallax-layer');
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
const speed = parseFloat(entry.target.dataset.speed || 0.1);
const rect = entry.target.getBoundingClientRect();
const scrollPercent = window.scrollY / (window.innerHeight + rect.height);
entry.target.style.transform = `translateY(${scrollPercent * speed * 100}%)`;
});
}, { threshold: 0.1 });
layers.forEach(layer => observer.observe(layer));
```
## Anti-patterns
| ❌ Плохо | ✅ Хорошо |
|----------|-----------|
| Без `prefers-reduced-motion` | Уважать настройки пользователя |
| `yPercent: 100` — слишком резко | `yPercent: 30–50` |
| Без `scrub: true` | `scrub: 1` для плавности |
|太重ых элементов | SVG/градиенты вместо фото |
## Mobile
```css
@media (prefers-reduced-motion: reduce) {
.parallax-layer {
transform: none !important;
}
}
```
## CV Service Validation
```bash
curl -s -X POST http://localhost:8001/design/analyze \
-H "Content-Type: application/json" \
-d '{"image_base64": "...", "full_analysis": false}' | python3 -c "
import sys, json
d = json.load(sys.stdin)
motion = d['dimensions'].get('motion_quality', {})
print(f\"Motion Quality: {motion.get('score', 'N/A')}/10\")
"
```
+229
View File
@@ -0,0 +1,229 @@
---
name: motion/procedural-patterns
title: Procedural Pattern Generator
description: Canvas-based procedural generation of animated patterns around title elements. Uses occupancy grid, BFS proximity map, and seeded RNG for deterministic layout.
tags: [motion, procedural, pattern, canvas, generation, animation, title-card]
source: github.com/halfof8/patterngen-oss
---
# Procedural Pattern Generator
## Что это
Инструментарий для автоматического создания анимированных заставок с процедурно размещёнными паттернами вокруг заголовков. Состоит из трёх компонентов:
1. **Web App** (React + Canvas 2D) — основной генератор
2. **Figma Plugin** — импорт дизайна из Figma
3. **Blender Add-on** — импорт PNG-последовательностей как emissive planes
Пайплайн: Figma → Web App (генерация) → PNG-последовательности → Blender (композиция).
## Алгоритм размещения
### 1. Occupancy Grid
Сетка 20px на 1920×1080 (96×54 ячеек). Title-элементы занимают ячейки, помечая их как занятые.
```typescript
class GridOccupancy {
private cells: boolean[][]; // GRID_ROWS × GRID_COLS
markRect(x, y, w, h) { /* occupy cells */ }
isBlockFree(col, row, spanCols, spanRows) { /* collision check */ }
findFreeBlocks(spanCols, spanRows, align) { /* find valid positions */ }
}
```
### 2. BFS Proximity Map
BFS от всех title-элементов к каждой ячейке сетки. Определяет «зону влияния» — где паттерны могут появляться относительно заголовка.
```typescript
function buildProximityMap(titles): number[][] {
const dist: number[][] = fill(Infinity);
// BFS from all title edges
while (queue.length > 0) {
const [cx, cy] = queue.shift();
for (const [dx, dy] of neighbors) {
const nd = dist[cy+dy][cx+dx];
if (nd > dist[cy][cx] + 1) {
dist[cy+dy][cx+dx] = dist[cy][cx] + 1;
queue.push([cx+dx, cy+dy]);
}
}
}
}
```
### 3. Seeded Placement
Deterministic RNG (mulberry32). Same seed = identical layout.
```typescript
function mulberry32(seed) {
let state = seed >>> 0;
return () => {
state = (state + 0x6D2B79F5) >>> 0;
let t = state;
t = Math.imul(t ^ (t >>> 15), t | 1);
t ^= t + Math.imul(t ^ (t >>> 7), t | 61);
return ((t ^ (t >>> 14)) >>> 0) / 4294967296;
};
}
```
## 7 типов анимации паттернов
| Тип | Эффект | CSS/Canvas |
|-----|--------|------------|
| `dots` | Поэлементный fade-in со stagger | `globalAlpha` per shape |
| `capsule` | Рост от центра по длинной оси | Clip rect grow |
| `circle` | Pie-fill по часовой | Arc + clip |
| `arrow` | Slide up снизу | `translateY` offset |
| `square` | Scale 1.6× → 1× | `scale()` transform |
| `stripes` | Полосы slide-in staggered | Y-offset per stripe |
| `cross` | Scale 1.6× → 1× | `scale()` transform |
## Timeline: 3 фазы
```
0──────────→ durationMs ──────────→ 2×durationMs ──────────→ 4×durationMs (loop)
│ ФАЗА 1 │ ФАЗА 2 │ ФАЗА 3 │
│ Title reveal │ Pattern reveal │ Dots pulse │
│ half-reveal из центра │ clip-side reveal │ sin wave │
```
```typescript
// Фазовая логика timeline
function evaluate(elapsedMs, durationMs, titles, patterns, dots, easing) {
// Phase 1: Title reveal (0→1×)
const titleT = Math.min(1, elapsedMs / durationMs);
const titleEased = applyEasing(titleT, easing); // half-reveal
// Phase 2: Pattern reveal (1×→2×)
const cycleMs = afterTitle % (4 * durationMs);
// Phase 3: Dots pulse (2×→4×)
const holdMs = cycleMs - durationMs;
const pulse = 0.15 + 0.85 * (0.5 + 0.5 * Math.sin(2π * speed * t + phase));
}
```
## Color System
```typescript
function getPairsForColor(primary: string): ColorPair[] {
const lum = luminance(r, g, b);
const dark = mixRGB(primary, #000000, 0.45);
const light = mixRGB(primary, #FFFFFF, 0.35);
if (lum > 0.5) {
return [
{ bg: primary, fg: '#000000' },
{ bg: dark, fg: primary },
{ bg: primary, fg: dark },
];
}
// inverted for dark colors
}
```
Конвенция SVG-паттернов (40×40): `fill="white"` = фон (перекрашивается runtime), `fill="black"` / `stroke="black"` = foreground.
## Figma Plugin
Экспортирует фреймы 1920×1080 в JSON. Каждый слой экспортируется как 2× PNG, цвета автодетектятся.
```javascript
// code.js — экспорт слоя
bytes = await child.exportAsync({
format: 'PNG',
constraint: { type: 'SCALE', value: 2 },
});
// Автодетект цветов
collectUsedColors(node, usedColors); // BFS по иерархии
```
## Blender Add-on
Импорт ZIP с 3 PNG-последовательностями (titles, patterns, dots) как emissive planes:
```python
# Создаёт plane с aspect-ratio из разрешения PNG
plane.scale = (6.0, 6.0, 6.0)
plane.rotation_euler = (90°, 0, 0)
# Emissive shader node graph
tex.image.source = 'SEQUENCE'
tex.image_user.use_cyclic = cyclic # dots = True
nt.links.new(tex.outputs['Color'], bsdf.inputs['Emission'])
nt.links.new(tex.outputs['Alpha'], bsdf.inputs['Alpha'])
```
## Ключевые параметры
| Параметр | Диапазон | Описание |
|----------|----------|----------|
| density | 1–20 | Количество паттернов (формула: `3 + 40 × (d/10)²`) |
| proximity | 1–20 | Зона влияния от title (maxRadius = `2 + proximity × 2.5`) |
| stagger | 0–5 | Stagger window: `(stagger/5) × 0.8` |
| durationMs | 100–20000 | Длительность фазы |
| EXPORT_SCALE | 4 | Рендер в 4× разрешении |
| FPS | 24 | Частота кадров |
## Anti-patterns
| ❌ Плохо | ✅ Хорошо |
|----------|-----------|
| Паттерны накладываются на title | BFS proximity map ограничивает placement |
| Без collision detection | Occupancy grid предотвращает наложения |
| Рандомная расстановка | Seeded RNG — детерминизм для итераций |
| Паттерны без анимации | 7 типов анимации с clip-side |
| Фиксированные цвета | Алгоритмическая генерация color pairs |
| Рендер без bg/fg разделения | white→bg, black→fg runtime colorize |
| 1 кадр = 1 экспорт | Layer export: titles/patterns/dots separately |
## Где используется
- **patterngen-oss** (github.com/halfof8/patterngen-oss) — open-source референс
- Title cards в motion design
- Анимированные фоны лендингов
- Procedural patterns для социальных сетей
- Генерация фоновых текстур для 3D-композиций в Blender
## CV Service Validation
Процедурные паттерны рендерятся на Canvas 2D — это клиентская генерация, не серверная. Для валидации дизайна страницы с pattern-генератором:
```bash
curl -X POST http://localhost:8001/design/full-analysis \
-H "Content-Type: application/json" \
-d '{"html": "<HTML>", "full_analysis": true}'
```
**Особенности:** Canvas-элементы не оцениваются по semantic HTML — паттерны — это визуальный слой. Оценивается обёртка (header, sidebar, controls).
## Технические детали
```
Структура проекта:
├── src/store.ts # Zustand store — 373 строки, вся бизнес-логика
├── src/core/
│ ├── patterns.ts # Load SVG, colorize (white→bg, black→fg)
│ ├── colors.ts # getPairsForColor, luminance-based
│ ├── grid.ts # GridOccupancy + snapToGrid(20px)
│ ├── placement.ts # generatePlacement — BFS + occupancy
│ └── rng.ts # mulberry32 seeded RNG
├── src/engine/
│ ├── animations.ts # 7 анимаций (drawAnimatedPattern)
│ ├── easing.ts # 7 easing функций
│ └── timeline.ts # evaluate() — фазовая логика
├── src/export/
│ ├── renderer.ts # renderFrame() — canvas layer compositing
│ └── exporter.ts # exportAll() → ZIP with 3 layers × 24fps
├── figma-plugin/
│ ├── code.js # Figma sandbox export
│ └── ui.html # Plugin UI
└── blender-addon/
└── patterngen_importer.py # Blender 3.4+ operator
```
**Зависимости:** React 18, Zustand 5, JSZip, FileSaver, Vite
+187
View File
@@ -0,0 +1,187 @@
---
name: motion/staggered-reveal
title: Staggered Element Reveal
description: Cascading entrance animations where elements appear sequentially. Used on Linear, Raycast, Linear.app hero sections.
tags: [motion, gsap, scrolltrigger, entrance, stagger]
source: linear.app, raycast.com, godly.website top 50
---
# Staggered Element Reveal
## Что это
Последовательное появление элементов с нарастающей задержкой. Создаёт ощущение "потока" и профессионализма. Элементы появляются не все сразу, а каскадом с интервалом.
## Где используется
- Hero-секции Linear, Raycast, Resend
- Feature-блоки с иконками
- Списки преимуществ
- Карточки с услугами
## Ключевые параметры
| Параметр | Значение | Почему |
|----------|----------|--------|
| `stagger` | `0.08–0.12` | Оптимальный ритм — не слишком быстро, не медленно |
| `duration` | `0.6–0.8s` | Достаточное время для заметности |
| `ease` | `power3.out` | Плавное замедление, ощущение "приземления" |
| `delay` | `0.2–0.3` | Задержка перед началом каскада |
| `from opacity` | `0.8` | Не полная прозрачность, чтобы не было "прыжка" |
| `from translateY` | `20–30px` | Элементы "влетают" снизу |
## Референсы
### Linear Hero
```
1. Logo появляется → 0.3s задержка
2. H1 появляется (line by line) → stagger 0.1s
3. Подзаголовок → 0.4s задержка
4. CTA кнопка → 0.6s задержка
5. Декоративные элементы → 0.8s задержка
```
### Resend Feature Grid
```
- Карточки появляются с задержкой 0.08s друг за другом
- Каждая карточка: fade + slide up 15px
- Hover на карточку: scale 1.02 + shadow
```
## Реализация
```javascript
// GSAP + ScrollTrigger
import gsap from 'gsap';
import { ScrollTrigger } from 'gsap/ScrollTrigger';
gsap.registerPlugin(ScrollTrigger);
// 1. Базовый stagger-ревеал
gsap.from('.stagger-item', {
opacity: 0,
y: 30,
duration: 0.7,
stagger: 0.08,
ease: 'power3.out',
delay: 0.3,
scrollTrigger: {
trigger: '.stagger-section',
start: 'top 80%',
toggleActions: 'play none none none'
}
});
// 2. Stagger для карточек
gsap.from('.feature-card', {
opacity: 0,
y: 25,
scale: 0.97,
duration: 0.6,
stagger: 0.06,
ease: 'power2.out',
scrollTrigger: {
trigger: '.features-grid',
start: 'top 85%'
}
});
// 3. Stagger для текстовых строк
gsap.from('.hero-line', {
opacity: 0,
y: 40,
duration: 0.8,
stagger: 0.12,
ease: 'power3.out',
delay: 0.2
});
// 4. Line-by-line reveal для заголовка
const lines = document.querySelectorAll('.hero h1 .line');
gsap.from(lines, {
opacity: 0,
y: 50,
rotateX: -15,
duration: 0.8,
stagger: 0.15,
ease: 'power3.out',
delay: 0.3
});
```
## CSS подготовка
```css
.stagger-item {
/* Начальное состояние до анимации */
opacity: 0;
transform: translateY(30px);
will-change: opacity, transform;
}
/* Альтернатива: CSS-only для простых случаев */
@keyframes fadeUp {
from {
opacity: 0;
transform: translateY(25px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.stagger-item {
opacity: 0;
animation: fadeUp 0.6s ease forwards;
}
.stagger-item:nth-child(1) { animation-delay: 0.1s; }
.stagger-item:nth-child(2) { animation-delay: 0.18s; }
.stagger-item:nth-child(3) { animation-delay: 0.26s; }
.stagger-item:nth-child(4) { animation-delay: 0.34s; }
.stagger-item:nth-child(5) { animation-delay: 0.42s; }
.stagger-item:nth-child(6) { animation-delay: 0.5s; }
```
## Anti-patterns (Чего НЕ делать)
| ❌ Плохо | ✅ Хорошо |
|----------|-----------|
| `stagger: 0.02` — слишком быстро, "мигание" | `stagger: 0.08` — чёткий ритм |
| `stagger: 0.3` — слишком медленно, скучно | `stagger: 0.06–0.12` — оптимально |
| Все элементы `opacity: 0 → 1` | `opacity: 0.8 → 1` + `y: 20 → 0` |
| Без `will-change` | `will-change: opacity, transform` |
| Без `stagger` (все сразу) | Каскадное появление |
## Performance
```javascript
// ✅ GPU accelerated
gsap.from('.item', { opacity: 0, y: 20, transform: 'translateY(20px)' });
// ❌ Bad — заставляет layout
gsap.from('.item', { marginLeft: '20px' });
gsap.from('.item', { top: '20px', left: '20px' });
```
## Validation (CV Service)
```bash
# После рендеринга страницы
curl -s -X POST http://localhost:8001/design/analyze-dom \
-H "Content-Type: application/json" \
-d '{"html": "...", "full_analysis": false}' | python3 -m json.tool
```
Проверить:
- `performance` score > 8 — анимации только на `transform` и `opacity`
- `ux_heuristics` score > 6 — анимации не мешают навигации
- `wcag_compliance` — анимация имеет `prefers-reduced-motion` фоллбэк
```css
@media (prefers-reduced-motion: reduce) {
.stagger-item {
opacity: 1 !important;
transform: none !important;
animation: none !important;
}
}
```