AngularAtlased. 22.2

Ch. 02Modern fundamentals11 / 75

Component styles & encapsulation

:host, the three encapsulation modes, theming with CSS variables, and why ::ng-deep is a last resort.

~0 min read Official docs

Every component brings its own CSS, and by default that CSS stays inside the component: a .title rule in one component doesn't touch a .title in another. Angular does this by rewriting your selectors when it compiles the component. The mechanism is called view encapsulation.

Knowing how the rewriting works answers the questions everyone asks in their first month: why the parent's CSS doesn't reach the child, why a style suddenly shows up on another page, and how to make a component themeable without ::ng-deep.

Why it matters
In the browser, CSS is global by nature. Encapsulation is what lets you write short, simple selectors in a large app without fear — as long as you know where its edges are.

Where a component's styles live

Inline in styles, or in a file with styleUrl. Either way they belong to the component: Angular adds them to the page when the first instance appears and removes them when the last one is destroyed. Global CSS — resets, fonts, design tokens — goes in styles.scss, which is not encapsulated.

@Component({
  selector: 'app-note',
  template: `<p class="note"><ng-content /></p>`,
  // inline styles…
  styles: `
    :host { display: block; }
    .note { padding: 0.5rem 0.75rem; border-left: 3px solid var(--accent); }
  `,
  // …or a file: styleUrl: './note.css'
})
export class Note {}

Emulated: the default

Angular gives each component a unique id and marks its elements with two attributes: _nghost-… on the host element and _ngcontent-… on every element of its template. Then it adds the attribute to every part of your selectors: .note becomes .note[_ngcontent-ng-c123]. Your rules can't get out of the component — but rules from outside still get in: global CSS and components without encapsulation apply as usual.

/* what you write */
:host { display: block; }
.note { border-left: 3px solid var(--accent); }
.list .note:hover { background: var(--surface-2); }

/* what the browser gets (Emulated) */
[_nghost-ng-c123] { display: block; }
.note[_ngcontent-ng-c123] { border-left: 3px solid var(--accent); }
.list[_ngcontent-ng-c123] .note[_ngcontent-ng-c123]:hover { background: var(--surface-2); }

The four modes, live

ViewEncapsulation.None skips the rewriting: the styles become global as soon as the component appears. ShadowDom uses the browser's real Shadow DOM, so the page's CSS doesn't get in. ExperimentalIsolatedShadowDom (experimental since v21) goes one step further, as you'll see below.

import { ViewEncapsulation } from '@angular/core';

@Component({
  selector: 'app-embed-widget',
  // Emulated (default) · None · ShadowDom · ExperimentalIsolatedShadowDom
  encapsulation: ViewEncapsulation.ShadowDom,
  template: `…`,
  styles: `…`,
})
export class EmbedWidget {}
LiveThe same rule in four encapsulation modes

Elsewhere on the page

A paragraph with the class .enc-lab-note

Emulated (default)

None

Not mounted.

ShadowDom

ExperimentalIsolatedShadowDom

ModeSelectors in the browserWhere the style lives
Every component has a .enc-lab-note rule. Tick the box to mount the None component and watch which paragraphs it reaches — one of them is inside a shadow root.

The None component's rule reaches the page and the Emulated component — and, surprisingly, the ShadowDom one too. In ShadowDom mode, Angular copies the styles of the app's other components into every shadow root. Emulated selectors don't match there, but global ones do. ExperimentalIsolatedShadowDom puts only the component's own styles in its shadow root. During SSR, both shadow modes are rendered as Emulated.

Which mode, when

ModeYour styles get out?Page styles get in?Use it for
Emulatednoyesalmost everything
Noneyes, they become globalyesrarely: markup you don't control, a global style sheet shipped as a component
ShadowDomnono — but Angular copies in the other components' styleswidgets embedded in other people's pages
ExperimentalIsolatedShadowDomnonofull isolation (experimental)

:host, :host() and :host-context()

