20 CSS Banners & Alerts

20 hand-coded CSS banner and alert bars for e-commerce storefronts, SaaS dashboards, and compliance-bound products. Covers GDPR cookie consent with a preferences panel, promo bars with countdown timers, free-shipping progress, app-install banners, incident status bars, age gates, inline form validation, and a text-wrapping lab for the unbreakable string that breaks most banners. Correct role="alert" and aria-live semantics throughout. Scoped under .ba-NN for no-collision pasting, prefers-reduced-motion guarded, framework-agnostic.

8 pure CSS12 light JSPublished 11 new designs · Updated on

Related30 CSS Notifications28 CSS Close Buttons36 CSS Modals30 CSS Badges

GDPR Cookie Consent Banner with Preferences — preview
01 / 20Light JSNEW

GDPR Cookie Consent Banner with Preferences

A consent card that actually satisfies the thing regulators care about: reject is as easy as accept, and every optional purpose has its own switch. The preferences drawer animates open from height:0 to height:auto using ::details-content and interpolate-size:allow-keywords, the live "3 of 4 purposes allowed" readout is pure CSS counters reading :has(:checked), and the card enters and leaves with @starting-style + transition-behavior:allow-discrete so there is no JavaScript animation code at all.

Published

Promo Bar with Countdown Timer — preview
02 / 20Light JSNEW

Promo Bar with Countdown Timer

A sale bar where the digits are rendered by CSS counters, not text nodes: JavaScript writes a single integer into a custom property and CSS prints it with counter(v, decimal-leading-zero). That makes the flip animation a registered-property transition, keeps the DOM untouched sixty times a minute, and gives you a countdown that never reflows because every cell is tabular-nums in a fixed ch box.

Published

Free Shipping Threshold Progress Bar — preview
03 / 20Light JSNEW

Free Shipping Threshold Progress Bar

The single highest-converting bar in e-commerce, built properly: a real role="progressbar" with aria-valuenow, milestone markers that light up as you cross them, an animated gradient fill driven by one registered percentage, and an unlocked state that the CSS derives itself with :has() — no state class juggling in JavaScript.

Published

App Install Smart Banner — preview
04 / 20Light JSNEW

App Install Smart Banner

One markup block that is a slim top bar on a laptop and a bottom sheet on a phone, decided by a container query rather than a user-agent sniff. The install button is a small state machine — idle, installing, installed — animated with a registered-property ring and a check that draws itself, and the whole banner remembers a dismissal so it does not nag.

Published

Maintenance and Incident Status Bar — preview
05 / 20Light JSNEW

Maintenance and Incident Status Bar

A status bar that a real on-call engineer could ship: four severity levels driven entirely by data-severity, a component-scoped update timeline that expands in place, per-service health rows, and an aria-live region that speaks once per state change instead of on every repaint. Change one attribute and the palette, icon, label and pulse all follow.

Published

Newsletter Signup Bar — preview
06 / 20Light JSNEW

Newsletter Signup Bar

Two lessons in one component. First, a close button that needs zero JavaScript: a hidden checkbox plus :has(:checked) removes the bar and animates it out with allow-discrete. Second, validation that only speaks when it should — :user-invalid waits until the field has actually been used, so nobody gets shouted at for an empty input they have not filled in yet.

Published

Age Verification Gate Banner — preview
07 / 20Light JSNEW

Age Verification Gate Banner

An age gate that doubles as a four-variant semantic alert system. One .ba-07__alert class plus data-variant="info | success | warning | danger" drives colour, icon and role — the Bootstrap idea rebuilt on custom properties and oklch(), with the roles wired correctly (role="alert" for errors, role="status" for confirmations).

Published

Limited Stock Urgency Banner — preview
08 / 20Light JSNEW

Limited Stock Urgency Banner

A scarcity banner wired to a proper top-right toast stack: toasts enter with @starting-style, stack with a --i index, run their own auto-dismiss timer as a CSS animation, and get removed on animationend — so the timing lives in the stylesheet where a designer can tune it, not buried in a setTimeout.

Published

Live Event Announcement Banner — preview
09 / 20Light JSNEW

Live Event Announcement Banner

A live bar that behaves like a broadcast lower-third: a pulsing LIVE badge built from two rings on one element, a seamless CSS ticker that pauses on hover and on keyboard focus, a viewer counter with tabular-nums so it never jitters, and new updates flipping into a polite live region — with a static, fully readable fallback under prefers-reduced-motion.

Published

Browser Upgrade Notice Bar — preview
10 / 20Light JSNEW

Browser Upgrade Notice Bar

Material 3's floating alert, rebuilt from first principles: elevation as tokens, a state layer instead of hover colours, a ripple driven by a registered length, and the M3 shape scale as custom properties. It carries a real job — telling someone their browser is two years out of date and what breaks because of it — with an update flow that reports progress instead of spinning forever.

