how to pass className to svg in styled components - css

I have been trying to style svg icon component via styled component and facing some issue but it's not working.The style i apply to close icon are not getting applied
import styled from 'styled-components'
import Close from './close.svg'
const CloseIcon = ({ className , ...props }) => <Close {...props} className={className} />
const styledCloseIcon = styled(CloseIcon)`
&.ui.modal {
.modal-icon-close {
width: 14px;
height: 24px;
fill: black;
top: 12px;
right: 14px;
}
}
`
export default styledCloseIcon
close.svg
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<path d="M13.627 12.213l9.9 9.9-1.414 1.414-9.9-9.9-9.9 9.9L.9 22.113l9.9-9.9-9.9-9.9L2.314.9l9.9 9.9 9.899-9.9 1.414 1.415-9.9 9.9z"/>
</svg>
This close icon is being used in semantic ui react modal
https://react.semantic-ui.com/modules/modal/#variations-close-icon
<Modal
size='small'
open={true}
closeIcon={<Close className='modal-icon-close'/>}
closeOnDimmerClick={false}
className={classNames(className)}
>

When you call styled(CloseIcon) you are already styling the SVG because the custom component accepts className prop so styled-components can inject the className and only renders the SVG icon.
Try omitting all the nested class names and directly apply the styles.
Here is an example,
import React from "react";
import ReactDOM from "react-dom";
import styled from "styled-components";
import { ReactComponent as Close } from "./close.svg";
const CloseIcon = ({ className, ...props }) => (
<Close {...props} className={className} />
);
const StyledCloseIcon = styled(CloseIcon)`
width: 30px;
height: 30px;
fill: #ee4845;
`;
const App = () => {
return (
<div>
<StyledCloseIcon />
</div>
);
};
Working demo in codesandbox

Issues
I think there are a couple of issues
import Close from './close.svg'; isn't a valid react component
Unnest the class in the styled component
Solution
First create a proper SVG react component
const CloseIcon = ({ className, ...props }) => (
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
{...props}
className={className}
>
<path d="M13.627 12.213l9.9 9.9-1.414 1.414-9.9-9.9-9.9 9.9L.9 22.113l9.9-9.9-9.9-9.9L2.314.9l9.9 9.9 9.899-9.9 1.414 1.415-9.9 9.9z" />
</svg>
);
Second make modal-icon-close a top-level class in the styled component
const StyledCloseIcon = styled(CloseIcon)`
&.modal-icon-close {
width: 14px;
height: 24px;
fill: black;
top: 12px;
right: 14px;
}
`;
What doesn't make much sense to me is why internalize the classname and CSS and then require the correct classname prop to be passed anyway? You could simplify the component by also specifying the className prop using .attrs
const StyledCloseIcon = styled(CloseIcon).attrs(() => ({
className: 'modal-icon-close',
}))`
&.modal-icon-close {
width: 14px;
height: 24px;
fill: black;
top: 12px;
right: 14px;
}
`;
Or just simply eliminate the classname altogether
const StyledCloseIcon = styled(CloseIcon)`
width: 14px;
height: 24px;
fill: black;
top: 12px;
right: 14px;
`;

here's how I assign className to SVG file:
import { ReactComponent as MyImage } from '../assets/img/myimage.svg'
<MyImage
className='myClassName'
fill='white'
/>
Index.css:
.hoverColor:hover {
fill: yellow !important;
opacity: 1 !important;
}

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

Does a component created with styled-components destroy the component when its style changes, rather than just modifying the component's style?

