Target specific CSS classes with Styled Components - reactjs

How would I go about styling something like the react-datepicker with styled-components? The datepicker library looks like it uses some type of BEM style (screenshot below) which I would normally just use their provided class names and style those using Sass. But, how would I target those class names with styled-components? Is that even possible?

Since styled-components is just CSS™ you'd do it like with any other style sheet, by using the class names react-datepicker provided.
The only difference is that you'll have to wrap the datepicker in one wrapper styled component which applies all of these classes:
const Wrapper = styled.div`
&.react-datepicker {
color: blue;
}
`
<Wrapper>
<Datepicker />
</Wrapper>

Searching for hours, I found that the best solution is to add a stylized parent component (it can be a div) overlying the component that needs a className and add the definition of the className directly into the stylized parent component.
VERY IMPORTANT:
A space is needed between the ampersand & and the class for this to take effect!
const StyledParent = styled.div`
& .your-class-name {
border-color: red;
}
`;
<StyledParent>
<Datepicker yourClassName="your-class-name" />
</StyledParent>

If you ever need to access and style another component you can do this if they are not in the same file:
Export whatever you need to access:
export { Wrapper as CircledButtonWrapper };
Import it where you need it:
import { CircledButtonWrapper } from "/Bla/CircledButton";
Use it where you need to style it:
const Wrapper = styled.div`
/* Some other styles */
&:hover {
${CircledButtonWrapper} {
opacity: 1;
}
} `;
Edit:
I see now that the question was refering to accessing className...

Related

Styled components vs props.children

I have been working with react using css modules till now. I wanted to switch to styled components and there is one thing that confuses me. When working with css modules i have been creating many UI components as wrappers, passing props.children in between and then styling the wrapper in specific ".module.css" file. Now when i saw how styled components work, its like creating a component and styling that one element or even styling nested jsx tags. So styled components have wrapper behaviour. Does it kinda replace usage of props.children?
No, Styled Components do not force you not to use props.children.
You can use a Styled Component as a wrapper and style children inside of it as you would do with a Component styled with a css.module:
I guess you are composing your React components like this:
// css.module
// styles.module.css
.container {
background-color: red;
}
.container > h2 {
color: green;
}
// Your React Component Wrapper:
const Wrapper = ({children}) => <div className={styles.container}>{children}</div>
// And you use it like this:
<Wrapper><h2>This text is green on a red background</h2></Wrapper>
You can do the same thing with Styled Components:
const Wrapper = ({ children }) => <StyledWrapper>{children}</StyledWrapper>;
const StyledWrapper = styled.div`
background-color: red;
& > h2 {
color: green;
}
`;
// And you use it like this:
<Wrapper><h2>This text is green on a red background</h2></Wrapper>
The result is the same and even the usage is the same, styled-components do not force you to use a certain React Composition Pattern, it's up to you whether to style container and then select children inside of it, or if to individually style all the children.
They just give you some super-powers, for example you can target a Styled Component Child inside a Styled Component Parent, by simply calling the Component name, or you have the chance to use JS variables more easily into your css code, since all props passed to a Styled Component can be retrieved by the css.
DEMO

Dealing with stylesheet order and css module className overrides

I have a component that has its own style, e.g.
.prompt { color: red; }
It's declared as so:
import cn from 'classnames';
import styles from './styles.css';
const MyComponent = ({className}) => {
return (
<div className={cn(styles.prompt, className)}>
test
</div>
);
}
In my specific use case, the stylesheet for the className being passed in is actually defined and added to the head BEFORE the stylesheet from the module, so the module's css always overrides the style of the className being passed in. Something like:
Notice the background: yellow from second class is being overridden by the background from the module's own class.
Other than using !important in the secondary class, how can I work around this?
Based on Mr. Lister's response, I re-evaluated my existing knowledge of specificity and came up with the following solution in the higher level css:
// in app.scss
.offline.prompt {
color: red;
}
// in app.tsx
const classes = cn(Styles.offline, Styles.prompt);
return <OfflineApp className={classes}>...</OfflineApp>;
Essentially, I just tag the module markup with another sibling class to increase specificity and target the necessary properties using that. WAY better than abusing !important.

Override styled-components in React

I'm trying to find a way to override a styled component with styled-components like this:
Let's say I have a reusable component Wrapper:
class Wrapper extends Component {
...
render() {
const Wrap = styled.div`
padding: 5px;
background-color: green;
`;
return (
<Wrap>
{children}
</Wrap>
)
}
}
export default Wrapper
And then I have Component B that imports Wrapper but should extend the styles like this: myNewWrap uses Wrapper
import Wrapper from './Wrapper'
class B extends Component {
...
render() {
const myNewWrap = styled(Wrapper)`
background-color: red;
`;
return (
<myNewWrap>
Some awesome text
</myNewWrap>
)
}
}
Result should be that Some awesome text has padding from Wrapper but background-color from own component. But what I get is only the styles from Wrapper.
Have you considered using extend from Styled-Components? Styled generates new component styles with a new class whereas extend is meant to use base styling from another Styled component, but then tweak or add additional styles.
Additionally, their docs explain when you should you use styled() (see section "When should I use styled()?")
when to use styled
you override the styled component simply by passing a className to the styled component and do styling with reference to that class
<myNewWrap className="my-wrap">

styled-component generate custom css class

I have a question about the styled-components library. I know that this may not be an idiomatic way to do things, but I am curious if there is a styled-component method that can return a CSS class name. I would then be able to pass that class name into components that I render. This way, I can reuse some CSS. Then again, maybe this defeats the purpose of having isolated styled scoped to a single component.
For example, is there a function like generateCss?
const Button = styled.div`
background-color: blue;
`
const myCssClassName = styled.generateCss`
background-color: blue;
`
...
<Button className={myCssClassName} />
No, this is not possible. The whole idea of styled-components is to return new components. And if you want to create classes you should have a look at glamor or aphrodite. And here is a link to a discussion on github.
If you want to customize an existing styled component, you can use the .extend() API.
For example:
const Button = styled.div`
background-color: red;
`;
const BlueButton = Button.extend`
background-color: blue;
`;
export default () => (
<BlueButton />
);

Material-UI #next Does not support props-based styling?

With the arrival of Material-UI#next comes the replacement of LESS-based styling with CSS-to-JS-based styling. However, the component demos on Material-UI's website appear to ignore the creation of props-based-styling. I'm trying to create divs containing various Material-UI components at specific absolute heights on my page, however, the requirement of the stylesheet being predefined outside of the class means that the properties within the stylesheet can't be based on props passed to the component. Am I missing something?
For example:
import React, {Component} from 'react';
import {withStyles, createStyleSheet} from 'material-ui/styles';
import Button from 'material-ui/Button';
const styleSheet = createStyleSheet({
container: {
position: "absolute",
top: /*How can this be dependent upon the props passed to the component?*/,
height: /*How can this be dependent upon the props passed to the component?*/,
}
});
class Foo extends Component {
render() {
let classes = this.props.classes;
return (
<div className={classes.container}>
<Button raised/>
</div>
);
}
}
export default withStyles(styleSheet)(Foo);
The example component displayed above can't have props-dependent styles, because props is not defined when the stylesheet is created. So how do I solve this problem? IS there a solution?
I would strongly advise you check out Styled Compoments. They make styling very simple and even allow you to pass third party components (in your case Material UI components). They also allow you to pass in props like the following:
const Stylesheet = styled.div`
color: ${props => props.primary ? 'white' :
`
<Stylesheet primary>My Div</Stylesheet>
Check out the docs for more details, that was a very simple example, but they are super easy to work with and you can accomplish exactly what you need with them.

Resources