stylization
برای هر برند، یک زبان حرکتی منسجم با presetهای حرفهای بسازید.
این skill مجموعهای از presetهای موشن برای سبکهای vintage، minimalist، brutalist، organic، playful، elegant، glitch، retro-game و cinematic ارائه میدهد. پارامترهای timing، easing، bounce، wobble، grain، flicker و distortion را برای ساخت یک زبان حرکتی یکپارچه تنظیم میکند.
با استفاده از tokens/stylization.tokens.json میتوانید این سبکها را در CSS، JavaScript، Framer Motion و Remotion پیادهسازی کنید. راهنمای انتخاب سبک، نمونهکد آماده، پشتیبانی از prefers-reduced-motion و ارجاع به استانداردهایی مثل W3C Design Tokens و Material Design 3، این skill را برای پروژههای برندمحور و رابطهای متحرک کاربردی میکند.
چه چیزی میگیرید
- presetهای آماده برای ۹ سبک حرکتی و هنری متفاوت
- پارامترهای منسجم برای duration، easing، spring، overshoot و شدت افکت
- نمونهکدهای CSS، JavaScript، Framer Motion و Remotion
- راهنمای انتخاب سبک بر اساس حالوهوا، برند و نوع محصول
- الگوهای دسترسپذیر برای کاهش یا حذف حرکت با `prefers-reduced-motion`
برای چه کاری خوب است
- طراحان و توسعهدهندگانی که برای یک محصول دیجیتال زبان حرکتی برندمحور میسازند
- پروژههایی که به انیمیشنهای themed، هویت بصری متحرک یا انتقالهای هماهنگ نیاز دارند
- ساخت رابطهای playful، glitch، retro، مینیمال یا سینمایی در وب و ویدیو
برای چه کاری نیست
- پروژههایی که فقط به یک انیمیشن ساده و بدون سبک بصری مشخص نیاز دارند
- تحلیل UX motion یا طراحی کامل interaction بدون اجرای فنی
- افکتهای پیچیده تخصصی که به سیستمهای جداگانه برای particle، distortion یا physics نیاز دارند
دربارهٔ این اسکیل
Stylization
Cohesive artistic motion styles for themed animation systems.
Standards Reference
| Standard | Documentation |
|---|---|
| W3C DTCG | designtokens.org |
| Material Design 3 | m3.material.io/styles/motion |
| IBM Carbon Motion | carbondesignsystem.com/guidelines/motion |
| Apple HIG | developer.apple.com/design/human-interface-guidelines/motion |
Quick Reference
Load from: tokens/stylization.tokens.json
Style Presets
| Style | Feel | Key Characteristics |
|---|---|---|
vintage | Nostalgic | Film grain, flicker, low fps |
minimalist | Clean | Deliberate, precise, no overshoot |
brutalist | Bold | Fast, linear, jarring steps |
organic | Natural | Noise, wobble, variance |
playful | Fun | Bouncy, squash-stretch |
elegant | Refined | Slow, blurred, choreographed |
glitch | Digital | Offset, slicing, chromatic |
retro-game | 8-bit | Stepped, pixelated |
cinematic | Filmic | Motion blur, letterbox |
Selection Guide
What's the brand/mood?
│
├── NOSTALGIC/RETRO
│ ├── Film/photo → vintage (sepia, grain, flicker)
│ ├── 8-bit games → retro-game (stepped, pixelated)
│ └── Digital errors → glitch (offset, chromatic)
│
├── PROFESSIONAL/CORPORATE
│ ├── Clean/modern → minimalist (precise, deliberate)
│ ├── Luxury/premium → elegant (slow, refined)
│ └── Bold/statement → brutalist (fast, linear)
│
├── CREATIVE/EXPRESSIVE
│ ├── Natural/organic → organic (noise, wobble)
│ ├── Fun/energetic → playful (bouncy, squash)
│ └── Dramatic/epic → cinematic (motion blur, slow)
│
└── TECHNICAL/DIGITAL
├── Glitch art → glitch (offset, slicing)
├── Cyberpunk → glitch + brutalist
└── Sci-fi → cinematic + minimalist
Patterns
Vintage Style
Retro film aesthetic with grain and flicker.
CSS + JavaScript:
.vintage-container {
position: relative;
filter: sepia(0.6) contrast(1.1); /* {motion.stylization.vintage.sepia} */
}
/* Film grain overlay */
.vintage-container::before {
content: '';
position: absolute;
inset: 0;
background-image: url('grain.png');
opacity: 0.15; /* {motion.stylization.vintage.film-grain} */
pointer-events: none;
animation: grain 0.5s steps(4) infinite;
}
/* Vignette */
.vintage-container::after {
content: '';
position: absolute;
inset: 0;
box-shadow: inset 0 0 100px rgba(0,0,0,0.3); /* {motion.stylization.vintage.vignette} */
pointer-events: none;
}
@keyframes grain {
0%, 100% { transform: translate(0, 0); }
25% { transform: translate(-2%, 2%); }
50% { transform: translate(2%, -2%); }
75% { transform: translate(-2%, -2%); }
}
/* Flicker effect */
@keyframes flicker {
0%, 100% { opacity: 1; }
50% { opacity: 0.95; } /* {motion.stylization.vintage.flicker-intensity} */
}
.vintage-flicker {
animation: flicker 0.1s infinite;
}
JavaScript (low framerate simulation):
// Simulate 12fps vintage film
function vintageAnimate(element, props, duration) {
const fps = 12; // {motion.stylization.vintage.frame-rate}
const frameTime = 1000 / fps;
const totalFrames = Math.ceil((duration / 1000) * fps);
let frame = 0;
function tick() {
const progress = frame / totalFrames;
// Apply interpolated values
for (const [prop, value] of Object.entries(props)) {
const current = value.from + (value.to - value.from) * progress;
element.style[prop] = typeof value.unit === 'string'
? `${current}${value.unit}`
: current;
}
frame++;
if (frame <= totalFrames) {
setTimeout(tick, frameTime);
}
}
tick();
}
Minimalist Style
Clean, precise motion with deliberate pacing.
Framer Motion:
const minimalistTransition = {
duration: 0.3 * 1.2, // {motion.stylization.minimalist.duration-multiplier}
ease: [0.2, 0, 0, 1], // {motion.stylization.minimalist.easing}
};
const MinimalistReveal = ({ children }) => (
<motion.div
initial={{ opacity: 0, y: 10 }}
animate={{ opacity: 1, y: 0 }}
transition={minimalistTransition}
>
{children}
</motion.div>
);
// Staggered list
const MinimalistList = ({ items }) => (
<motion.ul>
{items.map((item, i) => (
<motion.li
key={item.id}
initial={{ opacity: 0, x: -10 }}
animate={{ opacity: 1, x: 0 }}
transition={{
...minimalistTransition,
delay: i * 0.1 // {motion.stylization.minimalist.delay-between}
}}
>
{item.content}
</motion.li>
))}
</motion.ul>
);
CSS:
.minimalist-transition {
transition: all 360ms cubic-bezier(0.2, 0, 0, 1);
/* duration * 1.2, M3 standard easing */
}
/* No overshoot, clean endings */
.minimalist-entrance {
animation: minimalistEnter 360ms cubic-bezier(0.2, 0, 0, 1) forwards;
}
@keyframes minimalistEnter {
from {
opacity: 0;
transform: translateY(10px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
Brutalist Style
Bold, aggressive motion with jarring transitions.
CSS:
/* Stepped animation - no smooth interpolation */
.brutalist-enter {
animation: brutalistSlide 200ms steps(4) forwards;
/* {motion.stylization.brutalist.step-count} */
}
@keyframes brutalistSlide {
from {
transform: translateX(-100%);
opacity: 0;
}
to {
transform: translateX(0);
opacity: 1;
}
}
/* Linear easing - no softening */
.brutalist-transition {
transition: all 150ms linear; /* {motion.stylization.brutalist.easing} */
}
/* Overshoot snap-back */
@keyframes brutalistSnap {
0% { transform: scale(0); }
70% { transform: scale(1.3); } /* {motion.stylization.brutalist.overshoot} */
100% { transform: scale(1); }
}
.brutalist-snap {
animation: brutalistSnap 200ms linear forwards;
}
JavaScript (screen shake):
function brutalistShake(element) {
const intensity = 5; // {motion.stylization.brutalist.shake-intensity}
const duration = 200;
const start = performance.now();
function shake(timestamp) {
const elapsed = timestamp - start;
const progress = elapsed / duration;
if (progress < 1) {
const decay = 1 - progress;
const x = (Math.random() - 0.5) * 2 * intensity * decay;
const y = (Math.random() - 0.5) * 2 * intensity * decay;
element.style.transform = `translate(${x}px, ${y}px)`;
requestAnimationFrame(shake);
} else {
element.style.transform = '';
}
}
requestAnimationFrame(shake);
}
Organic Style
Natural motion with subtle randomness.
JavaScript:
class OrganicMotion {
constructor(element, options = {}) {
this.element = element;
this.amplitude = options.amplitude || 3; // {motion.stylization.organic.noise-amplitude}
this.frequency = options.frequency || 0.5; // {motion.stylization.organic.noise-frequency}
this.rotationWobble = options.rotation || 2; // {motion.stylization.organic.rotation-wobble}
this.time = Math.random() * 1000;
this.animate();
}
noise(t) {
// Simplified Perlin-like noise
return Math.sin(t) * 0.5 + Math.sin(t * 2.3) * 0.3 + Math.sin(t * 5.7) * 0.2;
}
animate() {
this.time += 0.016 * this.frequency;
const x = this.noise(this.time) * this.amplitude;
const y = this.noise(this.time + 100) * this.amplitude;
const rotation = this.noise(this.time + 200) * this.rotationWobble;
this.element.style.transform = `translate(${x}px, ${y}px) rotate(${rotation}deg)`;
requestAnimationFrame(() => this.animate());
}
}
// Usage
const organicElement = new OrganicMotion(document.querySelector('.organic'));
Framer Motion:
const OrganicFloat = () => {
const [position, setPosition] = useState({ x: 0, y: 0, rotate: 0 });
useEffect(() => {
const interval = setInterval(() => {
setPosition({
x: (Math.random() - 0.5) * 6, // ±3px
y: (Math.random() - 0.5) * 6,
rotate: (Math.random() - 0.5) * 4 // ±2deg
});
}, 2000);
return () => clearInterval(interval);
}, []);
return (
<motion.div
animate={position}
transition={{
duration: 2,
ease: [0.4, 0, 0.2, 1] // {motion.stylization.organic.easing}
}}
/>
);
};
Playful Style
Bouncy, energetic motion with squash-stretch.
Framer Motion:
const playfulSpring = {
type: 'spring',
stiffness: 300, // {motion.stylization.playful.spring-stiffness}
damping: 12, // {motion.stylization.playful.spring-damping}
};
const PlayfulButton = ({ children, onClick }) => (
<motion.button
whileHover={{ scale: 1.05 }}
whileTap={{ scale: 0.95 }}
transition={playfulSpring}
onClick={onClick}
>
{children}
</motion.button>
);
// With squash-stretch
const BouncyBall = () => (
<motion.div
animate={{ y: [0, 100, 0] }}
transition={{
duration: 0.6,
repeat: Infinity,
ease: [0.36, 0, 0.66, -0.56] // Custom bounce
}}
style={{ transformOrigin: 'bottom' }}
>
<motion.div
animate={{
scaleY: [1, 0.85, 1.15, 1], // Squash-stretch
scaleX: [1, 1.15, 0.85, 1]
}}
transition={{
duration: 0.6,
repeat: Infinity,
times: [0, 0.5, 0.55, 1]
}}
/>
</motion.div>
);
Glitch Style
Digital distortion and chromatic effects.
CSS:
.glitch-container {
position: relative;
}
/* RGB split */
.glitch-text {
position: relative;
}
.glitch-text::before,
.glitch-text::after {
content: attr(data-text);
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
}
.glitch-text::before {
color: cyan;
left: 3px; /* {motion.stylization.glitch.chromatic-offset} */
animation: glitchLeft 0.1s infinite linear alternate-reverse;
mix-blend-mode: multiply;
}
.glitch-text::after {
color: magenta;
left: -3px;
animation: glitchRight 0.1s infinite linear alternate-reverse;
mix-blend-mode: multiply;
}
@keyframes glitchLeft {
0% { clip-path: inset(20% 0 60% 0); }
20% { clip-path: inset(40% 0 10% 0); }
40% { clip-path: inset(60% 0 30% 0); }
60% { clip-path: inset(10% 0 80% 0); }
80% { clip-path: inset(50% 0 20% 0); }
100% { clip-path: inset(30% 0 50% 0); }
}
@keyframes glitchRight {
0% { clip-path: inset(60% 0 20% 0); }
20% { clip-path: inset(10% 0 40% 0); }
40% { clip-path: inset(30% 0 60% 0); }
60% { clip-path: inset(80% 0 10% 0); }
80% { clip-path: inset(20% 0 50% 0); }
100% { clip-path: inset(50% 0 30% 0); }
}
JavaScript (random glitch):
function glitchEffect(element) {
const frequency = 3; // {motion.stylization.glitch.frequency}
const duration = 100; // {motion.stylization.glitch.duration}
setInterval(() => {
if (Math.random() < 0.3) {
const offsetX = (Math.random() - 0.5) * 20; // {motion.stylization.glitch.offset-x}
const offsetY = (Math.random() - 0.5) * 10; // {motion.stylization.glitch.offset-y}
element.style.transform = `translate(${offsetX}px, ${offsetY}px)`;
element.style.filter = `hue-rotate(${Math.random() * 360}deg)`;
setTimeout(() => {
element.style.transform = '';
element.style.filter = '';
}, duration);
}
}, 1000 / frequency);
}
Remotion Implementation
import { useCurrentFrame, interpolate, Easing } from 'remotion';
// Vintage style composition
export const VintageStyle: React.FC = () => {
const frame = useCurrentFrame();
// Film grain animation
const grainOffset = frame % 4;
// Subtle flicker
const flicker = 1 - (Math.random() * 0.05); // {motion.stylization.vintage.flicker-intensity}
return (
<div style={{
filter: `sepia(0.6) contrast(1.1) brightness(${flicker})`,
position: 'relative'
}}>
{/* Grain overlay */}
<div style={{
position: 'absolute',
inset: 0,
backgroundImage: 'url(grain.png)',
backgroundPosition: `${grainOffset * 25}% ${grainOffset * 25}%`,
opacity: 0.15,
mixBlendMode: 'overlay'
}} />
{/* Content */}
{/* ... */}
{/* Vignette */}
<div style={{
position: 'absolute',
inset: 0,
boxShadow: 'inset 0 0 100px rgba(0,0,0,0.3)'
}} />
</div>
);
};
Reduced Motion
For prefers-reduced-motion: reduce:
| Full Style | Reduced Alternative |
|---|---|
| Vintage flicker | Static sepia filter |
| Organic wobble | No motion, static position |
| Glitch effects | Disabled entirely |
| Playful bounce | Simple scale or no effect |
Implementation:
@media (prefers-reduced-motion: reduce) {
.vintage-flicker,
.organic-wobble,
.glitch-text::before,
.glitch-text::after {
animation: none;
}
.playful-bounce {
transition: transform 0.1s ease-out;
}
.playful-bounce:hover {
transform: scale(1.02);
}
}
Cross-References
- See
color/for color grading within styles - See
distortion/for glitch effect details - See
blur/for motion blur in cinematic style - See
easing/for style-specific timing curves - See
particles/for styled particle effects - See
typography/for text animation styles
References
- No additional references needed for this skill