
Pure CSS Sticky Navbar (No JavaScript, position:sticky)
Published
25 hand-coded CSS sticky navigation patterns for SaaS marketing headers, e-commerce storefronts, documentation shells, article readers, and mobile app views. Covers shrink-on-scroll, hide-on-scroll-down / reveal-on-scroll-up, transparent-to-solid over hero, sticky mega menu, cart-badge header, dual-stacked announcement bar, sidebar table-of-contents, and mobile bottom tab bar with env(safe-area-inset-bottom). 7 Pure CSS + 18 Light JS. Scoped under .sn-NN for no-collision pasting, prefers-reduced-motion guarded, framework-agnostic.
Related15 CSS Navigation Menu Designs28 CSS Hamburger Menus16 CSS Mobile Navigation Patterns32 CSS Tab Designs

Published

space-between, which is why the centre group does not drift every time the CTA text changes.Published

Published

Published

Published

Published

Published

Published

Published

Published

Published

light-dark() function, so a single token definition serves both themes instead of two duplicated blocks.Published

Published

1fr auto 1fr guarantees and flexbox does not.Published

Published

Published

Published

Published

Published
Published

Published

Published

Published

Published
Published
position: sticky is a hybrid: an element behaves as position: relative until its containing block scrolls past a threshold you set (typically top: 0), then it flips to position: fixed and pins to that edge — but ONLY within the bounds of its scroll container / containing block. When the container's bottom scrolls past, the sticky element scrolls away with it. position: fixed, in contrast, pins to the viewport permanently regardless of scroll position or containing block. That difference matters for real product work: a sticky header inside a scrollable article panel stops sticking when the article ends (correct — the header belongs to that panel), while a fixed header overlays every scroll surface on the page (often wrong — it fights modal overlays, embedded scrollers, and any nested scroll container). Sticky also has no layout side-effects when NOT stuck: the reserved space is honored, so content doesn't jump when the flip happens. Every demo in this collection uses position: sticky as the base primitive; fixed is only used for the mobile bottom tab bar (Demo 22) where viewport-pinning IS the intent.overflow: hidden, overflow: auto, or overflow: scroll, that ancestor becomes the sticky element's scroll container — and the sticky no longer sticks against the viewport. It sticks against that shorter ancestor, which usually means it doesn't visibly stick at all. Common triggers: a card wrapper with overflow: hidden to clip rounded corners, a modal body with overflow-y: auto, a Tailwind class like overflow-clip anywhere in the chain. Fix: remove the overflow rule from the ancestor (usually the fastest fix), OR move the sticky element outside the constrained ancestor, OR change the ancestor's overflow to overflow: visible. Debug: in DevTools, right-click the sticky element → Inspect → search up the ancestor chain for overflow:. Every ancestor is a suspect until proven visible. Every demo in this collection ships with the sticky element as a direct child of body or a scroll-clean container so the bug can't reproduce.hidden class that translates the header up out of view, if current < previous → scrolling up → remove the class. Two production refinements: (1) add a minimum scroll threshold (e.g. if (current < 100) return) so the header always shows in the first viewport, and (2) throttle with requestAnimationFrame so the listener never runs more than once per frame. The CSS side is one transform: translateY(-100%); transition: transform 200ms ease-out — no display: none, no height: 0, because those cause layout jank and destroy focus. Modern browsers also support scroll-timeline as a JS-free alternative, but Safari lags on that as of 2026 — the JS approach is universal today.env(safe-area-inset-bottom) function, which returns the OS-reported inset (34px on newer iPhones, 0 on Android and older devices). Demo 22 is the reference — its bottom tab bar uses padding-block-end: env(safe-area-inset-bottom, 0px) so the tabs sit ABOVE the home indicator, not under it. Two setup steps you can't skip: (1) add <meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover"> to your HTML — WITHOUT viewport-fit=cover, iOS shrinks your layout to avoid the safe area and env() returns 0, so the padding does nothing. (2) Provide the fallback 0px in env(safe-area-inset-bottom, 0px) so browsers that don't support env() (very rare in 2026 — only pre-2020 Firefox / Edge) don't fall back to auto, which is invalid on padding. Android + desktop see zero extra padding; iOS with home indicator gets the correct offset. Zero conditional CSS, zero user-agent sniffing.IntersectionObserver with rootMargin tuned to the sticky header's height. Set up one observer with threshold: 0 and rootMargin: '-80px 0px -60% 0px' (top negative offset = sticky header height; bottom -60% = only mark active when the section is in the top 40% of the viewport). Point it at every section. In the callback, on isIntersecting switch, update the nav link's aria-current="true" attribute and let CSS style off that — a[aria-current="true"] { color: var(--accent); border-bottom-color: currentColor }. Never classList.add('active'): aria-current is semantically correct, screen-reader-announced, and doubles as the style hook. Bonus: on nav-link click, smooth-scroll to the target with scrollIntoView({ behavior: 'smooth', block: 'start' }) guarded behind prefers-reduced-motion. The full pattern is 22 lines of JS.<button type="button"> with aria-expanded="false" and aria-controls="mega-panel-id". (2) The mega panel has role="region" + aria-labelledby pointing at the trigger's ID. (3) On open (click OR keydown Enter/Space), flip aria-expanded to true, remove hidden from the panel, move focus to the FIRST link inside the panel — NOT to the panel itself. (4) Escape key closes the panel AND returns focus to the trigger. (5) Click-outside detection via a bubbling document.click listener that checks !panel.contains(e.target) && e.target !== trigger. (6) Tab from the last link inside the panel closes the panel and moves focus to the next nav element after the trigger. Skip focus trap for mega menus — traps belong to modals; menus should be exit-easy via Escape or Tab. Every interactive element inside the panel needs a visible focus ring (:focus-visible). Grid layout inside the panel needs proper heading structure (<h3> per column) so screen readers can navigate by heading..sn-NN selector, every keyframe, every custom property is framework-agnostic. What changes is where the JS lives. React: wrap the scroll listener in useEffect(() => { const cleanup = init(ref.current); return cleanup }, []) so it registers on mount and removes on unmount — never re-register on every render. Vue 3: onMounted(() => init(ref.value)) + onBeforeUnmount for cleanup. Svelte: onMount(() => { init(el); return cleanup }). Next.js App Router: mark the sticky-nav component with 'use client' — sticky positioning is a browser primitive, no SSR value. Tailwind: the class attribute on each demo can be mechanically translated (sticky → sticky, top: 0 → top-0, z-index: 40 → z-40, backdrop-blur → backdrop-blur-md); custom-property tokens become CSS vars in your @theme block. Bootstrap 5: the built-in .sticky-top class covers the base case; layer these demos' custom behaviours on top. Every demo's JS is written as a plain init function (usually 15-40 lines) — strip the IIFE wrapper and call from your framework's lifecycle hook. Zero framework lock-in.32 free CSS accordions covering pure-CSS FAQ blocks with details/summary, mobile navigation menus, e-commerce filter sidebars, nested tree views, exclusive single-open groups and smoothly animated height:auto — plus 26 visual variations in vertical, horizontal and hybrid layouts. Copy-paste HTML and CSS, no JavaScript.
39 hand-coded CSS breadcrumbs — Schema.org BreadcrumbList Microdata + WCAG 2.2 accessible (Google Rich Results eligible), :has() responsive collapse, dark/light theme toggle with color-mix, Shopify/Amazon e-commerce filter chip removal, Vercel/Linear/Notion SaaS dashboard app-shell pattern, Docusaurus/Mintlify sticky scroll-shrink header, Amazon-style dropdown sibling menus, NFT/Web3 holographic shimmer (@property + conic-gradient), SaaS marketing gradient text, Airbnb filter chip with container queries, editorial blog wave underline, developer docs one-click path copy (Clipboard API), and 26 more. Semantic HTML: <nav aria-label='Breadcrumb'> + <ol> + aria-current='page'. Framework-agnostic, MIT-licensed.
26 hand-coded CSS circular and radial menu designs, from the floating-action FAB speed dial and dashboard radial hubs to game-style pie-slice wheels, SVG gooey metaball fans, hexagonal honeycombs, iris-aperture reveals, orbiting satellites on offset-path, compass-rose directional nav, sci-fi HUD rings, glassmorphism popovers built on the native Popover API, vinyl-record players, and full-takeover nebula overlays on native dialog. Every design uses CSS trigonometry (sin(), cos()) or the counter-rotation trick for positioning, ships real keyboard navigation, aria-labels on icon-only buttons, and a prefers-reduced-motion guard. Copy-paste HTML, CSS and vanilla JS with zero framework dependencies — MIT licensed.