Pinterest Transition
Immersive transition with cards expanding to fill the screen
Pinterest Transition
The Pinterest transition is an expansion transition where small gallery items expand to full screen. The selected content enlarges to fill the entire screen, providing powerful immersion and visual continuity.
Demo
Loading demo...
UX Principles
When to Use?
Pinterest transitions are used for visual content exploration with expansion.
Content Relationship Suitability
콘텐츠 관계 | 적합성 | 설명 |
---|---|---|
Unrelated Content | ❌ | Unsuitable without expansion connection point |
Sibling Relationship | ⚠️ | Considerable for navigation within gallery items |
Hierarchical Relationship | ✅ | Optimal for expanded views like thumbnail→fullview |
Key Use Cases
- Image Gallery: Expanding from thumbnails to high-resolution images
- Product Catalog: Expanding from product grid to detail information
- Media Browsing: Transitioning from video thumbnails to player
- Portfolio: From work list to fullscreen presentation
Why Does It Work This Way?
- Spatial Expansion Metaphor: Feeling of entering a larger world through a small window
- Natural Focus Shift: User's gaze naturally follows the expanding element
- Context Preservation: Clear origin maintains spatial orientation
- Dramatic Effect: Expansion animation emphasizes content importance
Motion Design
Expansion Process:
1. Capture selected card position and size
2. Card moves to center while scaling up
3. Background fades and scales simultaneously
4. Finally fills entire screen
Contraction Process:
1. Detail screen shrinks back to original position
2. Background reappears restoring gallery
3. Settles with natural spring animation
Basic Usage
1. Transition Setup
import { Ssgoi } from '@ssgoi/react';
import { pinterest } from '@ssgoi/react/view-transitions';
const config = {
transitions: [
{
from: '/gallery',
to: '/gallery/*',
transition: pinterest(),
symmetric: true
}
]
};
export default function App() {
return (
<Ssgoi config={config}>
{/* App content */}
</Ssgoi>
);
}
2. Element Marking
Use different data attributes for gallery and detail pages:
// Gallery Page
function Gallery() {
return (
<div className="masonry-grid">
{items.map(item => (
<Link
key={item.id}
to={`/gallery/${item.id}`}
data-pinterest-gallery-key={item.id}
className="gallery-item"
>
<img src={item.image} alt={item.title} />
<h3>{item.title}</h3>
</Link>
))}
</div>
);
}
// Detail Page
function ItemDetail({ item }) {
return (
<div
data-pinterest-detail-key={item.id}
className="detail-container"
>
<button onClick={() => navigate('/gallery')}>
✕ Close
</button>
<img src={item.image} alt={item.title} />
<article>
<h1>{item.title}</h1>
<p>{item.description}</p>
</article>
</div>
);
}
Practical Examples
1. Masonry Gallery
Pinterest-style masonry layout:
// Masonry Grid
function MasonryGallery() {
return (
<div className="columns-2 md:columns-3 lg:columns-4 gap-4">
{images.map((img, index) => (
<div
key={img.id}
data-pinterest-gallery-key={img.id}
onClick={() => navigate(`/image/${img.id}`)}
className="break-inside-avoid mb-4 cursor-pointer
hover:scale-105 transition-transform"
>
<img
src={img.url}
alt={img.title}
className="w-full rounded-lg"
/>
<div className="p-2">
<p className="text-sm font-medium">{img.title}</p>
<p className="text-xs text-gray-500">{img.author}</p>
</div>
</div>
))}
</div>
);
}
// Expanded View
function ImageView({ image }) {
return (
<div
data-pinterest-detail-key={image.id}
className="fixed inset-0 bg-white z-50 overflow-auto"
>
<div className="max-w-4xl mx-auto p-4">
<img
src={image.url}
alt={image.title}
className="w-full rounded-lg shadow-xl"
/>
<div className="mt-4">
<h1 className="text-2xl font-bold">{image.title}</h1>
<p className="text-gray-600 mt-2">{image.description}</p>
</div>
</div>
</div>
);
}
2. Product Catalog
Product card expanding to detail page:
// Product Grid
function ProductGrid() {
return (
<div className="grid grid-cols-2 md:grid-cols-4 gap-6">
{products.map(product => (
<article
key={product.id}
data-pinterest-gallery-key={product.id}
onClick={() => navigate(`/product/${product.id}`)}
className="bg-white rounded-xl shadow-lg overflow-hidden
cursor-pointer hover:shadow-xl transition-shadow"
>
<img
src={product.image}
alt={product.name}
className="w-full h-48 object-cover"
/>
<div className="p-4">
<h3 className="font-semibold">{product.name}</h3>
<p className="text-lg font-bold mt-2">${product.price}</p>
</div>
</article>
))}
</div>
);
}
// Product Detail
function ProductDetail({ product }) {
return (
<div
data-pinterest-detail-key={product.id}
className="min-h-screen bg-white"
>
<div className="grid md:grid-cols-2 gap-8 p-8">
<img
src={product.image}
alt={product.name}
className="w-full rounded-lg"
/>
<div>
<h1 className="text-3xl font-bold">{product.name}</h1>
<p className="text-2xl font-bold mt-4">${product.price}</p>
<p className="mt-4 text-gray-600">{product.description}</p>
<button className="mt-6 bg-black text-white px-8 py-3 rounded-lg">
Add to Cart
</button>
</div>
</div>
</div>
);
}
Customization
Spring Settings
Adjust animation elasticity and speed:
pinterest({
spring: {
stiffness: 200, // Lower for smoother motion
damping: 25 // Higher for faster settling
}
})
Important Notes
Key Attribute Distinction
- Gallery:
data-pinterest-gallery-key
- Detail:
data-pinterest-detail-key
- Must use different attribute names (to differentiate from hero)
Layout Considerations
- Gallery items should have static position
- Detail pages need sufficient padding
- Works especially well with masonry layouts
Accessibility
- ESC key support to close detail page
- Focus trap for improved keyboard navigation
- Instant transition when motion is reduced
Best Practices
✅ DO
- Use for image-centric content
- Apply to gallery or catalog formats
- Provide clear close button
- Ensure responsive design for mobile
❌ DON'T
- Unsuitable for text-heavy content
- Avoid too many elements expanding simultaneously
- Avoid use when detail page loading is slow
- May cause unexpected behavior in complex layouts