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
- Maximum 4 levels deep to maintain readability
- Use BEM methodology for complex structures if needed
// ✅ 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:
- 🚫 Officially deprecated by Angular
- 💥 Breaks style encapsulation
- 🐛 Creates debugging nightmares
- 🔮 Will be removed in future Angular versions
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:
- ✅ Identify the styling goal
- ✅ Create CSS custom properties in parent component
- ✅ Update child component to consume variables
- ✅ Test thoroughly
- ✅ 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:
- ✅ Proper nesting (children inside parents)
- ✅ Max 4 levels of nesting depth
- ✅ CSS custom properties for styling
- ✅ No ::ng-deep usage
- ✅ Semantic variable names
- ✅ Separate .scss files (no inline styles)
- ✅ Logical grouping of related styles
- ✅ Responsive design considerations
Performance Checklist:
- ✅ CSS variables instead of @Input() for styling
- ✅ Minimal custom properties (avoid over-specification)
- ✅ Efficient selectors (avoid deep nesting when possible)
- ✅ Cached stylesheets (external .scss files)
📝 Enforcement
These rules are MANDATORY for all components in our codebase:
- Code Reviews will reject PRs with ::ng-deep usage
- Linting Rules should catch violations where possible
- Documentation must be updated when adding new patterns
- 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:
- Component:
widget-builder→ Classes:.widget-builder-header,.widget-builder-content - Component:
modular-universe-builder→ Classes:.modular-universe-builder-header,.modular-universe-builder-sidebar - Component:
ai-module-builder→ Classes:.ai-module-builder-header,.ai-module-builder-conversation
Exceptions (DO NOT RENAME)
These are intentionally reusable and should NOT be renamed:
Utility Classes (in global styles):
.container { /* Global utility */ } .text-center { /* Global utility */ }Theme System Classes (from
@theme-system):.ux-card { /* Theme system */ } .ux-btn { /* Theme system */ }Reusable Component Classes (intentionally shared):
.module-layout { /* Reusable layout component */ } .layout-header { /* Part of reusable component */ }Dynamically Generated Classes (runtime classes):
[class*="ng-"] { /* Angular generated */ } [class*="cdk-"] { /* CDK generated */ }
Why This Matters
- 🔍 Better Debugging: Component-specific names make it easy to identify which component styles are applied
- 🚫 Prevents Conflicts: Avoids accidental style collisions across components
- 📊 Clear Boundaries: Makes component boundaries explicit in CSS
- 🔧 Easier Maintenance: When debugging, you immediately know which component to check
Migration Checklist
When creating or updating components:
- All classes use component-specific prefixes
- No generic class names (
.header,.footer,.content, etc.) unless intentionally reusable - Classes are scoped to the component's root class
- HTML templates use the renamed classes
🎯 Summary
Golden Rules:
- 🏗️ Nest children inside parents - Always use proper SCSS hierarchy
- 🚫 Never use ::ng-deep - It's deprecated and breaks encapsulation
- ⚡ CSS variables for styling - Better performance than @Input()
- 📁 Separate files - Keep .ts, .html, .scss distinct
- 🎨 Semantic naming - Use descriptive variable names
- 🏷️ 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