Search code examples
csssass

Getting the default state color (currentColor) in a different state such as hovered


Is there a keyword like currentcolor which allows us to get the color of a class in its default state?

For example, I'm trying to create a re-useable button style, and currentcolor keyword helps a lot until I try to create the :hovered state.

.outline-btn {
  background-color: transparent;
  border: 1px solid currentColor;
  padding: 0.5em 1.5em;
}

.rounded-btn {
  border-radius: 50px;
}

The default state looks the way we want and changing the color or the font-size would also adjust the rest of the properties.

button

But we want the :hovered state to invert the colors (white text and orange background in this case)

.outline-btn:hover, .outline-btn:active, .outline-btn:focus {
  background-color: currentcolor;
  color: white;
}

But since in this state the color becomes white, everything else also turns white.

Is there a way that we can achieve the behavior that we want without having to create multiple classes for the different button styles that we want?

Desired effect on hover:

button on hover

Also I forgot to mention that I am using SCSS if that helps.

Thanks for your time :)


Solution

  • This can be done with CSS only. Works for any two colours, provided that one is set on background-color and one is set on color.

    .button--orange {
      background-color: white;
      color: orange;
    }
    
    /* CSS beyond this point is colour-agnostic - no mention of either white or orange */
    
    .button {
      border: 0.125rem solid currentColor;
      padding: 0.5rem 1rem;
      font-size: 1rem;
      position: relative;
    }
    .button, .button::before {
      border-radius: 5rem;
    }
    
    .button:hover::before {
      content: "";
      position: absolute;
      z-index: 1;
      inset: 0;
      
      background-color: currentColor;
    }
    
    .button:hover span {
      position: relative;
      z-index: 2;
    
      background-color: inherit;
      -webkit-background-clip: text;
      -webkit-text-fill-color: transparent;
    }
    <button class="button button--orange">
      <span>Button</span>
    </button>

    This works by establishing 3 layers.

    The first, .button, is the button itself with the default background colour and text colour that appear when not hovered.

    The second, .button::before, is a layer that appears when hovered to invert the background colour. It uses the text colour of the button by having its background colour set to currentColor. The reason we need this layer instead of simply setting background-color: currentColor on .button is because we need to keep the background colour intact for the third layer.

    The third layer, .button span, contains the text of the button. The goal is to set its colour to the background colour of the original button on hover.

    We can't simply set the color property because in theory we don't know what to set it to. We need to copy the parent element's background colour, but it could be anything. If there were a currentColor that contained the value of the parent's background colour, this would be trivial.

    Instead we can use -webkit-background-clip, a feature which has been around for ages and is commonly used to create gradients in text, but we can use it for regular colours too. We can copy the background from the parent with inherit and clip it to the text, which results in the visual appearance of text that is the same colour as the parent's background.

    The second layer provides enough contrast to make the text visible, and the colour swap is complete.


    An alternative, and perhaps better, approach would be to save the text colour and background colour into CSS variables (or SCSS variables as @alexbea suggests) and then reference those in the hover states of the button. But I just wanted to prove that this question can indeed be answered as originally asked.