25 CSS Sticky Navigations
02 / 25

All 25 in 25 CSS Sticky Navigations

  1. 01Pure CSS Sticky Navbar (No JavaScript, position:sticky)
  2. 02Sticky Top Header with Flexbox Layout (Logo · Links · CTA)
  3. 03Responsive Sticky Navbar with Hamburger Menu Toggle
  4. 04Sticky Header with Full-Bleed Bar and Container Max-Width Content
  5. 05Floating Glass Pill Sticky Navbar (Rounded, Backdrop Blur, Elevation)
  6. 06Sticky Header Shrink on Scroll (80px → 50px, Compact State)
  7. 07Sticky Nav with Directional Hide (Scroll Down) and Reveal (Scroll Up)
  8. 08Transparent-to-Solid Sticky Header on Hero Scroll
  9. 09Sticky Header with Article Reading Progress Bar Underneath
  10. 10Sticky Header with SVG Logo Wordmark → Mark-Only Morph on Scroll
  11. 11Sticky Navbar with Underline-Slide Active Link Indicator
  12. 12Sticky Header with Theme-Aware Token Swap (Light/Dark)
  13. 13Minimalist Sticky Header with Single High-Emphasis CTA
  14. 14Split Sticky Navbar with Centered Brand Logo (Links Left + Right)
  15. 15Neumorphic Sticky Navigation Bar (Soft-UI Inset/Outset Shadows)
  16. 16Sticky Header with Multi-Column Mega Menu Dropdown
  17. 17E-Commerce Sticky Header with Search Bar and Cart Badge Counter
  18. 18Sticky Announcement Bar Stacked Above Sticky Nav (Dual-Stick)
  19. 19Sticky Sub-Navigation Bar Locked Under the Main Sticky Header
  20. 20Sticky Header with Icon-to-Full-Width Search Expand on Click
  21. 21Sticky Sidebar Table of Contents That Follows a Long Article
  22. 22Mobile Bottom Tab Bar with Safe-Area Inset Padding
  23. 23Active-Section Scrollspy Highlighting in a Sticky Navbar
  24. 24Sticky Horizontal Scroll Tab Bar with Snap Points
  25. 25Sticky Header with Social Share Icons for Articles
Pure CSSMIT licensed

Sticky Top Header with Flexbox Layout (Logo · Links · CTA)

The three-zone product header — brand locked left, navigation held in the true optical centre, action button pinned right — that stays balanced when the link set grows or a locale doubles the label lengths. Built on auto margins rather than space-between, which is why the centre group does not drift every time the CTA text changes.

Live Demo
Plain CSSTailwind v4
Try it

Complete Tailwind CSS landing page templates in HTML, React, Next.js and Vue · from $49

Cruip · Affiliate link

Browse Cruip templates

The code

<section class="sn-02" aria-label="Flexbox sticky header demo">
    <header class="sn-02__bar">
      <div class="sn-02__zone sn-02__zone--start">
        <a class="sn-02__brand" href="#sn-02-s1"><span class="sn-02__logo" aria-hidden="true">◈</span>Halcyon Labs</a>
      </div>
      <nav class="sn-02__nav" aria-label="Primary">
        <a class="sn-02__link" href="#sn-02-s1" aria-current="page">Platform</a>
        <a class="sn-02__link" href="#sn-02-s2">Solutions</a>
        <a class="sn-02__link" href="#sn-02-s3">Developers</a>
        <a class="sn-02__link" href="#sn-02-s4">Company</a>
      </nav>
      <div class="sn-02__zone sn-02__zone--end">
        <a class="sn-02__ghost" href="#sn-02-s3">Sign in</a>
        <a class="sn-02__cta" href="#sn-02-s4">Book a demo</a>
      </div>
    </header>
  <div class="sn-02__intro">
    <div class="sn-02__introin">
    <p class="sn-02__intro-eyebrow">flex · gap · margin-inline: auto</p>
    <h2 class="sn-02__intro-title">Logo left · links centre · CTA right</h2>
    </div>
  </div>
    <main>
      <section class="sn-02__sec" id="sn-02-s1">
        <p class="sn-02__kicker">Platform</p>
        <h3 class="sn-02__h">Equal side zones, true centre</h3>
        <p class="sn-02__p">Both outer zones carry <code>flex:1 1 0</code>, so they always claim the same share of free space. The nav lands on the optical centre line whatever the button says.</p>
      </section>
      <section class="sn-02__sec" id="sn-02-s2">
        <p class="sn-02__kicker">Solutions</p>
        <h3 class="sn-02__h">Why not space-between</h3>
        <p class="sn-02__p">Space-between positions the middle group as a side effect of its neighbours' widths. Translate the CTA into German and the nav visibly drifts.</p>
      </section>
      <section class="sn-02__sec" id="sn-02-s3">
        <p class="sn-02__kicker">Developers</p>
        <h3 class="sn-02__h">min-width: 0 stops the overflow</h3>
        <p class="sn-02__p">A flex item refuses to shrink below its content by default. That single rule is behind most mystery horizontal scrollbars in headers.</p>
      </section>
      <section class="sn-02__sec" id="sn-02-s4">
        <p class="sn-02__kicker">Company</p>
        <h3 class="sn-02__h">Gap, not margins</h3>
        <p class="sn-02__p">Spacing that lives between items survives reordering, RTL mirroring and CMS-driven link lists with no trailing whitespace to clean up.</p>
      </section>
    </main>
  <footer class="sn-02__pagefoot">
    <p class="sn-02__pagefootin">Sticky navigation pattern 02 of 25 · codefronts.com</p>
  </footer>