I have created two boxes, one with the css module scheme and the other with styled-components. However, the transition will fail on the box created with styled-components, but not on the box created with the css modules scheme.
I have added a button to demonstrate the difference.
import React, { useState } from "react";
import styled from "styled-components";
import "./App.css";
const App = () => {
const [boxHeight, setBoxHeight] = useState(20);
const Box = styled.div`
border: 2px solid black;
height: ${boxHeight}px;
padding: 0 2rem;
width: 4rem;
transition: all 3s linear;
`;
return (
<div>
<div
className="box"
style={{ border: "2px solid black", height: `${boxHeight}px` }}
>
box 1
</div>
<Box>box 2</Box>
<button onClick={() => setBoxHeight(boxHeight + 100)}>+100</button>
</div>
);
};
/* App.css */
.box{
border: 2px solid black;
padding: 0 2rem;
width: 4rem;
transition: all 2s linear;
}
My guess is that when the style of a component created by styled-components changes, the component is destroyed and a new one is created with the changed style, rather than just modifying the component.
I wonder if there is any good solution to use transition through styled-components .
Thanks to my colleague, now I know that I should create the component created by styled-components outside of another component. If it is created inside the component, it will be recreated every time it is rendered.
import React, { useState } from "react";
import styled,{css} from "styled-components";
import "./App.css";
const Box = styled.div`
${props=>css`
height: ${props.height}px;
`}
border: 2px solid black;
padding: 0 2rem;
width: 4rem;
transition: all 3s linear;
`;
const App = () => {
const [boxHeight, setBoxHeight] = useState(20);
return (
<div>
<div
className="box"
style={{ border: "2px solid black", height: `${boxHeight}px` }}
>
box 1
</div>
<Box height={boxHeight}>box 2</Box>
<button onClick={() => setBoxHeight(boxHeight + 100)}>+100</button>
</div>
);
};
export default App;

How style endIcon of styled-component button?

Here is my simple styled button
import styled from 'styled-components';
import Button from '#material-ui/core/Button';
import ArrowForwardIosIcon from '#material-ui/icons/ArrowForwardIos';
const MyButton = styled(Button)`
font-size: 11px;
`;
<MyButton
variant="outlined"
color="primary"
size="small"
disableElevation
endIcon={<ArrowForwardIosIcon />}
>
CLICK ME
</MyButton>
So how do I change endIcon size. I can change it in Chrome dev tool but have no idea what to add to MyButton definition. Assume it should be something like this in styled button definition:
&.MuiButtonendIcon {
color: green;
font-size: 15px;
}
you can style the individual endIcon by targeting the svg element and setting its font-size property like this
const MyButton = styled(Button)`
& .MuiButton-endIcon svg {
font-size: 50px;
color: green;
}
`;
I solved it this way:
const MyButton = styled(Button)`
*:first-of-type svg{
font-size: 50px;
color: green;
}
`;

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

Styled component does not inherit styles when using "as" attribute

I'm using styled-system with styled components and have a basic case like this:
const buttonFont = {
fontFamily: "Chilanka"
};
// style a boilerplate for text
const Text = styled.div`
${typography}
${color}
`;
// button blueprint
const Button = ({ children, ...rest }) => {
return (
<Text as="button" {...buttonFont } {...rest}>
{children}
</Text>
);
};
// styled button using button
const StyledButton = styled(Button)`
color: white;
background-color: gray;
padding: 10px 20px;
border: 2px solid black;
`;
// When using "as" this component does not includes buttonFont styles
const StyledLabel = styled(StyledButton).attrs({
as: "label"
})``;
I want to create a StyledLabel which will inherit all styles from StyledButton, but change tag to label. But StyledLabel does not get the buttonFont styles when using "as" attribute.
Please see live example here: demo
I'm not sure what your end goal is, but these 2 examples worked in terms of inheritance. However, they might not help with your plan for composition:
import React from "react";
import styled, {css} from "styled-components";
import { typography, color } from "styled-system";
import ReactDOM from "react-dom";
import "./styles.css";
const buttonFont = {
fontFamily: "Chilanka"
};
const Text = styled.div`
${typography}
${color}
margin: 24px;
`;
const StyledButton = styled(Text)`
color: white;
background-color: gray;
padding: 10px 20px;
border: 2px solid black;
`;
const StyledLabel = styled(StyledButton)`
color: yellow;
`;
const __Text = styled.div`
${typography(buttonFont)}
${color}
margin: 24px;
`;
const __StyledButton = styled(__Text)`
color: white;
background-color: gray;
padding: 10px 20px;
border: 2px solid black;
`;
const __StyledLabel = styled(__StyledButton)`
color: yellow;
`;
function App() {
return (
<div className="App">
<StyledButton as="button" {...buttonFont}>styled button</StyledButton>
<StyledLabel as="label" {...buttonFont}>Does inherit styled font with "as"</StyledLabel>
<br />
<br />
<br />
<__StyledButton as="button">styled button</__StyledButton>
<__StyledLabel as="label">Does inherit styled font with "as"</__StyledLabel>
</div>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);

Resources