Skip to content

Commit e29fe9b

Browse files
committed
feat(ui): Edit in mast, system theme preference, sidebar nav hierarchy
Made-with: Cursor
1 parent 6dd6330 commit e29fe9b

6 files changed

Lines changed: 188 additions & 71 deletions

File tree

site/supplemental-ui/css/site-ftn-docs.css

Lines changed: 90 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -351,15 +351,15 @@ html.dark-theme .ftn-doc-mast {
351351
padding: 0 0 0.25rem 0;
352352
}
353353

354-
/* ——— Page title + Edit this page: title truncates, edit always visible ——— */
354+
/* ——— Page title (Edit link lives in the mast next to the breadcrumb trail) ——— */
355355
/* Antora: .doc h1 { margin: 1rem 0 0 } still matches the layout h1 (not only .doc > h1:first-child).
356-
Zero it here so the only top inset is .content’s padding. Font/line-height do not add margin. */
356+
Zero it here so the only top inset is .content’s padding. */
357357
.doc .ftn-page-header,
358358
.ftn-article .ftn-page-header {
359359
display: flex;
360360
flex-wrap: nowrap;
361361
align-items: flex-start;
362-
justify-content: space-between;
362+
justify-content: flex-start;
363363
gap: 0.75rem 1rem;
364364
min-width: 0;
365365
margin-top: 0;
@@ -393,41 +393,6 @@ html.dark-theme .ftn-doc-mast {
393393
}
394394
}
395395

