Fixing AOS and Tailwind CSS Compatibility Issues in Nuxt 4: A Developer’s Journey


When working with modern web frameworks, module conflicts can be frustrating roadblocks that consume hours of development time. Recently, I encountered a particularly tricky issue while setting up a Nuxt 4 project with both the nuxt-aos module and @nuxtjs/tailwindcss module. What seemed like a straightforward setup quickly turned into a debugging nightmare when Tailwind CSS stopped working properly, color mode functionality broke, and AOS animations refused to animate.

If you’ve stumbled upon this post, chances are you’re facing similar issues. Let me walk you through the problem and the solution that finally got everything working seamlessly.



The Problem: When Modules Collide

I was building a Nuxt 4 application that required:

  • Tailwind CSS for styling and responsive design
  • Color mode functionality for dark/light theme switching
  • AOS (Animate On Scroll) for smooth scroll animations

Initially, I installed both the nuxt-aos module and @nuxtjs/tailwindcss module, expecting them to work together harmoniously. Instead, I encountered:

  1. Tailwind CSS classes not applying – Styles weren’t loading correctly
  2. Color mode switching broken – Dark/light theme toggle stopped functioning
  3. AOS animations not working – Scroll animations weren’t triggering

The root cause appeared to be module initialization conflicts and CSS loading order issues between the automated module configurations.



The Solution: Manual Plugin and CSS Configuration

After extensive debugging, I discovered that the issue stemmed from module conflicts and initialization timing. The solution involved:

  1. Removing the nuxt-aos module from the modules array
  2. Creating a custom AOS client plugin for proper initialization
  3. Manually configuring Tailwind CSS with explicit CSS file paths

Here’s the complete working configuration:



1. Updated Nuxt Configuration

// nuxt.config.ts
export default defineNuxtConfig({
  compatibilityDate: '2025-07-15',
  devtools: { enabled: true },
  modules: [
    '@nuxt/icon',
    '@nuxt/image',
    '@nuxtjs/tailwindcss',
    '@nuxtjs/color-mode',
    '@nuxtjs/supabase',
    'nuxt-swiper',
    '@vueuse/nuxt',
    ['@nuxtjs/google-fonts',{
      families: {
        Inter: '100..900'
      }
    }],
    // Note: nuxt-aos module removed from here
  ],
  supabase: {
    redirect: false,
    redirectOptions: {
      login: '/auth/login',
      callback: '/auth/confirm',
      exclude: ['/', '/auth/login', '/auth/confirm', '/auth/register', '/auth/forgot-password', '/auth/reset-password'],
      saveRedirectToCookie: true
    }
  },
  css: [
    'aos/dist/aos.css',
    '~/assets/css/tailwind.css',
  ],
  tailwindcss: {
    cssPath: [
      '~/assets/css/tailwind.css',
      {
        injectPosition: 'last'
      },
    ],
  },
  app: {
    pageTransition: { 
      name: 'page', 
      mode: 'out-in' 
    }
  }
})
Enter fullscreen mode

Exit fullscreen mode



2. Custom AOS Client Plugin

Instead of relying on the nuxt-aos module, I created a custom client-side plugin:

// plugins/aos.client.ts
import AOS from 'aos'
import 'aos/dist/aos.css'

export default defineNuxtPlugin(() => {
  AOS.init({
    // Global settings:
    disable: false,
    startEvent: 'DOMContentLoaded',
    initClassName: 'aos-init',
    animatedClassName: 'aos-animate',
    useClassNames: false,
    disableMutationObserver: false,
    debounceDelay: 50,
    throttleDelay: 99,

    // Per-element settings:
    offset: 120,
    delay: 0,
    duration: 400,
    easing: 'ease',
    once: false,
    mirror: false,
    anchorPlacement: 'top-bottom',
  })

  const refreshAos = () => AOS.refresh()
  const refreshHardAos = () => AOS.refreshHard()

  return {
    provide: {
      refreshAos,
      refreshHardAos,
    },
  }
})
Enter fullscreen mode

Exit fullscreen mode



3. Explicit Tailwind CSS File

I created a dedicated Tailwind CSS file with explicit directive imports:

/* ~/assets/css/tailwind.css */
@tailwind base;
@tailwind components;
@tailwind utilities;
Enter fullscreen mode

Exit fullscreen mode



Key Insights and Why This Works



1. Module Initialization Conflicts

The nuxt-aos module was interfering with Tailwind CSS initialization. By removing it and creating a custom plugin, I gained full control over when and how AOS initializes.



2. CSS Loading Order

Explicitly defining the CSS path in the Tailwind configuration with injectPosition: 'last' ensures that Tailwind styles load after other CSS, preventing style conflicts.



3. Client-Side Plugin Benefits

The .client.ts suffix ensures AOS only initializes on the client side, preventing SSR hydration mismatches that could break animations.



4. Provide/Inject Pattern

The plugin exposes refreshAos() and refreshHardAos() methods globally, allowing you to manually refresh AOS when needed (useful for dynamic content).



Usage in Components

With this setup, you can now use AOS animations in your components normally:

<template>
  

data-aos="fade-up" data-aos-duration="1000"> This will fade up on scroll

data-aos="slide-left" data-aos-delay="200"> This will slide from left with delay

template>
Enter fullscreen mode

Exit fullscreen mode

You can also programmatically refresh AOS when needed:

<script setup>
// Access the provided methods
const { $refreshAos, $refreshHardAos } = useNuxtApp()

// Refresh AOS after dynamic content changes
const loadMoreContent = async () => {
  await fetchMoreData()
  $refreshAos()
}
script>
Enter fullscreen mode

Exit fullscreen mode



Additional Dependencies

Don’t forget to install the AOS package directly:

npm install aos
# or
yarn add aos
# or
pnpm add aos
Enter fullscreen mode

Exit fullscreen mode



Conclusion

Module conflicts in Nuxt can be tricky to debug, but understanding the underlying initialization and CSS loading processes helps identify solutions. By taking manual control over AOS initialization and explicitly configuring Tailwind CSS paths, we achieved a stable setup where:

  • ✅ Tailwind CSS classes work perfectly
  • ✅ Color mode switching functions correctly
  • ✅ AOS animations trigger smoothly
  • ✅ All modules coexist without conflicts

This approach gives you more control over your dependencies while maintaining the benefits of Nuxt’s module ecosystem. Sometimes the best solution is to step back from automated configurations and implement things manually with a deeper understanding of what’s happening under the hood.

Have you encountered similar module conflicts in your Nuxt projects? Share your experiences and solutions in the comments below!


Happy coding! 🚀



Source link

Leave a Reply

Your email address will not be published. Required fields are marked *