@rathnasgala2/template 2.0.0 → 2.2.0
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/README.md +211 -137
- package/contracts/render-policy.jcs +1 -1
- package/contracts/theme-styling-contract.jcs +1 -1
- package/package.json +5 -6
- package/src/core/errors.js +13 -13
- package/src/core/index.js +130 -75
- package/src/core/internal/appearance/base-layer.js +180 -0
- package/src/core/internal/appearance/bootstrap-script.js +11 -11
- package/src/core/internal/appearance/contract.js +59 -44
- package/src/core/internal/appearance/controller-markup.js +7 -8
- package/src/core/internal/appearance/styling-contract.js +132 -29
- package/src/core/internal/canonical-jcs.js +0 -0
- package/src/core/internal/content-security.js +0 -0
- package/src/core/internal/eleventy-render.js +52 -35
- package/src/core/internal/feeds.js +15 -15
- package/src/core/internal/fs-walk.js +2 -2
- package/src/core/internal/media/avif-probe.js +4 -4
- package/src/core/internal/media/font-policy.js +2 -2
- package/src/core/internal/media/jpeg-codec.js +0 -0
- package/src/core/internal/media/limits.js +32 -34
- package/src/core/internal/media/pipeline.js +4 -4
- package/src/core/internal/media/png-codec.js +7 -7
- package/src/core/internal/media/resize.js +2 -2
- package/src/core/internal/media/sniff.js +4 -4
- package/src/core/internal/media/theme-svg-sanitizer.js +278 -0
- package/src/core/internal/media/webp-probe.js +3 -3
- package/src/core/internal/messages.js +8 -8
- package/src/core/internal/page-kinds.js +50 -51
- package/src/core/internal/render-policy-content.js +57 -21
- package/src/core/internal/route-labels.js +5 -6
- package/src/core/internal/route.js +58 -20
- package/src/core/internal/search-index.js +17 -16
- package/src/core/internal/semver-range.js +84 -0
- package/src/core/internal/seo.js +24 -24
- package/src/core/internal/sitemap.js +11 -12
- package/src/core/internal/skeleton.js +65 -46
- package/src/core/internal/source-inventory.js +4 -5
- package/src/core/internal/text-direction.js +1 -2
- package/src/core/internal/theme-assets.js +314 -68
- package/src/core/internal/uuid.js +4 -5
- package/src/core/manifest.js +0 -0
- package/types/index.d.ts +22 -8
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The template-owned `gala-base` cascade layer (TPL-H3/TPL-M7): a
|
|
3
|
+
* stylesheet this renderer itself writes on every build, independent of
|
|
4
|
+
* whether a theme is selected, carrying sane defaults for every
|
|
5
|
+
* contract-mandated chrome element (`internal/skeleton.js`'s skip link,
|
|
6
|
+
* headings, code/table overflow, image sizing, responsive spacing) plus the
|
|
7
|
+
* one rule that makes the closed token catalog's
|
|
8
|
+
* `color-focus`/`focus-width` tokens actually paint a ring (THD-H1): a real
|
|
9
|
+
* `outline-style: solid` under `:focus-visible`.
|
|
10
|
+
*
|
|
11
|
+
* This file is not admitted through, or generated from, the theme package
|
|
12
|
+
* pipeline in `internal/theme-assets.js` — it is not subject to
|
|
13
|
+
* `check-css-hooks.mjs`'s closed selector-admission grammar and may use any
|
|
14
|
+
* selector, property or pseudo-class, including `:focus-visible`,
|
|
15
|
+
* `html`/`body` and functional colors, because it is authored by the
|
|
16
|
+
* template, not by a theme. Its bytes are a pure function of this module's
|
|
17
|
+
* own literal source, not of any per-publication or per-theme input, so a
|
|
18
|
+
* fixed, non-content-addressed output path carries no collision risk
|
|
19
|
+
* (mirrors `internal/appearance/bootstrap-script.js`'s own reasoning for its
|
|
20
|
+
* fixed path).
|
|
21
|
+
*
|
|
22
|
+
* Every custom property this file reads (`--gala-*`) is read through a
|
|
23
|
+
* fallback value, since a theme is optional (`internal/theme-assets.js`'s
|
|
24
|
+
* `defaultThemeStylesheetLinksHtml` fallback path never defines any
|
|
25
|
+
* `--gala-*` property at all) — this layer must still produce a sane,
|
|
26
|
+
* legible default with no theme selected whatsoever.
|
|
27
|
+
*
|
|
28
|
+
* Cascade-layer precedence (TPL-M7): this layer's own declarations always
|
|
29
|
+
* lose to a theme's `gala-tokens`/`gala-components`/`gala-utilities` layer
|
|
30
|
+
* declarations for the same property on the same element, because this
|
|
31
|
+
* stylesheet's own first line is the fixed `@layer gala-base, gala-tokens,
|
|
32
|
+
* gala-components, gala-utilities, gala-print;` order statement — layer
|
|
33
|
+
* order is declared once, explicitly, independent of `<link>` emission
|
|
34
|
+
* order, and independent of which optional stylesheets a theme ships (a
|
|
35
|
+
* theme that omits `utilities.css` never populates `gala-utilities`, which
|
|
36
|
+
* is harmless: an empty layer in a declared order does nothing). A bare
|
|
37
|
+
* `@layer name, name, ...;` statement is not itself a style rule, so it
|
|
38
|
+
* needs no `'unsafe-inline'`/nonce/hash exemption from the
|
|
39
|
+
* `style-src 'self'` CSP baseline the way an inline `<style>` element would
|
|
40
|
+
* (TPL-H4): it ships as the first bytes of this external, same-origin
|
|
41
|
+
* stylesheet, and `internal/eleventy-render.js` always emits this
|
|
42
|
+
* stylesheet's own `<link>` before every theme stylesheet `<link>`.
|
|
43
|
+
*/
|
|
44
|
+
|
|
45
|
+
import { ORDERED_LAYERS } from './styling-contract.js';
|
|
46
|
+
|
|
47
|
+
/** @type {string} the candidate-output-directory-relative path this
|
|
48
|
+
* stylesheet is always written to (never content-addressed; see module
|
|
49
|
+
* documentation). */
|
|
50
|
+
export const GALA_BASE_STYLESHEET_PATH = 'assets/gala-base-v1.css';
|
|
51
|
+
|
|
52
|
+
/** @type {string} the `manifestAsset`/`manifestRoute` `mediaType` for this
|
|
53
|
+
* stylesheet. */
|
|
54
|
+
export const GALA_BASE_STYLESHEET_MEDIA_TYPE = 'text/css; charset=utf-8';
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* The exact `gala-base` layer source, byte-for-byte deterministic across
|
|
58
|
+
* builds (no interpolated per-build value of any kind). Its first line is
|
|
59
|
+
* the fixed layer-order statement every published page relies on (see
|
|
60
|
+
* module documentation).
|
|
61
|
+
*
|
|
62
|
+
* @type {string}
|
|
63
|
+
*/
|
|
64
|
+
export const GALA_BASE_STYLESHEET_SOURCE = `@layer ${ORDERED_LAYERS.join(', ')};
|
|
65
|
+
|
|
66
|
+
@layer gala-base {
|
|
67
|
+
[data-gala-publication-root] {
|
|
68
|
+
box-sizing: border-box;
|
|
69
|
+
}
|
|
70
|
+
[data-gala-publication-root] *,
|
|
71
|
+
[data-gala-publication-root] *::before,
|
|
72
|
+
[data-gala-publication-root] *::after {
|
|
73
|
+
box-sizing: inherit;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/* Skip link: the first focusable element on every page, visually hidden
|
|
77
|
+
until it receives keyboard focus, so it is announced and reachable by
|
|
78
|
+
assistive technology and keyboard users without being the first visible
|
|
79
|
+
line of every published page. */
|
|
80
|
+
[data-gala-publication-root] a[href="#main-content"] {
|
|
81
|
+
position: absolute;
|
|
82
|
+
top: -9999px;
|
|
83
|
+
left: 0;
|
|
84
|
+
z-index: 9999;
|
|
85
|
+
padding: var(--gala-space-2, 0.5rem) var(--gala-space-4, 1rem);
|
|
86
|
+
background-color: var(--gala-color-surface, Canvas);
|
|
87
|
+
color: var(--gala-color-text, CanvasText);
|
|
88
|
+
}
|
|
89
|
+
[data-gala-publication-root] a[href="#main-content"]:focus-visible {
|
|
90
|
+
top: 0;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/* The one rule that makes the closed token catalog's color-focus/
|
|
94
|
+
focus-width tokens actually paint a ring: outline-style is never set by
|
|
95
|
+
the token catalog alone (outline-color/outline-width longhands paint
|
|
96
|
+
nothing without it), and this layer's own low cascade precedence means
|
|
97
|
+
any theme-declared outline-color/outline-width in gala-components still
|
|
98
|
+
wins for the same element. */
|
|
99
|
+
[data-gala-publication-root] :focus-visible {
|
|
100
|
+
outline-style: solid;
|
|
101
|
+
outline-width: var(--gala-focus-width, 2px);
|
|
102
|
+
outline-color: var(--gala-color-focus, Highlight);
|
|
103
|
+
outline-offset: 2px;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/* Type scale, expressed with clamp() so it scales between a phone and a
|
|
107
|
+
desktop viewport without a discrete breakpoint; no theme sets this. */
|
|
108
|
+
[data-gala-publication-root] h1 {
|
|
109
|
+
font-size: clamp(1.75rem, 1.4rem + 1.5vw, 2.5rem);
|
|
110
|
+
line-height: 1.2;
|
|
111
|
+
}
|
|
112
|
+
[data-gala-publication-root] h2 {
|
|
113
|
+
font-size: clamp(1.375rem, 1.2rem + 0.8vw, 1.875rem);
|
|
114
|
+
line-height: 1.25;
|
|
115
|
+
}
|
|
116
|
+
[data-gala-publication-root] h3 {
|
|
117
|
+
font-size: clamp(1.125rem, 1.05rem + 0.4vw, 1.375rem);
|
|
118
|
+
line-height: 1.3;
|
|
119
|
+
}
|
|
120
|
+
[data-gala-publication-root] h4,
|
|
121
|
+
[data-gala-publication-root] h5,
|
|
122
|
+
[data-gala-publication-root] h6 {
|
|
123
|
+
font-size: clamp(1rem, 0.96rem + 0.2vw, 1.125rem);
|
|
124
|
+
line-height: 1.35;
|
|
125
|
+
}
|
|
126
|
+
[data-gala-publication-root] p,
|
|
127
|
+
[data-gala-publication-root] li {
|
|
128
|
+
font-size: 1rem;
|
|
129
|
+
line-height: 1.6;
|
|
130
|
+
}
|
|
131
|
+
[data-gala-publication-root] code,
|
|
132
|
+
[data-gala-publication-root] pre {
|
|
133
|
+
/* Corrects the monospace optical-size mismatch against surrounding
|
|
134
|
+
proportional text at the same nominal font-size. */
|
|
135
|
+
font-size: 0.9em;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/* Overflow/wrap containment: constrains a fenced code block or an
|
|
139
|
+
unbroken long token in prose to the viewport, never the whole page. */
|
|
140
|
+
[data-gala-publication-root] pre {
|
|
141
|
+
overflow-x: auto;
|
|
142
|
+
}
|
|
143
|
+
[data-gala-publication-root] article {
|
|
144
|
+
overflow-wrap: anywhere;
|
|
145
|
+
}
|
|
146
|
+
[data-gala-publication-root] img {
|
|
147
|
+
max-width: 100%;
|
|
148
|
+
height: auto;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/* Responsive spacing: a small step at a tablet-and-up viewport width, so
|
|
152
|
+
header/main padding is not identical at 320px and 2560px. */
|
|
153
|
+
[data-gala-publication-root] header,
|
|
154
|
+
[data-gala-publication-root] footer {
|
|
155
|
+
padding: var(--gala-space-4, 1rem) var(--gala-space-4, 1rem);
|
|
156
|
+
}
|
|
157
|
+
[data-gala-publication-root] main {
|
|
158
|
+
padding: var(--gala-space-4, 1rem);
|
|
159
|
+
}
|
|
160
|
+
@media (min-width: 48rem) {
|
|
161
|
+
[data-gala-publication-root] header,
|
|
162
|
+
[data-gala-publication-root] footer {
|
|
163
|
+
padding: var(--gala-space-4, 1rem) var(--gala-space-6, 1.5rem);
|
|
164
|
+
}
|
|
165
|
+
[data-gala-publication-root] main {
|
|
166
|
+
padding: var(--gala-space-6, 1.5rem);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
@media (prefers-reduced-motion: reduce) {
|
|
171
|
+
[data-gala-publication-root] *,
|
|
172
|
+
[data-gala-publication-root] *::before,
|
|
173
|
+
[data-gala-publication-root] *::after {
|
|
174
|
+
animation-duration: 0.001ms !important;
|
|
175
|
+
animation-iteration-count: 1 !important;
|
|
176
|
+
transition-duration: 0.001ms !important;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
`;
|
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* The exact deterministic pre-paint appearance bootstrap script
|
|
3
|
-
*
|
|
4
|
-
*
|
|
2
|
+
* The exact deterministic pre-paint appearance bootstrap script: a versioned
|
|
3
|
+
* deterministic pre-paint bootstrap owned by `template`/`core` that resolves
|
|
4
|
+
* local selection plus system preference and sets
|
|
5
5
|
* the root state before first themed paint. It is covered by the artifact
|
|
6
6
|
* digest, is CSP-compatible under `script-src 'self'` (therefore an external
|
|
7
7
|
* file, not inline), touches no network, account, content or unrelated
|
|
8
|
-
* storage key, and cannot hide the document indefinitely
|
|
8
|
+
* storage key, and cannot hide the document indefinitely.
|
|
9
9
|
*
|
|
10
|
-
* This is the one and only browser bootstrap
|
|
11
|
-
*
|
|
12
|
-
* bootstrap
|
|
10
|
+
* This is the one and only browser bootstrap this renderer ever emits: the
|
|
11
|
+
* appearance controller is the only browser
|
|
12
|
+
* bootstrap it ships. Every name and value it embeds is read from
|
|
13
13
|
* `internal/appearance/contract.js`, the one module that owns them, so the
|
|
14
14
|
* server-rendered control markup and this script can never drift apart on a
|
|
15
15
|
* literal.
|
|
@@ -29,12 +29,12 @@
|
|
|
29
29
|
* any earlier: the control has not been parsed yet at phase 1's own
|
|
30
30
|
* execution point) and subscribes to live `prefers-color-scheme` changes,
|
|
31
31
|
* updating the resolved attribute only while the current selection is
|
|
32
|
-
* exactly `system
|
|
33
|
-
* overridden by later system changes
|
|
32
|
+
* exactly `system`: an explicit `light`/`dark` choice is not
|
|
33
|
+
* overridden by later system changes.
|
|
34
34
|
*
|
|
35
35
|
* Deliberately absent: no `document.documentElement.style` mutation of any
|
|
36
|
-
* kind (in particular, no hide-until-ready visibility toggle —
|
|
37
|
-
*
|
|
36
|
+
* kind (in particular, no hide-until-ready visibility toggle — this script
|
|
37
|
+
* must never hide the document indefinitely, and never needs to,
|
|
38
38
|
* since it only ever sets attributes, never inline style), no network
|
|
39
39
|
* request, no cookie, no account/tracking identifier, no storage key other
|
|
40
40
|
* than {@link APPEARANCE_STORAGE_KEY}.
|
|
@@ -1,32 +1,32 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* The single module `contracts/theme-styling-contract.jcs`
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* `templateStylingContract` catalog
|
|
7
|
-
*
|
|
2
|
+
* The single module `contracts/theme-styling-contract.jcs` targets for
|
|
3
|
+
* every Light/Dark/System appearance name this renderer owns: the
|
|
4
|
+
* `data-` attribute/class names the controller sets must be fixed
|
|
5
|
+
* constants exported from one module. The published
|
|
6
|
+
* `templateStylingContract` catalog's closed theme-package and stylesheet
|
|
7
|
+
* admission requires the root attribute row
|
|
8
8
|
* `{name:data-gala-publication-root,match:presence,values:[],role:root}` and
|
|
9
|
-
* the
|
|
10
|
-
* `{name:data-gala-resolved-color-mode,match:exact-value,values:[dark,light],role:state}
|
|
11
|
-
*
|
|
9
|
+
* the palette attribute row
|
|
10
|
+
* `{name:data-gala-resolved-color-mode,match:exact-value,values:[dark,light],role:state}`:
|
|
11
|
+
* the template renderer emits the exact root attribute and maintains
|
|
12
12
|
* exactly one `data-gala-resolved-color-mode="light"|"dark"` value on that
|
|
13
|
-
* same element before theme CSS is observed
|
|
13
|
+
* same element before theme CSS is observed.
|
|
14
14
|
*
|
|
15
|
-
* Two attributes are exposed, deliberately kept separate
|
|
16
|
-
*
|
|
17
|
-
*
|
|
15
|
+
* Two attributes are exposed, deliberately kept separate: selection and
|
|
16
|
+
* resolved state are exposed separately through template-owned root
|
|
17
|
+
* attributes; theme CSS consumes only resolved state.
|
|
18
18
|
*
|
|
19
19
|
* - {@link APPEARANCE_ROOT_ATTRIBUTE} and {@link APPEARANCE_RESOLVED_MODE_ATTRIBUTE}
|
|
20
|
-
* are the two
|
|
21
|
-
*
|
|
22
|
-
* of that contract.
|
|
20
|
+
* are the two fixed theme-facing attributes above — part of the published
|
|
21
|
+
* styling contract, never renamed here independent of that contract.
|
|
23
22
|
* - {@link APPEARANCE_SELECTION_ATTRIBUTE} is this renderer's own
|
|
24
23
|
* template-internal bookkeeping attribute (the reader's raw `light` /
|
|
25
24
|
* `dark` / `system` choice, as opposed to `system`'s *resolved* value). It
|
|
26
|
-
* is not a theme-styling-contract hook —
|
|
27
|
-
* not name it, and no theme selector may target it — but its name is
|
|
28
|
-
* fixed here, in this one module, exactly like the two
|
|
29
|
-
* attributes, so no other module ever invents its own
|
|
25
|
+
* is not a theme-styling-contract hook — the published attribute catalog
|
|
26
|
+
* does not name it, and no theme selector may target it — but its name is
|
|
27
|
+
* still fixed here, in this one module, exactly like the two
|
|
28
|
+
* contract-owned attributes, so no other module ever invents its own
|
|
29
|
+
* literal for it.
|
|
30
30
|
*
|
|
31
31
|
* Both the pre-paint bootstrap script ({@link ../appearance/bootstrap-script.js})
|
|
32
32
|
* and the server-rendered control markup ({@link ../appearance/controller-markup.js})
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
import { joinBasePathAndRoute } from '../route.js';
|
|
38
38
|
|
|
39
39
|
/**
|
|
40
|
-
* The
|
|
40
|
+
* The `templateStylingContract.publicationRootSelector` attribute:
|
|
41
41
|
* a bare-presence attribute the template always renders on the document's
|
|
42
42
|
* root element (`<html>`), independent of JavaScript (`match:presence`).
|
|
43
43
|
*
|
|
@@ -46,7 +46,7 @@ import { joinBasePathAndRoute } from '../route.js';
|
|
|
46
46
|
export const APPEARANCE_ROOT_ATTRIBUTE = 'data-gala-publication-root';
|
|
47
47
|
|
|
48
48
|
/**
|
|
49
|
-
* The
|
|
49
|
+
* The `templateStylingContract.resolvedPaletteSelectors` attribute:
|
|
50
50
|
* an exact-value attribute (`"light"` or `"dark"`, never `"system"`) the
|
|
51
51
|
* pre-paint bootstrap script sets on the root element before first themed
|
|
52
52
|
* paint. Theme CSS consumes only this attribute, never
|
|
@@ -75,9 +75,8 @@ export const APPEARANCE_MODE_DARK = 'dark';
|
|
|
75
75
|
export const APPEARANCE_MODE_SYSTEM = 'system';
|
|
76
76
|
|
|
77
77
|
/**
|
|
78
|
-
* Every canonical machine-readable selection value
|
|
79
|
-
*
|
|
80
|
-
* value").
|
|
78
|
+
* Every canonical machine-readable selection value: `light`, `dark`,
|
|
79
|
+
* `system`; `lite` is not a value.
|
|
81
80
|
*
|
|
82
81
|
* @type {readonly ['light', 'dark', 'system']}
|
|
83
82
|
*/
|
|
@@ -89,9 +88,9 @@ export const APPEARANCE_MODE_VALUES = Object.freeze([
|
|
|
89
88
|
|
|
90
89
|
/**
|
|
91
90
|
* Every value {@link APPEARANCE_RESOLVED_MODE_ATTRIBUTE} may carry. `system`
|
|
92
|
-
* always resolves to one of these two; it is never a third selector value
|
|
93
|
-
*
|
|
94
|
-
*
|
|
91
|
+
* always resolves to one of these two; it is never a third selector value —
|
|
92
|
+
* `system` is resolved to one of those values rather than becoming a third
|
|
93
|
+
* selector.
|
|
95
94
|
*
|
|
96
95
|
* @type {readonly ['light', 'dark']}
|
|
97
96
|
*/
|
|
@@ -101,8 +100,25 @@ export const APPEARANCE_RESOLVED_MODE_VALUES = Object.freeze([
|
|
|
101
100
|
]);
|
|
102
101
|
|
|
103
102
|
/**
|
|
104
|
-
*
|
|
105
|
-
*
|
|
103
|
+
* TPL-C1 fix: the server-rendered default for
|
|
104
|
+
* {@link APPEARANCE_RESOLVED_MODE_ATTRIBUTE} on `<html>`, present on every
|
|
105
|
+
* page independent of JavaScript (`internal/eleventy-render.js`'s
|
|
106
|
+
* `SKELETON_LAYOUT_SOURCE`). `light` is chosen because it is the value the
|
|
107
|
+
* bootstrap script's own `resolveMode` already falls back to when
|
|
108
|
+
* `prefers-color-scheme` cannot be read (`internal/appearance/
|
|
109
|
+
* bootstrap-script.js`'s `systemPrefersDark` catch branch), so the
|
|
110
|
+
* server-rendered default and the script's own no-signal fallback agree.
|
|
111
|
+
* The bootstrap script's phase 1 always overwrites this attribute
|
|
112
|
+
* synchronously before first paint for a reader whose browser runs it, so
|
|
113
|
+
* this default is observed only by a reader (or crawler) that never runs
|
|
114
|
+
* script at all.
|
|
115
|
+
*
|
|
116
|
+
* @type {'light'}
|
|
117
|
+
*/
|
|
118
|
+
export const APPEARANCE_SERVER_DEFAULT_RESOLVED_MODE = APPEARANCE_MODE_LIGHT;
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* The initial reader default is `system`.
|
|
106
122
|
*
|
|
107
123
|
* @type {'system'}
|
|
108
124
|
*/
|
|
@@ -110,9 +126,9 @@ export const APPEARANCE_DEFAULT_MODE = APPEARANCE_MODE_SYSTEM;
|
|
|
110
126
|
|
|
111
127
|
/**
|
|
112
128
|
* The one versioned local-storage key the pre-paint bootstrap script and the
|
|
113
|
-
* control's change handler both read/write
|
|
114
|
-
*
|
|
115
|
-
*
|
|
129
|
+
* control's change handler both read/write. Selection is stored under one
|
|
130
|
+
* versioned local key. The trailing `v1` is this key's own version; a
|
|
131
|
+
* future incompatible storage format change mints a new
|
|
116
132
|
* key rather than reinterpreting old values, so an old browser's already
|
|
117
133
|
* stored `v1` value is simply orphaned (ignored, never migrated or deleted)
|
|
118
134
|
* rather than misread as a new format.
|
|
@@ -133,10 +149,10 @@ export const APPEARANCE_SELECT_ID = 'gala-appearance-color-mode';
|
|
|
133
149
|
|
|
134
150
|
/**
|
|
135
151
|
* The candidate-output-directory-relative path the deterministic pre-paint
|
|
136
|
-
* bootstrap script is written to
|
|
137
|
-
*
|
|
138
|
-
*
|
|
139
|
-
*
|
|
152
|
+
* bootstrap script is written to, entered into the manifest as an ordinary
|
|
153
|
+
* `{path,byteLength,sha256}` asset row. Not content-addressed (unlike the
|
|
154
|
+
* media pipeline's derivative paths): this file's bytes are a pure
|
|
155
|
+
* function of this module's own constants, not
|
|
140
156
|
* of any per-publication input, so a stable path carries no collision risk.
|
|
141
157
|
* The `v1` component versions the bootstrap script's own behaviour,
|
|
142
158
|
* independent of {@link APPEARANCE_STORAGE_KEY}'s own versioning.
|
|
@@ -150,9 +166,9 @@ export const APPEARANCE_BOOTSTRAP_SCRIPT_PATH =
|
|
|
150
166
|
* The root-absolute `<script src>` reference for a root (`/`) `basePath`
|
|
151
167
|
* publication. Every actual render joins this with the publication's own
|
|
152
168
|
* `basePath` through {@link appearanceBootstrapScriptHref} instead of using
|
|
153
|
-
* this literal directly (
|
|
154
|
-
*
|
|
155
|
-
*
|
|
169
|
+
* this literal directly (basePath fix: an earlier version of this module
|
|
170
|
+
* used this root-absolute literal unconditionally, which 404s under a
|
|
171
|
+
* non-root `basePath` publication). This constant is
|
|
156
172
|
* retained as the one-place root-`basePath` literal every test and the
|
|
157
173
|
* `joinBasePathAndRoute` call below both agree on.
|
|
158
174
|
*
|
|
@@ -162,7 +178,7 @@ export const APPEARANCE_BOOTSTRAP_SCRIPT_HREF = `/${APPEARANCE_BOOTSTRAP_SCRIPT_
|
|
|
162
178
|
|
|
163
179
|
/**
|
|
164
180
|
* The `basePath`-joined `<script src>` reference every generated page's
|
|
165
|
-
* skeleton layout actually uses
|
|
181
|
+
* skeleton layout actually uses.
|
|
166
182
|
*
|
|
167
183
|
* @param {string} basePath the build input's `basePath`
|
|
168
184
|
* @returns {string} the joined `href`
|
|
@@ -184,9 +200,8 @@ export const APPEARANCE_BOOTSTRAP_SCRIPT_MEDIA_TYPE =
|
|
|
184
200
|
* The `<meta name="color-scheme">` content value: both palettes are
|
|
185
201
|
* admitted, so the browser's own user-agent styling (form controls,
|
|
186
202
|
* scrollbars) and initial paint follow `prefers-color-scheme` immediately,
|
|
187
|
-
* independent of this renderer's own resolved-mode attribute
|
|
188
|
-
*
|
|
189
|
-
* CSS media queries preserve `system`").
|
|
203
|
+
* independent of this renderer's own resolved-mode attribute — with
|
|
204
|
+
* JavaScript disabled, CSS media queries preserve `system`.
|
|
190
205
|
*
|
|
191
206
|
* @type {string}
|
|
192
207
|
*/
|
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* The server-rendered Light/Dark/System appearance control
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* rejected").
|
|
2
|
+
* The server-rendered Light/Dark/System appearance control, placed in the
|
|
3
|
+
* header-actions slot. The control must be a native `select`, a radio
|
|
4
|
+
* group, or a conforming menu/radio pattern; a three-state unlabeled icon
|
|
5
|
+
* cycle is rejected.
|
|
7
6
|
*
|
|
8
|
-
* Scoped decision (documented, not silently assumed): of
|
|
7
|
+
* Scoped decision (documented, not silently assumed): of those three
|
|
9
8
|
* admitted patterns, this module implements a native `<select>` bound to a
|
|
10
9
|
* visible, programmatically associated `<label>`. A native select needs no
|
|
11
10
|
* `aria-pressed`/`aria-checked` bookkeeping of its own — its accessible
|
|
@@ -19,8 +18,8 @@
|
|
|
19
18
|
* `DOMContentLoaded` phase once it runs (see `bootstrap-script.js`), so this
|
|
20
19
|
* server-rendered default is only ever visible for the brief instant before
|
|
21
20
|
* that phase completes, or permanently with JavaScript disabled — exactly
|
|
22
|
-
* the no-JS fallback
|
|
23
|
-
*
|
|
21
|
+
* the required no-JS fallback: with JavaScript disabled, `system` is
|
|
22
|
+
* preserved.
|
|
24
23
|
*
|
|
25
24
|
* This markup carries no inline `on*` handler and no `javascript:` URL, so
|
|
26
25
|
* it needs no CSP allowance beyond the renderer's existing
|