Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Dynamically assigning classes in Qwik

I am new to Qwik and trying to conditionally add/remove classes from items in my navbar component. The complication is that I'm using tailwind and I want to conditionally add/remove the tailwind class (using custom animations), but it seems to have no effect. I have read about doing something like this in React using clsx, but this doesn't appear to be available in Qwik. Is there any way to dynamically add the tailwind class or how else can I achieve the same result?

Component

import { $, component$, useStore } from '@builder.io/qwik';
import { Link } from '@builder.io/qwik-city';

export const Navigation = component$(() => {

  const baseClass = "py-2 bg-white text-black w-[100px]";

  const menuItems = useStore([
    { label: "Home", value: "/", class: baseClass },
    { label: "About", value: "/about", class: baseClass },
    { label: "Contact Us", value: "/contact", class: baseClass },
  ]);

  const fadeout = $((num: number) => {
    menuItems.forEach((ele, i) => {
      if(i < num && !ele.class.includes("animate-fadeout")) {
      ele.class = baseClass + " animate-fadeout";
      }
    })
  });

  const fadein = $((num: number) => {
    menuItems.forEach((ele, i) => {
      if(i < num && ele.class.includes("animate-fadeout")) {
      ele.class = baseClass + " animate-fadein";
      }
    })
  });

  return (
    <div class="flex flex-col space-y-2 py-2 bg-slate-500">
      {menuItems.map((item, index) => (
        <Link className={item.class} key={index} onMouseOver$={() => fadeout(index)} onMouseOut$={() => fadein(index)} href={item.value}>{item.label}</Link>
      ))}
    </div>
  )
});

tailwind.config.js

/** @type {import('tailwindcss').Config} */
module.exports = {
  content: ['./src/**/*.{js,ts,jsx,tsx,mdx}'],
  theme: {
    extend: {
      keyframes: {
        fadein: {
          '0%': { transform: 'translateY(-175%)', opacity: '0'},
          '10%': { transform: 'translateY(-150%)', opacity: '1'},
          '50%': { transform: 'translateY(0)'}
        },
        fadeout: {
          '50%': { transform: 'translateY(-175%)', opacity: '0'},
          '40%': { transform: 'translateY(-150%)', opacity: '1'},
          '0%': { transform: 'translateY(0)'}
        }
      },
      animation: {
        fadeout: 'fadeout 1s ease-in-out 1',
        fadein: 'fadein 1s ease-in-out 1',
      }
    },
  },
  plugins: [],
};
like image 711
Calvin P. Avatar asked Jul 02 '26 02:07

Calvin P.


2 Answers

You're missing {deep: true} as the options on your call to useStore. This will make the signaling work.

Also take a look at qwik-transition if you want to do transitions like this.

Furthermore, can't you use :hover to achieve what you want?

Finally, I would make a MenuItem component$ instead of composing everything in Navigation.

like image 150
w00t Avatar answered Jul 04 '26 16:07

w00t


I would recommend using classlist:

import { component$, useStore } from '@builder.io/qwik';
import { Link } from '@builder.io/qwik-city';

type ItemMode = 'fadein' | 'fadeout';

export const Navigation = component$(() => {
  const menuItems = useStore<
    { label: string; value: string; mode: ItemMode }[]
  >(
    [
      { label: 'Home', value: '/', mode: 'fadein' },
      { label: 'About', value: '/about', mode: 'fadein' },
      { label: 'Contact Us', value: '/contact', mode: 'fadein' }
    ],
    { deep: true }
  );

  return (
    <div class="flex flex-col space-y-2 py-2 bg-slate-500">
      {menuItems.map((item, index) => (
        <Link
          class={[
            'py-2 bg-white text-black w-[100px]',
            {
              'animate-fadeout': item.mode === 'fadeout',
              'animate-fadein': item.mode === 'fadein'
            }
          ]}
          key={index}
          onMouseOver$={() => {
            menuItems[index].mode = 'fadeout';
          }}
          onMouseOut$={() => {
            menuItems[index].mode = 'fadein';
          }}
          href={item.value}
        >
          {item.label}
        </Link>
      ))}
    </div>
  );
});

like image 45
Tob Avatar answered Jul 04 '26 16:07

Tob