How to use template literals in tailwindcss to change classes dynamically? - css

I tried to change it with this line of code it but it doesn't work
const [click, setClick] = useState(false);
const closeNav = () => {
setClick(!click);
};
const openNav = () => {
setClick(!click);
};
<div
className=" absolute inset-0 ${click ? translate-x-0 : -translate-x-full }
transform z-400 h-screen w-1/4 bg-blue-300 "
>
<XIcon onClick={closeNav} className=" absolute h-8 w-8 right-0 " />
</div>;

Do it like this:
<div className={`absolute inset-0 ${click ? 'translate-x-0' : '-translate-x-full'} transform z-400 h-screen w-1/4 bg-blue-300`}></div>
// Alternatively (without template literals):
<div className={'absolute inset-0 ' + (click ? 'translate-x-0' : '-translate-x-full') + ' transform z-400 h-screen w-1/4 bg-blue-300'}></div>
Just keep in mind not to use string concatenation to create class names, like this:
<div className={`text-${error ? 'red' : 'green'}-600`}></div>
Instead you can select complete class name:
<div className={`${error ? 'text-red-600' : 'text-green-600'}`}></div>
// following is also valid if you don't need to concat the classnames
<div className={error ? 'text-red-600' : 'text-green-600'}></div>
As long as a class name appears in your template in its entirety, Tailwind will not remove it from production build.
There are some more options available for you like using a library like classnames or clsx, or maybe Tailwind specific solutions like twin.macro, twind, xwind.
Further Reading:
React.js conditionally applying class names
How to dynamically add a class to manual class names?
Correct way to handle conditional styling in React
Embedding Expressions in JSX
Template literals - MDN
Optimizing for Production - Writing purgeable HTML - Tailwind CSS

const bgClass: any = {
gray: " bg-gray-300",
red: " bg-red-300",
orange: " bg-orange-300",
yellow: " bg-yellow-300",
green: " bg-green-300",
teal: " bg-teal-300",
blue: " bg-blue-300",
indigo: " bg-indigo-300",
purple: " bg-purple-300",
pink: " bg-pink-300 ",
}
const convertLabelToBg = (label: string, baseClass: string): string => {
let className: string = baseClass;
if (label) {
className += bgClass[label];
}
return className;
}
It worked for me.
I have followed the documentation.
https://tailwindcss.com/docs/content-configuration#dynamic-class-names

Related

Using arbitrary runtime strings for Tailwind CSS class positioning

