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
Related
I'm facing an extrange behavior trying to implement dynamic class by props on child element when using Nuxt 3 SSR + Tailwind.
My parent component includes a child component
<section-latest-news :count="12" :columns="4" />
My child component tries to render columns based on columns property
<template>
<p class="text-xl text-center uppercase font-semibold border-b-2 mb-4 pb-1 tracking-widest">Ăšltimas noticias {{gridCols}}</p>
<div :class="`grid gap-5 md:grid-cols-${columns}`" >
<div v-for="post in posts" :key="post.id" class="md:mb-0">
<post-card-image :post="post" />
</div>
</div>
</template>
<script setup>
import camelcaseKeys from 'camelcase-keys'
const props = defineProps({
excludeSlug: {
type: String,
required: false
},
count: {
type: Number,
required: false,
default: 6
},
columns: {
type: Number,
required: false,
default: 3
}
})
const runtimeConfig = useRuntimeConfig()
const route = useRoute()
const { data: posts } = await useFetch(`/public/latest`, {
params: {
count: props.count,
exclude_slug: props.excludeSlug
},
key: route.fullPath,
baseURL: runtimeConfig.public.apiBase,
transform: (response) => {
return camelcaseKeys(response, {deep: true})
}
})
</script>
For some reason, despite I correctly see the class md:grid-cols-3 in dev tools elements inspector, the class is not applied.
Please note that if I manually set the class without using backticks, the class works as expected, so it's not about CSS layout.
I'm guessing that is something related to SSR and lifecycle, but not sure how to fix it.
Actually, you cannot use dynamic classes with Tailwind: https://tailwindcss.com/docs/content-configuration#dynamic-class-names
Nothing related to Nuxt, interpolating a utility class is just not feasible since all the classes need to be known ahead of time (during the build): getting written in their full name in the code.
Here is how you can still achieve a similar result, but with more work of course.
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`.
I have this VueJS component that displays a star-shaped button
<template >
<div id="tenseFav">
<button>
<img :src="imgForFavButton">
</button>
</div>
</template>
The computed property imgForFavButton returns a different image depending on if the item is in the list of favorites or not.
computed: {
imgForFavButton() {
const isFav = this.favs.has(this.id);
return isFav ? starOn : starOff;
},
},
I want to add animation on hover, so the image is the one with the yellow star and maybe some transition effects. I am not sure if I have to do this on CSS or VueJS.
On VueJS, I have no event to control the hover natively so I have to use something like:
<template>
<div id="tenseFav">
<button #click="emisor">
<img
#mouseover="hover = true"
#mouseleave="hover = false"
:src="imgForFavButton">
</button>
</div>
</template>
and then in the computed property use an if:
computed: {
imgForFavButton() {
if (this.hover === true) {
return starOn;
}
const isFav = this.favs.has(this.id);
return isFav ? starOn : starOff;
},
},
This works but it looks a bit hacky.
Is there a better way in 2022 for this kind of thing?
I feel that I will face a lot of problems trying to add a transition effect to this.
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
I'm new to Material UI and I'm struggling.
I have a button component
export default function TheButton(props: PropsWithChildren<Props>) {
const { className, hover, level,...rest } = props;
const classes = useStyles();
return (
<Button
{...rest}
className={clsx(classes.root, className, hover === 'contained' && classes.hoverContained)}
>
{props.children}
</Button>
);
}
From this component, I'd like to have two variants: contained and outlined. Here is my outlined button.
<TheButton
variant="outlined"
color="secondary"
>
secondary
</TheButton>
When the variant outlined is selected the button has the class Muibutton-outlined. I'd like to override this class to change the border (only in the outlined variant, so only on this class).
So far I've tried something like:
const useStyles = makeStyles((theme: Theme) =>
createStyles({
'.MuiButton-outlinedSecondary':{
border:"2px solid red" ,
},
}
)
It doesn't work.
I have a similar setting, and I tried:
adding a class submit to my button component
<Button
type="submit"
variant="outlined"
disabled={isSaving || invalid || unchanged}
color="secondary"
className={classes.submit}
>
SAVE CHANGES
</Button>
since I have the submit class I can be more precise in my styling like so:
const useStyles = makeStyles({
submit: {
marginTop: padding.medium,
marginLeft: padding.small,
'&.MuiButton-outlinedSecondary': {
border: '1px solid pink',
},
},
});
Here is the result:
Material-ui button with pink border image
As long as I have used Ant design ui kit and I have overrided styles on its default style like this:
<Button type="primary" className={styles.custom_css}>click</Button>
This has done in react and custom_css class overrides its styles on default
This might can help you, if not please let me know