The host element — <app-card> itself — belongs to the parent's template, so a normal selector inside the component can't reach it. :host can. The host starts as display: inline, so most components need :host { display: block }. :host(.active) applies when the host has a class; :host-context(.dark) applies when any ancestor has it.

/* the host element: <app-card> itself */
:host {
  display: block; /* hosts are inline by default */
  border: 1px solid var(--border);
}

/* a variant the component offers: <app-card class="compact"> */
:host(.compact) {
  padding: 0.5rem;
}

/* when an ancestor has the class (emulated in Emulated mode) */
:host-context(.print) {
  border-color: black;
}

Prefer :host() for the variants a component offers. Angular emulates :host-context() in Emulated mode, but inside a real shadow root only Chromium supports it. For themes, CSS variables work everywhere.

Styling a child from the parent

The question everyone asks: “my CSS doesn't work on the child component!” The parent's selectors get the parent's attribute, while the elements inside the child carry the child's — so they never match. Here are the ways people try it, compiled live.

LiveRestyle the Buy button from the parent
Parent
Reached the button
no
Global rule
no

Every part of the selector got the parent's attribute. The button inside the child carries the child's attribute, so nothing matches.

You write
.attempt-plain app-buy-button button {
  background: var(--bad);
}
The browser gets
—
Try each approach and compare what you write with what the browser actually receives.

The last two survive refactors: the child publishes what can be changed, and the parent uses only that. ::ng-deep is deprecated but has no replacement, so Angular keeps it for cases where you don't own the child — styling a third-party library, for example. Even then, always start it with :host. Notice the specificity too: in Emulated mode, the child's .buy[_ngcontent-…] counts as two classes, so a rule from outside needs at least as much to win.

/* ✗ global: active on every page while the component exists */
::ng-deep .mat-mdc-button { text-transform: uppercase; }

/* ✓ if you really must: only inside this component */
:host ::ng-deep .mat-mdc-button { text-transform: uppercase; }

Themes with CSS variables

Declare variables with fallbacks in the child — var(--buy-bg, var(--surface-2)) — and set them anywhere above it: in styles.scss for the whole app, on a container for one section, or with [style.--buy-bg] for a single instance. Dark mode is then just another set of values.

@Component({
  selector: 'app-buy-button',
  template: `<button class="buy"><ng-content /></button>`,
  styles: `
    /* the public styling API: variables with fallbacks… */
    .buy {
      background: var(--buy-bg, var(--surface-2));
      color: var(--buy-fg, var(--text));
      border-radius: var(--buy-radius, 8px);
    }
    /* …and variants the child supports */
    :host(.danger) .buy {
      --buy-bg: var(--bad-soft);
      --buy-fg: var(--bad);
    }
  `,
})
export class BuyButton {}

What belongs in styles.scss

Resets, fonts, design tokens (colors, spacing and radii as variables), and styles for HTML that isn't rendered by your components — innerHTML from a CMS, for example. Everything else belongs to the component that renders it.

Dynamically bound classes and styles are covered in Class & style bindings; how Shadow DOM behaves inside a Web Component, in Web Components with Angular Elements.

Common pitfalls

  • A component that ignores width and margin. The host is display: inline by default. Add :host { display: block }.
  • ViewEncapsulation.None to “fix” a selector. The styles become global and stay that way: they hit every element with that class for as long as the component exists.
  • ::ng-deep without :host. The same problem, one rule at a time.
  • Styling the child's internal markup. It works with ::ng-deep until the child changes its template. Ask the child for a variable or a variant instead.
  • Relying on ShadowDom for isolation. Angular copies the other components' styles into the shadow root. If you really need isolation, look at ExperimentalIsolatedShadowDom.

Cheat sheet

