22 CSS Headers15 / 22

Pure CSSMIT licensed

Full Screen Overlay Hamburger Header

A header that opens into a full-screen navigation overlay with no JavaScript: a visually hidden checkbox, a label styled as the hamburger, and :has(:checked) on the root. The bars morph into an X with a spring curve, menu items stagger in on animation-delay, and the closed overlay is hidden with visibility so its links never sit in the tab order.

Published Updated

Live Demo
Try it

The code

<div class="hd-15">
  <a class="hd-15__skip" href="#hd-15-main">Skip to content</a>
    <input class="hd-15__sr" type="checkbox" id="hd-15-open" aria-label="Open navigation menu">

  <header class="hd-15__bar">
    <a class="hd-15__brand" href="#hd-15-main">
      <span class="hd-15__mark" aria-hidden="true">
        <svg viewBox="0 0 24 24" fill="none" focusable="false"><path d="M12 3.5 20.5 12 12 20.5 3.5 12 12 3.5Z" stroke="currentColor" stroke-width="1.8"/><circle cx="12" cy="12" r="2.6" fill="currentColor"/></svg>
      </span>
      <span class="hd-15__word">Vantage</span>
    </a>

    <p class="hd-15__tag">Studio for interface engineering</p>

    <input class="hd-15__sr" type="checkbox" id="hd-15-light">
    <label class="hd-15__theme" for="hd-15-light">
      <svg class="hd-15__moon" viewBox="0 0 24 24" fill="none" aria-hidden="true" focusable="false"><path d="M20.5 14.4A8.4 8.4 0 0 1 9.6 3.5 8.6 8.6 0 1 0 20.5 14.4Z" stroke="currentColor" stroke-width="1.8" stroke-linejoin="round"/></svg>
      <svg class="hd-15__sun" viewBox="0 0 24 24" fill="none" aria-hidden="true" focusable="false"><circle cx="12" cy="12" r="4.2" stroke="currentColor" stroke-width="1.8"/><path d="M12 2.8v2.1M12 19.1v2.1M2.8 12h2.1M19.1 12h2.1M5.5 5.5 7 7M17 17l1.5 1.5M18.5 5.5 17 7M7 17l-1.5 1.5" stroke="currentColor" stroke-width="1.7" stroke-linecap="round"/></svg>
      <span class="hd-15__vh">Switch light or dark theme</span>
    </label>
    <label class="hd-15__burger" for="hd-15-open">
      <span class="hd-15__bars" aria-hidden="true">
        <span class="hd-15__b1"></span><span class="hd-15__b2"></span><span class="hd-15__b3"></span>
      </span>
      <span class="hd-15__burger-t" aria-hidden="true">Menu</span>
    </label>
  </header>

  <div class="hd-15__overlay">
    <label class="hd-15__close-all" for="hd-15-open" aria-label="Close navigation menu"></label>
    <div class="hd-15__overlay-in">
      <nav class="hd-15__menu" aria-label="Main">
        <ul>
          <li style="--i:1"><a class="hd-15__link" href="#hd-15-main"><span class="hd-15__num">01</span>Work</a></li>
          <li style="--i:2"><a class="hd-15__link" href="#hd-15-main"><span class="hd-15__num">02</span>Services</a></li>
          <li style="--i:3"><a class="hd-15__link" href="#hd-15-main"><span class="hd-15__num">03</span>Studio</a></li>
          <li style="--i:4"><a class="hd-15__link" href="#hd-15-main"><span class="hd-15__num">04</span>Writing</a></li>
          <li style="--i:5"><a class="hd-15__link" href="#hd-15-main"><span class="hd-15__num">05</span>Contact</a></li>
        </ul>
      </nav>
      <div class="hd-15__meta" style="--i:6">
        <p class="hd-15__meta-h">Studio</p>
        <p>Unit 4, Ropewalk<br>Bristol BS1 6QT</p>
        <p class="hd-15__meta-h">New work</p>
        <p>hello@vantage.studio<br>+44 117 496 0142</p>
      </div>
    </div>
    <label class="hd-15__close" for="hd-15-open">
      <svg viewBox="0 0 24 24" fill="none" aria-hidden="true" focusable="false"><path d="M6 6l12 12M18 6 6 18" stroke="currentColor" stroke-width="2" stroke-linecap="round"/></svg>
      <span class="hd-15__vh">Close navigation menu</span>
    </label>
  </div>

  <main class="hd-15__main" id="hd-15-main">
    <p class="hd-15__eyebrow">Selected work &middot; 2026</p>
    <h1>Interfaces that survive the second release.</h1>
    <p class="hd-15__lead">Press the menu button &mdash; or tab to it and hit space. The bars morph, the overlay blurs the page and five links stagger in. Nothing here is JavaScript.</p>
    <ul class="hd-15__work">
      <li><span class="hd-15__w-n">01</span><span class="hd-15__w-t">Northwind &mdash; freight console</span><span class="hd-15__w-y">2026</span></li>
      <li><span class="hd-15__w-n">02</span><span class="hd-15__w-t">Halcyon &mdash; design system</span><span class="hd-15__w-y">2025</span></li>
      <li><span class="hd-15__w-n">03</span><span class="hd-15__w-t">Meridian &mdash; docs platform</span><span class="hd-15__w-y">2025</span></li>
      <li><span class="hd-15__w-n">04</span><span class="hd-15__w-t">Foundry &mdash; asset pipeline</span><span class="hd-15__w-y">2024</span></li>
    </ul>
    <div class="hd-15__filler" aria-hidden="true"></div>
  </main>
