Application toolbar

A sticky toolbar for an application view, with leading controls, a title, optional search, and trailing actions. Use SiteHeader for site branding, primary navigation, and mobile navigation collapse.

Default

With no named slots supplied, the default slot fills the title region.

Title

Code

<link
  rel="component"
  href="@threadlabs/looma/components/ui-top-bar/ui-top-bar.html"
>

<ui-top-bar> <strong>Title</strong> </ui-top-bar>
<script setup>
  import { UiTopBar } from "@threadlabs/looma";
</script>

<template>
  <UiTopBar> <strong>Title</strong> </UiTopBar>
</template>
import { UiTopBar } from "@threadlabs/looma";

export default function Example() {
  return <UiTopBar>{" "}<strong>Title</strong>{" "}</UiTopBar>;
}
/** @jsxImportSource preact */
import { UiTopBar } from "@threadlabs/looma";

export default function Example() {
  return <UiTopBar>{" "}<strong>Title</strong>{" "}</UiTopBar>;
}
<script>
  import { UiTopBar } from "@threadlabs/looma";
</script>

<UiTopBar> <strong>Title</strong> </UiTopBar>
import { Component } from "@angular/core";
import { UiTopBar } from "@threadlabs/looma";

@Component({
  selector: "example-view",
  standalone: true,
  imports: [UiTopBar],
  template: `
    <header
      ui-top-bar
    > <strong>Title</strong> </header>
  `,
})
export default class Example {}

leading, search, and actions slots

Each named slot adds its region around the default title without requiring internal wrapper markup.

Title

Code

<link
  rel="component"
  href="@threadlabs/looma/components/ui-top-bar/ui-top-bar.html"
>
<link
  rel="component"
  href="@threadlabs/looma/components/ui-button/ui-button.html"
>

<ui-top-bar>
  <ui-button
    slot="leading"
    size="sm"
  >Back</ui-button>
  <strong>Title</strong>
  <ui-button
    slot="search"
    size="sm"
  >Search</ui-button>
  <ui-button
    slot="actions"
    size="sm"
  >Save</ui-button>
</ui-top-bar>
<script setup>
  import { UiButton, UiTopBar } from "@threadlabs/looma";
</script>

<template>
  <UiTopBar>
    <template #leading>
      <UiButton
        size="sm"
      >Back</UiButton>
    </template>
    <strong>Title</strong>
    <template #search>
      <UiButton
        size="sm"
      >Search</UiButton>
    </template>
    <template #actions>
      <UiButton
        size="sm"
      >Save</UiButton>
    </template>
  </UiTopBar>
</template>
import { UiButton, UiTopBar } from "@threadlabs/looma";

export default function Example() {
  return <UiTopBar
    slots={{
      leading: <UiButton
        size="sm"
      >Back</UiButton>,
      search: <UiButton
        size="sm"
      >Search</UiButton>,
      actions: <UiButton
        size="sm"
      >Save</UiButton>,
    }}
  >{" "}{" "}<strong>Title</strong>{" "}{" "}{" "}</UiTopBar>;
}
/** @jsxImportSource preact */
import { UiButton, UiTopBar } from "@threadlabs/looma";

export default function Example() {
  return <UiTopBar
    slots={{
      leading: <UiButton
        size="sm"
      >Back</UiButton>,
      search: <UiButton
        size="sm"
      >Search</UiButton>,
      actions: <UiButton
        size="sm"
      >Save</UiButton>,
    }}
  >{" "}{" "}<strong>Title</strong>{" "}{" "}{" "}</UiTopBar>;
}
<script>
  import { UiButton, UiTopBar } from "@threadlabs/looma";
</script>