</section>
.sn-02 {
  width: 100%;
  min-height: 100vh;
  min-height: 100svh;
  display: block;
  background: var(--sn-02-bg);
  --sn-02-bg: oklch(0.19 0.024 260);
  --sn-02-surface: oklch(0.235 0.028 262);
  --sn-02-ink: oklch(0.97 0.006 260);
  --sn-02-mut: oklch(0.73 0.018 260);
  --sn-02-acc: oklch(0.83 0.16 172);
  --sn-02-line: oklch(1 0 0/.1);
  --sn-02-h: 66px;
  font-family: 'Segoe UI',system-ui,-apple-system,sans-serif;
  color: var(--sn-02-ink);
  -webkit-font-smoothing: antialiased;
}

.sn-02 *,
.sn-02 *::before,
.sn-02 *::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

.sn-02__eyebrow,
.sn-02__intro-eyebrow {
  font-family: ui-monospace,Menlo,Consolas,monospace;
  font-size: 11.5px;
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--sn-02-acc);
}

.sn-02__title,
.sn-02__intro-title {
  font-size: clamp(23px,3.5vw,37px);
  line-height: 1.06;
  letter-spacing: -.03em;
  font-weight: 700;
  text-wrap: balance;
}

.sn-02__bar {
  position: sticky;
  top: 0;
  z-index: 10;
  display: flex;
  align-items: center;
  gap: clamp(10px,2vw,24px);
  min-height: var(--sn-02-h);
  padding: 0 clamp(14px,2.4vw,24px);
  background: color-mix(in oklch,var(--sn-02-surface) 82%,transparent);
  backdrop-filter: blur(16px) saturate(1.4);
  -webkit-backdrop-filter: blur(16px) saturate(1.4);
  border-bottom: 1px solid var(--sn-02-line);
}

.sn-02__zone {
  display: flex;
  align-items: center;
  gap: 10px;
  flex: 1 1 0;
  min-width: 0;
}

.sn-02__zone--end {
  justify-content: flex-end;
}

