diff --git a/assets/diary/calendar.js b/assets/diary/calendar.js
index a08c62c..1752da3 100644
--- a/assets/diary/calendar.js
+++ b/assets/diary/calendar.js
@@ -2,6 +2,8 @@
var cal = document.querySelector(".diary-cal");
if (!cal) return;
+ cal.querySelectorAll(".dropdown > button").forEach(wireDropdown);
+
var toggle = document.createElement("button");
toggle.type = "button";
toggle.className = "panel-toggle";
@@ -13,20 +15,9 @@
});
var main = document.querySelector("main");
- if (main) {
- main.parentNode.insertBefore(toggle, main);
+ if (!main) return;
+ main.parentNode.insertBefore(toggle, main);
+ if (window.matchMedia("(max-width: 1100px)").matches) {
main.parentNode.insertBefore(cal, main);
}
-
- cal.querySelectorAll(".dropdown > button").forEach(wireDropdown);
-
- var pageHeader = document.querySelector("header");
- function updateTop() {
- if (!pageHeader || getComputedStyle(cal).position !== "fixed") return;
- var rect = pageHeader.getBoundingClientRect();
- cal.style.top = Math.max(8, rect.bottom + 8) + "px";
- }
- window.addEventListener("scroll", updateTop, { passive: true });
- window.addEventListener("resize", updateTop);
- updateTop();
})();
diff --git a/assets/page/actions.js b/assets/page/actions.js
index f9edc9e..f879e90 100644
--- a/assets/page/actions.js
+++ b/assets/page/actions.js
@@ -125,5 +125,27 @@ function deletePage() {
}
document.addEventListener('DOMContentLoaded', function () {
- wireDropdown(document.querySelector('[data-action="actions-drop"]'));
+ var panel = document.querySelector('.actions');
+ if (!panel) return;
+
+ var toggle = document.createElement('button');
+ toggle.type = 'button';
+ toggle.className = 'panel-toggle';
+ toggle.textContent = 'Actions';
+ toggle.setAttribute('aria-expanded', 'false');
+ toggle.addEventListener('click', function () {
+ var open = panel.classList.toggle('is-open');
+ toggle.setAttribute('aria-expanded', open ? 'true' : 'false');
+ });
+
+ var main = document.querySelector('main');
+ if (!main) return;
+ // Insert at the top of main's parent (right after the header) so the
+ // ACTIONS toggle sits above any toggles other panels may have added.
+ var header = document.querySelector('header');
+ var anchor = header ? header.nextSibling : main.parentNode.firstChild;
+ main.parentNode.insertBefore(toggle, anchor);
+ if (window.matchMedia('(max-width: 1100px)').matches) {
+ main.parentNode.insertBefore(panel, toggle.nextSibling);
+ }
});
diff --git a/assets/page/fab.js b/assets/page/fab.js
deleted file mode 100644
index 3ef6794..0000000
--- a/assets/page/fab.js
+++ /dev/null
@@ -1,18 +0,0 @@
-// Lift the floating action button above the footer when the footer is on
-// screen, so it never overlaps the request-time line or the companion icon.
-// Mirrors the TOC's header-aware top-offset behaviour in toc.js.
-(function () {
- var fab = document.querySelector(".fab");
- var footer = document.querySelector("footer");
- if (!fab || !footer) return;
-
- function updateBottom() {
- var rect = footer.getBoundingClientRect();
- var overlap = Math.max(0, window.innerHeight - rect.top);
- fab.style.bottom = (overlap + 16) + "px";
- }
-
- window.addEventListener("scroll", updateBottom, { passive: true });
- window.addEventListener("resize", updateBottom);
- updateBottom();
-})();
diff --git a/assets/page/main.html b/assets/page/main.html
index cbf99fc..eb70b08 100644
--- a/assets/page/main.html
+++ b/assets/page/main.html
@@ -35,20 +35,19 @@
{{end}}
{{define "extras"}}
-{{if .SidebarWidget}}{{.SidebarWidget}}{{end}}
-{{if .CanEdit}}
-
-
-
-
{{end}}
-{{end}}
diff --git a/assets/page/toc.js b/assets/page/toc.js
index bccd179..604e211 100644
--- a/assets/page/toc.js
+++ b/assets/page/toc.js
@@ -1,4 +1,4 @@
-(function () {
+document.addEventListener("DOMContentLoaded", function () {
var content = document.querySelector("main");
if (!content) return;
@@ -28,32 +28,24 @@
});
nav.appendChild(list);
- var toggle = document.createElement("button");
- toggle.type = "button";
- toggle.className = "panel-toggle";
- toggle.textContent = "Contents";
- toggle.setAttribute("aria-expanded", "false");
- toggle.addEventListener("click", function () {
+ var rail = document.querySelector(".right-rail");
+ if (!rail) {
+ rail = document.createElement("div");
+ rail.className = "right-rail";
+ document.body.appendChild(rail);
+ }
+ rail.appendChild(nav);
+
+ var fab = document.createElement("button");
+ fab.type = "button";
+ fab.className = "btn btn-fab fab";
+ fab.title = "Contents";
+ fab.setAttribute("aria-label", "Contents");
+ fab.setAttribute("aria-expanded", "false");
+ fab.textContent = "≡";
+ fab.addEventListener("click", function () {
var open = nav.classList.toggle("is-open");
- toggle.setAttribute("aria-expanded", open ? "true" : "false");
+ fab.setAttribute("aria-expanded", open ? "true" : "false");
});
-
- var main = document.querySelector("main");
- if (main) {
- main.parentNode.insertBefore(toggle, main);
- main.parentNode.insertBefore(nav, main);
- } else {
- document.body.appendChild(toggle);
- document.body.appendChild(nav);
- }
-
- var pageHeader = document.querySelector("header");
- function updateTop() {
- if (!pageHeader || getComputedStyle(nav).position !== "fixed") return;
- var rect = pageHeader.getBoundingClientRect();
- nav.style.top = Math.max(8, rect.bottom + 8) + "px";
- }
- window.addEventListener("scroll", updateTop, { passive: true });
- window.addEventListener("resize", updateTop);
- updateTop();
-})();
+ document.body.appendChild(fab);
+});
diff --git a/assets/style.css b/assets/style.css
index 1215277..e2f49ca 100644
--- a/assets/style.css
+++ b/assets/style.css
@@ -348,6 +348,12 @@ main > h2 {
z-index: 50;
}
+/* Standalone FAB buttons (page TOC) are mobile-only.
+ Wrapped FABs (search actions dropdown) stay visible on desktop. */
+button.fab {
+ display: none;
+}
+
.btn-fab {
background: var(--bg-panel);
border: 1px solid var(--secondary);
@@ -540,6 +546,43 @@ hr {
background: var(--primary-hover);
}
+/* === Right rail === */
+.right-rail {
+ position: fixed;
+ top: 1rem;
+ right: 1rem;
+ width: 14rem;
+ max-height: calc(100vh - 2rem);
+ overflow-y: auto;
+ display: flex;
+ flex-direction: column;
+ gap: 1rem;
+ z-index: 10;
+}
+
+/* Panels share visual treatment when inside the rail */
+.actions,
+.right-rail .toc,
+.right-rail .diary-cal {
+ position: static;
+ top: auto;
+ left: auto;
+ right: auto;
+ width: auto;
+ max-height: none;
+ border: 1px solid var(--secondary);
+ background: var(--bg);
+ padding: 0.5rem 0.75rem;
+ font-size: 0.85rem;
+}
+
+.actions {
+ display: flex;
+ flex-direction: column;
+ align-items: flex-start;
+ gap: 0.15rem;
+}
+
/* === Table of contents === */
.toc {
position: fixed;
@@ -832,6 +875,16 @@ hr {
/* === Responsive === */
@media (max-width: 1100px) {
+ .right-rail {
+ position: static;
+ width: auto;
+ max-height: none;
+ overflow: visible;
+ display: contents;
+ }
+ button.fab {
+ display: inline-flex;
+ }
.panel-toggle {
display: block;
background: none;
@@ -854,7 +907,7 @@ hr {
.panel-toggle[aria-expanded="true"]::before {
content: "▾ ";
}
- .toc,
+ .actions,
.diary-cal {
position: static;
display: none;
@@ -863,10 +916,29 @@ hr {
margin: 0 auto 1rem;
max-height: none;
}
- .toc.is-open,
+ .actions.is-open {
+ display: flex;
+ }
.diary-cal.is-open {
display: block;
}
+ /* TOC on mobile is a floating overlay toggled by the FAB. */
+ .toc {
+ position: fixed;
+ bottom: 5rem;
+ right: 1rem;
+ top: auto;
+ left: auto;
+ width: calc(100% - 2rem);
+ max-width: 20rem;
+ max-height: calc(100vh - 8rem);
+ overflow-y: auto;
+ display: none;
+ z-index: 60;
+ }
+ .toc.is-open {
+ display: block;
+ }
}
@media (max-width: 600px) {