8.4 KiB
8.4 KiB
name, title, description, tags, source
| name | title | description | tags | source | ||||||
|---|---|---|---|---|---|---|---|---|---|---|
| layout/sticky-feature | Sticky Feature Section (Scroll Reveal) | Text scrolls while image/visual stays pinned. Classic premium pattern from Stripe, Linear, Apple. |
|
stripe.com, linear.app, apple.com, raycast.com |
Sticky Feature Section
Что это
Секция где текст скроллится, а визуальный элемент (скриншот, видео, анимация) остаётся прилипшим (sticky). Классический паттерн Stripe.com и Apple.
Ключевые параметры
| Параметр | Значение |
|---|---|
min-height |
100vh |
sticky top |
0 или 100px |
padding |
128px 0 |
gap between text blocks |
80–120px |
scroll duration |
100–200vh (время просмотра) |
Pattern 1: Stripe-Style (text left, sticky image right)
<section class="feature-sticky">
<div class="feature-sticky-container">
<!-- Sticky visual -->
<div class="feature-sticky-visual">
<img src="/feature-demo.png" alt="Feature demo" loading="lazy">
</div>
<!-- Scrolling text -->
<div class="feature-sticky-texts">
<div class="feature-block">
<span class="feature-number">01</span>
<h3>Молниеносная скорость</h3>
<p>Загрузка страниц менее чем за 100мс. Оптимизированный движок, CDN по всему миру.</p>
</div>
<div class="feature-block">
<span class="feature-number">02</span>
<h3>Умная аналитика</h3>
<p>AI-анализ данных в реальном времени. Прогнозирование трендов и автоматические отчёты.</p>
</div>
<div class="feature-block">
<span class="feature-number">03</span>
<h3>Безопасность данных</h3>
<p>Шифрование end-to-end. Соответствие GDPR, PCI DSS, SOC 2 Type II.</p>
</div>
</div>
</div>
</section>
.feature-sticky {
min-height: 100vh;
padding: 128px 0;
background: var(--color-bg);
}
.feature-sticky-container {
max-width: 1200px;
margin: 0 auto;
padding: 0 var(--space-6);
display: grid;
grid-template-columns: 1fr 1fr;
gap: var(--space-16);
align-items: start;
}
/* Sticky visual */
.feature-sticky-visual {
position: sticky;
top: 100px;
height: 500px;
border-radius: 20px;
overflow: hidden;
background: var(--color-surface);
border: 1px solid var(--color-border);
}
.feature-sticky-visual img {
width: 100%;
height: 100%;
object-fit: cover;
}
/* Scrolling text blocks */
.feature-block {
padding: 64px 0;
border-bottom: 1px solid var(--color-border);
}
.feature-block:first-child {
padding-top: 0;
}
.feature-block:last-child {
border-bottom: none;
}
.feature-number {
display: block;
font-size: 14px;
font-weight: 700;
color: var(--color-accent);
letter-spacing: 0.1em;
margin-bottom: 16px;
}
.feature-block h3 {
font-size: 2rem;
font-weight: 700;
margin-bottom: 16px;
}
.feature-block p {
font-size: 1.125rem;
color: var(--color-text-secondary);
line-height: 1.7;
max-width: 400px;
}
/* Responsive */
@media (max-width: 768px) {
.feature-sticky-container {
grid-template-columns: 1fr;
}
.feature-sticky-visual {
position: relative;
top: 0;
height: 300px;
margin-bottom: 48px;
}
}
Pattern 2: Apple-Style (image center, text fades in)
<section class="feature-fade">
<div class="feature-fade-visual">
<img src="/product-shot.png" alt="Product" loading="lazy">
</div>
<div class="feature-fade-texts">
<div class="feature-fade-block" data-target="1">
<h3>Тонкий дизайн</h3>
<p>Всего 6.1мм толщина.</p>
</div>
<div class="feature-fade-block" data-target="2">
<h3>Мощный чип</h3>
<p>A17 Pro — самый быстрый в смартфоне.</p>
</div>
<div class="feature-fade-block" data-target="3">
<h3>Камера 48MP</h3>
<p>Детали, которые вы не увидите.</p>
</div>
</div>
</section>
.feature-fade {
min-height: 300vh; /* 3 экрана скролла */
position: relative;
}
.feature-fade-visual {
position: sticky;
top: 50%;
transform: translateY(-50%);
height: 70vh;
display: flex;
align-items: center;
justify-content: center;
}
.feature-fade-visual img {
max-height: 100%;
max-width: 100%;
object-fit: contain;
}
.feature-fade-block {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
text-align: center;
opacity: 0;
transition: opacity 0.3s ease;
}
.feature-fade-block.active {
opacity: 1;
}
.feature-fade-block h3 {
font-size: 3rem;
font-weight: 800;
margin-bottom: 16px;
}
.feature-fade-block p {
font-size: 1.25rem;
color: var(--color-text-secondary);
}
// Intersection Observer для fade-эффекта
const blocks = document.querySelectorAll('.feature-fade-block');
const visual = document.querySelector('.feature-fade-visual');
const container = document.querySelector('.feature-fade');
if (blocks.length && container) {
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
blocks.forEach(block => block.classList.remove('active'));
if (entry.isIntersecting) {
entry.target.classList.add('active');
}
});
}, {
root: container,
rootMargin: '0px',
threshold: 0.5
});
blocks.forEach(block => observer.observe(block));
}
Pattern 3: GSAP ScrollTrigger (scroll-based animations)
import gsap from 'gsap';
import { ScrollTrigger } from 'gsap/ScrollTrigger';
gsap.registerPlugin(ScrollTrigger);
// Image zoom on scroll
gsap.to('.feature-sticky-visual img', {
scale: 1.1,
scrollTrigger: {
trigger: '.feature-sticky',
start: 'top bottom',
end: 'bottom top',
scrub: 1
}
});
// Text color change on scroll
gsap.utils.toArray('.feature-block').forEach((block, i) => {
gsap.from(block.querySelector('h3'), {
opacity: 0,
y: 40,
duration: 0.8,
scrollTrigger: {
trigger: block,
start: 'top 80%',
toggleActions: 'play none none reverse'
}
});
});
Pattern 4: Horizontal Scroll Cards
<section class="horizontal-scroll">
<div class="horizontal-scroll-track">
<div class="scroll-card">
<h3>Feature 1</h3>
<p>Description</p>
</div>
<div class="scroll-card">
<h3>Feature 2</h3>
<p>Description</p>
</div>
<div class="scroll-card">
<h3>Feature 3</h3>
<p>Description</p>
</div>
</div>
</section>
.horizontal-scroll {
overflow: hidden;
padding: 128px 0;
}
.horizontal-scroll-track {
display: flex;
gap: 24px;
padding: 0 24px;
width: max-content;
}
.scroll-card {
min-width: 400px;
padding: 48px;
background: var(--color-surface);
border: 1px solid var(--color-border);
border-radius: 20px;
}
.scroll-card h3 {
font-size: 2rem;
font-weight: 700;
margin-bottom: 16px;
}
Anti-patterns
| ❌ Плохо | ✅ Хорошо |
|---|---|
top: 0 — прилипает к самому верху |
top: 100px — отступ от навигации |
min-height: 200vh — слишком длинно |
min-height: 100–200vh |
Без position: sticky fallback |
position: relative на мобильных |
Text без max-width |
max-width: 400px для читаемости |
| Нет scroll-анимации | GSAP ScrollTrigger или CSS scrub |
Mobile Considerations
@media (max-width: 768px) {
/* Sticky не работает так же на мобильных */
.feature-sticky-visual {
position: relative;
top: 0;
height: 300px;
}
/* Уменьшаем секцию */
.feature-sticky {
padding: 64px 0;
}
}
CV Service Validation
# Проверить UX heuristics sticky секции
curl -s -X POST http://localhost:8001/design/analyze-dom \
-H "Content-Type: application/json" \
-d '{"html": "..."}' | python3 -c "
import sys, json
d = json.load(sys.stdin)
ux = d['analyzers'].get('ux_heuristics', {})
print(f\"UX Heuristics: {ux.get('score', 'N/A')}/10\")
"
Ожидается: ux_heuristics > 6.