Use React variables in Tailwind class names - reactjs

I've just recently picked up React and Tailwind for a project, and I am still very much a beginner. I wanted to make an element have a background image as a custom class variable, something like this:
<div className="bg-[url(`https://example.com/${variable}.png`)]"></div>
But as Tailwind purges classes, would this somehow be possible? I hope I'm not missing anything, but it doesn't seem doable to me right now

Tailwind has a feature called "safelisting" which may provide you with the functionality you need – see here in the docs.
That said:
1 - There's nothing wrong with using both className and style props on the same element – you can have e.g. <div className="w-full" style={`background: url(https://example.com/${variable}.png`}>
2 – Depending on what flavor of React you are using, your might be better off using a component like Next.js's Image to optimize your images instead of setting background via CSS. Below is how I do it:
import Image from "next/image";
export default function MyBackground(props) {
return (
<div
className="z-0 absolute inset-0 w-full h-full flex justify-center items-center"
>
<Image
placeholder="blur"
alt={props.alt}
src={props.src}
className="h-full w-full object-cover"
/>
</div>
);
}

You can define a different variable for this. In that way, you can manipulate the value accordingly and use that variable to set the styling properties of that element. The code will look something like this:
const YourComponent = () => {
//the variable named variable will hold the dynamic value as per your need
let elementStyle = "bg-[url(`https://example.com/" + variable + ".png";
return (<>
<div className={elementStyle}"></div>
</>
);
}

Related

Tailwind CSS unresponsive to react state change

