Dynamic CSS in CSS Modules when coming from Styled Components - css

The following question came up when I wanted to migrate from Styled Components to CSS Modules.
Let's say I have the following styled component which accepts a dynamic parameter offset and a dynamic CSS string theme:
const Li = styled.li`
&.selected {
background-color: grey;
}
margin-left: ${({ offset }) => offset}px;
${({ theme }) => theme};
`;
In my code, I'd use it the following way:
const Parent = () => (
<List>
{list.map((item) => (
<Item
key={item.id}
id={item.id}
selectedIds={[]}
offset={24}
theme={`
&.selected {
background-color: green;
}
`}
>
{item.name}
</Item>
))}
</List>
);
const Item = ({ id, offset = 0, theme, children }) => {
return (
<Li
theme={theme}
offset={offset}
className={selectedIds.includes(id) && 'selected'}
>
{children}
</Li>
);
};
Requirement: Now I would really keep the Item's component API: passing a number offset and a style string theme. So essentially everything in Parent component should stay this way.
How can I convert the Item component internally to use CSS Modules instead of the styled Li component?

It's probably a different way of thinking than you used to but it can work
You can use css variable
style={{ [`--offset`]: `${offset}px` }}
.item {
margin-left: var(--offset);
}
You can have a css module (file) dedicated to themes. In your case, it has withSelected
.withSelected {
&.selected {
background-color: green;
}
}
So you could pass it as "theme"
theme={themes.withSelected}
This is how the components look
import styles from "./style.module.scss";
import themes from "./themes.module.scss";
const Parent = () => (
<ul>
{list.map((item) => (
<Item
key={item.id}
id={item.id}
selectedIds={[1]}
offset={24}
theme={themes.withSelected}
>
{item.name}
</Item>
))}
</ul>
);
const Item = ({ id, offset = 0, theme, children, selectedIds }) => {
return (
<li
className={`${styles.item} ${theme} ${
selectedIds.includes(id) && themes.selected
}`}
theme={theme}
style={{ [`--offset`]: `${offset}px` }}
>
{children}
</li>
);
};
Demo: https://codesandbox.io/s/styledcomponent-to-css-modules-1kbqx

With 1 I'd concur with #Mosh to just use the style prop. Css modules are static by design and there no way to get this done otherwise (I think that styled-components also uses the style prop so you're not losing anything).
For 2 you can leverage Sass modules which allow you to define your theme in a single place and import them as required:
/theme/_colors.scss
$red: rgb(200, 0 50);
/components/Item.module.scss
#import "../theme/colors"
.selected {
background: $red;
}
Note: if you use Create React App absolute paths you can import from root as ~theme/colors

Related

Why inline CSS is not working on component in react?

I'm using a component in react.
import style from "../../Style/Styles.module.scss";
const Anchor = (props) => {
return <a className={style["anchorTags"]} href={props.href}>{props.title}</a>;
};
export default Anchor;
where I have already written CSS for this component in my SCSS file.
.anchorTags {
border: 2px solid $Very-Light-Gray;
color: $Very-Light-Gray;
}
but when I'm writing inline css in a component when using this on another component it is not working.
import style from "../../Style/Styles.module.scss";
import Anchor from "./achortag";
<Anchor
style={{ paddingBlock: "0.7em", paddingInline: "2em" }}
href="/viewPlan"
title="VIEW PLANS"
/>
Please share your suggestion on how this will work?
In Anchor component:
const Anchor = (props) => {
return (
<a className={style.anchorTags} href="#" style={props.style}>
{props.title}
</a>
);
};
export default Anchor;
or use the spread operator, this will add any additional attributes:
const Anchor = (props) => {
return (
<a className={style.anchorTags} href="#" {...props}>
{props.title}
</a>
);
};
export default Anchor;
To use the desired style, you must use this syntax:
import style from "../../Style/Styles.module.scss";
const Anchor = (props) => (
<a className={style.anchorTags} href={props.href} {...props}>{props.title}</a>;
);
export default Anchor;
For more information, you can read this (Adding a CSS Modules Stylesheet).

How do you style your React.js components differently depending on where you are using them in your application?

Let's say you have a navbar and when you're using this component on your homepage you want it to have a certain background color and display property, but when you use that same navbar component on another page in your application you want to change these CSS properties. Seeing as the component has one CSS file linked how would you change the style of a component depending on where it is being rendered?
My personal favourite method nowadays is styled components. Your component might look something like this:
// NavBar.js
import styled from 'styled-components'
const StyledDiv = styled.div`
width: 100%;
height: 2rem;
background-color: ${props => props.bgColor};
`
const NavBar = (bgColor) => {
return <StyledDiv bgColor={bgColor}>
}
Then to use it in your different contexts you simply pass the color prop:
// homepage.js
<NavBar bgColor="red" />
// otherpage.js
<NavBar bgColor="#123ABC" />
Styled components are becoming a very popular way of doing things, but be aware that there are a huge array of ways you can do this.
https://styled-components.com/
(Code not tested)
Well If you just want to use plain CSS then you can change the className based on route so the styles also changes.
Example:
import { useLocation } from "react-router-dom";
const Navigation = () => {
let location = useLocation();
...
return(
<nav className={location.pathname === "/home" ? "homepage-navbar" : "default-navbar"}>
...
</nav>
)
}
You can write longer condition for multiple pages as well.
Other better thing you can do is pass the location.pathname and value of className as prop.
import { useLocation } from "react-router-dom";
const Home = () => {
let location = useLocation();
...
return (
<>...
<Navigation location={location.pathname} styleClass={"homepage-navbar"}/>
</>
)
}
const Navigation = ({location, styleClass}) => {
...
return(
<nav className={location === "/home" ? styleClass : "default-navbar"}>
...
</nav>
)
}
So now you can pass different values for className from different components and get different styles for the navbar.