.sn-02__brand {
  display: flex;
  align-items: center;
  gap: 9px;
  min-width: 0;
  font-size: 15px;
  font-weight: 700;
  letter-spacing: -.02em;
  color: inherit;
  text-decoration: none;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.sn-02__logo {
  flex: none;
  display: grid;
  place-items: center;
  width: 26px;
  height: 26px;
  border-radius: 8px;
  font-size: 13px;
  color: oklch(0.19 0.024 260);
  background: var(--sn-02-acc);
}

.sn-02__nav {
  display: flex;
  align-items: center;
  gap: 2px;
  flex: none;
}

.sn-02__link {
  display: flex;
  align-items: center;
  min-height: 38px;
  padding: 0 12px;
  border-radius: 9px;
  font-size: 13.6px;
  font-weight: 600;
  color: var(--sn-02-mut);
  text-decoration: none;
  white-space: nowrap;
  transition: color .18s ease,background .18s ease;
}

.sn-02__link:hover {
  color: var(--sn-02-ink);
  background: oklch(1 0 0/.06);
}

.sn-02__link[aria-current="page"] {
  color: var(--sn-02-acc);
  background: color-mix(in oklch,var(--sn-02-acc) 14%,transparent);
}

.sn-02__ghost {
  display: flex;
  align-items: center;
  min-height: 38px;
  padding: 0 12px;
  border-radius: 9px;
  font-size: 13.4px;
  font-weight: 600;
  color: var(--sn-02-mut);
  text-decoration: none;
  white-space: nowrap;
  transition: color .18s ease;
}

.sn-02__ghost:hover {
  color: var(--sn-02-ink);
}

.sn-02__cta {
  display: flex;
  align-items: center;
  min-height: 38px;
  padding: 0 16px;
  border-radius: 9px;
  font-size: 13.4px;
  font-weight: 670;
  color: oklch(0.19 0.024 260);
  background: var(--sn-02-acc);
  text-decoration: none;
  white-space: nowrap;
  box-shadow: 0 6px 18px -8px var(--sn-02-acc);
  transition: transform .18s ease,filter .18s ease;
}

.sn-02__cta:hover {
  transform: translateY(-1px);
  filter: brightness(1.06);
}

.sn-02__link:focus-visible,
.sn-02__ghost:focus-visible,
.sn-02__cta:focus-visible,
.sn-02__brand:focus-visible {
  outline: 2px solid var(--sn-02-acc);
  outline-offset: 2px;
}

.sn-02__sec {
  scroll-margin-top: var(--sn-02-h);
  display: grid;
  gap: 10px;
  align-content: center;
  min-height: 70svh;
  padding: clamp(24px,4vw,46px);
  border-bottom: 1px solid var(--sn-02-line);
}

.sn-02__sec:nth-child(even) {
  background: oklch(1 0 0/.025);
}

.sn-02__kicker {
  font-family: ui-monospace,Menlo,Consolas,monospace;
  font-size: 11px;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--sn-02-acc);
}

.sn-02__h {
  font-size: clamp(19px,2.6vw,26px);
  line-height: 1.16;
  letter-spacing: -.025em;
  font-weight: 690;
  text-wrap: balance;
}

.sn-02__p {
  max-width: 52ch;
  font-size: 14.8px;
  line-height: 1.64;
  color: var(--sn-02-mut);
  text-wrap: pretty;
}

.sn-02__p code {
  font-family: ui-monospace,Menlo,Consolas,monospace;
  font-size: .85em;
  background: oklch(1 0 0/.09);
  padding: 2px 6px;
  border-radius: 5px;
}

@media (max-width: 780px) {
  .sn-02__nav {
    display: none;
  }

  .sn-02__zone {
    flex: 0 1 auto;
  }

  .sn-02__zone--end {
    margin-inline-start: auto;
  }
}

@media (max-width: 480px) {
  .sn-02__ghost {
    display: none;
  }
}

@media (prefers-reduced-motion: reduce) {
  .sn-02 * {
    transition-duration: .01ms !important;
  }
}
/* ── full-page mode: the demo IS the document, so sticky pins against the real viewport ── */

:root:has(.sn-02) {
  scroll-behavior: smooth;
  scroll-padding-top: calc(var(--sn-02-h) + 10px);
}

@media (prefers-reduced-motion: reduce) {
  :root:has(.sn-02) {
    scroll-behavior: auto;
  }
}

.sn-02__bar,
.sn-02__barin {
  padding-inline: max(clamp(16px,3vw,26px),calc((100% - 1280px)/2));
}

.sn-02__sec {
  padding-inline: max(clamp(22px,5vw,44px),calc((100% - 1120px)/2));
}

.sn-02__intro {
  display: grid;
  place-items: center;
  min-height: min(90svh,900px);
  padding: clamp(56px,12vh,150px) clamp(20px,5vw,48px) clamp(40px,8vh,90px);
  background: radial-gradient(85% 55% at 50% 0%,oklch(0.3 0.06 190/.5),transparent);
}

.sn-02__introin {
  display: grid;
  justify-items: center;
  gap: clamp(12px,1.8vw,18px);
  text-align: center;
  max-width: 26ch;
}

