Scss Rules

Documentation for Developers

SCSS Development Rules & Best Practices

🏗️ Core Rule: Proper Nesting Structure

All SCSS code MUST use inheritance and nesting, placing children inside their parents:

// ✅ REQUIRED: Proper nesting structure
.class-father {
  // Father properties
  padding: 20px;
  background: var(--surface);

  .class-child {
    // Child properties
    margin-bottom: 16px;
    
    .class-grand-child {
      // Grand-child properties
      font-weight: 600;
    }
  }
}

📋 SCSS Nesting Rules

Rule 1: Maximum Nesting Depth

// ✅ GOOD: 3-4 levels maximum
.component {
  .header {
    .title {
      .icon {
        // Max recommended depth
      }
    }
  }
}

// ❌ BAD: Too deep nesting
.component {
  .wrapper {
    .container {
      .content {
        .section {
          .item {
            // Too deep - refactor needed
          }
        }
      }
    }
  }
}

Rule 2: Parent Selector Usage

Use & for modifiers and pseudo-selectors:

// ✅ CORRECT: Using & for modifiers
.button {
  padding: 12px 24px;
  background: var(--primary);
  
  &:hover {
    background: var(--primary-hover);
  }
  
  &:disabled {
    opacity: 0.6;
    cursor: not-allowed;
  }
  
  &--large {
    padding: 16px 32px;
    font-size: 1.125rem;
  }
  
  &--secondary {
    background: var(--secondary);
    
    &:hover {
      background: var(--secondary-hover);
    }
  }
}

Rule 3: Logical Grouping

Group related selectors and properties logically:

// ✅ CORRECT: Logical grouping
.form-field {
  margin-bottom: 1rem;
  position: relative;

  // Labels first
  label {
    display: block;
    margin-bottom: 0.5rem;
    font-weight: 500;
    color: var(--text-secondary);
    
    &.required::after {
      content: '*';
      color: var(--danger);
      margin-left: 0.25rem;
    }
  }

  // Form controls
  .form-control {
    width: 100%;
    padding: 0.75rem;
    border: 1px solid var(--border);
    border-radius: 0.375rem;
    
    &:focus {
      outline: none;
      border-color: var(--primary);
      box-shadow: 0 0 0 3px var(--primary-opacity-20);
    }
    
    &:invalid {
      border-color: var(--danger);
    }
    
    &:disabled {
      background: var(--surface-disabled);
      cursor: not-allowed;
    }
  }

  // Error messages last
  .error-message {
    margin-top: 0.25rem;
    font-size: 0.875rem;
    color: var(--danger);
  }
}

🚫 Forbidden Practices

NEVER Use ::ng-deep

// ❌ FORBIDDEN: ::ng-deep is DEPRECATED
:host {
  ::ng-deep .child-component {
    color: red;  // BREAKS ENCAPSULATION
  }
}

Why it's forbidden:

NEVER Mix Inline Styles

// ❌ FORBIDDEN: Inline styles with external stylesheets
@Component({
  styleUrl: './component.scss',
  styles: [`...`]  // DON'T DO THIS
})

NEVER Use ViewEncapsulation.None

// ❌ FORBIDDEN: Breaks component isolation
@Component({
  encapsulation: ViewEncapsulation.None
})

✅ Required Practices

Rule 4: CSS Custom Properties for Styling

Use CSS variables for all theming and cross-component styling:

