unit.gl 0.2.3 → 0.3.1

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 (41) hide show
  1. package/README.md +329 -1
  2. package/css/unit.gl.css +35819 -6
  3. package/css/unit.gl.docs.css +4156 -0
  4. package/css/unit.gl.docs.min.css +2 -0
  5. package/css/unit.gl.min.css +1 -1
  6. package/js/unit.gl.demo.js +708 -0
  7. package/js/unit.gl.demo.js.map +1 -0
  8. package/js/unit.gl.js +25 -0
  9. package/js/unit.gl.js.map +1 -1
  10. package/package.json +16 -3
  11. package/scss/classes/_docs.scss +4690 -0
  12. package/scss/classes/_index.scss +1 -0
  13. package/scss/classes/_utilities.scss +1488 -0
  14. package/scss/docs.scss +11 -0
  15. package/scss/formats.scss +27 -0
  16. package/scss/functions/_density.scss +311 -0
  17. package/scss/functions/_index.scss +3 -0
  18. package/scss/functions/_scale.scss +211 -1
  19. package/scss/guide.scss +22 -0
  20. package/scss/maps/_density.scss +141 -0
  21. package/scss/maps/_device.scss +13 -20
  22. package/scss/maps/_index.scss +6 -0
  23. package/scss/maps/_scale.scss +47 -4
  24. package/scss/mixins/_device.scss +1 -3
  25. package/scss/mixins/_display.scss +256 -0
  26. package/scss/mixins/_format.scss +75 -0
  27. package/scss/mixins/_index.scss +2 -1
  28. package/scss/mixins/_unit.scss +115 -6
  29. package/scss/mixins/_utilities.scss +303 -0
  30. package/scss/mixins/_view.scss +41 -11
  31. package/scss/tags/_global.scss +0 -3
  32. package/scss/tags/_unit.scss +1 -3
  33. package/scss/utilities.scss +20 -0
  34. package/scss/variables/_format.scss +80 -0
  35. package/scss/variables/_index.scss +4 -0
  36. package/scss/variables/_scale.scss +434 -63
  37. package/scss/variables/_view.scss +222 -64
  38. package/ts/demo.ts +889 -0
  39. package/ts/index.d.ts +72 -0
  40. package/ts/index.ts +45 -0
  41. package/scss/mixins/_paper.scss +0 -52
