Shared css declarations to styled component with props - css

I have some css declarations shared with multiple style-components.
For example:
margin-top:${props.marginTop};
margin-bottom:${props.marginBottom};
So i know can add file with:
export const baseStyles = css`
margin-top: 10px;
margin-bottom: 10px;
`;
and then import it and use:
const MyComponent = styled.div`
${(props: Props) => {
return css`
${baseStyles};
`;
}}
`;
But is there a way to this and still have the current component props used inside ${baseStyled} ?

props are automatically provided inside of mixins.
export const baseStyles = css`
margin-top: ${(props) => props.marginTop};
margin-bottom:${(props) => props.marginBottom};
`;
const MyComponent = styled.div`
${baseStyles}
`;
const App = () => {
return (
<MyComponent marginTop="12px" marginBottom="16px" />
)
};

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}
};

Reusable styled components (pseudo element) with logic

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 :

How can I reuse other external styled-components

I want to load an external styled component and use extending styles, but it doesn't work. How can I solve this problem?
common/Button.js
import styled from 'styled-components';
const StyledButton = styled.button`
padding: 1rem 2rem;
border: none;
border-radius: 8px;
`;
const Button = ({ children, onClick }) => {
return <StyledButton onClick={onClick}>{children}</StyledButton>;
};
export default Button;
pages/Login.js
import Button from '../Components/common/Button';
import styled from 'styled-components';
const StyledButton = styled(Button)`
// not working
color: red;
`;
const Login = () => {
return (
<div>
<StyledButton>Submit</StyledButton>
</div>
);
}
export default Login;
enter image description here
Component can be styled only if it passes className property to it's child. So you should make your Button component like this:
const Button = ({ children, onClick, className }) => {
return <StyledButton onClick={onClick} className={className}>{children}</StyledButton>;
};

Unable to override SSR styling of styled components from packages

I am working on a project where we make use of Styled Components with NextJS for server side rendering. We use a private package which we include in multiple custom projects. This private package contains styling, but in some projects we want to override the styling with our custom styling as can be seen in the code below. It seems like it has something to do with the component selector ${H1} not being able to find the actual H1 component. Why is this happening?
Code in private package:
const baseHeading = css`
font-family: ${(props) => props.theme.fonts.sec};
display: block;
line-height: 1.3;
font-weight: bold;
`;
export const H1Styles = css`
font-size: ${(props) => props.theme.fonts.content.headings.h1.b1};
text-transform: uppercase;
#media screen and (min-width: ${size.min.b4}) {
font-size: ${(props) => props.theme.fonts.content.headings.h1.b4};
}
`;
export const H1 = styled.h1`
${baseHeading};
${H1Styles};
`;
Code in custom component:
import { H1 } from '#testproject/testproject-core-ui'
....
<section className={className}>
<FooterBrandingInner>
<H1>
<span>{params.LeftColumnTitleRow1}</span>
<span>{params.LeftColumnTitleRow2}</span>
</H1>
....
const FooterBrandingInner = styled.div`
${H1} {
font-size: 3.8rem;
text-transform: none;
line-height: 1.2;
}
....
Code in _document.tsx
static async getInitialProps(ctx: any) {
const sheet = new ServerStyleSheet();
const originalRenderPage = ctx.renderPage;
try {
ctx.renderPage = () =>
originalRenderPage({
enhanceApp: (App: any) => (props: any) => sheet.collectStyles(<App {...props} />),
});
const initialProps = await Document.getInitialProps(ctx);
return {
...initialProps,
styles: (
<>
{initialProps.styles}
{sheet.getStyleElement()}
</>
),
};
} finally {
sheet.seal();
}
}
Code in .babelrc
[
"babel-plugin-styled-components",
{
"ssr": true,
"displayName": true,
"preprocess": false
}
]
Expected Behavior
I would expect to see the H1 without 'text-transform: uppercase' when server side rendering.
Actual Behavior
When we visit a page with the custom styling which is defined in our custom class, we will not see it while server side rendering. We do see it when we are not server side rendering. We do sometimes see a flash of the custom styling and it seems that the wrong ClassNames are being used on the component.

Why is the flex-value not updating in the styled-component?

I have a sidebar and some main content. The main content takes up the majority of the screen whilst the sidebar should only take a small portion. I've got a parent container that is a flexbox. The two children (sidebar and main content) are both div elements.
The sidebar is closed by default
The Issue: Toggling the sidebar does not expand the sidebar as expected
Things I've Checked:
Flex values in the sidebar css are being updated correctly
Sidebar events are being fired and the isOpen hook is being updated correctly
// main.ts
import styled from 'styled-components';
export const Content = styled.div`
flex: 4;
margin-top: 1em;
margin-bottom: 2em;
height: 100vh;
`;
export const Container = styled.div`
display: flex;
`;
// sidebar/styles.ts
import styled from 'styled-components';
interface RootProps {
isOpen: boolean;
}
export const Root = styled.div<RootProps>`
padding: 1em;
background-color: ${DARK};
flex: ${({ isOpen }: RootProps) => (isOpen ? 1 : 0)};
`;
// sidebar/index.tsx
export const Sidebar: React.FC = () => {
const dispatch = useDispatch();
const isOpen = useSidebar();
const handleOpen = () => dispatch(toggleSidebar());
return (
<Root isOpen={isOpen}>
<Button onClick={handleOpen}>
CLICK ME
</Button>
</Root>
);
};
// Usage
// Sidebar styles mirror what's in the sidebar styled component file
<Container>
<Sidebar />
<Content />
</Container>
Expected outcome is that the sidebar expands and collapses when toggling the button. No error messages are displayed and the sidebar flex values are being updated correctly.
JSFiddle with just HTML / CSS but essentially the desired effect: https://jsfiddle.net/5dLk9ex3/3/
The problem with your code is somewhere in the reducer scope (your question is incomplete), your Sidebar doesn't rerender after you dispatching an action.
Working example:
import React, { useState } from 'react';
import ReactDOM from 'react-dom';
import styled from 'styled-components';
const Container = styled.div`
display: flex;
`;
const First = styled.div`
flex: ${({ isOpen }) => (isOpen ? 1 : 0)};
background-color: red;
height: 20px;
`;
const Second = styled.div`
flex: 4;
background-color: green;
height: 20px;
`;
const DEFAULT_INITIAL = false;
const App = () => {
const [isOpen, setIsOpen] = useState(DEFAULT_INITIAL);
const onClick = () => {
console.log('Toggle Sidebar');
setIsOpen(p => !p);
};
return (
<>
<Container>
<First isOpen={isOpen}>FIRST</First>
<Second>SECOND</Second>
</Container>
<button onClick={onClick}>OpenSider</button>
</>
);
};
Please refer to the first comment of this answer
I'm unsure as to why but setting the main content div to min-width: 0 resolved the issue. See this for more: Flex items not shrinking when window gets smaller

Resources