- 74 brands: airbnb, stripe, apple, figma, linear, vercel, etc. - 147 files: DESIGN.md + README.md per brand (~2.1 MB) - Each DESIGN.md contains structured brand analysis (colors, typography, components) - Updated 00-overview.md with new design-systems directory
321 lines
7.2 KiB
Markdown
321 lines
7.2 KiB
Markdown
---
|
||
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.
|