396-
.doc .ftn-page-header-edit,
397-
.ftn-article .ftn-page-header-edit {
398-
flex: 0 0 auto;
399-
padding-top: 0.2rem;
400-
max-width: 100%;
401-
}
402-
403-
.doc .ftn-page-header-edit:empty,
404-
.ftn-article .ftn-page-header-edit:empty {
405-
display: none;
406-
}
407-
408-
.doc .ftn-page-header-edit .ftn-edit-this-page-link,
409-
.ftn-article .ftn-page-header-edit .ftn-edit-this-page-link {
410-
display: inline-flex;
411-
align-items: center;
412-
gap: 0.4rem;
413-
white-space: nowrap;
414-
font-size: 0.9rem;
415-
font-weight: 500;
416-
text-decoration: none;
417-
}
418-
419-
.doc .ftn-page-header-edit .ftn-edit-github-icon .ftn-ico-github,
420-
.ftn-article .ftn-page-header-edit .ftn-edit-github-icon .ftn-ico-github {
421-
width: 1em;
422-
height: 1em;
423-
flex-shrink: 0;
424-
}
425-
426-
.doc .ftn-page-header--no-title .edit-this-page,
427-
.ftn-article .ftn-page-header--no-title .edit-this-page {
428-
margin-left: auto;
429-
}
430-
431396
/* ——— Breadcrumb row in mast ——— */
432397
.ftn-breadcrumbs {
433398
display: block;
@@ -499,6 +464,55 @@ html.dark-theme .ftn-breadcrumb-trail-text {
499464
color: #e6e6e6;
500465
}
501466

467+
/* “Edit” next to page trail (compact label vs. old in-article “Edit this page”) */
468+
.ftn-breadcrumb-edit {
469+
flex: 0 0 auto;
470+
display: flex;
471+
align-items: center;
472+
margin-left: 0.1rem;
473+
list-style: none;
474+
}
475+
476+
.ftn-breadcrumb-edit::before {
477+
content: "\00b7";
478+
margin-right: 0.35rem;
479+
color: #888;
480+
font-weight: 400;
481+
user-select: none;
482+
}
483+
484+
html.dark-theme .ftn-breadcrumb-edit::before {
485+
color: #6b6b6b;
486+
}
487+
488+
.ftn-breadcrumb-edit .ftn-edit-inline-link {
489+
display: inline-flex;
490+
align-items: center;
491+
gap: 0.3rem;
492+
padding: 0.1rem 0.35rem;
493+
border-radius: 0.2rem;
494+
font-size: 0.85rem;
495+
font-weight: 500;
496+
line-height: 1.2;
497+
color: #1565c0;
498+
text-decoration: none;
499+
white-space: nowrap;
500+
}
501+
502+
.ftn-breadcrumb-edit .ftn-edit-inline-link:hover {
503+
text-decoration: underline;
504+
}
505+
506+
html.dark-theme .ftn-breadcrumb-edit .ftn-edit-inline-link {
507+
color: #4dabf7;
508+
}
509+
510+
.ftn-breadcrumb-edit .ftn-edit-github-icon .ftn-ico-github {
511+
width: 0.9em;
512+
height: 0.9em;
513+
flex-shrink: 0;
514+
}
515+
502516
.ftn-bc-menu-item a,
503517
.ftn-bc-menu-item a.ftn-cmp-version-pill {
504518
text-decoration: none;
@@ -732,11 +746,13 @@ nav.nav-menu.ftn-nav-tree-only {
732746

733747
/* Component label is <p class="ftn-nav-component-title"> — not a heading; avoids .nav-menu h3.title / .doc h3. */
734748
.ftn-nav-component-title {
735-
margin: 0 0 0.5rem 0;
736-
padding: 0;
737-
font-size: 0.95rem;
749+
margin: 0 0 0.4rem 0;
750+
padding: 0.12rem 0;
751+
font-size: 1.02rem;
738752
font-weight: 600;
739753
line-height: 1.3;
754+
letter-spacing: 0.01em;
755+
color: #2d2d2d;
740756
}
741757

742758
/* ——— Sidebar: component root + tree; hide legacy explore ——— */
@@ -753,7 +769,39 @@ nav.nav-menu.ftn-nav-tree-only {
753769
/* Dark nav link color (site-extra.css is overwritten by sync-antora-dark-theme.cjs) */
754770
html.dark-theme .nav .ftn-nav-component-title,
755771
html.dark-theme .nav .ftn-nav-component-title a {
756-
color: #c1c2c5;
772+
color: #d7d8da;
773+
}
774+
775+
/* Page tree: smaller + lighter + muted vs. component title */
776+
nav.nav-menu.nav-tree-only li.nav-item .nav-link,
777+
nav.nav-menu.ftn-nav-tree-only li.nav-item .nav-link {
778+
padding: 0.12rem 0.3rem;
779+
min-height: 0;
780+
}
781+
782+
nav.nav-menu.nav-tree-only li.nav-item .nav-text,
783+
nav.nav-menu.ftn-nav-tree-only li.nav-item .nav-text {
784+
font-size: 0.75rem;
785+
font-weight: 400;
786+
line-height: 1.35;
787+
color: #6a6a6a;
788+
}
789+
790+
nav.nav-menu.nav-tree-only .nav-item.is-current-page > .nav-link .nav-text,
791+
nav.nav-menu.ftn-nav-tree-only .nav-item.is-current-page > .nav-link .nav-text {
792+
font-weight: 500;
793+
color: #1a1a1a;
794+
}
795+
796+
html.dark-theme nav.nav-menu.nav-tree-only li.nav-item .nav-text,
797+
html.dark-theme nav.nav-menu.ftn-nav-tree-only li.nav-item .nav-text {
798+
color: #8f9194;
799+
}
800+
801+
html.dark-theme nav.nav-menu.nav-tree-only .nav-item.is-current-page > .nav-link .nav-text,
802+
html.dark-theme nav.nav-menu.ftn-nav-tree-only .nav-item.is-current-page > .nav-link .nav-text {
803+
color: #d4d4d4;
804+
font-weight: 500;
757805
}
758806

759807
.nav-panel-explore {

site/supplemental-ui/js/site-dark-mode.js

Lines changed: 67 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,46 @@
11
(function () {
2-
const themeKey = "antora-theme";
2+
const MODE_KEY = "antora-theme-mode";
3+
const LEGACY_KEY = "antora-theme";
34
const html = document.documentElement;
45
const darkThemeClass = "dark-theme";
56

6-
function setTheme(theme) {
7-
if (theme === "dark") {
7+
function getMode() {
8+
const m = localStorage.getItem(MODE_KEY);
9+
if (m === "system" || m === "dark" || m === "light") return m;
10+
const leg = localStorage.getItem(LEGACY_KEY);
11+
if (leg === "dark" || leg === "light") {
12+
localStorage.setItem(MODE_KEY, leg);
13+
localStorage.removeItem(LEGACY_KEY);
14+
return leg;
15+
}
16+
return "system";
17+
}
18+
19+
function applyVisibleTheme() {
20+
const mode = getMode();
21+
let useDark;
22+
if (mode === "system") {
23+
useDark = window.matchMedia("(prefers-color-scheme: dark)").matches;
24+
} else {
25+
useDark = mode === "dark";
26+
}
27+
if (useDark) {
828
html.classList.add(darkThemeClass);
929
} else {
1030
html.classList.remove(darkThemeClass);
1131
}
12-
localStorage.setItem(themeKey, theme);
1332
updateToggleLabel();
1433
}
1534

35+
function setMode(next) {
36+
if (next === "system") {
37+
localStorage.setItem(MODE_KEY, "system");
38+
} else {
39+
localStorage.setItem(MODE_KEY, next);
40+
}
41+
applyVisibleTheme();
42+
}
43+
1644
function isDark() {
1745
return html.classList.contains(darkThemeClass);
1846
}
@@ -23,31 +51,36 @@
2351
if (isDark()) {
2452
toggle.innerHTML =
2553
'<svg class="theme-icon" viewBox="0 0 24 24" aria-hidden="true" focusable="false"><circle cx="12" cy="12" r="5"></circle><line x1="12" y1="1" x2="12" y2="3"></line><line x1="12" y1="21" x2="12" y2="23"></line><line x1="4.22" y1="4.22" x2="5.64" y2="5.64"></line><line x1="18.36" y1="18.36" x2="19.78" y2="19.78"></line><line x1="1" y1="12" x2="3" y2="12"></line><line x1="21" y1="12" x2="23" y2="12"></line><line x1="4.22" y1="19.78" x2="5.64" y2="18.36"></line><line x1="18.36" y1="5.64" x2="19.78" y2="4.22"></line></svg>';
26-
toggle.setAttribute("aria-label", "Light mode");
54+
toggle.setAttribute("aria-label", "Switch to light mode");
2755
} else {
2856
toggle.innerHTML =
2957
'<svg class="theme-icon" viewBox="0 0 24 24" aria-hidden="true" focusable="false"><path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"></path></svg>';
30-
toggle.setAttribute("aria-label", "Dark mode");
58+
toggle.setAttribute("aria-label", "Switch to dark mode");
3159
}
3260
}
3361

3462
function toggleTheme() {
35-
setTheme(isDark() ? "light" : "dark");
36-
// Avoid :focus “stuck” hover look after mouse click (button stays focused)
63+
setMode(isDark() ? "light" : "dark");
3764
const toggle = document.getElementById("theme-toggle");
3865
if (toggle) toggle.blur();
3966
}
4067

41-
function applyInitialTheme() {
42-
const savedTheme = localStorage.getItem(themeKey);
43-
if (savedTheme === "dark" || savedTheme === "light") {
44-
setTheme(savedTheme);
45-
return;
68+
function onSystemThemeChange() {
69+
const mode = getMode();
70+
if (mode === "system") {
71+
applyVisibleTheme();
72+
} else {
73+
setMode("system");
4674
}
47-
if (window.matchMedia("(prefers-color-scheme: dark)").matches) {
48-
setTheme("dark");
75+
}
76+
77+
function applyInitialTheme() {
78+
applyVisibleTheme();
79+
const mq = window.matchMedia("(prefers-color-scheme: dark)");
80+
if (typeof mq.addEventListener === "function") {
81+
mq.addEventListener("change", onSystemThemeChange);
4982
} else {
50-
setTheme("light");
83+
mq.addListener(onSystemThemeChange);
5184
}
5285
}
5386

@@ -93,16 +126,26 @@
93126
function getRepoUrl() {
94127
const meta = document.querySelector('meta[name="antora-repo-url"]');
95128
if (meta && meta.content) return meta.content;
96-
const editLink = document.querySelector('.navbar-end a[href*="/edit/"], .navbar-end a[href*="/-/edit/"], .navbar-end a[href*="/blob/"]');
129+
const editLink = document.querySelector(
130+
'.navbar-end a[href*="/edit/"], .navbar-end a[href*="/-/edit/"], .navbar-end a[href*="/blob/"], a.ftn-edit-inline-link[href*="/"]'
131+
);
97132
if (editLink && editLink.href) {
98133
try {
99134
const u = new URL(editLink.href);
100135
const pathParts = u.pathname.split("/").filter(Boolean);
101-
if (u.hostname.includes("github") && pathParts.length >= 2) return u.origin + "/" + pathParts.slice(0, 2).join("/");
102-
if (u.hostname.includes("gitlab") && pathParts.length >= 2) return u.origin + "/" + pathParts.slice(0, 2).join("/");
103-
if (u.hostname.includes("bitbucket") && pathParts.length >= 2) return u.origin + "/" + pathParts.slice(0, 2).join("/");
136+
if (u.hostname.includes("github") && pathParts.length >= 2) {
137+
return u.origin + "/" + pathParts.slice(0, 2).join("/");
138+
}
139+
if (u.hostname.includes("gitlab") && pathParts.length >= 2) {
140+
return u.origin + "/" + pathParts.slice(0, 2).join("/");
141+
}
142+
if (u.hostname.includes("bitbucket") && pathParts.length >= 2) {
143+
return u.origin + "/" + pathParts.slice(0, 2).join("/");
144+
}
104145
if (pathParts.length >= 2) return u.origin + "/" + pathParts.slice(0, 2).join("/");
105-
} catch {}
146+
} catch {
147+
// ignore
148+
}
106149
}
107150
return null;
108151
}
@@ -116,7 +159,9 @@
116159
const u = new URL(script.src);
117160
u.pathname = u.pathname.replace(/\/[^/]*$/, "/");
118161
return u.pathname + u.search || ".";
119-
} catch (_e) {}
162+
} catch (_e) {
163+
// ignore
164+
}
120165
}
121166
return ".";
122167
}

site/supplemental-ui/partials/article.hbs

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,7 @@
22
{{#with page.title}}
33
<div class="ftn-page-header">
44
<h1 class="page ftn-page-title">{{{this}}}</h1>
5-
<div class="ftn-page-header-edit">{{> edit-this-page}}</div>
65
</div>
7-
{{else}}
8-
<div class="ftn-page-header ftn-page-header--no-title">{{> edit-this-page}}</div>
96
{{/with}}
107
{{{page.contents}}}
118
{{> pagination}}

site/supplemental-ui/partials/breadcrumbs.hbs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,5 +58,6 @@
5858
</li>
5959
{{/each}}
6060
{{/if}}
61+
{{> edit-breadcrumb}}
6162
</ul>
6263
</nav>
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
{{#if (and page.fileUri (not env.CI))}}
2+
<li class="ftn-breadcrumb-edit" role="presentation">
3+
<a class="ftn-edit-inline-link" href="{{page.fileUri}}" title="Edit this page" aria-label="Edit this page">
4+
<span class="ftn-edit-github-icon" aria-hidden="true">{{> ftn-icon-github-inline}}</span>
5+
<span class="ftn-edit-inline-text">Edit</span>
6+
</a>
7+
</li>
8+
{{else if (and page.editUrl (or env.FORCE_SHOW_EDIT_PAGE_LINK (not page.origin.private)))}}
9+
<li class="ftn-breadcrumb-edit" role="presentation">
10+
<a class="ftn-edit-inline-link" href="{{page.editUrl}}" title="Edit this page" aria-label="Edit this page">
11+
<span class="ftn-edit-github-icon" aria-hidden="true">{{> ftn-icon-github-inline}}</span>
12+
<span class="ftn-edit-inline-text">Edit</span>
13+
</a>
14+
</li>
15+
{{/if}}

site/supplemental-ui/partials/head-meta.hbs

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,21 @@
88
{{/if}}
99
<script>
1010
(function () {
11-
const themeKey = 'antora-theme'
12-
const savedTheme = localStorage.getItem(themeKey)
13-
if (savedTheme === 'dark' || (!savedTheme && window.matchMedia('(prefers-color-scheme: dark)').matches)) {
14-
document.documentElement.classList.add('dark-theme')
11+
const MODE = 'antora-theme-mode'
12+
const LEGACY = 'antora-theme'
13+
function mode() {
14+
const m = localStorage.getItem(MODE)
15+
if (m === 'system' || m === 'dark' || m === 'light') return m
16+
const o = localStorage.getItem(LEGACY)
17+
if (o === 'dark' || o === 'light') return o
18+
return 'system'
1519
}
20+
function isDark() {
21+
const m = mode()
22+
if (m === 'dark') return true
23+
if (m === 'light') return false
24+
return window.matchMedia('(prefers-color-scheme: dark)').matches
25+
}
26+
if (isDark()) document.documentElement.classList.add('dark-theme')
1627
})()
1728
</script>

0 commit comments

Comments
 (0)