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
| Input | Type | Default | Description |
|---|---|---|---|
options | IContainerOptions | undefined | Layout configuration |
class | string | '' | Extra CSS class on wrapper |
Content Projection
| Selector | Description |
|---|---|
| default | Body content rendered inside the wrapper |
Architecture
Three-layer pattern mirroring <smart-toggle>:
ContainerBaseComponent(@Directive()) — shared signals/inputs (options,cssClassviaclassalias) and thesmartType = 'container'discriminator. No outputs, no methods.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 />.ContainerComponent(selector:smart-container) — wrapper. Usesinject(CONTAINER_STANDARD_COMPONENT_TOKEN, { optional: true })+*ngComponentOutletto 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
ContainerStandardComponentworks fine because the wrapper places<ng-content />directly inside<smart-container-standard>in the default branch. - A custom component provided via
CONTAINER_STANDARD_COMPONENT_TOKENmust not rely on<ng-content />. Instead, drive the rendered content through inputs (e.g.options, additional input signals on the custom subclass, or aTemplateRefinput). - 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):
| Option | Classes |
|---|---|
mode: 'container' | smart:mx-auto smart:max-w-7xl |
mode: 'constrained' | smart:mx-auto smart:max-w-5xl |
mode: 'full-width' / unset | smart:w-full |
narrow: true | tightens 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' / unset | no 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.