@@ -0,0 +1,708 @@
1
+ "use strict";
2
+ function mq(query) {
3
+ try {
4
+ return window.matchMedia(query).matches;
5
+ }
6
+ catch (_) {
7
+ return false;
8
+ }
9
+ }
10
+ function setText(id, value) {
11
+ const el = document.getElementById(id);
12
+ if (el)
13
+ el.textContent = String(value);
14
+ }
15
+ function fmt(n, digits = 2) {
16
+ return (Math.round(n * (10 ** digits)) / (10 ** digits)).toFixed(digits);
17
+ }
18
+ function getViewport() {
19
+ const width = Math.max(document.documentElement.clientWidth || 0, window.innerWidth || 0);
20
+ const height = Math.max(document.documentElement.clientHeight || 0, window.innerHeight || 0);
21
+ return { width, height };
22
+ }
23
+ function initThemeToggle() {
24
+ const themeToggle = document.querySelector('[data-toggle="theme"]');
25
+ const html = document.documentElement;
26
+ const savedTheme = localStorage.getItem('theme');
27
+ const systemPrefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
28
+ if (savedTheme) {
29
+ html.setAttribute('data-theme', savedTheme);
30
+ }
31
+ else if (systemPrefersDark) {
32
+ html.setAttribute('data-theme', 'dark');
33
+ }
34
+ themeToggle?.addEventListener('click', function () {
35
+ const currentTheme = html.getAttribute('data-theme');
36
+ const newTheme = currentTheme === 'dark' ? 'light' : 'dark';
37
+ html.setAttribute('data-theme', newTheme);
38
+ localStorage.setItem('theme', newTheme);
39
+ });
40
+ }
41
+ function initGridToggle() {
42
+ document.querySelectorAll('.grid-controls button').forEach(btn => {
43
+ btn.addEventListener('click', function () {
44
+ const gridType = this.dataset.toggle;
45
+ const grid = document.querySelector(`[data-grid="${gridType}"]`);
46
+ if (grid) {
47
+ grid.classList.toggle('active');
48
+ this.classList.toggle('active');
49
+ }
50
+ });
51
+ });
52
+ }
53
+ function initMobileNav() {
54
+ const toggle = document.getElementById('nav-mobile-toggle');
55
+ const menu = document.getElementById('nav-menu');
56
+ const navWrapper = document.querySelector('.nav-wrapper');
57
+ if (!toggle || !menu)
58
+ return;
59
+ toggle.addEventListener('click', function () {
60
+ const isOpen = menu.classList.toggle('is-open');
61
+ navWrapper?.classList.toggle('nav-open', isOpen);
62
+ this.setAttribute('aria-expanded', String(isOpen));
63
+ document.body.classList.toggle('nav-open', isOpen);
64
+ });
65
+ menu.querySelectorAll('a').forEach(link => {
66
+ link.addEventListener('click', () => {
67
+ menu.classList.remove('is-open');
68
+ navWrapper?.classList.remove('nav-open');
69
+ toggle.setAttribute('aria-expanded', 'false');
70
+ document.body.classList.remove('nav-open');
71
+ });
72
+ });
73
+ document.addEventListener('keydown', function (e) {
74
+ if (e.key === 'Escape' && menu.classList.contains('is-open')) {
75
+ menu.classList.remove('is-open');
76
+ navWrapper?.classList.remove('nav-open');
77
+ toggle.setAttribute('aria-expanded', 'false');
78
+ document.body.classList.remove('nav-open');
79
+ toggle.focus();
80
+ }
81
+ });
82
+ const mediaQuery = window.matchMedia('(min-width: 768px)');
83
+ mediaQuery.addEventListener('change', (e) => {
84
+ if (e.matches && menu.classList.contains('is-open')) {
85
+ menu.classList.remove('is-open');
86
+ navWrapper?.classList.remove('nav-open');
87
+ toggle.setAttribute('aria-expanded', 'false');
88
+ document.body.classList.remove('nav-open');
89
+ }
90
+ });
91
+ }
92
+ function initDropdown() {
93
+ const dropdown = document.getElementById('nav-dropdown');
94
+ const toggle = dropdown?.querySelector('.nav__dropdown-toggle');
95
+ toggle?.addEventListener('click', function (e) {
96
+ e.stopPropagation();
97
+ const isOpen = dropdown.classList.toggle('open');
98
+ this.setAttribute('aria-expanded', String(isOpen));
99
+ });
100
+ document.addEventListener('click', function (e) {
101
+ if (dropdown && !dropdown.contains(e.target)) {
102
+ dropdown.classList.remove('open');
103
+ toggle?.setAttribute('aria-expanded', 'false');
104
+ }
105
+ });
106
+ document.addEventListener('keydown', function (e) {
107
+ if (e.key === 'Escape' && dropdown?.classList.contains('open')) {
108
+ dropdown.classList.remove('open');
109
+ toggle?.setAttribute('aria-expanded', 'false');
110
+ toggle?.focus();
111
+ }
112
+ });
113
+ }
114
+ function initLayersDemo() {
115
+ window.toggleLayer = function (id) {
116
+ const layer = document.getElementById('layer-' + id);
117
+ const btn = document.getElementById('btn-' + id);
118
+ layer?.classList.toggle('layer-box--hidden');
119
+ btn?.classList.toggle('layer-btn--active');
120
+ };
121
+ window.showAll = function () {
122
+ document.querySelectorAll('.layer-box').forEach(el => {
123
+ el.classList.remove('layer-box--hidden');
124
+ });
125
+ document.querySelectorAll('.layer-btn').forEach(el => {
126
+ if (el.id.startsWith('btn-')) {
127
+ el.classList.add('layer-btn--active');
128
+ }
129
+ });
130
+ };
131
+ window.hideAll = function () {
132
+ document.querySelectorAll('.layer-box').forEach(el => {
133
+ el.classList.add('layer-box--hidden');
134
+ });
135
+ document.querySelectorAll('.layer-btn').forEach(el => {
136
+ if (el.id.startsWith('btn-')) {
137
+ el.classList.remove('layer-btn--active');
138
+ }
139
+ });
140
+ };
141
+ }
142
+ function initDeviceDemo() {
143
+ if (!document.getElementById('dev-width'))
144
+ return;
145
+ function classifyOrientation(width, height) {
146
+ if (mq('(orientation: portrait)'))
147
+ return 'portrait';
148
+ if (mq('(orientation: landscape)'))
149
+ return 'landscape';
150
+ return width >= height ? 'landscape' : 'portrait';
151
+ }
152
+ function getColorScheme() {
153
+ if (mq('(prefers-color-scheme: dark)'))
154
+ return 'dark';
155
+ if (mq('(prefers-color-scheme: light)'))
156
+ return 'light';
157
+ return 'no-preference';
158
+ }
159
+ function getReducedMotion() {
160
+ if (mq('(prefers-reduced-motion: reduce)'))
161
+ return 'reduce';
162
+ if (mq('(prefers-reduced-motion: no-preference)'))
163
+ return 'no-preference';
164
+ return 'unknown';
165
+ }
166
+ function getPrefersContrast() {
167
+ if (mq('(prefers-contrast: more)'))
168
+ return 'more';
169
+ if (mq('(prefers-contrast: less)'))
170
+ return 'less';
171
+ if (mq('(prefers-contrast: custom)'))
172
+ return 'custom';
173
+ if (mq('(prefers-contrast: no-preference)'))
174
+ return 'no-preference';
175
+ return 'unknown';
176
+ }
177
+ function getForcedColors() {
178
+ if (mq('(forced-colors: active)'))
179
+ return 'active';
180
+ if (mq('(forced-colors: none)'))
181
+ return 'none';
182
+ return 'unknown';
183
+ }
184
+ function getHover() {
185
+ if (mq('(hover: hover)'))
186
+ return 'hover';
187
+ if (mq('(hover: none)'))
188
+ return 'none';
189
+ return 'unknown';
190
+ }
191
+ function getPointer() {
192
+ if (mq('(pointer: fine)'))
193
+ return 'fine';
194
+ if (mq('(pointer: coarse)'))
195
+ return 'coarse';
196
+ if (mq('(pointer: none)'))
197
+ return 'none';
198
+ return 'unknown';
199
+ }
200
+ function getAnyHover() {
201
+ if (mq('(any-hover: hover)'))
202
+ return 'hover';
203
+ if (mq('(any-hover: none)'))
204
+ return 'none';
205
+ return 'unknown';
206
+ }
207
+ function getAnyPointer() {
208
+ if (mq('(any-pointer: fine)'))
209
+ return 'fine';
210
+ if (mq('(any-pointer: coarse)'))
211
+ return 'coarse';
212
+ if (mq('(any-pointer: none)'))
213
+ return 'none';
214
+ return 'unknown';
215
+ }
216
+ function getColorGamut() {
217
+ if (mq('(color-gamut: rec2020)'))
218
+ return 'rec2020';
219
+ if (mq('(color-gamut: p3)'))
220
+ return 'p3';
221
+ if (mq('(color-gamut: srgb)'))
222
+ return 'srgb';
223
+ return 'unknown';
224
+ }
225
+ function getDisplayMode() {
226
+ if (mq('(display-mode: fullscreen)'))
227
+ return 'fullscreen';
228
+ if (mq('(display-mode: standalone)'))
229
+ return 'standalone';
230
+ if (mq('(display-mode: minimal-ui)'))
231
+ return 'minimal-ui';
232
+ if (mq('(display-mode: browser)'))
233
+ return 'browser';
234
+ return 'unknown';
235
+ }
236
+ function getReducedTransparency() {
237
+ if (mq('(prefers-reduced-transparency: reduce)'))
238
+ return 'reduce';
239
+ if (mq('(prefers-reduced-transparency: no-preference)'))
240
+ return 'no-preference';
241
+ return 'unknown';
242
+ }
243
+ function getReducedData() {
244
+ if (mq('(prefers-reduced-data: reduce)'))
245
+ return 'reduce';
246
+ if (mq('(prefers-reduced-data: no-preference)'))
247
+ return 'no-preference';
248
+ return 'unknown';
249
+ }
250
+ function scoreDeviceMatch({ width, dpr }, row) {
251
+ const min = Number(row.dataset.min || '0');
252
+ const max = Number(row.dataset.max || '0');
253
+ const targetDpr = Number(row.dataset.dpr || '1');
254
+ const inRange = width >= min && width <= max;
255
+ const dprDelta = Math.abs((dpr || 1) - targetDpr);
256
+ const dprOk = dprDelta <= 0.6;
257
+ if (!inRange)
258
+ return { match: false, label: '—' };
259
+ if (!dprOk)
260
+ return { match: true, label: 'width' };
261
+ return { match: true, label: 'width + dpr' };
262
+ }
263
+ function updateUI() {
264
+ const { width, height } = getViewport();
265
+ const dpr = Number(window.devicePixelRatio || 1);
266
+ setText('dev-width', width);
267
+ setText('dev-height', height);
268
+ setText('dev-dpr', dpr.toFixed(2));
269
+ setText('dev-orientation', classifyOrientation(width, height));
270
+ setText('dev-touch', (navigator.maxTouchPoints || 0));
271
+ setText('mf-hover', getHover());
272
+ setText('mf-pointer', getPointer());
273
+ setText('mf-any-hover', getAnyHover());
274
+ setText('mf-any-pointer', getAnyPointer());
275
+ setText('mf-scheme', getColorScheme());
276
+ setText('mf-motion', getReducedMotion());
277
+ setText('mf-contrast', getPrefersContrast());
278
+ setText('mf-forced', getForcedColors());
279
+ setText('mf-gamut', getColorGamut());
280
+ setText('mf-display', getDisplayMode());
281
+ setText('mf-transparency', getReducedTransparency());
282
+ setText('mf-data', getReducedData());
283
+ const filterEl = document.getElementById('dev-filter');
284
+ const filterValue = String(filterEl?.value || '').trim().toLowerCase();
285
+ let matches = 0;
286
+ document.querySelectorAll('.device-row').forEach(row => {
287
+ const key = String(row.dataset.key || '').toLowerCase();
288
+ const visible = !filterValue || key.includes(filterValue);
289
+ row.classList.toggle('is-filtered', !visible);
290
+ const badge = row.querySelector('[data-role="match"]');
291
+ if (!visible) {
292
+ row.classList.remove('is-match');
293
+ if (badge)
294
+ badge.textContent = '—';
295
+ return;
296
+ }
297
+ const result = scoreDeviceMatch({ width, dpr }, row);
298
+ row.classList.toggle('is-match', result.match);
299
+ if (badge)
300
+ badge.textContent = result.label;
301
+ if (result.match)
302
+ matches += 1;
303
+ });
304
+ setText('dev-matches', matches);
305
+ }
306
+ window.addEventListener('resize', updateUI);
307
+ window.addEventListener('orientationchange', updateUI);
308
+ const filterEl = document.getElementById('dev-filter');
309
+ if (filterEl)
310
+ filterEl.addEventListener('input', updateUI);
311
+ updateUI();
312
+ }
313
+ function initQScaleDemo() {
314
+ if (!document.getElementById('qs-root'))
315
+ return;
316
+ const qSteps = [0, 1, 2, 4, 6, 8, 12, 16, 20, 24, 32, 40, 48, 56, 64, 72, 80, 96, 112, 128, 144, 160, 192, 224, 256];
317
+ const slider = document.getElementById('qs-range');
318
+ function rootPx() {
319
+ const raw = getComputedStyle(document.documentElement).fontSize;
320
+ const value = Number.parseFloat(raw);
321
+ return Number.isFinite(value) ? value : 16;
322
+ }
323
+ function toPx(step) {
324
+ return (step * rootPx()) / 16;
325
+ }
326
+ function toRem(step) {
327
+ return step / 16;
328
+ }
329
+ function setStep(step) {
330
+ const stepNum = Number(step);
331
+ const px = toPx(stepNum);
332
+ const rem = toRem(stepNum);
333
+ setText('qs-root', fmt(rootPx(), 2));
334
+ setText('qs-step', stepNum);
335
+ setText('qs-px', fmt(px, 2));
336
+ setText('qs-rem', fmt(rem, 4));
337
+ const className = `.p_q${stepNum}`;
338
+ const classEl = document.getElementById('qs-class');
339
+ if (classEl)
340
+ classEl.textContent = className;
341
+ const size = Math.max(8, px);
342
+ const pad = Math.max(2, px / 4);
343
+ const gap = Math.max(2, px / 6);
344
+ const box = document.getElementById('qs-box');
345
+ if (box) {
346
+ box.style.width = `${size}px`;
347
+ box.style.height = `${size}px`;
348
+ box.style.padding = `${pad}px`;
349
+ box.style.gap = `${gap}px`;
350
+ }
351
+ setText('qs-w', `${fmt(size, 0)}px`);
352
+ setText('qs-h', `${fmt(size, 0)}px`);
353
+ setText('qs-pad', `${fmt(pad, 0)}px`);
354
+ setText('qs-gap', `${fmt(gap, 0)}px`);
355
+ setText('qs-u-step', stepNum);
356
+ setText('qs-u-step2', stepNum);
357
+ setText('qs-u-step3', stepNum);
358
+ setText('qs-u-step4', stepNum);
359
+ setText('qs-u-step5', stepNum);
360
+ if (slider) {
361
+ const sliderIndex = qSteps.indexOf(stepNum);
362
+ if (sliderIndex >= 0) {
363
+ slider.value = String(sliderIndex);
364
+ }
365
+ }
366
+ document.querySelectorAll('.qscale-chip').forEach(btn => {
367
+ btn.classList.toggle('active', Number(btn.dataset.step) === stepNum);
368
+ });
369
+ }
370
+ if (slider) {
371
+ slider.addEventListener('input', (e) => {
372
+ const index = parseInt(e.target.value);
373
+ if (index >= 0 && index < qSteps.length) {
374
+ setStep(qSteps[index]);
375
+ }
376
+ });
377
+ }
378
+ document.querySelectorAll('.qscale-chip').forEach(chip => {
379
+ chip.addEventListener('click', () => {
380
+ const step = parseInt(chip.dataset.step || '16');
381
+ setStep(step);
382
+ });
383
+ });
384
+ const chipRow = document.getElementById('qscale-chip-row');
385
+ if (chipRow) {
386
+ qSteps.forEach(step => {
387
+ const btn = document.createElement('button');
388
+ btn.className = 'qscale-chip' + (step === 16 ? ' active' : '');
389
+ btn.textContent = String(step);
390
+ btn.dataset.step = String(step);
391
+ btn.addEventListener('click', () => setStep(step));
392
+ chipRow.appendChild(btn);
393
+ });
394
+ }
395
+ function createQScaleGrid(canvasId, scale, className) {
396
+ const canvas = document.getElementById(canvasId);
397
+ if (!canvas)
398
+ return;
399
+ canvas.innerHTML = '';
400
+ const width = canvas.offsetWidth;
401
+ for (let x = scale; x < width; x += scale) {
402
+ const line = document.createElement('div');
403
+ line.className = 'grid-line ' + className;
404
+ if (x % 20 === 0) {
405
+ line.classList.add('lcm');
406
+ }
407
+ line.style.left = x + 'px';
408
+ canvas.appendChild(line);
409
+ }
410
+ }
411
+ createQScaleGrid('qs-type-grid', 4, 'type');
412
+ createQScaleGrid('qs-line-grid', 5, 'line-scale');
413
+ const combinedCanvas = document.getElementById('qs-combined-grid');
414
+ if (combinedCanvas) {
415
+ combinedCanvas.innerHTML = '';
416
+ const width = combinedCanvas.offsetWidth;
417
+ for (let x = 20; x < width; x += 20) {
418
+ const line = document.createElement('div');
419
+ line.className = 'grid-line lcm';
420
+ line.style.left = x + 'px';
421
+ combinedCanvas.appendChild(line);
422
+ }
423
+ }
424
+ setStep(16);
425
+ }
426
+ function initDensityDemo() {
427
+ if (!document.getElementById('current-dpr'))
428
+ return;
429
+ function updateDeviceInfo() {
430
+ const dpr = window.devicePixelRatio || 1;
431
+ const dpi = Math.round(dpr * 96);
432
+ setText('current-dpr', dpr.toFixed(2) + '×');
433
+ setText('current-dpi', dpi + 'dpi');
434
+ let bucket = 'mdpi';
435
+ if (dpr >= 4)
436
+ bucket = 'xxxhdpi';
437
+ else if (dpr >= 3)
438
+ bucket = 'xxhdpi';
439
+ else if (dpr >= 2)
440
+ bucket = 'xhdpi';
441
+ else if (dpr >= 1.5)
442
+ bucket = 'hdpi';
443
+ else if (dpr < 1)
444
+ bucket = 'ldpi';
445
+ setText('current-bucket', bucket);
446
+ }
447
+ function updateCalculator() {
448
+ const input = document.getElementById('calc-q');
449
+ const q = parseFloat(input?.value || '0') || 0;
450
+ const mm = q * 0.25;
451
+ const inches = mm / 25.4;
452
+ const pt = mm / 0.3528;
453
+ const rem = q * 0.0625;
454
+ setText('calc-mm', mm.toFixed(2) + 'mm');
455
+ setText('calc-in', inches.toFixed(3) + 'in');
456
+ setText('calc-pt', pt.toFixed(2) + 'pt');
457
+ setText('calc-rem', rem.toFixed(4) + 'rem');
458
+ setText('calc-px1', q + 'px');
459
+ setText('calc-px2', (q * 2) + 'px');
460
+ setText('calc-px3', (q * 3) + 'px');
461
+ }
462
+ const calcInput = document.getElementById('calc-q');
463
+ calcInput?.addEventListener('input', updateCalculator);
464
+ updateDeviceInfo();
465
+ updateCalculator();
466
+ if (window.matchMedia) {
467
+ const checkDPR = () => {
468
+ const mqQuery = window.matchMedia(`(resolution: ${window.devicePixelRatio}dppx)`);
469
+ mqQuery.addEventListener('change', () => {
470
+ updateDeviceInfo();
471
+ checkDPR();
472
+ }, { once: true });
473
+ };
474
+ checkDPR();
475
+ }
476
+ }
477
+ function initBreakpointsDemo() {
478
+ if (!document.getElementById('bp-width'))
479
+ return;
480
+ const breakpoints = [
481
+ { key: 'ul', min: 4320 },
482
+ { key: 'xl', min: 2880 },
483
+ { key: 'lg', min: 2160 },
484
+ { key: 'md', min: 1440 },
485
+ { key: 'sm', min: 720 },
486
+ { key: 'xs', min: 540 },
487
+ { key: 'ss', min: 360 },
488
+ { key: 'us', min: 240 },
489
+ ];
490
+ function updateBreakpointUI() {
491
+ const width = Math.max(document.documentElement.clientWidth || 0, window.innerWidth || 0);
492
+ const active = breakpoints.find(bp => width >= bp.min) || breakpoints[breakpoints.length - 1];
493
+ setText('bp-width', String(width));
494
+ setText('bp-active', active.key);
495
+ setText('bp-rule', `(min-width: ${active.min}px)`);
496
+ document.querySelectorAll('.bp-row').forEach(row => {
497
+ row.classList.toggle('active', row.dataset.bp === active.key);
498
+ });
499
+ }
500
+ window.addEventListener('resize', updateBreakpointUI);
501
+ updateBreakpointUI();
502
+ }
503
+ function initPaperDemo() {
504
+ const select = document.getElementById('paper-format');
505
+ const preview = document.getElementById('paper-preview');
506
+ const container = document.getElementById('paper-preview-container');
507
+ const title = document.getElementById('paper-title');
508
+ const outW = document.getElementById('paper-w');
509
+ const outH = document.getElementById('paper-h');
510
+ const code = document.getElementById('paper-code');
511
+ const codeOr = document.getElementById('paper-code-or');
512
+ const scaleDisplay = document.getElementById('scale-display');
513
+ if (!select || !preview)
514
+ return;
515
+ let orientation = 'portrait';
516
+ const maxRefSize = 1189;
517
+ const containerMaxPx = 400;
518
+ function currentDims() {
519
+ const opt = select?.selectedOptions?.[0];
520
+ if (!opt)
521
+ return { key: 'q04', w: 180, h: 270 };
522
+ const key = opt.value;
523
+ const w = Number(opt.dataset.w);
524
+ const h = Number(opt.dataset.h);
525
+ return { key, w, h };
526
+ }
527
+ function apply() {
528
+ if (!preview || !select || !container)
529
+ return;
530
+ const { key, w, h } = currentDims();
531
+ const pw = orientation === 'landscape' ? h : w;
532
+ const ph = orientation === 'landscape' ? w : h;
533
+ const maxDim = Math.max(pw, ph);
534
+ const scale = containerMaxPx / maxRefSize;
535
+ const scaledW = pw * scale;
536
+ const scaledH = ph * scale;
537
+ preview.style.width = `${scaledW}px`;
538
+ preview.style.height = `${scaledH}px`;
539
+ preview.style.aspectRatio = 'auto';
540
+ const displayScale = (maxRefSize / maxDim).toFixed(1);
541
+ if (scaleDisplay)
542
+ scaleDisplay.textContent = `Scale: 1:${displayScale}`;
543
+ if (title)
544
+ title.textContent = key;
545
+ if (outW)
546
+ outW.textContent = String(pw);
547
+ if (outH)
548
+ outH.textContent = String(ph);
549
+ if (code)
550
+ code.textContent = key;
551
+ if (codeOr)
552
+ codeOr.textContent = orientation;
553
+ }
554
+ document.querySelectorAll('.paper-orient__btn').forEach(btn => {
555
+ btn.addEventListener('click', () => {
556
+ orientation = btn.dataset.orient || 'portrait';
557
+ document.querySelectorAll('.paper-orient__btn').forEach(b => b.classList.toggle('active', b === btn));
558
+ apply();
559
+ });
560
+ });
561
+ select.addEventListener('change', apply);
562
+ apply();
563
+ function renderComparisonStacks() {
564
+ const scaleFactor = 0.5;
565
+ document.querySelectorAll('.comparison-sheet').forEach(sheet => {
566
+ const w = Number(sheet.dataset.w || 0);
567
+ const h = Number(sheet.dataset.h || 0);
568
+ sheet.style.width = `${w * scaleFactor}px`;
569
+ sheet.style.height = `${h * scaleFactor}px`;
570
+ });
571
+ }
572
+ renderComparisonStacks();
573
+ }
574
+ function initBaselineDemo() {
575
+ window.toggleBaseline = function () {
576
+ document.querySelectorAll('.guide--baseline, .guide--baseline_custom').forEach(el => {
577
+ el.classList.toggle('guide--baseline--hidden');
578
+ });
579
+ };
580
+ }
581
+ function initHybridScaleDemo() {
582
+ const slider = document.getElementById('scaleSlider');
583
+ const sliderValue = document.getElementById('sliderValue');
584
+ const chips = document.querySelectorAll('.chip');
585
+ const previewBox = document.getElementById('previewBox');
586
+ if (!slider)
587
+ return;
588
+ const typeQ = document.getElementById('typeQ');
589
+ const typePx = document.getElementById('typePx');
590
+ const typeMm = document.getElementById('typeMm');
591
+ const typeRem = document.getElementById('typeRem');
592
+ const lineQ = document.getElementById('lineQ');
593
+ const linePx = document.getElementById('linePx');
594
+ const lineMm = document.getElementById('lineMm');
595
+ const lineRem = document.getElementById('lineRem');
596
+ const lcmValue = document.getElementById('lcmValue');
597
+ const lcmMultiple = document.getElementById('lcmMultiple');
598
+ const lcmAligned = document.getElementById('lcmAligned');
599
+ function updateScale(value) {
600
+ const typeVal = value * 4;
601
+ const lineVal = value * 5;
602
+ const lcmVal = Math.ceil(Math.max(typeVal, lineVal) / 20) * 20;
603
+ const isAligned = typeVal % 20 === 0 && lineVal % 20 === 0;
604
+ slider.value = String(value);
605
+ if (sliderValue)
606
+ sliderValue.textContent = String(value);
607
+ chips.forEach(chip => {
608
+ chip.classList.toggle('active', parseInt(chip.dataset.value || '0') === value);
609
+ });
610
+ if (typeQ)
611
+ typeQ.textContent = typeVal + 'Q';
612
+ if (typePx)
613
+ typePx.textContent = typeVal + 'px';
614
+ if (typeMm)
615
+ typeMm.textContent = (typeVal / 4).toFixed(1) + 'mm';
616
+ if (typeRem)
617
+ typeRem.textContent = (typeVal / 16).toFixed(3) + 'rem';
618
+ if (lineQ)
619
+ lineQ.textContent = lineVal + 'Q';
620
+ if (linePx)
621
+ linePx.textContent = lineVal + 'px';
622
+ if (lineMm)
623
+ lineMm.textContent = (lineVal / 4).toFixed(2) + 'mm';
624
+ if (lineRem)
625
+ lineRem.textContent = (lineVal / 16).toFixed(3) + 'rem';
626
+ if (lcmValue)
627
+ lcmValue.textContent = lcmVal + 'Q';
628
+ if (lcmMultiple)
629
+ lcmMultiple.textContent = (lcmVal / 20) + '× LCM';
630
+ if (lcmAligned) {
631
+ if (typeVal === lineVal && typeVal % 20 === 0) {
632
+ lcmAligned.textContent = '✓ Perfect Alignment';
633
+ lcmAligned.classList.add('aligned');
634
+ }
635
+ else {
636
+ lcmAligned.textContent = 'Next: ' + lcmVal + 'Q';
637
+ lcmAligned.classList.remove('aligned');
638
+ }
639
+ }
640
+ if (previewBox) {
641
+ const size = Math.min(Math.max(typeVal * 2, 40), 200);
642
+ previewBox.style.width = size + 'px';
643
+ previewBox.style.height = size + 'px';
644
+ }
645
+ }
646
+ slider.addEventListener('input', (e) => updateScale(parseInt(e.target.value)));
647
+ chips.forEach(chip => {
648
+ chip.addEventListener('click', () => {
649
+ updateScale(parseInt(chip.dataset.value || '4'));
650
+ });
651
+ });
652
+ updateScale(4);
653
+ function createGrid(canvasId, scale, className, showLCM = false) {
654
+ const canvas = document.getElementById(canvasId);
655
+ if (!canvas)
656
+ return;
657
+ canvas.innerHTML = '';
658
+ const width = canvas.offsetWidth;
659
+ for (let x = scale; x < width; x += scale) {
660
+ const line = document.createElement('div');
661
+ line.className = 'grid-line ' + className;
662
+ if (showLCM && x % 20 === 0) {
663
+ line.classList.add('lcm');
664
+ }
665
+ line.style.left = x + 'px';
666
+ canvas.appendChild(line);
667
+ }
668
+ }
669
+ function createCombinedGrid(canvasId) {
670
+ const canvas = document.getElementById(canvasId);
671
+ if (!canvas)
672
+ return;
673
+ canvas.innerHTML = '';
674
+ const width = canvas.offsetWidth;
675
+ for (let x = 20; x < width; x += 20) {
676
+ const line = document.createElement('div');
677
+ line.className = 'grid-line lcm';
678
+ line.style.left = x + 'px';
679
+ canvas.appendChild(line);
680
+ }
681
+ }
682
+ function initGrids() {
683
+ createGrid('type-grid-canvas', 4, 'type-line', false);
684
+ createGrid('line-grid-canvas', 5, 'line-line', false);
685
+ createCombinedGrid('combined-grid-canvas');
686
+ }
687
+ initGrids();
688
+ let resizeTimeout;
689
+ window.addEventListener('resize', () => {
690
+ clearTimeout(resizeTimeout);
691
+ resizeTimeout = setTimeout(initGrids, 150);
692
+ });
693
+ }
694
+ document.addEventListener('DOMContentLoaded', function () {
695
+ initThemeToggle();
696
+ initGridToggle();
697
+ initMobileNav();
698
+ initDropdown();
699
+ initLayersDemo();
700
+ initDeviceDemo();
701
+ initQScaleDemo();
702
+ initDensityDemo();
703
+ initBreakpointsDemo();
704
+ initPaperDemo();
705
+ initBaselineDemo();
706
+ initHybridScaleDemo();
707
+ });
708
+ //# sourceMappingURL=demo.js.map