Skip to main content

Material Design Component (MDC) Styling Guide

This document provides comprehensive guidance on modifying Material Design Component colors and styles in Angular Material applications, covering best practices, debugging techniques, and specific implementation examples.

Table of Contents

  • Understanding Material Design Components (MDC)
  • Key Principles for Material Styling
  • Finding the Right Elements to Style
  • Common Styling Patterns
  • Toggle/Switch Styling Deep Dive
  • Dialog Surface Modifications
  • Debugging and Development Tips
  • Best Practices and Pitfalls

Understanding Material Design Components (MDC)

Legacy vs MDC Classes

Angular Material has transitioned from legacy Material components to MDC (Material Design Components) starting from version 15+. This transition introduced new CSS class naming conventions:

// Legacy Material (Pre-v15)
.mat-button
.mat-form-field
.mat-table
.mat-slide-toggle

// MDC Material (v15+)
.mat-mdc-button
.mat-mdc-form-field
.mat-mdc-table
.mat-mdc-slide-toggle

MDC Internal Classes

MDC components also use internal classes that follow the .mdc-* pattern:

// MDC Internal Classes
.mdc-button
.mdc-text-field
.mdc-data-table
.mdc-switch

Key Principles for Material Styling

1. Specificity Hierarchy

Always consider CSS specificity when overriding Material styles:

// Low specificity - may not work
.mat-button {
color: red;
}

// Higher specificity - more likely to work
.mat-mdc-button.mat-primary {
color: red !important;
}

// Highest specificity - will override most defaults
html .my-component .mat-mdc-button.mat-primary {
color: red !important;
}

2. CSS Custom Properties vs Direct Styling

Material components use CSS custom properties (CSS variables) extensively:

// Using CSS Custom Properties (Recommended)
.mat-mdc-button {
--mdc-filled-button-container-color: #714ac6;
--mdc-filled-button-label-text-color: white;
}

// Direct property override (Alternative)
.mat-mdc-button {
background-color: #714ac6 !important;
color: white !important;
}

3. Component State Targeting

Target specific component states using pseudo-classes and state classes:

// Checked state
.mat-mdc-slide-toggle.mat-checked {
--mdc-switch-selected-track-color: #714ac6;
}

// Unchecked state
.mat-mdc-slide-toggle:not(.mat-checked) {
--mdc-switch-unselected-track-color: #9e9e9e;
}

// Hover state
.mat-mdc-button:hover {
background-color: #5a3a9a;
}

// Disabled state
.mat-mdc-button:disabled {
opacity: 0.5;
}

Finding the Right Elements to Style

1. Browser Developer Tools

The most effective way to identify styling targets:

Steps:

  1. Open browser DevTools (F12)
  2. Right-click on the element and select "Inspect"
  3. Look for the component structure in the Elements panel
  4. Check the Computed styles to see which CSS rules are applied
  5. Look for CSS custom properties in the element's styles

Example - Finding Toggle Elements:

<!-- What you see in DevTools -->
<mat-slide-toggle class="mat-mdc-slide-toggle">
<div class="mdc-switch">
<div class="mdc-switch__track"></div>
<div class="mdc-switch__handle">
<div class="mdc-switch__icon"></div>
</div>
</div>
</mat-slide-toggle>

2. Angular Material Documentation

Check the official Angular Material documentation for:

  • Component API reference
  • Theming guide
  • CSS custom properties list
  • Example implementations

3. Source Code Investigation

For complex components, examine the Angular Material source code:

  • Check component TypeScript files for class names
  • Look at component SCSS files for default styling
  • Understand the component's DOM structure

Common Styling Patterns

1. Color Overrides

// Primary color changes
.mat-primary {
--mat-full-pseudo-checkbox-selected-icon-color: #714ac6 !important;
--mat-minimal-pseudo-checkbox-selected-checkmark-color: #714ac6 !important;
}

// Accent color changes
.mat-accent {
--mdc-switch-selected-handle-color: #00c2f3 !important;
--mdc-switch-selected-track-color: #70ddf8 !important;
}

2. Typography Overrides

// Font family changes
.mat-mdc-tab .mdc-tab__text-label {
font-family: 'Source Sans Pro', sans-serif !important;
font-weight: 600 !important;
font-size: 16px !important;
}