Published

Geo and Currency Switcher Bar — preview
11 / 20Light JSNEW

Geo and Currency Switcher Bar

The bar every international store needs, wearing a liquid-glass treatment that holds up in 2027: layered backdrop-filter blur and saturation, a specular edge built from a masked gradient border, and a slow sweep of highlight across the surface. Change country and every price on the page re-formats through Intl.NumberFormat — real currency behaviour, not a symbol swap.

Published

Sticky Top Announcement Bar — preview
12 / 20Pure CSS

Sticky Top Announcement Bar

A sticky bar that condenses as you scroll with zero JavaScript. animation-timeline: scroll() drives the height, padding and type scale from scroll position, a second timeline paints the reading-progress line, and a hidden checkbox plus :has() gives it a real close button. Scroll the demo to watch the bar compress and the progress line fill.

PublishedUpdated

Cookie Consent Banner Bottom Bar — preview
13 / 20Pure CSS

Cookie Consent Banner Bottom Bar

The utilitarian counterpart to demo 01: a bottom bar with no JavaScript whatsoever. Accept, reject and a settings disclosure are all CSS state — :has(:checked) for the dismiss, <details> for the policy detail — set in a mono/system type pairing with a hard 3:1 border and env(safe-area-inset-bottom) so it clears the iOS home indicator.

PublishedUpdated

Inline Form Validation Alert — preview
14 / 20Light JS

Inline Form Validation Alert

Validation done to the WCAG playbook, with modern CSS doing most of the work: :user-invalid for humane timing, :has() to style the whole field group from the input's state, a live password strength meter, field-sizing:content on the textarea, and an error summary at the top whose links move focus straight to the offending field.

PublishedUpdated

Alert Banner with Icon Alignment — preview
15 / 20Pure CSS

Alert Banner with Icon Alignment

The alignment problem every design system hits: an icon next to text that must stay optically centred on the first line, not on the whole block, and not drift when the text wraps to three lines. Solved properly with the lh unit — calc((1lh - var(--icon)) / 2) — and shown against the two wrong ways so the difference is impossible to unsee.

PublishedUpdated

Left Border Accent Alert — preview
16 / 20Pure CSS

Left Border Accent Alert

The quietest alert that still reads at a glance: an accent stripe on the leading edge, a hairline everywhere else, and type doing the rest. Built with border-inline-start rather than border-left, so it mirrors correctly in Arabic and Hebrew with no extra CSS, and each of the four variants can be dismissed with pure CSS.

PublishedUpdated

Diagonal Stripe Promo Banner — preview
17 / 20Pure CSS

Diagonal Stripe Promo Banner

Diagonal geometry done three ways in one banner: a hazard-stripe field from repeating-linear-gradient that scrolls on a registered angle, a hard slash split cut with clip-path, and a rotating conic badge — all pure CSS, all GPU-cheap, and all pausing under prefers-reduced-motion. The promo code reveals with a checkbox, no script.

PublishedUpdated

Full Width Hero Banner — preview
18 / 20Pure CSS

Full Width Hero Banner

A hero that holds up on a 320px phone and a 5K display from the same markup: <picture> art direction so the crop changes rather than shrinks, a two-stop scrim tuned for text contrast instead of decoration, fluid type on cqi units, and a scroll-driven parallax that is declared, not scripted.

PublishedUpdated

Animated Gradient Border Alert — preview
19 / 20Pure CSS

Animated Gradient Border Alert

The rotating gradient border, done the correct way: a registered <angle> so the conic gradient actually interpolates, and mask-composite:exclude so the fill stays a true 1.5px ring instead of a padded box faking it. Three states — processing, ready, failed — plus a soft outer bloom that inherits the same angle.

PublishedUpdated

Long Text Wrapping Banner — preview
20 / 20Pure CSS

Long Text Wrapping Banner

A live laboratory for the bug that breaks more banners than any other: one unbreakable string. Switch between five wrapping strategies with the radio group and watch the same hostile content — a 96-character support URL, a German compound noun, a wallet address — behave completely differently. Everything is CSS: the radios drive :has(), and the expand animates height:auto via interpolate-size.

PublishedUpdated

FAQ

Frequently asked questions

