# Expanding Bottom Navigation

> A compact bottom bar that expands into a full menu surface — modern mobile navigation with smooth morphing.

**Definition:** Expanding Bottom Navigation is a navigation UI animation from collections.html. It is built with HTML, CSS, JavaScript, and GSAP, ships with a live preview, and is free to copy into personal and commercial projects.

## Facts
- Category: Navigation
- Stack: HTML, CSS, JavaScript, and GSAP
- Added: 2026-08-01
- Canonical page: https://html.bosconvertpro.com/collection/expanding-bottom-navigation
- Full-page preview: https://html.bosconvertpro.com/collection/expanding-bottom-navigation/full
- Live demo: https://html.bosconvertpro.com/demos/expanding-bottom-navigation/index.html
- Markdown mirror: https://html.bosconvertpro.com/collection/expanding-bottom-navigation/index.md
- License: https://html.bosconvertpro.com/terms

## How to use
1. Open https://html.bosconvertpro.com/collection/expanding-bottom-navigation/full to preview the animation in full screen.
2. Copy index.html, styles.css, and script.js from the code tabs on https://html.bosconvertpro.com/collection/expanding-bottom-navigation.
3. Paste the files into your project and adjust timing, easing, and content as needed.

## FAQ
### What is Expanding Bottom Navigation?

Expanding Bottom Navigation is a navigation UI animation from collections.html. A compact bottom bar that expands into a full menu surface — modern mobile navigation with smooth morphing.

### What is Expanding Bottom Navigation built with?

Expanding Bottom Navigation is built with HTML, CSS, JavaScript, and GSAP. The source for every file is shown on the animation page and can be copied straight into your project.

### How do I use Expanding Bottom Navigation in my project?