.sn-02__intro-eyebrow {
  font-size: clamp(11px,1.1vw,13px);
}

.sn-02__intro-title {
  font-size: clamp(38px,7.5vw,86px);
  line-height: .98;
  letter-spacing: -.04em;
  max-width: none;
}

.sn-02__intro-sub {
  font-size: clamp(15px,1.7vw,19px);
  line-height: 1.55;
  max-width: 46ch;
}

.sn-02__intro::after {
  content: "Scroll — the bar stays";
  justify-self: center;
  margin-block-start: clamp(20px,4vh,44px);
  padding: 9px 18px;
  border: 1px solid var(--sn-02-line);
  border-radius: 99px;
  font-family: ui-monospace,Menlo,Consolas,monospace;
  font-size: 11.5px;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--sn-02-mut);
  animation: sn-02-cue 2.6s ease-in-out infinite;
}

@keyframes sn-02-cue {
  0%,
    100% {
    transform: translateY(0);
    opacity: .75;
  }

  50% {
    transform: translateY(5px);
    opacity: 1;
  }
}

@media (prefers-reduced-motion: reduce) {
  .sn-02__intro::after {
    animation: none;
  }
}

.sn-02__pagefoot {
  display: grid;
  place-items: center;
  padding: clamp(44px,9vh,110px) 24px;
  border-top: 1px solid var(--sn-02-line);
}

.sn-02__pagefootin {
  font-family: ui-monospace,Menlo,Consolas,monospace;
  font-size: 12px;
  letter-spacing: .06em;
  color: var(--sn-02-mut);
  text-align: center;
}

.sn-02__intro-title,
.sn-02__intro-sub {
  text-wrap: balance;
}
Paste this into ChatGPT, Claude, Cursor, or any coding assistant. The block below is pre-framed with everything the AI needs to integrate this demo into your project — markup, styles, scoping notes, and the source URL. Hit Copy and paste straight into your chat.
Here's a working CSS Sticky Navigation from CodeFronts. Use it as-is or adapt to your framework. All classes are scoped under a unique prefix so the code won't collide with your existing styles. MIT licensed.
Demo: Sticky Top Header with Flexbox Layout (Logo · Links · CTA)
Source: https://codefronts.com/navigation/css-sticky-navigation/sticky-top-header-with-flexbox-layout-logo-links-cta/