How do I build a GDPR cookie banner that is actually compliant?
The visual banner is the easy part; the compliance is in the behaviour. Four requirements decide whether a consent banner does its job. Reject must be as easy as accept — a prominent "Accept all" beside a buried text link is exactly what regulators have fined companies over, so both actions need equal visual weight at the top level. No pre-ticked boxes: every non-essential category must default to off, since silence is not consent under GDPR Article 4(11). Granular categories are required, not optional — analytics, marketing and functional cookies each need their own toggle, which is why this collection's version ships a real preferences panel rather than a single accept button. And no scripts may fire before consent: the banner must gate the actual loading, so putting your analytics tag in the head and showing a banner underneath it is theatre. Technically the panel is a <dialog> or a disclosure with real <input type="checkbox"> controls, the choice persists in localStorage, and the whole thing carries role="dialog" with a labelled heading. Demo 01 is the reference. This is not legal advice — the pattern is sound, the specific categories are yours to determine.
What is the correct ARIA markup for an alert banner?
It depends entirely on whether the message interrupts. Use role="alert" for something the user must know immediately — a failed payment, a validation error, a session about to expire. It implies aria-live="assertive", which interrupts whatever the screen reader is currently saying, and that is exactly why it should be rare. Use role="status" (implying aria-live="polite") for everything else: a saved confirmation, a promo bar, a shipping-progress update. Polite waits for a natural pause, which is the right default. Three details people miss. The live region must exist in the DOM before the message is injected — inserting the whole container at once often produces no announcement at all, so render an empty region and fill it. A purely decorative or promotional banner needs neither role; marking a sale bar as an alert is announcement spam. And a dismiss button must be a real <button> with an accessible name like "Dismiss announcement", not a bare × character, which screen readers read as "times".
How do I make a sticky banner that does not break the page?
Sticky and fixed banners cause more layout bugs than any other banner property, in three predictable ways. First, a position: fixed bar overlaps content because it is out of flow — the page needs compensating padding-block-start equal to the bar height, ideally driven by a custom property so one value updates both. position: sticky avoids this entirely by staying in flow, and is the better default for a top announcement bar. Second, stacking: a sticky bar must sit above page content but below modals and their backdrops, so give it a deliberate z-index from a documented scale rather than reaching for 9999. Third, sticky silently fails when any ancestor has overflow: hidden, auto or scroll — that ancestor becomes the scroll port and the bar stops sticking, which is why overflow-x: clip is the right way to kill horizontal scroll on a wrapper. On mobile, respect the safe area with padding-block-end: env(safe-area-inset-bottom) on bottom-anchored bars, or the home indicator will sit on your buttons.
How should a banner handle very long text and unbreakable strings?
This is the bug that breaks more banners than any other, and it is worth understanding rather than pasting a fix. A 96-character support URL, a German compound noun or a wallet address has no break opportunity, so the browser lets it overflow rather than break a word. Four properties, in escalating order. overflow-wrap: break-word breaks a long word only when it would otherwise overflow — the safest default for body copy. overflow-wrap: anywhere is more aggressive and also affects intrinsic sizing, which matters inside a flex or grid item that would otherwise refuse to shrink. word-break: break-all breaks anywhere regardless of need and is almost always too blunt for prose. hyphens: auto with a lang attribute adds real hyphenation where the browser has a dictionary. The structural half matters just as much: a flex or grid child defaults to min-width: auto, meaning "never shrink below my content", so long text forces the whole column open. Adding min-width: 0 is what actually lets the text wrap. Demo 20 is a live laboratory comparing all five strategies.
How do I build a promo bar with a countdown that does not mislead?
The mechanics are simple and the ethics are where teams get into trouble. Technically: render the deadline as a real <time datetime> element so the honest value is in the markup, compute remaining time from a server-provided timestamp rather than the visitor's clock, and update with a single setInterval that writes to a live region marked aria-live="off" — a countdown that announces every second is torture for screen reader users, so expose the deadline as text instead. Use font-variant-numeric: tabular-nums so digits do not jitter as they change. On the ethics: a countdown that resets when the visitor returns, or that counts down to a deadline which never arrives, is a dark pattern and is explicitly targeted by the EU Digital Services Act and the FTC's rules on deceptive urgency. If the sale genuinely ends Sunday, the timer is useful information. If it resets per session, you are manufacturing pressure, and increasingly that is a legal exposure rather than a growth tactic. Demo 02 ships the honest version.
Which banner belongs at the top of the page and which at the bottom?
Position communicates priority, and getting it wrong costs conversions in both directions. Top bars are for things that change the visitor's understanding of the whole page: an active sale, a shipping deadline, a service incident, a region or currency mismatch. They are seen immediately, which is the point, and also why an unimportant message at the top trains people to ignore the region entirely. Bottom bars suit anything that should not compete with the content on arrival — cookie consent above all, since it is a legal necessity rather than a message anyone wants, and bottom placement is now the convention precisely because it does not obstruct the first read. Inline banners, sitting in the content flow, are for context-specific messages: a stock warning next to the product, a validation error next to the field, an upgrade prompt next to the limit that was hit. The general rule is that the further a message is from its trigger, the more work the reader has to do to connect them. Never stack more than one persistent bar; two competing bars read as a broken site.
Are these banners accessible and Core Web Vitals safe?
Yes, and banners are one of the few components where a mistake shows up directly in a Core Web Vitals score. Cumulative Layout Shift is the risk: a banner injected after first paint pushes the whole page down, and that is a textbook CLS failure. The fix is to reserve the space before the banner renders, or to use a sticky bar that participates in normal flow, or to animate it in with transform rather than by changing height. Every demo here follows one of those three. On accessibility, roles follow the interrupt/no-interrupt distinction described above, dismiss controls are real buttons with meaningful accessible names and at least 44 by 44 CSS pixel hit targets, focus rings are visible at 2px minimum with 3:1 contrast, and a dismissed banner returns focus somewhere sensible rather than dropping it to the document. Colour is never the only signal — every semantic state pairs its colour with an icon and a word, which is what makes success and error distinguishable to colour-blind users. All motion is guarded by prefers-reduced-motion: reduce, and any auto-dismissing message stays long enough to read or offers a way to keep it.
How do these banners port to React, Vue, Svelte and Tailwind?
The eight pure-CSS banners port unchanged — copy the markup and the scoped .ba-NN rules and they behave identically anywhere. The twelve with JavaScript are all small, dependency-free init functions, and the porting work is lifecycle placement plus one architectural decision: banner visibility is state, so it belongs in your app's state layer rather than in a DOM class toggle. In React that means const [dismissed, setDismissed] = useState(() => localStorage.getItem(key) === '1') and conditional rendering, with useEffect only for the countdown interval and its cleanup — forgetting to clear that interval is the classic leak. Vue uses ref plus onBeforeUnmount, Svelte a store plus onMount returning cleanup. In the Next.js App Router these need 'use client', and consent banners specifically should render on the client only to avoid a hydration mismatch between the server's unknown state and the browser's stored choice. For Tailwind v4, colour and spacing map to utilities directly, while the keyframes, @property registrations and safe-area insets belong in a @theme block.