Re-writing CSS in Styled-Components

I am currently working on a project which involves a dropdown menu, and I am following a tutorial. An issue that I encountered is with the dynamic styling of the dropdown.
<ul className={`dropdown ${dropdown ? "show" : ""}`}>
this is the code from the tutorial, my question is how can I rewrite it using styled-components?
// my Dropdown component
const Dropdown = ({ submenus, dropdown }) => {
return (
<StyledDropdown dropdown={dropdown}>
{submenus.map((submenu, index) => (
<li key={index}>
{submenu.title}
</li>
))}
</StyledDropdown>
);
};
export default Dropdown;
//styling of dropdown
export const StyledDropdown = styled.ul``;
You can get props in styled components
import styled, { css } from 'styled-components'
export const StyledDropdown = styled.ul`
${props => props.dropdown ? css`
// Style for show
` : ''}
`;

In Mui, how do I use theme values in my css

My React app is using mui themes; index.js contains:
let theme = createTheme({
palette: {
primary: {
main: "#00aaa0",
contrastText: '#fcf4d9',
},
secondary: {
main: "#D55B3E",
contrastText: '#fcf4d9',
},
},
});
theme = responsiveFontSizes(theme);
ReactDOM.render(
<ThemeProvider theme={theme}>
<CssBaseline />
<App />
</ThemeProvider>
document.getElementById('root')
);
In one of my components (footer) I want to be able to add a top border which is in the primary colour. So far footer.tsx contains:
const Footer= () => {
return (
<div className="pageFooter">Footer Text</div>
);
};
export default Footer;
I want to style "pageFooter" so that it uses theme.palette.primary.main as a top border colour. With regular css I would link in my css file containing:
.pageFooter {
border-top: 2px solid "#00aaa0";
}
but I want to make sure that the colour is always the same as the primary colour, so I want to do something like this:
.pageFooter {
border-top: 2px solid theme.palette.primary.main;
}
This doesn't work, though, presumably because the theme is not available to the css file. I've read the docs and I can't really follow them. Can anyone explain what I should be doing here?
import { makeStyles } from '#material-ui/core'
const useStyles = makeStyles(theme => ({
pageFooter: {
borderColor: theme.palette.primary.main,
}
});
const Footer= () => {
const classes = useStyles()
return (
<div className={classes.pageFooter}>Footer Text</div>
);
};
export default Footer;
according to the MUI docs:
#mui/styles is the legacy styling solution for MUI. It depends on JSS as a styling solution, which is not used in the #mui/material anymore, deprecated in v5.
and also
#mui/styles is not compatible with React.StrictMode or React 18.
MUI use JSS for styling. so we don't have access to theme in CSS files.
my solution:
you can define a CSS variable same color as your theme palette color. then use it in CSS files.
:root {
--primary-main: #00aaa0;
}
.pageFooter {
border-top: 2px solid var(--primary-main);
}
It's supported now by MUI v5.6.0 as an experimental feature. They export theme variables as CSS variables. Read the docs.
Example:
/* external-scope.css */
.external-section {
background-color: var(--mui-palette-grey-50);
}

How can I dynamically set style of a mapped element without using inline styling?

I'm coding a React function with parent component containing an array of objects:
let const ingredients = [
{name:"lettuce",color:"green"},
{name:"tomato",color:"red"}
]
...
In a child component, there is a map function that breaks down an array to single items to be displayed in a div.
What is the best practice for defining CSS styling for an object className:"name" to set backgroundColor: {ingredient.color};? I'm trying to avoid manual entry of the entire set of key/values of 'ingredients', to allow updating the object without breaking the code.
I'm currently using inline styling, which I have been advised against. Currently using:
let burg = props.toppings.map((item) => {
const divColor = {backgroundColor: item.color};
return (<div style={divColor}>{item.name}</div>)
Inline style is bad when you have other solution to do what you want. Here, you have a string that is the color (red, green, etc.) so you could write a css class for every color, but that is of course a really bad idea. Inline style is the good way to do it here.
I would suggest setting the class of the div instead of the style. That way you can change the look without resorting to inlining the style.
You could create a css class for lettuce with the background color green, instead of using the item.color you'd set class={ item.name }
You can use this way.
css can be more handy if you use scss
// css
.color-green {
color: green;
}
.color-red {
color: red;
}
import React from "react";
import "./styles.css";
const ingredients = [
{ name: "lettuce", color: "green" },
{ name: "tomato", color: "red" }
];
const Vegetable = ({ color, text }) => {
return (
<p>
this is <span className={`color-${color}`}>{text}</span> color{" "}
</p>
);
};
export default function App() {
return (
<div className="App">
<h1>Hello CodeSandbox</h1>
<h2>Start editing to see some magic happen!</h2>
{ingredients.map((item, index) => {
return <Vegetable key={index} color={item.color} text={item.name} />;
})}
</div>
);
}

Resources