// Letter spacing adjustments
.mat-mdc-tab-link {
letter-spacing: unset !important;
}

3. Size and Layout Adjustments

// Form field height
.mat-mdc-text-field-wrapper {
height: 40px !important;
display: flex !important;
align-items: center !important;
}

// Icon sizing
.mat-icon {
height: 28px !important;
width: 28px !important;
}

Toggle/Switch Styling Deep Dive

Understanding Toggle Structure

<mat-slide-toggle class="mat-mdc-slide-toggle">
<div class="mdc-switch">
<div class="mdc-switch__track"></div>
<div class="mdc-switch__handle">
<div class="mdc-switch__icon"></div>
</div>
</div>
</mat-slide-toggle>

State-Based Styling

Checked State

// When toggle is ON
.mat-mdc-slide-toggle.mat-checked {
--mdc-switch-selected-track-color: #714ac6;
--mdc-switch-selected-handle-color: #714ac6;
}

// Alternative direct styling
.mat-mdc-slide-toggle.mat-checked .mdc-switch__track {
background-color: #714ac6 !important;
}

Unchecked State

// When toggle is OFF - multiple targeting approaches
.mat-mdc-slide-toggle:not(.mat-checked) .mdc-switch__track {
background-color: #9e9e9e !important;
opacity: 1 !important;
}

// Using MDC state class
.mat-mdc-slide-toggle .mdc-switch--unselected .mdc-switch__track {
background-color: #9e9e9e !important;
}

// Targeting both Angular and MDC state classes
.mat-mdc-slide-toggle:not(.mat-checked):not(.mat-mdc-slide-toggle-checked) .mdc-switch__track {
background-color: #9e9e9e !important;
}

Pseudo-Element Styling

Some Material components use pseudo-elements for visual effects:

// Track pseudo-elements
.mat-mdc-slide-toggle .mdc-switch--unselected .mdc-switch__track::before {
background-color: #9e9e9e !important;
}

.mat-mdc-slide-toggle .mdc-switch--unselected .mdc-switch__track::after {
background-color: #9e9e9e !important;
}

Icon Customization

// Remove default icons
.mdc-switch--selected .mdc-switch__icon,
.mdc-switch--unselected .mdc-switch__icon {
fill: transparent !important;
}

// Custom icon styling for unselected state
.mdc-switch--unselected .mdc-switch__icon {
fill: none !important;
background-color: #f2f3f4 !important;
width: 21px !important;
height: 21.5px !important;
border-radius: 20px;
}

Dialog Surface Modifications

Understanding Dialog Structure

<div class="cdk-overlay-container">
<div class="cdk-overlay-backdrop"></div>
<div class="cdk-global-overlay-wrapper">
<div class="cdk-overlay-pane">
<div class="mat-mdc-dialog-container">
<div class="mat-mdc-dialog-surface">
<!-- Your dialog content -->
</div>
</div>
</div>
</div>
</div>

Common Dialog Customizations

// Remove height constraints and overflow issues
.mat-mdc-dialog-surface {
height: unset !important;
overflow: hidden !important;
}

// Custom dialog sizing
.mat-mdc-dialog-container {
max-width: 90vw !important;
max-height: 90vh !important;
}

// Dialog backdrop customization
.cdk-overlay-backdrop {
background-color: rgba(0, 0, 0, 0.6) !important;
}

// Dialog positioning
.cdk-global-overlay-wrapper {
align-items: flex-start !important;
padding-top: 50px !important;
}

Dialog Content Styling

// Remove default padding
.mat-mdc-dialog-content {
padding: 0 !important;
margin: 0 !important;
}

// Custom action button styling
.mat-mdc-dialog-actions {
padding: 16px !important;
justify-content: flex-end !important;
}

Advanced Styling Techniques

1. CSS Custom Property Inheritance

// Set custom properties at root level
:root {
--app-primary-color: #714ac6;
--app-secondary-color: #00c2f3;
}

// Use in components
.mat-mdc-button {
--mdc-filled-button-container-color: var(--app-primary-color);
}

2. Conditional Styling with CSS Classes

// Theme-based styling
.dark-theme .mat-mdc-button {
--mdc-filled-button-container-color: #333;
--mdc-filled-button-label-text-color: white;
}

