banner announcement dismiss localStorage top-bar notification banner dismiss announcement localStorage top-bar persistent announcement banner with close button dismissable site-wide notification bar banner that stays hidden after closing
Dismissable Banner
Fetch pattern JSON:
curl https://webspire.de/patterns/banner/dismissable.json dismissable.html
<!--
Dismissable Banner
Persists dismissed state in localStorage — won't reappear after page reload.
Inline <script> uses IIFE; hides banner immediately if already dismissed.
-->
<div
id="ws-dismissable-banner"
class="ws-banner relative flex items-center justify-between gap-4 bg-[var(--ws-banner-bg)] px-4 py-3 sm:px-6"
role="banner"
aria-live="polite"
>
<!-- Left: icon + message -->
<div class="flex min-w-0 items-center gap-3">
<!-- Info icon -->
<svg class="h-4 w-4 shrink-0 text-[var(--ws-banner-text)]" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
<path fill-rule="evenodd" d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7-4a1 1 0 11-2 0 1 1 0 012 0zM9 9a.75.75 0 000 1.5h.253a.25.25 0 01.244.304l-.459 2.066A1.75 1.75 0 0010.747 15H11a.75.75 0 000-1.5h-.253a.25.25 0 01-.244-.304l.459-2.066A1.75 1.75 0 009.253 9H9z" clip-rule="evenodd"/>
</svg>
<p class="truncate text-sm font-medium text-[var(--ws-banner-text)]">
We just launched a new design — <a href="#" class="underline underline-offset-2 hover:opacity-80">see what's new →</a>
</p>
</div>
<!-- Right: dismiss button -->
<button
id="ws-banner-close"
type="button"
class="shrink-0 rounded p-1 text-[var(--ws-banner-text)] opacity-70 transition hover:opacity-100 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-white"
aria-label="Dismiss banner"
>
<svg class="h-4 w-4" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
<path d="M6.28 5.22a.75.75 0 00-1.06 1.06L8.94 10l-3.72 3.72a.75.75 0 101.06 1.06L10 11.06l3.72 3.72a.75.75 0 101.06-1.06L11.06 10l3.72-3.72a.75.75 0 00-1.06-1.06L10 8.94 6.28 5.22z"/>
</svg>
</button>
</div>
<script>
(function () {
const STORAGE_KEY = 'ws-banner-dismissed';
const banner = document.getElementById('ws-dismissable-banner');
const closeBtn = document.getElementById('ws-banner-close');
// Hide immediately if already dismissed
if (localStorage.getItem(STORAGE_KEY) === '1') {
if (banner) banner.style.display = 'none';
return;
}
if (closeBtn && banner) {
closeBtn.addEventListener('click', function () {
banner.style.maxHeight = banner.offsetHeight + 'px';
banner.style.overflow = 'hidden';
banner.style.transition = 'max-height 0.25s ease, opacity 0.2s ease';
requestAnimationFrame(function () {
banner.style.maxHeight = '0';
banner.style.opacity = '0';
});
banner.addEventListener('transitionend', function () {
banner.style.display = 'none';
}, { once: true });
localStorage.setItem(STORAGE_KEY, '1');
});
}
})();
</script>
Details
Responsive Dark Mode Tailwind Only Copy & Paste Requires JS
Stable Published
bannerannouncementdismisslocalStoragetop-barnotification
Slots
| Name | Required | Description |
|---|---|---|
| message | Yes | The announcement text — may include an inline link. |
| close-button | Yes | X button that triggers dismiss and persists state. |
Props
| Name | Type | Default | Description |
|---|---|---|---|
| STORAGE_KEY | string | ws-banner-dismissed | localStorage key used to persist the dismissed state. |
The banner hides immediately on load if localStorage.getItem(STORAGE_KEY) === '1', preventing any flash. On dismiss, it animates max-height and opacity to zero, then sets display: none. The localStorage write happens at click time — before the animation completes.
Changing the storage key
<script>
(function () {
const STORAGE_KEY = 'my-app-launch-banner-v2'; // bump version to re-show
// ...
})();
</script>
Without animation (instant dismiss)
Replace the click handler body with:
banner.style.display = 'none';
localStorage.setItem(STORAGE_KEY, '1');