Guidelines for professional sticky headers in modules.
app-sticky-header component.For standard list modules, use sticky stats plus sticky search from vendix-frontend-standard-module instead.
apps/frontend/src/app/shared/components/sticky-header/sticky-header.component.tsapps/frontend/src/app/shared/components/sticky-header/sticky-header.component.htmlapps/frontend/src/app/shared/components/sticky-header/README.mdPut the sticky header at the root of the page/form container. Do not place it inside a padded parent, because sticky top-0 sticks relative to the padded container and creates offset/jump issues.
<form [formGroup]="form" (ngSubmit)="onSubmit()" class="min-h-screen">
<app-sticky-header
title="General Settings"
subtitle="Manage store identity"
icon="settings"
[actions]="headerActions()"
(actionClicked)="onHeaderAction($event)"
/>
<div class="p-4 md:p-6">
<!-- form content -->
</div>
</form>
Inputs:
title: string required.subtitle: string = ''.icon: string = 'box'.variant: 'default' | 'glass' = 'glass'.showBackButton: boolean = false.backRoute: string | string[] = '/'.metadataContent: string = ''.badgePulse: boolean = false.badgeText: string = ''.badgeColor: 'green' | 'blue' | 'yellow' | 'gray' | 'red' = 'blue'.actions: StickyHeaderActionButton[] = [].tabs: StickyHeaderTab[] = [].activeTab: string = ''.tabsAriaLabel: string = 'Secciones'.Outputs:
actionClicked: output<string>(), emitting the action id.tabChanged: output<string>(), emitting the tab id.metadataContent is rendered as plain interpolated text in the current template, not [innerHTML]. The current template does not expose an ng-content slot.
readonly headerActions = computed<StickyHeaderActionButton[]>(() => [
{ id: 'cancel', label: 'Cancel', variant: 'outline', icon: 'x' },
{
id: 'save',
label: 'Save Changes',
variant: 'primary',
icon: 'save',
loading: this.saving(),
disabled: this.form.invalid,
},
]);
onHeaderAction(actionId: string): void {
if (actionId === 'cancel') this.onCancel();
if (actionId === 'save') this.onSubmit();
}
StickyHeaderActionButton supports id, label, variant, icon, loading, disabled, and visible.
Use header tabs for page-level module views. Tabs can be local state tabs or route tabs: The component renders tabs as a compact top row above the title/action row.
readonly activeTab = signal('overview');
readonly tabs: StickyHeaderTab[] = [
{ id: 'overview', label: 'Resumen', icon: 'file-text' },
{ id: 'pricing', label: 'Precios', icon: 'credit-card' },
];
<app-sticky-header
title="Nuevo plan"
subtitle="Crea un plan de suscripción"
icon="credit-card"
[tabs]="tabs"
[activeTab]="activeTab()"
(tabChanged)="activeTab.set($event)"
/>
Route tabs use route and rely on RouterLinkActive:
readonly tabs: StickyHeaderTab[] = [
{ id: 'overview', route: 'overview', label: 'Overview', icon: 'layout-dashboard' },
{ id: 'health', route: 'health', label: 'Salud', icon: 'heart-pulse' },
];
StickyHeaderTab supports id, label, shortLabel, icon, route, exact, disabled, and visible.
If a header action submits the form, keep the <form> around both app-sticky-header and the form body. Otherwise, the save action is disconnected from the form lifecycle.
app-sticky-header before custom markup unless the page has a documented exception.app-sticky-header before creating custom tab bars or sibling app-scrollable-tabs.icons.registry.ts.StickyHeaderTab[] and update a signal from (tabChanged).vendix-frontend-standard-module - List-page sticky stats/search patternvendix-frontend-icons - Icon registrationvendix-angular-forms - Form binding patternsvendix-zoneless-signals - Signal/computed state