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.
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 |
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
Setup an angular workspace locally with SCSS
npm i -g @angular/cli
ng new my-app --style scss
@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
tailwindcss
npm install tailwindcss @tailwindcss/postcss postcss --force
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 totrue
to fix a bug in tailwindcss with angular. Learn more here.
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:
@use 'tailwindcss';
directive imports the tailwindcss styles.@layer base
directive sets following:@utility form-field-error-icon
directive sets the styles for the form-field error icon.@utility icon-filled
directive sets the styles for the icon-filled class.@theme inline
directive sets the colors, font-families, radiuses to be the same as the ones in angular material.@custom-variant dark
directive sets the dark theme class.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
To apply the default typography, add the mat-typography
class to the body
element.
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.
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">
.
Below are some utilities that are used in the components.
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 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();
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>
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>
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));
}
Our example components are intentionally kept simple and self-contained. Each example:
When implementing these components in your own projects, feel free to:
We are using Inter as our default font and Poppins as our heading font.
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" />
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
We are already using @angular/material
's system variables in _tailwind.scss
file.
As per Material Design guidelines, primary tabs should have a 3px
height and a 3px 3px 0 0
shape.
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,
)
);
}
}
Include the _tabs.scss
file in src/styles.scss
file.
// src/styles.scss
@use './styles/tabs';
Create a file named _dialogs.scss
in the src/styles
folder.
In responsive mode, the dialog can be full screen.
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);
As per Material Design guidelines, dialog container should have a surface-container-high
color.
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),
)
);
}
Include the _dialogs.scss
file in src/styles.scss
file.
// src/styles.scss
@use './styles/dialogs';
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',
},
},
],
};
Below are some external libraries used in components and blocks.
@ngxpert/avvvatars is used in the blocks' codes for avatars components.
npm install @ngxpert/avvvatars
ng2-charts is used in the blocks' codes for charts components.
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 is used as file input component.
npm install @ngx-dropzone/cdk @ngx-dropzone/material
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