Join the conversation

Sign in to join this conversation, and others like it, in the communities you care about.

styled-components

Visual primitives for the component age. Use the best bits of ES6 and CSS to style your apps without stress!

styled-components / General

Is this :hover/.active an overkill?

Is this :hover/.active an overkill?

styled-components / GeneralJune 21, 2018 · 11:51am · (Edited 2 months ago)
Hi everyone. I've been using styled-components for a few days now and find it very convenient to style my react components so far. I've just a doubt about the approach I use for styling links (or buttons) which I'd like to be hollow when inactive and filled when active OR hovered.
This is how I did this but it doesn't feel right. Is there a way to declare this like I would in SASS, e.g. &:hover, &.active { /* ... */ } ?

const StyledLink = styled(Link)` color: ${({ theme, active }) => ( active ? theme.colors.primary : theme.colors.secondary )}; background-color: ${({ theme, active }) => ( active ? theme.colors.secondary : theme.colors.primary )}; &:hover { background-color: ${({ theme }) => theme.colors.secondary}; color: ${({ theme }) => theme.colors.primary}; `


June 21, 2018 · 2:16pm

This is exactly how you're supposed to do it. We have helper functions like getFg so you can write color: ${getFg}; and also helper css that implements all of this with ${colorCss}

like-fill
1
  • reply
  • like

(we being Yaska, the company I work for)

  • reply
  • like

June 22, 2018 · 10:11am

Oh, I'm Belgian too! Anyway, thanks for the head up about the helper function. It seems so obvious but I didn't think about extracting this little logic into one. This will indeed make my code cleaner.

  • reply
  • like

You can use the SCSS/SASS way when appropriate, eg.

const StyledLink = styled(Link)` &:hover { color: red; } `

Makes sense for HTML pseudo states (hover, focus, disabled). Combine it with what you already use (eg. to target props) for better flexibility. That's why I love CSS in JS!

  • reply
  • like

June 23, 2018 · 3:04pm

I agree with OP. Coming from SASS this is one of the biggest pain points for me, feels to wrong to have duplicated styles. Wish there was some way to solve this similarly to how SASS works

  • reply
  • like

June 23, 2018 · 9:34pm

It should indeed not be a problem to write a combined selector, e.g. &:hover, &.active {} as it's just CSS

  • reply
  • like

you can also create a CSS mixin using our css helper function :)

  • reply
  • like

Here's how you could rewrite your example to use an .active class without having to handle it outside the component definition:

  • reply
  • like
const activeClassName = 'active';

const StyledLink = styled(Link).attrs({
className: p => p.active ? activeClassName : '',
})`
color: ${({theme}) => theme.colors.secondary};
background-color: ${({theme}) => theme.colors.primary};

&:hover,
&.${activeClassName} { color: ${({theme}) => theme.colors.primary};
background-color: ${({theme}) => theme.colors.secondary};
}
`;

  • reply
  • like

June 24, 2018 · 5:28pm

Oh sorry if I was a bit vague, what I meant was when using sc I usually have e.g a isActive prop instead of a `.active` class, and if a want to have the same style for that as well as e.g. a `:hover` state there is as far as I can tell no way to do that similarly to SASS?

  • reply
  • like

The example I posted above is the closest you'll get, I think. It toggles the .active class based on an active prop (could just as well be isActive of course, and uses a css selector the same way you'd do it in Sass. You don't have to use a variable for the className like I did in the example (activeClassName). It just makes things a bit more robust against accidental changes in just one of the places where the class is used

  • reply
  • like

So you could also do this:

const StyledLink = styled(Link).attrs({
className: p => p.isActive ? 'active' : '',
})`
color: ${({theme}) => theme.colors.secondary};
background-color: ${({theme}) => theme.colors.primary};

&:hover,
&.active {
color: ${({theme}) => theme.colors.primary};
background-color: ${({theme}) => theme.colors.secondary};
}
`;
like-fill
1
  • reply
  • like

The magic is in the second line, where you toggle the class based on a prop.

  • reply
  • like

June 25, 2018 · 8:02am

@Dion Thank you! I think this your proposition is the closest to what I asked! Anyway, in the meantime I made a different approach (thanks to @wmertens) which I'm also satisfied with.

I wrote this helper :

// outputs a color from theme config (see any /themes/[theme].js)
export const tint = color => ({ theme }) => theme.colors[color]

// basically outputs color a or b if active
export const colorSwitch = (a, b) =>
({ theme, active }) => active ? a({ theme }) : b({ theme })

And use it like this :

const StyledLink = styled(Link)`
color: ${colorSwitch(tint('primary'), tint('secondary'))}
background-color: ${colorSwitch(tint('secondary'), tint('primary'))}

&:hover {
background-color: ${tint('secondary')};
color: ${tint('primary')};
}
`

tint() returns a function which can be used inside a component leveraging the theme prop.

colorSwitch() works similarly based on the active prop.

I'm quite happy with this now. Thank you everyone for your helpful answers!

  • reply
  • like
Your message here...

*bold*_italic_`code````codeblock```