How to Use `translateY` with Percentages for Responsive Animations
translateY() percentage values are relative to the element's own height, enabling animations that automatically adapt to any screen size without JavaScript calculations.
When building responsive web animations, hard-coding pixel values creates brittle code that breaks across different viewports. The emilkowalski/skills repository demonstrates how percentage-based translations solve this problem elegantly. According to the design engineering guidelines in skills/emil-design-eng/SKILL.md, "percentage values in translate() are relative to the element's own size," making them the preferred approach for toast notifications, drawers, and sliding interfaces.
Understanding Percentage Values in translateY
The CSS transform: translateY() function interprets percentages differently than most other CSS properties. While top: 100% positions an element relative to its parent container, translateY(100%) moves an element relative to its own height.
This distinction is critical for responsive design. As documented in skills/review-animations/STANDARDS.md (lines 132-136): "translate percentages are relative to the element's own size — translateY(100%) moves by the element's height regardless of dimensions."
Common Percentage Patterns
| Value | Effect |
|---|---|
translateY(0) |
No vertical movement (natural position) |
translateY(100%) |
Move down by full element height |
translateY(-100%) |
Move up by full element height |
translateY(50%) |
Move down by half element height |
Responsive Toast Notification Animation
The skills/emil-design-eng/SKILL.md file (lines 348-355) specifically recommends percentage translations for toast animations. Here's the implementation pattern:
/* Fade-in and slide-up using translateY percentages */
.toast {
opacity: 0;
transform: translateY(100%); /* start off-screen (below) */
transition: opacity 300ms ease,
transform 300ms ease;
}
/* When the toast becomes visible */
.toast.show {
opacity: 1;
transform: translateY(0); /* slide to its natural position */
}
/* Fade-out and slide-down on hide */
.toast.hide {
opacity: 0;
transform: translateY(100%); /* slide back down */
}
This pattern ensures the toast always slides exactly its own height—whether it's a compact mobile notification or an expanded desktop message with multiple lines of text.
Drawer Component with Top-Edge Entry
For drawers or panels that enter from above the viewport, negative percentages create predictable behavior:
/* Drawer that slides in from the top */
.drawer {
transform: translateY(-100%); /* hidden above the viewport */
transition: transform 400ms ease;
}
/* Open state */
.drawer.open {
transform: translateY(0);
}
The translateY(-100%) positions the drawer entirely above its natural position, ready to slide down into view. Because it uses percentages, the hidden offset automatically matches whatever height the drawer renders at.
When to Use JavaScript Instead
For the rare cases requiring pixel-precision, you can manipulate translateY dynamically:
/* If you need to manipulate it via JS (e.g., for dynamic distances) */
function slide(element, distancePx) {
element.style.transform = `translateY(${distancePx}px)`;
}
/* Prefer percentage when you can – the above helper is for rare pixel-specific cases */
The repository guidelines emphasize reaching for percentages first. JavaScript calculations add complexity and often recalculate on resize events, whereas percentage translations respond automatically to layout changes.
Benefits of Percentage-Based translateY
According to the emilkowalski/skills documentation, this approach delivers three core advantages:
- Responsive behavior — animation distance scales with the element, not the viewport
- Simpler code — no need to compute pixel offsets in JavaScript
- Consistent visual flow — entry and exit animations mirror each other naturally
These benefits compound in component-based architectures where a single toast or drawer component renders across dozens of contexts and screen sizes.
Summary
translateYpercentages reference the element's own height, not its parent's dimensionstranslateY(100%)slides an element down by its full rendered height- The
skills/emil-design-eng/SKILL.mdandskills/review-animations/STANDARDS.mdfiles codify this as the standard for entry/exit animations - Percentage translations eliminate resize listeners and breakpoint-specific calculations
- Use pixel values only when external constraints require exact positioning
Frequently Asked Questions
Why does translateY(100%) behave differently than top: 100%?
translateY percentages reference the element's own dimensions, while top percentages reference the parent container. This distinction makes translateY ideal for self-contained animations where the movement distance should match the element's size. If you positioned a 60px-tall toast with top: 100% inside a 400px container, it would move 400px—far off screen. translateY(100%) moves it exactly 60px.
Can I use percentages with translateX the same way?
Yes—translateX percentages reference the element's own width. The same principles apply for horizontal animations. translateX(100%) moves an element right by its full width, translateX(-100%) moves it left. This is particularly useful for carousels, side panels, and swipe-to-dismiss patterns.
Do percentage translations work with CSS keyframe animations?
Absolutely. The skills/review-animations/SKILL.md file includes examples of @keyframes using translateY percentages. Keyframe percentages (0%, 100%) control the timeline, while translateY percentages control the spatial movement—both systems work independently.
What about translate3d for GPU acceleration?
You can combine percentages with 3D transforms: translate3d(0, 100%, 0) forces hardware acceleration while maintaining percentage-based responsiveness. This is the recommended pattern for mobile animations where smooth 60fps performance matters.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →