This guide will help you setup project to use Angular Material & TailwindCSS together. So that you can use the codes from this platform easily in your projects.

Requirements

All of the components in Angular Material Dev are designed for/with the following versions:

Package Version
Angular 19
@angular/cdk 19
@angular/material 19
tailwindcss 4

TL;DR

If you are staring from scratch and don't want to read full instructions, clone the boilerplate repository and run the project.

git clone https://github.com/shhdharmen/angular-material-tailwind
cd angular-material-tailwind
npm i
npm start

Environment setup

1. Setup workspace

Setup an angular workspace locally with SCSS

npm i -g @angular/cli
ng new my-app --style scss

2. Install @angular/material

Install @angular/material and opt for Custom theme. For more information, please refer to angular material installation guide

ng add @angular/material --theme custom --typography --defaults

3. Install tailwindcss

npm install tailwindcss @tailwindcss/postcss postcss --force

3.1 PostCSS configuration

Create a file named .postcssrc.json in the root of the project with below content:

{
  "plugins": {
    "@tailwindcss/postcss": {
      "optimize": {
        "minify": true
      }
    }
  }
}

optimize.minify is set to true to fix a bug in tailwindcss with angular. Learn more here.

3.2 Tailwind Configuration file

Create a file named _tailwind.scss in the src/styles folder with below content:

// src/styles/_tailwind.scss
@use 'tailwindcss';

// base for setting the font-families
@layer base {
  body {
    // same as `plain-family` in angular material
    font-family: var(--mat-sys-body-large-font), sans-serif;

    // apply @angular/material's background and foreground colors
    background-color: var(--mat-sys-surface);
    color: var(--mat-sys-on-surface);
  }
  h1,
  h2,
  h3,
  h4,
  h5,
  h6 {
    // same as `brand-family` in angular material
    font-family: var(--mat-sys-display-large-font), sans-serif;
  }
}

@utility form-field-error-icon {
  vertical-align: bottom;
  font-variation-settings: 'FILL' 1;
  height: 16px !important;
  width: 16px !important;
  font-size: 16px !important;
}

@utility icon-filled {
  font-variation-settings: 'FILL' 1;
}

// `.dark-theme` is the class name to enable dark mode in our application
@custom-variant dark (&:where(.dark-theme, .dark-theme *));

// `inline` is needed to use the CSS variables in tailwind's @theme
@theme inline {
  --color-primary: var(--mat-sys-primary);
  --color-on-primary: var(--mat-sys-on-primary);
  --color-primary-container: var(--mat-sys-primary-container);
  --color-on-primary-container: var(--mat-sys-on-primary-container);
  --color-secondary: var(--mat-sys-secondary);
  --color-on-secondary: var(--mat-sys-on-secondary);
  --color-secondary-container: var(--mat-sys-secondary-container);
  --color-on-secondary-container: var(--mat-sys-on-secondary-container);
  --color-tertiary: var(--mat-sys-tertiary);
  --color-on-tertiary: var(--mat-sys-on-tertiary);
  --color-tertiary-container: var(--mat-sys-tertiary-container);
  --color-on-tertiary-container: var(--mat-sys-on-tertiary-container);
  --color-error: var(--mat-sys-error);
  --color-on-error: var(--mat-sys-on-error);
  --color-error-container: var(--mat-sys-error-container);
  --color-on-error-container: var(--mat-sys-on-error-container);
  --color-outline: var(--mat-sys-outline);
  --color-outline-variant: var(--mat-sys-outline-variant);
  --color-surface: var(--mat-sys-surface);
  --color-surface-container-highest: var(--mat-sys-surface-container-highest);
  --color-surface-container-high: var(--mat-sys-surface-container-high);
  --color-surface-container-medium: var(--mat-sys-surface-container-medium);
  --color-surface-container-low: var(--mat-sys-surface-container-low);
  --color-surface-container-lowest: var(--mat-sys-surface-container-lowest);
  --color-surface-container: var(--mat-sys-surface-container);
  --color-surface-variant: var(--mat-sys-surface-variant);
  --color-on-surface-variant: var(--mat-sys-on-surface-variant);
  --color-on-surface: var(--mat-sys-on-surface);

  // same as `plain-family` in angular material
  --font-sans: var(--mat-sys-body-large-font), sans-serif;

  // angular material radiuses
  --radius-corner-extra-large: var(--mat-sys-corner-extra-large);
  --radius-corner-extra-small: var(--mat-sys-corner-extra-small);
  --radius-corner-full: var(--mat-sys-corner-full);
  --radius-corner-large: var(--mat-sys-corner-large);
  --radius-corner-medium: var(--mat-sys-corner-medium);
  --radius-corner-small: var(--mat-sys-corner-small);
  --radius-mat-card: var(--mdc-outlined-card-container-shape, var(--mat-sys-corner-medium));
}

// #region: Tailwind X Angular Material fix
.mdc-notched-outline__notch {
  border-style: none;
}
.mat-mdc-icon-button {
  line-height: 1;
}
// #endregion

Let's break down the file:

  1. The @use 'tailwindcss'; directive imports the tailwindcss styles.
  2. The @layer base directive sets following:
    • font-families for the body and heading elements to be the same as the ones in angular material.
    • background and foreground colors for the body element to be the same as the ones in angular material.
  3. The @utility form-field-error-icon directive sets the styles for the form-field error icon.
  4. The @utility icon-filled directive sets the styles for the icon-filled class.
  5. The @theme inline directive sets the colors, font-families, radiuses to be the same as the ones in angular material.
  6. The @custom-variant dark directive sets the dark theme class.
  7. Few styles are overridden to fix the styles for the Angular Material components.

4. Theme file

Ideally, your main style file should look like this:

// src/styles.scss
@use '@angular/material' as mat;
@use './styles/tailwind';

// #region: Setting @angular/material custom theme
html {
  color-scheme: light;
  @include mat.theme(
    (
      color: (
        primary: mat.$azure-palette,
        tertiary: mat.$blue-palette,
      ),
      typography: Roboto,
      density: 0,
    )
  );
}
// #endregion

// #region: Setting @angular/material dark theme
.dark-theme {
  color-scheme: dark;
}
// #endregion

Material typography

To apply the default typography, add the mat-typography class to the body element.

Dark Mode

For all Blocks examples, we've added a dark-theme class to the html element. This is used to apply the dark theme to the components.

Font smoothing (antialiasing)

On our website, we apply font smoothing and recommend you do the same. Simply add the antialiased utility to the HTML tag <html class="antialiased">.

Utilities

Below are some utilities that are used in the components.

Warn theme

In mat-button and some other components, color="warn" is used to indicate urgent or error state.

Create a file called _warn.scss at src/styles/theme with below content:

// _warn.scss

@use '@angular/material' as mat;

[color='warn'],
.warn-theme {
  @include mat.theme-overrides(
    (
      primary: var(--mat-sys-error),
      primary-container: var(--mat-sys-error-container),
      on-primary: var(--mat-sys-on-error),
      on-primary-container: var(--mat-sys-on-error-container),
    )
  );
}

Density/sizes

Density is a utility that is used to set the density of the components.

// src/styles/_sizes.scss

@use '@angular/material' as mat;

$densities: 1, 2, 3, 4, 5;

@mixin sizes() {
  @each $density in $densities {
    .density-#{$density} {
      @include mat.theme(
        (
          density: -$density,
        )
      );

      mat-slide-toggle button {
        transform: scale(1 - calc($density / 10));
      }
    }
  }
}
// src/styles.scss
@use './styles/sizes';

@include sizes.sizes();

Filled Icons

To get the filled variant of an icon, add the icon-filled class to the mat-icon element.

<mat-icon class="icon-filled">check_circle</mat-icon>

Form-field error icons

To apply the form-field-error-icon class to the error icon, add the form-field-error-icon class to the mat-icon element.

<mat-form-field class="w-full">
  <mat-label>Password</mat-label>
  <input matInput type="password" formControlName="password" />
  @if (form.get('password')?.touched && form.get('password')?.hasError('required')) {
    <mat-error>
      <mat-icon class="form-field-error-icon">error</mat-icon>
      Password is required
    </mat-error>
  }
  @if (form.get('password')?.touched && form.get('password')?.hasError('minlength')) {
    <mat-error>
      <mat-icon class="form-field-error-icon">error</mat-icon>
      Password must be at least 8 characters
    </mat-error>
  }
</mat-form-field>

Services

DeviceService

Path: src/app/blocks/utils/services/device.service.ts

import { BreakpointObserver, Breakpoints } from '@angular/cdk/layout';
import { Injectable, inject } from '@angular/core';
import { Observable, map } from 'rxjs';

@Injectable({ providedIn: 'root' })
export class DeviceService {
  private breakpointObserver = inject(BreakpointObserver);

  isHandset$: Observable<boolean> = this.breakpointObserver
    .observe('(max-width: 640px)')
    .pipe(map((result) => result.matches));
}

Creating components

Our example components are intentionally kept simple and self-contained. Each example:

  1. Shows the core functionality in a single component
  2. Uses minimal data abstraction for clarity

When implementing these components in your own projects, feel free to:

  1. Split them into smaller, reusable components
  2. Add custom inputs and outputs
  3. Integrate with your preferred data management solution
  4. Adjust the architecture to match your project's needs

