CSS Hover State with Custom Easing: Implementation Guide and Code Examples
Apply a transition with a cubic-bezier() timing function to the base element selector to create bidirectional hover animations that feel smooth and intentional.
The emilkowalski/skills repository structures frontend knowledge as discrete, reusable "skills" documented in Markdown. According to the patterns established in skills/animate/SKILL.md and skills/animate/RECIPES.md, implementing a CSS hover state with custom easing requires declaring your transition properties on the base selector—not the :hover state—so the browser can animate both the entrance and exit phases using your specified easing curve.
How CSS Hover Transitions Work
The Four-Step Foundation
- Define the default style – Establish the element’s baseline appearance (size, color, position).
- Add a
:hoverrule – Specify the visual change you want, such astransform: scale(1.08)orbackground-color: #fff. - Apply a
transition– Instruct the browser to animate property changes over a specific duration. - Choose a custom easing function – Use
cubic-bezier(x1, y1, x2, y2)to craft motion curves that match your design language.
Because the transition is declared on the base selector, the browser interpolates values in both directions—entering and leaving hover—ensuring no flicker or abrupt jumps.
Why Custom Easing Matters
- Brand consistency – A unique cubic-bezier curve defines motion as distinctly as a color palette or typeface.
- Perceived performance – An "ease-out-back" curve can make a 250ms animation feel snappier than a linear 400ms transition.
- Fine-grained control – Create anticipation, overshoot, or elastic effects without JavaScript libraries.
Practical Code Examples
Simple Hover Scale with Overshoot
This example from the repository's animation patterns creates a button that scales up with a bouncy overshoot effect:
.button {
display: inline-block;
padding: .6rem 1.2rem;
background: #0066ff;
color: #fff;
border-radius: .4rem;
text-align: center;
/* Base transition – everything that changes on hover */
transition: transform 250ms cubic-bezier(0.68, -0.55, 0.265, 1.55);
}
.button:hover {
transform: scale(1.08);
}
Result: When the cursor enters, the button enlarges with a lively overshoot; when the cursor leaves, it returns smoothly along the same curve.
Multi-Property Card Hover
For complex UI elements like cards, transition multiple properties simultaneously using consistent easing:
.card {
background: #fafafa;
border: 1px solid #e0e0e0;
border-radius: .5rem;
box-shadow: 0 2px 4px rgba(0,0,0,.1);
transition:
background-color 200ms cubic-bezier(0.4, 0, 0.2, 1),
box-shadow 200ms cubic-bezier(0.4, 0, 0.2, 1),
transform 200ms cubic-bezier(0.4, 0, 0.2, 1);
}
.card:hover {
background-color: #fff;
box-shadow: 0 8px 16px rgba(0,0,0,.12);
transform: translateY(-4px);
}
Result: The card lifts, brightens, and deepens its shadow with Material Design-compatible easing defined by cubic-bezier(0.4, 0, 0.2, 1).
Reusable Easing with CSS Variables
To maintain consistency across large projects, define your easing curves as CSS variables. This pattern aligns with the standardization approach found in skills/review-animations/STANDARDS.md:
:root {
--easing-soft: cubic-bezier(0.25, 0.1, 0.25, 1);
}
/* Apply the variable wherever a transition is needed */
.nav-link {
color: #333;
transition: color 180ms var(--easing-soft);
}
.nav-link:hover {
color: #0066ff;
}
Result: Centralizing the curve in :root allows you to adjust the motion feel site-wide by changing a single value.
Repository Integration
To contribute a dedicated "Hover-Easing" skill to the emilkowalski/skills collection, follow the established folder structure demonstrated in skills/improve-animations/AUDIT.md:
skills/
└─ hover-easing/
├─ SKILL.md ← High-level description: "Create smooth hover states with custom cubic-bezier curves"
├─ RECIPES.md ← Ready-to-copy CSS examples (like those above)
└─ REFERENCES.md ← Links to cubic-bezier visualization tools and design system docs
Reference the core principles in skills/animate/SKILL.md when documenting your implementation, and audit your motion against the checklist in skills/improve-animations/AUDIT.md to ensure accessibility and performance standards.
Summary
- Declare transitions on the base selector, not the
:hoverpseudo-class, to ensure smooth bidirectional animation. - Use
cubic-bezier(x1, y1, x2, y2)instead of keywords likeeaseorlinearfor brand-specific motion curves. - Apply CSS variables for easing values to maintain consistency across components and simplify global updates.
- Reference
skills/animate/RECIPES.mdin the emilkowalski/skills repository for battle-tested animation patterns and integration guidelines.
Frequently Asked Questions
How does cubic-bezier work in CSS transitions?
The cubic-bezier(x1, y1, x2, y2) function defines a Bézier curve where the x-axis represents time progression (0 to 1) and the y-axis represents the animation's progress (0 to 1). The four values control the curve's control points, allowing you to create custom acceleration and deceleration patterns that standard keywords cannot achieve.
Should I declare the transition property on the base element or the :hover state?
Always declare transition on the base element selector. When placed on :hover, the transition only applies when entering the hover state, causing an abrupt jump when the mouse leaves. Declaring it on the base ensures the browser animates both entering and exiting phases using your specified duration and easing.
What are common cubic-bezier values for UI animations?
Standard smooth: cubic-bezier(0.4, 0, 0.2, 1) (Material Design standard), Soft ease: cubic-bezier(0.25, 0.1, 0.25, 1), and Overshoot/bounce: cubic-bezier(0.68, -0.55, 0.265, 1.55). Values outside the 0-1 range for the y-axis create anticipation or overshoot effects.
How do I maintain consistent easing across a large project?
Define your easing curves as CSS custom properties (variables) in :root, as shown in skills/review-animations/STANDARDS.md. Reference these variables in your transition declarations rather than hardcoding values, allowing you to update the motion system globally by editing a single line.
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 →