Reusable styled components (pseudo element) with logic - css

I've written some shared style to be used across different components, how can I change the value of left or width based a passed value or boolean logic so the values can be more dynamic?
And if possible, I don't want it passed as a prop in the actual component <bar left="20" />, but rather just within the declared styles.
const shared = css`
::after {
content: '';
left: 0;
width: 100%;
${(props) => props.beta &&
`
top: 0;
`
}
`
const foo = styled.div`
${shared}
`
const bar = styled.div`
${shared}
${child} {
${shared}
}
`

you can use a funtion instead:
const getShared = (props) => css`
::after {
content: '';
left: ${props.left || '0'};
width: ${props.width || '100%'};
${(otherProps) => otherProps.beta &&
`
top: 0;
`
}
`
const foo = styled.div`
${(props) => getShared(props)}
`
const bar = styled.div`
${(props) => getShared(props)}
${child} {
${(props) => getShared(props)}
}
`
if you want to simply override the shared css here is a simple exemple:
<div>
{/* this is a div that uses shared css */}
<div css={shared}>this is shared css</div>
{/* this is a div that uses shared css in his styling*/}
<FirstContainer>container extends shared css</FirstContainer>
{/* this is a div that uses shared css in his styling but overrides border color using a prop*/}
<SecondContainer borderColor="red">container overrriding the shared css</SecondContainer>
</div>
and here is the styling:
// this is the shared css
export const shared = css`
width: 100px;
height: 100px;
border: solid 1px green;
margin: 40px;
`
// div using the shared css
export const FirstContainer = styled.div`
${shared}
`
// div using the shared css but overriding border color
// props contains all the properties passed to the SecondContainer component (like left in bar component)
export const SecondContainer = styled.div`
${shared}
border-color: ${(props) => props.borderColor}
`
here is the result :

Related

react styled-component nested item's props

My problem is about props. I want to use nested components with props in styled-components. For example:
const MenuItem = ({ item }) => {
const router = useRouter();
const [isOpen, setIsOpen] = useState(false);
const isActive = router?.asPath === item?.path;
return (
<MenuItemWrapper key={item?.slug} onClick={() => setIsOpen(!isOpen)}>
<Link href={item?.path}>
<InnerMenuItem isActive={isActive}>
{item?.prefix && <Prefix>{item?.prefix}</Prefix>}
{item?.label}
{item?.children && <RightArrow isOpen={isOpen} />}
</InnerMenuItem>
</Link>
<Children>
{
item?.children?.map((child) => <MenuItem item={child} />)
}
</Children>
</MenuItemWrapper>
);
};
export default MenuItem;
this is MenuItem component. I use MenuItem component as a recursion component.
in styled-component i tried this but it doesnt work. I want to apply different style in Children > InnerMenuItem but it not working
export const Children = styled.div`
display: flex;
flex-direction: column;
margin-left: 65px;
${MenuItemWrapper} {
font-size: 16px;
padding: 9px 0;
&:not(:first-child) {
border-top:none;
}
}
${InnerMenuItem} {
${({ isActive }) => // HOW CAN I USE THIS PROPS HERE
isActive &&
css`
color: orange
`};
}
`;
from styled components official documentations:
"If the styled target is a simple element (e.g. styled.div), styled-components passes through any known HTML attribute to the DOM. If it is a custom React component (e.g. styled(MyComponent)), styled-components passes through all props."
example :
const Input = styled.input`
color: ${props => props.inputColor || "palevioletred"};
`;
return(
<Input inputColor="rebeccapurple" />
)
another way is by Extending Styles, example :
const Button = styled.button`
color: palevioletred;
font-size: 1em;
margin: 1em;
padding: 0.25em 1em;
border: 2px solid palevioletred;
border-radius: 3px;
`;
const TomatoButton = styled(Button)`
color: tomato;
border-color: tomato;
`;
return(
<div>
<Button>Normal Button</Button>
<TomatoButton>Tomato Button</TomatoButton>
</div>
);
more about styled-components read here
have you tried
${InnerMenuItem} {
color: ${({isActive})=> isActive ? 'orange' : undefined}
};

