Full-width message strip for cookie consent, alerts, and announcements. Sits in the page flow, sticks to the top or bottom of a section, or fixes to the viewport. Visibility and variant are driven by data attributes.
| Class | Description |
|---|---|
| .mdst-banner |
Banner container. Use role="region" with an aria-label for
persistent banners (cookies), or role="status" / role="alert"
for messages that appear at runtime
|
| .mdst-banner-content | Message text. Grows to fill the row and pushes actions to the end |
| .mdst-banner-actions | Group of buttons or links. Wraps below the content on narrow screens |
| .mdst-banner-close | Dismiss button |
| Data Attribute | Description |
|---|---|
| data-state="visible" | Banner is shown and fades in |
| data-state="hidden" | Banner fades out and is removed from layout (display: none) |
| Class | Example |
|---|---|
| .mdst-banner | |
| .mdst-banner--sm |
| Class | Example |
|---|---|
| .mdst-banner--borderless | |
| .mdst-banner--border-top | |
| .mdst-banner--border-bottom | |
|
.mdst-banner--border-start .mdst-banner--border-end |
Any border class removes the other edges. A data-variant accent edge is always kept.
| Class | Description |
|---|---|
| .mdst-banner | In the flow of the page (default) |
|
.mdst-banner--top .mdst-banner--bottom |
Sticks to the top or bottom edge of its section while it scrolls. Place --top as the
section’s first child and --bottom as its last
|
| .mdst-banner--fixed | Fixed to the viewport. Combine with --top or --bottom |
|
.mdst-banner--start .mdst-banner--end |
With --fixed: a compact card in the start or end corner instead of full width. Goes full
width below 30rem
|
| --mdst-banner-offset |
Distance from the edge. Defaults to 0 for sticky and
var(--mdst-space-md) for fixed. Set to 0 for an edge-to-edge
fixed banner
|
| --mdst-banner-width | Maximum width of a corner banner. Defaults to 28rem |
Scroll this section. The banners stay attached to its top and bottom edges but never leave it.
Sticky banners take up space in the layout, so they never overlap the first or last line of content.
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.
Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
A fixed corner cookie banner (--fixed --bottom --end --sm) wired to Google Analytics
Consent Mode. The
JavaScript below is not part of modest-ui — it’s an example of the logic you’d add where you use
the banner. The CSS only responds to data-state. This demo doesn’t load Google Analytics; the
calls it would send are logged below instead.
Demo source
<!-- 1. In <head>, before the GA tag: deny everything by default -->
<script>
window.dataLayer = window.dataLayer || [];
function gtag() { dataLayer.push(arguments); }
gtag("consent", "default", {
ad_storage: "denied",
ad_user_data: "denied",
ad_personalization: "denied",
analytics_storage: "denied",
});
</script>
<script async src="https://www.googletagmanager.com/gtag/js?id=G-XXXXXXXXXX"></script>
<script>
gtag("js", new Date());
gtag("config", "G-XXXXXXXXXX");
</script>
<!-- 2. The banner, hidden until we know there's no stored choice -->
<div class="mdst-banner mdst-banner--sm mdst-banner--fixed mdst-banner--bottom mdst-banner--end"
id="cookie-banner" data-state="hidden"
role="region" aria-label="Cookie consent">
<p class="mdst-banner-content">
We use Google Analytics cookies to see how the site is used.
<a href="/privacy/">Privacy</a>
</p>
<div class="mdst-banner-actions">
<button class="mdst-button mdst-button--sm mdst-button--inverted" data-consent="granted">Accept</button>
<button class="mdst-button mdst-button--sm mdst-button--ghost" data-consent="denied">Decline</button>
</div>
</div>
<!-- 3. Show the banner, store the choice, update GA -->
<script>
const STORAGE_KEY = "cookie-consent";
const banner = document.querySelector("#cookie-banner");
function readConsent() {
try { return localStorage.getItem(STORAGE_KEY); } catch { return null; }
}
function applyConsent(value) {
gtag("consent", "update", {
ad_storage: value,
ad_user_data: value,
ad_personalization: value,
analytics_storage: value,
});
}
function choose(value) {
try { localStorage.setItem(STORAGE_KEY, value); } catch {}
applyConsent(value);
banner.setAttribute("data-state", "hidden");
}
banner.querySelectorAll("[data-consent]").forEach((btn) => {
btn.addEventListener("click", () => choose(btn.dataset.consent));
});
const stored = readConsent();
if (stored) {
applyConsent(stored);
} else {
banner.setAttribute("data-state", "visible");
}
</script>
<!-- In the flow of the page -->
<div class="mdst-banner" data-variant="info" role="region" aria-label="Announcement">
<div class="mdst-banner-content">Scheduled maintenance on Sunday.</div>
<button class="mdst-banner-close" aria-label="Dismiss">✕</button>
</div>
<!-- Sticky to the top of a scrolling section -->
<section style="overflow-y: auto">
<div class="mdst-banner mdst-banner--top mdst-banner--border-bottom">...</div>
...
</section>
<!-- Fixed to the bottom of the viewport (cookie consent) -->
<div class="mdst-banner mdst-banner--fixed mdst-banner--bottom"
data-state="visible" role="region" aria-label="Cookie consent">
<div class="mdst-banner-content">We use analytics cookies.</div>
<div class="mdst-banner-actions">
<button class="mdst-button mdst-button--sm">Reject</button>
<button class="mdst-button mdst-button--sm mdst-button--inverted">Accept</button>
</div>
</div>
<!-- Compact card fixed to the bottom-right corner -->
<div class="mdst-banner mdst-banner--sm mdst-banner--fixed mdst-banner--bottom mdst-banner--end"
data-state="visible" role="region" aria-label="Cookie consent">...</div>
<!-- Edge-to-edge fixed banner -->
<div class="mdst-banner mdst-banner--fixed mdst-banner--top" style="--mdst-banner-offset: 0">...</div>
<!-- Example JS -->
<script>
function showBanner(el) {
el.setAttribute("data-state", "visible");
}
function hideBanner(el) {
el.setAttribute("data-state", "hidden");
}
</script>