styles / styleUrl      the component's CSS; added with the first instance, removed with the last
Emulated (default)     selectors get [_ngcontent-…]; nothing gets out, page CSS still gets in
None                   no rewriting: the styles are global while the component exists
ShadowDom              real shadow root; Angular still copies the other components' styles in
Isolated (exp. v21)    ExperimentalIsolatedShadowDom: only the component's own styles
:host                  the host element; inline by default → :host { display: block }
:host(.x)              a variant of the host: <app-card class="x">
:host-context(.x)      an ancestor has .x; avoid inside a real shadow root
::ng-deep              stops scoping; deprecated; always :host ::ng-deep, only for code you don't own
CSS variables          inherited across every boundary: the public styling API of a component
styles.scss            resets, fonts, tokens, CMS/innerHTML markup — not component styles

Ch. 02 · 0 of 8 complete

Finished this lesson?

Mark it and it shows up on your map: in the contents and on the home page.

Marks itself as done once you have read the whole lesson.

  1. Standalone & bootstrap
  2. Data binding & events
  3. Control flow: @if, @for, @switch
  4. @let and the new template expressions
  5. @defer — lazy loading in templates
  6. Class & style bindings
  7. Component styles & encapsulation
  8. Pipes: when and how
See your whole map

All 75 pages · type to filter

  • 01What's new in Angular 21 & 22All the important changes in v21 and v22, briefly, with a link to the page that explains each one.Start
  • 01Your first app, step by stepA reading list built from an empty component to a finished app — signals, @for, inputs and outputs, a service and a form, one step at a time.Start
  • 01Quick decisions“Where do I put this?”, “Signal or Observable?”, “Shared or feature?” — short answers to everyday questions.Start
  • 01Angular interview questions60 real interview questions for junior, mid and senior, with the answer to give, the follow-up and the red flags. Plus a mock interview.Start
  • 02Standalone & bootstrapHow an app starts without NgModule: bootstrapApplication, app.config.ts, imports in the component.Modern fundamentals
  • 02Data binding & eventsInterpolation, property and attribute bindings, events with $event and key filters, and how [( )] is built — what goes into the DOM and what comes back.Modern fundamentals
  • 02Control flow: @if, @for, @switchThe block syntax that replaces *ngIf/*ngFor/ngSwitch, with track, @empty and exhaustive checks.Modern fundamentals
  • 02@let and the new template expressionsLocal variables with @let, arrow functions, spread, regex, instanceof, comments inside tags.Modern fundamentals
  • 02@defer — lazy loading in templatesLoad heavy components only when needed: triggers, prefetch, placeholder, loading, error.Modern fundamentals
  • 02Class & style bindingsWhy [class.x] and [style.x] replaced ngClass and ngStyle (with automatic migration from v21).Modern fundamentals
  • 02Component styles & encapsulation:host, the three encapsulation modes, theming with CSS variables, and why ::ng-deep is a last resort.Modern fundamentals
  • 02Pipes: when and howPure vs impure pipes, custom pipes, and how you choose between a pipe, computed() and a method.Modern fundamentals
  • 03signal() and computed()The reactive primitives: writable state, derived state, equality, untracked and immutability rules.Signals & reactivity
  • 03effect() — when YES, when NOSide effects synchronised with signals: localStorage, logging, imperative APIs. And their traps.Signals & reactivity
  • 03linkedSignal() — editable derived stateA signal that resets when its source changes, but that you can also set manually.Signals & reactivity
  • 03resource, rxResource, httpResourceAsync data as signals: status, value, error, reload, automatic cancellation. Stable since v22.Signals & reactivity
  • 03debounced() — delayed signalsThe “debounce” version of a signal, without RxJS. Perfect for search.Signals & reactivity
  • 03Lists: search, filter, sort, paginateA chain of computed signals that recalculates only what changed, with the filters kept in the URL so a link reopens the same view.Signals & reactivity
  • 03Observables from zeroWhat an Observable is, subscribe and unsubscribe, the operators you use every week, cold vs hot — and when a signal is enough.Signals & reactivity
  • 03Signals ↔ RxJStoSignal, toObservable, takeUntilDestroyed, outputFromObservable — and when RxJS remains the right choice.Signals & reactivity
  • 04input() and output()Inputs as signals (required, transform, alias) and typed outputs. Data down, events up.Components: communication
  • 04model() — two-way bindingBanana-in-a-box [( )] with a single API. When to use it, and when input + output is better.Components: communication
  • 04viewChild & contentChildQueries as signals: viewChild, viewChildren, contentChild, contentChildren, the read option.Components: communication
  • 04Lifecycle & DestroyRefWhich hooks still matter in the signals era, afterNextRender, DestroyRef and typed ngOnChanges.Components: communication
  • 04Content projectionng-content with select, multiple slots, default content and the trap of conditional projection.Components: communication
  • 04ng-template & ngTemplateOutletTemplates as values: render props in Angular, typed context and customising components.Components: communication
  • 05Smart vs DumbContainer vs presentational: who holds the state, who only displays, and how to split them properly.Component design
  • 05Generic components <T>A correctly typed DataTable<T> or Select<T>, with templates for cells.Component design
  • 05Dynamic componentsNgComponentOutlet, createComponent with inputBinding/outputBinding, and a dashboard built from config.Component design
  • 05Directives & host compositionAttribute directives, the host: {} metadata and the Directive Composition API (hostDirectives).Component design
  • 05Custom form controlsFormValueControl (Signal Forms) vs ControlValueAccessor: a star rating, in both variants.Component design
  • 05Change detectionOnPush is the default from v22, zone.js is gone from v21. What triggers rendering and how not to get burned.Component design
  • 06inject() & @Service()The new @Service() decorator from v22, inject() vs the constructor, and the injection context.Dependency Injection
  • 06Providers & the injector hierarchyRoot vs route vs component, useClass/useValue/useFactory/useExisting, and the optional/self/skipSelf modifiers.Dependency Injection
  • 06InjectionToken & provideX()Typed configuration, provideFeature() functions, makeEnvironmentProviders and initializers.Dependency Injection
  • 06injectAsync() — lazy servicesLoad a heavy service only when you need it, with prefetch on idle.Dependency Injection
  • 07Folder structurecore / shared / features, what goes into each, and what a feature looks like inside.Application architecture
  • 07Shared vs featureWhen to move a component into shared and (especially) when NOT to. A concrete checklist.Application architecture
  • 07Interfaces & modelsInline interface or separate file? DTO vs domain model, type vs interface, where to put them.Application architecture
  • 07State management with signalsSimple signal stores, facades, local vs global state, and when you need NgRx.Application architecture
  • 07Style guide & namingThe current official conventions: file names without suffixes, protected, readonly, member order.Application architecture
  • 07Barrels, aliases & boundariesindex.ts, path aliases, a feature’s public API and dependency rules that can be checked automatically.Application architecture
  • 07Errors: @boundary & ErrorHandlerThe new @boundary block from v22.2, the global ErrorHandler, HTTP errors and errors from resource.Application architecture
  • 08Modern routing & lazy loadingloadComponent, child routes, parameters as inputs and parameter inheritance (the new default in v22).Routing
  • 08Guards & resolverscanActivate, canMatch, canDeactivate and resolve as functions, with redirects via UrlTree / RedirectCommand.Routing
  • 09Signal Forms — the basicsForms built on signals: form(), [formField], schema-based validation. Stable since v22.Forms
  • 09Signal Forms — advancedCustom and cross-field validation, async/HTTP with debounce, dynamic lists, disabled/hidden, reusable schemas.Forms
  • 09Typed Reactive FormsTyped FormGroup, nonNullable, FormArray — and how they coexist with Signal Forms.Forms
  • 10HttpClient, interceptors & the API layerHttpClient without setup (v21), Fetch by default (v22), functional interceptors and an API layer with DTO mapping.HTTP & data
  • 11Performance — a checklistWhat really matters: OnPush + signals, track, @defer, lazy routes, images, bundles and hydration.Quality & performance
  • 11Testing with VitestVitest is the default runner from v21. TestBed, components with signals, services, HTTP and harnesses.Quality & performance
  • 11DevTools: look inside your appChrome DevTools and Angular DevTools step by step: what each panel shows, with exercises that work on this site.Quality & performance
  • 11Angular AriaHeadless, accessible components that you style however you like: tabs, accordion, menu, listbox...Quality & performance
  • 12SSR, prerender & render modesWhen to render on the server, when at build time and when only in the browser: RenderMode per route, app.routes.server.ts and the real costs.SSR & hydration
  • 12Hydration: event replay & incrementalHow the browser takes over server HTML without re-rendering it: full hydration, event replay, @defer (hydrate on …) and mismatch errors.SSR & hydration
  • 12Server-compatible codewindow doesn’t exist on the server: PLATFORM_ID, afterNextRender, DOCUMENT, TransferState, the HTTP cache and PendingTasks.SSR & hydration
  • 13Micro frontendsWhen it is worth splitting an app into several deployments and when it is just complexity: Native Federation, Module Federation, iframes, Web Components.Architecture at scale
  • 13Web Components with Angular ElementscreateCustomElement: an Angular component used as an HTML tag anywhere, plus communication between apps through events and BroadcastChannel.Architecture at scale
  • 13Monorepo, libraries & boundariesA workspace with several apps and libraries: ng generate library, Nx, secondary entry points, publishing and dependency rules.Architecture at scale
  • 13Hexagonal architecture (ports & adapters)A domain without Angular, ports as abstract classes and interchangeable adapters: HTTP, localStorage or in-memory, without touching the UI.Architecture at scale
  • 14Signals under the hoodThe reactive graph: producers and consumers, push-pull, why computed is lazy and glitch-free, the diamond problem and when effects run.Advanced reactivity
  • 14Advanced RxJS: higher-order operatorsswitchMap, mergeMap, concatMap and exhaustMap on a live diagram, plus retry with backoff, shareReplay and how to avoid leaks.Advanced reactivity
  • 14NgRx SignalStoreLarge-scale state management on signals: withState, withComputed, withMethods, rxMethod, entities and hooks — and when a simple store is enough.Advanced reactivity
  • 14State machines with signalsNo more impossible states: a typed machine on signals, with transitions, guards and a live state diagram.Advanced reactivity
  • 15WebSockets: real time that survives the networkA typed socket as signals: reconnect with backoff and jitter, heartbeat, an outbox while offline, and SSR safety.Real time & offline
  • 15Optimistic updates & rollbackAn instant UI that stays honest: snapshot and rollback, out-of-order responses, idempotency keys.Real time & offline
  • 15Offline-first: Service Worker & cachingThe Angular Service Worker: caching strategies, update prompts and an outbox for changes made offline.Real time & offline
  • 16Web WorkersHeavy computation without blocking the UI: a real worker in the page, message passing and when it is not worth it.Advanced UI & performance
  • 16Huge lists: virtual scroll100,000 rows without lag: CDK virtual scroll, itemSize, trackBy and the alternatives (pagination, infinite scroll).Advanced UI & performance
  • 16Modern animations & View Transitionsanimate.enter and animate.leave instead of the deprecated @angular/animations, CSS transitions and View Transitions on navigation.Advanced UI & performance
  • 16Memory leaks: finding and fixing themSubscriptions, timers and listeners that outlive their component — how to spot them in DevTools and close them.Advanced UI & performance
  • 17Security: XSS, sanitisation & CSPWhat Angular does automatically against XSS, when bypassSecurityTrust becomes dangerous, plus CSP with nonce, Trusted Types and XSRF.Security & globalisation
  • 17Authentication: tokens & refreshAn interceptor that attaches the token, refreshes once on 401 and replays the queued requests — plus where you keep the token.Security & globalisation
  • 17OAuth 2.1 & OpenID Connect with PKCEThe authorization code flow with PKCE, step by step: verifier and challenge, state and nonce, the ID token, and why a BFF is safer.Security & globalisation
  • 17Internationalisation (i18n)$localize, the i18n attribute, ICU plural/select, a build per language vs runtime translations, and locale-based formatting.Security & globalisation