In an Angular component's styles, what do :host and :host-context() select, and how does Emulated mode compile them?
answer
- the tag the parent writes
- host attribute, not tag name
- host or any ancestor
- browser-deprecated, compiler-supported
basics
~20 s:host targets the component's host element, with :host(.x) adding a condition on it; :host-context(.x) applies when the host or any ancestor matches. In Emulated mode the compiler rewrites both into selectors on the _nghost attribute.
solid answer
~40 s`:host` matches the component's **host element**, the tag the parent writes (`<app-badge>`), which the component's own template does not contain, so ordinary selectors cannot reach it. `:host(.compact)` matches the host only when it carries `compact`, while `:host .label` scopes descendants. `:host-context(.theme-dark) .label` applies when the host **or any ancestor** matches `.theme-dark`, the classic way to react to a theme class on `<body>`. Under Emulated encapsulation these are not evaluated as native pseudo-classes: the compiler turns `:host` into `[_nghost-ng-c1234]` and `:host-context(.x)` into `.x[_nghost-…]` plus `.x [_nghost-…]`, so they work without Shadow DOM and follow attribute specificity. The docs note that `:host-context()` is deprecated in browsers but fully supported by Angular's compiler; `::shadow` and `::part` are not supported in Emulated mode.
code
ts · 14 linesimport { Component } from '@angular/core';
@Component({
selector: 'app-badge',
template: `<span class="label"><ng-content /></span>`,
styles: `
:host { display: inline-block; }
:host(.compact) .label { padding: 2px 4px; }
:host-context(.theme-dark) .label { background: #222; color: #eee; }
`,
})
export class Badge {}
// Parent template: <app-badge class="compact">New</app-badge>go deeper
Recall that :host styles the component's own tag, :host(.x) adds a condition on it, and :host-context(.x) reacts to a class on the host or an ancestor.
Explain the compile-time rewrite into _nghost selectors, the two-branch output of :host-context, and why the component's tag name cannot be used as a selector.
Use host variants and ancestor themes deliberately, decide who owns the host's layout, and know how behaviour changes under ShadowDom and None.
Set conventions for component theming in a library: host classes for variants, ancestor context for themes, and custom properties for tunable values.
## The host element problem An Angular component renders its template **inside** a host element: the element matching its selector, written by the parent, such as `<app-badge>`. That host is not part of the component's template, so an Emulated rule like `app-badge { }` is rewritten to `app-badge[_ngcontent-…]` for the component's own attribute and never matches: the host carries its own host attribute (and, under an emulated parent, the parent's content attribute), not the component's content attribute. Angular borrows two pseudo-classes from the Shadow DOM world to solve this. ## :host and :host(selector) - `:host { display: block; }` styles the host element itself. - `:host(.compact) { padding: 4px; }` styles the host only when the host matches `.compact`, for example because the parent wrote `<app-badge class="compact">`. - `:host .label { }` styles `.label` elements inside the component, anchored at the host. The difference between `:host(.compact)` and `:host .compact` trips people up: the first is a condition **on** the host, the second selects **descendants** with that class. A very common first rule is `:host { display: block; }`. The host is a tag the browser does not recognise, and unknown elements default to `display: inline`, so widths, margins and layout behave oddly until the component gives itself a box. ## :host-context(selector) `:host-context(.theme-dark) h2 { }` applies when the host element **or any of its ancestors** matches `.theme-dark`. This lets a component adapt to context set far above it, typically a theme or density class on `<body>` or an app shell, without every parent passing an input down. The docs describe `:host-context()` as deprecated in modern browsers, yet fully supported by Angular's compiler in Emulated mode, because the compiler replaces it before the browser ever sees it. ## What the compiler emits ```css /* authored */ :host { display: block; } :host(.compact) { padding: 4px; } :host-context(.theme-dark) h2 { color: white; } /* emitted (suffix shortened) */ [_nghost-ng-c1234] { display: block; } .compact[_nghost-ng-c1234] { padding: 4px; } .theme-dark[_nghost-ng-c1234] h2[_ngcontent-ng-c1234], .theme-dark [_nghost-ng-c1234] h2[_ngcontent-ng-c1234] { color: white; } ``` The `:host-context` output has two branches: one where the host itself carries the class, one where an ancestor does. Because the result is plain attribute and class selectors, it follows ordinary specificity, not the special rules of the native pseudo-classes; the docs call this out explicitly. ## Mode matters | Mode | How `:host` / `:host-context()` work | |---|---| | `Emulated` | rewritten by the compiler into `_nghost` attribute selectors | | `ShadowDom` | passed to the browser, evaluated natively inside the shadow root, subject to browser support | | `None` | not rewritten; the component's styles are global, so there is no host scope to anchor to | Emulated mode supports only these two: other Shadow DOM selectors such as `::shadow` and `::part` are not supported. ## Common mistakes - **Writing the tag name.** `app-badge { display: block; }` in the component's own styles is rewritten with the component's content attribute and never matches its host. - **Confusing condition and descent.** `:host(.compact)` asks whether the host has the class; `:host .compact` looks for descendants that have it. - **Expecting `:host-context()` to look only one level up.** It checks the host and every ancestor up to the document root. - **Relying on native specificity rules.** In Emulated mode the compiled attribute selectors follow ordinary specificity, so a specific global rule can still win. - **Porting to `None`.** Under `None` there is no host attribute, so `:host`-based rules lose their anchoring. ## Practical guidance 1. Start most components with `:host { display: block; }` (or `inline-block`, `flex`, `contents` as the design needs). 2. Express variants as `:host(.variant)` so the parent can toggle them with a class on the tag. 3. Use `:host-context()` for ancestor-driven themes; prefer CSS custom properties where the value, not the rule, is what varies. 4. Remember that the parent can style the host box too; decide which side owns layout (usually the parent) and which owns internals (always the component).
- Why does a component often need :host { display: block }?The host is a custom tag the browser does not know, and unknown elements default to `display: inline`, so width, height, vertical margins and layout rules behave unexpectedly. Declaring a display on `:host` gives the component a proper box without every parent having to style its tag.
- How do :host(.compact) and :host .compact differ?`:host(.compact)` matches the host element itself when it has the `compact` class, typically set by the parent on the component tag. `:host .compact` matches descendants inside the host that have the class. The first is a condition on the host; the second selects internal elements.
saying these in an interview costs you the question
- Writing the component's own tag name in its styles is how you style the host.
- :host-context() only checks the host's direct parent element.
- Emulated :host depends on the browser's native Shadow DOM support.
- :host(.compact) styles every element with class compact inside the component.
- Angular dropped :host-context() because browsers deprecated it.