Scale & Rotate
A box that scales up 10% and rotates 5° on hover in 0.4s — cubic-bezier with slight overshoot, violet box-shadow, pure CSS no JS.
Updated
This effect is free — the Hover category contains 48 effects total, including 5 free. Effect.Labs has 840 vanilla effects. Explore the category →
Usage examples
How it works
The .fx-0343-root wrapper (250 px tall) centres a 200 px button. On hover, transform: scale(1.1) rotate(5deg) enlarges it by 10% and rotates it 5° clockwise. The transition runs for 0.4 s using cubic-bezier(0.175, 0.885, 0.32, 1.275): fast start, slight overshoot at the end, then a ~2° elastic snap back. A violet box-shadow (rgba(99, 102, 241, 0.3)) appears at the same time, adding depth.
transition: all covers every CSS property on the element. To limit animation to only the relevant properties, replace all with transform 0.4s, box-shadow 0.4s. Pure CSS, no JavaScript. Both :hover and :focus-visible trigger the same visual, and focusability comes from the button element.
Accessibility
- Reduced motion: under
prefers-reduced-motion: reduce, all transitions are disabled and thetransformis suppressed on hover — the element stays at its original size and angle. The violetbox-shadowstill appears instantly to signal focus or hover. - Keyboard: the element is a natively focusable
<button>. The:focus-visiblerule triggers the same scale and rotation as hover, with an indigo 2 px outline added. The rotation may be surprising: replace the target with a real link or button in your interface for clear context. - Touch: on touchscreens, a tap activates the CSS hover state in most mobile browsers. The effect fades when the finger lifts or focus moves elsewhere.
- Rotation and screen readers: the rotation is purely visual. Screen readers read the element text without regard to the transform. Use a descriptive label if you adapt this effect.
Browser compatibility
Pure CSS, no dependency. Properties used: 2D transform, transition, box-shadow, cubic-bezier, :focus-visible. Verified in Chromium and Firefox: same render, elastic overshoot visible, two independent copies, 0 JS errors. :focus-visible requires Chrome 86+, Firefox 85+, Safari 15.4+ — on older versions, keyboard focus does not trigger the visual effect.
Without :focus-visible (browsers before 2021), keyboard focus does not trigger the visual effect — the element stays readable and clickable, the animation is absent for keyboard users only.
The code
Copy the two blocks (HTML and CSS) into your page. No JavaScript, no dependencies.
<div class="fx-0343-root">
<button class="hover-box hover-scale-rotate" type="button">
<span>Scale & Rotate</span>
</button>
</div>
.fx-0343-root,
.fx-0343-root *,
.fx-0343-root *::before,
.fx-0343-root *::after {
box-sizing: border-box
}
.fx-0343-root {
display: flex;
align-items: center;
justify-content: center;
width: 100%;
height: 250px;
overflow: hidden;
position: relative;
background: rgb(26, 26, 36);
color: rgb(255, 255, 255);
font-family: sans-serif;
}
/* Site design system variables this code depends on (values from css/style.css — adapt freely). */
.fx-0343-root {
--bg-card: #12121a;
--border: rgba(255, 255, 255, 0.08);
--radius-xl: 1rem;
}
.fx-0343-root .hover-box {
width: 200px;
height: 200px;
background: var(--bg-card);
border: 1px solid var(--border);
border-radius: var(--radius-xl);
display: flex;
align-items: center;
justify-content: center;
font-weight: 600;
cursor: pointer;
position: relative;
overflow: hidden;
font: inherit;
color: inherit;
-webkit-appearance: none;
appearance: none;
}
.fx-0343-root .hover-scale-rotate {
transition: all 0.4s cubic-bezier(0.175, 0.885, 0.32, 1.275);
}
.fx-0343-root .hover-scale-rotate:hover {
transform: scale(1.1) rotate(5deg);
box-shadow: 0 20px 40px rgba(99, 102, 241, 0.3);
}
.fx-0343-root .hover-scale-rotate:focus-visible {
transform: scale(1.1) rotate(5deg);
box-shadow: 0 20px 40px rgba(99, 102, 241, 0.3);
outline: 2px solid #6366f1;
outline-offset: 3px;
}
.fx-0343-root .hover-box span {
position: relative;
z-index: 1;
}
@media (prefers-reduced-motion: reduce) {
.fx-0343-root * {
transition: none !important;
animation-play-state: paused !important;
}
.fx-0343-root .hover-scale-rotate:hover,
.fx-0343-root .hover-scale-rotate:focus-visible {
transform: none;
}
}
Pure CSS — no JavaScript needed.
Customize
Options passed to the API or data-* attributes:
| Option / property | Default | Effect |
|---|---|---|
Scale factor (CSS — scale(1.1)) |
1.1 (10%) | scale(1.05) is more subtle; scale(1.15) more pronounced. Above 1.2 on a 390 px screen, edges may overflow the wrapper. |
Rotation angle (CSS — rotate(5deg)) |
5° | Positive value: clockwise; negative: anticlockwise. 0 removes the rotation, leaving only the scale. |
Duration (CSS — 0.4s) |
0.4 s | 0.2 s is snappier; 0.6 s more relaxed. A longer duration smooths out the elastic overshoot from the curve. |
Easing curve (CSS — cubic-bezier(0.175, 0.885, 0.32, 1.275)) |
slight overshoot | ease-out removes the overshoot; cubic-bezier(0.68, -0.55, 0.265, 1.55) amplifies the spring. |
Box-shadow colour (CSS — rgba(99, 102, 241, 0.3)) |
indigo at 30% | Replace the colour to match your palette; set to 0 to remove the shadow. |
Targeted transition (CSS — all) |
all properties | Replace with transform 0.4s cubic-bezier(...), box-shadow 0.4s to limit animation to relevant properties and reduce CSS recalculation. |
FAQ
How do I remove the rotation and keep only the scale?
Replace transform: scale(1.1) rotate(5deg) with transform: scale(1.1). The elastic curve stays active, giving a pure bounce effect.
The effect overflows onto neighbouring elements in my grid. How do I contain it?
Add isolation: isolate on the parent container, or overflow: hidden if you accept that the shadow is clipped. z-index: 1 on the target ensures it renders above its neighbours.
On mobile, the effect sticks after a tap. How do I fix it?
Add a JavaScript touchend listener that removes focus: el.addEventListener('touchend', () => el.blur()). On most modern mobile browsers this is enough to release the :hover state.