Related collections

30 CSS Badges preview

30 CSS Badges

30 hand-coded CSS badges for status indicators, notifications, membership tiers, live-data displays, and SEO / DevOps / financial dashboards — upload progress, typing indicator, transit line status, Core Web Vitals, ECG heartbeat, CI/CD build pipeline, countdown ring, live price ticker, keycap shortcut, wax seal, conference lanyard, and holographic collectibles. Copy-paste HTML and CSS, WCAG 2.2 accessible, MIT licensed.

25 CSS Blockquotes preview

25 CSS Blockquotes

25 hand-coded CSS blockquote designs for SaaS testimonials, editorial pull quotes, documentation callouts, and developer engineering blogs: large decorative quotation marks with ::before / ::after glyphs, Tailwind CSS blockquote component, pure CSS callout / admonition boxes for Docusaurus / Mintlify / Nextra documentation, responsive center-aligned quote banners, modern minimalist left-border editorial style, animated CSS gradient border, classic left border, brutalist high-contrast, glassmorphism frosted card, glowing neon border, article pull quote with text-wrap, speech bubble chat-style, thick vertical accent rail, customer testimonial cards with avatar and star rating, inline highlighted text, marker highlight underline, floating drop-shadow, aurora gradient background, Twitter / X card style, multi-column newspaper editorial, IDE code-comment style, Markdown-compatible defaults, JavaScript testimonial quote slider, expandable read-more, and one-click copy button. 22 Pure CSS + 3 Light JS (slider, expand, copy). Every design scoped under .bq-NN prefix for no-collision pasting, prefers-reduced-motion guarded per WCAG 2.3.3, WCAG 2.2 AA accessible, framework-agnostic (React, Vue, Svelte, Astro, Next.js, Nuxt, Remix, SvelteKit).

15 CSS Chat Bubbles preview

15 CSS Chat Bubbles

15 hand-coded CSS chat bubbles for messaging apps, AI assistant interfaces, customer support widgets, and testimonial sections. Covers the pure-CSS bubble with a pseudo-element arrow tail, iMessage and WhatsApp Web recreations, a ChatGPT-style AI response bubble, a Tailwind variant, animated typing indicators, avatar-paired bubbles, a live support widget, bordered and comic-book speech bubbles, image attachments, voice messages with a scrubable waveform, a full chat screen built on flexbox, thought bubbles, and a testimonial card built from a speech bubble. Every bubble is scoped under a .cb-NN prefix for no-collision pasting, uses real semantic markup rather than div soup, guards prefers-reduced-motion, and ports unchanged to React, Vue, Svelte, Astro, Next.js and Tailwind.

Search CodeFronts

Loading…