Css Basics

Documentation for Developers

CSS Development Best Practices

Table of Contents

Angular CSS Best Practices

✅ Recommended Approaches

1. CSS Custom Properties (CSS Variables)

The modern, performant way to style child components:

// ✅ BEST PRACTICE: CSS Custom Properties
:host {
  // Define custom properties for child components
  --panel-bg: var(--theme-surface, #ffffff);
  --panel-text: var(--theme-text, #000000);
  --panel-border: 1px solid var(--theme-border, #e5e7eb);
}

// Child component automatically inherits these values
.child-component {
  background: var(--panel-bg);
  color: var(--panel-text);
  border: var(--panel-border);
}

Benefits:

2. External Stylesheets Only

Always use separate .scss files:

// ✅ CORRECT: External stylesheet
@Component({
  selector: 'app-component',
  templateUrl: './component.html',
  styleUrl: './component.scss'  // External file only
})
// ❌ AVOID: Inline styles
@Component({
  selector: 'app-component',
  template: `...`,
  styles: [`...`]  // Avoid inline styles
})

❌ Anti-Patterns to Avoid

1. ::ng-deep (DEPRECATED)

// ❌ DEPRECATED: ::ng-deep breaks encapsulation
:host {
  ::ng-deep .child-component {
    color: red;  // Breaks style isolation
  }
}

Problems with ::ng-deep:

Modern Alternative:

// ✅ MODERN: CSS Custom Properties
:host {
  --child-text-color: red;
}

// Child component consumes the variable
.child-component {
  color: var(--child-text-color, #000);
}

2. ViewEncapsulation.None

// ❌ AVOID: Breaks style isolation
@Component({
  encapsulation: ViewEncapsulation.None
})

Why it's problematic:

Performance Optimization

CSS Variables vs @Input() Properties

Approach Performance Change Detection Bundle Size SSR Support
CSS Variables ✅ ~0.1ms ✅ None ✅ No impact ✅ Immediate
@Input() Properties ❌ ~2-5ms ❌ Every change ❌ Increases JS ❌ Needs hydration

When to Use Each:

// ✅ CSS Variables for: STYLING
:host {
  --button-bg: #007bff;
  --button-text: #ffffff;
  --button-padding: 12px 24px;
  --button-radius: 6px;
}
// ✅ @Input() Properties for: LOGIC
@Component({...})
export class ButtonComponent {
  @Input() disabled = false;        // Logic/behavior
  @Input() type: 'button' | 'submit' = 'button';  // Functionality
  @Input() loading = false;         // State management
  @Input() size: 'sm' | 'md' | 'lg' = 'md';      // Affects CSS classes
}

Performance Tips:

  1. Minimize CSS Custom Property Changes:
// ✅ GOOD: Few, semantic variables
:host {
  --theme-primary: #007bff;
  --theme-surface: #ffffff;
}

// ❌ AVOID: Too many specific variables
:host {
  --button-top-padding: 12px;
  --button-right-padding: 24px;
  --button-bottom-padding: 12px;
  --button-left-padding: 24px;
}
  1. Use CSS Custom Properties for Theming:
// ✅ Efficient theme switching
:host {
  &.theme-dark {
    --surface: #1f2937;
    --text: #f9fafb;
  }
  
  &.theme-light {
    --surface: #ffffff;
    --text: #111827;
  }
}

SCSS Structure and Nesting

Proper Nesting Hierarchy

Always nest child selectors inside their parents:

// ✅ CORRECT: Proper nesting structure
.page-details {
  padding: 20px;
  background: var(--surface);

  &__section {
    margin-bottom: 24px;
    border-radius: 8px;

    &-title {
      font-size: 16px;
      font-weight: 600;
      
      &::before {
        content: '';
        background: var(--accent);
      }
    }
  }

  &__field {
    margin-bottom: 16px;
    
    label {
      display: block;
      font-weight: 500;
      
      &.required::after {
        content: '*';
        color: var(--danger);
      }
    }

    .form-control {
      width: 100%;
      padding: 8px 12px;
      
      &:focus {
        border-color: var(--primary);
        
        & + .form-hint {
          color: var(--primary);
        }
      }
      
      &:disabled {
        opacity: 0.6;
        cursor: not-allowed;
      }
    }
  }
}

Benefits of Proper Nesting:

  1. 🔍 Clear Hierarchy: Visual structure matches DOM
  2. 📦 Better Organization: Related styles grouped together
  3. 🔧 Easier Maintenance: Find and modify styles quickly
  4. 🎯 Specificity Control: Natural CSS specificity progression

Nesting Guidelines:

// ✅ GOOD: Logical nesting (max 3-4 levels)
.component {
  .header {
    .title {
      .icon {
        // Max recommended depth
      }
    }
  }
}

// ❌ AVOID: Too deep nesting (hard to maintain)
.component {
  .wrapper {
    .container {
      .content {
        .section {
          .item {
            .text {
              // Too deep!
            }
          }
        }
      }
    }
  }
}

Component Styling Architecture

File Organization:

component/
├── component.ts      # Logic only
├── component.html    # Template only  
├── component.scss    # Styles only
└── component.spec.ts # Tests

SCSS File Structure:

// 1. Host styles
:host {
  display: block;
  --component-bg: var(--theme-surface);
}

// 2. Main component styles
.component {
  background: var(--component-bg);
  
  // 3. Nested elements
  &__header {
    padding: 1rem;
    
    &-title {
      font-weight: 600;
    }
  }
  
  &__body {
    padding: 1rem;
  }
  
  &__footer {
    padding: 1rem;
    border-top: 1px solid var(--border);
  }
}

// 4. State modifiers
.component {
  &--loading {
    opacity: 0.6;
  }
  
  &--error {
    border-color: var(--danger);
  }
}

// 5. Responsive styles
@media (max-width: 768px) {
  .component {
    &__header {
      padding: 0.75rem;
    }
  }
}

Migration Guide

From ::ng-deep to CSS Custom Properties:

// ❌ OLD: Using ::ng-deep
:host {
  ::ng-deep .child-component {
    background: #f0f0f0;
    color: #333;
  }
}
// ✅ NEW: Using CSS Custom Properties
:host {
  --child-bg: #f0f0f0;
  --child-text: #333;
}

// In child component:
.child-component {
  background: var(--child-bg, #fff);
  color: var(--child-text, #000);
}

Migration Steps:

  1. Identify ::ng-deep usage in your components
  2. Define CSS custom properties in parent component
  3. Update child component to consume variables
  4. Test thoroughly to ensure styling works
  5. Remove ::ng-deep from parent component

Common SCSS Import Issues

Path Resolution Errors

When you encounter "Can't find stylesheet to import" errors, the issue is typically incorrect relative paths:

// ❌ COMMON ERROR: Wrong path depth
@use '../../../theme-system/theme-styles/src/lib/styles/index' as *;

How to Fix:

  1. Count directory levels: From your component to the target file
  2. Test the path: Use terminal to verify: ls -la ../../path/to/file.scss
  3. Apply the correct path:
// ✅ CORRECT: Proper relative path calculation
@use '../../../../../../theme-system/theme-styles/src/lib/styles/index' as *;

Path Calculation Examples:

// From: libs/ux-test-lib/src/lib/storage/components/storage-inspector/
// To:   libs/theme-system/theme-styles/src/lib/styles/utilities/
// Path: ../../../../../../theme-system/theme-styles/src/lib/styles/utilities/

// From: libs/theme-system/theme-showcase/src/lib/components/theme-showcase/  
// To:   libs/theme-system/theme-styles/src/lib/styles/
// Path: ../../../../../theme-styles/src/lib/styles/

Debug Process:

  1. Navigate to your component directory in terminal
  2. Test the path: cd /path/to/component && ls -la ../../../../../target/file.scss
  3. Adjust ../ count until the file is found
  4. Update your @use statement with the working path

Best Practices Summary

✅ DO:

❌ DON'T:

Performance Benchmarks

Technique Processing Time Memory Usage Change Detection
CSS Variables ~0.1ms Minimal None
@Input() Styling ~2-5ms Higher Every change
::ng-deep ~1-3ms Moderate None (but deprecated)

Conclusion: CSS Custom Properties are the fastest, most maintainable approach for component styling in modern Angular applications.