X Tutup
Skip to content

fix(button): run favorite icon animation only on click or keyboard activation.#8152

Open
tarunvashishth wants to merge 4 commits intopatternfly:mainfrom
tarunvashishth:fix/button-favorite-animation-on-load
Open

fix(button): run favorite icon animation only on click or keyboard activation.#8152
tarunvashishth wants to merge 4 commits intopatternfly:mainfrom
tarunvashishth:fix/button-favorite-animation-on-load

Conversation

@tarunvashishth
Copy link
Contributor

@tarunvashishth tarunvashishth commented Feb 18, 2026

Fixes #7894

  • Updated the favorite icon animation selector in src/patternfly/components/Button/button.scss:765 from .pf-m-favorited to .pf-m-favorited:focus.
  • Restricts the animation to focused favorited buttons, preventing unintended animation on initial load/non-focus states.

Summary by CodeRabbit

  • Style
    • Refined the Favorite button icon animation so it only runs when the button is favorited and then focused or actively pressed, aligning animation with user interactions.
    • Added an animation fill-mode setting and set the animation to be paused by default so the animation is defined but not played until triggered.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 18, 2026

Walkthrough

Added a CSS variable for animation-fill-mode and paused the favorited icon animation by default; the animation is set to run only during interaction (focus/active or when .pf-m-is-animating is present), preserving existing animation-name and duration declarations.

Changes

Cohort / File(s) Summary
Button component stylesheet
src/patternfly/components/Button/button.scss
Added --pf-c-button--m-favorited__icon--AnimationFillMode and applied it to animation-fill-mode. Set .pf-m-favorited .pf-c-button__icon to animation-play-state: paused by default. Added one-shot interaction rules to set animation-play-state: running for .pf-m-is-animating .pf-c-button__icon, .pf-m-favorited:focus-visible .pf-c-button__icon, .pf-m-favorited:focus:not(:focus-visible) .pf-c-button__icon, and .pf-m-favorited:active .pf-c-button__icon, keeping existing animation-name/animation-duration intact.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title follows conventional commit format with 'fix' prefix and clearly describes the main change: restricting favorite animation to click/keyboard activation.
Linked Issues check ✅ Passed The changes directly address issue #7894 by preventing animation on page load and restricting it to focus/interaction states, matching the expected behavior of no animation when already favorited.
Out of Scope Changes check ✅ Passed All changes are scoped to the button.scss file and directly address the favorite animation behavior issue, with no unrelated modifications present.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@patternfly-build
Copy link
Collaborator

patternfly-build commented Feb 18, 2026

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
src/patternfly/components/Button/button.scss (2)

768-772: ⚠️ Potential issue | 🟠 Major

Animation replays on every focus of an already-favorited button.

When an already-favorited button loses focus and is then focused again (e.g., via Tab navigation), the pf-m-favorited:focus selector transitions from non-matching → matching, which causes animation-name to go from unset back to #{$button}-icon-favorited — the animation-name property specifies the name of a keyframe animation, and loading or applying a new name causes the animation to execute. This means the "favorited" scale animation will fire repeatedly — once for every time the user Tabs back onto an already-favorited button — rather than only on the one-time transition into the favorited state.

The root cause is that :focus is a momentary state: as soon as focus leaves and returns, CSS sees the animation-name being "freshly applied" again, restarting the animation. This is the same mechanism that causes the original page-load bug; it's just gated by a different condition now.

A CSS-only workaround that avoids the replay problem is to keep animation-name permanently set on .pf-m-favorited .#{$button}__icon (so it never gets "re-applied"), and instead suppress it at the moment of initial render using animation-play-state:

