With Styled Components why do pseudo classes seem to still work without ampersands?

Viewed 374

I've read in the docs and elsewhere that & refers "back to the main" Styled Component that it's inside of. For instance, in this example it refers to MyDiv and the div's text will turn red when the div is hovered:

const MyDiv = styled.div`
  &:hover { color: red; }
`

The docs also say that when the ampersand isn't present, the CSS declaration will refer to children of the components, however, I've noticed, particularly with pseudo classes like :hover, that if I omit the ampersand the behavior is still the same. For example, with the code below I would think that the hover only applies to the child Text span, but if you hover just the div the text in the span still turns red.

const MyDiv = styled.div`
  :hover { color: red; }
  // these other style declarations are provided just to help 
  // illustrate what happens if you want to try this in a code editor:
  color: blue;
  border: 1px solid black;
  width: 100%;
  padding: 10px;
`
const Text = styled.span`
  border: 1px solid blue;
`

export default App() {
  render(
    <MyDiv>
      <Text>Hello world</Text>
    </MyDiv>
  )
}

If anybody can help shed some light on this I'd appreciate it!

1 Answers

In your code example:

const MyDiv = styled.div`
  :hover { color: red; }
  // these other style declarations are provided just to help 
  // illustrate what happens if you want to try this in a code editor:
  color: blue;
  border: 1px solid black;
  width: 100%;
  padding: 10px;
`
const Text = styled.span`
  border: 1px solid blue;
  // --> inherits text color of parent
`

export default App() {
  render(
    <MyDiv>
      <Text>Hello world</Text>
    </MyDiv>
  )
}

The Text component, or span, inherits color styling from its parent element, the div.

CSS Selectors

Selector Example Example Description
.class1 element .name1 p Selects all <p> elements inside elements with name name1
element element div p Selects all <p> elements inside <div> elements

Notice in this example snippet the span in the div inherits the blue text color, and when the div is hovered, the span also inherits the red text color. The span that is outside the div doesn't inherit the blue or red hovered color.

.testDiv {
  color: blue;
  border: 1px solid black;
  width: 100%;
  padding: 10px;
}

.testDiv:hover {
  color: red;
}

.testSpan {
  border: 1px solid blue;
}
<div class="testDiv">
  This is text in the parent div.
  <br />
  <span class="testSpan">This is text in a span wrapped by a parent div.</span>
</div>
<span class="testSpan">This is text in a span outside the div.</span>

CSS color see Formal Definition

Inherited: yes

and Inheritance

In CSS, inheritance controls what happens when no value is specified for a property on an element.

CSS properties can be categorized in two types:

  • inherited properties, which by default are set to the computed value of the parent element
  • non-inherited properties, which by default are set to initial value of the property Refer to any CSS property definition to see whether a specific property inherits by default ("Inherited: yes") or not ("Inherited: no").

What is happening?

& does refer back to the parent but let's break down what is happening.

  1. & refers to the parent element, the div in this case.
  2. :hover is a pseudo-selector, not a selector.

So &:hover and :hover are both simply selecting the "modified" selected state of the div. In other words &:hover is roughly equivalent to div:hover.

Contrast this with & :hover where the space is a child selector, or specifically in this case, selecting the "hovered" pseudo-selector of a descendant of the parent element div.

Try this out:

const MyDiv = styled.div`
  &:hover {
    color: red;
  }
  
  & :hover {
    color: green;
  }

  color: blue;
  border: 1px solid black;
  width: 100%;
  padding: 10px;
`;

const Text = styled.span`
  border: 1px solid blue;
`;

<MyDiv>
  I'm the div text
  <Text>I'm the span text</Text>
</MyDiv>

Edit with-styled-components-why-do-pseudo-classes-seem-to-still-work-without-ampersan

Notice when the div is hovered, both the div and span inherit the red color, but then when specifically the span is hovered it inherits the green color while the div remains red.

Related