@aitrien/motifspec 1.13.0 → 1.13.3

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 (83) hide show
  1. package/CHANGELOG.md +275 -257
  2. package/LICENSE +21 -21
  3. package/README.md +128 -150
  4. package/dist/backdrop.html +1 -1
  5. package/dist/chroma.css +36 -36
  6. package/dist/codex.css +1145 -1145
  7. package/dist/codex.js +126 -126
  8. package/dist/contrast.md +101 -101
  9. package/dist/fonts.css +3 -3
  10. package/dist/icons.svg +18 -18
  11. package/dist/plate.html +1 -1
  12. package/dist/plate.js +292 -292
  13. package/dist/theme-init.js +1 -1
  14. package/dist/tokens.css +466 -466
  15. package/dist/tokens.js +3330 -3330
  16. package/dist/tokens.json +3330 -3330
  17. package/docs/components/_template.md +37 -37
  18. package/docs/components/accession-label.md +106 -106
  19. package/docs/components/backdrop.md +83 -83
  20. package/docs/components/button-and-link.md +87 -87
  21. package/docs/components/callout.md +75 -75
  22. package/docs/components/code-block.md +109 -109
  23. package/docs/components/data-table.md +73 -73
  24. package/docs/components/figure.md +68 -68
  25. package/docs/components/heading-anchor.md +76 -76
  26. package/docs/components/icon.md +63 -63
  27. package/docs/components/ornament.md +77 -77
  28. package/docs/components/pagination.md +78 -78
  29. package/docs/components/post-list-item.md +91 -91
  30. package/docs/components/project-card.md +82 -82
  31. package/docs/components/prose.md +122 -122
  32. package/docs/components/site-footer.md +99 -99
  33. package/docs/components/site-header.md +133 -133
  34. package/docs/components/specimen-plate.md +92 -92
  35. package/docs/components/table-of-contents.md +83 -83
  36. package/docs/components/tag.md +70 -70
  37. package/docs/components/tooltip.md +67 -67
  38. package/docs/hugo.md +132 -120
  39. package/docs/syntax-highlighting.md +55 -55
  40. package/docs/templates.md +21 -30
  41. package/fonts/OFL-jetbrains-mono.txt +93 -93
  42. package/fonts/OFL-national-park.txt +93 -93
  43. package/fonts/OFL-yarndings-12.txt +93 -93
  44. package/icons/LICENSE +202 -202
  45. package/icons/manifest.json +75 -75
  46. package/layouts/_markup/render-blockquote.html +30 -30
  47. package/layouts/_markup/render-codeblock.html +18 -18
  48. package/layouts/_markup/render-heading.html +2 -2
  49. package/layouts/_markup/render-image.html +14 -14
  50. package/layouts/_markup/render-link.html +14 -14
  51. package/layouts/_markup/render-table.html +31 -31
  52. package/layouts/_partials/motifspec/accession/id.html +2 -2
  53. package/layouts/_partials/motifspec/accession/label.html +18 -18
  54. package/layouts/_partials/motifspec/accession/status.html +2 -2
  55. package/layouts/_partials/motifspec/article.html +24 -24
  56. package/layouts/_partials/motifspec/back-link.html +2 -2
  57. package/layouts/_partials/motifspec/body-end.html +5 -5
  58. package/layouts/_partials/motifspec/body-start.html +5 -5
  59. package/layouts/_partials/motifspec/figure.html +14 -14
  60. package/layouts/_partials/motifspec/filed-under.html +5 -5
  61. package/layouts/_partials/motifspec/h2s.html +7 -7
  62. package/layouts/_partials/motifspec/head.html +15 -15
  63. package/layouts/_partials/motifspec/pagination-entries.html +34 -34
  64. package/layouts/_partials/motifspec/pagination-pages.html +30 -30
  65. package/layouts/_partials/motifspec/post-list-item.html +15 -15
  66. package/layouts/_partials/motifspec/project-card.html +23 -23
  67. package/layouts/_partials/motifspec/section-heading.html +3 -3
  68. package/layouts/_partials/motifspec/site-header.html +27 -27
  69. package/layouts/_partials/motifspec/table-of-contents.html +21 -21
  70. package/layouts/_partials/motifspec/tag-filter.html +22 -22
  71. package/layouts/_partials/motifspec/tags.html +8 -8
  72. package/layouts/_partials/motifspec/tokens.html +9 -9
  73. package/package.json +68 -68
  74. package/scripts/check.mjs +153 -153
  75. package/scripts/lib.mjs +176 -176
  76. package/tokens/README.md +3 -3
  77. package/tokens/border.json +63 -63
  78. package/tokens/color.json +1902 -1902
  79. package/tokens/motion.json +207 -207
  80. package/tokens/radius.json +53 -53
  81. package/tokens/size.json +378 -378
  82. package/tokens/space.json +289 -289
  83. package/tokens/typography.json +859 -859
