nav
aa-nav is a small cluster of behaviours bundled around a single nav element. Pick the ones you need by composing space-separated tokens on the aa-nav attribute and adding optional sub-attributes to children of the nav.
aa-nav token | Effect |
|---|---|
| (empty) | No hide, no class. Other features (is-current tracking, indicators, section classes) still work. |
hide | CSS-translate the nav up off-screen when the user scrolls down (only past the global ~50px scroll-started threshold), slides it back in when they scroll up. |
change | Add is-scrolled to the nav once scrollY > 100. Useful for shrinking padding or changing background. |
change-150 | Same, threshold 150px (any pixel value). |
hide change | Both. Tokens are space-separated and order-independent. |
none | Don’t init at this breakpoint. Combine with | or -sm / -md / -lg / -xl suffixes. |
The hide animation is driven by CSS, not GSAP. The library reads your aa-distance / aa-duration / aa-ease attributes and writes them onto the nav element as CSS variables — --aa-nav-hide-y (the precomputed off-screen translateY percentage), --aa-nav-duration, --aa-nav-ease. A single keyframe animation scoped on body[aa-scroll-direction="down"][aa-scroll-started="true"] then drives the nav. Cheap, no scroll-event listener, no GSAP cycle.
The hide uses CSS animation (not transition) on purpose: that way you can freely add transition: padding 0.3s, background 0.3s on .is-scrolled (or any other state class on the nav) without worrying about the transition shorthand clobbering the hide. Animation and transition are independent shorthands.
The hide threshold is shared, not per-nav: the body picks up aa-scroll-started="true" once window.scrollY > 50 (set by the body scroll-state tracker) and the hide rule is gated on that. This keeps the nav put while you’re at the top of the page so a small mouse-wheel nudge downward doesn’t immediately hide it. The change token, by contrast, is per-nav and configurable via change-<px> because it just toggles a class on its own element.
| Attribute | Default | Notes |
|---|---|---|
aa-distance | 1.5 | Multiplier on the -100% translate. 1 = the full nav height; 1.5 (default) clears most drop-shadows; 2 slides further still. |
aa-duration | (init duration) | CSS transition duration in seconds. |
aa-ease | osmo | Nav-specific default — the hide animation runs via CSS keyframes, so the ease must be CSS-expressible. The init({ ease }) default (power4.out) is GSAP-only and doesn’t apply here. Accepts any v8 named ease (osmo, energy, smooth, punch, relaxed, jump, pop, anticipate, fade), a cubic-bezier(...), or a CSS keyword (linear, ease, ease-in-out, …). |
elastic and bounce aren’t expressible as a single cubic-bezier; if you pass one of those, the nav falls back to osmo and a debug warning is logged. Use a GSAP-driven approach if you really need them — but they’re rarely the right call for a sticky nav anyway.
Scroll-spy: is-current link tracking
Section titled “Scroll-spy: is-current link tracking”Any [aa-scroll-target] link inside the nav whose target selector resolves to a section automatically picks up an is-current class while that section is the one closest to the viewport’s middle. ScrollTrigger watches each section between 0% 50% and 100% 50%. Style off [aa-scroll-target].is-current for active-state styling.
This is independent of the click-to-scroll behaviour described in the scroll-target section below — both share the same attribute.
Indicators (Flip)
Section titled “Indicators (Flip)”Add an empty element inside the nav with one of these attributes and the lib will Flip-morph it to track the active or hovered link:
| Attribute | Behaviour |
|---|---|
aa-nav-current-indicator | Morphs onto whichever [aa-scroll-target] has is-current. Updates via MutationObserver and on resize. |
aa-nav-hover-indicator | Morphs onto the hovered link, returns to the current link on mouseleave of the nav. Disabled on touch devices. |
Both read aa-duration (default 0.4s) and aa-ease (default power2.out) from the indicator element itself. Indicator easing is GSAP-driven (it animates via Flip.fit), so the full GSAP ease vocabulary is available — including elastic.out and bounce.out.
The lib starts indicators at opacity: 0 until the first Flip lands; set the initial style yourself if you want a different fade-in.
Section classes
Section titled “Section classes”Add aa-nav-section="my-class" to a section anywhere on the page. While that section is in view, my-class is on the nav element. Useful for swapping the nav’s colour scheme per section (light header over a hero, dark over a content panel).
<section aa-nav-section="is-light" aa-scroll-start="top 0%" aa-scroll-end="bottom 0%">…</section><section aa-nav-section="is-dark" >…</section>aa-scroll-start / aa-scroll-end on the section override the default ScrollTrigger window of top 0% / bottom 0%.
aa-scroll-target (click-to-scroll)
Section titled “aa-scroll-target (click-to-scroll)”A small global utility (not nav-specific): clicking any [aa-scroll-target="#selector"] element smooth-scrolls to that selector. If Lenis is loaded (the default), the scroll runs through lenis.scrollTo with a quartic ease and your aa-duration. If Lenis isn’t active, the lib falls back to native window.scrollTo({ behavior: 'smooth' }) — same aa-distance offset, no library configuration required.
| Attribute | Default | Notes |
|---|---|---|
aa-duration | 1.2 | Lenis scroll duration in seconds. Ignored on the native fallback (browser controls duration). |
aa-distance | 0 | Pixel offset applied to the target’s top — negative pulls the target above the viewport edge. |
Body scroll-state
Section titled “Body scroll-state”Two attributes the lib writes on <body> that any CSS rule can hook into. Always-on; opt out with init({ scrollState: false }).
| Body attribute | Values | When |
|---|---|---|
aa-scroll-direction | up / down | Flips when scroll movement exceeds 5px in the new direction. |
aa-scroll-started | true / false | Flips to true once scrollY > 50. Used by the nav-hide CSS rule to suppress the hide animation while at the top of the page. |
body[aa-scroll-direction="down"] .my-floating-button { opacity: 0; pointer-events: none; }body[aa-scroll-started="true"] .hero { background: var(--page-bg-pinned); }Required GSAP plugins
Section titled “Required GSAP plugins”ScrollTrigger for is-current tracking and section classes, Flip for the indicators. The hide / change / scroll-target / scroll-state behaviours all run without any GSAP plugin (CSS variables + plain scroll listeners). If Flip is missing, indicators silently no-op.
Live demo — sticky pill nav with hover + current indicators and section colour swap
Section titled “Live demo — sticky pill nav with hover + current indicators and section colour swap”A persistent (non-hiding) sticky nav. The current-link indicator (lime) sits behind the active link permanently; the hover indicator (accent-colour border, no fill) slides over whichever link the cursor is on and returns to the current link on mouseleave. While the third section is in view, aa-nav-section="is-accent" flips the nav into its accent colour scheme.
aa-nav (no token — no hide, no is-scrolled). Indicators carry aa-duration="0.45" + aa-ease="power3.out" (current) and aa-duration="0.35" + aa-ease="power2.out" (hover). Both pills get border-radius: 999px via CSS so Flip morphs between rounded shapes.
overview
The current-link indicator (lime pill) follows whichever section’s middle is in the viewport. Click any link to scroll to that section.
approach
Hover any link and the accent-bordered pill slides over it via Flip. Move the cursor away — it returns to the current pill.
work
This section adds is-accent to the nav. The whole bar inverts; the current pill flips to white so it stays readable on the dark background.
contact
Scrolling back up cleans up the section class in reverse order.
Live demo — same nav, hides on scroll-down
Section titled “Live demo — same nav, hides on scroll-down”Identical pill styling, only the current indicator (no hover indicator) and an aa-nav="hide change" modifier so it slides up off-screen when you scroll down and returns on scroll-up. Once you’re past 100px the is-scrolled class kicks in too.
aa-nav="hide change" — default aa-distance="1.5" clears any drop-shadow. The current indicator carries aa-duration="0.45" + aa-ease="power3.out".
overview
Scroll down — the nav slides up out of view. Scroll back up and it returns.
approach
Once you’ve scrolled more than 100px, the nav picks up is-scrolled — the demo uses that to add a subtle drop-shadow.
work
The current pill tracks whichever section is in view, morphed via Flip.
contact
The body picks up aa-scroll-direction and aa-scroll-started attributes — the hide rule is just a CSS transform keyed on those.