Files
neo 76c4c5b4fe design-systems: add 74 brand DESIGN.md files (VoltAgent/awesome-design-md)
- 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
2026-06-21 20:40:35 +05:00

4.0 KiB
Raw Permalink Blame History

name, title, description, tags, source
name title description tags source
motion/magnetic-button Magnetic Button Effect Buttons that follow cursor within radius. Creates premium tactile feel.
motion
magnetic
button
micro-interaction
premium
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

Реализация

<button class="magnetic-btn">Hover me</button>
.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);
}
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);
});

Варианты силы притяжения

// Слабое (для текста)
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

if (window.matchMedia('(pointer: fine)').matches) {
  document.querySelectorAll('.magnetic-btn').forEach(el => {
    new MagneticButton(el);
  });
}
// На мобильных — просто hover-эффект

CV Service Validation

# Проверить 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.