Understanding css helper function in styled components

Styled components has a helper css function. But I don't understand when should I used it.
For example this is their example where they use it:
import styled, { css } from 'styled-components'
const complexMixin = css`
color: ${props => (props.whiteColor ? 'white' : 'black')};
`
const StyledComp = styled.div`
/* This is an example of a nested interpolation */
${props => (props.complex ? complexMixin : 'color: blue;')};
`
But if we take similar example from docs here they don't use it:
const Button = styled.button`
/* Adapt the colors based on primary prop */
background: ${props => props.primary ? "palevioletred" : "white"};
color: ${props => props.primary ? "white" : "palevioletred"};
font-size: 1em;
margin: 1em;
padding: 0.25em 1em;
border: 2px solid palevioletred;
border-radius: 3px;
`;
Their description is also not clear and is confusing me:
A helper function to generate CSS from a template literal with
interpolations. You need to use this if you return a template literal
with functions inside an interpolation due to how tagged template
literals work in JavaScript.
Can someone help explain why we need it?
PS this answer also doesn't use it
I use the css function when I create variants for a component:
that is variant switcher:
const sizeVariant: Record<NonNullable<StyledLogoProps['size']>, ReturnType<typeof css>> = {
small: css`
width: 44px;
height: 44px;
`,
regular: css`
width: 93px;
height: 93px;
`,
};
that is component was created by 'styled-components':
interface StyledLogoProps {
size: 'small' | 'regular';
}
export const StyledLogo = styled.div<StyledLogoProps>`
${({ size }) => sizeVariant[size]}
`;
and this is the use in the react:
<>
<StyledLogo size="regular" />
<StyledLogo size="small" />
</>
quite a useful thing

How to conditionally render using object properties in styled-components?

I am trying to conditonally render in styled-components. This code seems to work in this case.
background-color: ${props => (props.active ? 'Black' : 'Green')};
I want to rather use object properties from a JSON file and provide 2 colours to the above condition. Something similar to these below instead of Black and Green.
${colors['Brand/PrimaryBlack']}
${colors['Brand/PrimaryGreen']}
colored.json
{
"colors": {
"Brand/PrimaryBlack": "#424449",
"Brand/PrimaryGreen": "#8ED6C9",
}
}
styles.js
import styled from 'styled-components'
import { colors } from './colored.json'
const Tabs = styled.button`
background-color: ${props => (props.active ? 'Black' : 'Green')};
`
How can I achieve this?
The ternary works exactly the same as your previous code, but just references the keys in your colors JSON, i.e. background-color: ${props => colors[props.active ? "Brand/PrimaryBlack" : "Brand/PrimaryGreen"]};.
{
"colors": {
"Brand/PrimaryBlack": "#424449",
"Brand/PrimaryGreen": "#8ED6C9",
}
}
import styled from 'styled-components'
import { colors } from './colored.json'
const Tabs = styled.button`
background-color: ${props => colors[props.active ? "Brand/PrimaryBlack" : "Brand/PrimaryGreen"]};
`;
You can do what you desire using styled components in the following way:
background-color: ${(props) =>
props.active ? colors["Brand/PrimaryGreen"] : colors["Brand/PrimaryBlack"]};
Find the working CodeSandBox here
Inside of Template literal you pass any valid JavaScript code inside of ${} expression even call to function so if you have an object which you want to access some keys you can just access those keys as you would do in a normal JavaScript code. so if you have an object colors with some properties you can access it inside of you Styled Component like this
const colors = {
"Brand/PrimaryGreen": "green",
"Brand/PrimaryBlack": "black"
};
const Comp = styled.div`
background: ${props => props.active? colors["Brand/PrimaryBlack"] : colors["Brand/PrimaryGreen"]};
color: #fff;
`;
You can simply do this
<TagName active={this.state.active}>Test</TagName>
And in your styles something like this:
const TagName = styled.button`
width: 100%;
outline: 0;
border: 0;
height: 100%;
justify-content: center;
align-items: center;
line-height: 0.2;
${({ active }) => active && `
background: blue;
`}
`;

CSS to React components