// ✅ REQUIRED: CSS Custom Properties approach
:host {
  // Define component-specific variables
  --component-bg: var(--theme-surface, #ffffff);
  --component-text: var(--theme-text, #000000);
  --component-border: 1px solid var(--theme-border, #e5e7eb);
  --component-radius: var(--theme-radius, 0.375rem);
}

.component {
  background: var(--component-bg);
  color: var(--component-text);
  border: var(--component-border);
  border-radius: var(--component-radius);
  
  &__header {
    border-bottom: var(--component-border);
    
    &-title {
      color: var(--component-text);
      font-weight: 600;
    }
  }
}

Rule 5: File Separation

ALWAYS keep files separate:

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

Rule 6: Component Structure

Follow this exact SCSS file structure:

// 1. Host styles (REQUIRED)
:host {
  display: block;  // or contents, flex, etc.
  
  // CSS custom properties
  --component-bg: var(--theme-surface);
  --component-text: var(--theme-text);
}

// 2. Main component styles
.component {
  background: var(--component-bg);
  color: var(--component-text);
  
  // Nested elements using proper hierarchy
  &__header {
    border-bottom: 1px solid var(--border);
    
    &-title {
      font-weight: 600;
      margin: 0;
    }
    
    &-actions {
      display: flex;
      gap: 0.5rem;
    }
  }
  
  &__body {
    padding: 1rem;
  }
  
  &__footer {
    border-top: 1px solid var(--border);
    padding: 1rem;
  }
}

// 3. State modifiers
.component {
  &--loading {
    opacity: 0.6;
    pointer-events: none;
  }
  
  &--error {
    border-color: var(--danger);
  }
  
  &--success {
    border-color: var(--success);
  }
}

// 4. Size variants
.component {
  &--small {
    padding: 0.5rem;
    font-size: 0.875rem;
  }
  
  &--large {
    padding: 1.5rem;
    font-size: 1.125rem;
  }
}

// 5. Responsive styles (LAST)
@media (max-width: 768px) {
  .component {
    &__header {
      flex-direction: column;
      gap: 0.5rem;
    }
  }
}

🎯 Performance Rules

Rule 7: CSS Variables for Performance

ALWAYS use CSS variables instead of @Input() for styling:

// ✅ FAST: CSS Variables (~0.1ms)
:host {
  --button-bg: var(--theme-primary);
  --button-text: var(--theme-on-primary);
}

.button {
  background: var(--button-bg);
  color: var(--button-text);
}
// ❌ SLOW: @Input() for styling (~2-5ms + change detection)
@Input() backgroundColor = '#007bff';  // DON'T DO THIS FOR STYLING

Rule 8: Semantic Variable Names

Use descriptive, semantic variable names:

// ✅ GOOD: Semantic names
:host {
  --panel-surface: var(--theme-surface);
  --panel-text-primary: var(--theme-text);
  --panel-text-secondary: var(--theme-text-muted);
  --panel-border: var(--theme-border);
  --panel-shadow: var(--theme-shadow-md);
}

// ❌ BAD: Generic/unclear names
:host {
  --color1: #ffffff;
  --color2: #000000;
  --size1: 16px;
  --padding: 12px;
}

📊 Performance Benchmarks

Approach Processing Time Change Detection Bundle Impact Future-Proof
CSS Variables ✅ ~0.1ms ✅ None ✅ Zero ✅ Yes
@Input() Styling ❌ ~2-5ms ❌ Every change ❌ Increases ❌ No
::ng-deep ⚠️ ~1-3ms ✅ None ⚠️ Minimal ❌ Deprecated
Global Styles ✅ ~0.1ms ✅ None ⚠️ Can grow ⚠️ Conflicts

🔄 Migration Requirements

When You Find ::ng-deep:

  1. ✅ Identify the styling goal
  2. ✅ Create CSS custom properties in parent component
  3. ✅ Update child component to consume variables
  4. ✅ Test thoroughly
  5. ✅ Remove ::ng-deep

Migration Example:

// ❌ BEFORE: Using ::ng-deep
:host {
  ::ng-deep .panel-header {
    background: #f0f0f0;
    color: #333;
  }
}
// ✅ AFTER: Using CSS Custom Properties
:host {
  --panel-header-bg: #f0f0f0;
  --panel-header-text: #333;
}

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

🏆 Excellence Standards

Code Quality Checklist:

Performance Checklist:

📝 Enforcement

These rules are MANDATORY for all components in our codebase:

  1. Code Reviews will reject PRs with ::ng-deep usage
  2. Linting Rules should catch violations where possible
  3. Documentation must be updated when adding new patterns
  4. Migration is required when touching legacy code

🏷️ Class Naming Convention (CRITICAL)

Rule 9: Component-Specific Class Names

ALWAYS use component-specific prefixes for all CSS classes to avoid conflicts and improve debugging:

// ✅ CORRECT: Component-specific naming
.my-component {
  &__header { /* ... */ }
  &__footer { /* ... */ }
  &__body { /* ... */ }
  &__content { /* ... */ }
}

// ❌ WRONG: Generic class names (causes conflicts across components)
.header { /* ... */ }
.footer { /* ... */ }
.body { /* ... */ }
.content { /* ... */ }

Naming Pattern

Format: {component-name}-{element-name}

Examples:

Exceptions (DO NOT RENAME)

These are intentionally reusable and should NOT be renamed:

  1. Utility Classes (in global styles):

    .container { /* Global utility */ }
    .text-center { /* Global utility */ }
    
  2. Theme System Classes (from @theme-system):

    .ux-card { /* Theme system */ }
    .ux-btn { /* Theme system */ }
    
  3. Reusable Component Classes (intentionally shared):

    .module-layout { /* Reusable layout component */ }
    .layout-header { /* Part of reusable component */ }
    
  4. Dynamically Generated Classes (runtime classes):

    [class*="ng-"] { /* Angular generated */ }
    [class*="cdk-"] { /* CDK generated */ }
    

Why This Matters

  1. 🔍 Better Debugging: Component-specific names make it easy to identify which component styles are applied
  2. 🚫 Prevents Conflicts: Avoids accidental style collisions across components
  3. 📊 Clear Boundaries: Makes component boundaries explicit in CSS
  4. 🔧 Easier Maintenance: When debugging, you immediately know which component to check

Migration Checklist

When creating or updating components:

🎯 Summary

Golden Rules:

  1. 🏗️ Nest children inside parents - Always use proper SCSS hierarchy
  2. 🚫 Never use ::ng-deep - It's deprecated and breaks encapsulation
  3. ⚡ CSS variables for styling - Better performance than @Input()
  4. 📁 Separate files - Keep .ts, .html, .scss distinct
  5. 🎨 Semantic naming - Use descriptive variable names
  6. 🏷️ Component-specific classes - Always prefix classes with component name

Following these rules ensures maintainable, performant, and future-proof styling in our Angular applications.


Version: 1.0
Last Updated: January 2024
Applies To: Angular 20+ with SCSS preprocessor