Hero Transition
Transitions maintaining context through shared element continuity
Hero Transition
The hero transition is a continuity transition where shared elements between two screens naturally move and transform. Elements "travel" from one screen to another, providing users with strong visual connectivity and context.
Demo
Loading demo...
UX Principles
When to Use?
Hero transitions are used when continuity and context preservation are important.
Content Relationship Suitability
콘텐츠 관계 | 적합성 | 설명 |
---|---|---|
Unrelated Content | ❌ | Unsuitable when there are no shared elements |
Sibling Relationship | ⚠️ | Effective in specific situations like galleries |
Hierarchical Relationship | ✅ | Optimal for different views of the same object (list→detail) |
Key Use Cases
- Gallery → Fullscreen: Image expands to detailed view
- Product List → Detail: Product card expands to detail page
- Profile → Edit: Profile photo maintains while transitioning to edit mode
- Media Player: Mini player expands to full player
Why Does It Work This Way?
- Visual Continuity: Track element movement to maintain spatial orientation
- Spatial Relationship: From small to large, summary to detail
- Minimize Cognitive Load: Natural transformation instead of abrupt changes
- Interest and Delight: Dynamic motion increases user engagement
Motion Design
Hero Element Journey:
1. Starting Point → Record position, size, shape
2. During Transform → Smooth interpolation animation
3. Destination → Settle into new position and size
Other Elements:
- Outgoing Page: Fade out
- Incoming Page: Fade in after hero element settles
Basic Usage
1. Transition Setup
import { Ssgoi } from '@ssgoi/react';
import { hero } from '@ssgoi/react/view-transitions';
const config = {
transitions: [
{
from: '/gallery',
to: '/gallery/*',
transition: hero(),
symmetric: true
}
]
};
export default function App() {
return (
<Ssgoi config={config}>
{/* App content */}
</Ssgoi>
);
}
2. Marking Elements
Add data-hero-key
attribute to shared elements:
// List Page
function ProductList() {
return (
<div className="grid">
{products.map(product => (
<Link key={product.id} to={`/product/${product.id}`}>
<img
data-hero-key={`product-${product.id}`}
src={product.image}
alt={product.name}
/>
<h3>{product.name}</h3>
</Link>
))}
</div>
);
}
// Detail Page
function ProductDetail({ product }) {
return (
<div>
<img
data-hero-key={`product-${product.id}`}
src={product.image}
alt={product.name}
className="w-full"
/>
<h1>{product.name}</h1>
<p>{product.description}</p>
</div>
);
}
Practical Examples
1. Image Gallery
Thumbnail to fullscreen expansion:
// Gallery Grid
function Gallery() {
return (
<div className="grid grid-cols-3 gap-4">
{images.map(img => (
<Link to={`/photo/${img.id}`} key={img.id}>
<div
data-hero-key={`photo-${img.id}`}
className="aspect-square overflow-hidden"
>
<img src={img.thumb} className="object-cover w-full h-full" />
</div>
</Link>
))}
</div>
);
}
// Fullscreen View
function PhotoView({ id }) {
return (
<div
data-hero-key={`photo-${id}`}
className="fixed inset-0 bg-black"
>
<img src={image.full} className="w-full h-full object-contain" />
</div>
);
}
2. Card Expansion
Small card to fullscreen detail:
// Card List
function CardGrid() {
return (
<div className="grid gap-6">
{items.map(item => (
<article
key={item.id}
data-hero-key={`card-${item.id}`}
onClick={() => navigate(`/item/${item.id}`)}
className="bg-white rounded-lg shadow-lg p-4 cursor-pointer"
>
<h3>{item.title}</h3>
<p>{item.summary}</p>
</article>
))}
</div>
);
}
// Expanded Detail
function ItemDetail({ item }) {
return (
<article
data-hero-key={`card-${item.id}`}
className="min-h-screen bg-white p-8"
>
<h1 className="text-4xl">{item.title}</h1>
<p className="text-lg">{item.fullContent}</p>
</article>
);
}
3. Multiple Hero Elements
Multiple elements transitioning simultaneously:
// Profile Card
function ProfileCard({ user }) {
return (
<div onClick={() => navigate(`/profile/${user.id}`)}>
<img
data-hero-key={`avatar-${user.id}`}
src={user.avatar}
className="w-16 h-16 rounded-full"
/>
<h3 data-hero-key={`name-${user.id}`}>
{user.name}
</h3>
</div>
);
}
// Profile Detail
function ProfileDetail({ user }) {
return (
<div>
<img
data-hero-key={`avatar-${user.id}`}
src={user.avatar}
className="w-32 h-32 rounded-full"
/>
<h1 data-hero-key={`name-${user.id}`}>
{user.name}
</h1>
</div>
);
}
Important Notes
Hero Key Rules
data-hero-key
must be unique on each page- Keys are strings and can be generated dynamically
- Only elements with the same key are connected
Performance Considerations
- Hero elements are cloned in the DOM for animation
- Too many hero elements impact performance
- Preload images to prevent flickering
Accessibility
- Instant transition when motion is reduced
- Focus management supports keyboard navigation
- Screen readers perceive as normal page transition
Best Practices
✅ DO
- Use for visually important elements
- Apply to different representations of the same object
- Maintain natural size/position changes
- Use to guide user attention
❌ DON'T
- Don't apply to too many elements simultaneously
- Don't connect unrelated elements
- Avoid extreme size changes (more than 10x)
- Use cautiously with text content