Variables as a color? - tailwind-css

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;

Related

How to format tailwind classes in a cleaner way for re-usable styles dynamically?

I am making this reusable badge using Tailwind CSS where the colour of the badge is supposed to change depending on the status. The result is very unclean and I am looking for a way to optimise this.
I have tried using string literals to try make the styles cleaner as following;
bg-${color}-100 and same fo other classNames. Apparently this does not work because tailwind expects you to pass a full class name like this bg-green-100. This means I have to conditionally check for each colour as this
color === "green"? "bg-green-100" : "bg-red-100" this forces you to write so many classes if you have a lot of colour props that you are checking.
This is how to whole component looks like
import ctl from "#netlify/classnames-template-literals";
interface BadgeProps {
color: string;
status: string | boolean;
}
function Badge({ color, status }: BadgeProps) {
return <span className={badgeStyle(color)}>{status}</span>;
}
const badgeStyle = (color: string) =>
ctl(
`
${
color === "green"
? `bg-green-100 text-green-800 ${containerStyle} dark:bg-green-900 dark:text-green-300`
: color === "red"
? `bg-red-100 text-red-800 ${containerStyle} dark:bg-red-900 dark:text-red-300`
: color === "yellow"
? `bg-yellow-100 text-yellow-800 ${containerStyle} dark:bg-yellow-900 dark:text-yellow-300`
: color === "blue"
? "bg-blue-100 text-blue-800 ${containerStyle} dark:bg-blue-900 dark:text-blue-300"
: color === "indigo"
? `bg-indigo-100 text-indigo-800 ${containerStyle} dark:bg-indigo-900 dark:text-indigo-300`
: color === "purple"
? `bg-purple-100 text-purple-800 ${containerStyle} dark:bg-purple-900 dark:text-purple-300`
: color === "pink"
? `bg-pink-100 text-pink-800 ${containerStyle} dark:bg-pink-900 dark:text-pink-300`
: color === "orange"
? `bg-orange-100 text-orange-800 ${containerStyle} dark:bg-orange-900 dark:text-orange-300`
: color === "gray"
? `bg-gray-100 text-gray-800 ${containerStyle} dark:bg-gray-900 dark:text-gray-300`
: color === "gray"
? `bg-gray-100 text-gray-800 ${containerStyle} dark:bg-gray-900 dark:text-gray-300`
: ""
}
`
);
const containerStyle = ctl(`
font-medium mr-2 px-2.5 py-0.5 rounded text-sm text-sm
`);
export default Badge;
You could create a separate file that maps the different color props to the corresponding Tailwind classes.
Create file called colorClasses.js that exports an object with the different color props as keys, and the corresponding Tailwind classes as values.
colorClasses.js
export default {
green: 'bg-green-100 text-green-800 dark:bg-green-900 dark:text-green-300',
red: 'bg-red-100 text-red-800 dark:bg-red-900 dark:text-red-300',
yellow: 'bg-yellow-100 text-yellow-800 dark:bg-yellow-900 dark:text-yellow-300',
blue: 'bg-blue-100 text-blue-800 dark:bg-blue-900 dark:text-blue-300',
indigo: 'bg-indigo-100 text-indigo-800 dark:bg-indigo-900 dark:text-indigo-300',
purple: 'bg-purple-100 text-purple-800 dark:bg-purple-900 dark:text-purple-300',
pink: 'bg-pink-100 text-pink-800 dark:bg-pink-900 dark:text-pink-300',
orange: 'bg-orange-100 text-orange-800 dark:bg-orange-900 dark:text-orange-300',
gray: 'bg-gray-100 text-gray-800 dark:bg-gray-900 dark:text-gray-300'
}
Then in your component file, you can import the colorClasses object and use it to dynamically set the class names based on the color prop.
import colorClasses from './colorClasses';
const Badge = ({ color }) => {
const containerStyle = 'font-medium mr-2 px-2.5 py-0.5 rounded text-sm';
const colorClass = colorClasses[color];
return (
<div className={`${colorClass} ${containerStyle}`}>
{/* Your content here */}
</div>
);
};
The only option would be to reformat these ternary statements into a dictionary as per their documentation. Source
function Button({ color, children }) {
const colorVariants = {
blue: 'bg-blue-600 hover:bg-blue-500',
red: 'bg-red-600 hover:bg-red-500',
}
return (
<button className={`${colorVariants[color]} ...`}>
{children}
</button>
)
}

Why is NativeWind style not applied in custom components?

I have the following function to create the base for a custom button:
function CustomButton({ title, onPress }) {
return (
<TouchableOpacity
onPress={() => onPress()}
children={<Text>{title}</Text>}
/>
)
}
However, when I try to style it using the styled method, it won't work:
export const StyledButton = styled(
CustomButton,
'border bg-Primary rounded-full p-2'
)
However, it does work if I apply the style directly the style to the TouchableOpacity:
function CustomButton({ title, onPress }) {
const StyledTouchableOpacity = styled(
TouchableOpacity,
'border bg-Primary rounded-full p-2 text-OnPrimary'
)
return (
<StyledTouchableOpacity
onPress={() => onPress()}
children={<Text>{title}</Text>}
/>
)
}
Why is that? No errors are displayed to the consoleā€¦ I tried returning the StyledTouchableOpacity (without a style) in case returning a styled component works, but it didn't.

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

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

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

Add CSS for html selector based on React state?

I'd like to set overflow-y: hidden for the html selector (not an element) based on whether a React class component state variable is true. Is that possible?
If you mean you want to apply the overflow-y to the actual HTML tag then putting this code in the render worked for me
...
render() {
let html = document.querySelector('html');
this.state.test === "test" ? html.style.overflowY = "hidden" : html.style.overflowY = "visible";
return (
....
)
};
You can do
function MyComponent() {
// Set your state somehow
const [something, setSomething] = useState(initialState)
// Use it in your className`
return <div className={!!something && 'class-name'} />
}
If you have multiple class names to work with, a popular package is (aptly named) classnames. You might use it like so:
import cx from 'classnames'
function MyComponent() {
const [something, setSomething] = useState(initialState)
return <div className={cx({
'some-class' : something // if this is truthy, 'some-class' gets applie
})} />
}
Yes, It's possible. You can do this.
function App() {
const [visible, setVisible] = useState(false);
useEffect(() => {
const htmlSelector = document.querySelector("html");
htmlSelector.style.overflowY = visible ? "unset" : "hidden";
}, [visible]);
return (
<button onClick={() => setVisible(prevState => !prevState)}>
Toggle overflow
</button>
);
}
See the full example on CodeSandbox
You can use the style property to set inline CSS:
<div style={{ overflowY: hide ? 'hidden' : 'auto' }}>

Resources