Instruction file imported from ericaxelrod-1/frontend-templates (
.cursor/rules/101-angular-design-patterns.mdc). Copyright stays with the author.
Angular Design Patterns and Implementation Guide
CRITICAL ANGULAR COMPONENT RULES
⚠️ STOP: You MUST understand these fundamental Angular principles before making any component changes:
1. Template Priority and Conflicts
⚠️ CRITICAL: Angular components can have EITHER inline templates OR external templates, NEVER both.
- If a component has
template: '...'(inline), Angular IGNORES anytemplateUrl: './component.html'file - Always check which template is actually being used before making edits
- To switch from inline to external: Remove
templateproperty, ensuretemplateUrlexists
Investigation Steps:
- Check the component's
@Componentdecorator - Look for
template:(inline) vstemplateUrl:(external) - If both exist, only the inline template is used
2. Event-Driven Architecture Pattern
ALWAYS verify the complete event flow:
- ✓ Child component has @Output() decorator
- ✓ Child component emits event
- ✓ Parent template has (eventName)="handler()"
- ✓ Parent component has handler() method
- ✓ No typos in event names
Child Component (Emitter):
@Output() eventName = new EventEmitter<void>();
onAction(): void {
this.eventName.emit();
}
Parent Component (Listener):
<child-component (eventName)="handleEvent()"></child-component>
ANGULAR MATERIAL BEST PRACTICES - ALWAYS DO
⚠️ STOP: You MUST follow these patterns when implementing Angular Material components:
1. Menu vs Sidebar Decision Tree
User Menu Requirements:
├─ Simple (≤5 items) → Use mat-menu
├─ Complex (>5 items) → Use mat-sidenav (right-side)
├─ Mobile-first → Use mat-sidenav or MatBottomSheet
└─ Has submenus → Use mat-sidenav
2. Sidebar Implementation Pattern
Follow the working left sidebar pattern:
Container Structure:
<mat-sidenav-container>
<mat-sidenav [mode]="isMobile ? 'over' : 'side'">
<!-- Sidebar content -->
</mat-sidenav>
<mat-sidenav-content>
<!-- Main content -->
</mat-sidenav-content>
</mat-sidenav-container>
Responsive Behavior:
- Use
BreakpointObserverfor mobile detection - Mobile:
mode="over", starts closed - Desktop:
mode="side", starts open
State Management:
- Track
isCollapsedfor desktop - Track
isOpenfor mobile - Persist state in localStorage
3. Component Communication Pattern
Header → Layout → Sidebar Flow:
-
Header Component:
- Has user button
- Emits userMenuToggle event
- No direct menu implementation
-
Layout Component:
- Listens for userMenuToggle
- Manages sidebar state
- Calls openUserSidebar()
-
User Sidebar Component:
- Receives isOpen state
- Displays user menu items
- Emits closeSidebar event
PROHIBITED PRACTICES - ALWAYS AVOID
⚠️ WARNING: NEVER use these anti-patterns in Angular components:
1. NEVER mix inline and external templates
/* PROHIBITED */
@Component({
selector: 'app-header',
template: `<div>...</div>`, // This will be used
templateUrl: './header.component.html' // This will be IGNORED!
})
2. NEVER emit events without listeners
/* PROHIBITED */
// Child emits event
@Output() userMenuToggle = new EventEmitter();
// But parent doesn't listen
<app-header></app-header> <!-- Missing (userMenuToggle)="handler()" -->
3. NEVER use mat-menu for complex user menus
/* PROHIBITED for user menus with >5 items */
<button [matMenuTriggerFor]="userMenu">
User Menu
</button>
<mat-menu #userMenu="matMenu">
<!-- Too many items, use mat-sidenav instead -->
</mat-menu>
4. NEVER ignore CDK overlay issues
- Known issue: CDK overlay blocks clicks (GitHub #9320)
- Workarounds rarely work reliably
- Use alternative UI patterns (sidebars, bottom sheets)
5. NEVER skip event flow verification
- Always trace events from emission to handling
- Add console.log statements to verify flow
- Check browser console for errors
DEBUGGING ANGULAR ISSUES
Investigation Priority
-
Check Template Rendering:
- Which template is actually being used?
- Is it inline or external?
- View browser DevTools Elements tab
-
Trace Event Flow:
- Add console.log in emit method
- Add console.log in handler method
- Check browser console for output
-
Verify Component Structure:
- Are all components imported?
- Are selectors correct?
- Is component declared/standalone?
Common Angular Pitfalls and Solutions
-
Template Override: Component has both inline and external templates
- Solution: Remove one, keep the other
-
Missing Event Listener: Event emitted but not listened to
- Solution: Add (eventName)="handler()" to parent template
-
CDK Overlay Issues: Mat-menu clicks blocked
- Solution: Use mat-sidenav instead for complex menus
-
Change Detection: Async data not updating view
- Solution: Use ChangeDetectorRef.detectChanges() or async pipe
IMPLEMENTATION CHECKLIST
Before Implementing User Menus
- Count menu items and complexity
- Choose appropriate pattern (menu vs sidebar)
- Review working left sidebar implementation
- Plan complete event flow
Before Fixing "Not Clickable" Issues
- Verify element exists in DOM
- Check event listeners are attached
- Verify z-index and pointer-events
- Consider CDK overlay conflicts
Before Declaring Component Communication "Fixed"
- Event names match exactly
- All components properly imported
- Event flow logged and verified
- Template syntax is correct
TESTING REQUIREMENTS
Before Submitting Changes
- Test on desktop (wide screen)
- Test on mobile (narrow screen)
- Test keyboard navigation
- Test screen reader compatibility
- Check browser console for errors
- Verify all user flows work
KEY LESSON FROM BUG-054
The simplest solution that follows Angular patterns is usually the correct one. In BUG-054, hours were spent on complex overlay fixes when the actual issue was a missing event listener in the parent component template.
Always trace the complete event flow before assuming complex problems.
ANGULAR MATERIAL TABLE CHIPS AND INDICATORS - CRITICAL PATTERNS
⚠️ STOP: You MUST understand these patterns before implementing status indicators in tables:
1. The Mat-Chip Styling Problem (Angular Material 18+)
⚠️ CRITICAL: Angular Material 18+ uses MDC (Material Design Components) architecture that makes custom chip styling extremely difficult and unreliable.
Root Cause: Angular Material 18+ chips have nested internal structure:
<mat-chip class="mat-mdc-chip mat-mdc-standard-chip custom-class">
<span class="mdc-evolution-chip__action"> <!-- Controls background -->
<span class="mdc-evolution-chip__text-label">Custom Text</span> <!-- Controls text -->
</span>
</mat-chip>
The Problem:
- Outer container styles are overridden by inner MDC elements
- Inner elements have
background: noneand CSS custom properties - CSS specificity battles with Angular Material's internal styling
!importantdeclarations often don't work reliably- Different Angular Material versions have different MDC structures
2. RECOMMENDED APPROACH: Separate Indicator Elements
✅ ALWAYS use this pattern for status/severity indicators in tables:
<!-- RECOMMENDED: Separate indicator column or inline indicators -->
<ng-container matColumnDef="severity">
<mat-header-cell *matHeaderCellDef>Severity</mat-header-cell>
<mat-cell *matCellDef="let item">
<div class="severity-cell">
<div class="severity-indicator" [class]="getSeverityColor(item.severity)"></div>
<span class="severity-text">{{ item.severity | uppercase }}</span>
</div>
</mat-cell>
</ng-container>
SCSS Implementation:
.severity-cell {
display: flex;
align-items: center;
gap: 8px;
}
.severity-indicator {
width: 12px;
height: 12px;
border-radius: 50%;
flex-shrink: 0;
&.critical { background-color: #b71c1c; }
&.high { background-color: #c62828; }
&.medium { background-color: #e65100; }
&.low { background-color: #1b5e20; }
}
.severity-text {
font-size: 0.75rem;
font-weight: 600;
}
3. PROHIBITED PATTERNS - NEVER DO THESE
❌ NEVER attempt to style mat-chips with custom colors in Angular Material 18+:
<!-- PROHIBITED: Unreliable and causes endless debugging -->
<mat-chip [ngClass]="getSeverityClass(item.severity)">
{{ item.severity }}
</mat-chip>
/* PROHIBITED: These selectors rarely work reliably */
.mat-mdc-chip.severity-critical {
background-color: red !important; /* Often doesn't work */
}
.mat-mdc-chip.mat-mdc-standard-chip.severity-critical .mdc-evolution-chip__action {
background-color: red !important; /* Complex and unreliable */
}
❌ NEVER use [class] binding with mat-chips:
<!-- PROHIBITED: Removes Angular Material's required classes -->
<mat-chip [class]="getSeverityClass(item.severity)">
{{ item.severity }}
</mat-chip>
❌ NEVER create separate columns for simple indicators:
<!-- AVOID: Wastes table space -->
<ng-container matColumnDef="indicator">
<mat-header-cell *matHeaderCellDef>Color</mat-header-cell>
<mat-cell *matCellDef="let item">
<div class="indicator" [class]="getColor(item)"></div>
</mat-cell>
</ng-container>
<ng-container matColumnDef="text">
<mat-header-cell *matHeaderCellDef>Status</mat-header-cell>
<mat-cell *matCellDef="let item">{{ item.status }}</mat-cell>
</ng-container>
4. BEST PRACTICES FOR TABLE INDICATORS
✅ ALWAYS follow these patterns:
- Use Simple HTML Elements: Basic
<div>,<span>with CSS classes - Combine Indicator and Text: Single column with flexbox layout
- Small Indicators: 12px circles work best inline
- Consistent Spacing: 8px gap between indicator and text
- Semantic Colors: Follow Material Design color tokens
- Accessibility: Include tooltips and proper contrast ratios
Method Implementation:
getSeverityColor(severity: string): string {
const colorMap: { [key: string]: string } = {
'critical': 'critical',
'high': 'high',
'medium': 'medium',
'low': 'low'
};
return colorMap[severity?.toLowerCase()] || 'default';
}
5. WHEN TO USE MAT-CHIPS
✅ USE mat-chips for:
- Simple text labels without custom colors
- Removable tags (with mat-chip-remove)
- Selection lists (mat-chip-listbox)
- Filter chips (mat-chip-set)
Example - Appropriate mat-chip usage:
<!-- GOOD: Simple text chip without custom styling -->
<mat-chip>{{ item.category }}</mat-chip>
<!-- GOOD: Interactive chip with removal -->
<mat-chip (removed)="removeTag(tag)">
{{ tag }}
<mat-icon matChipRemove>cancel</mat-icon>
</mat-chip>
6. DEBUGGING CHIP STYLING ISSUES
Investigation Steps:
- Check Angular Material Version: MDC migration affects 15+
- Inspect DOM Structure: Look for nested
.mdc-evolution-chipelements - Verify CSS Specificity: Calculate selector specificity scores
- Test Without ViewEncapsulation: Temporarily disable to isolate issues
- Consider Alternative Approaches: Often simpler solutions work better
Common Issues:
- CSS not applying → Check specificity and ViewEncapsulation
- Colors not visible → Inner elements override outer styles
- Inconsistent behavior → Angular Material version differences
- Performance issues → Complex CSS selectors slow rendering
7. REAL-WORLD EXAMPLE: FEAT-123 Journey
The Problem: Attempted to add severity color coding to mat-chips 6 Failed Attempts:
- Basic chip styling → Overridden by Angular Material
- Compound selectors → Insufficient specificity
- Maximum specificity → Still overridden
- Inner MDC element targeting → No visual result
!importantdeclarations → Inconsistent behavior- Class binding fixes → Template issues
The Solution: Abandoned mat-chip approach, used separate indicator elements Result: Reliable, maintainable, and visually appealing severity indicators
Key Lesson: Sometimes the "simple" approach (basic HTML + CSS) is more reliable than fighting with complex component styling.
IMPLEMENTATION CHECKLIST
Before Implementing Table Indicators
- Determine if mat-chips are actually needed
- Consider using simple HTML elements instead
- Plan combined indicator + text layout
- Design consistent color scheme
- Test accessibility and contrast ratios
Before Styling Mat-Chips
- Verify Angular Material version compatibility
- Check if simple text chips are sufficient
- Consider alternative UI patterns
- Plan fallback approach if styling fails
- Budget time for potential debugging
After Implementation
- Test across different screen sizes
- Verify accessibility compliance
- Check performance with large datasets
- Document any custom patterns used
- Plan maintenance for Angular Material updates
KEY LESSONS FROM PRODUCTION
The Angular Material 18+ Reality
- Expectation: Custom chip styling should be straightforward
- Reality: MDC architecture makes it complex and unreliable
- Solution: Use simpler, more reliable approaches
The Simplicity Principle
- Complex: Fighting Angular Material's internal styling
- Simple: Using basic HTML elements with direct CSS
- Winner: Simple approach - more reliable, maintainable, and performant
The User Experience Focus
- Goal: Clear visual indicators for users
- Method: Whatever works reliably
- Result: Users don't care about implementation complexity
Remember: The best Angular pattern is the one that works reliably, is maintainable, and provides good user experience. Don't fight the framework - work with it or around it.
RESPONSIVE DESIGN AND FLUID LAYOUT PATTERNS (2024/2025 BEST PRACTICES)
⚠️ STOP: You MUST understand these modern responsive design principles before implementing layouts:
1. CSS Syntax Errors Can Break Entire Responsive Systems
⚠️ CRITICAL: A single CSS syntax error can prevent ALL subsequent CSS from loading, making responsive systems appear broken.
Root Cause Example from BUG-124.7.1:
/* BROKEN - Missing closing bracket */
.stats-grid {
display: grid;
gap: clamp(8px, 2vw, 16px);
// Missing closing bracket here!
.next-rule { /* This and everything after won't load */ }
Debugging Methodology:
- ALWAYS check CSS syntax first before investigating complex responsive issues
- Browser DevTools: Look for red error indicators in CSS
- Build vs Runtime: SCSS compiles successfully but browser CSS parser fails
- Symptoms: Classes exist in TypeScript but not in parsed CSS
2. Modern Fluid Design Principles (Replacing Traditional Responsive)
✅ CORRECT - Fluid Design Approach (2024/2025 Standard):
// Remove ALL fixed max-width constraints
.content-wrapper {
width: 100%; // Full width on all screens
// NO max-width constraint
}
// Only constrain text content for readability
.text-content {
max-width: 65ch; // Optimal reading width
margin: 0 auto;
}
// Data tables and dashboards use full width
.data-dashboard,
.admin-panel {
width: 100%; // Use all available space
}
❌ PROHIBITED - Traditional Fixed Constraints:
/* OLD APPROACH - Don't use */
.content-wrapper {
max-width: 1200px; // Fixed constraint
}
.admin-container {
max-width: 1400px !important; // Prevents full-screen
}
3. Fluid Typography and Spacing System
✅ IMPLEMENT - CSS Clamp() for Fluid Scaling:
// Fluid typography
:root {
--fluid-text-sm: clamp(0.75rem, 1.5vw, 0.875rem);
--fluid-text-base: clamp(0.875rem, 2vw, 1rem);
--fluid-text-lg: clamp(1rem, 2.5vw, 1.25rem);
--fluid-text-xl: clamp(1.25rem, 3vw, 1.5rem);
--fluid-text-2xl: clamp(1.5rem, 4vw, 2rem);
}
// Fluid spacing
.container {
padding: clamp(8px, 2vw, 24px);
gap: clamp(8px, 2vw, 16px);
margin-bottom: clamp(12px, 3vw, 20px);
}
4. Responsive Grid Patterns That Actually Work
✅ CORRECT - Auto-Fit Grid Pattern:
.pattern-tiles-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: clamp(8px, 2vw, 16px);
// Progressive enhancement
@include respond-to(md) {
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
}
@include respond-to(lg) {
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
}
}
❌ PROHIBITED - Fixed Column Counts:
/* DON'T DO THIS - Breaks responsive behavior */
.mobile-spacing {
.pattern-tiles-grid {
grid-template-columns: 1fr !important; // Overrides responsive grid
}
}
5. Angular Responsive Layout Service Pattern
✅ BEST PRACTICE - Centralized Responsive Service:
// responsive-layout.service.ts
@Injectable({ providedIn: 'root' })
export class ResponsiveLayoutService {
private breakpoints = {
mobile: "(max-width: 575.98px)",
tablet: "(min-width: 576px) and (max-width: 1023.98px)",
desktop: "(min-width: 1024px) and (max-width: 1279.98px)",
largeDesktop: "(min-width: 1280px)"
};
getResponsiveClass(): Observable<string> {
return this.breakpointObserver.observe(Object.values(this.breakpoints))
.pipe(map(() => this.getCurrentBreakpoint()));
}
getGridColumns(type: 'stats' | 'pattern'): Observable<string> {
return this.getResponsiveClass().pipe(
map(breakpoint => this.gridConfigs[type][breakpoint])
);
}
}
Component Usage:
// Use observables for reactive responsive behavior
responsiveClass$ = this.responsiveService.getResponsiveClass();
gridColumns$ = this.responsiveService.getGridColumns('pattern');
<!-- Template bindings -->
<div [ngClass]="responsiveClass$ | async">
<div class="grid" [style.grid-template-columns]="gridColumns$ | async">
<!-- Content -->
</div>
</div>
6. Container Queries for Component-Level Responsiveness
✅ MODERN APPROACH - Container Queries:
.component-container {
container-type: inline-size;
.content {
// Respond to component size, not viewport
@container (max-width: 320px) {
grid-template-columns: 1fr;
}
@container (min-width: 480px) {
grid-template-columns: repeat(2, 1fr);
}
@container (min-width: 768px) {
grid-template-columns: repeat(3, 1fr);
}
}
}
7. Common Responsive Design Pitfalls and Solutions
Problem 1: CSS Not Loading
- Symptom: Responsive classes don't work
- Cause: CSS syntax error earlier in file
- Solution: Validate CSS syntax first
Problem 2: Fixed Constraints on Large Screens
- Symptom: Content doesn't use full width on desktop
- Cause: max-width constraints in layouts
- Solution: Remove constraints, use fluid design
Problem 3: Responsive Classes Override Grid
- Symptom: Grid always shows single column
- Cause: Dynamic classes with !important overrides
- Solution: Remove grid-template-columns from responsive classes
Problem 4: Inline Styles Override CSS
- Symptom: CSS classes have no effect
- Cause: [style.property] bindings in template
- Solution: Use [ngClass] instead of inline styles
RESPONSIVE DESIGN DEBUGGING CHECKLIST
Before Debugging Responsive Issues
- Validate CSS syntax (check for missing brackets)
- Check browser console for CSS parsing errors
- Verify responsive classes exist in parsed CSS
- Confirm no inline styles override CSS classes
- Test without dynamic responsive classes first
CSS Syntax Validation
- All blocks have closing brackets
- No orphaned rules at end of file
- Media queries properly closed
- No stray characters between rules
Responsive Implementation
- Remove ALL max-width constraints from layouts
- Use clamp() for fluid typography and spacing
- Implement auto-fit grid patterns
- Add container queries for components
- Create responsive service for Angular
Testing Requirements
- Test on mobile (320px - 575px)
- Test on tablet (576px - 1023px)
- Test on desktop (1024px - 1279px)
- Test on large desktop (1280px+)
- Test on ultra-wide monitors (2560px+)
KEY LESSONS FROM BUG-124.7.1
The CSS Syntax Error That Broke Everything
- Problem: Missing closing bracket in .stats-grid
- Impact: Entire responsive system appeared broken
- Solution: Fixed syntax error, all responsive features worked
- Lesson: Always validate CSS syntax before complex debugging
The Evolution to Fluid Design
- Old Way: Fixed breakpoints and max-width constraints
- New Way: Fluid design with clamp() and full-width layouts
- Result: Better user experience on all screen sizes
- Future: Container queries and intrinsic design
The Angular Service Pattern Success
- Problem: Responsive logic scattered across components
- Solution: Centralized ResponsiveLayoutService
- Benefits: Type safety, reusability, testability
- Pattern: Observable-based reactive responsiveness
Remember: Modern responsive design is about fluid adaptation, not fixed breakpoints. Remove constraints, embrace fluidity, and always check CSS syntax first when debugging.
Remember: The best Angular pattern is the one that works reliably, is maintainable, and provides good user experience. Don't fight the framework - work with it or around it.