DEV Community

Cover image for ๐ŸŽจ Smooth UI Transitions in HTML with Grains.js, TailwindCSS and g-class directive
Marko Jakic
Marko Jakic

Posted on

๐ŸŽจ Smooth UI Transitions in HTML with Grains.js, TailwindCSS and g-class directive

Spoiler alert: I'm author of Grains.js. If you missed my previous article on Grains.js, please check it out here.

Now I want to show how to use g-class directive for switching classes dynamically based on state. If you're building interactive UIs with plain HTML and want smooth transitions, animations, or dynamic styling โ€” without using a full JS framework โ€” then you'll love how g-class directive works in Grains.js.

Letโ€™s dive into a real-world example.

Note: It's best to utilize TailwindCSS to use ready-made styles via their classes. g-class directive has nothing to do with TailwindCSS, however. It only switches class names based on state. After that, you can use whatever you want.

โœจ Animated Notification Example

Weโ€™ll build a notification box with:

  • Visibility toggle
  • Error/success state styles
  • Shake animation for emphasis

You can try the live demo here.

You can see updated list of examples at https://github.com/mk0y/grains.js/tree/main/examples.

โœ… Setup

Add Grains.js and TailwindCSS to your page:

<script defer src="https://mk0y.github.io/grains.js/dist/grains.min.js"></script>
<script src="https://cdn.tailwindcss.com"></script>
Enter fullscreen mode Exit fullscreen mode

Remember, using cdn.tailwindcss.com is not for production environments. I'm using it only for presentational purposes - you should build your css file instead.

Next, add a simple @keyframes animation for shake:

<style>
  @keyframes shake {
    0%, 100% { transform: translateX(0); }
    25% { transform: translateX(-5px); }
    75% { transform: translateX(5px); }
  }
  .shake {
    animation: shake 0.5s ease-in-out;
  }
</style>
Enter fullscreen mode Exit fullscreen mode

๐Ÿง  The Markup

<div
  g-state="notifications"
  g-init='{"isVisible": false, "isError": false, "isShaking": false}'
>
  <div
    g-class="[
      'transform transition-all duration-300 ease-in-out',
      'p-4 rounded-lg shadow-lg mb-4',
      isVisible && 'translate-y-0 opacity-100',
      !isVisible && 'translate-y-4 opacity-0',
      isError && 'bg-red-100 text-red-700',
      !isError && 'bg-green-100 text-green-700',
      isShaking && 'shake'
    ]"
  >
    <p class="font-medium">Notification Message</p>
  </div>

  <div class="space-x-2">
    <button g-on:click="toggleVisibility">Toggle Visibility</button>
    <button g-on:click="toggleError">Toggle Error State</button>
    <button g-on:click="triggerShake">Shake Animation</button>
  </div>
</div>
Enter fullscreen mode Exit fullscreen mode

โš™๏ธ State Changers

Grains.js uses global pure functions to update state. Hereโ€™s how we control the behavior:

<script>
window.toggleVisibility = (ctx) => {
  ctx.set({ isVisible: !ctx.get("isVisible") });
};

window.toggleError = (ctx) => {
  ctx.set({ isError: !ctx.get("isError") });
};

window.triggerShake = (ctx) => {
  ctx.set({ isShaking: true });
  setTimeout(() => ctx.set({ isShaking: false }), 500);
};
</script>
Enter fullscreen mode Exit fullscreen mode

๐Ÿ’ก How g-class Works

  • You pass an array of strings or expressions.
  • Only truthy values are included in the final class list.
  • This makes toggling animations, colors, and layout transitions declarative and simple.

In the example above, when isVisible is true, classes like translate-y-0 and opacity-100 are added. When false, it falls back to a faded, translated state โ€” giving a nice slide/fade transition.

๐Ÿ Wrap-Up

The g-class directive in Grains.js makes conditional styling effortless, and when combined with Tailwind and a few keyframes, you can build delightful UI transitions in plain HTML โ€” no frameworks or build steps required.

๐Ÿ˜Š Please let me know what you'd like to change or add as a next feature in the comments.

Thank you!

Top comments (0)