Navigation
These pieces build the floating navigation that defines Joy's look: a pill-shaped topbar and a pill-shaped dock. They're optional — a site can use only the content classes (cards, buttons, forms) with none of this.
joy.js looks for these elements automatically on DOMContentLoaded, by the ids below. If one is missing from the page, that piece of setup just does nothing — you don't need to remove any code, only skip the markup.
Topbar
<nav class="joy-topbar joy-glass joy-rounded-pill">
<a href="/"><img src="/logo.png" alt="Brand" style="width:26px;height:26px;"></a>
<button type="button" class="joy-btn joy-btn-ghost joy-btn-icon" id="joy-theme-toggle"><i class="ph ph-moon"></i></button>
<div id="joy-topbar-auth-slot">
<a href="/login" class="joy-btn joy-btn-ghost"><i class="ph ph-sign-in"></i><span>Log in</span></a>
<a href="/register" class="joy-btn joy-btn-solid"><i class="ph ph-user-plus"></i><span>Sign up</span></a>
</div>
</nav>| Piece | Role |
|---|---|
#joy-theme-toggle | wires up JoyTheme.toggle() on click, if present |
#joy-topbar-auth-slot | swapped by JoyAuth.renderLoggedIn(user) after a login, no reload needed |
User menu (topbar dropdown)
<div class="joy-user-menu" id="joy-user-menu">
<button type="button" class="joy-btn joy-btn-ghost" id="joy-user-trigger" aria-haspopup="true">
<img src="/avatar.png" alt="" class="joy-user-avatar">
<span>Jane Doe</span>
<i class="ph ph-caret-down"></i>
</button>
<div class="joy-user-dropdown" id="joy-user-dropdown">
<a href="/profile" class="joy-user-dropdown-item"><i class="ph ph-user"></i> My profile</a>
<a href="/logout" class="joy-user-dropdown-item"><i class="ph ph-sign-out"></i> Log out</a>
</div>
</div>All three ids (#joy-user-menu, #joy-user-trigger, #joy-user-dropdown) are required together for the open/close toggle to work.
Bottom dock
The main floating navigation — on desktop, each item expands on hover to show its label; on mobile (≤600px) it collapses into a ‹ Icon Label › selector with arrows, and swiping switches tabs.
<nav class="joy-dock joy-glass">
<button type="button" class="joy-dock-arrow joy-dock-prev" aria-label="Previous"><i class="ph ph-caret-left"></i></button>
<a href="/" class="joy-dock-item is-active"><i class="ph ph-house"></i><span class="joy-dock-label">Home</span></a>
<a href="/products" class="joy-dock-item"><i class="ph ph-squares-four"></i><span class="joy-dock-label">Products</span></a>
<a href="/about" class="joy-dock-item"><i class="ph ph-info"></i><span class="joy-dock-label">About</span></a>
<button type="button" class="joy-dock-arrow joy-dock-next" aria-label="Next"><i class="ph ph-caret-right"></i></button>
</nav>| Detail | Behavior |
|---|---|
| Active item | the .is-active class on one .joy-dock-item |
| Clicking a link | navigates normally — the dock never intercepts a click in plain anchor mode |
| Tapping the active tab (mobile) | opens a "jump to" popup, built automatically, listing every tab |
| Dragging on mobile | follows your finger in real time, with an axis lock so it doesn't fight vertical scrolling |
The mobile dock uses touch-action and dedicated touch listeners — if your page already has its own swipe/carousel system, test the two together before shipping.