This very simple toggle in react is not working with tailwind CSS. When I click the "toggle color" button, the className of the div changes between bg-[#222222] and bg-[#DDDDDD], but the div never actually colors -- it has the className, but tailwind doesn't seem to be able to fill in the background property.
import React, { useState } from "react";
function App() {
const [color, setColor] = useState('222222')
const toggleColor = () => color === '222222' ? setColor('DDDDDD') : setColor('222222');
return (
<div className='h-screen w-full flex flex-col justify-center items-center'>
<button onClick={toggleColor}>Toggle Color</button>
<div className={`bg-[#${color}] w-[100px] h-[100px]`}></div>
</div>
)
}
export default App;
To clarify, if I store the full string 'bg-[#2222222]' or 'bg-[#DDDDDD]' and then drop that directly into the className, it works. (AKA, in the below code segment, the div renders with the proper color and toggles properly:
import React, { useState } from "react";
function App() {
const [color, setColor] = useState('bg-[#222222]')
const toggleColor = () => color === 'bg-[#222222]' ? setColor('bg-[#DDDDDD]') : setColor('bg-[#222222]');
return (
<div className='h-screen w-full flex flex-col justify-center items-center'>
<button onClick={toggleColor}>Toggle Color</button>
<div className={`${color} w-[100px] h-[100px]`}></div>
</div>
)
}
export default App;
However, in the larger context of the application I'm hoping to build, this doesn't do me much good. It would be much more helpful to simply store the color. It's unclear to me why the first way is not working, considering that in both cases, the full string is successfully dropped into the className. Any ideas?
This is the expected behaviour of Tailwind and the reason is explained here: https://tailwindcss.com/docs/content-configuration#class-detection-in-depth
One option would be to use a safe list which is described here: https://tailwindcss.com/docs/content-configuration#safelisting-classes
Another solution for an exception like this is to ignore Tailwind and add a dynamic style to the element. For example:
<div className={'w-[100px] h-[100px]'} style={{'background-color': `#${color}`}}></div>
This might be more flexible than safelisting Tailwind classes, if you have a lot of colors or if you don't know the color value until runtime.

Conditionally set background color in React component with Tailwind CSS

I am trying to use a hex color code passed through props to set the background color of a div. These are one-off colors that are generated dynamically, so cannot be added as a theme extension in tailwind.config.
I thought a template literal would be the best way to achieve this, but have not been able to get this to work with arbitrary color values in Tailwind CSS.
interface Props {
color: string;
}
const ColorSwatch = ({ color }: Props) => {
return (
<div className="flex flex-col gap-1 p-2">
<div
className={`h-20 w-20 border border-gray-400 shadow-md bg-[${color}]`}
></div>
<p className="text-center">{color}</p>
</div>
);
};
export default ColorSwatch;
Pasting the hex color code directly into the className list produces expected results, but trying to use the prop value in a template literal results in a transparent background (no background effect applied).
Looking for advice on how to correct this or different approaches to dynamically setting background color with a hex code passed through props.
Unfortunately, Tailwind requires the color to be hardcoded into the className prop as it cannot compute arbitrary styles from dynamic className values.
Your best bet would be to set the background color using the style prop as shown below;
interface Props {
color: string;
}
const ColorSwatch = ({ color }: Props) => {
return (
<div className="flex flex-col gap-1 p-2">
<div
className="h-20 w-20 border border-gray-400 shadow-md"
style={{backgroundColor: color}}
></div>
<p className="text-center">{color}</p>
</div>
);
};
export default ColorSwatch;
You can look here and here to read more on how Tailwind generates arbitrary styles.

React: State of a variable changes, but UI Icon dosen't get updated

So I tried creating a simple navbar that would have its buttons controlled by useState. However I have a problem where the button icon color wont update even though the state of the variable that controls it changes.
Now, I did some testing and and added text into the icon component (not show here) and made it so it was controlled by the same state as the color on the icon is now. And for some reason when I did that the state and the text inside the component both changed correctly. Could anyone provide an explanation on why that happens? Because to me it seems like I've misunderstood how react binds things to states and controls them.
Navigation bar component
import NavButton from "./NavButton"
import { useState } from "react";
function NavBar(){
const [buttons, setButtons] = useState([
{id:1, name:"Orders", icon:"bx:bx-dollar-circle", active:false},
{id:2, name:"Menu", icon:"ic:round-restaurant-menu", active:false},
{id:3, name:"Leave", icon:"uil:exit", active:false}
]);
const toggleButton = (id) => {
setButtons(buttons.map(button => (
button.id === id ? {...button, active:!button.active} : {...button, active:false}
)))
}
return (
<div className="h-1/6 bg-white border-b-lebo flex flex-row justify-around">
<>
{buttons.map((button) => (<NavButton button={button} key={button.id} onToggle={toggleButton}/>))}
</>
</div>
)
}
export default NavBar;
Navigation button component
import Icon from "./Icon";
function NavButton({button, onToggle}){
return (
<button onClick={() => onToggle(button.id)} className={`font-bold text-gray-500 flex flex-col items-center justify-center flex-grow w-5 hover:bg-gray-100`}>
<p className="self-center">{button.name}</p>
<Icon icon={button.icon} name={button.name} color={button.active ? "#454545" : "#8b8b8b"}/>
</button>
)
}
export default NavButton;
Icon component
function Icon({icon, color, name}) {
return (
<div>
<span color={color} className="iconify h-10 w-auto self-center" data-icon={icon}></span>
</div>
)
}
export default Icon
I solved my problem by creating 2 different Icon components.
Icon and IconDark and conditionally rendering them inside the NavButton component.
Not sure if it is the "correct" way of doing things but it got the job done.
I'm going to guess the reason why it didn't render the colors correctly earlier is because of the attribute "color" inside the component. I think JSX just took it in as another prop and did nothing with it after the first render of the element.
edit 1: nvm it definitely didn't get the job done. At least not well enough. The icon swap in the render isn't fast enough so it causes the user to see the icon swap.
edit 2: This article held the answer that I needed.
https://dev.to/abachi/how-to-change-svg-s-color-in-react-42g2
It turns out that to change an svg color with react you need to set the initial fill (or for me color) value inside the svg component to "current" and then pass the real value in from the parent element conditionally.
Long story short - Controlling SVG values is a little different to controlling text values in react.

Props, TailwindCSS & Twin.Macro

I am trying to get the following component to work properly with Twin.Macro:
import "twin.macro";
const Pattern = ({ classes }) => {
return (
<div css={[classes.wrapper]}>
<img
src="..."
css={[classes.img]}
/>
</div>
);
};
In other words, I would like to receive the props via the classes prop and pass the values on to Twin.Macro. However, it is not working properly. For example, here is an implementation of that component:
<Pattern
classes={{
wrapper: "absolute -left-2 z-10 lg:left-0 top-0 h-full bg-contain bg-line-left-md lg:bg-line-left bg-no-repeat xl:bg-line-left-md hidden sm:block",
img: "h-full",
}}
/>
And here is the css that is output:
.css-w36jn9-Pattern {
absolute -left-2 z-10 lg: left-0 top-0 h-full bg-contain bg-line-left-md lg:bg-line-left bg-no-repeat xl:bg-line-left-md hidden sm:block;
}
As you can see, all it does is pass on the values of the tailwind css classes. It does not process those classes in the proper property-value pairs (such as position: absolute; left: -2px, etc).
I have tried different ways to get this to work, but nothing has been successful. Any idea what I am doing wrong and how to fix it?
Thanks.
accordingly to Twin.Macro when passing css property, you need to pass your classes to tw function using tagged template literals. given you are passing a variable use also string interpolation.
finally, your code should look like:
import tw from "twin.macro";
const Pattern = ({ classes }) => {
return (
<div css={[tw`${classes.wrapper}`]}>
<img
src="..."
css={[tw`${classes.img}`]}
/>
</div>
);
};

React-Materialize change brand text color

I think I have found an error with React-Materialize. Still new, probably won't stop people from being harsh ;).
Using the normal means of changing the text color i.e.
`className="black-text"`
does not work. I was forced to change it manually by finding the element
`nav .brand-logo {
color: black;
}`
and making the change within my own css file. I wanted to stay within the framework, but could not get anything to work. Here is my complete React-Materialize element:
`<Navbar brand="ARWA" className="transparent black-text" right>
<NavItem href="get-started.html">
<div className="blue-text">Getting started </div>
</NavItem>
<NavItem href="components.html">
<div className="blue-text"> Components</div>
</NavItem>
</Navbar>`
I took a look at the source code, but to no avail. No gleaming errors stood out to me. This may be an opportunity for someone better than I to make a contribution. Or at least see what I'm doing wrong and help me change the brand color within the React-Materialize framework.
Changing Text Color of React Attribute
If you want to edit the color of text within an attribute tag or add an image into the tag, you can put your code into a JSX variable and pass that variable into the attribute tag in react-materialize.
Changing Text Color
import YourStuff
import reactThings
const brandColor = <span style={{color: "#FFFFFF"}}>Your Brand</span>;
class Header extends Component {
...
<Navbar className="Header red" brand={brandColor} right />
}
brandColor changes whatever the default color was to #FFFFFF which is white. Make sure brandColor is outside of the component class.
Inserting Image
import Logo as "./logo.png";
const Img = <img src={Logo} alt={"YourBrand"}/>;
class Header extends Component {
...
<Navbar className="Header red" brand={Img} right />
}
The syntax for inserting an image is slightly different so I thought I'd include this in here along with your answer.
According to this you may need to wrap what you want to change in a div and change the class on that. Does that work?

Resources