EmpireUI
Get Pro
← Blog7 min read#glassmorphism#alert-banner#announcement-bar

Glassmorphism Alert Banner: Announcement Bar with Blur

Build a glassmorphism alert banner with backdrop-filter blur, frosted glass CSS, and Tailwind v4. Dismissible announcement bar with rgba overlays and smooth transitions.

Frosted glass alert banner floating above a gradient background with blurred bokeh light effects

Why Glassmorphism Still Works for Alert Banners

Honestly, frosted glass never got old — it just got misused. When designers slapped backdrop-filter: blur(20px) on every single element in 2022, the style earned a bad reputation. But used with restraint — especially on an announcement bar that sits at the top of your app — it's one of the cleanest ways to communicate urgency without breaking the visual hierarchy of the page behind it.

An alert banner has a specific job. It needs to sit above your content without completely interrupting it. That tension — visible but not obtrusive — is exactly what glassmorphism solves better than any solid-color approach. The blur effect creates depth while the semi-transparent layer lets users perceive the page is still there, just temporarily beneath a message.

The style also pairs perfectly with modern app backgrounds. Gradients, particle backgrounds, video headers — they all become part of the frosted glass aesthetic rather than competing with it. You're not covering your background; you're using it as an artistic element.

The Core CSS: backdrop-filter and rgba Together

The foundation of any glassmorphism component is two properties working in tandem: backdrop-filter: blur() and a semi-transparent background using rgba(). Skipping either one kills the effect. Without the blur, you just have a transparent div. Without the rgba tint, you lose the frosted quality and readability suffers.

For an announcement bar specifically, you want a relatively subtle blur — somewhere between blur(8px) and blur(16px). Too much blur on a thin banner looks muddy. The background color should use around rgba(255, 255, 255, 0.12) for dark themes and rgba(255, 255, 255, 0.55) for light ones. Those exact values matter. 0.12 gives you just enough tint to distinguish the bar from the content behind it.

Here's the base CSS you'd write from scratch:

.glass-banner {
  background: rgba(255, 255, 255, 0.12);
  backdrop-filter: blur(12px);
  -webkit-backdrop-filter: blur(12px);
  border-bottom: 1px solid rgba(255, 255, 255, 0.18);
  box-shadow: 0 4px 24px rgba(0, 0, 0, 0.08);
}

Always include -webkit-backdrop-filter — Safari still requires it as of mid-2026. And that border-bottom with rgba(255, 255, 255, 0.18) is doing important work. Without it the banner floats without any visual separation from the page body.

Building the React Component with Tailwind v4

Tailwind v4.0.2 ships backdrop-blur-* utilities out of the box, so you don't need any custom config for the blur itself. The tricky part is the rgba background — Tailwind's opacity modifiers work on named colors but get awkward with custom hex values. The cleanest approach is to drop a style prop for the background and let Tailwind handle everything else.

Here's the full dismissible announcement bar component:

import { useState } from 'react';
import { X } from 'lucide-react';

interface GlassBannerProps {
  message: string;
  ctaLabel?: string;
  ctaHref?: string;
  variant?: 'info' | 'warning' | 'success';
}

const variantColors: Record<string, string> = {
  info:    'rgba(99, 179, 237, 0.15)',
  warning: 'rgba(251, 191, 36, 0.15)',
  success: 'rgba(52, 211, 153, 0.15)',
};

const variantBorders: Record<string, string> = {
  info:    'rgba(99, 179, 237, 0.3)',
  warning: 'rgba(251, 191, 36, 0.3)',
  success: 'rgba(52, 211, 153, 0.3)',
};

export function GlassBanner({
  message,
  ctaLabel,
  ctaHref,
  variant = 'info',
}: GlassBannerProps) {
  const [dismissed, setDismissed] = useState(false);

  if (dismissed) return null;

  return (
    <div
      className="w-full px-4 py-3 flex items-center justify-between
                 gap-4 backdrop-blur-md transition-all duration-300"
      style={{
        background: variantColors[variant],
        borderBottom: `1px solid ${variantBorders[variant]}`,
        boxShadow: '0 4px 16px rgba(0, 0, 0, 0.06)',
      }}
      role="alert"
      aria-live="polite"
    >
      <p className="text-sm font-medium text-white/90 flex-1 text-center">
        {message}
        {ctaLabel && ctaHref && (
          <a
            href={ctaHref}
            className="ml-3 underline underline-offset-2 hover:text-white
                       transition-colors duration-150"
          >
            {ctaLabel}
          </a>
        )}
      </p>

      <button
        onClick={() => setDismissed(true)}
        className="shrink-0 p-1 rounded-md hover:bg-white/10
                   transition-colors duration-150"
        aria-label="Dismiss banner"
      >
        <X className="w-4 h-4 text-white/70" />
      </button>
    </div>
  );
}