</div>
.hd-15 {
  --bg: #000000;
  --surface: #0b0d0f;
  --ink: #f4f7f8;
  --muted: #8c9599;
  --line: rgba(244,247,248,.14);
  --accent: #22e0d0;
  --accent-ink: #00201d;
  --menu-size: clamp(2.1rem,9vw,4.6rem);
  --font-display: "Avenir Next Condensed","Arial Narrow",system-ui,sans-serif;
  --font-body: system-ui,-apple-system,"Segoe UI",sans-serif;
  width: 100%;
  min-height: 100vh;
  display: block;
  position: relative;
  background: var(--bg);
  color: var(--ink);
  font-family: var(--font-body);
  line-height: 1.55;
}

@supports (color: oklch(50% .1 200)) {
  .hd-15 {
    --surface: oklch(14% .006 200);
    --ink: oklch(97% .006 200);
    --muted: oklch(68% .015 200);
    --accent: oklch(84% .14 185);
  }
}

.hd-15 * {
  box-sizing: border-box;
}

.hd-15 h1,
.hd-15 p,
.hd-15 ul {
  margin: 0;
  padding: 0;
}

.hd-15 ul {
  list-style: none;
}

.hd-15 [hidden] {
  display: none;
}

.hd-15 :focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 3px;
}

