Stacked List
The <smart-stacked-list> component renders a vertical list of records with optional title, description, per-item icon/avatar, link, badge, action template, and bottom footer slot. It follows the Base + Standard + Wrapper pattern with an InjectionToken-based extension mechanism. The abstract StackedListBaseComponent defines the shared API — optional IStackedListOptions and cssClass (alias class). StackedListStandardComponent is a barebones placeholder concrete implementation. StackedListPresetComponent is a styled Tailwind drop-in replacement. StackedListComponent is the public wrapper that renders StackedListStandardComponent by default and accepts a custom replacement (such as the preset) via STACKED_LIST_STANDARD_COMPONENT_TOKEN.
Usage
<smart-stacked-list [options]="options" />
Components
StackedListComponent (<smart-stacked-list>)
Main wrapper component. Renders StackedListStandardComponent by default. When STACKED_LIST_STANDARD_COMPONENT_TOKEN is provided, renders the injected component via NgComponentOutlet.
StackedListStandardComponent (<smart-stacked-list-standard>)
Barebones placeholder concrete implementation. Renders a wrapper <div> containing an optional <h3 class="title">, optional <p class="description">, and a <ul role="list"> with one <li class="item"> per item. Each item renders the icon/avatar (icon template wins over avatar URL), a body group with the title (rendered as <a class="title"> if href is provided, otherwise <span class="title">) and optional description/meta, and optional badge/action template slots. When the item list is empty, the optional emptyTpl is rendered inside <div class="empty">. A bottom footerTpl renders inside <div class="footer">. The external cssClass is applied to the root wrapper. It does not include any visual styling — it exists solely as the default structural placeholder until a custom implementation is registered through the token.
StackedListPresetComponent (<smart-stacked-list-preset>)
Styled variation that extends StackedListBaseComponent and is a drop-in replacement for StackedListStandardComponent. See Preset.
StackedListBaseComponent (abstract)
Abstract base directive for extending custom stacked-list implementations. Exposes options as an InputSignal<IStackedListOptions | undefined> and cssClass as an InputSignal<string> (with alias class).
API
Inputs
| Input | Type | Default | Description |
|---|---|---|---|
options | InputSignal<IStackedListOptions | undefined> | - | Optional configuration (title, description, items, layout flags, empty/footer slots) |
class | InputSignal<string> | '' | External CSS classes (alias for cssClass) |
IStackedListOptions
All properties are optional except IStackedListItem.title. The default StackedListStandardComponent renders every content property but ignores the withDividers and fullWidthOnMobile layout hints; StackedListPresetComponent honours them (see Preset). A section is rendered only when its template/string is provided. Within an item, iconTpl wins over avatarUrl when both are set.
Preset
StackedListPresetComponent (selector smart-stacked-list-preset) is the styled skin used by the Storybook stories and the docs site. It extends StackedListBaseComponent, so it takes the same options and cssClass inputs.
It renders the Tailwind UI stacked list look in smart:-prefixed Tailwind v4 classes with a smart:dark: variant on every colour:
- Header:
text-base font-semiboldgray-900 / white title and atext-smgray-500 / gray-400 description. - Rows:
flex items-center justify-between gap-x-6 py-5. The leading media is asize-12rounded-full avatar (avatarUrl) or a gray-100 / gray-800 rounded-full tile wrappingiconTpl(the icon template wins). The body shows the title (text-sm/6 font-semibold, rendered as a link with hover underline whenhrefis set), a truncatedtext-xs/5description and a meta line.badgeTplandactionTplrender in a trailing group. - Empty state:
emptyTplrenders in a dashed gray-200 / white-10 rounded-lg box whenitemsis empty. - Footer:
footerTplrenders under the list. - External class:
cssClassis appended to the root wrapper.
It honours both layout hints that the standard component ignores:
| Option | Preset behaviour |
|---|---|
withDividers | true draws divide-y gray-100 / white-10 hairlines between rows. Unset or false separates rows by spacing only. |
fullWidthOnMobile | true renders the list as a white / gray-900 card with a ring. Below sm it bleeds to the screen edge (-mx-4, square corners); from sm up it is a rounded-xl card. Rows get px-4 sm:px-6 padding. |
Register it on the token to restyle every <smart-stacked-list>:
StackedListComponentrenders injected components throughNgComponentOutlet, which passes inputs by canonical name. For that reasonStackedListPresetComponentdeclarescssClassasinput<string>('')without theclassalias. When you use the<smart-stacked-list-preset>selector directly, bind[cssClass]. On<smart-stacked-list>just passclassand the wrapper forwards it.
The class recipes live in preset/preset-classes.util.ts.
STACKED_LIST_STANDARD_COMPONENT_TOKEN
InjectionToken that allows replacing the default StackedListStandardComponent with a custom implementation. Provide a Type<StackedListBaseComponent> to override.
Extending the base class
import {
ChangeDetectionStrategy,
Component,
computed,
input,
ViewEncapsulation,
} from '@angular/core';
import {
IStackedListOptions,
STACKED_LIST_STANDARD_COMPONENT_TOKEN,
StackedListBaseComponent,
StackedListComponent,
} from '@smartsoft001/angular';
@Component({
selector: 'docs-custom-stacked-list',
template: `
<div [class]="containerClasses()">
@if (options()?.title) {
<h3 class="docs-stacked-list__title">{{ options()!.title }}</h3>
}
@if (options()?.description) {
<p class="docs-stacked-list__description">
{{ options()!.description }}
</p>
}
<ul role="list">
@for (item of options()?.items ?? []; track item.id ?? $index) {
<li class="docs-stacked-list__item">
@if (item.avatarUrl) {
<img
class="docs-stacked-list__avatar"
[src]="item.avatarUrl"
alt=""
/>
}
<span class="docs-stacked-list__body">
@if (item.href) {
<a [attr.href]="item.href">{{ item.title }}</a>
} @else {
<span>{{ item.title }}</span>
}
@if (item.description) {
<span class="docs-stacked-list__meta">
{{ item.description }}
</span>
}
</span>
@if (item.meta) {
<span class="docs-stacked-list__joined">{{ item.meta }}</span>
}
</li>
}
</ul>
</div>
`,
encapsulation: ViewEncapsulation.None,
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class CustomStackedListComponent extends StackedListBaseComponent {
// NgComponentOutlet passes 'cssClass' by canonical name, not the 'class' alias.
override cssClass = input<string>('');
containerClasses = computed(() =>
[
'docs-stacked-list',
this.options()?.withDividers ? 'docs-stacked-list--divided' : '',
this.options()?.fullWidthOnMobile ? 'docs-stacked-list--bleed' : '',
this.cssClass(),
]
.filter(Boolean)
.join(' '),
);
}
@Component({
selector: 'docs-stacked-list-custom-example',
changeDetection: ChangeDetectionStrategy.OnPush,
imports: [StackedListComponent],
// The token swaps the standard list for the custom one everywhere below this
// component, so consumers keep writing `<smart-stacked-list>`.
providers: [
{
provide: STACKED_LIST_STANDARD_COMPONENT_TOKEN,
useValue: CustomStackedListComponent,
},
],
template: `<smart-stacked-list [options]="options" />`,
})
export class StackedListCustomExampleComponent {
// withDividers and fullWidthOnMobile are styling hints: the standard list
// ignores them, a custom implementation decides what they mean.
options: IStackedListOptions = {
title: 'Team members',
description: 'People with access to this workspace.',
withDividers: true,
items: [
{
id: '1',
title: 'Lindsay Walton',
description: 'lindsay.walton@example.com',
meta: 'Joined 12 January 2026',
},
{
id: '2',
title: 'Courtney Henry',
description: 'courtney.henry@example.com',
meta: 'Joined 3 February 2026',
},
{
id: '3',
title: 'Tom Cook',
description: 'tom.cook@example.com',
meta: 'Joined 27 February 2026',
},
],
};
}
Source
The component lives in packages/shared/angular/src/lib/components/stacked-list and is documented for Claude Code by the angular-components-stacked-list skill.