All blocks
Block
Empty-state onboarding
The 'first message' surface every chat app needs. A hero with suggestion cards, plus a row of pill chips above a working prompt input. Picking a card or chip pre-fills the input.
Onboarding hero with prefill
How can I help today?
Pick a starting point or just start typing.
<!-- spartan empty state with suggestion cards -->
<hlm-empty>
<hlm-empty-header>
<h2 hlmEmptyTitle>How can I help today?</h2>
<p hlmEmptyDescription>Pick a starting point or just start typing.</p>
</hlm-empty-header>
<hlm-empty-content class="flex-row flex-wrap justify-center gap-3">
@for (s of suggestions; track s.label) {
<button type="button" (click)="prefill(s.prompt)" class="text-left">
<div hlmCard class="hover:bg-accent h-full">
<div hlmCardContent class="flex flex-col gap-2">
<ng-icon [name]="s.icon" />
<span class="text-sm font-medium">{{ s.label }}</span>
</div>
</div>
</button>
}
</hlm-empty-content>
</hlm-empty>
<div class="flex flex-wrap justify-center gap-2">
@for (q of quickPrompts; track q) {
<pk-prompt-suggestion [content]="q" (clicked)="prefill(q)" />
}
</div>
<!-- spartan input-group: textarea plus a block-end addon -->
<div hlmInputGroup class="rounded-3xl">
<textarea
hlmInputGroupTextarea
placeholder="Ask anything..."
[value]="value()"
(input)="value.set($any($event.target).value)"
(keydown.enter)="onEnter($event)"
></textarea>
<div hlmInputGroupAddon align="block-end" class="justify-end">
<button hlmBtn size="icon-sm" class="rounded-full" hlmTooltip="Send" (click)="onSubmit()">
<ng-icon name="lucideArrowUp" class="text-[length:--spacing(3)]" />
</button>
</div>
</div>
// In the component
protected readonly value = signal('');
protected readonly suggestions = [
{ label: 'Draft release notes...', icon: 'lucideFileText', prompt: '...' },
// ...
];
protected readonly quickPrompts = ['Summarise', 'Translate', 'Shorter'];
protected prefill(p: string) { this.value.set(p); }
protected onEnter(event: Event) {
if ((event as KeyboardEvent).shiftKey) return; // Shift+Enter adds a line
event.preventDefault();
this.onSubmit();
}
protected onSubmit() {
const v = this.value().trim();
if (!v) return;
this.value.set('');
}