Create an animated hamburger menu using Tailwind CSS and Angular framework

Within my Angular project, I have successfully integrated Tailwind UI to handle the opening and closing of the hamburger menu by utilizing the code below:

<header>
  <div class="-mr-2 -my-2 md:hidden">
    <button type="button" (click)="toggleMobileMenu()"
      class="bg-white rounded-md p-2 inline-flex items-center justify-center text-gray-400 hover:text-gray-500 hover:bg-gray-100 focus:outline-none focus:ring-2 focus:ring-inset focus:ring-indigo-500">
      <svg class="h-6 w-6" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor"
        aria-hidden="true">
        <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h16" />
      </svg>
    </button>
  </div>
  <div class="absolute z-30 top-0 inset-x-0 p-2 transition transform origin-top-right md:hidden" *ngIf="mobileMenuOpen">
    <div class="rounded-lg shadow-lg ring-1 ring-black ring-opacity-5 bg-white divide-y-2 divide-gray-50">

      <div class="flex items-center justify-between">
        <button (click)="toggleMobileMenu()" type="button"
          class="bg-white rounded-md p-2 inline-flex items-center justify-center text-gray-400 hover:text-gray-500 hover:bg-gray-100 focus:outline-none focus:ring-2 focus:ring-inset focus:ring-indigo-500">
          <svg class="h-6 w-6" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor"
            aria-hidden="true">
            <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />
          </svg>
        </button>
      </div>
      <div class="py-6 px-5">
        <div class="grid gap-4">
          <a routerLink="/" class="text-base font-medium text-gray-900 hover:text-gray-700">
            Home
          </a>
        </div>
      </div>
    </div>
  </div>
</header>


toggleMobileMenu() {
  this.mobileMenuOpen = !this.mobileMenuOpen;
}

To add animation effects to this functionality, I'm following these instructions:

    Mobile menu, show/hide based on mobile menu state.

    Entering: "duration-200 ease-out"
    From: "opacity-0 scale-95"
    To: "opacity-100 scale-100"
    Leaving: "duration-100 ease-in"
    From: "opacity-100 scale-100"
    To: "opacity-0 scale-95"

I am now looking for guidance on how to implement these animations in an Angular setting.

Answer №1

Here is my recommended approach:

  1. Make sure to include BrowserAnimationsModule
@NgModule({
  imports: [ BrowserModule, BrowserAnimationsModule, ... ],
  ...
})
  1. In the @Component decorator, create an array named animations as shown below
@Component({
  ,,,,
  animations: [
    trigger("openClose", [
      // ...
      state(
        "open",
        style({
          opacity: 1,
          transform: "scale(1, 1)"
        })
      ),
      state(
        "closed",
        style({
          opacity: 0,
          transform: "scale(0.95, 0.95)"
        })
      ),
      transition("open => closed", [animate("100ms ease-in")]),
      transition("closed => open", [animate("200ms ease-out")])
    ])
  ]
})

