Angular Material Style Guide Skill
Guide the creation of a shareable style guide built on Angular Material's M3 theming API, structured as a publishable SCSS package with design tokens, custom themes, and component-level overrides.
Workflow
Step 1: Assess the Project
Determine the current state of the project:
# Check Angular Material version
node -e "const p = require('./package.json'); console.log(p.dependencies['@angular/material'])"
# Check if a custom theme already exists
find src -name "*.scss" | head -20
grep -rl "mat\." src/styles/ 2>/dev/null || echo "No existing Material theme files found"
Confirm:
- Angular Material v17+ is installed (required for M3 theming API)
- If using v15 or v16, recommend upgrading first (use the
angular-upgradeskill) - Whether a custom theme already exists or this is a fresh setup
If Angular Material is not installed:
ng add @angular/material
Step 2: Define Design Tokens
Create a design tokens file that serves as the single source of truth for the style guide. Ask the user about their brand requirements, or use sensible defaults.
Create src/styles/tokens/ directory with the following files:
_colors.scss
Define the color palette using M3 token structure:
// Brand colors -- the primary seed colors for M3 palette generation
$brand-primary: #1a73e8; // Primary brand color
$brand-secondary: #34a853; // Secondary brand color
$brand-tertiary: #fbbc04; // Tertiary/accent color
$brand-error: #ea4335; // Error color
// Neutral colors for surfaces and text
$neutral: #191c1e;
$neutral-variant: #404944;
// Custom semantic colors (extend beyond M3 defaults)
$semantic-success: #34a853;
$semantic-warning: #fbbc04;
$semantic-info: #1a73e8;
// Surface colors
$surface-dim: #d9dbd9;
$surface-bright: #f7faf7;
_typography.scss
Define the type scale:
@use '@angular/material' as mat;
// Font families
$font-family-plain: 'Inter', 'Roboto', sans-serif;
$font-family-brand: 'Inter', 'Roboto', sans-serif;
$font-family-code: 'Fira Code', 'Roboto Mono', monospace;
// Custom type scale overrides (M3 type scale tokens)
// These will be applied via mat.define-theme()
$type-scale: (
// Display
display-large-font: $font-family-brand,
display-large-size: 57px,
display-large-line-height: 64px,
display-large-weight: 400,
// Headline
headline-large-font: $font-family-brand,
headline-large-size: 32px,
headline-large-line-height: 40px,
// Title
title-large-font: $font-family-plain,
title-large-size: 22px,
title-large-line-height: 28px,
// Body
body-large-font: $font-family-plain,
body-large-size: 16px,
body-large-line-height: 24px,
body-medium-font: $font-family-plain,
body-medium-size: 14px,
body-medium-line-height: 20px,
// Label
label-large-font: $font-family-plain,
label-large-size: 14px,
label-large-line-height: 20px,
label-large-weight: 500,
);
_spacing.scss
Define spacing scale and layout tokens:
// Base spacing unit (4px grid)
$spacing-unit: 4px;
// Spacing scale
$spacing-xxs: $spacing-unit; // 4px
$spacing-xs: $spacing-unit * 2; // 8px
$spacing-sm: $spacing-unit * 3; // 12px
$spacing-md: $spacing-unit * 4; // 16px
$spacing-lg: $spacing-unit * 6; // 24px
$spacing-xl: $spacing-unit * 8; // 32px
$spacing-xxl: $spacing-unit * 12; // 48px
$spacing-xxxl: $spacing-unit * 16; // 64px
// Layout widths
$layout-max-width: 1280px;
$layout-content-width: 960px;
$layout-sidebar-width: 280px;
// Border radius (M3 shape tokens)
$radius-none: 0;
$radius-xs: 4px;
$radius-sm: 8px;
$radius-md: 12px;
$radius-lg: 16px;
$radius-xl: 28px;
$radius-full: 9999px;
_elevation.scss
Define elevation/shadow tokens aligned with M3 levels:
// M3 elevation levels (0-5)
$elevation-0: none;
$elevation-1: 0 1px 2px 0 rgba(0, 0, 0, 0.3), 0 1px 3px 1px rgba(0, 0, 0, 0.15);
$elevation-2: 0 1px 2px 0 rgba(0, 0, 0, 0.3), 0 2px 6px 2px rgba(0, 0, 0, 0.15);
$elevation-3: 0 4px 8px 3px rgba(0, 0, 0, 0.15), 0 1px 3px rgba(0, 0, 0, 0.3);
$elevation-4: 0 6px 10px 4px rgba(0, 0, 0, 0.15), 0 2px 3px rgba(0, 0, 0, 0.3);
$elevation-5: 0 8px 12px 6px rgba(0, 0, 0, 0.15), 0 4px 4px rgba(0, 0, 0, 0.3);
// Semantic elevation mappings
$elevation-card: $elevation-1;
$elevation-dialog: $elevation-3;
$elevation-nav: $elevation-2;
$elevation-fab: $elevation-3;
$elevation-menu: $elevation-2;
Step 3: Create the M3 Theme
Create the main theme file that uses Angular Material's M3 theming API.
src/styles/_theme.scss
@use '@angular/material' as mat;
@use './tokens/colors' as colors;
@use './tokens/typography' as typography;
// Define the M3 theme using Angular Material's define-theme API
$light-theme: mat.define-theme((
color: (
theme-type: light,
primary: mat.$azure-palette, // Replace with custom palette if needed
tertiary: mat.$blue-palette,
use-system-variables: true,
),
typography: (
plain-family: typography.$font-family-plain,
brand-family: typography.$font-family-brand,
use-system-variables: true,
),
density: (
scale: 0,
),
));
$dark-theme: mat.define-theme((
color: (
theme-type: dark,
primary: mat.$azure-palette,
tertiary: mat.$blue-palette,
use-system-variables: true,
),
typography: (
plain-family: typography.$font-family-plain,
brand-family: typography.$font-family-brand,
use-system-variables: true,
),
density: (
scale: 0,
),
));
src/styles/styles.scss (application entry point)
@use '@angular/material' as mat;
@use './theme' as theme;
@use './tokens/spacing' as spacing;
@use './tokens/elevation' as elevation;
@use './component-overrides' as overrides;
// Apply the base M3 theme
html {
@include mat.all-component-themes(theme.$light-theme);
@include mat.system-level-colors(theme.$light-theme);
@include mat.system-level-typography(theme.$light-theme);
}
// Dark mode support
.dark-theme,
[data-theme="dark"] {
@include mat.system-level-colors(theme.$dark-theme);
}
// Apply component overrides
@include overrides.all();
Step 4: Create Component-Level Style Overrides
Create src/styles/_component-overrides.scss for targeted adjustments to specific Material components.
@use '@angular/material' as mat;
@use './tokens/spacing' as spacing;
@use './tokens/elevation' as elevation;
@mixin all() {
@include button-overrides();
@include card-overrides();
@include form-field-overrides();
@include dialog-overrides();
@include table-overrides();
}
@mixin button-overrides() {
.mat-mdc-button,
.mat-mdc-raised-button,
.mat-mdc-flat-button,
.mat-mdc-outlined-button {
border-radius: spacing.$radius-xl;
letter-spacing: 0.02em;
font-weight: 500;
}
// Increase touch target for mobile
.mat-mdc-icon-button {
--mdc-icon-button-state-layer-size: 48px;
}
}
@mixin card-overrides() {
.mat-mdc-card {
border-radius: spacing.$radius-md;
box-shadow: elevation.$elevation-card;
padding: spacing.$spacing-lg;
}
.mat-mdc-card-title {
margin-bottom: spacing.$spacing-xs;
}
}
@mixin form-field-overrides() {
.mat-mdc-form-field {
width: 100%;
}
// Consistent spacing between stacked form fields
.mat-mdc-form-field + .mat-mdc-form-field {
margin-top: spacing.$spacing-sm;
}
}
@mixin dialog-overrides() {
.mat-mdc-dialog-container {
--mdc-dialog-container-shape: #{spacing.$radius-lg};
}
.mat-mdc-dialog-actions {
padding: spacing.$spacing-md spacing.$spacing-lg;
gap: spacing.$spacing-xs;
}
}
@mixin table-overrides() {
.mat-mdc-table {
border-radius: spacing.$radius-sm;
overflow: hidden;
}
.mat-mdc-header-cell {
font-weight: 600;
letter-spacing: 0.01em;
}
.mat-mdc-row:hover {
background-color: var(--mat-sys-surface-variant);
}
}
Step 5: Structure as a Publishable Package
Organize the style guide so it can be consumed by other Angular projects:
src/styles/
_theme.scss # Main theme definition
_component-overrides.scss # Component-level overrides
styles.scss # Application entry point
tokens/
_colors.scss # Color tokens
_typography.scss # Typography tokens
_spacing.scss # Spacing and shape tokens
_elevation.scss # Elevation/shadow tokens
_index.scss # Barrel export for all tokens
Create src/styles/tokens/_index.scss:
@forward 'colors';
@forward 'typography';
@forward 'spacing';
@forward 'elevation';
If this should be a standalone publishable package, create a package.json for the styles directory:
{
"name": "@org/design-tokens",
"version": "1.0.0",
"description": "Design tokens and Angular Material theme for [Organization]",
"main": "styles.scss",
"files": [
"*.scss",
"tokens/**/*.scss"
],
"peerDependencies": {
"@angular/material": ">=17.0.0"
}
}
Step 6: Verify the Theme
Run a build to verify everything compiles:
ng build
Check for:
- No SCSS compilation errors
- No missing
@useor@forwardreferences - M3 system-level CSS variables are being generated in the output
- Dark mode toggle works (if applicable)
Rules
- Always use the M3 theming API (
mat.define-theme()) rather than the legacymat.define-light-theme()/mat.define-dark-theme()for Angular Material v17+. - Use
@useand@forwardexclusively. Never use@import(deprecated in Dart Sass). - Token values should be customized to the user's brand. The defaults in this skill are starting points -- always ask the user for brand colors, fonts, and design preferences before finalizing.
- Keep component overrides minimal. Prefer token customization over CSS overrides wherever possible.
- Use CSS custom properties (
var(--mat-sys-*)) in application code rather than referencing SCSS variables directly, to support runtime theme switching. - If the user has an existing theme, migrate it to the M3 API rather than starting from scratch. Preserve their design intent.
- Test both light and dark themes. Verify contrast ratios meet WCAG 2.1 AA minimum (4.5:1 for normal text, 3:1 for large text).