@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.
Files changed (42) hide show
  1. package/README.md +211 -137
  2. package/contracts/render-policy.jcs +1 -1
  3. package/contracts/theme-styling-contract.jcs +1 -1
  4. package/package.json +5 -6
  5. package/src/core/errors.js +13 -13
  6. package/src/core/index.js +130 -75
  7. package/src/core/internal/appearance/base-layer.js +180 -0
  8. package/src/core/internal/appearance/bootstrap-script.js +11 -11
  9. package/src/core/internal/appearance/contract.js +59 -44
  10. package/src/core/internal/appearance/controller-markup.js +7 -8
  11. package/src/core/internal/appearance/styling-contract.js +132 -29
  12. package/src/core/internal/canonical-jcs.js +0 -0
  13. package/src/core/internal/content-security.js +0 -0
  14. package/src/core/internal/eleventy-render.js +52 -35
  15. package/src/core/internal/feeds.js +15 -15
  16. package/src/core/internal/fs-walk.js +2 -2
  17. package/src/core/internal/media/avif-probe.js +4 -4
  18. package/src/core/internal/media/font-policy.js +2 -2
  19. package/src/core/internal/media/jpeg-codec.js +0 -0
  20. package/src/core/internal/media/limits.js +32 -34
  21. package/src/core/internal/media/pipeline.js +4 -4
  22. package/src/core/internal/media/png-codec.js +7 -7
  23. package/src/core/internal/media/resize.js +2 -2
  24. package/src/core/internal/media/sniff.js +4 -4
  25. package/src/core/internal/media/theme-svg-sanitizer.js +278 -0
  26. package/src/core/internal/media/webp-probe.js +3 -3
  27. package/src/core/internal/messages.js +8 -8
  28. package/src/core/internal/page-kinds.js +50 -51
  29. package/src/core/internal/render-policy-content.js +57 -21
  30. package/src/core/internal/route-labels.js +5 -6
  31. package/src/core/internal/route.js +58 -20
  32. package/src/core/internal/search-index.js +17 -16
  33. package/src/core/internal/semver-range.js +84 -0
  34. package/src/core/internal/seo.js +24 -24
  35. package/src/core/internal/sitemap.js +11 -12
  36. package/src/core/internal/skeleton.js +65 -46
  37. package/src/core/internal/source-inventory.js +4 -5
  38. package/src/core/internal/text-direction.js +1 -2
  39. package/src/core/internal/theme-assets.js +314 -68
  40. package/src/core/internal/uuid.js +4 -5
  41. package/src/core/manifest.js +0 -0
  42. 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 (brief S2
3
- * section 3: "a versioned deterministic pre-paint bootstrap owned by
4
- * `template`/`core` resolves local selection plus system preference and sets
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 S2 ever emits ("Module absence
11
- * and CSP equality": "the appearance controller is the only browser
12
- * bootstrap"). Every name and value it embeds is read from
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` (brief: "an explicit `light`/`dark` choice is not
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 — brief:
37
- * "cannot hide the document indefinitely" — this script never needs one,
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` (S2-T12) targets
3
- * for every Light/Dark/System appearance name this renderer owns (task
4
- * packet S2-T07: "the `data-` attribute/class names the controller sets must
5
- * be fixed constants exported from one module"; DEC-097's closed
6
- * `templateStylingContract` catalog, section "Closed theme-package and
7
- * stylesheet admission": the required root attribute row
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 required 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
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 (brief S2 section 3:
16
- * "selection and resolved state are exposed separately through
17
- * template-owned root attributes; theme CSS consumes only resolved state"):
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 DEC-097-fixed theme-facing attributes above — part of the
21
- * published styling contract S2-T12 emits, never renamed here independent
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 — DEC-097's attribute catalog does
27
- * not name it, and no theme selector may target it — but its name is still
28
- * fixed here, in this one module, exactly like the two contract-owned
29
- * attributes, so no other module ever invents its own literal for it.
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 DEC-097 `templateStylingContract.publicationRootSelector` attribute:
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 DEC-097 `templateStylingContract.resolvedPaletteSelectors` attribute:
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 (brief S2 section 3:
79
- * "canonical machine values are `light`, `dark`, `system`; `lite` is not a
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
- * (DEC-097: "`system` is resolved to one of those values rather than
94
- * becoming a third selector").
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
- * The initial reader default (brief S2 section 3: "initial reader default is
105
- * `system`").
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 (brief S2 section 3: "selection
114
- * is stored under one versioned local key"). The trailing `v1` is this
115
- * key's own version; a future incompatible storage format change mints a new
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 (task packet S2-T07: "entered into the
137
- * manifest through the S2-T03 path with `{path,byteLength,sha256}`"). Not
138
- * content-addressed (unlike the S2-T05 media pipeline's derivative paths):
139
- * this file's bytes are a pure function of this module's own constants, not
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 (S2-T12 basePath fix, LOCAL-7 follow-up: an earlier
154
- * version of this module used this root-absolute literal unconditionally,
155
- * which 404s under a non-root `basePath` publication). This constant is
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 (S2-T12 basePath fix).
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 (brief S2
188
- * section 3: "`color-scheme` meta/CSS handling"; "with JavaScript disabled,
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 (task packet
3
- * S2-T07: "the three-mode appearance control in the header-actions slot";
4
- * brief S2 section 3: "the control is a native `select`, a radio group, or a
5
- * conforming menu/radio pattern. A three-state unlabeled icon cycle is
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 the brief's three
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 the brief requires ("with JavaScript disabled, ...
23
- * `system` [is preserved]").
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