type-tester-tdf 2.0.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.
@@ -0,0 +1,541 @@
1
+ // src/core/opentype.ts
2
+ var FEATURES = [
3
+ // Ligatures
4
+ { tag: "liga", label: "Standard Ligatures", group: "Ligatures" },
5
+ { tag: "dlig", label: "Discretionary Ligatures", group: "Ligatures" },
6
+ { tag: "hlig", label: "Historical Ligatures", group: "Ligatures" },
7
+ { tag: "clig", label: "Contextual Ligatures", group: "Ligatures" },
8
+ // Letter Case
9
+ { tag: "smcp", label: "Small Capitals", group: "Letter Case" },
10
+ { tag: "c2sc", label: "Capitals to Small Capitals", group: "Letter Case" },
11
+ { tag: "case", label: "Case-Sensitive Forms", group: "Letter Case" },
12
+ { tag: "cpsp", label: "Capital Spacing", group: "Letter Case" },
13
+ // Figures
14
+ { tag: "lnum", label: "Lining Figures", group: "Figures" },
15
+ { tag: "onum", label: "Oldstyle Figures", group: "Figures" },
16
+ { tag: "pnum", label: "Proportional Figures", group: "Figures" },
17
+ { tag: "tnum", label: "Tabular Figures", group: "Figures" },
18
+ { tag: "zero", label: "Slashed Zero", group: "Figures" },
19
+ { tag: "ordn", label: "Ordinals", group: "Figures" },
20
+ // Fractions
21
+ { tag: "frac", label: "Fractions", group: "Fractions" },
22
+ { tag: "afrc", label: "Alternative Fractions", group: "Fractions" },
23
+ // Alternates
24
+ { tag: "swsh", label: "Swash", group: "Alternates" },
25
+ { tag: "calt", label: "Contextual Alternates", group: "Alternates" },
26
+ { tag: "salt", label: "Stylistic Alternates", group: "Alternates" },
27
+ { tag: "hist", label: "Historical Forms", group: "Alternates" },
28
+ { tag: "nalt", label: "Alternate Annotation Forms", group: "Alternates" },
29
+ // Position
30
+ { tag: "sups", label: "Superscript", group: "Position" },
31
+ { tag: "subs", label: "Subscript", group: "Position" },
32
+ // Stylistic Sets ss01–ss20
33
+ ...Array.from({ length: 20 }, (_, i) => {
34
+ const n = i + 1;
35
+ const tag = `ss${String(n).padStart(2, "0")}`;
36
+ return { tag, label: `Stylistic Set ${n}`, group: "Stylistic Sets" };
37
+ })
38
+ ];
39
+ var FEATURE_BY_TAG = new Map(
40
+ FEATURES.map((f) => [f.tag, f])
41
+ );
42
+ function featureLabel(tag) {
43
+ return FEATURE_BY_TAG.get(tag)?.label ?? tag.toUpperCase();
44
+ }
45
+ function isKnownFeature(tag) {
46
+ return FEATURE_BY_TAG.has(tag);
47
+ }
48
+ function featureSettings(active) {
49
+ const tags = Array.from(active).filter(isKnownFeature);
50
+ if (tags.length === 0) return "normal";
51
+ return tags.map((tag) => `"${tag}" 1`).join(", ");
52
+ }
53
+
54
+ // src/core/dom.ts
55
+ function el(tag, props = {}) {
56
+ const node = document.createElement(tag);
57
+ if (props.class) node.className = props.class;
58
+ if (props.text != null) node.textContent = props.text;
59
+ if (props.attrs) {
60
+ for (const [name, value] of Object.entries(props.attrs)) {
61
+ if (value == null || value === false) continue;
62
+ node.setAttribute(name, value === true ? "" : String(value));
63
+ }
64
+ }
65
+ if (props.children) {
66
+ for (const child of props.children) node.appendChild(child);
67
+ }
68
+ return node;
69
+ }
70
+ function clamp(value, min, max) {
71
+ return Math.min(max, Math.max(min, value));
72
+ }
73
+ function toNumber(value, fallback) {
74
+ if (value == null || value === "") return fallback;
75
+ const n = typeof value === "number" ? value : Number(value);
76
+ return Number.isFinite(n) ? n : fallback;
77
+ }
78
+
79
+ // src/core/fit.ts
80
+ var REFERENCE_SIZE = 100;
81
+ var MIRROR_PROPS = [
82
+ "fontFamily",
83
+ "fontWeight",
84
+ "fontStyle",
85
+ "fontStretch",
86
+ "letterSpacing",
87
+ "fontFeatureSettings",
88
+ "fontVariationSettings",
89
+ "textTransform"
90
+ ];
91
+ var Fitter = class {
92
+ /**
93
+ * @param target Element whose text is being fitted.
94
+ * @param min Minimum font-size in px.
95
+ * @param max Maximum font-size in px.
96
+ */
97
+ constructor(target, min, max) {
98
+ this.mirror = null;
99
+ this.observer = null;
100
+ this.frame = 0;
101
+ this.onFit = null;
102
+ this.destroyed = false;
103
+ // Last container width fitted against. Used to ignore ResizeObserver
104
+ // notifications caused purely by height changes (a fit grows the line
105
+ // height, which would otherwise re-trigger the observer in a loop).
106
+ this.lastWidth = -1;
107
+ this.target = target;
108
+ this.min = min;
109
+ this.max = max;
110
+ }
111
+ /**
112
+ * Starts observing the target's container and reports the fitted size via
113
+ * `onFit` whenever it should change. Re-fits once fonts have loaded.
114
+ */
115
+ start(onFit) {
116
+ this.onFit = onFit;
117
+ if (typeof ResizeObserver !== "undefined") {
118
+ const container = this.target.parentElement ?? this.target;
119
+ this.observer = new ResizeObserver(() => {
120
+ if (container.clientWidth !== this.lastWidth) this.schedule();
121
+ });
122
+ this.observer.observe(container);
123
+ }
124
+ const fonts = document.fonts;
125
+ if (fonts?.ready) {
126
+ fonts.ready.then(() => this.schedule()).catch(() => {
127
+ });
128
+ }
129
+ this.schedule();
130
+ }
131
+ /** Requests a fit on the next animation frame, coalescing rapid calls. */
132
+ schedule() {
133
+ if (this.destroyed) return;
134
+ if (typeof requestAnimationFrame === "undefined") {
135
+ this.fit();
136
+ return;
137
+ }
138
+ if (this.frame) return;
139
+ this.frame = requestAnimationFrame(() => {
140
+ this.frame = 0;
141
+ this.fit();
142
+ });
143
+ }
144
+ /** Measures the text and reports the fitted size. */
145
+ fit() {
146
+ if (this.destroyed || !this.onFit) return;
147
+ const container = this.target.parentElement ?? this.target;
148
+ const width = container.clientWidth;
149
+ if (width <= 0) return;
150
+ const mirror = this.ensureMirror();
151
+ const computed = getComputedStyle(this.target);
152
+ for (const prop of MIRROR_PROPS) {
153
+ mirror.style[prop] = computed[prop];
154
+ }
155
+ mirror.style.fontSize = `${REFERENCE_SIZE}px`;
156
+ mirror.textContent = this.target.textContent ?? "";
157
+ const measured = mirror.getBoundingClientRect().width;
158
+ if (measured <= 0) return;
159
+ this.lastWidth = width;
160
+ const size = clamp(REFERENCE_SIZE * width / measured, this.min, this.max);
161
+ this.onFit(Math.round(size * 100) / 100);
162
+ }
163
+ /** Lazily creates the shared offscreen measurement mirror. */
164
+ ensureMirror() {
165
+ if (this.mirror) return this.mirror;
166
+ const mirror = document.createElement("span");
167
+ mirror.setAttribute("aria-hidden", "true");
168
+ Object.assign(mirror.style, {
169
+ position: "absolute",
170
+ left: "-9999px",
171
+ top: "0",
172
+ visibility: "hidden",
173
+ whiteSpace: "nowrap",
174
+ pointerEvents: "none"
175
+ });
176
+ document.body.appendChild(mirror);
177
+ this.mirror = mirror;
178
+ return mirror;
179
+ }
180
+ /** Stops observing and removes the mirror. */
181
+ destroy() {
182
+ this.destroyed = true;
183
+ if (this.frame) cancelAnimationFrame(this.frame);
184
+ this.observer?.disconnect();
185
+ this.observer = null;
186
+ this.mirror?.remove();
187
+ this.mirror = null;
188
+ this.onFit = null;
189
+ }
190
+ };
191
+
192
+ // src/core/typeTester.ts
193
+ var DEFAULT_RANGES = {
194
+ size: { min: 8, max: 300, step: 1 },
195
+ tracking: { min: -0.1, max: 0.5, step: 5e-3 },
196
+ weight: { min: 100, max: 900, step: 100 }
197
+ };
198
+ var ALIGNS = ["left", "center", "right"];
199
+ var idCounter = 0;
200
+ function nextId() {
201
+ return ++idCounter;
202
+ }
203
+ function resolveRange(value, fallback) {
204
+ if (!value) return null;
205
+ if (value === true) return { ...fallback };
206
+ return { min: value.min, max: value.max, step: value.step ?? fallback.step };
207
+ }
208
+ var TypeTester = class {
209
+ /**
210
+ * @param host Element to render the tester into.
211
+ * @param options Configuration; all fields optional.
212
+ */
213
+ constructor(host, options = {}) {
214
+ this.activeFeatures = /* @__PURE__ */ new Set();
215
+ this.cleanups = [];
216
+ this.sizeOutput = null;
217
+ this.fitter = null;
218
+ this.host = host;
219
+ this.options = options;
220
+ this.controls = options.controls ?? {};
221
+ const fit = options.size === "fit";
222
+ for (const tag of options.features ?? []) {
223
+ if (isKnownFeature(tag)) this.activeFeatures.add(tag);
224
+ }
225
+ this.state = {
226
+ text: options.text ?? "",
227
+ size: fit ? 0 : toNumber(options.size, 80),
228
+ fit,
229
+ tracking: toNumber(options.tracking, 0),
230
+ weight: toNumber(options.weight, 400),
231
+ italic: options.italic ?? false,
232
+ align: options.align ?? "left",
233
+ wrap: options.wrap ?? true,
234
+ features: Array.from(this.activeFeatures)
235
+ };
236
+ this.render();
237
+ }
238
+ /** Returns a snapshot of the current state. */
239
+ getState() {
240
+ return { ...this.state, features: Array.from(this.activeFeatures) };
241
+ }
242
+ /** Removes all DOM, listeners and observers created by this instance. */
243
+ destroy() {
244
+ this.fitter?.destroy();
245
+ this.fitter = null;
246
+ for (const off of this.cleanups.splice(0)) off();
247
+ this.host.replaceChildren();
248
+ }
249
+ // ---- rendering -------------------------------------------------------
250
+ render() {
251
+ this.host.classList.add("tt");
252
+ this.textEl = el("div", {
253
+ class: "tt__text",
254
+ text: this.state.text,
255
+ attrs: {
256
+ role: "textbox",
257
+ "aria-label": this.options.ariaLabel ?? "Sample text",
258
+ "aria-multiline": String(this.state.wrap),
259
+ contenteditable: this.options.editable !== false ? "true" : null,
260
+ spellcheck: "true",
261
+ "data-placeholder": this.options.placeholder ?? "Type to test\u2026"
262
+ }
263
+ });
264
+ this.typeEl = el("div", { class: "tt__type", children: [this.textEl] });
265
+ const stage = el("div", { class: "tt__stage", children: [this.typeEl] });
266
+ this.liveEl = el("div", {
267
+ class: "tt__sr-only",
268
+ attrs: { "aria-live": "polite", "aria-atomic": "true" }
269
+ });
270
+ const controls = this.buildControls();
271
+ const children = [stage];
272
+ if (controls) children.push(controls);
273
+ children.push(this.liveEl);
274
+ this.host.replaceChildren(...children);
275
+ if (this.options.editable !== false) this.wireEditable();
276
+ this.applyStyles();
277
+ this.setupFit();
278
+ }
279
+ buildControls() {
280
+ const items = [];
281
+ const sizeRange = resolveRange(this.controls.size, DEFAULT_RANGES.size);
282
+ if (sizeRange && !this.state.fit) items.push(this.buildSize(sizeRange));
283
+ const trackingRange = resolveRange(this.controls.tracking, DEFAULT_RANGES.tracking);
284
+ if (trackingRange) items.push(this.buildTracking(trackingRange));
285
+ const weightRange = resolveRange(
286
+ this.controls.weight,
287
+ this.options.variable?.wght ?? DEFAULT_RANGES.weight
288
+ );
289
+ if (weightRange) items.push(this.buildWeight(weightRange));
290
+ if (this.controls.italic) items.push(this.buildItalic());
291
+ if (this.controls.align) items.push(this.buildAlign());
292
+ if (this.controls.wrap) items.push(this.buildWrap());
293
+ if (this.controls.features) items.push(this.buildFeatures());
294
+ if (items.length === 0) return null;
295
+ return el("div", {
296
+ class: "tt__controls",
297
+ attrs: { role: "group", "aria-label": "Typography controls" },
298
+ children: items
299
+ });
300
+ }
301
+ // ---- generic control builders (no per-control copy-paste) -------------
302
+ buildSlider(key, label, range, value, unit, onInput) {
303
+ const output = el("output", { class: "tt__value", text: `${value}${unit}` });
304
+ const input = el("input", {
305
+ class: `tt__slider tt__slider--${key}`,
306
+ attrs: {
307
+ type: "range",
308
+ min: range.min,
309
+ max: range.max,
310
+ step: range.step ?? 1,
311
+ value,
312
+ "aria-label": label
313
+ }
314
+ });
315
+ const handler = () => {
316
+ const v = Number(input.value);
317
+ output.textContent = `${v}${unit}`;
318
+ onInput(v);
319
+ this.announce(`${label} ${v}${unit}`);
320
+ };
321
+ input.addEventListener("input", handler);
322
+ this.cleanups.push(() => input.removeEventListener("input", handler));
323
+ if (key === "size") this.sizeOutput = output;
324
+ return el("label", {
325
+ class: `tt__control tt__control--${key}`,
326
+ children: [el("span", { class: "tt__label", text: label }), input, output]
327
+ });
328
+ }
329
+ buildSize(range) {
330
+ this.state.size = clamp(this.state.size, range.min, range.max);
331
+ return this.buildSlider("size", "Size", range, this.state.size, "px", (v) => {
332
+ this.state.size = v;
333
+ this.applyStyles();
334
+ this.emitChange();
335
+ });
336
+ }
337
+ buildTracking(range) {
338
+ this.state.tracking = clamp(this.state.tracking, range.min, range.max);
339
+ return this.buildSlider(
340
+ "tracking",
341
+ "Tracking",
342
+ range,
343
+ this.state.tracking,
344
+ "em",
345
+ (v) => {
346
+ this.state.tracking = v;
347
+ this.applyStyles();
348
+ this.emitChange();
349
+ }
350
+ );
351
+ }
352
+ buildWeight(range) {
353
+ this.state.weight = clamp(this.state.weight, range.min, range.max);
354
+ return this.buildSlider("weight", "Weight", range, this.state.weight, "", (v) => {
355
+ this.state.weight = v;
356
+ this.applyStyles();
357
+ this.emitChange();
358
+ });
359
+ }
360
+ buildToggle(key, label, pressed, onToggle) {
361
+ const button = el("button", {
362
+ class: `tt__toggle tt__toggle--${key}`,
363
+ text: label,
364
+ // aria-pressed is a string-valued ARIA state and must always be present
365
+ // ("false"), unlike boolean HTML attributes which are omitted when off.
366
+ attrs: { type: "button", "aria-pressed": String(pressed) }
367
+ });
368
+ const handler = () => {
369
+ const next = button.getAttribute("aria-pressed") !== "true";
370
+ button.setAttribute("aria-pressed", String(next));
371
+ onToggle(next);
372
+ this.announce(`${label} ${next ? "on" : "off"}`);
373
+ };
374
+ button.addEventListener("click", handler);
375
+ this.cleanups.push(() => button.removeEventListener("click", handler));
376
+ return button;
377
+ }
378
+ buildItalic() {
379
+ const button = this.buildToggle("italic", "Italic", this.state.italic, (on) => {
380
+ this.state.italic = on;
381
+ this.applyStyles();
382
+ this.emitChange();
383
+ });
384
+ return el("div", { class: "tt__control", children: [button] });
385
+ }
386
+ buildWrap() {
387
+ const button = this.buildToggle("wrap", "Wrap", this.state.wrap, (on) => {
388
+ this.state.wrap = on;
389
+ this.textEl.setAttribute("aria-multiline", String(on));
390
+ this.applyStyles();
391
+ this.emitChange();
392
+ });
393
+ return el("div", { class: "tt__control", children: [button] });
394
+ }
395
+ buildAlign() {
396
+ const select = el("select", {
397
+ class: "tt__select tt__select--align",
398
+ attrs: { "aria-label": "Alignment" },
399
+ children: ALIGNS.map(
400
+ (a) => el("option", {
401
+ text: a.charAt(0).toUpperCase() + a.slice(1),
402
+ attrs: { value: a, selected: a === this.state.align }
403
+ })
404
+ )
405
+ });
406
+ const handler = () => {
407
+ const value = select.value;
408
+ if (ALIGNS.includes(value)) {
409
+ this.state.align = value;
410
+ this.applyStyles();
411
+ this.emitChange();
412
+ this.announce(`Alignment ${value}`);
413
+ }
414
+ };
415
+ select.addEventListener("change", handler);
416
+ this.cleanups.push(() => select.removeEventListener("change", handler));
417
+ return el("label", {
418
+ class: "tt__control tt__control--align",
419
+ children: [el("span", { class: "tt__label", text: "Align" }), select]
420
+ });
421
+ }
422
+ buildFeatures() {
423
+ const offered = Array.isArray(this.controls.features) ? this.controls.features.filter(isKnownFeature).map((tag) => FEATURE_BY_TAG.get(tag) ?? { tag, label: featureLabel(tag), group: "Alternates" }) : FEATURES;
424
+ const panelId = `tt-feat-${nextId()}`;
425
+ const toggle = el("button", {
426
+ class: "tt__toggle tt__toggle--features",
427
+ text: "Features",
428
+ attrs: {
429
+ type: "button",
430
+ "aria-haspopup": "true",
431
+ "aria-expanded": "false",
432
+ "aria-controls": panelId
433
+ }
434
+ });
435
+ const checks = offered.map((f) => {
436
+ const input = el("input", {
437
+ attrs: { type: "checkbox", value: f.tag, checked: this.activeFeatures.has(f.tag) }
438
+ });
439
+ const onChange = () => this.toggleFeature(f.tag, input.checked);
440
+ input.addEventListener("change", onChange);
441
+ this.cleanups.push(() => input.removeEventListener("change", onChange));
442
+ return el("label", {
443
+ class: "tt__feature",
444
+ children: [input, el("span", { text: f.label })]
445
+ });
446
+ });
447
+ const panel = el("div", {
448
+ class: "tt__panel",
449
+ attrs: { id: panelId, role: "group", "aria-label": "OpenType features", hidden: true },
450
+ children: checks
451
+ });
452
+ const wrapper = el("div", {
453
+ class: "tt__control tt__control--features",
454
+ children: [toggle, panel]
455
+ });
456
+ const setOpen = (open) => {
457
+ toggle.setAttribute("aria-expanded", String(open));
458
+ panel.toggleAttribute("hidden", !open);
459
+ if (open) {
460
+ const first = panel.querySelector("input");
461
+ first?.focus();
462
+ }
463
+ };
464
+ const onToggleClick = () => setOpen(toggle.getAttribute("aria-expanded") !== "true");
465
+ const onKeydown = (e) => {
466
+ if (e.key === "Escape" && toggle.getAttribute("aria-expanded") === "true") {
467
+ setOpen(false);
468
+ toggle.focus();
469
+ }
470
+ };
471
+ const onOutside = (e) => {
472
+ if (toggle.getAttribute("aria-expanded") !== "true") return;
473
+ if (!wrapper.contains(e.target)) setOpen(false);
474
+ };
475
+ toggle.addEventListener("click", onToggleClick);
476
+ document.addEventListener("keydown", onKeydown);
477
+ document.addEventListener("click", onOutside);
478
+ this.cleanups.push(() => {
479
+ toggle.removeEventListener("click", onToggleClick);
480
+ document.removeEventListener("keydown", onKeydown);
481
+ document.removeEventListener("click", onOutside);
482
+ });
483
+ return wrapper;
484
+ }
485
+ toggleFeature(tag, on) {
486
+ if (on) this.activeFeatures.add(tag);
487
+ else this.activeFeatures.delete(tag);
488
+ this.state.features = Array.from(this.activeFeatures);
489
+ this.applyStyles();
490
+ this.emitChange();
491
+ this.announce(`${featureLabel(tag)} ${on ? "on" : "off"}`);
492
+ }
493
+ // ---- behaviour -------------------------------------------------------
494
+ wireEditable() {
495
+ const handler = () => {
496
+ this.state.text = this.textEl.textContent ?? "";
497
+ if (this.state.fit) this.fitter?.schedule();
498
+ this.emitChange();
499
+ };
500
+ this.textEl.addEventListener("input", handler);
501
+ this.cleanups.push(() => this.textEl.removeEventListener("input", handler));
502
+ }
503
+ setupFit() {
504
+ if (!this.state.fit) return;
505
+ const range = resolveRange(this.controls.size, DEFAULT_RANGES.size) ?? DEFAULT_RANGES.size;
506
+ this.fitter = new Fitter(this.textEl, range.min, range.max);
507
+ this.fitter.start((size) => {
508
+ this.state.size = size;
509
+ this.typeEl.style.fontSize = `${size}px`;
510
+ if (this.sizeOutput) this.sizeOutput.textContent = `${size}px`;
511
+ });
512
+ }
513
+ /** Applies the full typographic state to the type element via element.style. */
514
+ applyStyles() {
515
+ const s = this.typeEl.style;
516
+ const family = this.options.fontFamily ? `"${this.options.fontFamily}"${this.options.fallback ? `, ${this.options.fallback}` : ", sans-serif"}` : this.options.fallback ?? "sans-serif";
517
+ s.fontFamily = family;
518
+ if (!this.state.fit) s.fontSize = `${this.state.size}px`;
519
+ s.letterSpacing = `${this.state.tracking}em`;
520
+ s.fontStyle = this.state.italic ? "italic" : "normal";
521
+ s.textAlign = this.state.align;
522
+ this.textEl.style.whiteSpace = this.state.wrap ? "normal" : "nowrap";
523
+ if (this.options.variable?.wght) {
524
+ s.fontVariationSettings = `"wght" ${this.state.weight}`;
525
+ }
526
+ s.fontWeight = String(this.state.weight);
527
+ const settings = featureSettings(this.activeFeatures);
528
+ s.fontFeatureSettings = settings;
529
+ s.setProperty("-webkit-font-feature-settings", settings);
530
+ }
531
+ announce(message) {
532
+ this.liveEl.textContent = message;
533
+ }
534
+ emitChange() {
535
+ this.options.onChange?.(this.getState());
536
+ }
537
+ };
538
+
539
+ export { FEATURES, FEATURE_BY_TAG, TypeTester, featureLabel, featureSettings, isKnownFeature };
540
+ //# sourceMappingURL=chunk-TSLTIRBT.js.map
541
+ //# sourceMappingURL=chunk-TSLTIRBT.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/core/opentype.ts","../src/core/dom.ts","../src/core/fit.ts","../src/core/typeTester.ts"],"names":[],"mappings":"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","file":"chunk-TSLTIRBT.js","sourcesContent":["// OpenType feature registry: correct, spec-accurate tags, labels and grouping.\n// Replaces the legacy optValues table which had duplicate/ambiguous labels\n// (\"Contextual\" for both calt and clig, \"Historical\" for both hlig and hist)\n// and a mislabelled `nalt`.\n\n/** A four-character OpenType GSUB/GPOS feature tag. */\nexport type FeatureTag = string;\n\n/** Logical grouping used to organise features in a control UI. */\nexport type FeatureGroup =\n\t| \"Ligatures\"\n\t| \"Letter Case\"\n\t| \"Figures\"\n\t| \"Fractions\"\n\t| \"Alternates\"\n\t| \"Position\"\n\t| \"Stylistic Sets\";\n\n/** Definition of a single OpenType feature exposed by the tester. */\nexport interface FeatureDef {\n\t/** The OpenType feature tag, e.g. \"smcp\". */\n\ttag: FeatureTag;\n\t/** Human-readable, unambiguous label. */\n\tlabel: string;\n\t/** Group the feature belongs to. */\n\tgroup: FeatureGroup;\n}\n\n/**\n * The default, ordered list of supported OpenType features. Labels are unique\n * and follow the Microsoft OpenType feature registry naming.\n */\nexport const FEATURES: readonly FeatureDef[] = [\n\t// Ligatures\n\t{ tag: \"liga\", label: \"Standard Ligatures\", group: \"Ligatures\" },\n\t{ tag: \"dlig\", label: \"Discretionary Ligatures\", group: \"Ligatures\" },\n\t{ tag: \"hlig\", label: \"Historical Ligatures\", group: \"Ligatures\" },\n\t{ tag: \"clig\", label: \"Contextual Ligatures\", group: \"Ligatures\" },\n\n\t// Letter Case\n\t{ tag: \"smcp\", label: \"Small Capitals\", group: \"Letter Case\" },\n\t{ tag: \"c2sc\", label: \"Capitals to Small Capitals\", group: \"Letter Case\" },\n\t{ tag: \"case\", label: \"Case-Sensitive Forms\", group: \"Letter Case\" },\n\t{ tag: \"cpsp\", label: \"Capital Spacing\", group: \"Letter Case\" },\n\n\t// Figures\n\t{ tag: \"lnum\", label: \"Lining Figures\", group: \"Figures\" },\n\t{ tag: \"onum\", label: \"Oldstyle Figures\", group: \"Figures\" },\n\t{ tag: \"pnum\", label: \"Proportional Figures\", group: \"Figures\" },\n\t{ tag: \"tnum\", label: \"Tabular Figures\", group: \"Figures\" },\n\t{ tag: \"zero\", label: \"Slashed Zero\", group: \"Figures\" },\n\t{ tag: \"ordn\", label: \"Ordinals\", group: \"Figures\" },\n\n\t// Fractions\n\t{ tag: \"frac\", label: \"Fractions\", group: \"Fractions\" },\n\t{ tag: \"afrc\", label: \"Alternative Fractions\", group: \"Fractions\" },\n\n\t// Alternates\n\t{ tag: \"swsh\", label: \"Swash\", group: \"Alternates\" },\n\t{ tag: \"calt\", label: \"Contextual Alternates\", group: \"Alternates\" },\n\t{ tag: \"salt\", label: \"Stylistic Alternates\", group: \"Alternates\" },\n\t{ tag: \"hist\", label: \"Historical Forms\", group: \"Alternates\" },\n\t{ tag: \"nalt\", label: \"Alternate Annotation Forms\", group: \"Alternates\" },\n\n\t// Position\n\t{ tag: \"sups\", label: \"Superscript\", group: \"Position\" },\n\t{ tag: \"subs\", label: \"Subscript\", group: \"Position\" },\n\n\t// Stylistic Sets ss01–ss20\n\t...Array.from({ length: 20 }, (_, i): FeatureDef => {\n\t\tconst n = i + 1;\n\t\tconst tag = `ss${String(n).padStart(2, \"0\")}`;\n\t\treturn { tag, label: `Stylistic Set ${n}`, group: \"Stylistic Sets\" };\n\t}),\n];\n\n/** Lookup map from tag to its definition. */\nexport const FEATURE_BY_TAG: ReadonlyMap<FeatureTag, FeatureDef> = new Map(\n\tFEATURES.map((f) => [f.tag, f]),\n);\n\n/** Returns the label for a tag, falling back to the upper-cased tag itself. */\nexport function featureLabel(tag: FeatureTag): string {\n\treturn FEATURE_BY_TAG.get(tag)?.label ?? tag.toUpperCase();\n}\n\n/** True if the tag is a known, supported OpenType feature. */\nexport function isKnownFeature(tag: string): boolean {\n\treturn FEATURE_BY_TAG.has(tag);\n}\n\n/**\n * Builds a CSS `font-feature-settings` value from a set of active tags so that\n * multiple features compose (e.g. small caps + oldstyle figures) instead of\n * each selection overwriting the others. Returns \"normal\" when none are active.\n */\nexport function featureSettings(active: Iterable<FeatureTag>): string {\n\tconst tags = Array.from(active).filter(isKnownFeature);\n\tif (tags.length === 0) return \"normal\";\n\treturn tags.map((tag) => `\"${tag}\" 1`).join(\", \");\n}\n","// Minimal, safe DOM construction helpers. All text is set via textContent and\n// all attributes via setAttribute, so untrusted values can never be interpreted\n// as HTML or script (no innerHTML, no eval).\n\n/** Properties accepted by {@link el}. */\nexport interface ElProps {\n\t/** Space-separated class names. */\n\tclass?: string;\n\t/** Text content (set via textContent — never parsed as HTML). */\n\ttext?: string;\n\t/** Attributes to set via setAttribute. Nullish values are skipped. */\n\tattrs?: Record<string, string | number | boolean | null | undefined>;\n\t/** Child nodes to append. */\n\tchildren?: Node[];\n}\n\n/**\n * Creates an element of the given tag with safe text/attribute/children\n * assignment. Returns the typed element.\n */\nexport function el<K extends keyof HTMLElementTagNameMap>(\n\ttag: K,\n\tprops: ElProps = {},\n): HTMLElementTagNameMap[K] {\n\tconst node = document.createElement(tag);\n\tif (props.class) node.className = props.class;\n\tif (props.text != null) node.textContent = props.text;\n\tif (props.attrs) {\n\t\tfor (const [name, value] of Object.entries(props.attrs)) {\n\t\t\tif (value == null || value === false) continue;\n\t\t\tnode.setAttribute(name, value === true ? \"\" : String(value));\n\t\t}\n\t}\n\tif (props.children) {\n\t\tfor (const child of props.children) node.appendChild(child);\n\t}\n\treturn node;\n}\n\n/** Clamps a number into the inclusive [min, max] range. */\nexport function clamp(value: number, min: number, max: number): number {\n\treturn Math.min(max, Math.max(min, value));\n}\n\n/**\n * Parses a numeric attribute/string, returning the fallback when the value is\n * missing or not a finite number. Prevents NaN leaking into the UI.\n */\nexport function toNumber(value: unknown, fallback: number): number {\n\tif (value == null || value === \"\") return fallback;\n\tconst n = typeof value === \"number\" ? value : Number(value);\n\treturn Number.isFinite(n) ? n : fallback;\n}\n","// Auto-fit engine. Replaces the legacy `bigtext` dependency.\n//\n// Sizing is computed by measuring the text once at a reference size in an\n// offscreen mirror, then scaling linearly: glyph advances and em-based\n// letter-spacing both scale linearly with font-size, so\n// fittedSize = referenceSize * containerWidth / measuredWidth\n// is exact and needs a single read + write per fit (no read/write thrashing,\n// no binary-search loop). Re-fitting is driven by ResizeObserver (so it tracks\n// container resizes) and rAF-debounced (so typing does not thrash layout).\n\nimport { clamp } from \"./dom.js\";\n\nconst REFERENCE_SIZE = 100;\n\n/** Font properties copied to the mirror so measurement matches the real text. */\nconst MIRROR_PROPS = [\n\t\"fontFamily\",\n\t\"fontWeight\",\n\t\"fontStyle\",\n\t\"fontStretch\",\n\t\"letterSpacing\",\n\t\"fontFeatureSettings\",\n\t\"fontVariationSettings\",\n\t\"textTransform\",\n] as const;\n\n/** Drives auto-fit sizing for a single text element. */\nexport class Fitter {\n\tprivate readonly target: HTMLElement;\n\tprivate readonly min: number;\n\tprivate readonly max: number;\n\tprivate mirror: HTMLSpanElement | null = null;\n\tprivate observer: ResizeObserver | null = null;\n\tprivate frame = 0;\n\tprivate onFit: ((size: number) => void) | null = null;\n\tprivate destroyed = false;\n\t// Last container width fitted against. Used to ignore ResizeObserver\n\t// notifications caused purely by height changes (a fit grows the line\n\t// height, which would otherwise re-trigger the observer in a loop).\n\tprivate lastWidth = -1;\n\n\t/**\n\t * @param target Element whose text is being fitted.\n\t * @param min Minimum font-size in px.\n\t * @param max Maximum font-size in px.\n\t */\n\tconstructor(target: HTMLElement, min: number, max: number) {\n\t\tthis.target = target;\n\t\tthis.min = min;\n\t\tthis.max = max;\n\t}\n\n\t/**\n\t * Starts observing the target's container and reports the fitted size via\n\t * `onFit` whenever it should change. Re-fits once fonts have loaded.\n\t */\n\tstart(onFit: (size: number) => void): void {\n\t\tthis.onFit = onFit;\n\t\tif (typeof ResizeObserver !== \"undefined\") {\n\t\t\tconst container = this.target.parentElement ?? this.target;\n\t\t\tthis.observer = new ResizeObserver(() => {\n\t\t\t\t// Only re-fit when the available width changes; height-only changes\n\t\t\t\t// are a consequence of fitting and must not feed back.\n\t\t\t\tif (container.clientWidth !== this.lastWidth) this.schedule();\n\t\t\t});\n\t\t\tthis.observer.observe(container);\n\t\t}\n\t\t// Re-fit after web fonts load so we never measure fallback metrics.\n\t\tconst fonts = (document as Document & { fonts?: FontFaceSet }).fonts;\n\t\tif (fonts?.ready) {\n\t\t\tfonts.ready.then(() => this.schedule()).catch(() => {});\n\t\t}\n\t\tthis.schedule();\n\t}\n\n\t/** Requests a fit on the next animation frame, coalescing rapid calls. */\n\tschedule(): void {\n\t\tif (this.destroyed) return;\n\t\tif (typeof requestAnimationFrame === \"undefined\") {\n\t\t\tthis.fit();\n\t\t\treturn;\n\t\t}\n\t\tif (this.frame) return;\n\t\tthis.frame = requestAnimationFrame(() => {\n\t\t\tthis.frame = 0;\n\t\t\tthis.fit();\n\t\t});\n\t}\n\n\t/** Measures the text and reports the fitted size. */\n\tprivate fit(): void {\n\t\tif (this.destroyed || !this.onFit) return;\n\t\tconst container = this.target.parentElement ?? this.target;\n\t\tconst width = container.clientWidth;\n\t\tif (width <= 0) return;\n\n\t\tconst mirror = this.ensureMirror();\n\t\tconst computed = getComputedStyle(this.target);\n\t\tfor (const prop of MIRROR_PROPS) {\n\t\t\tmirror.style[prop] = computed[prop];\n\t\t}\n\t\tmirror.style.fontSize = `${REFERENCE_SIZE}px`;\n\t\tmirror.textContent = this.target.textContent ?? \"\";\n\n\t\tconst measured = mirror.getBoundingClientRect().width;\n\t\tif (measured <= 0) return;\n\n\t\tthis.lastWidth = width;\n\t\tconst size = clamp((REFERENCE_SIZE * width) / measured, this.min, this.max);\n\t\tthis.onFit(Math.round(size * 100) / 100);\n\t}\n\n\t/** Lazily creates the shared offscreen measurement mirror. */\n\tprivate ensureMirror(): HTMLSpanElement {\n\t\tif (this.mirror) return this.mirror;\n\t\tconst mirror = document.createElement(\"span\");\n\t\tmirror.setAttribute(\"aria-hidden\", \"true\");\n\t\tObject.assign(mirror.style, {\n\t\t\tposition: \"absolute\",\n\t\t\tleft: \"-9999px\",\n\t\t\ttop: \"0\",\n\t\t\tvisibility: \"hidden\",\n\t\t\twhiteSpace: \"nowrap\",\n\t\t\tpointerEvents: \"none\",\n\t\t});\n\t\tdocument.body.appendChild(mirror);\n\t\tthis.mirror = mirror;\n\t\treturn mirror;\n\t}\n\n\t/** Stops observing and removes the mirror. */\n\tdestroy(): void {\n\t\tthis.destroyed = true;\n\t\tif (this.frame) cancelAnimationFrame(this.frame);\n\t\tthis.observer?.disconnect();\n\t\tthis.observer = null;\n\t\tthis.mirror?.remove();\n\t\tthis.mirror = null;\n\t\tthis.onFit = null;\n\t}\n}\n","// Framework-agnostic, dependency-free type tester.\n//\n// Replaces the legacy jQuery/jQuery-UI/bigtext widget. Key differences:\n// - No eval, no innerHTML with user data — DOM is built with createElement and\n// textContent, styles via element.style (no string injection).\n// - Native, accessible controls: <input type=range>, <button aria-pressed>,\n// <select>, and checkbox-based feature toggles with a focus-managed panel.\n// - OpenType features compose (multiple active at once).\n// - Auto-fit via ResizeObserver (tracks container resizes) instead of bigtext.\n// - No global state; each instance owns its DOM, listeners and observers and is\n// fully torn down by destroy().\n\nimport { Fitter } from \"./fit.js\";\nimport { clamp, el, toNumber } from \"./dom.js\";\nimport {\n\tFEATURES,\n\tFEATURE_BY_TAG,\n\ttype FeatureDef,\n\ttype FeatureTag,\n\tfeatureLabel,\n\tfeatureSettings,\n\tisKnownFeature,\n} from \"./opentype.js\";\nimport type {\n\tAlign,\n\tControlsConfig,\n\tRange,\n\tTypeTesterOptions,\n\tTypeTesterState,\n} from \"./types.js\";\n\n/** Default slider ranges, overridable per control. */\nconst DEFAULT_RANGES = {\n\tsize: { min: 8, max: 300, step: 1 },\n\ttracking: { min: -0.1, max: 0.5, step: 0.005 },\n\tweight: { min: 100, max: 900, step: 100 },\n} as const;\n\nconst ALIGNS: readonly Align[] = [\"left\", \"center\", \"right\"];\n\n// Monotonic counter for unique element ids (deterministic, collision-free even\n// for instances created within the same millisecond).\nlet idCounter = 0;\nfunction nextId(): number {\n\treturn ++idCounter;\n}\n\n/** Resolves a boolean|Range control config against a default range. */\nfunction resolveRange(value: boolean | Range | undefined, fallback: Range): Range | null {\n\tif (!value) return null;\n\tif (value === true) return { ...fallback };\n\treturn { min: value.min, max: value.max, step: value.step ?? fallback.step };\n}\n\n/** A type tester instance bound to a host element. */\nexport class TypeTester {\n\tprivate readonly host: HTMLElement;\n\tprivate readonly options: TypeTesterOptions;\n\tprivate readonly controls: ControlsConfig;\n\tprivate readonly state: TypeTesterState;\n\tprivate readonly activeFeatures = new Set<FeatureTag>();\n\tprivate readonly cleanups: Array<() => void> = [];\n\n\tprivate typeEl!: HTMLElement;\n\tprivate textEl!: HTMLElement;\n\tprivate liveEl!: HTMLElement;\n\tprivate sizeOutput: HTMLOutputElement | null = null;\n\tprivate fitter: Fitter | null = null;\n\n\t/**\n\t * @param host Element to render the tester into.\n\t * @param options Configuration; all fields optional.\n\t */\n\tconstructor(host: HTMLElement, options: TypeTesterOptions = {}) {\n\t\tthis.host = host;\n\t\tthis.options = options;\n\t\tthis.controls = options.controls ?? {};\n\n\t\tconst fit = options.size === \"fit\";\n\t\tfor (const tag of options.features ?? []) {\n\t\t\tif (isKnownFeature(tag)) this.activeFeatures.add(tag);\n\t\t}\n\t\tthis.state = {\n\t\t\ttext: options.text ?? \"\",\n\t\t\tsize: fit ? 0 : toNumber(options.size, 80),\n\t\t\tfit,\n\t\t\ttracking: toNumber(options.tracking, 0),\n\t\t\tweight: toNumber(options.weight, 400),\n\t\t\titalic: options.italic ?? false,\n\t\t\talign: options.align ?? \"left\",\n\t\t\twrap: options.wrap ?? true,\n\t\t\tfeatures: Array.from(this.activeFeatures),\n\t\t};\n\n\t\tthis.render();\n\t}\n\n\t/** Returns a snapshot of the current state. */\n\tgetState(): TypeTesterState {\n\t\treturn { ...this.state, features: Array.from(this.activeFeatures) };\n\t}\n\n\t/** Removes all DOM, listeners and observers created by this instance. */\n\tdestroy(): void {\n\t\tthis.fitter?.destroy();\n\t\tthis.fitter = null;\n\t\tfor (const off of this.cleanups.splice(0)) off();\n\t\tthis.host.replaceChildren();\n\t}\n\n\t// ---- rendering -------------------------------------------------------\n\n\tprivate render(): void {\n\t\tthis.host.classList.add(\"tt\");\n\n\t\tthis.textEl = el(\"div\", {\n\t\t\tclass: \"tt__text\",\n\t\t\ttext: this.state.text,\n\t\t\tattrs: {\n\t\t\t\trole: \"textbox\",\n\t\t\t\t\"aria-label\": this.options.ariaLabel ?? \"Sample text\",\n\t\t\t\t\"aria-multiline\": String(this.state.wrap),\n\t\t\t\tcontenteditable: this.options.editable !== false ? \"true\" : null,\n\t\t\t\tspellcheck: \"true\",\n\t\t\t\t\"data-placeholder\": this.options.placeholder ?? \"Type to test…\",\n\t\t\t},\n\t\t});\n\t\tthis.typeEl = el(\"div\", { class: \"tt__type\", children: [this.textEl] });\n\t\tconst stage = el(\"div\", { class: \"tt__stage\", children: [this.typeEl] });\n\n\t\tthis.liveEl = el(\"div\", {\n\t\t\tclass: \"tt__sr-only\",\n\t\t\tattrs: { \"aria-live\": \"polite\", \"aria-atomic\": \"true\" },\n\t\t});\n\n\t\tconst controls = this.buildControls();\n\t\tconst children: Node[] = [stage];\n\t\tif (controls) children.push(controls);\n\t\tchildren.push(this.liveEl);\n\t\tthis.host.replaceChildren(...children);\n\n\t\tif (this.options.editable !== false) this.wireEditable();\n\t\tthis.applyStyles();\n\t\tthis.setupFit();\n\t}\n\n\tprivate buildControls(): HTMLElement | null {\n\t\tconst items: Node[] = [];\n\t\tconst sizeRange = resolveRange(this.controls.size, DEFAULT_RANGES.size);\n\t\tif (sizeRange && !this.state.fit) items.push(this.buildSize(sizeRange));\n\t\tconst trackingRange = resolveRange(this.controls.tracking, DEFAULT_RANGES.tracking);\n\t\tif (trackingRange) items.push(this.buildTracking(trackingRange));\n\t\tconst weightRange = resolveRange(\n\t\t\tthis.controls.weight,\n\t\t\tthis.options.variable?.wght ?? DEFAULT_RANGES.weight,\n\t\t);\n\t\tif (weightRange) items.push(this.buildWeight(weightRange));\n\t\tif (this.controls.italic) items.push(this.buildItalic());\n\t\tif (this.controls.align) items.push(this.buildAlign());\n\t\tif (this.controls.wrap) items.push(this.buildWrap());\n\t\tif (this.controls.features) items.push(this.buildFeatures());\n\n\t\tif (items.length === 0) return null;\n\t\treturn el(\"div\", {\n\t\t\tclass: \"tt__controls\",\n\t\t\tattrs: { role: \"group\", \"aria-label\": \"Typography controls\" },\n\t\t\tchildren: items,\n\t\t});\n\t}\n\n\t// ---- generic control builders (no per-control copy-paste) -------------\n\n\tprivate buildSlider(\n\t\tkey: \"size\" | \"tracking\" | \"weight\",\n\t\tlabel: string,\n\t\trange: Range,\n\t\tvalue: number,\n\t\tunit: string,\n\t\tonInput: (value: number) => void,\n\t): HTMLElement {\n\t\tconst output = el(\"output\", { class: \"tt__value\", text: `${value}${unit}` });\n\t\tconst input = el(\"input\", {\n\t\t\tclass: `tt__slider tt__slider--${key}`,\n\t\t\tattrs: {\n\t\t\t\ttype: \"range\",\n\t\t\t\tmin: range.min,\n\t\t\t\tmax: range.max,\n\t\t\t\tstep: range.step ?? 1,\n\t\t\t\tvalue,\n\t\t\t\t\"aria-label\": label,\n\t\t\t},\n\t\t});\n\t\tconst handler = () => {\n\t\t\tconst v = Number(input.value);\n\t\t\toutput.textContent = `${v}${unit}`;\n\t\t\tonInput(v);\n\t\t\tthis.announce(`${label} ${v}${unit}`);\n\t\t};\n\t\tinput.addEventListener(\"input\", handler);\n\t\tthis.cleanups.push(() => input.removeEventListener(\"input\", handler));\n\t\tif (key === \"size\") this.sizeOutput = output;\n\t\treturn el(\"label\", {\n\t\t\tclass: `tt__control tt__control--${key}`,\n\t\t\tchildren: [el(\"span\", { class: \"tt__label\", text: label }), input, output],\n\t\t});\n\t}\n\n\tprivate buildSize(range: Range): HTMLElement {\n\t\tthis.state.size = clamp(this.state.size, range.min, range.max);\n\t\treturn this.buildSlider(\"size\", \"Size\", range, this.state.size, \"px\", (v) => {\n\t\t\tthis.state.size = v;\n\t\t\tthis.applyStyles();\n\t\t\tthis.emitChange();\n\t\t});\n\t}\n\n\tprivate buildTracking(range: Range): HTMLElement {\n\t\tthis.state.tracking = clamp(this.state.tracking, range.min, range.max);\n\t\treturn this.buildSlider(\n\t\t\t\"tracking\",\n\t\t\t\"Tracking\",\n\t\t\trange,\n\t\t\tthis.state.tracking,\n\t\t\t\"em\",\n\t\t\t(v) => {\n\t\t\t\tthis.state.tracking = v;\n\t\t\t\tthis.applyStyles();\n\t\t\t\tthis.emitChange();\n\t\t\t},\n\t\t);\n\t}\n\n\tprivate buildWeight(range: Range): HTMLElement {\n\t\tthis.state.weight = clamp(this.state.weight, range.min, range.max);\n\t\treturn this.buildSlider(\"weight\", \"Weight\", range, this.state.weight, \"\", (v) => {\n\t\t\tthis.state.weight = v;\n\t\t\tthis.applyStyles();\n\t\t\tthis.emitChange();\n\t\t});\n\t}\n\n\tprivate buildToggle(\n\t\tkey: string,\n\t\tlabel: string,\n\t\tpressed: boolean,\n\t\tonToggle: (pressed: boolean) => void,\n\t): HTMLButtonElement {\n\t\tconst button = el(\"button\", {\n\t\t\tclass: `tt__toggle tt__toggle--${key}`,\n\t\t\ttext: label,\n\t\t\t// aria-pressed is a string-valued ARIA state and must always be present\n\t\t\t// (\"false\"), unlike boolean HTML attributes which are omitted when off.\n\t\t\tattrs: { type: \"button\", \"aria-pressed\": String(pressed) },\n\t\t});\n\t\tconst handler = () => {\n\t\t\tconst next = button.getAttribute(\"aria-pressed\") !== \"true\";\n\t\t\tbutton.setAttribute(\"aria-pressed\", String(next));\n\t\t\tonToggle(next);\n\t\t\tthis.announce(`${label} ${next ? \"on\" : \"off\"}`);\n\t\t};\n\t\tbutton.addEventListener(\"click\", handler);\n\t\tthis.cleanups.push(() => button.removeEventListener(\"click\", handler));\n\t\treturn button;\n\t}\n\n\tprivate buildItalic(): HTMLElement {\n\t\tconst button = this.buildToggle(\"italic\", \"Italic\", this.state.italic, (on) => {\n\t\t\tthis.state.italic = on;\n\t\t\tthis.applyStyles();\n\t\t\tthis.emitChange();\n\t\t});\n\t\treturn el(\"div\", { class: \"tt__control\", children: [button] });\n\t}\n\n\tprivate buildWrap(): HTMLElement {\n\t\tconst button = this.buildToggle(\"wrap\", \"Wrap\", this.state.wrap, (on) => {\n\t\t\tthis.state.wrap = on;\n\t\t\tthis.textEl.setAttribute(\"aria-multiline\", String(on));\n\t\t\tthis.applyStyles();\n\t\t\tthis.emitChange();\n\t\t});\n\t\treturn el(\"div\", { class: \"tt__control\", children: [button] });\n\t}\n\n\tprivate buildAlign(): HTMLElement {\n\t\tconst select = el(\"select\", {\n\t\t\tclass: \"tt__select tt__select--align\",\n\t\t\tattrs: { \"aria-label\": \"Alignment\" },\n\t\t\tchildren: ALIGNS.map((a) =>\n\t\t\t\tel(\"option\", {\n\t\t\t\t\ttext: a.charAt(0).toUpperCase() + a.slice(1),\n\t\t\t\t\tattrs: { value: a, selected: a === this.state.align },\n\t\t\t\t}),\n\t\t\t),\n\t\t});\n\t\tconst handler = () => {\n\t\t\tconst value = select.value as Align;\n\t\t\tif (ALIGNS.includes(value)) {\n\t\t\t\tthis.state.align = value;\n\t\t\t\tthis.applyStyles();\n\t\t\t\tthis.emitChange();\n\t\t\t\tthis.announce(`Alignment ${value}`);\n\t\t\t}\n\t\t};\n\t\tselect.addEventListener(\"change\", handler);\n\t\tthis.cleanups.push(() => select.removeEventListener(\"change\", handler));\n\t\treturn el(\"label\", {\n\t\t\tclass: \"tt__control tt__control--align\",\n\t\t\tchildren: [el(\"span\", { class: \"tt__label\", text: \"Align\" }), select],\n\t\t});\n\t}\n\n\tprivate buildFeatures(): HTMLElement {\n\t\tconst offered: readonly FeatureDef[] = Array.isArray(this.controls.features)\n\t\t\t? this.controls.features\n\t\t\t\t\t.filter(isKnownFeature)\n\t\t\t\t\t// Preserve each feature's real group/label from the registry rather\n\t\t\t\t\t// than flattening everything into \"Alternates\".\n\t\t\t\t\t.map((tag) => FEATURE_BY_TAG.get(tag) ?? { tag, label: featureLabel(tag), group: \"Alternates\" })\n\t\t\t: FEATURES;\n\n\t\tconst panelId = `tt-feat-${nextId()}`;\n\t\tconst toggle = el(\"button\", {\n\t\t\tclass: \"tt__toggle tt__toggle--features\",\n\t\t\ttext: \"Features\",\n\t\t\tattrs: {\n\t\t\t\ttype: \"button\",\n\t\t\t\t\"aria-haspopup\": \"true\",\n\t\t\t\t\"aria-expanded\": \"false\",\n\t\t\t\t\"aria-controls\": panelId,\n\t\t\t},\n\t\t});\n\n\t\tconst checks = offered.map((f) => {\n\t\t\tconst input = el(\"input\", {\n\t\t\t\tattrs: { type: \"checkbox\", value: f.tag, checked: this.activeFeatures.has(f.tag) },\n\t\t\t});\n\t\t\tconst onChange = () => this.toggleFeature(f.tag, input.checked);\n\t\t\tinput.addEventListener(\"change\", onChange);\n\t\t\tthis.cleanups.push(() => input.removeEventListener(\"change\", onChange));\n\t\t\treturn el(\"label\", {\n\t\t\t\tclass: \"tt__feature\",\n\t\t\t\tchildren: [input, el(\"span\", { text: f.label })],\n\t\t\t});\n\t\t});\n\n\t\tconst panel = el(\"div\", {\n\t\t\tclass: \"tt__panel\",\n\t\t\tattrs: { id: panelId, role: \"group\", \"aria-label\": \"OpenType features\", hidden: true },\n\t\t\tchildren: checks,\n\t\t});\n\n\t\tconst wrapper = el(\"div\", {\n\t\t\tclass: \"tt__control tt__control--features\",\n\t\t\tchildren: [toggle, panel],\n\t\t});\n\n\t\tconst setOpen = (open: boolean) => {\n\t\t\ttoggle.setAttribute(\"aria-expanded\", String(open));\n\t\t\tpanel.toggleAttribute(\"hidden\", !open);\n\t\t\tif (open) {\n\t\t\t\tconst first = panel.querySelector<HTMLInputElement>(\"input\");\n\t\t\t\tfirst?.focus();\n\t\t\t}\n\t\t};\n\t\tconst onToggleClick = () => setOpen(toggle.getAttribute(\"aria-expanded\") !== \"true\");\n\t\tconst onKeydown = (e: KeyboardEvent) => {\n\t\t\tif (e.key === \"Escape\" && toggle.getAttribute(\"aria-expanded\") === \"true\") {\n\t\t\t\tsetOpen(false);\n\t\t\t\ttoggle.focus();\n\t\t\t}\n\t\t};\n\t\tconst onOutside = (e: MouseEvent) => {\n\t\t\tif (toggle.getAttribute(\"aria-expanded\") !== \"true\") return;\n\t\t\tif (!wrapper.contains(e.target as Node)) setOpen(false);\n\t\t};\n\t\ttoggle.addEventListener(\"click\", onToggleClick);\n\t\t// Escape is handled at the document level so it closes the panel no matter\n\t\t// where focus currently is, then restores focus to the toggle.\n\t\tdocument.addEventListener(\"keydown\", onKeydown);\n\t\tdocument.addEventListener(\"click\", onOutside);\n\t\tthis.cleanups.push(() => {\n\t\t\ttoggle.removeEventListener(\"click\", onToggleClick);\n\t\t\tdocument.removeEventListener(\"keydown\", onKeydown);\n\t\t\tdocument.removeEventListener(\"click\", onOutside);\n\t\t});\n\n\t\treturn wrapper;\n\t}\n\n\tprivate toggleFeature(tag: FeatureTag, on: boolean): void {\n\t\tif (on) this.activeFeatures.add(tag);\n\t\telse this.activeFeatures.delete(tag);\n\t\tthis.state.features = Array.from(this.activeFeatures);\n\t\tthis.applyStyles();\n\t\tthis.emitChange();\n\t\tthis.announce(`${featureLabel(tag)} ${on ? \"on\" : \"off\"}`);\n\t}\n\n\t// ---- behaviour -------------------------------------------------------\n\n\tprivate wireEditable(): void {\n\t\tconst handler = () => {\n\t\t\tthis.state.text = this.textEl.textContent ?? \"\";\n\t\t\tif (this.state.fit) this.fitter?.schedule();\n\t\t\tthis.emitChange();\n\t\t};\n\t\tthis.textEl.addEventListener(\"input\", handler);\n\t\tthis.cleanups.push(() => this.textEl.removeEventListener(\"input\", handler));\n\t}\n\n\tprivate setupFit(): void {\n\t\tif (!this.state.fit) return;\n\t\tconst range = resolveRange(this.controls.size, DEFAULT_RANGES.size) ?? DEFAULT_RANGES.size;\n\t\tthis.fitter = new Fitter(this.textEl, range.min, range.max);\n\t\tthis.fitter.start((size) => {\n\t\t\tthis.state.size = size;\n\t\t\tthis.typeEl.style.fontSize = `${size}px`;\n\t\t\tif (this.sizeOutput) this.sizeOutput.textContent = `${size}px`;\n\t\t});\n\t}\n\n\t/** Applies the full typographic state to the type element via element.style. */\n\tprivate applyStyles(): void {\n\t\tconst s = this.typeEl.style;\n\t\tconst family = this.options.fontFamily\n\t\t\t? `\"${this.options.fontFamily}\"${this.options.fallback ? `, ${this.options.fallback}` : \", sans-serif\"}`\n\t\t\t: (this.options.fallback ?? \"sans-serif\");\n\t\ts.fontFamily = family;\n\t\tif (!this.state.fit) s.fontSize = `${this.state.size}px`;\n\t\ts.letterSpacing = `${this.state.tracking}em`;\n\t\ts.fontStyle = this.state.italic ? \"italic\" : \"normal\";\n\t\ts.textAlign = this.state.align;\n\t\tthis.textEl.style.whiteSpace = this.state.wrap ? \"normal\" : \"nowrap\";\n\n\t\tif (this.options.variable?.wght) {\n\t\t\ts.fontVariationSettings = `\"wght\" ${this.state.weight}`;\n\t\t}\n\t\ts.fontWeight = String(this.state.weight);\n\n\t\tconst settings = featureSettings(this.activeFeatures);\n\t\ts.fontFeatureSettings = settings;\n\t\t// Older Safari needs the prefixed property; modern engines ignore it.\n\t\ts.setProperty(\"-webkit-font-feature-settings\", settings);\n\t}\n\n\tprivate announce(message: string): void {\n\t\tthis.liveEl.textContent = message;\n\t}\n\n\tprivate emitChange(): void {\n\t\tthis.options.onChange?.(this.getState());\n\t}\n}\n"]}