Reputation: 451
I am trying to conditionally render a hover state / view within styled-components, leveraging the props coming in from react...
Currently, my code looks something like this:
${isHovered}, &:hover {
background: red;
}
Unfortunately, this does no work to be able to have a true/false as I am missing something I presume to be able to do the either / or pseudo..
I want to be able to explicitly show the hover state while retaining the default pseudo hover. How can I achieve this?
Upvotes: 2
Views: 3021
Reputation: 445
import styled, { css } from 'styled-components';
const getHoverStyle = (props) => {
if(props?.isHovered) {
return (
css`
&:hover {
background: red;
}
`
)
}
}
export const SomeName = styled.div`
${getHoverStyle}
`;
Upvotes: 2
Reputation: 19772
The way you have your selectors right now is invalid:
${isHovered}, &:hover {
background: red;
}
As of now, this will always translate to:
undefined, &:hover {
background: red;
}
For simplicity, in order to interpolate properly, it must be a function accepting props
that returns a string
:
color: ${props => props.isHovered && "pink" };
On a related note, even if you wrapped your styled component in a higher order component where isHovered
is defined as an argument, it unfortunately still won't work in production for styled-components v5 -- this worked in v4, but v5.x doesn't handle css interpolations properly within a styled component when compiled for production (see issue tracker here).
A better and recommended approach would be to interpolate within a CSS property:
background: ${({ isHovered }) => isHovered && "red"};
:hover {
background: red;
}
Alternatively, if you have multiple CSS rules, then you can interpolate outside of a CSS property:
${({ isHovered }) => isHovered && "background: red;color: white;"};
:hover {
background: red;
}
Now you just would pass your component an isHovered
prop.
<StyledComponent isHovered={isHovered} />
Although, technically you can do this (notice that falsey values equate to true, which may be a bug or an unhandled edge case) ...
${({ isHovered }) => !isHovered && ".hovered"}, &:hover {
background: red;
}
...it is NOT recommended because of how it's being interpreted:
".hovered", &:hover {
background: red;
}
Arguably, this isn't what you'd want because .hovered
isn't being used at all within the DOM, which may be confusing to other developers. Instead, it reuses the compiled hashed class name to add the CSS property within another rule (focus on the Styles
tab to see the difference):
screenshot
While the recommended approach sets the CSS property to the hashed class within the same rule block: screenshot
Working demo (this demo includes both example codes above, where Title
uses the recommended approach and SubTitle
doesn't):
Upvotes: 2
Reputation: 135
I have created an interactive playground. You can visit the below link. It might help you.
https://codesandbox.io/s/focused-sky-vp8d9?file=/src/App.js
Below is the code.
import React, { useState } from "react";
import "./styles.css";
import styled, { css } from "styled-components";
const HoverExample = styled.div`
background: pink;
width: 100%;
min-height: 80px;
${(props) =>
props.isHovered &&
css`
&:hover {
background: green;
}
`}
`;
export default function App() {
const [isHovered, setisHovered] = useState(false);
return (
<div className="App">
<button onClick={() => setisHovered(!isHovered)}>
{!isHovered ? "Enable" : "disabled"} Hover
</button>
<p>Hover is {isHovered ? "enabled" : "disabled"}</p>
<HoverExample isHovered={isHovered}></HoverExample>
</div>
);
}
Thanks.
Upvotes: 0
Reputation: 26085
You can use class
selector to apply style and in your styled component you can use attr
call to set className
attribute based on property value like:
const MyComp = styled.div.attrs((props) => ({
className: props.isHovered ? "hovered" : ""
}))`
color: ${(props) => props.color};
&.hovered,
&:hover {
color: blue;
}
`;
Take a look at this react sandbox.
Upvotes: 0