Banner

An announcement bar across the top of the page.

<script setup lang="ts">
  import { Banner, Link } from '@hina-ui/vue'
</script>

<template>
  <Banner closable>
    Hina UI 1.2 is out.
    <Link href="#" underline>Read the release notes</Link>
  </Banner>
</template>
vue

Usage

import { Banner } from '@hina-ui/vue'
ts

A banner spans the full width of the page, usually at the very top, for site-wide news such as a release, scheduled maintenance or a campaign. It is a solid block with no radius or border. For a message about one action use Alert; for a fixed note written into the content use Callout. Add sticky top-0 to keep it at the top while the page scrolls.

<script setup lang="ts">
  import { Banner } from '@hina-ui/vue'
</script>

<template>
  <Banner>The site will be down for maintenance on 1 March, 02:00 to 04:00.</Banner>
</template>
vue

Examples

Tones

Six tones, accent by default. The icon follows the tone; set icon to false to hide it.

<script setup lang="ts">
  import { Banner, Stack } from '@hina-ui/vue'
</script>

<template>
  <Stack class="w-full">
    <Banner tone="neutral">Comments now run on the new system.</Banner>
    <Banner tone="accent">Hina UI 1.2 is out.</Banner>
    <Banner tone="info">You are reading the docs for an older version.</Banner>
    <Banner tone="success">Your email address has been verified.</Banner>
    <Banner tone="warning">The site will be down for maintenance tonight at 02:00.</Banner>
    <Banner tone="danger">Payments are temporarily unavailable. We are on it.</Banner>
    <Banner tone="accent" :icon="false">Hina UI 1.2 is out.</Banner>
  </Stack>
</template>
vue

Custom icon

The icon slot replaces the default icon.

<script setup lang="ts">
  import { Sparkles } from '@lucide/vue'
  import { Banner } from '@hina-ui/vue'
</script>

<template>
  <Banner>
    <template #icon><Sparkles class="size-4 shrink-0" /></template>
    Anniversary sale: 20% off every book for a limited time.
  </Banner>
</template>
vue

Actions

The actions slot follows the text and holds a single button.

<script setup lang="ts">
  import { Banner, Button } from '@hina-ui/vue'
</script>

<template>
  <Banner tone="warning">
    Your session expires in 5 minutes.
    <template #actions>
      <Button size="sm" tone="neutral">Stay signed in</Button>
    </template>
  </Banner>
</template>
vue

Closable

With closable a close button appears at the end and emits close when pressed. v-model:open controls visibility; remembering that the user closed it is up to the application.

<script setup lang="ts">
  import { ref } from 'vue'
  import { Banner, Button, Stack } from '@hina-ui/vue'

  const open = ref(true)
</script>

<template>
  <Stack class="min-h-24 w-full">
    <Banner v-model:open="open" tone="success" closable>
      Your email address has been verified.
    </Banner>
    <Button variant="soft" tone="neutral" :disabled="open" class="self-start" @click="open = true">
      Show again
    </Button>
  </Stack>
</template>
vue

Several notices

Pass several notices through items and render each one in the item slot. With more than one, previous and next buttons and a counter appear at the end, the last notice wraps around to the first, and v-model:index binds the current notice. Each notice may carry its own tone and icon; otherwise it takes the banner’s. On a switch the icon and text slide together in the direction of travel, and the background and text colours ease over. The close button closes the whole bar.

<script setup lang="ts">
  import { Sparkles } from '@lucide/vue'
  import { Banner, Link, type BannerNotice } from '@hina-ui/vue'

  const notices: (BannerNotice & { text: string; link?: string })[] = [
    { text: 'Hina UI 1.2 is out.', link: 'Read the release notes' },
    { text: 'The site will be down for maintenance on 1 March, 02:00 to 04:00.', tone: 'warning' },
    {
      text: 'Anniversary sale: 20% off every book for a limited time.',
      link: 'Learn more',
      icon: Sparkles,
    },
  ]
