How to Make your Angular Responsive Web App Design?

Angular Responsive Design

Quick Summary:

How to make Angular responsive? There are a couple of ways, like using CSS media queries, Angular Flex Layout, responsive CSS libraries like Angular Material Design, and more, to make your Angular project mobile-friendly. In this ultimate Angular responsive guide, we will dive deeper into the various methods of improving Angular responsiveness and discuss some Angular best practices you should follow. 

As mobile devices, including foldable screens, become more prevalent, upgrading your legacy Angular app to be fully responsive is no longer optional—it’s essential. Your app must adapt to various screen sizes and orientations while maintaining fast load times and flawless functionality. This ensures that users have a seamless experience, whether they access your app on a smartphone, tablet, or desktop, without any compromise in performance or usability.

Benefits of Responsive Angular – Why Focus on Angular Responsive Design?

Before diving into how to make Angular application responsive, we should understand why it is important. When we say responsive design in Angular, you want your website or web app to work and look well on various devices, such as desktops, laptops, mobile phones, tablets, and more. This way, your target audience can view and interact with your project on any device without interruptions or limitations. Responsive design is a crucial aspect of modern website development and comes with many benefits, such as:

Cross-Platform Compatibility

Responsive Angular applications can be seamlessly adapted to various screen sizes, devices, and orientations, ensuring a consistent user experience across desktops, tablets, and smartphones.

Enhanced User Experience

Making Angular responsive enables users to browse and interact with your web app easily, avoiding overlapping functions, uncomfortable click areas, and other issues. This helps improve the overall user experience of your Angular app.

Improved SERP Rankings

SERP – Search Engine Results Page refers to the rankings of websites/webpages on popular search engines such as Google. Google is said to prioritize web pages or websites that follow a mobile-first design approach, and hence Responsive Angular could help you rank higher on SERP rankings. However, you might also need to focus on other aspects of Angular SEO if you’re aiming to rank first on SERP rankings.

Reduced Angular Development and Maintenance Costs

Following Angular responsive design allows developers to create a single Angular project that works well on most devices, thus reducing the overall maintenance and development costs.

Better Scope for Accessibility

Making Angular applications responsive can help people with disabilities better access your website or web apps using their assistive tools or smaller-screen devices. You can follow the W3C accessibility standards to make your Angular app more accessible.

Angular Responsive Design Principles

Angular as a platform realizes the importance of responsive web design and provides developers with some top Angular developer tools and frameworks to make Angular responsive. Having a basic understanding of these Angular responsive principles can help you make Angular projects responsive:

1. Using Angular Material for Responsive Grid System

Grid systems are used to create Angular responsive layouts by using a series of columns and rows to create a structured layout. Angular Material is the go-to framework that provides developers with a highly responsive grid system that can be used for organizing the layout of your Angular app. It uses a 12-column layout helping developers place their content in precise locations. The grids are highly responsive, so they automatically adjust the layout as per the screen or device it is being viewed upon.

2. Utilizing a Mobile-First Approach Instead of Media Queries

One of the most commonly known and previously used methods to make Angular projects responsive was to use media queries. Media queries work on CSS to apply different styles to the project’s content based on what screen or device it is being used on. It allows developers to adjust the font size, layouts, and other essential Angular responsive design elements.