I started rewriting CSS into React Components but i've encountered a problem, I do know how to do this (using styled-components btw ) :
You have 5 ways to styling component in React.
Every approach have pros & cons(personally I use 4-th ways.)
1.Inline Css
const divStyle = {
color: 'blue',
backgroundImage: 'url(' + imgUrl + ')',
};
function HelloWorldComponent() {
return <div style={divStyle}>Hello World!</div>;
2. CSS in JS
const Button = (props) => (
<button className={ 'large' in props && 'large' }>
{ props.children }
<style jsx>{`
button {
padding: 20px;
background: #eee;
color: #999
}
.large {
padding: 50px
}
`}</style>
</button>
)
/* Creates a regular button */
<Button>Hi</Button>
/* Creates a large button */
<Button large>Big</Button>
3. Styled Components
const Wrapper = styled.section`
padding: 4em;
background: papayawhip;
`;
render(
<Wrapper>
<Title>
Hello World!
</Title>
</Wrapper>
);
4. Css Modules (scss || sass)
#width: 10px;
#height: #width + 10px;
#header {
width: #width;
height: #height;
}
5. Stylable - https://github.com/wix/stylable
import React from 'react';
import styled, {css} from 'styled-components';
const Info = styled.div`
display: flex;
// etc.
${props => props.title && css`font-size: 15px`}
`
const Box = styled.div`
&:first-of-type {
// some more css
}
`
// usage
<Info title>some info</Info>
I recommend you follow the official docs as stated in comments by #Mikkel

Targeting a styled component in React with a webpack generated identifier?

I have a component called LargeDialog which encapsulates a StyledDialogContent (both of which are from the Dialog class of the Material UI library).
LargeDialog.jsx
...
const StyledDialogContent = styled(DialogContent)`
padding: 30px;
`;
class LargeDialog extends Component {
...
render(){
return (<StyledDialogContent> ... </StyledDialogContent>) // Some content within.
}
}
...
The styled components adds a padding: 30px to the DialogContent.
I would like to override this with padding: 0px if the LargeDialog modal is reused in another place.
However, the generated webpack CSS has a random identifier i.e. MuiDialogContentroot-0-3-439 FullDialogModal__StyledDialogContent-ogd6um-6 iMpISc and I'm not sure how to target this.
AnotherComponent.jsx
import LargeDialog from './LargeDialog'
...
const LargeDialogWrapper = styled(LargeDialog)`
// What do I put here to override StyledDialogContent with a random identifier?
`;
class AnotherComponent extends Component {
}
...
I tried exporting StyledDialogContent and targetting it as such:
import LargeDialog, {StyledDialogContent} from './LargeDialog'
...
const LargeDialogWrapper = styled(LargeDialog)`
${StyledDialogContent} {
padding: 0px;
}
`;
But that didn't work too.
Example:
https://codesandbox.io/embed/styled-components-d5pzv?fontsize=14&hidenavigation=1&theme=dark
You target it within the style like so:
const Box = styled.div`
background-color: black;
height: 100px;
`;
const Yellow = styled.div`
background-color: blue;
height: 200px;
${Box} {
background-color: yellow;
}
`;
const App = () => {
return (
<>
<Box />
<Yellow>
<Box />
</Yellow>
</>
);
};
Refer to the related docs section.
If it helps, you can check this example file (note the Heading style for example).
An edit after OP question update
In your example, you missing className if you want to enable styling for your components.
Also, you need WrapperDiv to be a direct child, this is how the CSS works, remember that you writing simple CSS just in javascript:
class LargeDialog extends Component {
render() {
return (
<WrapperDiv className={this.props.className}>
<div>{this.props.children}</div>
</WrapperDiv>
);
}
}
const WrapperLargeDialog = styled(LargeDialog)`
${WrapperDiv} {
background-color: blue;
}
`;
// LargeDialog should be red.
// WrapperLargeDialog should be blue.
class App extends Component {
render() {
return (
<div>
<LargeDialog />
<br />
<WrapperLargeDialog>
<WrapperDiv />
</WrapperLargeDialog>
</div>
);
}
}

Resources