.light-theme .mat-mdc-button {
--mdc-filled-button-container-color: #714ac6;
--mdc-filled-button-label-text-color: white;
}

3. Component-Specific Styling

// Scope styles to specific components
.deals-page .mat-mdc-option.mdc-list-item--selected {
color: #714ac6 !important;
background-color: #eee7f8 !important;
}

Debugging and Development Tips

1. Using Browser DevTools Effectively

Console Commands:

// Find all elements with specific class
document.querySelectorAll('.mat-mdc-slide-toggle')

// Get computed styles
getComputedStyle(document.querySelector('.mat-mdc-button'))

// Check CSS custom property values
getComputedStyle(document.documentElement).getPropertyValue('--mdc-filled-button-container-color')

2. Testing Specificity

// Add temporary background to test if selector works
.mat-mdc-button {
background: red !important; /* Temporary - remove after testing */
}

3. CSS Custom Property Debugging

// Override custom properties to test
.mat-mdc-slide-toggle {
--mdc-switch-unselected-track-color: lime !important; /* Temporary test color */
}

4. State Debugging

// Add borders to understand component structure
.mdc-switch__track {
border: 2px solid red !important; /* Temporary debugging */
}

.mdc-switch__handle {
border: 2px solid blue !important; /* Temporary debugging */
}

Best Practices and Pitfalls

Best Practices

  1. Use CSS Custom Properties When Available

    // Preferred
    .mat-mdc-button {
    --mdc-filled-button-container-color: #714ac6;
    }

    // Less preferred
    .mat-mdc-button {
    background-color: #714ac6 !important;
    }
  2. Target Specific States

    // Good - specific state targeting
    .mat-mdc-slide-toggle:not(.mat-checked) .mdc-switch__track {
    background-color: #9e9e9e;
    }

    // Bad - affects all states
    .mdc-switch__track {
    background-color: #9e9e9e;
    }
  3. Use Appropriate Specificity

    // Good - specific enough without being excessive
    .mat-mdc-option.mdc-list-item--selected {
    color: #714ac6;
    }

    // Excessive specificity
    html body div.container .mat-mdc-option.mdc-list-item--selected {
    color: #714ac6;
    }

Common Pitfalls

  1. Styling Wrong Element

    // Wrong - styling the wrapper instead of the actual element
    .mat-mdc-slide-toggle {
    background-color: red; /* Won't work */
    }

    // Correct - styling the internal element
    .mat-mdc-slide-toggle .mdc-switch__track {
    background-color: red;
    }
  2. Insufficient Specificity

    // May not work due to low specificity
    .mdc-switch__track {
    background-color: #9e9e9e;
    }

    // Better specificity
    .mat-mdc-slide-toggle .mdc-switch__track {
    background-color: #9e9e9e !important;
    }
  3. Ignoring Component States

    // Wrong - affects both checked and unchecked states
    .mdc-switch__track {
    background-color: gray;
    }

    // Correct - only affects unchecked state
    .mat-mdc-slide-toggle:not(.mat-checked) .mdc-switch__track {
    background-color: gray;
    }
  4. Overusing !important

    // Overuse of !important
    .mat-mdc-button {
    color: red !important;
    background: blue !important;
    border: none !important;
    padding: 10px !important;
    }

    // Better approach
    .my-component .mat-mdc-button {
    color: red;
    background: blue;
    border: none;
    padding: 10px;
    }

Performance Considerations

  1. Avoid Complex Selectors

    // Avoid deeply nested selectors
    .app .container .content .mat-mdc-button .mdc-button__label {
    color: red;
    }

    // Prefer shorter, more specific selectors
    .my-component .mat-mdc-button {
    color: red;
    }
  2. Use Class-Based Targeting

    // Efficient
    .mat-mdc-slide-toggle.custom-toggle {
    --mdc-switch-selected-track-color: #714ac6;
    }

    // Less efficient
    div[class*="mat-mdc-slide-toggle"] {
    --mdc-switch-selected-track-color: #714ac6;
    }

This guide provides a comprehensive foundation for styling Material Design Components effectively. Remember to always test changes across different browsers and component states to ensure consistent behavior.