Search code examples
reactjstypescriptstyled-components

Styled Components → Inline Styling of Pseudo Selector in attrs()


MWE

https://codesandbox.io/s/slider-example-ev250?file=/src/App.js

Question

I am attempting to create an <input type='range'> Styled Component that has a custom appearance:

const SliderInput = styled.input.attrs((props) => {
  const val = Number(props.value ?? 0);
  const min = Number(props.min ?? 0);
  const max = Number(props.max ?? 1);
  const breakpoint = 100 * ((val - min) / (max - min));

  return {
    style: {
      background: `linear-gradient(to right, ${props.color}, ${props.color} ${breakpoint}%, white ${breakpoint}%, white 100%)`,
      border: `solid 1px ${props.color}`
    }
  };
})`
  -webkit-appearance: none;
  width: 200px;
  height: 8px;
  border-radius: 12px;

  &::-webkit-slider-thumb {
    -webkit-appearance: none;
    background: ${(props) => props.color};
    border: 2px solid white;
    border-radius: 50%;
    width: 16px;
    height: 16px;
  }

  &:hover {
    cursor: grab;
  }
`;

The issue I am facing is that the background color of the thumb slider changes too frequently, which causes lagging and I see the following warning:

Over 200 classes were generated for component styled.input with the id of "sc-dlnjwi".
Consider using the attrs method, together with a style object for frequently changed styles.
Example:
  const Component = styled.div.attrs(props => ({
    style: {
      background: props.background,
    },
  }))`width: 100%;`

How can this be achieved with pseudo-selectors like ::-webkit-slider-thumb?

I've tried:

style: {
      background: `linear-gradient(to right, ${props.color}, ${props.color} ${breakpoint}%, white ${breakpoint}%, white 100%)`,
      border: `solid 1px ${props.color}`
      "::WebkitSliderThumb": {
        background: props.color
      }
    }

To no avail.

Any suggestions?


Solution

  • It's not possible to style psuedo-selectors using inline styles (see this SO thread for more context). In other words, this isn't possible:

    <input type="range" style="::WebkitSliderThumb:background:red" />
    

    That's what styled-components's .attrs is doing under the hood, it's just applying inline styles [docs].

    The only way to apply styles to psuedo-selectors is to use CSS, and as you've already seen it's not practical to do this with styled-components. I'd suggest determining a fixed number of slider steps, generating CSS classes for each of those ahead of time (you can use a preprocessor like SASS to do this effeciently), and then just dynamically applying the correct class when the slider's value changes.