Container

Layout primitive that constrains/wraps page content. The <smart-container> wrapper renders ContainerStandardComponent by default (a neutral <div> that exposes data-mode / data-padding attributes for downstream styling). Consumers can replace the standard with a custom variant via CONTAINER_STANDARD_COMPONENT_TOKEN.


Usage

<smart-container [options]="options">
  <h1>Account settings</h1>
  <p>Manage your profile, notifications and billing details.</p>
</smart-container>

Public API

Wrapper: smart-container

InputTypeDefaultDescription
optionsIContainerOptionsundefinedLayout configuration
classstring''Extra CSS class on wrapper

Content Projection

SelectorDescription
defaultBody content rendered inside the wrapper

Architecture

Three-layer pattern mirroring <smart-toggle>:

  1. ContainerBaseComponent (@Directive()) — shared signals/inputs (options, cssClass via class alias) and the smartType = 'container' discriminator. No outputs, no methods.
  2. ContainerStandardComponent (selector: smart-container-standard) — default concrete implementation extending the base. Renders a single <div> with [class], [attr.data-mode], [attr.data-padding], and <ng-content />.
  3. ContainerComponent (selector: smart-container) — wrapper. Uses inject(CONTAINER_STANDARD_COMPONENT_TOKEN, { optional: true }) + *ngComponentOutlet to render the injected component, falling back to <smart-container-standard> with <ng-content /> inside it so default-mode projection works.

Content Projection Limitation (IMPORTANT)

The wrapper uses *ngComponentOutlet to render any token-provided implementation. NgComponentOutlet does not forward content projection — children placed between <smart-container>...</smart-container> are dropped when an injected component is active.

Consequences for custom implementations:

  • The default ContainerStandardComponent works fine because the wrapper places <ng-content /> directly inside <smart-container-standard> in the default branch.
  • A custom component provided via CONTAINER_STANDARD_COMPONENT_TOKEN must not rely on <ng-content />. Instead, drive the rendered content through inputs (e.g. options, additional input signals on the custom subclass, or a TemplateRef input).
  • If you genuinely need projected children with a custom variant, expose a bodyTpl: input<TemplateRef>() style input and render it with <ng-container *ngTemplateOutlet="bodyTpl()" />.

Preset

ContainerPresetComponent (selector smart-container-preset, dir container/preset/) is a styled drop-in for the neutral standard component. It extends ContainerStandardComponent and maps IContainerOptions to real Tailwind layout utilities (all smart:-prefixed) on a single root <div data-role="container"> that preserves <ng-content />. No new options fields are introduced.

Class mapping (getContainerClasses(mode, padding, narrow) in preset/preset-classes.util.ts):

OptionClasses
mode: 'container'smart:mx-auto smart:max-w-7xl
mode: 'constrained'smart:mx-auto smart:max-w-5xl
mode: 'full-width' / unsetsmart:w-full
narrow: truetightens max-width to smart:max-w-3xl (wins over the mode max-width, stays smart:mx-auto)
padding: 'always'smart:px-4 smart:sm:px-6 smart:lg:px-8
padding: 'mobile'smart:px-4 smart:sm:px-0
padding: 'none' / unsetno padding classes

The wrapper forwards inputs canonically through NgComponentOutlet, so the preset does override cssClass = input<string>('') (dropping the class alias). External cssClass is appended after the mapped classes.

Register it via the token:

Documented gap: because NgComponentOutlet does not forward content projection, projected children only render when <smart-container-preset> is used directly, not through <smart-container> (see the projection limitation above). Story: container.component.stories.ts → Preset.

Source

The component lives in packages/shared/angular/src/lib/components/container and is documented for Claude Code by the angular-components-container skill.