How can I specify a div's position from runtime data? Should I use some hook to set the className string? I find a lot of the time it's async issues that cause this kind of problem.
Anyway I was hoping someone could point me in the right direction, it's possible what I want to do isn't even supported by tailwind:
{timeRecord.clockedTimes.map((time, index) => {
let date = new Date(time.start);
let topOffset = getTopOffset(date).toFixed(4);
let stringOffset = `top-[${topOffset}%]`;
let className = "absolute " +stringOffset+" outline outline-red-400 w-1/2 left-1/2";
return (
<div className={className} >
{stringOffset}
</div>
)
})}
If I copy the text displayed inside the div by rendering the stringOffset from within the div and remove the composition of the className and just make it a static string with the runtime data copy and pasted it set's the right position.
Tailwind isn't built dynamically upon render. If you know what the value is before you compile you can include it or us something like cx to append a className, but you'll have to style in this case, you may need to play with the style prop a bit:
interface getTopOffsetProps {
timeDate: Date
}
const getTopOffset = ({ timeDate }: getTopOffsetProps) => {
return timeDate
}
interface ClockedTimes {
time: string
}
const ChildComponent = ({ time }: ClockedTimes) => {
const date = new Date(time)
const stringOffsetStyle = `${getTopOffset({ timeDate: date })}`
return (
<div className="absolute outline outline-red-400 w-1/2 left-1/2" style={{ top: stringOffsetStyle }}>
{stringOffset}
</div>
)
}
interface ParentComponentProps {
timeRecord: string[]
}
const ParentComponent = ({ timeRecord }: ParentComponentProps) => {
return (
<div>
{timeRecord.map((time, index) => {
<ChildComponent time={time} />
})
</div>
)
}

Transition doesn't work on child component, but works when applied to the parent component

I'm trying to animate the width of a div of a child component. My code is simple:
export const OuterComponent = () => {
const sidebarCtx = useContext(SidebarContext);
const style =
"w-[100px] h-[60px] transition-all duration-1000 bg-purple-900 text-2xl text-white " +
(sidebarCtx.isOpen ? " w-[400px] " : "");
const InnerComponent = ({ children }) => {
return (
<div className={style}> // if I apply style just to this div transition won't work anymore
{children}
<a>testing</a>
</div>
);
};
return (
<div className={style}> // if I apply style here and remove the above style on the InnerComponent transition works OK as normal
<InnerComponent />
</div>
);
};
As explained in the code comments, the problem is that if I apply style to InnerComponent directly, the width change is not animated.
What is happening since you have defined InnerComponent inside OuterComponent, it is reinitiated on each OuterComponent render, and that is probably messing with your animation.
try this:
const InnerComponent = ({ children, classNames }) => {
return (
<div className={classNames}> // if I apply style just to this div transition won't work anymore
{children}
<a>testing</a>
</div>
);
};
export const OuterComponent = () => {
const sidebarCtx = useContext(SidebarContext);
const classNames =
"w-[100px] h-[60px] transition-all duration-1000 bg-purple-900 text-2xl text-white " +
(sidebarCtx.isOpen ? " w-[400px] " : "");
return (
<div className={style}> // if I apply style here and remove the above style on the InnerComponent transition works OK as normal
<InnerComponent classNames={classNames} />
</div>
);
};
Through CSS you can just apply the transition to the parent element; then set overflow of the parent element to hidden. I think this should work.
I'm not sure but u can try this.
const style =
`w-[${sidebarCtx.isOpen ? 400 : 100}px] h-[60px] transition-all duration-1000 bg-purple-900 text-2xl text-white`.

Variables as a color?

I know that tailwind default, not support variable as parameter CSS like color etc. Are there any hacks on the market for this?
My code:
code in .tsx file:
<Disclosure.Button className={`${error}`}>
tailwind.config:
error: "#cf222e",
You can do like this:
First define the color in tailwind.config.js file like this:
module.exports = {
theme: {
colors: {
// Configure your color palette here
'error':{
100:'#cf222e',
200:'#cf222e',
300:'#cf222e',
.
.
900:'#cf222e',
}
}
}
First you can use this color in the .tsx file directly.
<Disclosure.Button className="text-error-100">
Second You can define as variable like this
const errorColor = `text-error-100 bg-error-300`
and then use it like this
<Disclosure.Button className={`${errorColor}`}>
Third you can also add some condition like this
function change_error_color(res) {
if(res === "small_error") return `text-error-100`
else if(res === "large_error") return `text-error-900`
}
And use it like
<Disclosure.Button className={`${change_error_color(res)}`}>
At the moment my fast hack:
const DisclureCustom = ({ title, color = "metal", children }: Props) => {
const switchColor = ` text-${color} bg-${color}/60 hover:bg-${color}/25 focus-visible:ring-${color}`;
return (
<Disclosure>
{({ open }) => (
<div className="pt-2">
<Disclosure.Button
className={
switchColor +
`flex justify-start w-full px-4 py-2 rounded-lg focus:outline-none focus-visible:ring focus-visible:ring-opacity-75`
}
> ...
</Disclosure>
);
};
export default DisclureCustom;

Convey complex css in tailwindcss

Functional css make DX a pleasant, but I always wonder how do we convey some thing like
.item + item {
margin-top: 20px;
}
into our template using tailwindcss ?
You may write your own plugin for this
// tailwind.config.js
const plugin = require('tailwindcss/plugin')
module.exports = {
plugins: [
plugin( ({ addVariant, e }) => {
addVariant('item', ({ modifySelectors, separator }) => {
modifySelectors(
({ className }) => {
const eClassName = e(`item${separator}${className}`); // escaped class. This is like `item:bg-red-500` basically
return `[class^="item"] + .${eClassName}`; // this is your CSS selector
}
)
})
})
],
}
I chose this [class^="item"] selector (class starts with item) as it is allows not to have extra classes in HTML layout but it is up to you which you want to have
Use it as custom Tailwind variant like item:any-tailwind-class
<div class="item:mt-5 item:bg-red-500">1 - No `item` above - no CSS applied</div>
<div class="item:mt-5 item:text-yellow-500">2 - There is `item` above - CSS applied</div>
<div class="item:mt-5 item:bg-red-500 item:text-yellow-500">3</div>
<div class="item:mt-5 item:bg-red-500">4</div>
<div class="item:mt-5">5 - Has margin-top</div>
<div class="">6 - Nothing here</div>
<div class="item:bg-red-500">7- No `item` above - no color applied</div>
<div class="item">8 - This class starts with `item` so the next div should have styles</div>
<div class="item:mt-5 item:bg-red-500">9 - There is `item` above - CSS applied</div>
DEMO

Tailwind CSS : Is there a way to target next sibling?

I have a radio input with a label like below. input is hidden and label is used to make a visually appealing circle on which user can click.
<input id="choice-yes" type="radio" class="opacity-0 w-0 fixed"/>
<label for="choice-yes" class="transition duration-500 bg-blue-300 hover:bg-blue-500 w-20 h-20 rounded-full mr-5 flex items-center align-middle justify-center">Yes</label>
When user clicked on the label input get checked. I'm trying to figure out how to target that, so I could give the label a different style.
This can be achieved in pure css using next sibling selector.
input[type="radio"]:checked + label {
background-color: #bfb !important;
border-color: #4c4 !important;
}
Is there something similar in tailwind.css that I could use instead?
For those looking to implement this, since introduction of just-in-time mode (Tailwind CSS v2.1+) sibling selector variant is available.
Here's a complex variant I wrote for this purpose (tested with tailwindcss 2.0.1)
// tailwind.config.js
const plugin = require("tailwindcss/plugin");
const focusedSiblingPlugin = plugin(function ({ addVariant, e }) {
addVariant("focused-sibling", ({ container }) => {
container.walkRules((rule) => {
rule.selector = `:focus + .focused-sibling\\:${rule.selector.slice(1)}`;
});
});
});
module.exports = {
// ...
plugins: [focusedSiblingPlugin],
variants: {
extend: {
backgroundColor: ["focused-sibling"],
},
},
};
There isn't anything built into Tailwind presently. The closest feature would be Pseudo-Class Variants of which there is a "checked" prefix. But that would only apply to the radio itself. In order to target siblings you'd need to write your own complex variant and then you could do something like
<input id="choice-yes" type="radio"/>
<label for="choice-yes" class="bg-gray-100 sibling-checked:bg-blue-500">Yes</label>
So, I've been wondering the same and since I've created my own variants since my Tailwind debut, I thought I could create a quick one for you.
My problem with previous answers is that the class is focused on the sibling. The class should be appended to the first item and used as such :
<input id="choice-yes" type="radio" class="nextOnChecked:bg-blue-500 nextOnChecked:border-blue-800"/>
The code for the variant should be something like :
const plugin = require("tailwindcss/plugin");
const nextOnChecked = plugin(function ({ addVariant, e }) {
addVariant('nextOnChecked', ({ modifySelectors, separator }) => {
modifySelectors(({ className }) => {
return `.${e(`nextOnChecked${separator}${className}`)}:checked + *`;
})
});
});
module.exports = {
variants: {
extend:{
border: ['nextOnChecked'],
backgroundColor: ['nextOnChecked'],
},
},
plugins: [
nextOnChecked
],
};
This part .${e('nextOnChecked${separator}${className}')}:checked + * will be used, in the context of this example, as the following :
.nextOnChecked\:bg-blue-500:checked + *{
background-color: ...;
}
That's how I build most of my variants, from the DOM that changes the state to the DOM that is being re-styled.
You can use group-hover for this situation.
https://tailwindcss.com/docs/hover-focus-and-other-states
Since Tailwind v2.1 the JIT engine supports the peer variant, who works similarly to group, but is based on state of direct sibling.
Ex.:
<input id="choice-yes" type="radio" class="peer opacity-0 w-0 fixed"/>
<label for="choice-yes" class="peer-checked:bg-[#bfb] border peer-checked:border-[#4c4] transition duration-500 bg-blue-300 hover:bg-blue-500 w-20 h-20 rounded-full mr-5 flex items-center align-middle justify-center">Yes</label>
Source: https://v2.tailwindcss.com/docs/just-in-time-mode#sibling-selector-variants

Resources