/* Define styles for screens with a maximum width of 600 pixels */ @media screen and (max-width: 600px) {
 .my-element {
 font-size: 16px;
 padding: 10px;
 
/* Define styles for screens with a minimum width of 601 pixels */@media screen and (min-width: 601px) {
.my-element {
font-size: 24px;
padding: 20px;
  }
 }

Though this approach seems practical for small-scale Angular projects, it can be problematic for larger Angular projects or even small-scale projects looking to scale. This is because you’d constantly have to keep adding or defining more and more styles for any new screen sizes that show up, making the codebase complex and heavy.

So, what can be a better alternative?

Mobile-First Approach to Responsive Angular

The mobile-first Angular responsive principle first focuses on designing the Angular project for the smallest screens. Then for larger screens, additional features, and styles are added. This works way better than Angular media queries because the mobile-first approach makes it easier to manage and handle the application’s layout and styles. It also ensures your project is highly optimized for mobile devices or smaller screens. This is how to implement a Mobile-First approach to responsive Angular:

  1. Start by designing Angular project for the smallest screen you aim to target—ideally, smartphone – portrait orientation or any low-res screen.
  2. Define the base style for your targeted mobile devices in the global style sheet.
/* Base styles for mobile devices */.container {
 max-width: 100%;
 padding: 1rem;
}

@media (min-width: 768px) {
 /* Styles for larger screens */ .container {
   margin: 0 auto;
   max-width: 768px;
  }
} 
  • Here we first defined the base style for how we want our Angular app to look on mobile devices. Later we used the media query method for applying different styles to our ‘.container’ element for situations where screen size exceeds 768 pixels.
/* Styles for my-component */.my-component {
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  font-size: 1rem;
}
@media (min-width: 768px) {
 /* Styles for larger screens */ .my-component {
 font-size: 1.2rem;
 }
} 
  • Next, define component-specific styles in their respective style sheets
  • Use the ‘class’ attribute to the specific HTML elements you want to style. This would apply your pre-defined styles in the component-specific styles to the <div> element.
<div class="my-component">
     <p>Hello, world!</p>
</div>

Performance First Approach in Responsive Angular

With all the modern abilities and features we can add to our websites, additional costs also start creeping up. The price of keeping elements in the DOM that are visible on some resolutions/screens only can be too expensive. Generally, images and media on desktop require full-quality high-res images but smartphone images can be resized to smaller pixels as they require lesser pixels. Similarly, videos on a desktop website is a great idea as it creates engaging visitor experience but the same won’t perform well on a mobile screen as:

  1. There are good chances your video will not be visible aesthetically
  2. It would impact the bandwidth and battery considerably
  3. Page scrolling on mobile phones can lead to accidental or unwanted touches on the video

which can be disruptive for the user experience. The idea is not just to change the aesthetics and UI only but also make the UX more accessible on smaller devices. Other than this you should also focus on removing some heavy elements that aren’t needed or would affect the mobile website’s performance negatively.

Desktop websites are opened on fairly stable networks whereas mobile websites are accessed using net services which can fluctuate or be weak at times. Hence you want to focus on delivering performance first and give your users fast experience on mobile to match the speed and responsiveness they are used to with your desktop site. For achieving this it is important to be able to add/remove DOM elements depending on the device.

Also Check our Article : React vs Angular 2024- The Ultimate Comparison

3. Utilizing Flexbox and Grid for Flexible Layouts

Flexbox and Grid are the most commonly used techniques for making Angular responsive layouts. Flexbox is generally used for simple layouts, whereas grids can be used for more complex layouts. Moreover, they can be used together to achieve Responsiveness in Angular for highly complex layouts or structures. Let’s understand both Flexbox and Grid individually:

Using Flexbox for Angular Responsiveness

Flexbox is better suited for simple layout purposes. If you need to create basic responsive Angular layouts with horizontal or vertical alignment options, flexbox is ideal. Using the CSS ‘display: flex’ on a container element converts it into a flex container where you can add ‘justify-content’ and ‘align-items’ for controlling the layout of the child elements.

.container {
  display: flex;
  justify-content: center;
  align-items: center;
}

Grids are more robust than Flexbox since they can handle two-dimension layouts. Here we will use the ‘display: grid’ property on the container and then add ‘grid-template-columns’ and ‘grid-template-rows’ to define the position and size of the grid cells.

.container {
  display: grid;
  grid-template-columns: 1fr 2fr 1fr;
  grid-template-rows: 100px 200px;
}

4. Give Priority to Content

Content needs to be given the first and foremost priority in Angular responsive design. The purpose of displaying your site or web app on any platform is for the users to understand your offerings to improve engagement and conversion rates. You should prioritize the most important content first, and content with lesser priority should be collapsed or hidden on smaller screens if unnecessary.

5. Properly Optimize Images

Images are crucial for any website or web app from an engagement point of view. However, they are also heavy and can cause your Angular project to slow down significantly, impacting the site’s Responsiveness, especially for smaller screen sizes. You should optimize images by reducing their sizes and resolution and ensure they are responsive enough to fit and adapt to different screen sizes.

6. Leverage Angular CDK for Advanced Responsiveness

To enhance your Angular application’s responsiveness, you can use the Angular CDK’s BreakpointObserver to listen for screen size changes and adjust your layout dynamically. Here’s an example:

import { BreakpointObserver, Breakpoints } from '@angular/cdk/layout';
import { Component } from '@angular/core';

@Component({
  selector: 'app-responsive-layout',
  template: `
    <div [ngClass]="layoutClass">
      <!-- Your content goes here -->
    </div>
  `
})
export class ResponsiveLayoutComponent {
  layoutClass: string = '';

  constructor(private breakpointObserver: BreakpointObserver) {
    this.breakpointObserver.observe([
      Breakpoints.Handset,
      Breakpoints.Tablet,
      Breakpoints.Web
    ]).subscribe(result => {
      if (result.matches) {
        if (result.breakpoints[Breakpoints.Handset]) {
          this.layoutClass = 'handset-layout';
        } else if (result.breakpoints[Breakpoints.Tablet]) {
          this.layoutClass = 'tablet-layout';
        } else if (result.breakpoints[Breakpoints.Web]) {
          this.layoutClass = 'web-layout';
        }
      }
    });
  }
}

In this example, BreakpointObserver detects whether the user is on a handset, tablet, or web device and applies a corresponding CSS class (handset-layout, tablet-layout, web-layout). This allows your application to adjust its layout dynamically based on the screen size.

7. Implement Lazy Loading for Performance Optimization

Angular’s lazy loading can be implemented by configuring your AppRoutingModule to load modules only when they are needed. Here’s an example:

import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';

const routes: Routes = [
  {
    path: 'feature-module',
    loadChildren: () => import('./feature/feature.module').then(m => m.FeatureModule)
  },
  {
    path: '',
    redirectTo: '/home',
    pathMatch: 'full'
  }
];

@NgModule({
  imports: [RouterModule.forRoot(routes)],
  exports: [RouterModule]
})
export class AppRoutingModule { }

In this example, the FeatureModule is only loaded when the user navigates to the /feature-module path. This approach reduces the initial load time of your application, particularly in responsive designs where not all features are needed on every device. By implementing lazy loading, you ensure that the application remains lightweight and performs optimally across various devices.

By combining Angular CDK’s responsive capabilities with lazy loading, you can create a highly responsive and performant application that adapts seamlessly to different screen sizes and device capabilities.

Looking to hire AngularJs Developers?

Aglowid Helps You Build Dynamic, High-Performance User Interfaces with AngularJS for Modern Web Applications.

Connect Now

How to make an Angular App Responsive? Angular App Responsive Techniques

Now that we have understood the various principles to make Angular responsive and the benefits it provides, we should address how to create an Angular application responsive. Here are some of the industry-grade Responsive Angular techniques that can help you:

Get familiar with MediaQuery and matchMedia

MediaQuery and matchmedia are two of the most commonly used techniques for making Angular responsive. Media queries are not only limited to CSS, they are supported in all popular Javascript frameworks. The Window object establishes a function matchMedia that returns a response of type MediaQueryList. MediaQueryList extends EventTarget which means it can have listeners set up and receive events as well.

interface MediaQueryList extends EventTarget {
  matches: boolean; // => true if document matches the passed media query, false if not
  media: string; // => the media query used for the matching
}

A basic example of this can look like

const query = '(orientation: portrait)';
const mediaQueryList = window.matchMedia(query);

// check the match
if (mediaQueryList.matches) {
  /* we are in the portrait mode */} else {
  /* viewport is in the landscape mode */}

Enhance this code with listeners

const query = '(orientation: portrait)';
const mediaQueryList = window.matchMedia(query);

// define the callback function for our event listener
function listener(mql: MediaQueryList) {
  if (mql.matches) {
    /* we are in the portrait mode */  } else {
    /* viewport is in the landscape mode */  }
}

// run check once
listener(mediaQueryList);

// run check on every subsequent change
mediaQueryList.addEventListener('change', listener);

Also Check our Article : How to make Angular SEO Friendly?

The Media Service Solution

All even listeners create a stream of events. This allows skilled Angular developers to wrap up the information as an Observable using this service. The consumer who wants these services can subscribe to the stream of media changes and react to them. At the core of such services is ReplaySubject. We pass the values to this core service from MatchMediafunction.

class MediaService {
  private matches = new ReplaySubject<boolean>(1);
  public match$ = this.matches.asObservable();

  constructor(public readonly query: string) {
    // we need to make sure we are in browser
    if (window) {
      const mediaQueryList = window.matchMedia(this.query);
      // here we pass value to our ReplaySubject
      const listener = event => this.matches.next(event.matches);
      // run once and then add listener
      listener(mediaQueryList);
      mediaQueryList.addEventListener('change', listener);
    }
  }
}

This service is now usable for our components. It would help us control the visibility of parts of templates. Whenever media query match changes, the property is Desktop will be changed and hence influence the rendering of the template.

@Component({
  selector: 'foo-bar',
  template: `
    <div *ngIf='isDesktop; else isMobile'>I am visible only on desktop</div>
    <ng-template #isMobile>
      <div>I am visible only on mobile</div>
    </ng-template>
  `
})
class FooBarComponent implements OnInit {
  isDesktop: boolean;
  private mediaService = new MediaService('(min-width: 768px)');

  ngOnInit() {
    this.mediaService.match$.subscribe(value => this.isDesktop = value);
  }
}

In Angular Responsive Design, you can find many uses of MatchService such as

  • Calculations based on media or complex business logic
  • Fetching various resources from the backend

However, if your requirement is just to manipulate the template, the best thing to do is use a dedicated component or directive implementation.

The Media Component Solution

Another way if you don’t want to subscribe to media changes is to list services directly in the media component.

@Component({
  selector: 'use-media',
  template: '<ng-content *ngIf="isMatch"></ng-content>'
})
class MediaComponent {
  @Input() set query(value: string) {
    // cleanup old listener
    if (this.removeListener) {
      this.removeListener();
    }
    this.setListener(value);
  }
  isMatch = false;
  private removeListener: () => void;

  private setListener(query: string) {
    const mediaQueryList = window.matchMedia(query);
    const listener = event => this.isMatch = event.matches;
    // run once and then add listener
    listener(mediaQueryList);
    mediaQueryList.addEventListener('change', listener);
    // add cleanup listener
    this.removeListener = () => this.removeEventListener('change', listener);
  }
}

If you notice, the first noticeable difference between component and service is removeListener. When the service has the query set as ‘read-only’ the component is able to change the value of the query in runtime which creates a new match media listener. It is best to avoid having more than 2 listeners in a race condition. For this, we ensure that all the previous listeners have been cleared up.
The component would be used to control the template the same way a service does. Now you witness the magic in the template:

@Component({
  selector: 'foo-bar',
  template: `
    <use-media query="(min-width: 768px)">
      I am visible only on desktop
    </use-media>
    <use-media query="(max-width: 767px)">
      I am visible only on mobile
    </use-media>
  `
})
class FooBarComponent { }

Now for better readability and reusability we can extract two media queries – min width:768 px and max-width: 767 px to constants and apply them across our application. Using this method exposes clear intent, however, we still end up with two extra use-media DOM elements dedicated to controlling visibility. Furthermore, the inner content gets processed first before ngIF, since we use media projection.

@Component({ selector: 'child-component' })
class ChildComponent implements OnInit {
  @Input() value: string;

  ngOnInit() {
    console.log(`From child: ${value}`);
  }
}

@Component({
  selector: 'foo-bar',
  template: `
    <use-media query="(min-width: 768px)">
      <child-component value="Desktop"></child-component>
    </use-media>
    <use-media query="(max-width: 767px)">
      <child-component value="Mobile"></child-component>
    </use-media>
  `
})
class FooBarComponent implements OnInit {
  ngOnInit() {
    console.log(`From FooBar`);
  }
}

The Media Directive Solution

We saw the limitations of both Media Services and Media Component. A way to solve these problems is to build a structural directive on top of the same logic. With Media Directives you solve the issues of:

  • The requirement of an extra DOM element
  • Content rendering with all values, and not only on receiving positive values.
@Directive({ selector: '[media]' })
class MediaDirective {
  @Input() set media(query: string) {
    // cleanup old listener
    if (this.removeListener) {
      this.removeListener();
    }
    this.setListener(value);
  }
  private hasView = false;
  private removeListener: () => void;

  constructor(
    private readonly viewContainer: ViewContainerRef,
    private readonly template: TemplateRef<any>
  ) { }

  private setListener(query: string) {
    const mediaQueryList = window.matchMedia(query);
    const listener = event => {
      // create view if true and not created already
      if (event.matches && !this.hasView) {
        this.hasView = true;
        this.viewContainer.createEmbeddedView(this.template);
      }
      // destroy view if false and created
      if (!event.matches && this.hasView) {
        this.hasView = false;
        this.viewContainer.clear();
      }
    };
    // run once and then add listener
    listener(mediaQueryList);
    mediaQueryList.addEventListener('change', listener);
    // add cleanup listener
    this.removeListener = () => this.removeEventListener('change', listener);
  }
}

If we actually see, the only difference between media directive and component is in how the listener callback functions. In the component, we were setting public property is Match, the directive, it is creating or clearing the view as per the value.

@Component({
  selector: 'foo-bar',
  template: `
    <div *media="'(min-width: 768px)'">I am visible only on desktop</div>
    <div *media="'(max-width: 767px)'">I am visible only on mobile</div>
  `
})
class FooBarComponent { }

Use Angular Flex Layout for Responsive Design

Angular Flex Layout provides a set of directives that make it easier to build responsive layouts using Flexbox. To get started, you’ll need to install the @angular/flex-layout package:

npm install @angular/flex-layout

Once installed, you can use it in your components. Here’s an example of a responsive layout using Angular Flex Layout:

<div fxLayout="row" fxLayout.xs="column" fxLayoutGap="10px">
  <div fxFlex="50%" fxFlex.xs="100%">Item 1</div>
  <div fxFlex="50%" fxFlex.xs="100%">Item 2</div>
  <div fxFlex="50%" fxFlex.xs="100%">Item 3</div>
  <div fxFlex="50%" fxFlex.xs="100%">Item 4</div>
</div>

In this example, the fxLayout directive arranges items in a row by default. When the screen size is extra small (xs), the layout switches to a column. The fxFlex directive is used to define how much space each item should take up, and fxLayoutGap adds space between the items.

Incorporate Responsive Images with srcset and Angular

To optimize images for different screen sizes, you can use the srcset attribute along with Angular’s directives. Here’s an example:

<img 
  [src]="smallImage"
  srcset="{{mediumImage}} 768w, {{largeImage}} 1200w"
  sizes="(max-width: 768px) 100vw, (min-width: 769px) and (max-width: 1200px) 50vw, 33vw"
  alt="Responsive Image Example">

In this example:

  • src is the default image source.
  • srcset defines different images for different screen widths (768w and 1200w).
  • sizes attribute instructs the browser on how much space the image will occupy, helping it select the best image from the srcset.

You can also combine srcset with Angular’s ngIf directive to conditionally load images based on device context:

<img 
  *ngIf="isMobile; else desktopImage"
  [src]="mobileImage"
  alt="Mobile Image">

<ng-template #desktopImage>
  <img [src]="desktopImage" alt="Desktop Image">
</ng-template>

In this case, isMobile is a boolean variable determined by Angular’s logic to check if the user is on a mobile device. Depending on the value, it conditionally displays the appropriate image for mobile or desktop.

How to Test Responsiveness in Angular?

Once you’re done making your Angular project responsive, you must conduct a thorough testing process to ensure it’s truly responsive. There are many different ways to test Angular Responsiveness that you can leverage:

1. Testing on Real Devices

If you have a considerably limited fragmentation of devices where you want your website to work smoothly, test your Angular project on the actual devices to see how the responsive Angular layout works on all those devices. You can use various real devices such as smartphones, desktops, or tablets.

2. Make use of Chrome DevTools Emulator

One of the most convenient ways to test your Angular web app for Responsiveness is to use Chrome DevTools to test your app on various devices and screen sizes. You can pick from their list of devices or manually enter dimensions to test your project on unique screen sizes/devices.

3. Using Angular automation testing tools

Angular is a popular framework with an active and growing community. Hence, many Angular automation tools are available, such as Cypress, built specifically to help Angular developers test their apps for Responsiveness. Here are two of the top Angular responsive testing tools you can use:

1. Cypress – Test, Automate, Accelerate

Cypress is one of the most popular web app testing frameworks that support Angular testing and can be used to test our Angular project’s Responsiveness. It is feature-packed and has many benefits that can reduce the hassle of testing Angular Responsiveness significantly:

Cypress Testing Features Description
Real-Time Testing Cypress gives real-time feedback on your tests, helping you fix any errors and address responsiveness issues as soon as they get detected.
Automated Testing You can automate Angular responsive testing to ensure the app remains functional when adding or changing project features.
Debugging Tools Cypress has robust debugging tools that help developers inspect the DOM and see the step-by-step process of the test.
Friendly API Cypress’s simple API makes testing responsiveness seamless for your Angular project. The API has commands for selecting elements, setting the viewport size, and defining elements that remain visible or hidden per the set viewport size.

2. Selenium – Selenium Automates Browser!

Selenium is among the most popular testing frameworks developers use across all modern web frameworks, such as Angular, React, and Vue. Using Selenium for Angular testing has many benefits, such as:

Selenium Description
Cross-Browser Compatibility With Selenium, you can test your Angular app on various modern browsers such as Firefox, Chrome, and Safari.
Active Community and Proper Documentation Selenium has one of the largest active communities, making it easy to find help whenever you get stuck. Also, their site has extensive documentation, how-to, and other important tutorials to help you.
Ease of Integration with other tools If you’re already using other testing tools like JUnit or TestNG, Selenium can easily work with them, enhancing your Angular testing capabilities.

4. Test for accessibility

As we discussed earlier, making Angular responsive is key to ensuring your project is accessible to everyone, regardless of their physical abilities. Hence it becomes imperative to test your Angular app for accessibility. You can do so by utilizing popular Angular testing tools such as Accessibility Insight which can be downloaded as a browser extension and can identify and highlight accessibility issues regarding readability, screen reader support, and keyboard navigation.

5. Use Responsive Design Checker Tools

Responsive design checker tools like BrowserStack and Responsinator allow you to test your Angular app across various devices and browsers without owning them. BrowserStack provides access to real devices and browsers for thorough cross-browser testing, while Responsinator quickly shows how your app appears on different screen sizes. These tools help ensure your app’s responsiveness and consistency across different platforms.

Angular Best Practices for Improving Responsiveness

Now to quickly sum up everything necessary, here are some of the Angular Responsiveness best practices to keep in mind or use as an Angular checklist when optimizing your project for Responsiveness:

  1. Take a Mobile-first Approach: Start with a smaller screen, config for larger screens later.
  2. Make Use of Angular Material: Angular Material is an Angular UI design tool in the form of a UI component library that provides pre-built responsive components that can help you save development time while keeping your app responsive.
  3. Relative Styling Units: It’s better to use relative units like em and rem in place of fixed values like pixels to define different design elements for attaining responsive Angular design,
  4. Test your Angular App on Different Devices: Testing your Angular app on different screen sizes and devices reassures that your app is working as intended. You can use tools like ChromeDevTools for virtually stimulating different screen sizes too.
  5. Implement Server-Side Rendering (SSR) with Angular Universal:  Enhancing responsiveness can also involve improving perceived performance by using Server-Side Rendering (SSR). Angular Universal allows you to render your Angular app on the server, which can significantly reduce initial load times, especially on slower networks or lower-end devices. SSR not only improves load times but also boosts SEO and overall user experience.
  6. Leverage the Angular CDK’s Layout Module: While you’ve mentioned using Angular Material, you might also consider incorporating the Angular CDK’s Layout Module. This module offers responsive utilities that help manage layouts more effectively by simplifying the process of creating responsive grids, breakpoints, and other layout features.

Cutting Costs but
NOT Cutting Quality

that’s how we roll! 🚀

Hire Web Developers

Wrapping up!

This is everything you need to know about how to make your Angular app responsive and the benefits it provides. If you’re still running on Angular and still need to follow all these best practices to improve the Responsiveness of your Angular project, hire dedicated developers from Aglowid who can help you with the process!

This post was last modified on August 30, 2024 5:49 pm

Saurabh Barot: Saurabh Barot, CTO at Aglowid IT Solutions, brings over a decade of expertise in web, mobile, data engineering, Salesforce, and cloud computing. Known for his strategic leadership, he drives technology initiatives, oversees data infrastructure, and leads cross-functional teams. His expertise spans across Big Data, ETL processes, CRM systems, and cloud infrastructure, ensuring alignment with business goals and keeping the company at the forefront of innovation.
Related Post