💡 Alternative approach using animation-play-state
- // Favorite button will run an animation when favorited
- &.pf-m-favorited:focus .#{$button}__icon {
-   animation-name: #{$button}-icon-favorited;
-   animation-duration: var(--#{$button}--m-favorited__icon--AnimationDuration);
-   animation-timing-function: var(--#{$button}--m-favorited__icon--AnimationTimingFunction);
- }

+ // Favorite button runs an animation when transitioning to favorited
+ &.pf-m-favorited .#{$button}__icon {
+   animation-name: #{$button}-icon-favorited;
+   animation-duration: var(--#{$button}--m-favorited__icon--AnimationDuration);
+   animation-timing-function: var(--#{$button}--m-favorited__icon--AnimationTimingFunction);
+   animation-play-state: paused;
+ }
+
+ &.pf-m-favorited:focus .#{$button}__icon,
+ &.pf-m-favorited:active .#{$button}__icon {
+   animation-play-state: running;
+ }

Note: animation-play-state: paused keeps the animation-name always set (so it is never "re-applied"), while running only during an active focus/click interaction. Resuming a paused animation will start the animation from where it left off at the time it was paused, rather than starting over from the beginning of the animation sequence — so this still may not perfectly solve all scenarios. The most reliable fix for "play once on state change" requires a JavaScript animationend handler to clear the triggering class, as purely CSS-driven approaches cannot distinguish "first application" from "re-application."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/patternfly/components/Button/button.scss` around lines 768 - 772, The
animation is being re-applied on every focus because the rule sets
animation-name only for the :focus state; update the selectors so the
animation-name for the favorited icon is set permanently on the favorited state
(selector .pf-m-favorited .#{$button}__icon) and move the play control into a
separate property (use animation-play-state: paused by default and set to
running only on :focus/.pf-m-favorited:focus .#{$button}__icon) so the
animation-name is not re-applied when focus toggles; modify the rules around
.pf-m-favorited .#{$button}__icon and .pf-m-favorited:focus .#{$button}__icon to
implement this change.

768-772: ⚠️ Potential issue | 🟠 Major

Animation silently absent on mouse-click in macOS Safari.

On macOS, Chrome and Firefox will focus all interactive controls on click — including inputs, buttons, links, and even elements with tabindex="-1". Safari, on the other hand, does not focus buttons and links when clicked. The reason is that macOS didn't transfer focus to operating system and application buttons when they were clicked, and the stakeholders decided to be consistent with the operating system instead. The discussions have lasted 13 years and the team at Apple is adamant: buttons, links, and non-text inputs won't get focus.

Because the animation is now gated on :focus, macOS Safari users who click to favorite a button will never see the animation — the :focus pseudo-class simply isn't set on a button click in that browser. The PR title states the animation should fire "on click or keyboard activation", but :focus alone cannot fulfill the click case in Safari.

Adding :active to the selector partially addresses the click case (:active fires on mousedown in all browsers), though :active is only held during the press duration and animation-fill-mode: both would be needed to ensure the animation completes:

💡 Proposed addition of :active to cover mouse-click in all browsers
- &.pf-m-favorited:focus .#{$button}__icon {
+ &.pf-m-favorited:focus .#{$button}__icon,
+ &.pf-m-favorited:active .#{$button}__icon {
    animation-name: #{$button}-icon-favorited;
    animation-duration: var(--#{$button}--m-favorited__icon--AnimationDuration);
    animation-timing-function: var(--#{$button}--m-favorited__icon--AnimationTimingFunction);
+   animation-fill-mode: both;
  }

This does not fully resolve the replay-on-re-focus concern raised separately, and ultimately a JS-driven approach (toggling a one-shot class on click/keydown, then removing it on animationend) would be the most semantically correct solution.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/patternfly/components/Button/button.scss` around lines 768 - 772, The
animation is only triggered on :focus which Safari doesn't apply on click;
update the CSS selector for the favorited icon (the .pf-m-favorited rule that
targets .#{$button}__icon and sets
animation-name/animation-duration/animation-timing-function) to also match
:active (e.g. .pf-m-favorited:focus and .pf-m-favorited:active) so mouse clicks
in Safari trigger it, and add animation-fill-mode: both (or set the
corresponding --#{$button}--m-favorited__icon--AnimationFillMode variable) so
the animation completes after :active ends; consider a separate JS-driven
one-shot class if replay-on-refocus is required.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In `@src/patternfly/components/Button/button.scss`:
- Around line 768-772: The animation is being re-applied on every focus because
the rule sets animation-name only for the :focus state; update the selectors so
the animation-name for the favorited icon is set permanently on the favorited
state (selector .pf-m-favorited .#{$button}__icon) and move the play control
into a separate property (use animation-play-state: paused by default and set to
running only on :focus/.pf-m-favorited:focus .#{$button}__icon) so the
animation-name is not re-applied when focus toggles; modify the rules around
.pf-m-favorited .#{$button}__icon and .pf-m-favorited:focus .#{$button}__icon to
implement this change.
- Around line 768-772: The animation is only triggered on :focus which Safari
doesn't apply on click; update the CSS selector for the favorited icon (the
.pf-m-favorited rule that targets .#{$button}__icon and sets
animation-name/animation-duration/animation-timing-function) to also match
:active (e.g. .pf-m-favorited:focus and .pf-m-favorited:active) so mouse clicks
in Safari trigger it, and add animation-fill-mode: both (or set the
corresponding --#{$button}--m-favorited__icon--AnimationFillMode variable) so
the animation completes after :active ends; consider a separate JS-driven
one-shot class if replay-on-refocus is required.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/patternfly/components/Button/button.scss`:
- Around line 768-781: The current CSS uses &:focus and &:active on
.pf-m-favorited .#{$button}__icon to set animation-play-state: running, which
causes animations to play when tabbing to pre-favorited buttons; change the
implementation to toggle animation via a transient class added/removed by the
favoriting action instead: remove the &:focus and &:active animation-play-state
rules for .pf-m-favorited .#{$button}__icon and instead have the favoriting
click/keyboard handler on the button element add a one-shot class (suggested
name pf-m-is-animating) to the button, set animation-play-state: running in the
.pf-m-is-animating .#{$button}__icon rule in this stylesheet, and have the
handler remove pf-m-is-animating on the button after the icon’s animationend
event so only transitions from unfavorited→favorited trigger the animation.
- Around line 769-775: The CSS sets animation-play-state: paused on
.pf-m-favorited .#{$button}__icon which can freeze the icon at an intermediate
scale if focus is lost mid-animation; change that rule so the animation does not
remain paused — either remove the animation-play-state property or set it to
animation-play-state: running for the selector .pf-m-favorited
.#{$button}__icon, or alternatively scope paused only to a focus state (e.g.
.pf-m-favorited:focus .#{$button}__icon) so the favorited animation always
completes and does not remain stuck at scale 1.5.

Comment on lines +768 to 781
// Keep animation definition on favorited state so focus toggles don't reapply animation-name
&.pf-m-favorited .#{$button}__icon {
animation-name: #{$button}-icon-favorited;
animation-duration: var(--#{$button}--m-favorited__icon--AnimationDuration);
animation-play-state: paused;
animation-timing-function: var(--#{$button}--m-favorited__icon--AnimationTimingFunction);
animation-fill-mode: var(--#{$button}--m-favorited__icon--AnimationFillMode);
}

// Run animation while the favorited button is keyboard focused or actively pressed
&.pf-m-favorited:focus .#{$button}__icon,
&.pf-m-favorited:active .#{$button}__icon {
animation-play-state: running;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Animation fires on Tab navigation to already-favorited buttons, contradicting the linked issue's expected behavior.

Using :focus means the animation triggers every time a keyboard user tabs to a button that is already in the pf-m-favorited state — not just when the button transitions from unfavorited → favorited. A page with many pre-favorited items would play the animation for each as the user Tabs through them. This directly contradicts issue #7894's stated expectation:

"animation should occur only when transitioning from not-favorited to favorited (e.g., via click or keyboard activation)"

Root cause: CSS pseudo-classes cannot distinguish "focus gained because the user just favorited this button" from "focus gained because the user tabbed here." The only pure-CSS escape is :focus-visible, but that also fires on Tab.

Recommended fix (requires JS): Remove animation-play-state toggling from CSS pseudo-classes and instead have the click/keyboard handler on the button element briefly add a one-shot class (e.g., pf-m-is-animating) that sets animation-play-state: running, then remove it after animationend. This precisely scopes the animation to the favoriting action.

- // Run animation while the favorited button is keyboard focused or actively pressed
- &.pf-m-favorited:focus .#{$button}__icon,
- &.pf-m-favorited:active .#{$button}__icon {
-   animation-play-state: running;
- }
+ // Run animation only when the is-animating class is added (e.g., on click/keyboard activation)
+ &.pf-m-favorited.pf-m-is-animating .#{$button}__icon {
+   animation-play-state: running;
+ }

If a pure-CSS fix is the hard constraint, :focus-visible is marginally better than :focus (avoids triggering on mouse-click focus in browsers that support it), but still fires on Tab and cannot eliminate the Tab-navigation animation. In that case, document the intentional behavior difference from the issue's expectation.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/patternfly/components/Button/button.scss` around lines 768 - 781, The
current CSS uses &:focus and &:active on .pf-m-favorited .#{$button}__icon to
set animation-play-state: running, which causes animations to play when tabbing
to pre-favorited buttons; change the implementation to toggle animation via a
transient class added/removed by the favoriting action instead: remove the
&:focus and &:active animation-play-state rules for .pf-m-favorited
.#{$button}__icon and instead have the favoriting click/keyboard handler on the
button element add a one-shot class (suggested name pf-m-is-animating) to the
button, set animation-play-state: running in the .pf-m-is-animating
.#{$button}__icon rule in this stylesheet, and have the handler remove
pf-m-is-animating on the button after the icon’s animationend event so only
transitions from unfavorited→favorited trigger the animation.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@src/patternfly/components/Button/button.scss`:
- Around line 782-787: The CSS selector rules for triggering the icon animation
currently include focus fallbacks (&.pf-m-favorited:focus-visible
.#{$button}__icon and &.pf-m-favorited:focus:not(:focus-visible)
.#{$button}__icon) which cause Tab navigation to animate pre-favorited buttons;
remove those focus-related selectors and only keep the pf-m-is-animating path
and the :active fallback so animation runs only when transitioning or on press
(i.e., leave &.pf-m-is-animating .#{$button}__icon and &.pf-m-favorited:active
.#{$button}__icon, remove the :focus-visible and :focus:not(:focus-visible)
selectors), or if you must keep focus fallbacks, add explicit documentation
noting they deviate from the “only on transition” requirement.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Bug - Button - Favorites button shows animation on page load

2 participants

X Tutup