Tailwind not working when using variables (React.js) - reactjs

currently have been facing this issue using tailwind and making rehusable react components where you can pass as a prop some styles as tailwind classes. The actual problem is with the "pb-{number}" propierty. I can pass it this way and will work fine. This also happens with "border-{number}" property, but someway it accepts border-2 and border-4 (only these).
import './button.css'
export default function Button({
color = "orange",
inset = "pb-3", <--- this will work
border = "border-8",
className,
onClick,
link
, ...props}){
return (
<div onClick={onClick}
className={`btn-${color} ${border}
${className} ${inset}`}> <--- this will work
<div>
{props.children}
</div>
</div>
But if I try to make it cleaner so a person who don't use tailwind only has to pass a value (like the example below) it wont work.
import './button.css'
export default function Button({
color = "orange",
inset = "1", <--- this
border = "4",
className,
onClick,
link
, ...props}){
return (
<div onClick={onClick}
className={`btn-${color} border-${border}
${className} pb-${inset}`}> <--- this wont work
<div>
{props.children}
</div>
</div>
)
}
Sincerely I have no idea why is this happening. Hope someone with more experience can clarify my doubt.
Thanks in advance.

In tailwind you can't use dynamic class naming like bg-${color}, though we can mock it to be that, but it is not preferred. Because Tailwind compiles its CSS, it looks up over all of your code and checks if a class name matches.
For your approach you can try this.
const Button = () => {
const color = "red-500";
const inset = "3";
const border = "border-8";
return <div className={`bg-${color} ${border} pb-${inset}`}>Hello</div>;
};
export default Button;
Output with proper padding is applied
But try avoiding this workaround, as it is not recommended by the Tailwind CSS.

Related

TailwindCSS styles not rendered when applied dynamically in NextJs

To set the background cover using TailwindCSS I have extracted the color from bookId (10 digit number) inside useEffect. The color gets updated and component re-renders with the updated color value but the background color on the rendered page is still the same of its parent div.
const colors = [
'from-red-500',
'from-orange-500',
'from-yellow-500',
'from-green-500',
'from-cyan-500',
'from-blue-500',
'from-indigo-500',
'from-violet-500',
'from-purple-500',
'from-pink-500',
]
function BgCover(props) {
const [color, setColor] = useState(null)
const router = useRouter()
useEffect(() => {
const id = router.query.bookId
const index = id.slice(-1) //extract the index from bookId
const bgColor = colors[index]
setColor(bgColor)
}, [])
return (
<Fragment>
{color ? (
<div className='flex-grow scrollbar-hide select-none relative'>
<div className={`bg-gradient-to-b ${color} to-black`}>
<section
className={`flex flex-col md:flex-row items-center justify-center p-4`}>
{props.children}
</section>
</div>
</div>
) : (
<p className='text-2xl'>Loading..</p>
)}
</Fragment>
)
}
But when I replace the color variable with a color value (say 'from-red-500') then the background color is visible in the rendered page.
I have also tried to replace the setColor code in useEffect with getStaticProps but the static version of the code cannot solve this problem (when color varible is used).
Thanks for any help.
This is a known issue with tailwindcss and dynamic classes, because the class is applied after rendering so its effect won't be generated by tailwind unless there was another element in that had the same class as a static class.
So, you can use tailwind "safelist" to resolve this.
In your tailwind.config, define a safelist array of all tailwind classes that you need to be generated and that don't exist in your code as static classes.
tailwind.config.js:
module.exports = {
content: [
'./pages/**/*.{html,js}',
'./components/**/*.{html,js}',
],
safelist: [
'from-red-500',
'from-orange-500',
'from-yellow-500',
'from-green-500',
'from-cyan-500',
'from-blue-500',
'from-indigo-500',
'from-violet-500',
'from-purple-500',
'from-pink-500',
]
// ...
}
Now this classes will always get generated so when you apply them dynamically, they will change accordingly.
Note that you need to restart your server after adding to the safelist.
Source
Another manual solution is to make a hidden element and add all your needed classes to it so they will be generated even if you get them dynamically after render
<div className="hidden from-red-500"></div>
but safelist is better I think

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.

Tailwind's background color is not being applied when added dynamically

I am trying to set dynamic background colors using Tailwind.
However, the background color is not being applied to the div. I am confused because when I check the inspector, I can see that in the browser, the correct bg-${colors[index]} was applied to each div, but the color is not being rendered.
const colors = ['#7a5195', '#bc5090','#ef5675']
export default function App() {
const names = ['Tyler', "Charles", 'Vince']
let labels = {}
names.forEach((name,index)=>{
labels[name] = `bg-[${colors[index]}]`
})
return (
<>
{
names.map((name)=>{
return(
<div className={`${labels[name]}`}>
{name}
</div>
)
})
}
</>
);
}
in Tailwind you can't use dynamic class naming like bg-${color}.
This because when Tailwind compiles its CSS, it looks up over all of your code and checks if a class name matches.
If you want dynamic name classes you should write all the class name.
But for your specific use case, I would not use the JIT of Tailwind and instead use the style attribute and dynamically change the backgroundColor value.
It will use less CSS and also give you less headache.
Finally, this is my suggestion
const colors = ['#7a5195', '#bc5090','#ef5675'];
export default function App() {
const names = ['Tyler', "Charles", 'Vince']
const labels = {};
names.forEach((name, index) => {
labels[name] = colors[index];
});
return (
<>
{
names.map((name) => (
<div style={{ backgroundColor: `${labels[name]}` }}>
{name}
</div>
)
}
</>
);
}

Having trouble with onMouseEnter event in React

I'm trying to build a navbar, using React and hooks, where each div will change to a specific color on an onMouseEnter and onMouseLeave. I can't figure out why they're all affected if i hover over one. I guess I'm asking how I could make them independent of one another.
Sorry if this is a really obvious mistake. Still really green. Thanks again!
Here is a link to the CodeSandbox: https://codesandbox.io/s/holy-snowflake-twojb?file=/src/navbar.js
I refactored your code.
You can check it in https://codesandbox.io/s/lucid-lake-u3oox?file=/src/navbar.js
You are using the function setBackground to set the background color in the hoverStyle CSS class which affects to all <div className="hoverStyle"> tags.
There are many options to do that. If you want to do it with React, one way of do it is creating a CSS class like this:
.active {
background-color: #ffac4e;
}
then, create a functional component
const Activable = ({ className, children, bgColor}) => {
const [active, setActive] = useState('#fff');
return (
<div className={`${ className } ${ active }`
onMouseEnter = {() => setActive( bgColor )}
onMouseLeave = {() => setActive('#fff')}
>
{ children }
</div>
)
}
then replace your ` with this new component like this:
<Activable className="hoverStyle" bgColor="#ffac4e">
<div style = {navChildLeft}>2020</div>
<div style = {navChildTop}>
Shy RL <br />
Digital - Album art
</div>
</Activable>
and repeat with the rest of <div>'s
With react, it is important to think about reusable components. How can you create something that you can reuse over and over again in different parts of your project.
Take a look at this sample of your project broken into components.
https://codesandbox.io/s/holy-brook-3jror?fontsize=14&hidenavigation=1&theme=dark
I would recommend reading this to help out: https://reactjs.org/docs/thinking-in-react.html

Dropdown menu flip position issue. React-Select + Material-UI Popper

I use the example autocomplete field from the Material-UI lib documentation. (https://material-ui.com/demos/autocomplete/#react-select)
There is a problem with fliping the menu when it opens at the bottom of the page or the browser's viewport.
Is there a way to fix this problem with Material-UI and react-select?
Or do I need to write something custom?
If you are not using a <Menu/> custom component, you can use the menuPlacement="auto" prop of <Select/>, then your problem is solved.
const components = {
Control,
// Menu , <-- delete it
NoOptionsMessage,
Option,
Placeholder,
SingleValue,
ValueContainer
};
https://github.com/JedWatson/react-select/issues/403
Otherwise you can choose another selector, material-ui provides 2 more differents integration with the <Popper/> component: react-autosuggest and downshift.
https://material-ui.com/demos/autocomplete/
Hope it helps!
i've faced the same problem, for <Select /> component i have used what TomLgls suggest, but for <AsyncSelect /> as a work-around, i used some offset calculations in my component :
const rootHeight = document.getElementById('root').offsetHeight ;
const selectElement = document.getElementById('async_select_container_id');
const selectOffsetBottom= selectElement.offsetHeight + selectElement.offsetTop;
...
<AsyncSelect
{...listProps}
menuPlacement={
rootHeight - selectOffsetBottom > 210 ? 'auto' : 'top' // react-select menu height is 200px in my case
}
/>
i hope it helps as well
If you have created customMenu component then in that give className as open-menu-top and write this code for class:
.menu-open-top {
top: auto;
bottom: 100%;
}
Your CustomMenu maybe look like this:
const CustomMenu = ({ children, innerProps, innerRef, selectProps }) => {
return (
<div
ref={innerRef}
{...innerProps}
className={`rs-menu ${customMenuClass} open-menu-top`}
>
{Your Logic}
</div>
);
};

Resources