The three-zone product header — brand locked left, navigation held in the true optical centre, action button pinned right — that stays balanced when the link set grows or a locale doubles the label lengths. Built on auto margins rather than space-between, which is why the centre group does not drift every time the CTA text changes.
## HTML
```html
<section class="sn-02" aria-label="Flexbox sticky header demo">
    <header class="sn-02__bar">
      <div class="sn-02__zone sn-02__zone--start">
        <a class="sn-02__brand" href="#sn-02-s1"><span class="sn-02__logo" aria-hidden="true">◈</span>Halcyon Labs</a>
      </div>
      <nav class="sn-02__nav" aria-label="Primary">
        <a class="sn-02__link" href="#sn-02-s1" aria-current="page">Platform</a>
        <a class="sn-02__link" href="#sn-02-s2">Solutions</a>
        <a class="sn-02__link" href="#sn-02-s3">Developers</a>
        <a class="sn-02__link" href="#sn-02-s4">Company</a>
      </nav>
      <div class="sn-02__zone sn-02__zone--end">
        <a class="sn-02__ghost" href="#sn-02-s3">Sign in</a>
        <a class="sn-02__cta" href="#sn-02-s4">Book a demo</a>
      </div>
    </header>
  <div class="sn-02__intro">
    <div class="sn-02__introin">
    <p class="sn-02__intro-eyebrow">flex · gap · margin-inline: auto</p>
    <h2 class="sn-02__intro-title">Logo left · links centre · CTA right</h2>
    </div>
  </div>
    <main>
      <section class="sn-02__sec" id="sn-02-s1">
        <p class="sn-02__kicker">Platform</p>
        <h3 class="sn-02__h">Equal side zones, true centre</h3>
        <p class="sn-02__p">Both outer zones carry <code>flex:1 1 0</code>, so they always claim the same share of free space. The nav lands on the optical centre line whatever the button says.</p>
      </section>
      <section class="sn-02__sec" id="sn-02-s2">
        <p class="sn-02__kicker">Solutions</p>
        <h3 class="sn-02__h">Why not space-between</h3>
        <p class="sn-02__p">Space-between positions the middle group as a side effect of its neighbours' widths. Translate the CTA into German and the nav visibly drifts.</p>
      </section>
      <section class="sn-02__sec" id="sn-02-s3">
        <p class="sn-02__kicker">Developers</p>
        <h3 class="sn-02__h">min-width: 0 stops the overflow</h3>
        <p class="sn-02__p">A flex item refuses to shrink below its content by default. That single rule is behind most mystery horizontal scrollbars in headers.</p>
      </section>
      <section class="sn-02__sec" id="sn-02-s4">
        <p class="sn-02__kicker">Company</p>
        <h3 class="sn-02__h">Gap, not margins</h3>
        <p class="sn-02__p">Spacing that lives between items survives reordering, RTL mirroring and CMS-driven link lists with no trailing whitespace to clean up.</p>
      </section>
    </main>
  <footer class="sn-02__pagefoot">
    <p class="sn-02__pagefootin">Sticky navigation pattern 02 of 25 · codefronts.com</p>
  </footer>
</section>
```
## CSS
```css
.sn-02 {
  width: 100%;
  min-height: 100vh;
  min-height: 100svh;
  display: block;
  background: var(--sn-02-bg);
  --sn-02-bg: oklch(0.19 0.024 260);
  --sn-02-surface: oklch(0.235 0.028 262);
  --sn-02-ink: oklch(0.97 0.006 260);
  --sn-02-mut: oklch(0.73 0.018 260);
  --sn-02-acc: oklch(0.83 0.16 172);
  --sn-02-line: oklch(1 0 0/.1);
  --sn-02-h: 66px;
  font-family: 'Segoe UI',system-ui,-apple-system,sans-serif;
  color: var(--sn-02-ink);
  -webkit-font-smoothing: antialiased;
}

.sn-02 *,
.sn-02 *::before,
.sn-02 *::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

.sn-02__eyebrow,
.sn-02__intro-eyebrow {
  font-family: ui-monospace,Menlo,Consolas,monospace;
  font-size: 11.5px;
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--sn-02-acc);
}

.sn-02__title,
.sn-02__intro-title {
  font-size: clamp(23px,3.5vw,37px);
  line-height: 1.06;
  letter-spacing: -.03em;
  font-weight: 700;
  text-wrap: balance;
}

.sn-02__bar {
  position: sticky;
  top: 0;
  z-index: 10;
  display: flex;
  align-items: center;
  gap: clamp(10px,2vw,24px);
  min-height: var(--sn-02-h);
  padding: 0 clamp(14px,2.4vw,24px);
  background: color-mix(in oklch,var(--sn-02-surface) 82%,transparent);
  backdrop-filter: blur(16px) saturate(1.4);
  -webkit-backdrop-filter: blur(16px) saturate(1.4);
  border-bottom: 1px solid var(--sn-02-line);
}

.sn-02__zone {
  display: flex;
  align-items: center;
  gap: 10px;
  flex: 1 1 0;
  min-width: 0;
}

.sn-02__zone--end {
  justify-content: flex-end;
}

.sn-02__brand {
  display: flex;
  align-items: center;
  gap: 9px;
  min-width: 0;
  font-size: 15px;
  font-weight: 700;
  letter-spacing: -.02em;
  color: inherit;
  text-decoration: none;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.sn-02__logo {
  flex: none;
  display: grid;
  place-items: center;
  width: 26px;
  height: 26px;
  border-radius: 8px;
  font-size: 13px;
  color: oklch(0.19 0.024 260);
  background: var(--sn-02-acc);
}

.sn-02__nav {
  display: flex;
  align-items: center;
  gap: 2px;
  flex: none;
}

.sn-02__link {
  display: flex;
  align-items: center;
  min-height: 38px;
  padding: 0 12px;
  border-radius: 9px;
  font-size: 13.6px;
  font-weight: 600;
  color: var(--sn-02-mut);
  text-decoration: none;
  white-space: nowrap;
  transition: color .18s ease,background .18s ease;
}

.sn-02__link:hover {
  color: var(--sn-02-ink);
  background: oklch(1 0 0/.06);
}

.sn-02__link[aria-current="page"] {
  color: var(--sn-02-acc);
  background: color-mix(in oklch,var(--sn-02-acc) 14%,transparent);
}

.sn-02__ghost {
  display: flex;
  align-items: center;
  min-height: 38px;
  padding: 0 12px;
  border-radius: 9px;
  font-size: 13.4px;
  font-weight: 600;
  color: var(--sn-02-mut);
  text-decoration: none;
  white-space: nowrap;
  transition: color .18s ease;
}

.sn-02__ghost:hover {
  color: var(--sn-02-ink);
}

.sn-02__cta {
  display: flex;
  align-items: center;
  min-height: 38px;
  padding: 0 16px;
  border-radius: 9px;
  font-size: 13.4px;
  font-weight: 670;
  color: oklch(0.19 0.024 260);
  background: var(--sn-02-acc);
  text-decoration: none;
  white-space: nowrap;
  box-shadow: 0 6px 18px -8px var(--sn-02-acc);
  transition: transform .18s ease,filter .18s ease;
}

.sn-02__cta:hover {
  transform: translateY(-1px);
  filter: brightness(1.06);
}

.sn-02__link:focus-visible,
.sn-02__ghost:focus-visible,
.sn-02__cta:focus-visible,
.sn-02__brand:focus-visible {
  outline: 2px solid var(--sn-02-acc);
  outline-offset: 2px;
}

.sn-02__sec {
  scroll-margin-top: var(--sn-02-h);
  display: grid;
  gap: 10px;
  align-content: center;
  min-height: 70svh;
  padding: clamp(24px,4vw,46px);
  border-bottom: 1px solid var(--sn-02-line);
}

.sn-02__sec:nth-child(even) {
  background: oklch(1 0 0/.025);
}

.sn-02__kicker {
  font-family: ui-monospace,Menlo,Consolas,monospace;
  font-size: 11px;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--sn-02-acc);
}

.sn-02__h {
  font-size: clamp(19px,2.6vw,26px);
  line-height: 1.16;
  letter-spacing: -.025em;
  font-weight: 690;
  text-wrap: balance;
}

.sn-02__p {
  max-width: 52ch;
  font-size: 14.8px;
  line-height: 1.64;
  color: var(--sn-02-mut);
  text-wrap: pretty;
}

.sn-02__p code {
  font-family: ui-monospace,Menlo,Consolas,monospace;
  font-size: .85em;
  background: oklch(1 0 0/.09);
  padding: 2px 6px;
  border-radius: 5px;
}

@media (max-width: 780px) {
  .sn-02__nav {
    display: none;
  }

  .sn-02__zone {
    flex: 0 1 auto;
  }

  .sn-02__zone--end {
    margin-inline-start: auto;
  }
}

@media (max-width: 480px) {
  .sn-02__ghost {
    display: none;
  }
}

@media (prefers-reduced-motion: reduce) {
  .sn-02 * {
    transition-duration: .01ms !important;
  }
}
/* ── full-page mode: the demo IS the document, so sticky pins against the real viewport ── */

:root:has(.sn-02) {
  scroll-behavior: smooth;
  scroll-padding-top: calc(var(--sn-02-h) + 10px);
}

@media (prefers-reduced-motion: reduce) {
  :root:has(.sn-02) {
    scroll-behavior: auto;
  }
}

.sn-02__bar,
.sn-02__barin {
  padding-inline: max(clamp(16px,3vw,26px),calc((100% - 1280px)/2));
}

.sn-02__sec {
  padding-inline: max(clamp(22px,5vw,44px),calc((100% - 1120px)/2));
}

.sn-02__intro {
  display: grid;
  place-items: center;
  min-height: min(90svh,900px);
  padding: clamp(56px,12vh,150px) clamp(20px,5vw,48px) clamp(40px,8vh,90px);
  background: radial-gradient(85% 55% at 50% 0%,oklch(0.3 0.06 190/.5),transparent);
}

.sn-02__introin {
  display: grid;
  justify-items: center;
  gap: clamp(12px,1.8vw,18px);
  text-align: center;
  max-width: 26ch;
}

.sn-02__intro-eyebrow {
  font-size: clamp(11px,1.1vw,13px);
}

.sn-02__intro-title {
  font-size: clamp(38px,7.5vw,86px);
  line-height: .98;
  letter-spacing: -.04em;
  max-width: none;
}

.sn-02__intro-sub {
  font-size: clamp(15px,1.7vw,19px);
  line-height: 1.55;
  max-width: 46ch;
}

.sn-02__intro::after {
  content: "Scroll — the bar stays";
  justify-self: center;
  margin-block-start: clamp(20px,4vh,44px);
  padding: 9px 18px;
  border: 1px solid var(--sn-02-line);
  border-radius: 99px;
  font-family: ui-monospace,Menlo,Consolas,monospace;
  font-size: 11.5px;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--sn-02-mut);
  animation: sn-02-cue 2.6s ease-in-out infinite;
}

@keyframes sn-02-cue {
  0%,
    100% {
    transform: translateY(0);
    opacity: .75;
  }

  50% {
    transform: translateY(5px);
    opacity: 1;
  }
}

@media (prefers-reduced-motion: reduce) {
  .sn-02__intro::after {
    animation: none;
  }
}

.sn-02__pagefoot {
  display: grid;
  place-items: center;
  padding: clamp(44px,9vh,110px) 24px;
  border-top: 1px solid var(--sn-02-line);
}

.sn-02__pagefootin {
  font-family: ui-monospace,Menlo,Consolas,monospace;
  font-size: 12px;
  letter-spacing: .06em;
  color: var(--sn-02-mut);
  text-align: center;
}

.sn-02__intro-title,
.sn-02__intro-sub {
  text-wrap: balance;
}
```

