Home > Software design >  Appending styling in reactjs whilst using tailwind
Appending styling in reactjs whilst using tailwind

Time:10-24

I want to animate an element when the user clicks on a button. The element displays a count of how many times the button has been clicked. This is managed through a state that is incremented on every button click.

Whenever the button is clicked, the displayed number should bounce/animate, to emphasize its change.

In essence I want to conditionally append a CSS class. There's many examples of how to do this, but almost all of them only have a single class applied to them, not lots of classes.

I use tailwindcss for styling. This means lots of CSS classes on lots of elements.

What I have considered

String concatenation

Having a classNames array, where the animation class can be added and removed easily.

const [count, setCount] = useState(0);
const [classNames, setClassNames] = useState([
    'text-2xl', 'mb-2', 'font-bold', 'text-black', 'dark:text-white'
]);

const countElement = <p className={classNames.join(' ')}>{count}</p>;

const handleClick = () => {
    
    setCount(count   1);
    
    // Append the animation class.
    setClassNames(classNames.concat('animate-bounce'));

    // Remove the animation class after the animation is done.
    setTimeout(() => {
        setClassNames(classNames.filter(className => 
            className !== 'animate-bounce'));
    }, 1000);
};

return <>
    {count > 0 ? countElement : null}
    <button className="px-4 py-3 mb-2 rounded-lg font-semibold transition-colors duration-300
                       bg-cardinal-default hover:bg-cardinal-dark active:bg-cardinal-darkest
                       text-black dark:text-white" 
                       onClick={handleClick}>Click me</button>
</>;

Though with tailwindcss this is kind of ugly as it separates all of the styling from its actual element. Not exactly ideal. It feels very hacky.

Animate an inner element

    const [isBouncing, setIsBouncing] = useState(false);

    const countElement = (
        <p className="text-2xl mb-2 font-bold text-black dark:text-white">
            <span className={isBouncing ? 'inline-block animate-spin' : ''}>{count}</span>
        </p>
    );

    ...
    
        setCount(count   1);
        setIsBouncing(true);

        setTimeout(() => setIsBouncing(false), 1000);
    
    ... 
    

This is better, yet it still requires me to set inline-block

Is there a better way of doing this? Or have I exhausted all worthwhile solutions?

CodePudding user response:

TailwindCSS is a utility first CSS Framework, so there's probably no other option other than appending classes to your elements.

If you were to use an animation library, like Framer Motion, it wouldn't be necessary to append Tailwind classes. On the other hand, you would need to create the animation yourself, which is quite easy.

Regarding your considerations, the first alternative seems indeed very hacky, whereas the second one doesn't and should work as well.

Nonetheless, it's up to you to decide which method suits you best.

CodePudding user response:

You could use a css-in-js solution like styled-components which will make it really easy to enable/disable styles.

For tailwind there is a library to help with css-in-js called twin

This allows you to conditionally enable styles:

const Button = styled.button`
  ${tw`your tailwind styles here`}
  ${props => props.shouldAnimate ? `some animation css here` : "" }
`
  • Related