<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>
Usage
import { Banner } from '@hina-ui/vue'
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>
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>
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>
Link
Put a Link next to the text in the default slot. It takes the banner’s text colour and is told apart by its underline.
<script setup lang="ts">
import { Banner, Link } from '@hina-ui/vue'
</script>
<template>
<Banner tone="info">
You are reading the docs for 1.x.
<Link href="#" underline>Go to the latest version</Link>
</Banner>
</template>
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>
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>
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>
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>
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>
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.prevandbanner.nextin 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 |