The backdrop-blur-md class in Tailwind v4 maps to blur(12px) — right in the sweet spot for a banner. Notice that role="alert" and aria-live="polite" are not optional here. Screen readers need to announce the banner content when it mounts, and skipping accessibility on a component that exists specifically to communicate information is a real problem.

Positioning the Banner: Fixed vs Sticky vs Static

Where you place the banner in the DOM and what CSS position you use will change the entire experience. Static placement is the simplest — the banner scrolls away with the page. Good for marketing sites where the announcement isn't urgent. But for app-level alerts, you usually want it to stay visible.

Fixed positioning (position: fixed; top: 0) keeps the banner on screen at all times but requires you to offset your main content by the banner's height — typically around 44px to 52px depending on your padding. Sticky positioning is the middle ground: it acts like a static element until you scroll, then pins to the top. For a glassmorphism banner, sticky is often the best choice because the blur effect looks great as content scrolls underneath it.

One thing developers often miss: if you're using a fixed navbar, you'll need to stack the banner above it with z-index. Set the banner to z-50 (or higher if your nav is also elevated) and the nav to something lower. If both compete for the same z-index layer, the blur effect on the banner will start clipping through the navbar background in Chrome.

Animation: Slide-In and Fade-Out on Dismiss

A banner that just pops in and vanishes on close feels abrupt. A 200-300ms transition makes the whole interaction feel intentional. You don't need Framer Motion for this — CSS transitions on max-height and opacity handle it fine.

Here's how to add mount/unmount animation without a library:

import { useState, useEffect } from 'react';

export function AnimatedGlassBanner({ message }: { message: string }) {
  const [visible, setVisible] = useState(false);
  const [mounted, setMounted] = useState(true);

  useEffect(() => {
    // Slight delay triggers the CSS transition on mount
    const t = setTimeout(() => setVisible(true), 10);
    return () => clearTimeout(t);
  }, []);

  const handleDismiss = () => {
    setVisible(false);
    setTimeout(() => setMounted(false), 300);
  };

  if (!mounted) return null;

  return (
    <div
      className="overflow-hidden transition-all duration-300 ease-in-out"
      style={{
        maxHeight: visible ? '64px' : '0px',
        opacity: visible ? 1 : 0,
      }}
    >
      <div
        className="w-full px-4 py-3 flex items-center justify-between
                   backdrop-blur-md"
        style={{ background: 'rgba(255, 255, 255, 0.12)' }}
      >
        <span className="text-sm text-white/90 flex-1 text-center">
          {message}
        </span>
        <button onClick={handleDismiss} className="ml-4 text-white/60
          hover:text-white transition-colors">
          ✕
        </button>
      </div>
    </div>
  );
}

The maxHeight trick is reliable but has one limitation: if your banner content is dynamic and wraps to two lines, 64px will clip it. Either calculate the height dynamically with a ref or use grid-template-rows animation instead, which is better supported in 2026 than it was two years ago.

Dark Mode and Theme Switching

Glassmorphism on a dark background needs a different rgba value than on light. On dark UIs, rgba(255, 255, 255, 0.10) to rgba(255, 255, 255, 0.15) reads well. On light backgrounds, you actually get better results flipping to rgba(0, 0, 0, 0.06) for a dark-frosted look, or going higher — rgba(255, 255, 255, 0.65) — for a bright milky glass. If you're building a theme toggle in React, make sure your banner variant accounts for both.

Tailwind's dark: modifier makes this manageable without a JS class swap. You can set dark:bg-white/10 bg-white/60 and let Tailwind handle the rest, as long as you're comfortable relying on named color opacity modifiers instead of raw rgba. For the border, dark:border-white/20 border-black/10 works well.

Compare this against other styles like neumorphism — neumorphic banners are practically impossible to pull off in dark mode because the inset shadow technique relies on a specific mid-tone background. Glassmorphism doesn't have that constraint, which is one of the reasons it's stuck around.

