Manual setup for Angular Material Blocks.
If you want to follow CLI setup, go to
CLI Setup page.
@angular/materialRun the following command to install @angular/material:
Replace
PROJECT_NAMEwith your project name.
ng add @angular/material --theme custom --typography --defaults --project PROJECT_NAME --skip-confirmation --non-interactive
tailwindcssRun the following command to install tailwindcss:
npm install tailwindcss @tailwindcss/postcss postcss --force
Create a .postcssrc.json file in the root of your project with below content:
// .postcssrc.json
{
"plugins": {
"@tailwindcss/postcss": {}
}
}
tailwindcssWe are going to modify tailwindcss theme config in such a way that it allows utilities to use @angular/material's CSS variables.
Create a file at src/styles/vendors/tailwind.css with below content:
@import '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;
}
@utility scrollbar-gutter-stable {
scrollbar-gutter: stable;
}
@utility line-t {
@apply relative before:absolute before:top-0 before:-left-[100vw] before:h-px before:w-[200vw] before:bg-gray-950/5 dark:before:bg-white/10;
}
@utility line-b {
@apply relative after:absolute after:bottom-0 after:-left-[100vw] after:h-px after:w-[200vw] after:bg-gray-950/5 dark:after:bg-white/10;
}
@utility line-y {
@apply relative;
@apply before:absolute before:top-0 before:-left-[100vw] before:h-px before:w-[200vw] before:bg-gray-950/5 dark:before:bg-white/10;
@apply after:absolute after:bottom-0 after:-left-[100vw] after:h-px after:w-[200vw] after:bg-gray-950/5 dark:after:bg-white/10;
}
@utility line-t/half {
@apply relative before:absolute before:top-0 before:right-0 before:h-px before:w-screen before:bg-gray-950/5 dark:before:bg-white/10;
}
@utility line-b/half {
@apply relative after:absolute after:right-0 after:bottom-0 after:h-px after:w-screen after:bg-gray-950/5 dark:after:bg-white/10;
}
@utility line-y/half {
@apply relative;
@apply before:absolute before:top-0 before:right-0 before:h-px before:w-screen before:bg-gray-950/5 dark:before:bg-white/10;
@apply after:absolute after:right-0 after:bottom-0 after:h-px after:w-screen after:bg-gray-950/5 dark:after:bg-white/10;
}
/* `.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);
--color-inverse-surface: var(--mat-sys-inverse-surface);
--color-inverse-on-surface: var(--mat-sys-inverse-on-surface);
--color-inverse-primary: var(--mat-sys-inverse-primary);
/* same as `plain-family` in angular material */
--font-sans: var(--mat-sys-body-large-font), sans-serif;
--font-mono:
'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas,
Liberation Mono, Courier New, monospace;
--font-display: var(--mat-sys-display-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(
--mat-card-outlined-container-shape,
var(--mat-sys-corner-medium)
);
/* angular material font sizes */
/* body */
--text-body-small: var(--mat-sys-body-small-size);
--text-body-small--line-height: var(--mat-sys-body-small-line-height);
--text-body-small--letter-spacing: var(--mat-sys-body-small-tracking);
--text-body-small--font-weight: var(--mat-sys-body-small-weight);
--font-body-small: var(--mat-sys-body-small-font);
--text-body-medium: var(--mat-sys-body-medium-size);
--text-body-medium--line-height: var(--mat-sys-body-medium-line-height);
--text-body-medium--letter-spacing: var(--mat-sys-body-medium-tracking);
--text-body-medium--font-weight: var(--mat-sys-body-medium-weight);
--font-body-medium: var(--mat-sys-body-medium-font);
--text-body-large: var(--mat-sys-body-large-size);
--text-body-large--line-height: var(--mat-sys-body-large-line-height);
--text-body-large--letter-spacing: var(--mat-sys-body-large-tracking);
--text-body-large--font-weight: var(--mat-sys-body-large-weight);
--font-body-large: var(--mat-sys-body-large-font);
/* display */
--text-display-small: var(--mat-sys-display-small-size);
--text-display-small--line-height: var(--mat-sys-display-small-line-height);
--text-display-small--letter-spacing: var(--mat-sys-display-small-tracking);
--text-display-small--font-weight: var(--mat-sys-display-small-weight);
--font-display-small: var(--mat-sys-display-small-font);
--text-display-medium: var(--mat-sys-display-medium-size);
--text-display-medium--line-height: var(--mat-sys-display-medium-line-height);
--text-display-medium--letter-spacing: var(--mat-sys-display-medium-tracking);
--text-display-medium--font-weight: var(--mat-sys-display-medium-weight);
--font-display-medium: var(--mat-sys-display-medium-font);
--text-display-large: var(--mat-sys-display-large-size);
--text-display-large--line-height: var(--mat-sys-display-large-line-height);
--text-display-large--letter-spacing: var(--mat-sys-display-large-tracking);
--text-display-large--font-weight: var(--mat-sys-display-large-weight);
--font-display-large: var(--mat-sys-display-large-font);
/* headline */
--text-headline-small: var(--mat-sys-headline-small-size);
--text-headline-small--line-height: var(--mat-sys-headline-small-line-height);
--text-headline-small--letter-spacing: var(--mat-sys-headline-small-tracking);
--text-headline-small--font-weight: var(--mat-sys-headline-small-weight);
--font-headline-small: var(--mat-sys-headline-small-font);
--text-headline-medium: var(--mat-sys-headline-medium-size);
--text-headline-medium--line-height: var(
--mat-sys-headline-medium-line-height
);
--text-headline-medium--letter-spacing: var(
--mat-sys-headline-medium-tracking
);
--text-headline-medium--font-weight: var(--mat-sys-headline-medium-weight);
--font-headline-medium: var(--mat-sys-headline-medium-font);
--text-headline-large: var(--mat-sys-headline-large-size);
--text-headline-large--line-height: var(--mat-sys-headline-large-line-height);
--text-headline-large--letter-spacing: var(--mat-sys-headline-large-tracking);
--text-headline-large--font-weight: var(--mat-sys-headline-large-weight);
--font-headline-large: var(--mat-sys-headline-large-font);
/* title */
--text-title-small: var(--mat-sys-title-small-size);
--text-title-small--line-height: var(--mat-sys-title-small-line-height);
--text-title-small--letter-spacing: var(--mat-sys-title-small-tracking);
--text-title-small--font-weight: var(--mat-sys-title-small-weight);
--font-title-small: var(--mat-sys-title-small-font);
--text-title-medium: var(--mat-sys-title-medium-size);
--text-title-medium--line-height: var(--mat-sys-title-medium-line-height);
--text-title-medium--letter-spacing: var(--mat-sys-title-medium-tracking);
--text-title-medium--font-weight: var(--mat-sys-title-medium-weight);
--font-title-medium: var(--mat-sys-title-medium-font);
--text-title-large: var(--mat-sys-title-large-size);
--text-title-large--line-height: var(--mat-sys-title-large-line-height);
--text-title-large--letter-spacing: var(--mat-sys-title-large-tracking);
--text-title-large--font-weight: var(--mat-sys-title-large-weight);
--font-title-large: var(--mat-sys-title-large-font);
/* label */
--text-label-small: var(--mat-sys-label-small-size);
--text-label-small--line-height: var(--mat-sys-label-small-line-height);
--text-label-small--letter-spacing: var(--mat-sys-label-small-tracking);
--text-label-small--font-weight: var(--mat-sys-label-small-weight);
--font-label-small: var(--mat-sys-label-small-font);
--text-label-medium: var(--mat-sys-label-medium-size);
--text-label-medium--line-height: var(--mat-sys-label-medium-line-height);
--text-label-medium--letter-spacing: var(--mat-sys-label-medium-tracking);
--text-label-medium--font-weight: var(--mat-sys-label-medium-weight);
--font-label-medium: var(--mat-sys-label-medium-font);
--text-label-large: var(--mat-sys-label-large-size);
--text-label-large--line-height: var(--mat-sys-label-large-line-height);
--text-label-large--letter-spacing: var(--mat-sys-label-large-tracking);
--text-label-large--font-weight: var(--mat-sys-label-large-weight);
--font-label-large: var(--mat-sys-label-large-font);
/* animations */
/* fade-in */
--animate-fadeIn: fadeIn 300ms ease-in-out forwards
var(--animation-delay, 0ms);
@keyframes fadeIn {
0% {
opacity: 0;
transform: translateY(-5px);
}
100% {
opacity: 1;
transform: translateY(0);
}
}
/* marquee */
--animate-marquee: marquee var(--duration, 40s) linear infinite;
@keyframes marquee {
0% {
transform: translateX(0);
}
100% {
transform: translateX(calc(-100% - var(--gap, 1rem)));
}
}
--animate-marquee-vertical: marquee-vertical var(--duration, 40s) linear
infinite;
@keyframes marquee-vertical {
0% {
transform: translateY(0);
}
100% {
transform: translateY(calc(-100% - var(--gap, 1rem)));
}
}
/* blink */
--animate-blink: blink var(--duration, 500ms) linear infinite;
@keyframes blink {
100% {
opacity: 0;
}
}
}
/* #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:
@import '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._dark.scssCreate a file at src/styles/themes/_dark.scss with below content:
/*
To make `color-scheme: dark;` work make sure to have
`color-scheme: light;` just before `@include mat.theme`
and not `theme-type` in `color` property of `mat.theme` mixin
in your @angular/material theme file
*/
.dark-theme {
color-scheme: dark;
}
_warn.scssCreate a file at src/styles/themes/_warn.scss with below content:
@use '@angular/material' as mat;
// In mat-button and some other components, color="warn" is used to indicate urgent or error state.
[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),
)
);
}
_dialogs.scssCreate a file at src/styles/_dialogs.scss with below content:
@use '@angular/material' as mat;
.side-dialog {
position: absolute !important;
top: 0;
bottom: 0;
right: 0;
&-lg {
--mat-dialog-container-max-width: 80vw;
width: var(--mat-dialog-container-max-width);
}
.mat-mdc-dialog-content {
max-height: 100%;
}
.mat-mdc-dialog-surface {
border-top-right-radius: 0;
border-bottom-right-radius: 0;
}
}
.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);
}
}
}
.dialog-outlined {
.dark-theme & {
.mat-mdc-dialog-surface {
border: 1px solid var(--mat-sys-outline);
}
}
}
_sizes.scssCreate a file at src/styles/_sizes.scss with below content:
@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));
}
}
}
}
@include sizes();
_tabs.scssCreate a file at src/styles/_tabs.scss with below content:
@use '@angular/material' as mat;
.m3-primary-tab-group > .mat-mdc-tab-header {
.mdc-tab {
mat-icon {
font-variation-settings: 'FILL' 0;
transition: font-variation-settings
var(--mat-tab-animation-duration, 250ms) cubic-bezier(0.35, 0, 0.25, 1);
}
&--active {
mat-icon {
font-variation-settings: 'FILL' 1;
}
}
}
}
[fitInkBarToContent],
[ng-reflect-fit-ink-bar-to-content='true'] {
> .mat-mdc-tab-header,
> .mat-mdc-tab-link-container {
@include mat.tabs-overrides(
(
active-indicator-shape: 3px 3px 0 0,
active-indicator-height: 3px,
)
);
}
}
// apply below class to <mat-tab-group />
.pill-tab-group {
--active-indicator-color: var(--mat-sys-primary-container);
--active-indicator-height: 32px;
--active-indicator-shape: var(--mat-sys-corner-small);
// set this to transparent if you don't want ripple to be visible
--ripple-color: var(--mat-sys-on-surface);
@include mat.tabs-overrides(
(
active-indicator-height: var(--active-indicator-height),
active-indicator-color: var(--active-indicator-color),
active-focus-indicator-color: var(--active-indicator-color),
active-hover-indicator-color: var(--active-indicator-color),
active-indicator-shape: var(--active-indicator-shape),
active-ripple-color: var(--ripple-color),
inactive-ripple-color: var(--ripple-color),
)
);
.mdc-tab {
/**
/* need to keep the z-index high so that
/* active indicator of next tab does not overlap label
*/
&:not(.mdc-tab--active) {
z-index: 2;
}
/**
/* keep active indicator vertically center aligned
*/
.mdc-tab-indicator {
top: 50%;
transform: translateY(-50%);
height: var(--active-indicator-height);
border-radius: var(--active-indicator-shape);
}
/**
/* keep ripple vertically & horizontally center aligned
*/
.mat-ripple,
.mdc-tab__ripple::before {
top: 50%;
transform: translateY(-50%) translateX(-50%);
height: var(--active-indicator-height);
border-radius: var(--active-indicator-shape);
left: 50%;
// reduced width to visually show gap when hovered
width: 90%;
}
}
}
_base.scssCreate a file at src/styles/_base.scss with below content:
html {
background-color: var(--mat-sys-background);
color: var(--mat-sys-on-background);
}
index.scssCreate a file at src/styles/index.scss with below content:
@forward './themes/dark';
@forward './themes/warn';
@forward './dialogs';
@forward './sizes';
@forward './tabs';
@forward './base';
Make sure to add src/styles/index.scss & src/styles/vendors/tailwind.css to your styles array in angular.json file:
"styles": [
"src/styles/index.scss",
"src/styles/vendors/tailwind.css"
]
We are using new Material Symbols in our project. So, we need to add the following link to our index.html file:
<link
rel="stylesheet"
href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:opsz,wght,FILL,[email protected],100..700,0..1,-50..200" />
import { MAT_ICON_DEFAULT_OPTIONS } from "@angular/material/icon";
export const appConfig: ApplicationConfig = {
providers: [
{
provide: MAT_ICON_DEFAULT_OPTIONS,
useValue: {
fontSet: "material-symbols-outlined",
},
},
],
};
Now you can also use filled variants of the icons by adding
icon-filledto the class. We have already added this class to_tailwind.cssfile.<mat-icon class="icon-filled">home</mat-icon>
ng2-chartsSome blocks use ng2-charts to render charts. Run below command to install ng2-charts:
ng add ng2-charts
In case CLI does not add providers, you can add them manually:
import { provideCharts, withDefaultRegisterables } from "ng2-charts";
export const appConfig: ApplicationConfig = {
providers: [
provideCharts(withDefaultRegisterables()),
],
};
Our components depend on a few utilities. You can read more about them in the
On our website, we apply font smoothing and recommend you do the same. Simply add the antialiased utility to the HTML tag <html className="antialiased">.
Angular Material Dev UI (or Angular Material Blocks) 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), LinkedIn, Instagram & Threads