.hd-15__sr,
.hd-15__vh {
  position: absolute;
  inline-size: 1px;
  block-size: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

.hd-15__skip {
  position: absolute;
  inset-inline-start: 1rem;
  top: -3rem;
  z-index: 80;
  padding: .55rem 1rem;
  background: var(--accent);
  color: var(--accent-ink);
  font-size: .8rem;
  font-weight: 700;
  text-decoration: none;
  transition: top .18s ease;
}

.hd-15__skip:focus-visible {
  top: 1rem;
}
/* ── header: no chrome, type only ─────────────────────────────────── */

.hd-15__bar {
  position: sticky;
  top: 0;
  z-index: 60;
  display: flex;
  align-items: center;
  gap: 1.5rem;
  padding: clamp(1rem,3vw,1.5rem) clamp(1rem,4vw,2.5rem);
  background: color-mix(in srgb, var(--bg) 78%, transparent);
  backdrop-filter: blur(10px);
}

.hd-15__brand {
  display: inline-flex;
  align-items: center;
  gap: .6rem;
  color: var(--ink);
  text-decoration: none;
  min-inline-size: 0;
}

.hd-15__mark {
  display: grid;
  place-items: center;
  inline-size: 1.9rem;
  block-size: 1.9rem;
  color: var(--accent);
  flex: none;
}

.hd-15__mark svg {
  inline-size: 1.9rem;
  block-size: 1.9rem;
}

.hd-15__word {
  font-family: var(--font-display);
  font-size: 1.2rem;
  font-weight: 700;
  letter-spacing: .06em;
  text-transform: uppercase;
  min-inline-size: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.hd-15__tag {
  font-size: .72rem;
  letter-spacing: .16em;
  text-transform: uppercase;
  color: var(--muted);
  min-inline-size: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.hd-15__burger {
  display: inline-flex;
  align-items: center;
  gap: .6rem;
  margin-inline-start: auto;
  min-block-size: 2.75rem;
  padding: .5rem .75rem;
  border: 1px solid var(--line);
  cursor: pointer;
  transition: border-color .18s ease, background-color .18s ease;
}

.hd-15__burger:hover {
  border-color: var(--accent);
  background: rgba(34,224,208,.08);
}

.hd-15__bars {
  display: grid;
  gap: 4px;
  inline-size: 1.4rem;
}

.hd-15__bars span {
  display: block;
  block-size: 2px;
  background: var(--ink);
  border-radius: 2px;
  transition: translate .3s cubic-bezier(.5,-.35,.4,1.4), rotate .3s cubic-bezier(.5,-.35,.4,1.4), opacity .2s ease, scale .2s ease, background-color .2s ease;
}

.hd-15__burger-t {
  font-family: var(--font-display);
  font-size: .82rem;
  font-weight: 700;
  letter-spacing: .18em;
  text-transform: uppercase;
  color: var(--muted);
}

.hd-15:has(#hd-15-open:checked) .hd-15__b1 {
  translate: 0 6px;
  rotate: 45deg;
  background: var(--accent);
}

.hd-15:has(#hd-15-open:checked) .hd-15__b2 {
  opacity: 0;
  scale: .3 1;
}

.hd-15:has(#hd-15-open:checked) .hd-15__b3 {
  translate: 0 -6px;
  rotate: -45deg;
  background: var(--accent);
}

.hd-15:has(#hd-15-open:focus-visible) .hd-15__burger {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}
/* ── full-screen overlay ──────────────────────────────────────────── */

.hd-15__overlay {
  position: fixed;
  inset: 0;
  z-index: 70;
  display: grid;
  align-items: center;
  background: rgba(0,0,0,.94);
  visibility: hidden;
  opacity: 0;
  pointer-events: none;
  transition: opacity .28s ease, visibility .28s allow-discrete;
}

@supports (backdrop-filter: blur(2px)) {
  .hd-15__overlay {
    background: rgba(0,0,0,.72);
    backdrop-filter: blur(22px) saturate(120%);
  }
}

.hd-15:has(#hd-15-open:checked) .hd-15__overlay {
  visibility: visible;
  opacity: 1;
  pointer-events: auto;
}

.hd-15__close-all {
  position: absolute;
  inset: 0;
  cursor: default;
}

.hd-15__overlay-in {
  position: relative;
  inline-size: 100%;
  max-inline-size: 72rem;
  margin-inline: auto;
  display: grid;
  grid-template-columns: minmax(0,1fr) 14rem;
  gap: clamp(1.5rem,5vw,4rem);
  align-items: end;
  padding: clamp(2rem,8vh,5rem) clamp(1.25rem,5vw,3rem);
}

.hd-15__menu ul {
  display: grid;
  gap: .1rem;
}

.hd-15__link {
  display: flex;
  align-items: baseline;
  gap: 1rem;
  padding: .15rem 0;
  font-family: var(--font-display);
  font-size: var(--menu-size);
  line-height: 1.05;
  font-weight: 700;
  letter-spacing: -.01em;
  text-transform: uppercase;
  color: var(--ink);
  text-decoration: none;
  transition: color .18s ease, translate .18s ease;
}

.hd-15__link:hover {
  color: var(--accent);
  translate: .6rem 0;
}

.hd-15__num {
  font-family: var(--font-body);
  font-size: .8rem;
  font-weight: 600;
  letter-spacing: .14em;
  color: var(--muted);
  flex: none;
}

.hd-15__meta {
  display: grid;
  gap: .25rem;
  color: var(--muted);
  font-size: .85rem;
}

.hd-15__meta-h {
  font-size: .68rem;
  font-weight: 700;
  letter-spacing: .16em;
  text-transform: uppercase;
  color: var(--accent);
  margin-top: .75rem;
}

.hd-15__meta-h:first-child {
  margin-top: 0;
}

.hd-15__close {
  position: absolute;
  top: clamp(1rem,3vw,1.5rem);
  inset-inline-end: clamp(1rem,4vw,2.5rem);
  display: grid;
  place-items: center;
  inline-size: 2.75rem;
  block-size: 2.75rem;
  border: 1px solid var(--line);
  color: var(--ink);
  cursor: pointer;
  transition: border-color .18s ease, color .18s ease;
}

.hd-15__close svg {
  inline-size: 1.3rem;
  block-size: 1.3rem;
}

.hd-15__close:hover {
  border-color: var(--accent);
  color: var(--accent);
}

.hd-15:has(#hd-15-open:checked) .hd-15__link,
.hd-15:has(#hd-15-open:checked) .hd-15__meta {
  animation: hd-15-in .45s cubic-bezier(.2,.8,.2,1) backwards;
  animation-delay: calc(var(--i,1) * 55ms + 80ms);
}

@keyframes hd-15-in {
  from {
    opacity: 0;
    translate: 0 1.4rem;
  }
}
/* ── page body ────────────────────────────────────────────────────── */

.hd-15__main {
  max-inline-size: 64rem;
  margin-inline: auto;
  padding: clamp(3rem,9vw,6rem) clamp(1.25rem,4vw,2.5rem) 5rem;
}

.hd-15__eyebrow {
  font-size: .72rem;
  font-weight: 700;
  letter-spacing: .2em;
  text-transform: uppercase;
  color: var(--accent);
}

.hd-15__main h1 {
  font-family: var(--font-display);
  font-size: clamp(2.2rem,8vw,4.4rem);
  line-height: 1.02;
  font-weight: 700;
  letter-spacing: -.01em;
  text-transform: uppercase;
  margin-top: 1rem;
  max-inline-size: 22ch;
  text-wrap: balance;
}

.hd-15__lead {
  margin-top: 1.2rem;
  max-inline-size: 52ch;
  color: var(--muted);
  font-size: 1.02rem;
  text-wrap: pretty;
}

.hd-15__work {
  margin-top: clamp(2.5rem,7vw,4rem);
  border-top: 1px solid var(--line);
}

.hd-15__work li {
  display: grid;
  grid-template-columns: 3rem minmax(0,1fr) 4rem;
  align-items: center;
  gap: 1rem;
  padding: 1rem .25rem;
  border-bottom: 1px solid var(--line);
}

.hd-15__work li:hover {
  background: var(--surface);
}

.hd-15__w-n {
  font-size: .75rem;
  letter-spacing: .14em;
  color: var(--muted);
}

.hd-15__w-t {
  font-family: var(--font-display);
  font-size: 1.25rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: .02em;
  min-inline-size: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.hd-15__w-y {
  font-size: .8rem;
  color: var(--muted);
  text-align: end;
  font-variant-numeric: tabular-nums;
}

.hd-15__filler {
  block-size: 40vh;
}

@media (max-width: 720px) {
  .hd-15__tag {
    display: none;
  }

  .hd-15__overlay-in {
    grid-template-columns: minmax(0,1fr);
    align-items: start;
  }

  .hd-15__meta {
    display: none;
  }
}

@media (prefers-reduced-motion: reduce) {
  .hd-15 * {
    animation-duration: 1ms !important;
    transition-duration: 1ms !important;
  }
}

@media (forced-colors: active) {
  .hd-15__overlay {
    background: Canvas;
  }

  .hd-15__burger,
    .hd-15__close {
    border: 1px solid CanvasText;
  }
}
/* ── manual theme toggle — beats the OS default ──────────────────── */

.hd-15:has(#hd-15-light:checked) {
  --bg: #f6f8f8;
  --surface: #ffffff;
  --ink: #0b0d0f;
  --muted: #5b6568;
  --line: rgba(11,13,15,.14);
  --accent: #0f8f85;
  --accent-ink: #ffffff;
}
/* icon: shows the scheme the toggle will switch you to */

.hd-15__moon {
  display: none;
}

.hd-15:has(#hd-15-light:checked) .hd-15__sun {
  display: none;
}

.hd-15:has(#hd-15-light:checked) .hd-15__moon {
  display: block;
}

@media (prefers-color-scheme: light) {
  .hd-15:not([data-theme="dark"]):not(:has(#hd-15-light:checked)) {
    --bg: #f6f8f8;
    --surface: #ffffff;
    --ink: #0b0d0f;
    --muted: #5b6568;
    --line: rgba(11,13,15,.14);
    --accent: #0f8f85;
    --accent-ink: #ffffff;
  }
  /* FLIP-BACK — light OS + checked toggle returns to the dark surface */

  .hd-15:has(#hd-15-light:checked) {
    --bg: #000000;
    --surface: #0b0d0f;
    --ink: #f4f7f8;
    --muted: #8c9599;
    --line: rgba(244,247,248,.14);
    --accent: #22e0d0;
    --accent-ink: #00201d;
  }
  /* the icon always offers the scheme you are not in */

  .hd-15:not([data-theme="dark"]):not(:has(#hd-15-light:checked)) .hd-15__sun {
    display: none;
  }

  .hd-15:not([data-theme="dark"]):not(:has(#hd-15-light:checked)) .hd-15__moon {
    display: block;
  }

  .hd-15:has(#hd-15-light:checked) .hd-15__moon {
    display: none;
  }

  .hd-15:has(#hd-15-light:checked) .hd-15__sun {
    display: block;
  }
}

.hd-15[data-theme="light"] {
  --bg: #f6f8f8;
  --surface: #ffffff;
  --ink: #0b0d0f;
  --muted: #5b6568;
  --line: rgba(11,13,15,.14);
  --accent: #0f8f85;
  --accent-ink: #ffffff;
}
/* ── theme toggle — visually hidden checkbox, zero JS ────────────── */

.hd-15__sr {
  position: absolute;
  inline-size: 1px;
  block-size: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

.hd-15__theme {
  display: grid;
  place-items: center;
  inline-size: 2.75rem;
  block-size: 2.75rem;
  border: 0;
  border-radius: 999px;
  background: transparent;
  color: inherit;
  cursor: pointer;
  transition: background-color .18s ease;
}

.hd-15__theme svg {
  inline-size: 1.15rem;
  block-size: 1.15rem;
}

.hd-15__theme:hover {
  background: rgba(128,128,128,.16);
}

.hd-15:has(#hd-15-light:focus-visible) .hd-15__theme {
  outline: 2px solid currentColor;
  outline-offset: 2px;
}
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 Header 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: Full Screen Overlay Hamburger Header
Source: https://codefronts.com/layouts/css-header-designs/full-screen-overlay-hamburger-header/

A header that opens into a full-screen navigation overlay with no JavaScript: a visually hidden checkbox, a label styled as the hamburger, and :has(:checked) on the root. The bars morph into an X with a spring curve, menu items stagger in on animation-delay, and the closed overlay is hidden with visibility so its links never sit in the tab order.
## HTML
```html
<div class="hd-15">
  <a class="hd-15__skip" href="#hd-15-main">Skip to content</a>
    <input class="hd-15__sr" type="checkbox" id="hd-15-open" aria-label="Open navigation menu">

  <header class="hd-15__bar">
    <a class="hd-15__brand" href="#hd-15-main">
      <span class="hd-15__mark" aria-hidden="true">
        <svg viewBox="0 0 24 24" fill="none" focusable="false"><path d="M12 3.5 20.5 12 12 20.5 3.5 12 12 3.5Z" stroke="currentColor" stroke-width="1.8"/><circle cx="12" cy="12" r="2.6" fill="currentColor"/></svg>
      </span>
      <span class="hd-15__word">Vantage</span>
    </a>

    <p class="hd-15__tag">Studio for interface engineering</p>

    <input class="hd-15__sr" type="checkbox" id="hd-15-light">
    <label class="hd-15__theme" for="hd-15-light">
      <svg class="hd-15__moon" viewBox="0 0 24 24" fill="none" aria-hidden="true" focusable="false"><path d="M20.5 14.4A8.4 8.4 0 0 1 9.6 3.5 8.6 8.6 0 1 0 20.5 14.4Z" stroke="currentColor" stroke-width="1.8" stroke-linejoin="round"/></svg>
      <svg class="hd-15__sun" viewBox="0 0 24 24" fill="none" aria-hidden="true" focusable="false"><circle cx="12" cy="12" r="4.2" stroke="currentColor" stroke-width="1.8"/><path d="M12 2.8v2.1M12 19.1v2.1M2.8 12h2.1M19.1 12h2.1M5.5 5.5 7 7M17 17l1.5 1.5M18.5 5.5 17 7M7 17l-1.5 1.5" stroke="currentColor" stroke-width="1.7" stroke-linecap="round"/></svg>
      <span class="hd-15__vh">Switch light or dark theme</span>
    </label>
    <label class="hd-15__burger" for="hd-15-open">
      <span class="hd-15__bars" aria-hidden="true">
        <span class="hd-15__b1"></span><span class="hd-15__b2"></span><span class="hd-15__b3"></span>
      </span>
      <span class="hd-15__burger-t" aria-hidden="true">Menu</span>
    </label>
  </header>

  <div class="hd-15__overlay">
    <label class="hd-15__close-all" for="hd-15-open" aria-label="Close navigation menu"></label>
    <div class="hd-15__overlay-in">
      <nav class="hd-15__menu" aria-label="Main">
        <ul>
          <li style="--i:1"><a class="hd-15__link" href="#hd-15-main"><span class="hd-15__num">01</span>Work</a></li>
          <li style="--i:2"><a class="hd-15__link" href="#hd-15-main"><span class="hd-15__num">02</span>Services</a></li>
          <li style="--i:3"><a class="hd-15__link" href="#hd-15-main"><span class="hd-15__num">03</span>Studio</a></li>
          <li style="--i:4"><a class="hd-15__link" href="#hd-15-main"><span class="hd-15__num">04</span>Writing</a></li>
          <li style="--i:5"><a class="hd-15__link" href="#hd-15-main"><span class="hd-15__num">05</span>Contact</a></li>
        </ul>
      </nav>
      <div class="hd-15__meta" style="--i:6">
        <p class="hd-15__meta-h">Studio</p>
        <p>Unit 4, Ropewalk<br>Bristol BS1 6QT</p>
        <p class="hd-15__meta-h">New work</p>
        <p>hello@vantage.studio<br>+44 117 496 0142</p>
      </div>
    </div>
    <label class="hd-15__close" for="hd-15-open">
      <svg viewBox="0 0 24 24" fill="none" aria-hidden="true" focusable="false"><path d="M6 6l12 12M18 6 6 18" stroke="currentColor" stroke-width="2" stroke-linecap="round"/></svg>
      <span class="hd-15__vh">Close navigation menu</span>
    </label>
  </div>

  <main class="hd-15__main" id="hd-15-main">
    <p class="hd-15__eyebrow">Selected work &middot; 2026</p>
    <h1>Interfaces that survive the second release.</h1>
    <p class="hd-15__lead">Press the menu button &mdash; or tab to it and hit space. The bars morph, the overlay blurs the page and five links stagger in. Nothing here is JavaScript.</p>
    <ul class="hd-15__work">
      <li><span class="hd-15__w-n">01</span><span class="hd-15__w-t">Northwind &mdash; freight console</span><span class="hd-15__w-y">2026</span></li>
      <li><span class="hd-15__w-n">02</span><span class="hd-15__w-t">Halcyon &mdash; design system</span><span class="hd-15__w-y">2025</span></li>
      <li><span class="hd-15__w-n">03</span><span class="hd-15__w-t">Meridian &mdash; docs platform</span><span class="hd-15__w-y">2025</span></li>
      <li><span class="hd-15__w-n">04</span><span class="hd-15__w-t">Foundry &mdash; asset pipeline</span><span class="hd-15__w-y">2024</span></li>
    </ul>
    <div class="hd-15__filler" aria-hidden="true"></div>
  </main>
</div>
```
## CSS
```css
.hd-15 {
  --bg: #000000;
  --surface: #0b0d0f;
  --ink: #f4f7f8;
  --muted: #8c9599;
  --line: rgba(244,247,248,.14);
  --accent: #22e0d0;
  --accent-ink: #00201d;
  --menu-size: clamp(2.1rem,9vw,4.6rem);
  --font-display: "Avenir Next Condensed","Arial Narrow",system-ui,sans-serif;
  --font-body: system-ui,-apple-system,"Segoe UI",sans-serif;
  width: 100%;
  min-height: 100vh;
  display: block;
  position: relative;
  background: var(--bg);
  color: var(--ink);
  font-family: var(--font-body);
  line-height: 1.55;
}

@supports (color: oklch(50% .1 200)) {
  .hd-15 {
    --surface: oklch(14% .006 200);
    --ink: oklch(97% .006 200);
    --muted: oklch(68% .015 200);
    --accent: oklch(84% .14 185);
  }
}

.hd-15 * {
  box-sizing: border-box;
}

.hd-15 h1,
.hd-15 p,
.hd-15 ul {
  margin: 0;
  padding: 0;
}

.hd-15 ul {
  list-style: none;
}

.hd-15 [hidden] {
  display: none;
}

.hd-15 :focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 3px;
}

.hd-15__sr,
.hd-15__vh {
  position: absolute;
  inline-size: 1px;
  block-size: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

.hd-15__skip {
  position: absolute;
  inset-inline-start: 1rem;
  top: -3rem;
  z-index: 80;
  padding: .55rem 1rem;
  background: var(--accent);
  color: var(--accent-ink);
  font-size: .8rem;
  font-weight: 700;
  text-decoration: none;
  transition: top .18s ease;
}

.hd-15__skip:focus-visible {
  top: 1rem;
}
/* ── header: no chrome, type only ─────────────────────────────────── */

.hd-15__bar {
  position: sticky;
  top: 0;
  z-index: 60;
  display: flex;
  align-items: center;
  gap: 1.5rem;
  padding: clamp(1rem,3vw,1.5rem) clamp(1rem,4vw,2.5rem);
  background: color-mix(in srgb, var(--bg) 78%, transparent);
  backdrop-filter: blur(10px);
}

.hd-15__brand {
  display: inline-flex;
  align-items: center;
  gap: .6rem;
  color: var(--ink);
  text-decoration: none;
  min-inline-size: 0;
}

.hd-15__mark {
  display: grid;
  place-items: center;
  inline-size: 1.9rem;
  block-size: 1.9rem;
  color: var(--accent);
  flex: none;
}

.hd-15__mark svg {
  inline-size: 1.9rem;
  block-size: 1.9rem;
}

.hd-15__word {
  font-family: var(--font-display);
  font-size: 1.2rem;
  font-weight: 700;
  letter-spacing: .06em;
  text-transform: uppercase;
  min-inline-size: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.hd-15__tag {
  font-size: .72rem;
  letter-spacing: .16em;
  text-transform: uppercase;
  color: var(--muted);
  min-inline-size: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.hd-15__burger {
  display: inline-flex;
  align-items: center;
  gap: .6rem;
  margin-inline-start: auto;
  min-block-size: 2.75rem;
  padding: .5rem .75rem;
  border: 1px solid var(--line);
  cursor: pointer;
  transition: border-color .18s ease, background-color .18s ease;
}

.hd-15__burger:hover {
  border-color: var(--accent);
  background: rgba(34,224,208,.08);
}

.hd-15__bars {
  display: grid;
  gap: 4px;
  inline-size: 1.4rem;
}

.hd-15__bars span {
  display: block;
  block-size: 2px;
  background: var(--ink);
  border-radius: 2px;
  transition: translate .3s cubic-bezier(.5,-.35,.4,1.4), rotate .3s cubic-bezier(.5,-.35,.4,1.4), opacity .2s ease, scale .2s ease, background-color .2s ease;
}

.hd-15__burger-t {
  font-family: var(--font-display);
  font-size: .82rem;
  font-weight: 700;
  letter-spacing: .18em;
  text-transform: uppercase;
  color: var(--muted);
}

.hd-15:has(#hd-15-open:checked) .hd-15__b1 {
  translate: 0 6px;
  rotate: 45deg;
  background: var(--accent);
}

.hd-15:has(#hd-15-open:checked) .hd-15__b2 {
  opacity: 0;
  scale: .3 1;
}

.hd-15:has(#hd-15-open:checked) .hd-15__b3 {
  translate: 0 -6px;
  rotate: -45deg;
  background: var(--accent);
}

.hd-15:has(#hd-15-open:focus-visible) .hd-15__burger {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}
/* ── full-screen overlay ──────────────────────────────────────────── */

.hd-15__overlay {
  position: fixed;
  inset: 0;
  z-index: 70;
  display: grid;
  align-items: center;
  background: rgba(0,0,0,.94);
  visibility: hidden;
  opacity: 0;
  pointer-events: none;
  transition: opacity .28s ease, visibility .28s allow-discrete;
}

@supports (backdrop-filter: blur(2px)) {
  .hd-15__overlay {
    background: rgba(0,0,0,.72);
    backdrop-filter: blur(22px) saturate(120%);
  }
}

.hd-15:has(#hd-15-open:checked) .hd-15__overlay {
  visibility: visible;
  opacity: 1;
  pointer-events: auto;
}

.hd-15__close-all {
  position: absolute;
  inset: 0;
  cursor: default;
}

.hd-15__overlay-in {
  position: relative;
  inline-size: 100%;
  max-inline-size: 72rem;
  margin-inline: auto;
  display: grid;
  grid-template-columns: minmax(0,1fr) 14rem;
  gap: clamp(1.5rem,5vw,4rem);
  align-items: end;
  padding: clamp(2rem,8vh,5rem) clamp(1.25rem,5vw,3rem);
}

.hd-15__menu ul {
  display: grid;
  gap: .1rem;
}

.hd-15__link {
  display: flex;
  align-items: baseline;
  gap: 1rem;
  padding: .15rem 0;
  font-family: var(--font-display);
  font-size: var(--menu-size);
  line-height: 1.05;
  font-weight: 700;
  letter-spacing: -.01em;
  text-transform: uppercase;
  color: var(--ink);
  text-decoration: none;
  transition: color .18s ease, translate .18s ease;
}

.hd-15__link:hover {
  color: var(--accent);
  translate: .6rem 0;
}

.hd-15__num {
  font-family: var(--font-body);
  font-size: .8rem;
  font-weight: 600;
  letter-spacing: .14em;
  color: var(--muted);
  flex: none;
}

.hd-15__meta {
  display: grid;
  gap: .25rem;
  color: var(--muted);
  font-size: .85rem;
}

.hd-15__meta-h {
  font-size: .68rem;
  font-weight: 700;
  letter-spacing: .16em;
  text-transform: uppercase;
  color: var(--accent);
  margin-top: .75rem;
}

.hd-15__meta-h:first-child {
  margin-top: 0;
}

.hd-15__close {
  position: absolute;
  top: clamp(1rem,3vw,1.5rem);
  inset-inline-end: clamp(1rem,4vw,2.5rem);
  display: grid;
  place-items: center;
  inline-size: 2.75rem;
  block-size: 2.75rem;
  border: 1px solid var(--line);
  color: var(--ink);
  cursor: pointer;
  transition: border-color .18s ease, color .18s ease;
}

.hd-15__close svg {
  inline-size: 1.3rem;
  block-size: 1.3rem;
}

.hd-15__close:hover {
  border-color: var(--accent);
  color: var(--accent);
}

.hd-15:has(#hd-15-open:checked) .hd-15__link,
.hd-15:has(#hd-15-open:checked) .hd-15__meta {
  animation: hd-15-in .45s cubic-bezier(.2,.8,.2,1) backwards;
  animation-delay: calc(var(--i,1) * 55ms + 80ms);
}

@keyframes hd-15-in {
  from {
    opacity: 0;
    translate: 0 1.4rem;
  }
}
/* ── page body ────────────────────────────────────────────────────── */

.hd-15__main {
  max-inline-size: 64rem;
  margin-inline: auto;
  padding: clamp(3rem,9vw,6rem) clamp(1.25rem,4vw,2.5rem) 5rem;
}

.hd-15__eyebrow {
  font-size: .72rem;
  font-weight: 700;
  letter-spacing: .2em;
  text-transform: uppercase;
  color: var(--accent);
}

.hd-15__main h1 {
  font-family: var(--font-display);
  font-size: clamp(2.2rem,8vw,4.4rem);
  line-height: 1.02;
  font-weight: 700;
  letter-spacing: -.01em;
  text-transform: uppercase;
  margin-top: 1rem;
  max-inline-size: 22ch;
  text-wrap: balance;
}

.hd-15__lead {
  margin-top: 1.2rem;
  max-inline-size: 52ch;
  color: var(--muted);
  font-size: 1.02rem;
  text-wrap: pretty;
}

.hd-15__work {
  margin-top: clamp(2.5rem,7vw,4rem);
  border-top: 1px solid var(--line);
}

.hd-15__work li {
  display: grid;
  grid-template-columns: 3rem minmax(0,1fr) 4rem;
  align-items: center;
  gap: 1rem;
  padding: 1rem .25rem;
  border-bottom: 1px solid var(--line);
}

.hd-15__work li:hover {
  background: var(--surface);
}

.hd-15__w-n {
  font-size: .75rem;
  letter-spacing: .14em;
  color: var(--muted);
}

.hd-15__w-t {
  font-family: var(--font-display);
  font-size: 1.25rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: .02em;
  min-inline-size: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.hd-15__w-y {
  font-size: .8rem;
  color: var(--muted);
  text-align: end;
  font-variant-numeric: tabular-nums;
}

.hd-15__filler {
  block-size: 40vh;
}

@media (max-width: 720px) {
  .hd-15__tag {
    display: none;
  }

  .hd-15__overlay-in {
    grid-template-columns: minmax(0,1fr);
    align-items: start;
  }

  .hd-15__meta {
    display: none;
  }
}

@media (prefers-reduced-motion: reduce) {
  .hd-15 * {
    animation-duration: 1ms !important;
    transition-duration: 1ms !important;
  }
}

@media (forced-colors: active) {
  .hd-15__overlay {
    background: Canvas;
  }

  .hd-15__burger,
    .hd-15__close {
    border: 1px solid CanvasText;
  }
}
/* ── manual theme toggle — beats the OS default ──────────────────── */

.hd-15:has(#hd-15-light:checked) {
  --bg: #f6f8f8;
  --surface: #ffffff;
  --ink: #0b0d0f;
  --muted: #5b6568;
  --line: rgba(11,13,15,.14);
  --accent: #0f8f85;
  --accent-ink: #ffffff;
}
/* icon: shows the scheme the toggle will switch you to */

.hd-15__moon {
  display: none;
}

.hd-15:has(#hd-15-light:checked) .hd-15__sun {
  display: none;
}

.hd-15:has(#hd-15-light:checked) .hd-15__moon {
  display: block;
}

@media (prefers-color-scheme: light) {
  .hd-15:not([data-theme="dark"]):not(:has(#hd-15-light:checked)) {
    --bg: #f6f8f8;
    --surface: #ffffff;
    --ink: #0b0d0f;
    --muted: #5b6568;
    --line: rgba(11,13,15,.14);
    --accent: #0f8f85;
    --accent-ink: #ffffff;
  }
  /* FLIP-BACK — light OS + checked toggle returns to the dark surface */

  .hd-15:has(#hd-15-light:checked) {
    --bg: #000000;
    --surface: #0b0d0f;
    --ink: #f4f7f8;
    --muted: #8c9599;
    --line: rgba(244,247,248,.14);
    --accent: #22e0d0;
    --accent-ink: #00201d;
  }
  /* the icon always offers the scheme you are not in */

  .hd-15:not([data-theme="dark"]):not(:has(#hd-15-light:checked)) .hd-15__sun {
    display: none;
  }

  .hd-15:not([data-theme="dark"]):not(:has(#hd-15-light:checked)) .hd-15__moon {
    display: block;
  }

  .hd-15:has(#hd-15-light:checked) .hd-15__moon {
    display: none;
  }

  .hd-15:has(#hd-15-light:checked) .hd-15__sun {
    display: block;
  }
}

.hd-15[data-theme="light"] {
  --bg: #f6f8f8;
  --surface: #ffffff;
  --ink: #0b0d0f;
  --muted: #5b6568;
  --line: rgba(11,13,15,.14);
  --accent: #0f8f85;
  --accent-ink: #ffffff;
}
/* ── theme toggle — visually hidden checkbox, zero JS ────────────── */

.hd-15__sr {
  position: absolute;
  inline-size: 1px;
  block-size: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

.hd-15__theme {
  display: grid;
  place-items: center;
  inline-size: 2.75rem;
  block-size: 2.75rem;
  border: 0;
  border-radius: 999px;
  background: transparent;
  color: inherit;
  cursor: pointer;
  transition: background-color .18s ease;
}

.hd-15__theme svg {
  inline-size: 1.15rem;
  block-size: 1.15rem;
}

.hd-15__theme:hover {
  background: rgba(128,128,128,.16);
}

.hd-15:has(#hd-15-light:focus-visible) .hd-15__theme {
  outline: 2px solid currentColor;
  outline-offset: 2px;
}
```

How this works

The checkbox stays focusable on purpose. display:none on the input would remove it from the tab order and break keyboard access — the most common flaw in checkbox-hack menus. Clipping it instead keeps it operable with Space while the label carries the visuals:

.hd-15__sr{position:absolute; inline-size:1px; block-size:1px;
  overflow:hidden; clip-path:inset(50%);}   /* focusable, invisible */
.hd-15:has(#hd-15-open:checked) .hd-15__overlay{visibility:visible; opacity:1;}

The morph is two spans and four declarations. The middle bar fades; the outer bars translate to the centre and rotate. Using a spring-ish cubic-bezier gives the X a slight overshoot, which is what makes the interaction feel physical rather than mechanical:

.hd-15:has(:checked) .hd-15__b1{translate:0 6px; rotate:45deg;}
.hd-15:has(:checked) .hd-15__b2{opacity:0; scale:.4 1;}
.hd-15:has(:checked) .hd-15__b3{translate:0 -6px; rotate:-45deg;}
.hd-15__bar span{transition:translate .3s cubic-bezier(.5,-.4,.4,1.4), rotate .3s;}

Stagger with one custom property. Each list item declares style="--i:3" and the animation delay is computed from it, so adding a link needs no new CSS. The entrance runs on opacity and translate only, so eight items animating at once still hold 60 fps on a mid-range phone:

.hd-15:has(:checked) .hd-15__link{
  animation:hd-15-in .45s cubic-bezier(.2,.8,.2,1) backwards;
  animation-delay:calc(var(--i) * 55ms + 80ms);
}

The trap: an overlay that is invisible but still there. If you hide it with opacity:0 alone, its links stay clickable and focusable across the whole viewport — the page appears frozen. Hide with visibility (transitioned with allow-discrete), keep pointer-events:none as a belt-and-braces measure, and ship the [hidden]{display:none} guard so a stray author display:flex cannot re-open it.

Make it yours

  • Change the stagger rhythm with the 55ms step in animation-delay; 40-70 ms feels lively, above 120 ms it drags.
  • Swap the cyan accent through --accent; the hamburger, hovers and the meta column follow.
  • Set the menu type scale with --menu-size; it is a clamp(), so one edit rescales every breakpoint.
  • Turn the overlay into a panel by constraining inline-size and adding inset-inline-start:auto.
  • Add a secondary column (contact details, social links) inside the overlay grid — it is a two-column grid above 720 px.
  • Keep the overlay open on desktop instead by moving the nav out of the overlay above a breakpoint with display:contents.

Gotchas — read before shipping

  • display:none on the checkbox removes it from the tab order — the menu then cannot be opened by keyboard at all. Clip it instead.
  • An overlay hidden with only opacity:0 intercepts every click on the page; hide with visibility and keep the [hidden] guard rule.
  • Pure CSS cannot listen for Esc. Provide a real close control (this demo uses a full-bleed close label plus a visible X) so users are never trapped.
  • Staggering with transition-delay instead of animation-delay makes the items animate out in reverse on close, which reads as sluggish; keyframes with backwards fill avoid it.

Browser support

ChromeSafariFirefoxEdge
117+17.4+129+117+

:has() (Chrome 105 / Safari 15.4 / Firefox 121) drives the whole demo. transition-behavior: allow-discrete (Chrome 117 / Safari 17.4 / Firefox 129) smooths the close; without it the overlay disappears instantly. backdrop-filter degrades to the solid background declared first.

Techniques used in this demo

Search CodeFronts

Loading…