Search code examples
cssemotion

Emotion JS: How to apply styles to child when hover parent?


Seems like this question has been asked and answered many different ways, but the answers I've seen either don't apply to Emotion or the Emotion-related answered haven't worked for me. I'm on @emtion/core@10.0.28 and @emtion/styled@10.0.27.

Essentially I want to apply styles to a child component when the parent is hovered/active/focused. The parent is a button and the child is an optional icon. The following styles are added to the (parent) button via the styled syntax.

const iconWrapperStyles = (props) => {
  return css`
    ${props.IconWrapper} {
      width: ${iconSizeMedium};
      height: ${iconSizeMedium};
      margin-left: ${spacingSizeSmall};
      color: ${textColor};
      fill: ${textColor};
      background: ${backgroundColor};
      border-color: ${borderColor};
    }

    &:hover:not(:disabled),
    &:focus:not(:disabled),
    &:active:not(:disabled) ${props.IconWrapper} {
      outline: none;
      color: ${textColorHover};
      fill: ${textColorHover};
      background: ${backgroundColorHover};
      border-color: ${borderColorHover};
    }
  `;
};

The first block of styles is successfully applied. Therefore, at first blush, the button and child icon appear properly styled. However, when you hover/focus/make active the button, the icon does not change. I've tried the implementation above, along with ... + ${IconWrapper} and ... & ${IconWrapper}; all three fail for me. Official docs indicate that the & should work.


Solution

  • Regardless of the JS framework, the following should always work.

    button {
      background: darkblue;
      color: white;
      border: none;
      padding: 5px;
    }
    
    button:hover i {
      color: red;
    }
    <link href="//netdna.bootstrapcdn.com/font-awesome/3.2.1/css/font-awesome.css" rel="stylesheet">
    
    <button>
      <i class='icon-edit'></i> Click to edit
    </button>

    In your case, that becomes

       ${props.IconWrapper} {
         width: ${iconSizeMedium};
         height: ${iconSizeMedium};
         margin-left: ${spacingSizeSmall};
         color: ${textColor};
         fill: ${textColor};
         background: ${backgroundColor};
         border-color: ${borderColor};
       }
    
       &:hover:not(:disabled) ${props.IconWrapper},
       &:focus:not(:disabled) ${props.IconWrapper},
       &:active:not(:disabled) ${props.IconWrapper} {
         outline: none;
         color: ${textColorHover};
         fill: ${textColorHover};
         background: ${backgroundColorHover};
         border-color: ${borderColorHover};
       }