package/dist/plate.js CHANGED
@@ -1,292 +1,292 @@
1
- // Specimen plate and backdrop: a contour survey of simplex-noise terrain.
2
- // The build runs contours() once (t = 0) to write the static SVG, so the plate and the backdrop are complete
3
- // without JavaScript.
4
- // In the browser, mount() redraws the same contours as the terrain drifts through time. Same code, same seed,
5
- // so the first animated frame matches the static one exactly.
6
- (function (g) {
7
- 'use strict';
8
-
9
- // ------------------------------------------------------------ seeded 3D simplex noise (after Gustavson)
10
- function rng(seed) {
11
- return function () {
12
- seed |= 0; seed = (seed + 0x6d2b79f5) | 0;
13
- var t = Math.imul(seed ^ (seed >>> 15), 1 | seed);
14
- t = (t + Math.imul(t ^ (t >>> 7), 61 | t)) ^ t;
15
- return ((t ^ (t >>> 14)) >>> 0) / 4294967296;
16
- };
17
- }
18
- var GRAD = [1,1,0, -1,1,0, 1,-1,0, -1,-1,0, 1,0,1, -1,0,1, 1,0,-1, -1,0,-1, 0,1,1, 0,-1,1, 0,1,-1, 0,-1,-1];
19
- var F3 = 1 / 3, G3 = 1 / 6;
20
- function simplex(seed) {
21
- var r = rng(seed), p = new Uint8Array(256), perm = new Uint8Array(512), pm = new Uint8Array(512), i;
22
- for (i = 0; i < 256; i++) p[i] = i;
23
- for (i = 255; i > 0; i--) { var k = Math.floor(r() * (i + 1)), s = p[i]; p[i] = p[k]; p[k] = s; }
24
- for (i = 0; i < 512; i++) { perm[i] = p[i & 255]; pm[i] = (perm[i] % 12) * 3; }
25
- return function (x, y, z) {
26
- var s = (x + y + z) * F3, i = Math.floor(x + s), j = Math.floor(y + s), k = Math.floor(z + s);
27
- var t = (i + j + k) * G3, x0 = x - i + t, y0 = y - j + t, z0 = z - k + t;
28
- var i1, j1, k1, i2, j2, k2;
29
- if (x0 >= y0) {
30
- if (y0 >= z0) { i1 = 1; j1 = 0; k1 = 0; i2 = 1; j2 = 1; k2 = 0; }
31
- else if (x0 >= z0) { i1 = 1; j1 = 0; k1 = 0; i2 = 1; j2 = 0; k2 = 1; }
32
- else { i1 = 0; j1 = 0; k1 = 1; i2 = 1; j2 = 0; k2 = 1; }
33
- } else {
34
- if (y0 < z0) { i1 = 0; j1 = 0; k1 = 1; i2 = 0; j2 = 1; k2 = 1; }
35
- else if (x0 < z0) { i1 = 0; j1 = 1; k1 = 0; i2 = 0; j2 = 1; k2 = 1; }
36
- else { i1 = 0; j1 = 1; k1 = 0; i2 = 1; j2 = 1; k2 = 0; }
37
- }
38
- var x1 = x0 - i1 + G3, y1 = y0 - j1 + G3, z1 = z0 - k1 + G3;
39
- var x2 = x0 - i2 + 2 * G3, y2 = y0 - j2 + 2 * G3, z2 = z0 - k2 + 2 * G3;
40
- var x3 = x0 - 1 + 0.5, y3 = y0 - 1 + 0.5, z3 = z0 - 1 + 0.5;
41
- var ii = i & 255, jj = j & 255, kk = k & 255, n = 0, tt, gi;
42
- tt = 0.6 - x0 * x0 - y0 * y0 - z0 * z0;
43
- if (tt > 0) { gi = pm[ii + perm[jj + perm[kk]]]; tt *= tt; n += tt * tt * (GRAD[gi] * x0 + GRAD[gi + 1] * y0 + GRAD[gi + 2] * z0); }
44
- tt = 0.6 - x1 * x1 - y1 * y1 - z1 * z1;
45
- if (tt > 0) { gi = pm[ii + i1 + perm[jj + j1 + perm[kk + k1]]]; tt *= tt; n += tt * tt * (GRAD[gi] * x1 + GRAD[gi + 1] * y1 + GRAD[gi + 2] * z1); }
46
- tt = 0.6 - x2 * x2 - y2 * y2 - z2 * z2;
47
- if (tt > 0) { gi = pm[ii + i2 + perm[jj + j2 + perm[kk + k2]]]; tt *= tt; n += tt * tt * (GRAD[gi] * x2 + GRAD[gi + 1] * y2 + GRAD[gi + 2] * z2); }
48
- tt = 0.6 - x3 * x3 - y3 * y3 - z3 * z3;
49
- if (tt > 0) { gi = pm[ii + 1 + perm[jj + 1 + perm[kk + 1]]]; tt *= tt; n += tt * tt * (GRAD[gi] * x3 + GRAD[gi + 1] * y3 + GRAD[gi + 2] * z3); }
50
- return 32 * n; // about -1..1
51
- };
52
- }
53
-
54
- // ------------------------------------------------------------ the survey
55
- var DEFAULTS = {
56
- seed: 3, // which terrain
57
- size: 240, // viewBox units
58
- cells: 32, // marching-squares grid per side
59
- scale: 1.4, // noise features across the plate
60
- octaves: 2,
61
- massif: 1.1, // how strongly the terrain rises toward the centre
62
- levels: 14, // contour lines
63
- index: 5 // every nth line is an index contour
64
- };
65
-
66
- function survey(opts) {
67
- var o = {}, k;
68
- for (k in DEFAULTS) o[k] = opts && opts[k] != null ? opts[k] : DEFAULTS[k];
69
- var noise = simplex(o.seed), N = o.cells, S = o.size, cell = S / N;
70
- var lo = -0.45, hi = 1.75, step = (hi - lo) / (o.levels + 1);
71
- var levels = [];
72
- for (k = 1; k <= o.levels; k++) levels.push(lo + k * step);
73
- var h = new Float32Array((N + 1) * (N + 1));
74
-
75
- function height(t) {
76
- for (var j = 0; j <= N; j++) for (var i = 0; i <= N; i++) {
77
- var x = i / N, y = j / N, f = o.scale, a = 1, v = 0, norm = 0;
78
- for (var oc = 0; oc < o.octaves; oc++) { v += a * noise(x * f, y * f, t + oc * 13.7); norm += a; a *= 0.35; f *= 2.3; }
79
- var dx = x - 0.5, dy = y - 0.5, r = Math.min(1, Math.sqrt(dx * dx + dy * dy) / 0.62);
80
- h[j * (N + 1) + i] = v / norm + o.massif * (1 - r * r);
81
- }
82
- }
83
-
84
- // Marching squares for one level. Points sit on cell edges and are numbered by edge, so neighbouring cells
85
- // share them exactly. Horizontal edges come first (N per row, N + 1 rows), then vertical ones.
86
- var E = 2 * N * (N + 1), px = new Float32Array(E), py = new Float32Array(E), lk = new Int32Array(2 * E), stamp = new Int32Array(E), pass = 0;
87
- function trace(level) {
88
- pass++;
89
- var touched = [];
90
- function pt(i, j, vert) {
91
- var id = vert ? N * (N + 1) + j * (N + 1) + i : j * N + i;
92
- if (stamp[id] !== pass) {
93
- stamp[id] = pass; lk[2 * id] = -1; lk[2 * id + 1] = -1; touched.push(id);
94
- var a = h[j * (N + 1) + i], b = vert ? h[(j + 1) * (N + 1) + i] : h[j * (N + 1) + i + 1], u = (level - a) / (b - a);
95
- px[id] = vert ? i * cell : (i + u) * cell; py[id] = vert ? (j + u) * cell : j * cell;
96
- }
97
- return id;
98
- }
99
- function link(a, b) { lk[2 * a + (lk[2 * a] === -1 ? 0 : 1)] = b; lk[2 * b + (lk[2 * b] === -1 ? 0 : 1)] = a; }
100
- for (var j = 0; j < N; j++) for (var i = 0; i < N; i++) {
101
- var tl = h[j * (N + 1) + i], tr = h[j * (N + 1) + i + 1], br = h[(j + 1) * (N + 1) + i + 1], bl = h[(j + 1) * (N + 1) + i];
102
- var c = (tl > level ? 8 : 0) | (tr > level ? 4 : 0) | (br > level ? 2 : 0) | (bl > level ? 1 : 0);
103
- if (c === 0 || c === 15) continue;
104
- var centre = (tl + tr + br + bl) / 4 > level;
105
- switch (c) {
106
- case 1: case 14: link(pt(i, j, 1), pt(i, j + 1, 0)); break;
107
- case 2: case 13: link(pt(i, j + 1, 0), pt(i + 1, j, 1)); break;
108
- case 3: case 12: link(pt(i, j, 1), pt(i + 1, j, 1)); break;
109
- case 4: case 11: link(pt(i, j, 0), pt(i + 1, j, 1)); break;
110
- case 6: case 9: link(pt(i, j, 0), pt(i, j + 1, 0)); break;
111
- case 7: case 8: link(pt(i, j, 1), pt(i, j, 0)); break;
112
- case 5:
113
- if (centre) { link(pt(i, j, 1), pt(i, j, 0)); link(pt(i, j + 1, 0), pt(i + 1, j, 1)); }
114
- else { link(pt(i, j, 1), pt(i, j + 1, 0)); link(pt(i, j, 0), pt(i + 1, j, 1)); }
115
- break;
116
- case 10:
117
- if (centre) { link(pt(i, j, 0), pt(i + 1, j, 1)); link(pt(i, j, 1), pt(i, j + 1, 0)); }
118
- else { link(pt(i, j, 1), pt(i, j, 0)); link(pt(i, j + 1, 0), pt(i + 1, j, 1)); }
119
- break;
120
- }
121
- }
122
- // Chain the segments into lines: open lines start at an end (one link), then whatever is left is a loop.
123
- var lines = [], seen = pass * 2; // reuse stamp: a point is used once stamp[id] === -seen
124
- function walk(start) {
125
- var pts = [], prev = -1, cur = start;
126
- while (cur !== -1 && stamp[cur] !== -seen) {
127
- stamp[cur] = -seen; pts.push(px[cur], py[cur]);
128
- var n0 = lk[2 * cur], n1 = lk[2 * cur + 1], next = n0 !== prev && n0 !== -1 && stamp[n0] !== -seen ? n0 : n1 !== prev && n1 !== -1 && stamp[n1] !== -seen ? n1 : -1;
129
- prev = cur; cur = next;
130
- }
131
- var closed = lk[2 * prev] === start || lk[2 * prev + 1] === start;
132
- return { pts: pts, closed: closed && pts.length > 4 };
133
- }
134
- var k, id;
135
- for (k = 0; k < touched.length; k++) { id = touched[k]; if (stamp[id] === pass && lk[2 * id + 1] === -1) lines.push(walk(id)); }
136
- for (k = 0; k < touched.length; k++) { id = touched[k]; if (stamp[id] === pass) lines.push(walk(id)); }
137
- return lines;
138
- }
139
-
140
- // Clean each traced line before drawing it. Marching squares puts two points almost on top of each other
141
- // wherever a contour passes close to a grid corner; merging them removes the burrs that flicker as the
142
- // terrain moves. Loops smaller than two cells, and edge stubs shorter than that, pop in and out, so they are dropped.
143
- var minGap = cell * 0.4, minLoop = cell * 2;
144
- function clean(ln) {
145
- var p = ln.pts, n = p.length / 2, q = [p[0], p[1]], k, dx, dy;
146
- for (k = 1; k < n; k++) {
147
- dx = p[2 * k] - q[q.length - 2]; dy = p[2 * k + 1] - q[q.length - 1];
148
- if (dx * dx + dy * dy >= minGap * minGap) q.push(p[2 * k], p[2 * k + 1]);
149
- else if (!ln.closed && k === n - 1) { q[q.length - 2] = p[2 * k]; q[q.length - 1] = p[2 * k + 1]; } // keep the true end on the edge
150
- }
151
- if (ln.closed) {
152
- dx = q[0] - q[q.length - 2]; dy = q[1] - q[q.length - 1];
153
- if (q.length > 2 && dx * dx + dy * dy < minGap * minGap) q.length -= 2;
154
- var x0 = Infinity, x1 = -Infinity, y0 = Infinity, y1 = -Infinity;
155
- for (k = 0; k < q.length; k += 2) { x0 = Math.min(x0, q[k]); x1 = Math.max(x1, q[k]); y0 = Math.min(y0, q[k + 1]); y1 = Math.max(y1, q[k + 1]); }
156
- if (q.length < 8 || Math.max(x1 - x0, y1 - y0) < minLoop) return null;
157
- }
158
- if (!ln.closed) { // stubs clipped by the plate's edge flicker the same way
159
- var len = 0;
160
- for (k = 2; k < q.length; k += 2) len += Math.sqrt((q[k] - q[k - 2]) * (q[k] - q[k - 2]) + (q[k + 1] - q[k - 1]) * (q[k + 1] - q[k - 1]));
161
- if (len < minLoop) return null;
162
- }
163
- return q.length >= 4 ? { pts: q, closed: ln.closed } : null;
164
- }
165
-
166
- // Centripetal Catmull-Rom through the points (alpha 0.5), written as cubic Béziers rounded to 0.1 units.
167
- // Unlike the uniform form, it never overshoots into a cusp or a loop when neighbouring points are unevenly spaced.
168
- function r1(n) { return Math.round(n * 10) / 10; }
169
- function path(lines) {
170
- var d = [];
171
- for (var l = 0; l < lines.length; l++) {
172
- var ln = clean(lines[l]);
173
- if (!ln) continue;
174
- var p = ln.pts, n = p.length / 2, closed = ln.closed;
175
- d.push('M', r1(p[0]), ' ', r1(p[1]));
176
- var segs = closed ? n : n - 1;
177
- for (var k = 0; k < segs; k++) {
178
- var a = k === 0 ? (closed ? n - 1 : 0) : k - 1, b = k, c = k + 1 < n ? k + 1 : (closed ? 0 : n - 1), e = k + 2 < n ? k + 2 : (closed ? k + 2 - n : n - 1);
179
- a *= 2; b *= 2; c *= 2; e *= 2;
180
- var d1 = Math.pow((p[b] - p[a]) * (p[b] - p[a]) + (p[b + 1] - p[a + 1]) * (p[b + 1] - p[a + 1]), 0.25);
181
- var d2 = Math.pow((p[c] - p[b]) * (p[c] - p[b]) + (p[c + 1] - p[b + 1]) * (p[c + 1] - p[b + 1]), 0.25);
182
- var d3 = Math.pow((p[e] - p[c]) * (p[e] - p[c]) + (p[e + 1] - p[c + 1]) * (p[e + 1] - p[c + 1]), 0.25);
183
- var c1x, c1y, c2x, c2y;
184
- if (d1 < 1e-6) { c1x = p[b] + (p[c] - p[b]) / 3; c1y = p[b + 1] + (p[c + 1] - p[b + 1]) / 3; }
185
- else {
186
- var m1 = 3 * d1 * (d1 + d2), w1 = 2 * d1 * d1 + 3 * d1 * d2 + d2 * d2;
187
- c1x = (d1 * d1 * p[c] - d2 * d2 * p[a] + w1 * p[b]) / m1; c1y = (d1 * d1 * p[c + 1] - d2 * d2 * p[a + 1] + w1 * p[b + 1]) / m1;
188
- }
189
- if (d3 < 1e-6) { c2x = p[c] + (p[b] - p[c]) / 3; c2y = p[c + 1] + (p[b + 1] - p[c + 1]) / 3; }
190
- else {
191
- var m2 = 3 * d3 * (d3 + d2), w2 = 2 * d3 * d3 + 3 * d3 * d2 + d2 * d2;
192
- c2x = (d3 * d3 * p[b] - d2 * d2 * p[e] + w2 * p[c]) / m2; c2y = (d3 * d3 * p[b + 1] - d2 * d2 * p[e + 1] + w2 * p[c + 1]) / m2;
193
- }
194
- d.push('C', r1(c1x), ' ', r1(c1y), ' ', r1(c2x), ' ', r1(c2y), ' ', r1(p[c]), ' ', r1(p[c + 1]));
195
- }
196
- if (closed) d.push('Z');
197
- }
198
- return d.join('');
199
- }
200
-
201
- return {
202
- options: o,
203
- levels: levels,
204
- // Path data for every level at time t (in noise units).
205
- contours: function (t) {
206
- height(t || 0);
207
- return levels.map(function (lv, i) { return { level: i + 1, index: (i + 1) % o.index === 0, d: path(trace(lv)) }; });
208
- }
209
- };
210
- }
211
-
212
- // ------------------------------------------------------------ static markup (used by the build)
213
- // kind 'backdrop' draws the same survey full-bleed behind a page (docs/components/backdrop.md): sliced to cover
214
- // the viewport instead of fitted into the plate's square.
215
- function markup(opts, kind) {
216
- var bg = kind === 'backdrop';
217
- var s = survey(opts), S = s.options.size, q = S / 4;
218
- var grid = [q, 2 * q, 3 * q].map(function (v) { return 'M' + v + ' 0V' + S + 'M0 ' + v + 'H' + S; }).join('');
219
- var paths = s.contours(0).map(function (c, i) {
220
- return '<path class="cx-plate__line' + (c.index ? ' cx-plate__line--index' : '') + '" style="--i: ' + i + '" pathLength="1" d="' + c.d + '"></path>';
221
- }).join('');
222
- return '<div class="' + (bg ? 'cx-backdrop' : 'cx-plate') + '" aria-hidden="true" data-plate-seed="' + s.options.seed + '">' +
223
- '<svg class="cx-plate__map" viewBox="0 0 ' + S + ' ' + S + '"' + (bg ? ' preserveAspectRatio="xMidYMid slice"' : '') + ' focusable="false">' +
224
- '<path class="cx-plate__grid" d="' + grid + '"></path>' + paths + '</svg></div>';
225
- }
226
- function backdrop(opts) { return markup(opts, 'backdrop'); }
227
-
228
- // ------------------------------------------------------------ animation (browser only)
229
- function mount(root) {
230
- var doc = root.ownerDocument, win = doc.defaultView;
231
- // A plate's pause control sits in its frame; the backdrop's sits in the site header.
232
- var toggle = root.classList.contains('cx-backdrop')
233
- ? doc.querySelector('.cx-backdrop__pause')
234
- : (root.closest('.cx-plate-frame') || root.parentElement).querySelector('.cx-plate__pause');
235
- var input = toggle && toggle.querySelector('input');
236
- // The backdrop's pause is a reader preference, like the theme: it is stored under cx-backdrop ("paused", or no key
237
- // for running) and restored on every page. A plate's pause belongs to that plate and isn't kept.
238
- if (input && root.classList.contains('cx-backdrop')) {
239
- try { if (win.localStorage.getItem('cx-backdrop') === 'paused') input.checked = true; } catch (e) { /* storage blocked */ }
240
- input.addEventListener('change', function () {
241
- try { if (input.checked) win.localStorage.setItem('cx-backdrop', 'paused'); else win.localStorage.removeItem('cx-backdrop'); }
242
- catch (e) { /* storage blocked: the pause still applies to this page */ }
243
- });
244
- }
245
- var paths = root.querySelectorAll('.cx-plate__line');
246
- var reduce = win.matchMedia('(prefers-reduced-motion: reduce)');
247
- function ms(name) { // durations come from the tokens, through custom properties on the plate
248
- var v = win.getComputedStyle(root).getPropertyValue(name).trim();
249
- return v.endsWith('ms') ? parseFloat(v) : parseFloat(v) * 1000;
250
- }
251
- var drift = ms('--cx-plate-drift'), delay = ms('--cx-plate-draw');
252
- var s = survey({ seed: Number(root.getAttribute('data-plate-seed')) || DEFAULTS.seed });
253
- var backdrop = root.classList.contains('cx-backdrop');
254
- // The drift never starts at full speed: from rest its speed eases up over one draw duration, on load and again
255
- // after a pause, a hidden tab or scrolling back into view. Pausing still stops it at once.
256
- var elapsed = 0, last = null, raf = 0, frames = 0, visible = true, ready = false, warm = 0;
257
-
258
- function running() { return ready && visible && !doc.hidden && !reduce.matches && !(input && input.checked); }
259
- function tick(now) {
260
- raf = 0;
261
- if (!running()) { last = null; warm = 0; return; }
262
- if (last !== null) {
263
- var dt = Math.min(now - last, delay / 16); // a long gap (background tab) never jumps the terrain
264
- warm = Math.min(1, warm + dt / delay);
265
- elapsed += dt * warm * warm * (3 - 2 * warm); // speed follows a smoothstep from 0 to full
266
- }
267
- last = now;
268
- // Redraw on every second frame: the terrain moves so little per frame that this looks the same at half the cost.
269
- if ((frames++ & 1) === 0) s.contours(elapsed / drift).forEach(function (c, i) { paths[i].setAttribute('d', c.d); });
270
- raf = win.requestAnimationFrame(tick);
271
- }
272
- function update() {
273
- if (toggle) toggle.hidden = reduce.matches;
274
- if (running() && !raf) raf = win.requestAnimationFrame(tick);
275
- }
276
- if (input) input.addEventListener('change', update);
277
- if (reduce.addEventListener) reduce.addEventListener('change', update);
278
- doc.addEventListener('visibilitychange', update);
279
- if ('IntersectionObserver' in win) new win.IntersectionObserver(function (e) { visible = e[0].isIntersecting; update(); }).observe(root);
280
- // A plate draws its lines in first; the backdrop has no draw-in, so it starts as soon as the script runs.
281
- if (backdrop) ready = true; else win.setTimeout(function () { ready = true; update(); }, delay);
282
- update();
283
- }
284
-
285
- g.cxPlate = { survey: survey, markup: markup, backdrop: backdrop, mount: mount, defaults: DEFAULTS };
286
-
287
- // In a browser, start every plate and backdrop on the page. Without this script the static contours stay as they are.
288
- if (typeof document !== 'undefined' && document.querySelectorAll) {
289
- var start = function () { Array.prototype.forEach.call(document.querySelectorAll('.cx-plate[data-plate-seed], .cx-backdrop[data-plate-seed]'), mount); };
290
- if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', start); else start();
291
- }
292
- })(typeof window !== 'undefined' ? window : globalThis);
1
+ // Specimen plate and backdrop: a contour survey of simplex-noise terrain.
2
+ // The build runs contours() once (t = 0) to write the static SVG, so the plate and the backdrop are complete
3
+ // without JavaScript.
4
+ // In the browser, mount() redraws the same contours as the terrain drifts through time. Same code, same seed,
5
+ // so the first animated frame matches the static one exactly.
6
+ (function (g) {
7
+ 'use strict';
8
+
9
+ // ------------------------------------------------------------ seeded 3D simplex noise (after Gustavson)
10
+ function rng(seed) {
11
+ return function () {
12
+ seed |= 0; seed = (seed + 0x6d2b79f5) | 0;
13
+ var t = Math.imul(seed ^ (seed >>> 15), 1 | seed);
14
+ t = (t + Math.imul(t ^ (t >>> 7), 61 | t)) ^ t;
15
+ return ((t ^ (t >>> 14)) >>> 0) / 4294967296;
16
+ };
17
+ }
18
+ var GRAD = [1,1,0, -1,1,0, 1,-1,0, -1,-1,0, 1,0,1, -1,0,1, 1,0,-1, -1,0,-1, 0,1,1, 0,-1,1, 0,1,-1, 0,-1,-1];
19
+ var F3 = 1 / 3, G3 = 1 / 6;
20
+ function simplex(seed) {
21
+ var r = rng(seed), p = new Uint8Array(256), perm = new Uint8Array(512), pm = new Uint8Array(512), i;
22
+ for (i = 0; i < 256; i++) p[i] = i;
23
+ for (i = 255; i > 0; i--) { var k = Math.floor(r() * (i + 1)), s = p[i]; p[i] = p[k]; p[k] = s; }
24
+ for (i = 0; i < 512; i++) { perm[i] = p[i & 255]; pm[i] = (perm[i] % 12) * 3; }
25
+ return function (x, y, z) {
26
+ var s = (x + y + z) * F3, i = Math.floor(x + s), j = Math.floor(y + s), k = Math.floor(z + s);
27
+ var t = (i + j + k) * G3, x0 = x - i + t, y0 = y - j + t, z0 = z - k + t;
28
+ var i1, j1, k1, i2, j2, k2;
29
+ if (x0 >= y0) {
30
+ if (y0 >= z0) { i1 = 1; j1 = 0; k1 = 0; i2 = 1; j2 = 1; k2 = 0; }
31
+ else if (x0 >= z0) { i1 = 1; j1 = 0; k1 = 0; i2 = 1; j2 = 0; k2 = 1; }
32
+ else { i1 = 0; j1 = 0; k1 = 1; i2 = 1; j2 = 0; k2 = 1; }
33
+ } else {
34
+ if (y0 < z0) { i1 = 0; j1 = 0; k1 = 1; i2 = 0; j2 = 1; k2 = 1; }
35
+ else if (x0 < z0) { i1 = 0; j1 = 1; k1 = 0; i2 = 0; j2 = 1; k2 = 1; }
36
+ else { i1 = 0; j1 = 1; k1 = 0; i2 = 1; j2 = 1; k2 = 0; }
37
+ }
38
+ var x1 = x0 - i1 + G3, y1 = y0 - j1 + G3, z1 = z0 - k1 + G3;
39
+ var x2 = x0 - i2 + 2 * G3, y2 = y0 - j2 + 2 * G3, z2 = z0 - k2 + 2 * G3;
40
+ var x3 = x0 - 1 + 0.5, y3 = y0 - 1 + 0.5, z3 = z0 - 1 + 0.5;
41
+ var ii = i & 255, jj = j & 255, kk = k & 255, n = 0, tt, gi;
42
+ tt = 0.6 - x0 * x0 - y0 * y0 - z0 * z0;
43
+ if (tt > 0) { gi = pm[ii + perm[jj + perm[kk]]]; tt *= tt; n += tt * tt * (GRAD[gi] * x0 + GRAD[gi + 1] * y0 + GRAD[gi + 2] * z0); }
44
+ tt = 0.6 - x1 * x1 - y1 * y1 - z1 * z1;
45
+ if (tt > 0) { gi = pm[ii + i1 + perm[jj + j1 + perm[kk + k1]]]; tt *= tt; n += tt * tt * (GRAD[gi] * x1 + GRAD[gi + 1] * y1 + GRAD[gi + 2] * z1); }
46
+ tt = 0.6 - x2 * x2 - y2 * y2 - z2 * z2;
47
+ if (tt > 0) { gi = pm[ii + i2 + perm[jj + j2 + perm[kk + k2]]]; tt *= tt; n += tt * tt * (GRAD[gi] * x2 + GRAD[gi + 1] * y2 + GRAD[gi + 2] * z2); }
48
+ tt = 0.6 - x3 * x3 - y3 * y3 - z3 * z3;
49
+ if (tt > 0) { gi = pm[ii + 1 + perm[jj + 1 + perm[kk + 1]]]; tt *= tt; n += tt * tt * (GRAD[gi] * x3 + GRAD[gi + 1] * y3 + GRAD[gi + 2] * z3); }
50
+ return 32 * n; // about -1..1
51
+ };
52
+ }
53
+
54
+ // ------------------------------------------------------------ the survey
55
+ var DEFAULTS = {
56
+ seed: 3, // which terrain
57
+ size: 240, // viewBox units
58
+ cells: 32, // marching-squares grid per side
59
+ scale: 1.4, // noise features across the plate
60
+ octaves: 2,
61
+ massif: 1.1, // how strongly the terrain rises toward the centre
62
+ levels: 14, // contour lines
63
+ index: 5 // every nth line is an index contour
64
+ };
65
+
66
+ function survey(opts) {
67
+ var o = {}, k;
68
+ for (k in DEFAULTS) o[k] = opts && opts[k] != null ? opts[k] : DEFAULTS[k];
69
+ var noise = simplex(o.seed), N = o.cells, S = o.size, cell = S / N;
70
+ var lo = -0.45, hi = 1.75, step = (hi - lo) / (o.levels + 1);
71
+ var levels = [];
72
+ for (k = 1; k <= o.levels; k++) levels.push(lo + k * step);
73
+ var h = new Float32Array((N + 1) * (N + 1));
74
+
75
+ function height(t) {
76
+ for (var j = 0; j <= N; j++) for (var i = 0; i <= N; i++) {
77
+ var x = i / N, y = j / N, f = o.scale, a = 1, v = 0, norm = 0;
78
+ for (var oc = 0; oc < o.octaves; oc++) { v += a * noise(x * f, y * f, t + oc * 13.7); norm += a; a *= 0.35; f *= 2.3; }
79
+ var dx = x - 0.5, dy = y - 0.5, r = Math.min(1, Math.sqrt(dx * dx + dy * dy) / 0.62);
80
+ h[j * (N + 1) + i] = v / norm + o.massif * (1 - r * r);
81
+ }
82
+ }
83
+
84
+ // Marching squares for one level. Points sit on cell edges and are numbered by edge, so neighbouring cells
85
+ // share them exactly. Horizontal edges come first (N per row, N + 1 rows), then vertical ones.
86
+ var E = 2 * N * (N + 1), px = new Float32Array(E), py = new Float32Array(E), lk = new Int32Array(2 * E), stamp = new Int32Array(E), pass = 0;
87
+ function trace(level) {
88
+ pass++;
89
+ var touched = [];
90
+ function pt(i, j, vert) {
91
+ var id = vert ? N * (N + 1) + j * (N + 1) + i : j * N + i;
92
+ if (stamp[id] !== pass) {
93
+ stamp[id] = pass; lk[2 * id] = -1; lk[2 * id + 1] = -1; touched.push(id);
94
+ var a = h[j * (N + 1) + i], b = vert ? h[(j + 1) * (N + 1) + i] : h[j * (N + 1) + i + 1], u = (level - a) / (b - a);
95
+ px[id] = vert ? i * cell : (i + u) * cell; py[id] = vert ? (j + u) * cell : j * cell;
96
+ }
97
+ return id;
98
+ }
99
+ function link(a, b) { lk[2 * a + (lk[2 * a] === -1 ? 0 : 1)] = b; lk[2 * b + (lk[2 * b] === -1 ? 0 : 1)] = a; }
100
+ for (var j = 0; j < N; j++) for (var i = 0; i < N; i++) {
101
+ var tl = h[j * (N + 1) + i], tr = h[j * (N + 1) + i + 1], br = h[(j + 1) * (N + 1) + i + 1], bl = h[(j + 1) * (N + 1) + i];
102
+ var c = (tl > level ? 8 : 0) | (tr > level ? 4 : 0) | (br > level ? 2 : 0) | (bl > level ? 1 : 0);
103
+ if (c === 0 || c === 15) continue;
104
+ var centre = (tl + tr + br + bl) / 4 > level;
105
+ switch (c) {
106
+ case 1: case 14: link(pt(i, j, 1), pt(i, j + 1, 0)); break;
107
+ case 2: case 13: link(pt(i, j + 1, 0), pt(i + 1, j, 1)); break;
108
+ case 3: case 12: link(pt(i, j, 1), pt(i + 1, j, 1)); break;
109
+ case 4: case 11: link(pt(i, j, 0), pt(i + 1, j, 1)); break;
110
+ case 6: case 9: link(pt(i, j, 0), pt(i, j + 1, 0)); break;
111
+ case 7: case 8: link(pt(i, j, 1), pt(i, j, 0)); break;
112
+ case 5:
113
+ if (centre) { link(pt(i, j, 1), pt(i, j, 0)); link(pt(i, j + 1, 0), pt(i + 1, j, 1)); }
114
+ else { link(pt(i, j, 1), pt(i, j + 1, 0)); link(pt(i, j, 0), pt(i + 1, j, 1)); }
115
+ break;
116
+ case 10:
117
+ if (centre) { link(pt(i, j, 0), pt(i + 1, j, 1)); link(pt(i, j, 1), pt(i, j + 1, 0)); }
118
+ else { link(pt(i, j, 1), pt(i, j, 0)); link(pt(i, j + 1, 0), pt(i + 1, j, 1)); }
119
+ break;
120
+ }
121
+ }
122
+ // Chain the segments into lines: open lines start at an end (one link), then whatever is left is a loop.
123
+ var lines = [], seen = pass * 2; // reuse stamp: a point is used once stamp[id] === -seen
124
+ function walk(start) {
125
+ var pts = [], prev = -1, cur = start;
126
+ while (cur !== -1 && stamp[cur] !== -seen) {
127
+ stamp[cur] = -seen; pts.push(px[cur], py[cur]);
128
+ var n0 = lk[2 * cur], n1 = lk[2 * cur + 1], next = n0 !== prev && n0 !== -1 && stamp[n0] !== -seen ? n0 : n1 !== prev && n1 !== -1 && stamp[n1] !== -seen ? n1 : -1;
129
+ prev = cur; cur = next;
130
+ }
131
+ var closed = lk[2 * prev] === start || lk[2 * prev + 1] === start;
132
+ return { pts: pts, closed: closed && pts.length > 4 };
133
+ }
134
+ var k, id;
135
+ for (k = 0; k < touched.length; k++) { id = touched[k]; if (stamp[id] === pass && lk[2 * id + 1] === -1) lines.push(walk(id)); }
136
+ for (k = 0; k < touched.length; k++) { id = touched[k]; if (stamp[id] === pass) lines.push(walk(id)); }
137
+ return lines;
138
+ }
139
+
140
+ // Clean each traced line before drawing it. Marching squares puts two points almost on top of each other
141
+ // wherever a contour passes close to a grid corner; merging them removes the burrs that flicker as the
142
+ // terrain moves. Loops smaller than two cells, and edge stubs shorter than that, pop in and out, so they are dropped.
143
+ var minGap = cell * 0.4, minLoop = cell * 2;
144
+ function clean(ln) {
145
+ var p = ln.pts, n = p.length / 2, q = [p[0], p[1]], k, dx, dy;
146
+ for (k = 1; k < n; k++) {
147
+ dx = p[2 * k] - q[q.length - 2]; dy = p[2 * k + 1] - q[q.length - 1];
148
+ if (dx * dx + dy * dy >= minGap * minGap) q.push(p[2 * k], p[2 * k + 1]);
149
+ else if (!ln.closed && k === n - 1) { q[q.length - 2] = p[2 * k]; q[q.length - 1] = p[2 * k + 1]; } // keep the true end on the edge
150
+ }
151
+ if (ln.closed) {
152
+ dx = q[0] - q[q.length - 2]; dy = q[1] - q[q.length - 1];
153
+ if (q.length > 2 && dx * dx + dy * dy < minGap * minGap) q.length -= 2;
154
+ var x0 = Infinity, x1 = -Infinity, y0 = Infinity, y1 = -Infinity;
155
+ for (k = 0; k < q.length; k += 2) { x0 = Math.min(x0, q[k]); x1 = Math.max(x1, q[k]); y0 = Math.min(y0, q[k + 1]); y1 = Math.max(y1, q[k + 1]); }
156
+ if (q.length < 8 || Math.max(x1 - x0, y1 - y0) < minLoop) return null;
157
+ }
158
+ if (!ln.closed) { // stubs clipped by the plate's edge flicker the same way
159
+ var len = 0;
160
+ for (k = 2; k < q.length; k += 2) len += Math.sqrt((q[k] - q[k - 2]) * (q[k] - q[k - 2]) + (q[k + 1] - q[k - 1]) * (q[k + 1] - q[k - 1]));
161
+ if (len < minLoop) return null;
162
+ }
163
+ return q.length >= 4 ? { pts: q, closed: ln.closed } : null;
164
+ }
165
+
166
+ // Centripetal Catmull-Rom through the points (alpha 0.5), written as cubic Béziers rounded to 0.1 units.
167
+ // Unlike the uniform form, it never overshoots into a cusp or a loop when neighbouring points are unevenly spaced.
168
+ function r1(n) { return Math.round(n * 10) / 10; }
169
+ function path(lines) {
170
+ var d = [];
171
+ for (var l = 0; l < lines.length; l++) {
172
+ var ln = clean(lines[l]);
173
+ if (!ln) continue;
174
+ var p = ln.pts, n = p.length / 2, closed = ln.closed;
175
+ d.push('M', r1(p[0]), ' ', r1(p[1]));
176
+ var segs = closed ? n : n - 1;
177
+ for (var k = 0; k < segs; k++) {
178
+ var a = k === 0 ? (closed ? n - 1 : 0) : k - 1, b = k, c = k + 1 < n ? k + 1 : (closed ? 0 : n - 1), e = k + 2 < n ? k + 2 : (closed ? k + 2 - n : n - 1);
179
+ a *= 2; b *= 2; c *= 2; e *= 2;
180
+ var d1 = Math.pow((p[b] - p[a]) * (p[b] - p[a]) + (p[b + 1] - p[a + 1]) * (p[b + 1] - p[a + 1]), 0.25);
181
+ var d2 = Math.pow((p[c] - p[b]) * (p[c] - p[b]) + (p[c + 1] - p[b + 1]) * (p[c + 1] - p[b + 1]), 0.25);
182
+ var d3 = Math.pow((p[e] - p[c]) * (p[e] - p[c]) + (p[e + 1] - p[c + 1]) * (p[e + 1] - p[c + 1]), 0.25);
183
+ var c1x, c1y, c2x, c2y;
184
+ if (d1 < 1e-6) { c1x = p[b] + (p[c] - p[b]) / 3; c1y = p[b + 1] + (p[c + 1] - p[b + 1]) / 3; }
185
+ else {
186
+ var m1 = 3 * d1 * (d1 + d2), w1 = 2 * d1 * d1 + 3 * d1 * d2 + d2 * d2;
187
+ c1x = (d1 * d1 * p[c] - d2 * d2 * p[a] + w1 * p[b]) / m1; c1y = (d1 * d1 * p[c + 1] - d2 * d2 * p[a + 1] + w1 * p[b + 1]) / m1;
188
+ }
189
+ if (d3 < 1e-6) { c2x = p[c] + (p[b] - p[c]) / 3; c2y = p[c + 1] + (p[b + 1] - p[c + 1]) / 3; }
190
+ else {
191
+ var m2 = 3 * d3 * (d3 + d2), w2 = 2 * d3 * d3 + 3 * d3 * d2 + d2 * d2;
192
+ c2x = (d3 * d3 * p[b] - d2 * d2 * p[e] + w2 * p[c]) / m2; c2y = (d3 * d3 * p[b + 1] - d2 * d2 * p[e + 1] + w2 * p[c + 1]) / m2;
193
+ }
194
+ d.push('C', r1(c1x), ' ', r1(c1y), ' ', r1(c2x), ' ', r1(c2y), ' ', r1(p[c]), ' ', r1(p[c + 1]));
195
+ }
196
+ if (closed) d.push('Z');
197
+ }
198
+ return d.join('');
199
+ }
200
+
201
+ return {
202
+ options: o,
203
+ levels: levels,
204
+ // Path data for every level at time t (in noise units).
205
+ contours: function (t) {
206
+ height(t || 0);
207
+ return levels.map(function (lv, i) { return { level: i + 1, index: (i + 1) % o.index === 0, d: path(trace(lv)) }; });
208
+ }
209
+ };
210
+ }
211
+
212
+ // ------------------------------------------------------------ static markup (used by the build)
213
+ // kind 'backdrop' draws the same survey full-bleed behind a page (docs/components/backdrop.md): sliced to cover
214
+ // the viewport instead of fitted into the plate's square.
215
+ function markup(opts, kind) {
216
+ var bg = kind === 'backdrop';
217
+ var s = survey(opts), S = s.options.size, q = S / 4;
218
+ var grid = [q, 2 * q, 3 * q].map(function (v) { return 'M' + v + ' 0V' + S + 'M0 ' + v + 'H' + S; }).join('');
219
+ var paths = s.contours(0).map(function (c, i) {
220
+ return '<path class="cx-plate__line' + (c.index ? ' cx-plate__line--index' : '') + '" style="--i: ' + i + '" pathLength="1" d="' + c.d + '"></path>';
221
+ }).join('');
222
+ return '<div class="' + (bg ? 'cx-backdrop' : 'cx-plate') + '" aria-hidden="true" data-plate-seed="' + s.options.seed + '">' +
223
+ '<svg class="cx-plate__map" viewBox="0 0 ' + S + ' ' + S + '"' + (bg ? ' preserveAspectRatio="xMidYMid slice"' : '') + ' focusable="false">' +
224
+ '<path class="cx-plate__grid" d="' + grid + '"></path>' + paths + '</svg></div>';
225
+ }
226
+ function backdrop(opts) { return markup(opts, 'backdrop'); }
227
+
228
+ // ------------------------------------------------------------ animation (browser only)
229
+ function mount(root) {
230
+ var doc = root.ownerDocument, win = doc.defaultView;
231
+ // A plate's pause control sits in its frame; the backdrop's sits in the site header.
232
+ var toggle = root.classList.contains('cx-backdrop')
233
+ ? doc.querySelector('.cx-backdrop__pause')
234
+ : (root.closest('.cx-plate-frame') || root.parentElement).querySelector('.cx-plate__pause');
235
+ var input = toggle && toggle.querySelector('input');
236
+ // The backdrop's pause is a reader preference, like the theme: it is stored under cx-backdrop ("paused", or no key
237
+ // for running) and restored on every page. A plate's pause belongs to that plate and isn't kept.
238
+ if (input && root.classList.contains('cx-backdrop')) {
239
+ try { if (win.localStorage.getItem('cx-backdrop') === 'paused') input.checked = true; } catch (e) { /* storage blocked */ }
240
+ input.addEventListener('change', function () {
241
+ try { if (input.checked) win.localStorage.setItem('cx-backdrop', 'paused'); else win.localStorage.removeItem('cx-backdrop'); }
242
+ catch (e) { /* storage blocked: the pause still applies to this page */ }
243
+ });
244
+ }
245
+ var paths = root.querySelectorAll('.cx-plate__line');
246
+ var reduce = win.matchMedia('(prefers-reduced-motion: reduce)');
247
+ function ms(name) { // durations come from the tokens, through custom properties on the plate
248
+ var v = win.getComputedStyle(root).getPropertyValue(name).trim();
249
+ return v.endsWith('ms') ? parseFloat(v) : parseFloat(v) * 1000;
250
+ }
251
+ var drift = ms('--cx-plate-drift'), delay = ms('--cx-plate-draw');
252
+ var s = survey({ seed: Number(root.getAttribute('data-plate-seed')) || DEFAULTS.seed });
253
+ var backdrop = root.classList.contains('cx-backdrop');
254
+ // The drift never starts at full speed: from rest its speed eases up over one draw duration, on load and again
255
+ // after a pause, a hidden tab or scrolling back into view. Pausing still stops it at once.
256
+ var elapsed = 0, last = null, raf = 0, frames = 0, visible = true, ready = false, warm = 0;
257
+
258
+ function running() { return ready && visible && !doc.hidden && !reduce.matches && !(input && input.checked); }
259
+ function tick(now) {
260
+ raf = 0;
261
+ if (!running()) { last = null; warm = 0; return; }
262
+ if (last !== null) {
263
+ var dt = Math.min(now - last, delay / 16); // a long gap (background tab) never jumps the terrain
264
+ warm = Math.min(1, warm + dt / delay);
265
+ elapsed += dt * warm * warm * (3 - 2 * warm); // speed follows a smoothstep from 0 to full
266
+ }
267
+ last = now;
268
+ // Redraw on every second frame: the terrain moves so little per frame that this looks the same at half the cost.
269
+ if ((frames++ & 1) === 0) s.contours(elapsed / drift).forEach(function (c, i) { paths[i].setAttribute('d', c.d); });
270
+ raf = win.requestAnimationFrame(tick);
271
+ }
272
+ function update() {
273
+ if (toggle) toggle.hidden = reduce.matches;
274
+ if (running() && !raf) raf = win.requestAnimationFrame(tick);
275
+ }
276
+ if (input) input.addEventListener('change', update);
277
+ if (reduce.addEventListener) reduce.addEventListener('change', update);
278
+ doc.addEventListener('visibilitychange', update);
279
+ if ('IntersectionObserver' in win) new win.IntersectionObserver(function (e) { visible = e[0].isIntersecting; update(); }).observe(root);
280
+ // A plate draws its lines in first; the backdrop has no draw-in, so it starts as soon as the script runs.
281
+ if (backdrop) ready = true; else win.setTimeout(function () { ready = true; update(); }, delay);
282
+ update();
283
+ }
284
+
285
+ g.cxPlate = { survey: survey, markup: markup, backdrop: backdrop, mount: mount, defaults: DEFAULTS };
286
+
287
+ // In a browser, start every plate and backdrop on the page. Without this script the static contours stay as they are.
288
+ if (typeof document !== 'undefined' && document.querySelectorAll) {
289
+ var start = function () { Array.prototype.forEach.call(document.querySelectorAll('.cx-plate[data-plate-seed], .cx-backdrop[data-plate-seed]'), mount); };
290
+ if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', start); else start();
291
+ }
292
+ })(typeof window !== 'undefined' ? window : globalThis);
@@ -1 +1 @@
1
- try{var t=localStorage.getItem('cx-theme');if(t==='light'||t==='dark')document.documentElement.setAttribute('data-theme',t)}catch(e){}
1
+ try{var t=localStorage.getItem('cx-theme');if(t==='light'||t==='dark')document.documentElement.setAttribute('data-theme',t)}catch(e){}