</script>

<template>
  <Banner :items="notices" closable>
    <template #item="{ item }">
      {{ item.text }}
      <Link v-if="item.link" href="#" underline>{{ item.link }}</Link>
    </template>
  </Banner>
</template>
vue

Autoplay

autoplay takes an interval in milliseconds and moves to the next notice at that pace. It pauses while the pointer is over the bar, while focus is inside it or while the page is hidden, and resumes afterwards. It does not run when the user has asked the system for reduced motion.

<script setup lang="ts">
  import { Banner } from '@hina-ui/vue'

  const notices = [
    { text: '86 new reviews this week, 1,204 active readers.' },
    { text: 'The most popular title this week is Spice and Wolf.' },
    { text: 'Shelves can now be filtered by tag.' },
  ]
</script>

<template>
  <Banner :items="notices" :autoplay="4000" tone="info">
    <template #item="{ item }">{{ item.text }}</template>
  </Banner>
</template>
vue

In an AppShell

The banner slot of AppShell sits above everything, spanning the sidebar and the main area. When the banner is closed, both move up to fill the space.

Home

Close the banner and the sidebar and main area move up to fill the space.

<script setup lang="ts">
  import {
    AppShell,
    Banner,
    Heading,
    Link,
    NavLink,
    Sidebar,
    SidebarTrigger,
    Text,
  } from '@hina-ui/vue'
</script>

<template>
  <AppShell collapsible="hidden" class="border-line h-96 w-full rounded-lg border">
    <template #banner>
      <Banner closable>
        Hina UI 1.2 is out.
        <Link href="#" underline>Read the release notes</Link>
      </Banner>
    </template>

    <template #sidebar>
      <Sidebar>
        <NavLink href="#" active label="Home">Home</NavLink>
        <NavLink href="#" label="My shelf">My shelf</NavLink>
      </Sidebar>
    </template>

    <template #header>
      <SidebarTrigger />
      <Heading :level="2" size="sm">Home</Heading>
    </template>

    <Text size="sm" tone="muted" class="block p-6">
      Close the banner and the sidebar and main area move up to fill the space.
    </Text>
  </AppShell>
</template>
vue

Behaviour

  • At viewport widths of 640 pixels and above the text is centred on the bar; below that it starts at the leading edge, takes the whole row apart from the close button and wraps like a paragraph.
  • On close the banner collapses and fades out, and the content below moves up smoothly.
  • No entrance animation on first render.
  • Switching between notices slides the icon and text together in the direction of travel: the next notice enters from the end, the previous one from the start, mirrored in right-to-left writing. The old notice slides out before the new one slides in, and the bar keeps its height.
  • Switching to a notice of a different tone eases the background and text colours.

Accessibility

  • The banner is not a live region; screen readers read it in document order.
  • The icon is decorative and hidden from assistive technology.
  • When notices are switched by hand, the content is a polite live region and screen readers read the new notice; during autoplay it is not, so users are not interrupted repeatedly.
  • The close button is named “Close”; the previous and next buttons take their names from banner.prev and banner.next in the locale.

API

Props

Prop
Type
Default
Description
tone
'neutral' | 'accent' | 'info' | 'success' | 'warning' | 'danger'
'accent'
Tone
icon
boolean
true
Whether to show the icon
closable
boolean
false
Whether to show a close button
open
boolean
true
Visibility, supports v-model:open
items
T[]
—
Several notices, rendered by the item slot; each may carry tone and icon
index
number
0
Current notice, supports v-model:index
autoplay
number
—
Interval between notices, in milliseconds
class
string
—
Extra classes on the bar

Events

Event
Payload
Description
update:open
open: boolean
Visibility changed
update:index
index: number
Current notice changed
close
—
The close button was pressed

Slots

Slot
Props
Description
default
—
Text
item
{ item: T, index: number }
One notice when items is set
icon
—
Replaces the icon
actions
—
Actions following the text