Hero Section Layout
Hero section is divided into three layers:
Page Layout Layer
Controls the overall direction of the page:
– mobile uses flex-col
– desktop switches to md:flex-row
This determines whether the Hero appears above or beside the secondary content — in this case, the GitHub Grid.
Hero Component Layer
Maintains a consistent internal structure using flex-col, so the name, line, role, description, and other elements stack vertically regardless of screen size.
Button Group Layer
Uses its own breakpoint (flex-col → sm:flex-row) to adjust button layout without affecting the rest of the component.
flex-col
flex-col)
flex-col)
md:flex-row
flex-col; flex-1
flex-1
In this layout, the page controls overall direction, the Hero controls its own structure, and the buttons handle their own breakpoint
Best Pratice for transform in absolute container
- Why need
transformwhen there aretoporleft
Without transform
The element’s left edge is aligned to the target position, so it appears shifted to the right.
/* Container */
.parent {
position: relative;
}
/* Center target (incorrect) */
.target {
position: absolute;
top: 50%;
left: 50%;
}
With transform: translateX(-50%)
The element’s center aligns to the target position, so it stays visually centered no matter the width.
/* Container */
.parent {
position: relative;
}
/* Center target (correct) */
.target {
position: absolute;
top: 50%;
left: 50%;
transform: translateX(-50%);
transform: translateY(-50%)
}
2. Best practice when using <motion.div>
motion.div uses the transform property to animate movement, scaling, rotation, and other effects.
During animation, Framer Motion rewrites the entire transform string, not just part of it.
Because of this:
Any layout transform you apply (like translateX(-50%)) will be overwritten by Framer Motion’s transform updates.
This is why the layout transform must live on an outer <div>, and the inner motion.div should handle animation only.
For example,
<motion.div
initial={{ y: 40 }}
animate={{ y: 0 }}
/>
What the browser actually renders in the DOM
<div style="transform: translateY(40px);"></div>
During the animation, Framer Motion continuously updates the transform value
transform: translateY(32px);
transform: translateY(20px);
transform: translateY(10px);
transform: translateY(0px);
Therefore, translateY will be overwritten over and over again.
The better practice is to wrap motion.div with a div container:
{/* Layout container (keeps transform untouched) */}
<div className="absolute left-1/2 -translate-x-1/2">
{/* Framer Motion only applies animation */}
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
>
Centered badge
</motion.div>
</div>
Summary
- Page-Hero component: buttons can control each stack independently
- Centering uses a combination of
left/topwithtransform -
motion.divis wrapped inside a container div to prevent transform override and keep items centered
Leave a Reply