{#snippet leading()}
  <UiButton
    size="sm"
  >Back</UiButton>
{/snippet}

{#snippet search()}
  <UiButton
    size="sm"
  >Search</UiButton>
{/snippet}

{#snippet actions()}
  <UiButton
    size="sm"
  >Save</UiButton>
{/snippet}

<UiTopBar
  slots={{ leading, search, actions }}
>  <strong>Title</strong>   </UiTopBar>
import { Component } from "@angular/core";
import { UiButton, UiTopBar } from "@threadlabs/looma";

@Component({
  selector: "example-view",
  standalone: true,
  imports: [UiTopBar, UiButton],
  template: `
    <header
      ui-top-bar
    >
      <ng-container ngProjectAs="[slot='leading']">
        <ng-template
          ui-button
          [size]="'sm'"
        >Back</ng-template>
      </ng-container>
      <strong>Title</strong>
      <ng-container ngProjectAs="[slot='search']">
        <ng-template
          ui-button
          [size]="'sm'"
        >Search</ng-template>
      </ng-container>
      <ng-container ngProjectAs="[slot='actions']">
        <ng-template
          ui-button
          [size]="'sm'"
        >Save</ng-template>
      </ng-container>
    </header>
  `,
})
export default class Example {}

Reveal the title after its heading

Scroll the document past its heading. The sticky title slides up and fades in once the heading leaves the visible reading area, and disappears when the heading returns. Reduced motion removes that transition.

A document with a long descriptive title

The heading labels the document while it is visible. Navigation and actions stay available.

Scroll to keep reading the document and its supporting notes.

The toolbar keeps the same height as the heading leaves the reading area.

Return to the beginning to show the full heading again.

The duplicate toolbar title is concealed while the heading is visible.

Code

<link
  rel="component"
  href="@threadlabs/looma/components/ui-stack/ui-stack.html"
>
<link
  rel="component"
  href="@threadlabs/looma/components/ui-scroll-area/ui-scroll-area.html"
>
<link
  rel="component"
  href="@threadlabs/looma/components/ui-top-bar/ui-top-bar.html"
>
<link
  rel="component"
  href="@threadlabs/looma/components/ui-button/ui-button.html"
>
<link
  rel="component"
  href="@threadlabs/looma/components/ui-container/ui-container.html"
>
<link
  rel="component"
  href="@threadlabs/looma/components/ui-text/ui-text.html"
>

<ui-stack
  height="256"
  gap="none"
>
  <ui-scroll-area
    fill
  >
    <ui-top-bar
      title-for="top-bar-example-heading"
    >
      <ui-button
        slot="leading"
        size="sm"
      >Back</ui-button>
      A document with a long descriptive title
      <ui-button
        slot="actions"
        size="sm"
      >Save</ui-button>
    </ui-top-bar>
    <ui-container
      fill
    >
      <ui-stack
        gap="m"
      >
        <ui-text
          as="h2"
          variant="heading"
          id="top-bar-example-heading"
        >A document with a long descriptive title</ui-text>
        <ui-text>The heading labels the document while it is visible. Navigation and actions stay available.</ui-text>
        <ui-text>Scroll to keep reading the document and its supporting notes.</ui-text>
        <ui-text>The toolbar keeps the same height as the heading leaves the reading area.</ui-text>
        <ui-text>Return to the beginning to show the full heading again.</ui-text>
        <ui-text>The duplicate toolbar title is concealed while the heading is visible.</ui-text>
      </ui-stack>
    </ui-container>
  </ui-scroll-area>
</ui-stack>
<script setup>
  import { UiButton, UiContainer, UiScrollArea, UiStack, UiText, UiTopBar } from "@threadlabs/looma";
</script>

<template>
  <UiStack
    :height="256"
    gap="none"
  >
    <UiScrollArea
      :fill="true"
    >
      <UiTopBar
        title-for="top-bar-example-heading"
      > <template #leading>
          <UiButton
            size="sm"
          >Back</UiButton>
        </template> A document with a long descriptive title <template #actions>
          <UiButton
            size="sm"
          >Save</UiButton>
        </template> </UiTopBar>
      <UiContainer
        :fill="true"
      >
        <UiStack
          gap="m"
        >
          <UiText
            as="h2"
            variant="heading"
            id="top-bar-example-heading"
          >A document with a long descriptive title</UiText>
          <UiText>The heading labels the document while it is visible. Navigation and actions stay available.</UiText>
          <UiText>Scroll to keep reading the document and its supporting notes.</UiText>
          <UiText>The toolbar keeps the same height as the heading leaves the reading area.</UiText>
          <UiText>Return to the beginning to show the full heading again.</UiText>
          <UiText>The duplicate toolbar title is concealed while the heading is visible.</UiText>
        </UiStack>
      </UiContainer>
    </UiScrollArea>
  </UiStack>
</template>
import { UiButton, UiContainer, UiScrollArea, UiStack, UiText, UiTopBar } from "@threadlabs/looma";

export default function Example() {
  return <UiStack
    height={256}
    gap="none"
  >
    <UiScrollArea
      fill
    >
      <UiTopBar
        titleFor="top-bar-example-heading"
        slots={{
          leading: <UiButton
            size="sm"
          >Back</UiButton>,
          actions: <UiButton
            size="sm"
          >Save</UiButton>,
        }}
      >{" "}{" A document with a long descriptive title "}{" "}</UiTopBar>
      <UiContainer
        fill
      >
        <UiStack
          gap="m"
        >
          <UiText
            as="h2"
            variant="heading"
            id="top-bar-example-heading"
          >A document with a long descriptive title</UiText>
          <UiText>The heading labels the document while it is visible. Navigation and actions stay available.</UiText>
          <UiText>Scroll to keep reading the document and its supporting notes.</UiText>
          <UiText>The toolbar keeps the same height as the heading leaves the reading area.</UiText>
          <UiText>Return to the beginning to show the full heading again.</UiText>
          <UiText>The duplicate toolbar title is concealed while the heading is visible.</UiText>
        </UiStack>
      </UiContainer>
    </UiScrollArea>
  </UiStack>;
}
/** @jsxImportSource preact */
import { UiButton, UiContainer, UiScrollArea, UiStack, UiText, UiTopBar } from "@threadlabs/looma";

export default function Example() {
  return <UiStack
    height={256}
    gap="none"
  >
    <UiScrollArea
      fill
    >
      <UiTopBar
        titleFor="top-bar-example-heading"
        slots={{
          leading: <UiButton
            size="sm"
          >Back</UiButton>,
          actions: <UiButton
            size="sm"
          >Save</UiButton>,
        }}
      >{" "}{" A document with a long descriptive title "}{" "}</UiTopBar>
      <UiContainer
        fill
      >
        <UiStack
          gap="m"
        >
          <UiText
            as="h2"
            variant="heading"
            id="top-bar-example-heading"
          >A document with a long descriptive title</UiText>
          <UiText>The heading labels the document while it is visible. Navigation and actions stay available.</UiText>
          <UiText>Scroll to keep reading the document and its supporting notes.</UiText>
          <UiText>The toolbar keeps the same height as the heading leaves the reading area.</UiText>
          <UiText>Return to the beginning to show the full heading again.</UiText>
          <UiText>The duplicate toolbar title is concealed while the heading is visible.</UiText>
        </UiStack>
      </UiContainer>
    </UiScrollArea>
  </UiStack>;
}
<script>
  import { UiButton, UiContainer, UiScrollArea, UiStack, UiText, UiTopBar } from "@threadlabs/looma";
</script>

{#snippet leading()}
  <UiButton
    size="sm"
  >Back</UiButton>
{/snippet}

{#snippet actions()}
  <UiButton
    size="sm"
  >Save</UiButton>
{/snippet}

<UiStack
  height={256}
  gap="none"
>
  <UiScrollArea
    fill
  >
    <UiTopBar
      titleFor="top-bar-example-heading"
      slots={{ leading, actions }}
    >  A document with a long descriptive title  </UiTopBar>
    <UiContainer
      fill
    >
      <UiStack
        gap="m"
      >
        <UiText
          as="h2"
          variant="heading"
          id="top-bar-example-heading"
        >A document with a long descriptive title</UiText>
        <UiText>The heading labels the document while it is visible. Navigation and actions stay available.</UiText>
        <UiText>Scroll to keep reading the document and its supporting notes.</UiText>
        <UiText>The toolbar keeps the same height as the heading leaves the reading area.</UiText>
        <UiText>Return to the beginning to show the full heading again.</UiText>
        <UiText>The duplicate toolbar title is concealed while the heading is visible.</UiText>
      </UiStack>
    </UiContainer>
  </UiScrollArea>
</UiStack>
import { Component } from "@angular/core";
import { UiButton, UiContainer, UiScrollArea, UiStack, UiText, UiTopBar } from "@threadlabs/looma";

@Component({
  selector: "example-view",
  standalone: true,
  imports: [UiStack, UiScrollArea, UiTopBar, UiButton, UiContainer, UiText],
  template: `
    <div
      ui-stack
      [height]="256"
      [gap]="'none'"
    >
      <div
        ui-scroll-area
        [fill]="true"
      >
        <header
          ui-top-bar
          [titleFor]="'top-bar-example-heading'"
        > <ng-container ngProjectAs="[slot='leading']">
            <ng-template
              ui-button
              [size]="'sm'"
            >Back</ng-template>
          </ng-container> A document with a long descriptive title <ng-container ngProjectAs="[slot='actions']">
            <ng-template
              ui-button
              [size]="'sm'"
            >Save</ng-template>
          </ng-container> </header>
        <div
          ui-container
          [fill]="true"
        >
          <div
            ui-stack
            [gap]="'m'"
          >
            <ng-template
              ui-text
              [as]="'h2'"
              [variant]="'heading'"
              [hnAttributes]="{ id: 'top-bar-example-heading' }"
            >A document with a long descriptive title</ng-template>
            <ng-template
              ui-text
            >The heading labels the document while it is visible. Navigation and actions stay available.</ng-template>
            <ng-template
              ui-text
            >Scroll to keep reading the document and its supporting notes.</ng-template>
            <ng-template
              ui-text
            >The toolbar keeps the same height as the heading leaves the reading area.</ng-template>
            <ng-template
              ui-text
            >Return to the beginning to show the full heading again.</ng-template>
            <ng-template
              ui-text
            >The duplicate toolbar title is concealed while the heading is visible.</ng-template>
          </div>
        </div>
      </div>
    </div>
  `,
})
export default class Example {}

API

Package: @threadlabs/looma · Native root: header

Attributes

NameDescriptionPropertyTypeDefaultAllowed values
title-forThe ID of the page heading represented by the default title slot. While that heading is visible below the sticky bar, its duplicate title is concealed. After the heading scrolls above the bar, the single-line title slides up and fades in. Leading and action slots stay available, and the bar keeps its size. Omit it for an always-visible title. Reduced motion removes the transition.titleForstring—

Framework props

NameTypeDefaultAllowed values
titleForstring—

Events

No events.

Slots

NamePurpose
leadingNamed content for the leading region.
defaultDefault child content.
searchNamed content for the search region.
actionsNamed content for the actions region.

Design tokens

Set shared theme properties on a theme boundary. Component hooks apply only to their owning component.

Component tokens

TokenDeclared valuesFallback
--ui-top-bar-actions-gap—var(--ui-space-1)
--ui-top-bar-border—var(--ui-border-divider)
--ui-top-bar-gap—var(--ui-space-3)
--ui-top-bar-height—56px
--ui-top-bar-padding-inline—var(--ui-space-4)
--ui-top-bar-surface—var(--ui-surface-elevated)
--ui-top-bar-z-index—100

Shared tokens

TokenFallback
--ui-border-divider—
--ui-border-width—
--ui-motion-ease—
--ui-motion-fast—
--ui-space-1—
--ui-space-2—
--ui-space-3—
--ui-space-4—
--ui-surface-elevated—