How this works

Most three-zone headers reach for justify-content:space-between and then wonder why the nav slides left as soon as the button says Get started free instead of Go. Space-between distributes leftover space between items, so the centre group's position is a function of how wide its neighbours are. Auto margins fix it:

.bar{ display:flex; align-items:center; gap:1.5rem }
.brand{ flex: 1 1 0; }                 /* equal-weight side */
.nav  { display:flex; gap:.25rem }      /* natural width centre */
.actions{ flex: 1 1 0; display:flex; justify-content:flex-end }

Giving the two side zones an identical flex:1 1 0 makes them consume the free space in equal shares no matter what they contain, so the middle group lands on the true centre line. If instead you want the nav pushed right against the actions (the SaaS-app arrangement), delete the flex on the sides and use a single margin-inline-start:auto — one declaration, no wrapper.

Two details keep it from breaking in production. gap instead of per-child margins means removing a link never leaves a phantom indent, and the whole row mirrors correctly for RTL because every inset uses logical properties. And min-width:0 on the brand zone lets a long product name truncate with an ellipsis instead of forcing the flex line wider than the header — the default min-width:auto on a flex item is what causes the mysterious horizontal scrollbar.

Make it yours

  • Nav hard-right instead of centred: remove flex:1 1 0 from both side zones and give .nav a margin-inline-start:auto.
  • Full-height hover targets: swap the link padding for align-self:stretch plus a flex centre — the hover background then spans the whole bar height, a bank/enterprise convention.
  • Add a divider between zones: .actions::before{content:'';inline-size:1px;block-size:20px;background:var(--line)}, no extra markup.
  • Wrap instead of overflow at medium widths: flex-wrap:wrap on the bar plus flex-basis:100% on the nav drops the links to a second sticky row.

Gotchas — read before shipping

  • justify-content:space-between does not centre the middle child — it only looks centred while both sides happen to be the same width.
  • Flex items default to min-width:auto, so a long unbreakable brand name or URL can blow the header wider than the viewport. min-width:0 on the zone that holds text is the fix.
  • align-items:stretch (the default) makes a sticky flex child full height, which cancels sticky behaviour when the header itself is the flex item. Use align-self:start.
  • Margins on nav links break RTL and leave trailing space after the last item. Use gap.
  • Setting a fixed height on the bar clips descenders and focus rings at large font sizes. Use min-height so the bar can grow with user font settings.

Browser support

ChromeSafariFirefoxEdge
114+17.5+121+114+

Floor set by :has(), oklch(), color-mix(), backdrop-filter, text-wrap as this demo is written. The core technique itself goes back further — Chrome 111+.

Flexbox gap is supported in every engine since Safari 14.1 (2021); logical properties since 2019. The quoted floor reflects the oklch() colour tokens only.

Techniques used in this demo

Search CodeFronts

Loading…