@iyulab/canopy-page 0.10.0 → 0.11.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -7,6 +7,43 @@ Notable changes to canopy-page. The format follows
7
7
  The `settings.json` contract is what consuming projects plan their upgrades around, so changes
8
8
  to it — its fields, its validation, and what the checks reject — are what this file is about.
9
9
 
10
+ ## [0.11.1] — 2026-08-17
11
+
12
+ ### Changed
13
+
14
+ - **Upgraded to canopy 0.11.1** — the sidebar's current-page highlight now fills the row instead
15
+ of just the label text, and the mobile topbar no longer stacks three separate rows of chrome
16
+ (breadcrumb dropped, search collapses to its icon and expands while focus stays inside the
17
+ search form) before a reader reaches the page. See
18
+ [canopy's own changelog](https://github.com/iyulab/canopy/blob/main/CHANGELOG.md#0111--2026-08-17)
19
+ for the full list.
20
+
21
+ ## [0.11.0] — 2026-08-17
22
+
23
+ ### Added
24
+
25
+ - **`strings.breadcrumb`**, overriding the accessible label of the topbar's new ancestor-trail
26
+ nav (canopy 0.11.0). Rides the same validated `strings` object every other reader-chrome
27
+ override already does.
28
+
29
+ ### Changed
30
+
31
+ - **Upgraded to canopy 0.11.0** — collapsible sidebar groups and the breadcrumb trail (above),
32
+ a stronger sidebar current-page tint, a placeholder and icon on the search input, styled
33
+ topbar links, and the on-page outline now following the article instead of preceding it. See
34
+ [canopy's own changelog](https://github.com/iyulab/canopy/blob/main/CHANGELOG.md#0110--2026-08-17)
35
+ for the full list.
36
+
37
+ ### Fixed
38
+
39
+ - **The mobile nav no longer opens on every page load.** canopy's shell ships the site
40
+ navigation as `<details open>` unconditionally — the one default that works with no script at
41
+ all — but on a narrow viewport, that meant a reader landed on a full navigation (rendered as a
42
+ full-screen overlay by canopy's own mobile styling) in front of the article they followed a
43
+ link to read, every single page. canopy-page's script now closes it by default on a narrow
44
+ viewport and remembers a reader's own choice to leave it open for the rest of that browser
45
+ session. A wide viewport, and a build with no script attached, are unaffected.
46
+
10
47
  ## [0.10.0] — 2026-08-11
11
48
 
12
49
  ### Added
package/README.md CHANGED
@@ -40,7 +40,12 @@ go away.
40
40
  system setting
41
41
  - **Prev/next cards** linking to a page's neighbors in the sidebar's own order, and **backlinks**
42
42
  listing every page that points to it
43
- - **A full-screen menu on narrow screens**, rather than one that pushes the page's content down
43
+ - **Sidebar groups collapse**, open exactly along the path to the page you're on and closed
44
+ everywhere else
45
+ - **A breadcrumb trail** in the topbar, when there's a title, a logo, `home`, or search for it to
46
+ sit beside
47
+ - **A full-screen menu on narrow screens** that starts closed instead of opening on every page,
48
+ remembering a reader's own choice to leave it open for the rest of that visit
44
49
  - **Sitemap and `robots.txt`**, once `siteUrl` is set
45
50
 
46
51
  See it live at <https://iyulab.github.io/canopy-page>, or read
@@ -112,7 +117,7 @@ unpublished.
112
117
  | `title` | Site name. Defaults to the folder's name |
113
118
  | `description` | Fills `<meta name="description">`, which is what link previews show |
114
119
  | `lang` | BCP 47 tag for `<html lang>`. Worth setting for any non-English site: assistive technology reads pronunciation from it |
115
- | `strings` | Overrides for the reader chrome's own text — `search`, `toggleTheme`, `siteNav`, `pageNav`, `onThisPage`, `indexTitle` (the auto-generated contents page's title/heading), `backlinks` (a page's "linked references" heading), `searchFailed` (the client search's failure message). `lang` only changes what `<html lang>` declares; this text is canopy's own UI or canopy-page's own search script, not vault content, so it stays English otherwise. No built-in translation table — the same reasoning `home.label` already follows: link text has to be written in the site's own language. Keys left out keep their English default |
120
+ | `strings` | Overrides for the reader chrome's own text — `search`, `toggleTheme`, `siteNav`, `pageNav`, `onThisPage`, `indexTitle` (the auto-generated contents page's title/heading), `backlinks` (a page's "linked references" heading), `breadcrumb` (the topbar's ancestor-trail nav's label), `searchFailed` (the client search's failure message). `lang` only changes what `<html lang>` declares; this text is canopy's own UI or canopy-page's own search script, not vault content, so it stays English otherwise. No built-in translation table — the same reasoning `home.label` already follows: link text has to be written in the site's own language. Keys left out keep their English default |
116
121
  | `icon` | Favicon, relative to the settings file. Must be a published file |
117
122
  | `tokens` | CSS of design-token overrides, relative to the settings file. Appended *after* canopy's own tokens, so a file naming one value keeps the rest. It is configuration rather than content, so — unlike `icon` and `logo` — it is excluded from the published site automatically. Absent: canopy's default palette |
118
123
  | `logo` | Image shown beside the site title, relative to the settings file. Must be a published file — the opposite direction from `tokens`, because this one is content. Rendered with an empty `alt`, deliberately: the site title beside it already names the site, so there is no separate text to give it. Absent: the sidebar header shows the title text alone |
@@ -145,6 +150,13 @@ specificity over a bare selector — so a bare `:root` appended after that block
145
150
  schemes. A one-block file naming only a light-mode colour would ship that colour onto a dark
146
151
  sidebar too.
147
152
 
153
+ Unlike `settings.json` (below), `tokens` gets no such validation — it's a plain CSS file appended
154
+ to canopy's own, and CSS silently ignores a custom property nothing reads. A file carried over
155
+ from a previous documentation tool's own token names (or a `.dark`/`.light` class toggle instead
156
+ of canopy's `[data-theme]` attribute) parses fine, builds fine, and changes nothing: the site
157
+ keeps canopy's default colours with no error anywhere. If a `tokens` override doesn't show up on
158
+ the built site, that silent mismatch — not a build failure — is the first thing to check.
159
+
148
160
  The settings file itself is never published, and neither is anything `exclude` names. A file
149
161
  named `settings.json` deeper in the site is content, and ships.
150
162
 
@@ -0,0 +1,81 @@
1
+ /**
2
+ * canopy-page's mobile nav default — vanilla JS, no dependencies. Wires the
3
+ * `<details class="canopy-nav" open>` disclosure canopy's shell always ships
4
+ * open: canopy itself writes no JavaScript (see canopy's docs/SCOPE.md), so
5
+ * a static page has no way to default that state per breakpoint on its own,
6
+ * and `open` has to pick one default for every viewport. It picks the safe
7
+ * one — a script-free reader on any screen still gets a working, fully
8
+ * visible navigation — but on a narrow viewport that leaves every fresh page
9
+ * load showing the full site navigation (canopy's shell renders an open
10
+ * `.canopy-nav` there as a full-screen overlay) in front of the article a
11
+ * reader actually followed a link to see.
12
+ *
13
+ * This script narrows that default, never widens it: a wide viewport is left
14
+ * exactly as canopy's shell already renders it (open, no toggle needed). On
15
+ * a narrow one it starts closed, unless the reader already chose to leave it
16
+ * open earlier in the same session — sessionStorage rather than
17
+ * localStorage, since a stale "open" choice from a different visit days ago
18
+ * would be more surprising than useful.
19
+ *
20
+ * `shouldOpenOnLoad` is exposed for tests: it is the one piece of this file
21
+ * with real logic, and it needs no DOM to run.
22
+ */
23
+ var CanopyMobileNav = (function () {
24
+ "use strict";
25
+
26
+ var STORAGE_KEY = "canopy-nav-open";
27
+ var NARROW_QUERY = "(max-width: 40rem)";
28
+
29
+ /**
30
+ * Whether the nav should start open, given the viewport it's rendering at
31
+ * and the reader's last explicit choice this session (undefined if they
32
+ * never toggled it). A wide viewport always opens — matching the shell's
33
+ * own always-there design there, which this script only ever narrows for
34
+ * a narrow viewport, never overrides for a wide one. A narrow viewport
35
+ * starts closed unless the reader explicitly left it open last time.
36
+ */
37
+ function shouldOpenOnLoad(isNarrowViewport, storedChoice) {
38
+ if (!isNarrowViewport) return true;
39
+ return storedChoice === "open";
40
+ }
41
+
42
+ function readStoredChoice() {
43
+ try {
44
+ return sessionStorage.getItem(STORAGE_KEY) || undefined;
45
+ } catch (e) {
46
+ // Storage access can throw (private browsing with storage disabled) —
47
+ // a reader who hits that just gets the narrow-viewport default every
48
+ // load, not a broken page.
49
+ return undefined;
50
+ }
51
+ }
52
+
53
+ function storeChoice(open) {
54
+ try {
55
+ sessionStorage.setItem(STORAGE_KEY, open ? "open" : "closed");
56
+ } catch (e) {
57
+ // Same as above: a reader who can't write storage just loses the
58
+ // memory across pages, not the toggle itself.
59
+ }
60
+ }
61
+
62
+ function main() {
63
+ var nav = document.querySelector(".canopy-nav");
64
+ if (!nav || typeof matchMedia !== "function") return;
65
+
66
+ var isNarrow = matchMedia(NARROW_QUERY).matches;
67
+ nav.open = shouldOpenOnLoad(isNarrow, readStoredChoice());
68
+
69
+ // A reader's own click (native <details> toggling, no JS involved in the
70
+ // click itself) still fires this — the same event a script-driven
71
+ // assignment above fires too, so the very first automatic close is
72
+ // recorded as "closed" for free rather than needing a separate write.
73
+ nav.addEventListener("toggle", function () {
74
+ storeChoice(nav.open);
75
+ });
76
+ }
77
+
78
+ if (typeof document !== "undefined") main();
79
+
80
+ return { shouldOpenOnLoad: shouldOpenOnLoad };
81
+ })();
@@ -34,10 +34,11 @@ const SEARCH_FAILED_DEFAULT = "Search failed to load.";
34
34
  * asset stays valid, readable JavaScript on its own.
35
35
  */
36
36
  export async function assembleScript(searchFailed) {
37
- const [search, scrollspy, themeToggle] = await Promise.all([
37
+ const [search, scrollspy, themeToggle, mobileNav] = await Promise.all([
38
38
  readAsset("search.js"),
39
39
  readAsset("scrollspy.js"),
40
40
  readAsset("theme-toggle.js"),
41
+ readAsset("mobile-nav.js"),
41
42
  ]);
42
43
  // A function replacer, not a replacement string: String.replace treats
43
44
  // "$&"/"$'"/"$$" etc. in a replacement string as patterns, and a site
@@ -45,7 +46,7 @@ export async function assembleScript(searchFailed) {
45
46
  const searchWithStrings = searchFailed === undefined
46
47
  ? search
47
48
  : search.replace(JSON.stringify(SEARCH_FAILED_DEFAULT), () => JSON.stringify(searchFailed));
48
- return `${searchWithStrings}\n${scrollspy}\n${themeToggle}`;
49
+ return `${searchWithStrings}\n${scrollspy}\n${themeToggle}\n${mobileNav}`;
49
50
  }
50
51
  /**
51
52
  * CSS canopy-page contributes on top of a site's own tokens, carried via
@@ -131,6 +131,8 @@ export interface Settings {
131
131
  indexTitle?: string;
132
132
  /** Heading over a page's list of pages that link to it. */
133
133
  backlinks?: string;
134
+ /** Accessible label for the topbar's ancestor-trail nav. */
135
+ breadcrumb?: string;
134
136
  /**
135
137
  * Message shown in place of results when the client search index fails to
136
138
  * load. This key rides the same JSON `--strings` flag as every other one
package/dist/settings.js CHANGED
@@ -67,6 +67,7 @@ export const STRINGS_KEYS = new Set([
67
67
  "onThisPage",
68
68
  "indexTitle",
69
69
  "backlinks",
70
+ "breadcrumb",
70
71
  "searchFailed",
71
72
  ]);
72
73
  export const NAV_ITEM_KEYS = new Set(["label", "path", "items"]);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@iyulab/canopy-page",
3
- "version": "0.10.0",
3
+ "version": "0.11.1",
4
4
  "description": "Authoring pipeline for documentation sites: one settings file, integrity checks, and a build.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -53,7 +53,7 @@
53
53
  "vitest": "^4.1.9"
54
54
  },
55
55
  "dependencies": {
56
- "@iyulab/canopy": "^0.10.0",
56
+ "@iyulab/canopy": "^0.11.1",
57
57
  "chokidar": "^5.0.0"
58
58
  }
59
59
  }