Optional: Fonts

We are using Inter as our default font and Poppins as our heading font.

Add fonts in index.html

<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link href="https://fonts.googleapis.com/css2?family=Inter:ital,opsz,wght@0,14..32,100..900;1,14..32,100..900&family=Poppins:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;0,800;0,900;1,100;1,200;1,300;1,400;1,500;1,600;1,700;1,800;1,900&display=swap" rel="stylesheet" />

Add fonts to src/styles.scss

// src/styles.scss
// imports remains same

$heading-font-family: Poppins, sans-serif;
$regular-font-family: Inter, sans-serif;

html {
  @include mat.theme(
    (
      // colors: ...
      typography: (plain-family: #{meta.inspect($regular-font-family)}, brand-family: #{meta.inspect($heading-font-family)}),
      // density: ...
    )
  );
}

// rest remains same

Fonts in tailwind

We are already using @angular/material's system variables in _tailwind.scss file.

Optional: Material Design Primary Tabs

As per Material Design guidelines, primary tabs should have a 3px height and a 3px 3px 0 0 shape.

Tabs styles

Create a file named _tabs.scss in the src/styles folder with below content:

// src/styles/_tabs.scss
@use '@angular/material' as mat;

[fitInkBarToContent],
[ng-reflect-fit-ink-bar-to-content='true'] {
  > .mat-mdc-tab-header {
    @include mat.tabs-overrides(
      (
        active-indicator-shape: 3px 3px 0 0,
        active-indicator-height: 3px,
      )
    );
  }
}

Use tabs styles

Include the _tabs.scss file in src/styles.scss file.

// src/styles.scss
@use './styles/tabs';

Changes for Angular Material Dialog

Create a file named _dialogs.scss in the src/styles folder.

Full screen dialog

In responsive mode, the dialog can be full screen.

Full Dialog styles

Add below content to _dialogs.scss file:

// src/styles/_dialogs.scss

.full-screen-dialog {
  @media (width < 40rem) {
    @include mat.dialog-overrides(
      (
        container-shape: 0,
      )
    );
    .mat-mdc-dialog-content {
      // device height - header height -actions height
      max-height: calc(100dvh - 68px);
    }
  }
}

Now whenever you want to make a dialog full screen in small devices, just add the full-screen-dialog as panelClass to the dialog.

const options: MatDialogConfig = {
  maxWidth: isHandset ? '100dvw' : '1024px',
  panelClass: 'full-screen-dialog',
};
if (isHandset) {
  options.minWidth = '100dvw';
  options.minHeight = '100dvh';
}
this.dialog.open(AddApplicationDialogComponent, options);

Optional: Material Design Dialog Container Color

As per Material Design guidelines, dialog container should have a surface-container-high color.

Dialog styles

Add below content to _dialogs.scss file:

// src/styles/_dialogs.scss
@use '@angular/material' as mat;

:root {
  @include mat.dialog-overrides(
    (
      container-color: var(--mat-sys-surface-container-high),
    )
  );
}

Use dialog styles

Include the _dialogs.scss file in src/styles.scss file.

// src/styles.scss
@use './styles/dialogs';

Optional: Material Symbols

To use icons from Material Symbols, make sure to include that fonts in index.html:

<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:opsz,wght,FILL,[email protected],100..700,0..1,-50..200" />

And configure app.config.ts to use the font:

import { MAT_ICON_DEFAULT_OPTIONS } from '@angular/material/icon';

export const appConfig: ApplicationConfig = {
  providers: [
    {
      provide: MAT_ICON_DEFAULT_OPTIONS,
      useValue: {
        fontSet: 'material-symbols-outlined',
      },
    },
  ],
};

External libraries

Below are some external libraries used in components and blocks.

@ngxpert/avvvatars

@ngxpert/avvvatars is used in the blocks' codes for avatars components.

Install @ngxpert/avvvatars

npm install @ngxpert/avvvatars

ng2-charts

ng2-charts is used in the blocks' codes for charts components.

Install ng2-charts

npm install ng2-charts chart.js --save

Provide a configuration, typically in your app.config.ts:

import { provideCharts, withDefaultRegisterables } from 'ng2-charts';

export const appConfig: ApplicationConfig = {
  providers: [provideCharts(withDefaultRegisterables())],
};

ngx-dropzone

ngx-dropzone is used as file input component.

Install ngx-dropzone

npm install @ngx-dropzone/cdk @ngx-dropzone/material
Angular Material Dev

Angular Material Dev UI is one place stop for developers to explore components and blocks for their Angular Material and Tailwind CSS based applications.

Find us on X (Twitter) and LinkedIn