318ff45f727a
fold the navbar links behind a menu button on phones, wrapping them when the script is absent (v3.0.168)
- time
- 2026-10-01 13:50 UTC · 1 week ago
- machine
- Junes-MacBook-Pro.local
- agent
- claude-opus-5-5
- session
- mobile nav menu
- parent
- a4e1f5407345
↓ kepr-318ff45f.tar.gzatlas at this save
files changed3 files
package.json+1 −1public/css/kepr.css+68 −1public/js/kepr.js+34 −1package.json+1 −1
@@ -1,6 +1,6 @@11 {22 "name": "kepr",3- "version": "3.0.167",3+ "version": "3.0.168",44 "private": true,55 "type": "module",66 "scripts": {public/css/kepr.css+68 −1
@@ -135,6 +135,9 @@135135 .nav-logout {136136 margin: 0;137137 }138+.nav-toggle {139+ display: none;140+}138141 .nav-logout button {139142 border: 0;140143 padding: 0;@@ -1259,9 +1262,70 @@12591262 body > nav {12601263 padding: 20px 16px;12611264 }1265+ /* Without the menu script the links wrap in the bar instead of running off it. */12621266 .nav-links {1263- gap: 18px;1267+ flex-wrap: wrap;1268+ justify-content: flex-end;1269+ gap: 8px 18px;1270+ min-width: 0;12641271 }1272+ .nav--menu {1273+ position: relative;1274+ }1275+ .nav--menu .nav-toggle {1276+ display: flex;1277+ flex-direction: column;1278+ justify-content: center;1279+ gap: 4px;1280+ width: 36px;1281+ height: 36px;1282+ margin: -8px -8px -8px auto;1283+ padding: 8px;1284+ border: 0;1285+ background: none;1286+ color: var(--text-secondary);1287+ cursor: pointer;1288+ }1289+ .nav-toggle span {1290+ height: 1.5px;1291+ background: currentColor;1292+ transition:1293+ transform 0.2s ease,1294+ opacity 0.2s ease;1295+ }1296+ .nav--open .nav-toggle {1297+ color: var(--accent);1298+ }1299+ .nav--open .nav-toggle span:nth-child(1) {1300+ transform: translateY(5.5px) rotate(45deg);1301+ }1302+ .nav--open .nav-toggle span:nth-child(2) {1303+ opacity: 0;1304+ }1305+ .nav--open .nav-toggle span:nth-child(3) {1306+ transform: translateY(-5.5px) rotate(-45deg);1307+ }1308+ .nav--menu .nav-links {1309+ display: none;1310+ }1311+ .nav--menu.nav--open .nav-links {1312+ position: absolute;1313+ top: 100%;1314+ left: 0;1315+ right: 0;1316+ z-index: 10;1317+ display: flex;1318+ flex-direction: column;1319+ gap: 0;1320+ padding: 0 16px 8px;1321+ background: var(--bg);1322+ border-bottom: 1px solid var(--border);1323+ font-size: 0.8125rem;1324+ }1325+ .nav--open .nav-links > * {1326+ padding: 14px 0;1327+ border-top: 1px solid var(--border);1328+ }12651329 main {12661330 padding: 16px 16px 80px;12671331 }@@ -2692,6 +2756,9 @@26922756 .fv-overlay,26932757 .repo-panel {26942758 animation: none;2759+ }2760+ .nav-toggle span {2761+ transition: none;26952762 }26962763 .hex-col {26972764 opacity: 0.12;public/js/kepr.js+34 −1
@@ -1,7 +1,7 @@11 // Kepr's browser islands (§18). Every page is complete without this file; it only adds the22 // ticker's pace, the drifting timeline, copy buttons, the inline panels, history's scrolling,33 // line-range highlighting, the Atlas panel, picker, filter and file viewer, the signed-in4-// editor, and the profile's glow.4+// editor, the phone nav menu, and the profile's glow.55 66 // Kepr 2's save kinds (canvas.js), first match wins: each dot's colour and strength.77 const KINDS = [@@ -644,6 +644,38 @@644644 }645645 }646646 647+// On a phone the nav links fold behind a menu button (2026-10-01). The button only exists with648+// JavaScript, so without it the links stay in the bar; the CSS hides them only on `.nav--menu`.649+function navMenu() {650+ const nav = document.querySelector("body > nav");651+ const links = nav?.querySelector(".nav-links");652+ if (!links) return;653+ links.id = "nav-links";654+ const button = element("button", "nav-toggle");655+ button.type = "button";656+ button.setAttribute("aria-label", "menu");657+ button.setAttribute("aria-controls", "nav-links");658+ button.setAttribute("aria-expanded", "false");659+ button.append(element("span", ""), element("span", ""), element("span", ""));660+ const open = () => nav.classList.contains("nav--open");661+ const set = (opened) => {662+ nav.classList.toggle("nav--open", opened);663+ button.setAttribute("aria-expanded", String(opened));664+ };665+ button.addEventListener("click", () => set(!open()));666+ document.addEventListener("click", (event) => {667+ if (open() && !nav.contains(event.target)) set(false);668+ });669+ document.addEventListener("keydown", (event) => {670+ if (event.key !== "Escape" || !open()) return;671+ set(false);672+ button.focus();673+ });674+ nav.classList.add("nav--menu");675+ // Before the links, so tabbing from the open button reaches them next.676+ links.before(button);677+}678+647679 function element(tag, className, text) {648680 const node = document.createElement(tag);649681 node.className = className;@@ -948,6 +980,7 @@948980 atlasPicker();949981 repoEditor();950982 navbarLogin();983+navMenu();951984 lineRanges();952985 atlas();953986 hexGlow();