Performance: Does backdrop-filter Kill Your Paint Times?

This is a legitimate concern and worth actually measuring. backdrop-filter creates a new stacking context and triggers compositing. On most modern hardware it's GPU-accelerated and won't hurt performance. But on lower-end Android devices and certain Chromebook configurations, you can see paint jitter — especially if the blurred element overlaps complex gradients or canvas elements.

The quick test: open Chrome DevTools, hit the Rendering panel, enable "Paint flashing". If your banner triggers constant green flashes while static content is underneath it and not changing, you have a compositing problem. The fix is usually adding will-change: backdrop-filter or transform: translateZ(0) to force GPU layer promotion early.

What's the browser support story in 2026? backdrop-filter has full support in Chrome, Edge, Firefox, and Safari — it's no longer a risk. The only remaining gotcha is that it won't work if any ancestor element has overflow: hidden combined with certain transform states. That specific combination can silently disable the blur with no warning, which is frustrating to debug the first time you run into it.

Using Empire UI's Pre-Built Glass Banner Component

If you'd rather not wire all this up from scratch, Empire UI includes a production-ready glassmorphism banner in the best free glassmorphism components collection. The component ships with all three variants (info, warning, success), built-in dismiss state, animation, and dark mode support out of the box. It also integrates with Empire UI's 40-style system, so if you're using the Neobrutalism or Claymorphism themes elsewhere in your app, swapping the banner style is a single prop change.

Installation is straightforward — the component is tree-shakeable and doesn't pull in any runtime dependencies beyond React itself. The blur value, border opacity, and background rgba are all exposed as CSS custom properties so you can tune them in your own stylesheet without forking the component.

One thing worth noting: Empire UI's glass banner is built against Tailwind v4's new @theme system, not the old theme.extend pattern. If you're still on Tailwind v3, you'll need to either upgrade or manually extract the CSS. The Tailwind vs CSS Modules article covers the tradeoffs if you're weighing that decision now.

FAQ

Why isn't my backdrop-filter blur showing up in Firefox?

Firefox requires the layout.css.backdrop-filter.enabled flag to be true — which it has been by default since Firefox 103. If your blur still isn't rendering, check whether an ancestor element has overflow: hidden. That combination silently disables backdrop-filter in Firefox without throwing any errors.

What rgba background value should I use for a glassmorphism banner on a dark theme?

Start with rgba(255, 255, 255, 0.12) for dark backgrounds. If you need more contrast for readability, push it to 0.18. Anything above 0.25 and you lose the transparency effect — at that point you might as well use a solid color with reduced opacity.

How do I prevent the banner from clipping above my sticky navbar?

Set the banner's z-index higher than the navbar. In Tailwind, use z-50 on the banner and z-40 on the navbar. If both are position: fixed, the higher z-index element wins. Also verify your navbar doesn't have overflow: hidden set, which can cause the banner's blur to stop rendering at the navbar boundary.

Can I use glassmorphism banners with a video background?

Yes, and it actually looks great. Video backgrounds are one of the best use cases because the motion behind the frosted glass adds depth. The one performance caveat: test on mobile. If the video and backdrop-filter are both running on the GPU at the same time on low-end devices, you may see frame drops. Consider pausing the video or reducing its resolution on smaller screens.

Is there a way to persist banner dismissal so it doesn't show on every page load?

Use localStorage. When the user dismisses the banner, write a key like banner-dismissed-v1 to localStorage. On mount, check for that key before rendering. The v1 suffix lets you reset dismissal state when you update the banner message — just bump it to v2.

Does backdrop-filter work inside a CSS grid or flexbox container?

Yes, the layout context doesn't affect backdrop-filter. The properties that can break it are: overflow: hidden on an ancestor, filter applied to a parent element, or transform on a parent with certain compositing bugs in older Chrome versions (pre-115). All of those are edge cases in 2026, but worth knowing if you're debugging a broken blur.

Free components in 40 styles
React & Tailwind, copy-paste ready.
Browse →

Read next

Glassmorphism Profile Card: User Avatar ComponentGlassmorphism Bank Card: Credit Card Component in ReactGlassmorphism Card Hover: Blur Depth Change on Mouse EnterCSS backdrop-filter: blur, brightness, saturate and When to Use Each