The animations array consists of the following:

  • trigger("openClose", [...])
    , which sets the trigger name

  • States defined using state('stateName'. In this case, states are open and closed

  • Style definitions under each state using style({ ... })

  • Transitions described by transition(...), specifying timing and animation style

For more information on animations, visit Angular animations

  1. Create a getter for the state
get openCloseTrigger() {
  return this.mobileMenuOpen ? "open" : "closed";
}
  1. Bind the state getter in the HTML
<div [@openClose]="openCloseTrigger" ...> 
  <!-- Other content here -->
</div>

Your menu should now animate as intended

View the demo on stackblitz

Closing the browser upon navigation To achieve this, we can use the NavigationEnd event. The menu will remain open when the user stays on a page, but it will close when they navigate to another page triggered by the NavigationEnd event.

This is how it's implemented:

constructor(private router: Router) {}
  navigationEnd$ = this.router.events.pipe(
    filter(event => event instanceof NavigationEnd),
    tap(() => (this.mobileMenuOpen = false))
  );

  ngOnInit() {
    this.navigationEnd$.subscribe();
  }

See this feature on stackblitz

Answer №2

Check out this innovative method that utilizes solely tailwind classes for animation. You can view a stackblitz demo here

Additionally, a concise explanation is provided below.

Initially, the hidden state of the mobile menu div is maintained by applying specific tailwind classes. The following classes are used:

scale-95, opacity-0, and pointer-events-none - ensuring no interactions occur in this hidden state.

When the menu becomes active, these classes are added:

scale-100, opacity-100, and pointer-events-auto.

To animate these properties, the transition class is applied to the div along with the duration-200 class for defining the transition duration.

<link href="https://unpkg.com/tailwindcss@^1.0/dist/tailwind.min.css" rel="stylesheet">
<header>
    <div class="-mr-2 -my-2 md:hidden">
        <button type="button" (click)="toggleMobileMenu()"
          class="bg-white rounded-md p-2 inline-flex items-center justify-center text-gray-400 hover:text-gray-500 hover:bg-gray-100 focus:outline-none focus:ring-2 focus:ring-inset focus:ring-indigo-500">
          <svg class="h-6 w-6" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor"
            aria-hidden="true">
            <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h16" />
          </svg>
        </button>
        </div>
<!-- Specific classes added: transition transform scale-95 opacity-0 duration-200 pointer-events-none -->
        <div class="absolute z-30 top-0 inset-x-0 p-2 transition transform scale-95 opacity-0 duration-200 pointer-events-none md:hidden"
            [ngClass]="{'scale-100 pointer-events-auto opacity-100': mobileMenuOpen}">
<!-- Toggle classes: scale-100 opacity-100 and pointer-events-auto -->
            <div class="rounded-lg shadow-lg ring-1 ring-black ring-opacity-5 bg-white divide-y-2 divide-gray-50">
                <div class="flex items-center justify-between">
                    <button (click)="toggleMobileMenu()" type="button"
              class="bg-white rounded-md p-2 inline-flex items-center justify-center text-gray-400 hover:text-gray-500 hover:bg-gray-100 focus:outline-none focus:ring-2 focus:ring-inset focus:ring-indigo-500">
              <svg class="h-6 w-6" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor"
                aria-hidden="true">
                <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />
              </svg>
            </button>
                </div>
                <div class="py-6 px-5">
                    <div class="grid gap-4">
                        <a routerLink="/" (click)="toggleMobileMenu()" class="text-base font-medium text-gray-900 hover:text-gray-700">
                            Home
                        </a>
                    </div>
                </div>
            </div>
        </div>
    </header> 

By utilizing the appropriate tailwind classes, you can effortlessly achieve the desired animation effect.

Answer №3

Thanks to this thread, I finally found the solution I needed with Angular 14 & TailwindUI. Here's what worked for me:

I had to implement a notification to show the response from an http request. I created a NotificationService that handles the display using subscriptions. I discovered that this SO thread(s) was extremely helpful in achieving this.

// app.component.ts

@Component({
    selector: 'app-root',
    templateUrl: './app.component.html',
    styleUrls: ['./app.component.css'],
    providers: [],
    animations: [
        trigger('renderNotification', [
            transition(':enter', [
                style({
                    transform: 'translateY(0.5rem)',
                    opacity: 0,
                }),
                animate(
                    '150ms 0ms ease-out',
                    style({
                        transform: 'translateY(0)',
                        opacity: 1,
                    })
                ),
            ]),
            transition(':leave', [
                style({
                    transform: 'translateY(0)',
                    opacity: 1,
                }),
                animate(
                    '100ms 0ms ease-in',
                    style({
                        transform: 'translateX(0.5rem)',
                        opacity: 0,
                    })
                ),
            ]),
        ]),
    ],
})
export class AppComponent implements OnInit {
    showNotification: boolean = false;
    ...
}
<!-- app.component.html -->

<div class="flex h-full">
    <div
        aria-live="assertive"
        class="pointer-events-none fixed inset-0 z-50 flex items-end px-4 py-6 sm:items-start sm:p-6"
    >
        <div class="flex w-full flex-col items-center space-y-4 sm:items-end">
            <app-notifications
                class=""
                [@renderNotification]="notification.show"
                *ngIf="showNotification"
                [type]="notification.type"
                [title]="notification.title"
                [body]="notification.body"
                (closeNotificationEvent)="closeNotification()"
            ></app-notifications>
        </div>
    </div>

    ...

    <div class="flex min-w-0 flex-1 flex-col overflow-hidden">
        <router-outlet></router-outlet>
    </div>
</div>

Hopefully, this can be of help to someone else out there!

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Tips for configuring Angular 2 to send all requests in the form of application/x-www-form-urlencoded

My experience with Angular 1 has helped me in understanding how to implement a similar solution, but I'm stuck on the final step. Just like before, the backend developer for our application is set up to accept requests with type application/x-www-for ...

Is there a way to utilize an Event Emitter to invoke a function that produces a result, and pause until the answer is provided before continuing?

Looking for a way to emit an event from a child component that triggers a function in the parent component, but with a need to wait for a response before continuing. Child @Output() callParentFunction = new EventEmitter<any>(); ... this.callParen ...

Tips for displaying backend error messages on the frontend

I am facing an issue with returning error messages from the backend to the frontend in my Angular project. The specific requirement is to display an error message when the value of msisdn is not eligible for renewal. Currently, the hardcoded error message ...

Efficiently adjust the width of a child div to automatically fit within

I stumbled upon this fascinating jsfiddle on this website that almost completely addresses my concern, solving up to 90% of it. Check out the JSFiddle Nevertheless, I am keen to incorporate margins within the inner divs. Despite attempting to modify the ...

Exploring the power of directives in AngularJS for dynamically manipulating the

Using angular directives, I am attempting to dynamically replace a portion of HTML within a portlet on a webpage. The portlet contains two embedded sections. The top section includes a heading obtained from a separate backend service. <div class="head ...

Having trouble getting the hover effect to work when selecting a different section of the SVG

In my SVG checkbox design, I have a circle element surrounding a polyline element (which acts as the checkmark). The boundaries of the icon extend beyond the circle, causing hover styles to trigger even when hovering outside the circle. I want to change st ...

When aligning to the right, input box does not wrap

Is there a way to prevent an input box from displaying on a lower line than the menu when using CSS to create a one line menu floated to the right? This is the CSS code being used: <style type="text/css"> #nav { margin:0; padding:0; lis ...

Conceal and reposition divs based on device with Bootstrap's responsive utilities

Utilizing Bootstrap to design a layout that adapts to desktop, tablet, and mobile screens. The desired output is depicted below: In order to achieve this, three divs were created: <div class="row"> <div class="col-md-3">Text</div> & ...

Is it feasible to automate the cropping process with selenium?

Exploring a new challenge: simulating a cropping feature. I understand that replicating human cropping is impossible, but the image I intend to crop remains a fixed size each time I run the test. My goal is to establish the cropping WebElement at a constan ...

What is the process for implementing a third-party component in my web application?

After some experimentation, I've discovered that it's necessary to include the link to the css files in the header and then mention the link to the js files before the closing tag. However, I encountered difficulties when trying to use a compone ...

Unable to select the initial element

Having trouble targeting the first child in this code snippet. I've tried various methods but nothing seems to be working. Any suggestions on how to resolve this? <div id="main"> <div class="page"> <p>random 1</p> </div ...

How to hide the border on the left and right sides of a phone number input in Tail

I'm currently working on a React component that allows users to select a country code and enter a phone number. However, I'm facing a challenge in trying to hide the left border of the country code select input and the right border of the phone n ...

Tips on increasing the button size automatically as the elements inside the button grow in size

I have a button with an image and text inside, styled using CSS properties to achieve a specific look. However, I encountered an issue where the text overflows outside of the button when it's too long. I want to find a way to wrap the text inside the ...

CSS layout: The full-width header should align its left margin with the centered content-div

Can someone help me out with a solution to my problem? My page has a 1040px centered div for content, menu, and footer. I want the header image to align with the left margin of the content div and expand towards the right side for higher screen resolutio ...

Add the component view to the webpage's body section

Using Angular 7 and ngx-bootstrap 4.0.1 Dependencies: "bootstrap": "3.3.7", "bootstrap-colorpicker": "2.5.1", "bootstrap-duallistbox": "3.0.6", "bootstrap-markdown": "2.10.0", "bootstrap-progressbar": "0.9.0", "bootstrap-slider": "9.8.0", "bootstrap-tags ...

What steps can I take to adapt my component in order to incorporate RxJs?

I am trying to gain proficiency in RxJs and, for educational purposes, I am interested in understanding how to modify the following code or if it is even feasible. Within my project, there is a component called MeetingObjectComponent which contains a chil ...

Troubleshooting a problematic dependency in Angular with the ngx-favicon package

Could someone please clarify why I am encountering issues when trying to run npm install ngx-favicon? npm install ngx-favicon npm ERR! code ERESOLVE npm ERR! ERESOLVE unable to resolve dependency tree npm ERR! npm ERR! While resolving: <a href="/cdn-cgi ...

Including the --aot flag in the Angular CLI can cause issues with the app

Recently, I encountered an issue with my Angular app while using dynamic forms. Everything was working fine until I added the --aot flag to my CLI command. Suddenly, I started receiving the error message "Property 'controls' does not exist on typ ...

Adding a blank line in an Angular table using primeNG to display database information requires a specific method

In my Angular application, I am utilizing primeNG table to display data fetched from a database. The table comes with 'add' and 'delete' buttons for user interaction. To view the interface, click on this link: https://i.stack.imgur.com/ ...

What causes Font Awesome 5 icons to vanish when changing the font-family?

I am facing an issue with the code I have below. It changes the font style successfully, but it also causes the icon to disappear. Can anyone provide tips on how to ensure that the icon remains visible while changing the font style? <link rel="styles ...