Gatefold Documentation 1.1

Header, Footer & Scroll

HTML Markup

The menu in the header. From 992px up it is a row of links, and an item with entries under it opens a dropdown. Below 992px the links fold into a panel under the header, opened by a menu button, and a dropdown expands in place.


<header class="cel-site-header">
  <a class="cel-site-brand" href="…">Your name</a>

  <button class="cel-nav-toggle" type="button" aria-expanded="false"
          aria-controls="cel-site-menu" aria-label="Menu">
    <span class="cel-nav-toggle-open"><i data-lucide="menu" aria-hidden="true"></i></span>
    <span class="cel-nav-toggle-close"><i data-lucide="x" aria-hidden="true"></i></span>
  </button>

  <nav class="cel-site-nav" aria-label="Main">
    <ul class="cel-site-menu" id="cel-site-menu">
      <li><a class="cel-site-link" href="./index.html" aria-current="page">Home</a></li>
      <li><a class="cel-site-link" href="./about.html">About</a></li>

      <li class="cel-has-sub">
        <button class="cel-site-link cel-site-parent" type="button">
          Resources <i data-lucide="chevron-down" aria-hidden="true"></i>
        </button>
        <ul class="cel-sub-menu">
          <li><a href="…">Documentation</a></li>
          <li><a href="…">Changelog</a></li>
        </ul>
      </li>
    </ul>
  </nav>
</header>
Hook Where Purpose
.cel-nav-toggle in the header, before the nav The menu button, shown below 992px. Its aria-controls names the list.
.cel-site-menu the list inside the nav The links: a row from 992px, the panel's rows below.
aria-current="page" on a link Marks the page you are on with the red dot. One per page, set by hand.
.cel-has-sub on an item An item with a dropdown: a .cel-site-parent button, then a .cel-sub-menu list.

Why does a button, not a link, open a dropdown? A link that also opens a list means two things on one tap, and a touch screen has no hover to tell them apart. The button only opens; every destination is a link inside the list. To add a page, add an <li>; to drop the dropdown, delete the .cel-has-sub item — nothing else needs changing.

celNav.init()

Binds the menu button, every dropdown, and the ways they close.


Initialization

celNav.init();

Called for you on DOMContentLoaded, right after the icons are drawn, at the bottom of main.js. The script is assets/js/nav.js and needs nothing else. Safe to call again: a nav already bound is skipped.

Custom Configuration

Pass an object to the call in main.js, for example celNav.init({ closeDelay: 300 }).

Option Default Description
breakpoint 992 Width in px from which the menu is a row. Keep it equal to the lg breakpoint in _variables.scss, where the CSS switches.
closeDelay 150 Milliseconds a dropdown opened by hover waits before closing, so the pointer can reach the list.

Behavior

  • A dropdown opens on click, tap and Enter; with a mouse, also on hover. One opened by hover closes when the pointer leaves; one opened by a click stays until you click elsewhere, press Escape or Tab out of it. One is open at a time.
  • Escape closes the innermost thing open — the dropdown first, then the panel — and hands focus back to the button that opened it.
  • A tap outside the open panel only closes it. It does not also follow a link or open a photograph lying under the panel's edge.
  • Nothing of the menu stays open under an overlay, or across the breakpoint when a window is resized.
  • The buttons say their state with aria-expanded; each dropdown button names its list with aria-controls, filled in by the script.

Styling

The dropdowns and the phone panel share one surface, set by two tokens in _colors.scss.


:root {
  --cel-menu-bg:    var(--cel-overlay-bg);   /* the overlay's warm sheet */
  --cel-menu-color: var(--cel-ink);          /* ink on it: 7.26:1 */
}

By default the menu wears the overlay's sheet, so a menu and a project detail read as one material. They are separate tokens, so either can change without the other. The pointer's highlight swaps the two colours; the page you are on gets a faint wash of the ink.

The styles live in Source/assets/sass/components/_nav.scss. The header itself, the brand and the link type are in _shell.scss.

One line of small print at the foot of the About page: the copyright on the left, the licence on the right.


</main>

<footer class="cel-site-footer">
  <p><small>&copy; 2026 Gatefold by <a href="…">Celestial Themes</a>.</small></p>
  <p><small>Free and open source under the <a href="…" rel="license">MIT License</a>.</small></p>
</footer>
  • It goes after <main>, inside .cel-shell, not inside <main> or an <article>. There it is the page’s contentinfo landmark, which screen readers list next to the header and the main content.
  • <small> marks the text as small print, the element HTML has for a copyright. The footer keeps it at the body size.
  • rel="license" on the licence link says what the link is, for browsers and search engines.
  • Two paragraphs, side by side from 768px and stacked below. Add a third and it joins the row.
  • The carousel page has none: its strip is flush with the bottom of the screen. A footer there would sit under the strip; the shell already has a row for it, so the markup is all it takes.

White text, as on the rest of the page. The links are underlined, so they read as links in text of the same colour, and turn red under the pointer, as in the header. The styles are .cel-site-footer in _shell.scss.

Smooth Scroll

Lenis smooths the mouse wheel on the About page, with the settings celestialthemes.com runs. The carousel page scrolls natively.


Initialization

<script src="assets/js/lenis.js"></script>
<script src="assets/js/smooth-scroll.js"></script>

A page opts in by loading these two files before main.js, which then calls celSmoothScroll.init() on DOMContentLoaded. A page without them scrolls the way the browser does. To smooth another page, copy the two tags from about.html.

Custom Configuration

Pass an object to the call in main.js, for example celSmoothScroll.init({ lerp: 0.08 }).

Option Default Description
lerp 0.1 How fast the page catches up with the wheel. Lower is smoother and slower.
wheelMultiplier 1 Distance per wheel step.
smoothWheel true Smooth the mouse wheel and the trackpad.
syncTouch false Leave touch screens to their own scrolling, which is what a phone user expects.
minDuration 1 Seconds a short jump to a #link takes.
maxDuration 3 Seconds a jump across the whole page takes. Distances in between scale.

Behavior

  • Off under prefers-reduced-motion: Lenis never starts and the page scrolls natively.
  • Links to an #id on the same page glide there, and stop short by the target’s scroll-margin-top. Links to other pages, a bare #, new tabs and modified clicks stay with the browser.
  • It moves the real window scroll, not a wrapper, so position: sticky, deep links and window.scrollY keep working.
  • Paused while an overlay is open; the overlay’s panel scrolls on its own. Mark any other scrolling box with data-lenis-prevent for the same.
  • celSmoothScroll.lenis is the running instance, or null, for calls such as celSmoothScroll.lenis.scrollTo(0).