@abreen/tada 1.16.8 → 1.16.9

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@abreen/tada",
3
- "version": "1.16.8",
3
+ "version": "1.16.9",
4
4
  "type": "module",
5
5
  "description": "A static site generator",
6
6
  "license": "MIT",
@@ -4,6 +4,33 @@ export type ContrastPreference = 'standard' | 'high';
4
4
  const FONT_STORAGE_KEY = 'fontPreference';
5
5
  const CONTRAST_STORAGE_KEY = 'contrastPreference';
6
6
 
7
+ interface FontPreferenceRequest {
8
+ generation: number;
9
+ preference: FontPreference;
10
+ promise: Promise<void>;
11
+ }
12
+
13
+ interface FontPreferenceLoader {
14
+ supported: boolean;
15
+ pending: FontPreferenceRequest | null;
16
+ failedPreference: FontPreference | null;
17
+ request(preference: FontPreference): FontPreferenceRequest | null;
18
+ isCurrent(request: FontPreferenceRequest): boolean;
19
+ complete(request: FontPreferenceRequest): void;
20
+ fail(request: FontPreferenceRequest): void;
21
+ cancel(): void;
22
+ }
23
+
24
+ function getFontPreferenceLoader(
25
+ window: Window,
26
+ ): FontPreferenceLoader | undefined {
27
+ return (
28
+ window as Window & {
29
+ __tadaFontPreferenceLoader?: FontPreferenceLoader;
30
+ }
31
+ ).__tadaFontPreferenceLoader;
32
+ }
33
+
7
34
  function getStorage(window: Window): Storage | null {
8
35
  try {
9
36
  return window.localStorage;
@@ -91,6 +118,12 @@ function getDefaultFontPreference(document: Document): FontPreference {
91
118
  : 'sans';
92
119
  }
93
120
 
121
+ function getAppliedFontPreference(document: Document): FontPreference {
122
+ return document.documentElement.dataset.fontPreference === 'serif'
123
+ ? 'serif'
124
+ : 'sans';
125
+ }
126
+
94
127
  function getDefaultContrastPreference(window: Window): ContrastPreference {
95
128
  const configured =
96
129
  window.document.documentElement.dataset.defaultContrastPreference === 'high'
@@ -179,27 +212,78 @@ export default function mountAppearancePicker(window: Window): () => void {
179
212
  container.querySelectorAll<HTMLButtonElement>('button'),
180
213
  );
181
214
  const storage = getStorage(window);
215
+ const fontLoader = getFontPreferenceLoader(window);
182
216
  const defaultFontPreference = getDefaultFontPreference(document);
183
217
  const defaultContrastPreference = getDefaultContrastPreference(window);
184
- let fontPreference = getFontPreference(storage, defaultFontPreference);
218
+ const requestedFontPreference = getFontPreference(
219
+ storage,
220
+ defaultFontPreference,
221
+ );
222
+ let fontPreference = getAppliedFontPreference(document);
185
223
  let contrastPreference = getContrastPreference(
186
224
  storage,
187
225
  defaultContrastPreference,
188
226
  );
189
227
 
190
- applyFontPreference(document, fontPreference);
228
+ const requestedFontIsWaiting =
229
+ (fontLoader?.pending?.preference === requestedFontPreference ||
230
+ fontLoader?.failedPreference === requestedFontPreference) ??
231
+ false;
232
+ if (!requestedFontIsWaiting) {
233
+ fontPreference = requestedFontPreference;
234
+ applyFontPreference(document, fontPreference);
235
+ }
191
236
  applyContrastPreference(document, contrastPreference);
192
237
  syncAppearance(document, fontPreference, contrastPreference);
193
238
 
239
+ const observedRequests = new Set<number>();
240
+ const observeFontRequest = (request: FontPreferenceRequest) => {
241
+ if (observedRequests.has(request.generation)) return;
242
+ observedRequests.add(request.generation);
243
+ request.promise.then(
244
+ () => {
245
+ if (fontLoader?.isCurrent(request)) {
246
+ fontPreference = request.preference;
247
+ saveFontPreference(storage, fontPreference, defaultFontPreference);
248
+ applyFontPreference(document, fontPreference);
249
+ fontLoader.complete(request);
250
+ } else {
251
+ fontPreference = getAppliedFontPreference(document);
252
+ }
253
+ syncAppearance(document, fontPreference, contrastPreference);
254
+ },
255
+ () => {
256
+ fontLoader?.fail(request);
257
+ fontPreference = getAppliedFontPreference(document);
258
+ syncAppearance(document, fontPreference, contrastPreference);
259
+ },
260
+ );
261
+ };
262
+
263
+ if (fontLoader?.pending) {
264
+ observeFontRequest(fontLoader.pending);
265
+ }
266
+
194
267
  const handleClick = (event: Event) => {
195
268
  const button = event.currentTarget as HTMLButtonElement;
196
269
  const fontValue = button.dataset.fontPreferenceValue;
197
270
  const contrastValue = button.dataset.contrastPreferenceValue;
198
271
 
199
272
  if (fontValue === 'sans' || fontValue === 'serif') {
200
- fontPreference = fontValue;
201
- saveFontPreference(storage, fontPreference, defaultFontPreference);
202
- applyFontPreference(document, fontPreference);
273
+ fontPreference = getAppliedFontPreference(document);
274
+ if (fontValue === fontPreference) {
275
+ fontLoader?.cancel();
276
+ saveFontPreference(storage, fontPreference, defaultFontPreference);
277
+ } else {
278
+ const request = fontLoader?.request(fontValue) ?? null;
279
+ if (request) {
280
+ observeFontRequest(request);
281
+ return;
282
+ }
283
+ fontPreference = fontValue;
284
+ saveFontPreference(storage, fontPreference, defaultFontPreference);
285
+ applyFontPreference(document, fontPreference);
286
+ }
203
287
  } else if (contrastValue === 'standard' || contrastValue === 'high') {
204
288
  contrastPreference = contrastValue;
205
289
  saveContrastPreference(
@@ -38,7 +38,70 @@
38
38
  <% } %>
39
39
  <title><%= page.title %><%= site.titlePostfix %></title>
40
40
  <script>
41
+ (() => {
41
42
  document.documentElement.classList.add('js');
43
+ const fontPreferenceFamilies = {
44
+ sans: ['Inter', 'Google Sans Code'],
45
+ serif: [
46
+ '<%= site.fontOverrides?.serif ? 'Tada Custom Serif' : 'Source Serif 4' %>',
47
+ '<%= site.fontOverrides?.serifMono ? 'Tada Custom Serif Mono' : 'Libertinus Mono' %>',
48
+ ],
49
+ };
50
+ const fontPreferenceLoader = {
51
+ generation: 0,
52
+ pending: null,
53
+ failedPreference: null,
54
+ supported:
55
+ document.fonts && typeof document.fonts.load === 'function',
56
+ request(preference) {
57
+ if (!this.supported) return null;
58
+ if (this.pending?.preference === preference) return this.pending;
59
+
60
+ const generation = ++this.generation;
61
+ const [bodyFamily, monoFamily] = fontPreferenceFamilies[preference];
62
+ const promise = Promise.all([
63
+ document.fonts.load('normal 400 1em "' + bodyFamily + '"'),
64
+ document.fonts.load('normal 700 1em "' + bodyFamily + '"'),
65
+ document.fonts.load('normal 400 1em "' + monoFamily + '"'),
66
+ ]).then(matches => {
67
+ if (matches.some(faces => faces.length === 0)) {
68
+ throw new Error('A required font face did not load');
69
+ }
70
+ });
71
+ const request = { generation, preference, promise };
72
+ this.pending = request;
73
+ this.failedPreference = null;
74
+ return request;
75
+ },
76
+ isCurrent(request) {
77
+ return (
78
+ this.pending === request &&
79
+ this.generation === request.generation
80
+ );
81
+ },
82
+ complete(request) {
83
+ if (this.isCurrent(request)) this.pending = null;
84
+ },
85
+ fail(request) {
86
+ if (!this.isCurrent(request)) return;
87
+ this.pending = null;
88
+ this.failedPreference = request.preference;
89
+ },
90
+ cancel() {
91
+ ++this.generation;
92
+ this.pending = null;
93
+ this.failedPreference = null;
94
+ },
95
+ };
96
+ window.__tadaFontPreferenceLoader = fontPreferenceLoader;
97
+
98
+ const applyFontPreference = preference => {
99
+ if (preference === 'serif') {
100
+ document.documentElement.dataset.fontPreference = 'serif';
101
+ } else {
102
+ delete document.documentElement.dataset.fontPreference;
103
+ }
104
+ };
42
105
  try {
43
106
  const preference = window.localStorage.getItem('fontPreference');
44
107
  const configured =
@@ -47,10 +110,27 @@
47
110
  (preference === 'serif' || preference === 'sans') &&
48
111
  preference !== configured
49
112
  ) {
50
- if (preference === 'serif') {
51
- document.documentElement.dataset.fontPreference = 'serif';
113
+ const request = fontPreferenceLoader.request(preference);
114
+ if (!request) {
115
+ applyFontPreference(preference);
52
116
  } else {
53
- delete document.documentElement.dataset.fontPreference;
117
+ request.promise.then(
118
+ () => {
119
+ let storedPreference;
120
+ try {
121
+ storedPreference =
122
+ window.localStorage.getItem('fontPreference');
123
+ } catch {}
124
+ if (
125
+ fontPreferenceLoader.isCurrent(request) &&
126
+ storedPreference === preference
127
+ ) {
128
+ applyFontPreference(preference);
129
+ fontPreferenceLoader.complete(request);
130
+ }
131
+ },
132
+ () => fontPreferenceLoader.fail(request),
133
+ );
54
134
  }
55
135
  }
56
136
  } catch {}
@@ -81,6 +161,7 @@
81
161
  } else {
82
162
  delete document.documentElement.dataset.contrastPreference;
83
163
  }
164
+ })();
84
165
  </script>
85
166
  <% if (!isWatchMode) { %>
86
167
  <script type="speculationrules">