Open the animation page (https://html.bosconvertpro.com/collection/expanding-bottom-navigation), preview it live, then copy index.html, styles.css, and script.js from the code tabs. Paste the files into your project and adjust timing, easing, and content as needed. A standalone demo runs at https://html.bosconvertpro.com/demos/expanding-bottom-navigation/index.html.

### Is Expanding Bottom Navigation free to use?

Yes. The Expanding Bottom Navigation demo code may be used in personal and commercial projects. You may not resell or redistribute the collection itself as a competing template pack. Full terms: https://html.bosconvertpro.com/terms.

## Source — index.html
````html
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Expanding Bottom Navigation</title>
    <link rel="stylesheet" href="styles.css" />
  </head>
  <body>
    <main class="page">
      <nav
        class="ebn"
        id="ebn"
        data-state="closed"
        aria-label="Primary"
      >
        <div class="ebn__shell" id="shell">
          <div class="ebn__clip" id="clip">
            <div class="ebn-open" id="openView" aria-hidden="true">
              <ul class="ebn-menu" role="list">
                <li>
                  <a class="ebn-menu__link" href="#home" data-menu-link>
                    <span class="ebn-menu__label">Home</span>
                  </a>
                </li>
                <li>
                  <a class="ebn-menu__link" href="#work" data-menu-link>
                    <span class="ebn-menu__label">Work</span>
                  </a>
                </li>
                <li>
                  <a class="ebn-menu__link" href="#studio" data-menu-link>
                    <span class="ebn-menu__label">Studio</span>
                  </a>
                </li>
                <li>
                  <a class="ebn-menu__link" href="#journal" data-menu-link>
                    <span class="ebn-menu__label">Journal</span>
                  </a>
                </li>
                <li>
                  <a
                    class="ebn-menu__link ebn-menu__link--accent"
                    href="mailto:abbosnurgulshanov@gmail.com"
                    data-menu-link
                  >
                    <span class="ebn-menu__label">Contact</span>
                    <svg
                      class="ebn-menu__arrow"
                      viewBox="0 0 16 16"
                      fill="none"
                      aria-hidden="true"
                    >
                      <path
                        d="M4.5 11.5 11.5 4.5M6.2 4.5h5.3v5.3"
                        stroke="currentColor"
                        stroke-width="1.7"
                        stroke-linecap="round"
                        stroke-linejoin="round"
                      />
                    </svg>
                  </a>
                </li>
              </ul>

              <div class="ebn-meta" data-meta>
                <div class="ebn-meta__col">
                  <span class="ebn-meta__heading">Socials</span>
                  <ul class="ebn-meta__list" role="list">
                    <li>
                      <a href="https://www.instagram.com/abbosdesign" target="_blank" rel="noopener">Instagram</a>
                    </li>
                    <li>
                      <a href="https://x.com/nurgulshanov" target="_blank" rel="noopener">X/Twitter</a>
                    </li>
                  </ul>
                </div>
                <div class="ebn-meta__col">
                  <span class="ebn-meta__heading">Studio</span>
                  <ul class="ebn-meta__list" role="list">
                    <li>
                      <a href="tel:+123456789">+123456789</a>
                    </li>
                    <li>
                      <a href="mailto:abbosnurgulshanov@gmail.com">Contact</a>
                    </li>
                  </ul>
                </div>
              </div>
            </div>

            <div class="ebn-bar" id="bar">
              <a class="ebn-logo" href="#" aria-label="Abbos home">
                <span class="ebn-logo__text">ABBOS</span>
              </a>

              <button
                type="button"
                class="ebn-toggle"
                id="toggle"
                aria-expanded="false"
                aria-controls="openView"
                aria-label="Open menu"
              >
                <span class="ebn-toggle__icon ebn-toggle__icon--menu" aria-hidden="true">
                  <span class="ebn-toggle__line"></span>
                  <span class="ebn-toggle__line"></span>
                </span>
                <span class="ebn-toggle__icon ebn-toggle__icon--close" aria-hidden="true">
                  <svg viewBox="0 0 16 16" fill="none">
                    <path
                      d="M4 4l8 8M12 4l-8 8"
                      stroke="currentColor"
                      stroke-width="1.8"
                      stroke-linecap="round"
                    />
                  </svg>
                </span>
              </button>
            </div>
          </div>
        </div>
      </nav>
    </main>

    <script src="https://cdn.jsdelivr.net/npm/gsap@3.12.5/dist/gsap.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/gsap@3.12.5/dist/CustomEase.min.js"></script>
    <script src="script.js"></script>
  </body>
</html>
````

## Source — styles.css
````css
@font-face {
  font-family: "Haffer VF";
  src: url("https://cdn.prod.website-files.com/68a5787bba0829184628bd51/68a5df2de5b7c113032b4047_HafferStandardUprights-VF.ttf")
    format("truetype");
  font-weight: 100 1000;
  font-style: normal;
  font-display: swap;
}

:root {
  --font: "Haffer VF", "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI",
    system-ui, sans-serif;

  --bg: #b8c4d2;

  --shell: #ffffff;
  --ink: #111111;
  --ink-soft: #1c1c1c;
  --muted: #a0a0a0;
  --meta: #6a6a6a;
  --line: rgba(0, 0, 0, 0.1);
  --blue: #2f6bff;
  --blue-hover: #1f5aef;

  --pill-w: 300px;
  --pill-h: 68px;
  --r-pill: 18px;

  --card-w: 420px;
  --card-h: 540px;
  --r-card: 22px;

  --toggle: 44px;
  --r-toggle: 11px;

  --bar-h: 68px;

  --inset-x: 34px;
}

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

html,
body {
  height: 100%;
}

body {
  font-family: var(--font);
  color: var(--ink);
  background: var(--bg);
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

a {
  color: inherit;
  text-decoration: none;
}

button {
  font: inherit;
  color: inherit;
  border: 0;
  background: none;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}

ul {
  list-style: none;
}

.page {
  min-height: 100%;
  min-height: 100dvh;
  display: flex;
  align-items: flex-end;
  justify-content: center;
  padding: 2rem 1rem 2.75rem;
  background: var(--bg);
}

.ebn {
  position: relative;
  width: var(--pill-w);
  height: var(--pill-h);
  outline: none;
  will-change: width, height;
  transform-origin: center bottom;
  transform: translateZ(0);
  backface-visibility: hidden;
}

.ebn__shell {
  position: absolute;
  inset: 0;
  background: var(--shell);
  border-radius: var(--r-pill);
  overflow: hidden;
  will-change: border-radius, box-shadow;
  box-shadow:
    0 1px 2px rgba(0, 0, 0, 0.04),
    0 4px 12px rgba(0, 0, 0, 0.06),
    0 16px 40px rgba(0, 0, 0, 0.08);
}

.ebn__clip {
  position: absolute;
  inset: 0;
  border-radius: inherit;
  overflow: hidden;
  transform: translateZ(0);
}

.ebn-open {
  position: absolute;
  left: 0;
  right: 0;
  top: 0;
  bottom: var(--bar-h);
  display: flex;
  flex-direction: column;
  padding: 34px var(--inset-x) 6px;
  overflow: hidden;
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
}

.ebn[data-state="open"] .ebn-open {
  pointer-events: auto;
}

.ebn-menu {
  display: flex;
  flex-direction: column;
  gap: 0;
  flex-shrink: 0;
}

.ebn-menu li {
  overflow: hidden;
}

.ebn-menu__link {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-size: 46px;
  font-weight: 720;
  letter-spacing: -0.05em;
  line-height: 1.1;
  color: var(--ink);
  transition: color 0.2s ease;
}

.ebn-menu__link:hover {
  color: var(--blue);
}

.ebn-menu__link--accent {
  color: var(--blue);
}

.ebn-menu__link--accent:hover {
  color: var(--blue-hover);
}

.ebn-menu__label {
  display: block;
}

.ebn-menu__arrow {
  width: 20px;
  height: 20px;
  flex-shrink: 0;
  position: relative;
  top: 2px;
}

.ebn-meta {
  margin-top: auto;
  padding-top: 22px;
  border-top: 1px solid var(--line);
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 24px;
  flex-shrink: 0;
}

.ebn-meta__heading {
  display: block;
  font-size: 12px;
  font-weight: 550;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--muted);
  margin-bottom: 13px;
  line-height: 1;
}

.ebn-meta__list {
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.ebn-meta__list a {
  font-size: 17px;
  font-weight: 500;
  letter-spacing: -0.015em;
  color: var(--ink-soft);
  line-height: 1.3;
  transition: color 0.18s ease;
}

.ebn-meta__list a:hover {
  color: var(--blue);
}

.ebn-bar {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: var(--bar-h);
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 0 12px 0 var(--inset-x);
  z-index: 2;
}

.ebn-logo {
  display: inline-flex;
  align-items: center;
  line-height: 1;
}

.ebn-logo__text {
  font-size: 26px;
  font-weight: 820;
  letter-spacing: -0.055em;
  color: var(--ink);
  line-height: 1;
  user-select: none;
}

.ebn-toggle {
  position: relative;
  width: var(--toggle);
  height: var(--toggle);
  border-radius: var(--r-toggle);
  background: var(--blue);
  color: #fff;
  display: grid;
  place-items: center;
  flex-shrink: 0;
  overflow: hidden;
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.18),
    0 1px 2px rgba(47, 107, 255, 0.25);
  transition: background 0.18s ease, transform 0.15s ease;
}

.ebn-toggle:hover {
  background: var(--blue-hover);
}

.ebn-toggle:active {
  transform: scale(0.96);
}

.ebn-toggle__icon {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  will-change: opacity, transform;
}

.ebn-toggle__icon--close {
  opacity: 0;
  transform: scale(0.5) rotate(-45deg);
}

.ebn-toggle__icon--close svg {
  width: 16px;
  height: 16px;
}

.ebn-toggle__icon--menu {
  opacity: 1;
  transform: scale(1);
  flex-direction: column;
  gap: 5px;
}

.ebn-toggle__line {
  display: block;
  width: 15px;
  height: 2px;
  border-radius: 99px;
  background: #fff;
  flex-shrink: 0;
}

.ebn-toggle:focus-visible {
  outline: 2px solid var(--ink);
  outline-offset: 3px;
}

.ebn-menu__link:focus-visible,
.ebn-meta__list a:focus-visible,
.ebn-logo:focus-visible {
  outline: 2px solid var(--blue);
  outline-offset: 3px;
  border-radius: 4px;
}

@media (max-width: 460px) {
  :root {
    --pill-w: min(300px, calc(100vw - 32px));
    --card-w: min(420px, calc(100vw - 24px));
    --card-h: min(540px, calc(100dvh - 48px));
    --inset-x: 26px;
  }

  .ebn-menu__link {
    font-size: 40px;
  }

  .ebn-open {
    padding: 28px var(--inset-x) 4px;
  }

  .ebn-meta__list a {
    font-size: 16px;
  }
}
````

## Source — script.js
````javascript
(() => {
  "use strict";

  if (typeof gsap === "undefined") {
    console.error("GSAP is required");
    return;
  }

  if (typeof CustomEase !== "undefined") {
    CustomEase.create("nav", "0.32, 0.72, 0, 1");
    CustomEase.create("navSoft", "0.4, 0, 0.1, 1");
  }
  const ease = typeof CustomEase !== "undefined" ? "nav" : "power3.out";
  const easeSoft = typeof CustomEase !== "undefined" ? "navSoft" : "power2.inOut";

  const ebn = document.getElementById("ebn");
  const shell = document.getElementById("shell");
  const openView = document.getElementById("openView");
  const toggle = document.getElementById("toggle");
  const menuIcon = toggle?.querySelector(".ebn-toggle__icon--menu");
  const closeIcon = toggle?.querySelector(".ebn-toggle__icon--close");
  const menuLinks = gsap.utils.toArray("[data-menu-link]");
  const meta = document.querySelector("[data-meta]");

  if (!ebn || !shell || !toggle) return;

  const root = getComputedStyle(document.documentElement);
  const px = (name, fb) => parseFloat(root.getPropertyValue(name)) || fb;

  const PILL_W = px("--pill-w", 300);
  const PILL_H = px("--pill-h", 68);
  const CARD_W = px("--card-w", 420);
  const CARD_H = px("--card-h", 540);
  const R_PILL = px("--r-pill", 16);
  const R_CARD = px("--r-card", 22);

  const SHADOW_CLOSED =
    "0 1px 2px rgba(0,0,0,0.04), 0 4px 12px rgba(0,0,0,0.06), 0 16px 40px rgba(0,0,0,0.08)";
  const SHADOW_OPEN =
    "0 2px 4px rgba(0,0,0,0.03), 0 8px 20px rgba(0,0,0,0.06), 0 24px 56px rgba(0,0,0,0.1)";

  const DUR = 0.58;

  const focusables = gsap.utils.toArray(
    openView.querySelectorAll("a, button, [tabindex]")
  );

  function setA11y(open) {
    ebn.dataset.state = open ? "open" : "closed";
    toggle.setAttribute("aria-expanded", String(open));
    toggle.setAttribute("aria-label", open ? "Close menu" : "Open menu");
    openView.setAttribute("aria-hidden", String(!open));
    focusables.forEach((el) => {
      if (open) el.removeAttribute("tabindex");
      else el.setAttribute("tabindex", "-1");
    });
  }

  gsap.set(ebn, {
    width: PILL_W,
    height: PILL_H,
    force3D: true,
  });
  gsap.set(shell, {
    borderRadius: R_PILL,
    boxShadow: SHADOW_CLOSED,
    force3D: true,
  });
  gsap.set(openView, { autoAlpha: 0 });
  gsap.set(menuLinks, { y: 18, autoAlpha: 0 });
  gsap.set(meta, { y: 12, autoAlpha: 0 });
  gsap.set(menuIcon, { autoAlpha: 1, scale: 1, rotation: 0 });
  gsap.set(closeIcon, { autoAlpha: 0, scale: 0.5, rotation: -45 });
  setA11y(false);

  const tl = gsap.timeline({
    paused: true,
    defaults: { ease, overwrite: "auto" },
    onReverseComplete() {
      setA11y(false);
    },
    onComplete() {
      setA11y(true);
    },
  });

  tl
    .to(
      ebn,
      {
        width: CARD_W,
        height: CARD_H,
        duration: DUR,
      },
      0
    )
    .to(
      shell,
      {
        borderRadius: R_CARD,
        boxShadow: SHADOW_OPEN,
        duration: DUR,
      },
      0
    )
    .to(
      openView,
      {
        autoAlpha: 1,
        duration: 0.22,
        ease: "power2.out",
      },
      0.08
    )
    .to(
      menuLinks,
      {
        y: 0,
        autoAlpha: 1,
        duration: 0.42,
        stagger: 0.038,
        ease: easeSoft,
      },
      0.14
    )
    .to(
      meta,
      {
        y: 0,
        autoAlpha: 1,
        duration: 0.36,
        ease: easeSoft,
      },
      0.32
    )
    .to(
      menuIcon,
      {
        autoAlpha: 0,
        scale: 0.5,
        rotation: 45,
        duration: 0.28,
        ease: "power2.inOut",
      },
      0
    )
    .to(
      closeIcon,
      {
        autoAlpha: 1,
        scale: 1,
        rotation: 0,
        duration: 0.32,
        ease: "power2.out",
      },
      0.08
    );

  function open() {
    if (tl.progress() === 1 && !tl.isActive()) return;
    tl.play();
  }

  function close() {
    if (tl.progress() === 0 && !tl.isActive()) return;
    tl.reverse();
  }

  function toggleMenu() {
    tl.progress() > 0.5 ? close() : open();
  }

  toggle.addEventListener("click", (e) => {
    e.stopPropagation();
    toggleMenu();
  });

  toggle.addEventListener("keydown", (e) => {
    if (e.key === "Enter" || e.key === " ") {
      e.preventDefault();
      toggleMenu();
    }
  });

  document.addEventListener("keydown", (e) => {
    if (e.key === "Escape" && tl.progress() > 0) {
      e.preventDefault();
      close();
    }
  });

  document.addEventListener("pointerdown", (e) => {
    if (tl.progress() < 0.5) return;
    if (!ebn.contains(e.target)) close();
  });

  menuLinks.forEach((link) => {
    link.addEventListener("click", () => {
      gsap.delayedCall(0.12, close);
    });
  });

  window.expandingNav = { open, close, toggle: toggleMenu };
})();
````
