twiddle-panel 0.7.0 → 0.8.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.
- package/README.md +15 -3
- package/bin/init.js +118 -0
- package/dist/twiddle.js +69 -38
- package/dist/twiddle.mjs +1210 -1098
- package/package.json +5 -1
package/dist/twiddle.mjs
CHANGED
|
@@ -1,148 +1,148 @@
|
|
|
1
|
-
const
|
|
2
|
-
let
|
|
3
|
-
function
|
|
4
|
-
const
|
|
5
|
-
if (!
|
|
1
|
+
const gt = /* @__PURE__ */ new Set();
|
|
2
|
+
let M = sa(), Q = [], ke = [], qe = null, an = 0;
|
|
3
|
+
function sa() {
|
|
4
|
+
const e = new URLSearchParams(location.search).get("twd");
|
|
5
|
+
if (!e) return {};
|
|
6
6
|
try {
|
|
7
|
-
return JSON.parse(
|
|
7
|
+
return JSON.parse(e);
|
|
8
8
|
} catch {
|
|
9
9
|
return {};
|
|
10
10
|
}
|
|
11
11
|
}
|
|
12
|
-
function
|
|
13
|
-
const
|
|
14
|
-
Object.keys(
|
|
12
|
+
function ia() {
|
|
13
|
+
const e = new URL(location.href);
|
|
14
|
+
Object.keys(M).length ? e.searchParams.set("twd", JSON.stringify(M)) : e.searchParams.delete("twd"), history.replaceState(history.state, "", e);
|
|
15
15
|
}
|
|
16
|
-
function
|
|
17
|
-
|
|
16
|
+
function Et() {
|
|
17
|
+
ia(), gt.forEach((e) => e(M));
|
|
18
18
|
}
|
|
19
|
-
function
|
|
20
|
-
if (JSON.stringify(
|
|
19
|
+
function pe(e, t) {
|
|
20
|
+
if (JSON.stringify(e) === JSON.stringify(M)) return;
|
|
21
21
|
const n = Date.now();
|
|
22
|
-
|
|
22
|
+
t && t === qe && n - an < 400 || (Q.push(M), Q.length > 100 && Q.shift()), ke = [], qe = t, an = n, M = e, Et();
|
|
23
23
|
}
|
|
24
|
-
function
|
|
25
|
-
const n = { ...
|
|
24
|
+
function Ve(e, t) {
|
|
25
|
+
const n = { ...e, ...t };
|
|
26
26
|
return Object.keys(n).forEach((a) => {
|
|
27
27
|
n[a] == null && delete n[a];
|
|
28
28
|
}), Object.keys(n).length ? n : null;
|
|
29
29
|
}
|
|
30
|
-
function
|
|
31
|
-
const a = { ...
|
|
32
|
-
return n == null ? delete a[
|
|
30
|
+
function B(e, t, n) {
|
|
31
|
+
const a = { ...e };
|
|
32
|
+
return n == null ? delete a[t] : a[t] = n, a;
|
|
33
33
|
}
|
|
34
|
-
function
|
|
35
|
-
return JSON.parse(JSON.stringify(
|
|
34
|
+
function mt() {
|
|
35
|
+
return JSON.parse(JSON.stringify(M));
|
|
36
36
|
}
|
|
37
|
-
function
|
|
38
|
-
|
|
37
|
+
function la(e, t) {
|
|
38
|
+
pe(B(M, e, Ve(M[e], t)), `${e}:${Object.keys(t)}`);
|
|
39
39
|
}
|
|
40
|
-
function
|
|
41
|
-
const n = Object.entries(
|
|
42
|
-
|
|
40
|
+
function da(e, t = `multi:${Object.keys(e)}`) {
|
|
41
|
+
const n = Object.entries(e).reduce((a, [o, s]) => B(a, o, Ve(a[o], s)), M);
|
|
42
|
+
pe(n, t);
|
|
43
43
|
}
|
|
44
|
-
function
|
|
45
|
-
const a =
|
|
46
|
-
|
|
44
|
+
function Mn(e, t, n) {
|
|
45
|
+
const a = B(M.scopes?.[e], t, Ve(M.scopes?.[e]?.[t], n)), o = B(M.scopes, e, Object.keys(a).length ? a : null);
|
|
46
|
+
pe(B(M, "scopes", Object.keys(o).length ? o : null), `${e}|${t}:${Object.keys(n)}`);
|
|
47
47
|
}
|
|
48
|
-
function
|
|
49
|
-
if (
|
|
50
|
-
Object.keys(
|
|
48
|
+
function ca(e, t) {
|
|
49
|
+
if (t) return Mn(e, t, Object.fromEntries(
|
|
50
|
+
Object.keys(M.scopes?.[e]?.[t] || {}).map((a) => [a, null])
|
|
51
51
|
));
|
|
52
|
-
const n =
|
|
53
|
-
|
|
54
|
-
}
|
|
55
|
-
function
|
|
56
|
-
const n =
|
|
57
|
-
if (!n ||
|
|
58
|
-
let a = { ...
|
|
59
|
-
Object.entries(n).forEach(([
|
|
60
|
-
a =
|
|
52
|
+
const n = B(M.scopes, e, null);
|
|
53
|
+
pe(B(M, "scopes", Object.keys(n).length ? n : null));
|
|
54
|
+
}
|
|
55
|
+
function pa(e, t) {
|
|
56
|
+
const n = M.scopes?.[e];
|
|
57
|
+
if (!n || e === t) return;
|
|
58
|
+
let a = { ...M.scopes[t] };
|
|
59
|
+
Object.entries(n).forEach(([s, r]) => {
|
|
60
|
+
a = B(a, s, Ve(a[s], r));
|
|
61
61
|
});
|
|
62
|
-
const o =
|
|
63
|
-
|
|
62
|
+
const o = B(B(M.scopes, e, null), t, a);
|
|
63
|
+
pe(B(M, "scopes", o));
|
|
64
64
|
}
|
|
65
|
-
function
|
|
66
|
-
|
|
65
|
+
function Tn() {
|
|
66
|
+
pe({});
|
|
67
67
|
}
|
|
68
|
-
function
|
|
69
|
-
|
|
68
|
+
function An() {
|
|
69
|
+
Q.length && (ke.push(M), M = Q.pop(), qe = null, Et());
|
|
70
70
|
}
|
|
71
|
-
function
|
|
72
|
-
|
|
71
|
+
function Ln() {
|
|
72
|
+
ke.length && (Q.push(M), M = ke.pop(), qe = null, Et());
|
|
73
73
|
}
|
|
74
|
-
const
|
|
75
|
-
function
|
|
76
|
-
return
|
|
74
|
+
const ua = () => Q.length > 0, ha = () => ke.length > 0;
|
|
75
|
+
function Pn(e) {
|
|
76
|
+
return gt.add(e), () => gt.delete(e);
|
|
77
77
|
}
|
|
78
|
-
const
|
|
78
|
+
const fa = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
|
|
79
79
|
__proto__: null,
|
|
80
|
-
canRedo:
|
|
81
|
-
canUndo:
|
|
82
|
-
getState:
|
|
83
|
-
moveScope:
|
|
84
|
-
onChange:
|
|
85
|
-
redo:
|
|
86
|
-
removeScope:
|
|
87
|
-
resetState:
|
|
88
|
-
setScope:
|
|
89
|
-
setSection:
|
|
90
|
-
setSections:
|
|
91
|
-
undo:
|
|
92
|
-
}, Symbol.toStringTag, { value: "Module" })),
|
|
93
|
-
let
|
|
94
|
-
function
|
|
95
|
-
typeof
|
|
96
|
-
}
|
|
97
|
-
const
|
|
80
|
+
canRedo: ha,
|
|
81
|
+
canUndo: ua,
|
|
82
|
+
getState: mt,
|
|
83
|
+
moveScope: pa,
|
|
84
|
+
onChange: Pn,
|
|
85
|
+
redo: Ln,
|
|
86
|
+
removeScope: ca,
|
|
87
|
+
resetState: Tn,
|
|
88
|
+
setScope: Mn,
|
|
89
|
+
setSection: la,
|
|
90
|
+
setSections: da,
|
|
91
|
+
undo: An
|
|
92
|
+
}, Symbol.toStringTag, { value: "Module" })), On = [];
|
|
93
|
+
let bt = [], Ct = {}, R = null, de = !1, Fn = {};
|
|
94
|
+
function ga(e) {
|
|
95
|
+
typeof e == "function" && On.push(e);
|
|
96
|
+
}
|
|
97
|
+
const on = (e) => Object.assign({}, ...On.map((t) => t(e) || {})), xt = (e) => /^(\.[\w-]+|\[data-twd="[^"]+"\])$/.test(e), ma = (e) => {
|
|
98
98
|
try {
|
|
99
|
-
return document.querySelector(
|
|
99
|
+
return document.querySelector(e), !0;
|
|
100
100
|
} catch {
|
|
101
101
|
return !1;
|
|
102
102
|
}
|
|
103
103
|
};
|
|
104
|
-
function
|
|
104
|
+
function ba() {
|
|
105
105
|
return (!R || !R.isConnected) && (R = document.createElement("style"), R.id = "twiddle-scoped", document.head.appendChild(R)), R;
|
|
106
106
|
}
|
|
107
|
-
function
|
|
108
|
-
const t = document.documentElement;
|
|
109
|
-
ht.forEach((e) => t.style.removeProperty(e)), ht = [], kt = {};
|
|
110
|
-
}
|
|
111
|
-
function gt(t) {
|
|
112
|
-
if (Cn = t, oe) return;
|
|
107
|
+
function Nn() {
|
|
113
108
|
const e = document.documentElement;
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
109
|
+
bt.forEach((t) => e.style.removeProperty(t)), bt = [], Ct = {};
|
|
110
|
+
}
|
|
111
|
+
function wt(e) {
|
|
112
|
+
if (Fn = e, de) return;
|
|
113
|
+
const t = document.documentElement;
|
|
114
|
+
Nn();
|
|
115
|
+
const n = on(e), a = Object.entries(e.scopes || {}).filter(([s]) => ma(s)).sort(([s], [r]) => xt(r) - xt(s)).map(([s, r]) => [s, on(r)]);
|
|
116
|
+
Object.entries(n).forEach(([s, r]) => t.style.setProperty(s, r)), bt = Object.keys(n), Ct = n;
|
|
117
|
+
const o = a.filter(([, s]) => Object.keys(s).length).map(([s, r]) => `${s} {
|
|
118
|
+
${Object.entries(r).map(([i, l]) => ` ${i}: ${l} !important;`).join(`
|
|
119
119
|
`)}
|
|
120
120
|
}`).join(`
|
|
121
121
|
`);
|
|
122
|
-
(o || R) && (
|
|
122
|
+
(o || R) && (ba().textContent = o);
|
|
123
123
|
}
|
|
124
|
-
function
|
|
125
|
-
const
|
|
126
|
-
Object.keys(a).forEach((o) =>
|
|
124
|
+
function jn(e) {
|
|
125
|
+
const t = document.documentElement, n = de, a = { ...Ct };
|
|
126
|
+
Object.keys(a).forEach((o) => t.style.removeProperty(o)), R && (R.disabled = !0);
|
|
127
127
|
try {
|
|
128
|
-
return
|
|
128
|
+
return e();
|
|
129
129
|
} finally {
|
|
130
|
-
n || (Object.entries(a).forEach(([o,
|
|
130
|
+
n || (Object.entries(a).forEach(([o, s]) => t.style.setProperty(o, s)), R && (R.disabled = !1));
|
|
131
131
|
}
|
|
132
132
|
}
|
|
133
|
-
function
|
|
134
|
-
return
|
|
135
|
-
const n = getComputedStyle(
|
|
136
|
-
return Object.fromEntries(
|
|
133
|
+
function xa(e, t = document.documentElement) {
|
|
134
|
+
return jn(() => {
|
|
135
|
+
const n = getComputedStyle(t);
|
|
136
|
+
return Object.fromEntries(e.map((a) => [a, n.getPropertyValue(a).trim()]));
|
|
137
137
|
});
|
|
138
138
|
}
|
|
139
|
-
function
|
|
140
|
-
|
|
139
|
+
function wa() {
|
|
140
|
+
de || (Nn(), R && (R.disabled = !0), de = !0);
|
|
141
141
|
}
|
|
142
|
-
function
|
|
143
|
-
|
|
142
|
+
function ya() {
|
|
143
|
+
de && (de = !1, R && (R.disabled = !1), wt(Fn));
|
|
144
144
|
}
|
|
145
|
-
const
|
|
145
|
+
const va = `
|
|
146
146
|
:host { all: initial; } /* block fonts/colours inherited from the page */
|
|
147
147
|
* { box-sizing: border-box; }
|
|
148
148
|
|
|
@@ -160,11 +160,17 @@ const ua = `
|
|
|
160
160
|
position: fixed;
|
|
161
161
|
right: 16px;
|
|
162
162
|
bottom: 16px;
|
|
163
|
+
/* Where it sits is set inline by position.js (drag / nudge, per site);
|
|
164
|
+
--off-x / --off-y = distance from the two nearest edges. */
|
|
165
|
+
--off-x: 16px;
|
|
166
|
+
--off-y: 16px;
|
|
163
167
|
z-index: 2147483647;
|
|
164
168
|
font: 13px/1.4 ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
|
|
165
169
|
color: var(--p-fg);
|
|
170
|
+
/* The panel opens to the left of the launcher, bottoms aligned,
|
|
171
|
+
so it can use the full window height. */
|
|
166
172
|
display: flex;
|
|
167
|
-
flex-direction:
|
|
173
|
+
flex-direction: row;
|
|
168
174
|
align-items: flex-end;
|
|
169
175
|
gap: 10px;
|
|
170
176
|
}
|
|
@@ -196,6 +202,18 @@ const ua = `
|
|
|
196
202
|
cursor: pointer;
|
|
197
203
|
padding: 0;
|
|
198
204
|
}
|
|
205
|
+
/* Open or minimised: centred on the tabs. 10px = panel border 1 + tab bar
|
|
206
|
+
padding 6 + half a tab (~49px) − half the launcher (22). */
|
|
207
|
+
.launcher { transition: margin-bottom .2s cubic-bezier(0.2, 0.8, 0.2, 1); }
|
|
208
|
+
.wrap.is-open .launcher { margin-bottom: 10px; }
|
|
209
|
+
/* Left corners: the panel opens to the right of the launcher. Top corners: tops aligned. */
|
|
210
|
+
.wrap.at-left { flex-direction: row-reverse; }
|
|
211
|
+
.wrap.at-top { align-items: flex-start; }
|
|
212
|
+
.wrap.at-top.is-open .launcher { margin-bottom: 0; }
|
|
213
|
+
.launcher { touch-action: none; }
|
|
214
|
+
.wrap.dragging .launcher { cursor: grabbing; }
|
|
215
|
+
.wrap.dragging .panel { pointer-events: none; }
|
|
216
|
+
@media (prefers-reduced-motion: reduce) { .launcher { transition: none; } }
|
|
199
217
|
.mark { display: block; overflow: visible; }
|
|
200
218
|
.mark-disc { fill: #16161a; }
|
|
201
219
|
.mark-knob { fill: #4f46e5; }
|
|
@@ -212,11 +230,12 @@ const ua = `
|
|
|
212
230
|
/* Panel */
|
|
213
231
|
.panel {
|
|
214
232
|
width: 360px; /* tabbed layout (Figma 254:323); was 300 */
|
|
215
|
-
|
|
233
|
+
/* 70px = 16 room on the far side + 44 launcher + 10 gap */
|
|
234
|
+
max-width: calc(100vw - var(--off-x) - 70px);
|
|
216
235
|
/* As tall as its content; scrolls only when the window is shorter.
|
|
217
|
-
|
|
218
|
-
max-height: calc(100vh -
|
|
219
|
-
max-height: calc(100dvh -
|
|
236
|
+
32px = 16 margin below + 16 room at the top. */
|
|
237
|
+
max-height: calc(100vh - var(--off-y) - 16px);
|
|
238
|
+
max-height: calc(100dvh - var(--off-y) - 16px);
|
|
220
239
|
background: var(--p-bg);
|
|
221
240
|
border: 1px solid var(--p-line);
|
|
222
241
|
border-radius: 14px;
|
|
@@ -443,8 +462,7 @@ const ua = `
|
|
|
443
462
|
padding: 1px 4px;
|
|
444
463
|
}
|
|
445
464
|
|
|
446
|
-
/*
|
|
447
|
-
above the launcher, so a toast here never covers the footer's Copy prompt. */
|
|
465
|
+
/* Closed: beside the launcher (on the panel's side of it, centred on it). */
|
|
448
466
|
.toast {
|
|
449
467
|
position: absolute; bottom: 22px; right: 54px; transform: translateY(50%);
|
|
450
468
|
background: var(--p-accent); color: var(--p-on-accent);
|
|
@@ -453,6 +471,13 @@ const ua = `
|
|
|
453
471
|
opacity: 0; transition: opacity .15s ease; pointer-events: none;
|
|
454
472
|
}
|
|
455
473
|
.toast.show { opacity: 1; }
|
|
474
|
+
.wrap.at-left .toast { right: auto; left: 54px; }
|
|
475
|
+
.wrap.at-top .toast { bottom: auto; top: 22px; transform: translateY(-50%); }
|
|
476
|
+
/* Open or minimised: just above the panel, right-aligned with it, so it
|
|
477
|
+
never covers a row (Ravi, 2026-10-16; Figma 269:1037). Top corners: below it. */
|
|
478
|
+
.wrap.is-open .toast { bottom: calc(100% + 8px); right: 54px; left: auto; top: auto; transform: none; z-index: 3; }
|
|
479
|
+
.wrap.is-open.at-left .toast { right: auto; left: 54px; }
|
|
480
|
+
.wrap.is-open.at-top .toast { bottom: auto; top: calc(100% + 8px); }
|
|
456
481
|
|
|
457
482
|
/* ---------- Proposals below: not yet designed in Figma ---------- */
|
|
458
483
|
|
|
@@ -763,6 +788,11 @@ const ua = `
|
|
|
763
788
|
.tab:hover { background: var(--p-hover); color: var(--p-fg); }
|
|
764
789
|
.tab[aria-pressed="true"] { background: var(--p-hover); color: var(--p-fg); font-weight: 600; }
|
|
765
790
|
.tab span { max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
791
|
+
|
|
792
|
+
/* Minimised (proposal, not yet in Figma): only the tab bar (and the Compare
|
|
793
|
+
strip while it shows); clicking a tab opens the panel on that tab. */
|
|
794
|
+
.panel.minimised > :not(.tabbar):not(.original-pill) { display: none; }
|
|
795
|
+
.panel.minimised .tabbar { border-top: 0; }
|
|
766
796
|
.page-fields { gap: 16px; }
|
|
767
797
|
.page-heading { display: flex; align-items: baseline; justify-content: space-between; font-size: 13px; font-weight: 500; color: var(--p-fg); margin-bottom: -6px; }
|
|
768
798
|
.page-heading .muted { font-size: 12px; font-weight: 400; }
|
|
@@ -871,7 +901,7 @@ const ua = `
|
|
|
871
901
|
.summary-swatch { width: 14px; height: 14px; border-radius: 4px; flex: none; }
|
|
872
902
|
.summary-swatch[hidden] { display: none; }
|
|
873
903
|
.summary-row .chev { display: grid; color: var(--p-muted); }
|
|
874
|
-
`, P = (
|
|
904
|
+
`, P = (e, t, n) => `<svg width="${e}" height="${e}" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="${t}" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">${n}</svg>`, A = {
|
|
875
905
|
dial: P(20, 1.8, '<circle cx="12" cy="12" r="8"/><path d="M12 12l4-4"/><path d="M12 2v2M12 20v2M2 12h2M20 12h2"/>'),
|
|
876
906
|
link: P(15, 2, '<path d="M10 13a5 5 0 0 0 7.07 0l3-3a5 5 0 0 0-7.07-7.07l-1 1"/><path d="M14 11a5 5 0 0 0-7.07 0l-3 3a5 5 0 0 0 7.07 7.07l1-1"/>'),
|
|
877
907
|
reset: P(15, 2, '<path d="M3 12a9 9 0 1 0 3-6.7"/><path d="M3 3v6h6"/>'),
|
|
@@ -884,6 +914,7 @@ const ua = `
|
|
|
884
914
|
redo: P(15, 2, '<path d="M15 14l5-5-5-5"/><path d="M20 9H9a5 5 0 0 0 0 10h3"/>'),
|
|
885
915
|
back: P(15, 2, '<path d="M15 18l-6-6 6-6"/>'),
|
|
886
916
|
down: P(12, 2.4, '<path d="M6 9l6 6 6-6"/>'),
|
|
917
|
+
minimise: P(15, 2, '<path d="M5 12h14"/>'),
|
|
887
918
|
copy: P(13, 2, '<rect x="9" y="9" width="11" height="11" rx="2"/><path d="M5 15V5a2 2 0 0 1 2-2h10"/>'),
|
|
888
919
|
warn: P(13, 2, '<path d="M12 3l9.5 17h-19z"/><path d="M12 10v4M12 17.5v.01"/>'),
|
|
889
920
|
// Launcher: the Panel product mark (Twiddle brand; Figma Logo page › Panel mark; also site/favicon.svg).
|
|
@@ -898,211 +929,280 @@ const ua = `
|
|
|
898
929
|
tabShadows: P(18, 2, '<rect x="4" y="4" width="12" height="12" rx="2"/><path d="M20 8v10a2 2 0 0 1-2 2H8"/>'),
|
|
899
930
|
sliders: P(15, 2, '<path d="M4 7h10M18 7h2M4 17h4M12 17h8"/><circle cx="16" cy="7" r="2"/><circle cx="10" cy="17" r="2"/>')
|
|
900
931
|
// Figma: Icon/Sliders (proposal)
|
|
901
|
-
},
|
|
902
|
-
function
|
|
932
|
+
}, Rn = "twiddle:";
|
|
933
|
+
function Z(e) {
|
|
903
934
|
try {
|
|
904
|
-
return localStorage.getItem(
|
|
935
|
+
return localStorage.getItem(Rn + e);
|
|
905
936
|
} catch {
|
|
906
937
|
return null;
|
|
907
938
|
}
|
|
908
939
|
}
|
|
909
|
-
function
|
|
940
|
+
function oe(e, t) {
|
|
910
941
|
try {
|
|
911
|
-
localStorage.setItem(
|
|
942
|
+
localStorage.setItem(Rn + e, t);
|
|
912
943
|
} catch {
|
|
913
944
|
}
|
|
914
945
|
}
|
|
915
|
-
|
|
946
|
+
const re = 44, ae = 8, ka = 4, zn = { corner: "br", x: 16, y: 16 };
|
|
947
|
+
function Sa() {
|
|
948
|
+
try {
|
|
949
|
+
const e = JSON.parse(Z("position"));
|
|
950
|
+
if (e && /^[tb][lr]$/.test(e.corner) && isFinite(e.x) && isFinite(e.y)) return e;
|
|
951
|
+
} catch {
|
|
952
|
+
}
|
|
953
|
+
return { ...zn };
|
|
954
|
+
}
|
|
955
|
+
function rn(e) {
|
|
956
|
+
const t = Math.max(ae, innerWidth - re - ae), n = Math.max(ae, innerHeight - re - ae);
|
|
957
|
+
return { corner: e.corner, x: Math.round(Math.min(Math.max(e.x, ae), t)), y: Math.round(Math.min(Math.max(e.y, ae), n)) };
|
|
958
|
+
}
|
|
959
|
+
function $a({ wrapEl: e, launcherEl: t }) {
|
|
960
|
+
let n = Sa();
|
|
961
|
+
function a() {
|
|
962
|
+
const d = rn(n), p = d.corner[1] === "l", u = d.corner[0] === "t";
|
|
963
|
+
Object.assign(e.style, {
|
|
964
|
+
left: p ? `${d.x}px` : "auto",
|
|
965
|
+
right: p ? "auto" : `${d.x}px`,
|
|
966
|
+
top: u ? `${d.y}px` : "auto",
|
|
967
|
+
bottom: u ? "auto" : `${d.y}px`
|
|
968
|
+
}), e.style.setProperty("--off-x", `${d.x}px`), e.style.setProperty("--off-y", `${d.y}px`), e.classList.toggle("at-left", p), e.classList.toggle("at-top", u);
|
|
969
|
+
}
|
|
970
|
+
function o(d, p = !0) {
|
|
971
|
+
n = rn(d), a(), p && oe("position", JSON.stringify(n));
|
|
972
|
+
}
|
|
973
|
+
function s(d, p) {
|
|
974
|
+
const u = d + re / 2 < innerWidth / 2, v = p + re / 2 < innerHeight / 2, f = !v && e.classList.contains("is-open") ? 10 : 0;
|
|
975
|
+
return {
|
|
976
|
+
corner: (v ? "t" : "b") + (u ? "l" : "r"),
|
|
977
|
+
x: u ? d : innerWidth - d - re,
|
|
978
|
+
y: v ? p : innerHeight - p - re - f
|
|
979
|
+
};
|
|
980
|
+
}
|
|
981
|
+
let r = null, i = !1;
|
|
982
|
+
t.addEventListener("pointerdown", (d) => {
|
|
983
|
+
if (d.button !== 0) return;
|
|
984
|
+
const p = t.getBoundingClientRect();
|
|
985
|
+
r = { id: d.pointerId, sx: d.clientX, sy: d.clientY, dx: d.clientX - p.left, dy: d.clientY - p.top, moving: !1 };
|
|
986
|
+
}), t.addEventListener("pointermove", (d) => {
|
|
987
|
+
if (!(!r || d.pointerId !== r.id)) {
|
|
988
|
+
if (!r.moving) {
|
|
989
|
+
if (Math.hypot(d.clientX - r.sx, d.clientY - r.sy) < ka) return;
|
|
990
|
+
r.moving = !0, t.setPointerCapture(d.pointerId), e.classList.add("dragging");
|
|
991
|
+
}
|
|
992
|
+
o(s(d.clientX - r.dx, d.clientY - r.dy), !1);
|
|
993
|
+
}
|
|
994
|
+
});
|
|
995
|
+
const l = (d) => {
|
|
996
|
+
!r || d.pointerId !== r.id || (r.moving && (i = !0, e.classList.remove("dragging"), o(n)), r = null);
|
|
997
|
+
};
|
|
998
|
+
return t.addEventListener("pointerup", l), t.addEventListener("pointercancel", l), t.addEventListener("keydown", (d) => {
|
|
999
|
+
const p = { ArrowLeft: [-1, 0], ArrowRight: [1, 0], ArrowUp: [0, -1], ArrowDown: [0, 1] }[d.key];
|
|
1000
|
+
if (!p || d.altKey || d.metaKey || d.ctrlKey) return;
|
|
1001
|
+
d.preventDefault();
|
|
1002
|
+
const u = d.shiftKey ? 10 : 1, v = n.corner[1] === "l" ? 1 : -1, f = n.corner[0] === "t" ? 1 : -1;
|
|
1003
|
+
o({ corner: n.corner, x: n.x + p[0] * u * v, y: n.y + p[1] * u * f });
|
|
1004
|
+
}), window.addEventListener("resize", a), a(), {
|
|
1005
|
+
/** Call from the launcher's click handler: true = ignore this click (it ended a drag). */
|
|
1006
|
+
consumeClick() {
|
|
1007
|
+
const d = i;
|
|
1008
|
+
return i = !1, d;
|
|
1009
|
+
},
|
|
1010
|
+
reset() {
|
|
1011
|
+
o({ ...zn });
|
|
1012
|
+
}
|
|
1013
|
+
};
|
|
1014
|
+
}
|
|
1015
|
+
function ce({ label: e, options: t, onSelect: n }) {
|
|
916
1016
|
const a = document.createElement("div");
|
|
917
|
-
a.className = "seg", a.setAttribute("role", "radiogroup"), a.setAttribute("aria-label",
|
|
918
|
-
const o =
|
|
919
|
-
const
|
|
920
|
-
return
|
|
1017
|
+
a.className = "seg", a.setAttribute("role", "radiogroup"), a.setAttribute("aria-label", e);
|
|
1018
|
+
const o = t.map((l) => {
|
|
1019
|
+
const d = document.createElement("button");
|
|
1020
|
+
return d.type = "button", d.setAttribute("role", "radio"), d.dataset.value = l.value, d.textContent = l.label, l.title && (d.title = l.title), d.addEventListener("click", () => i(l.value)), d;
|
|
921
1021
|
});
|
|
922
1022
|
a.append(...o);
|
|
923
|
-
let
|
|
924
|
-
function
|
|
925
|
-
|
|
926
|
-
const
|
|
927
|
-
o.forEach((
|
|
928
|
-
|
|
1023
|
+
let s = null;
|
|
1024
|
+
function r(l) {
|
|
1025
|
+
s = l, o.forEach((p) => p.setAttribute("aria-checked", String(p.dataset.value === l)));
|
|
1026
|
+
const d = Math.max(0, o.findIndex((p) => p.dataset.value === l));
|
|
1027
|
+
o.forEach((p, u) => {
|
|
1028
|
+
p.tabIndex = u === d ? 0 : -1;
|
|
929
1029
|
});
|
|
930
1030
|
}
|
|
931
|
-
function i(l,
|
|
932
|
-
l !==
|
|
1031
|
+
function i(l, d) {
|
|
1032
|
+
l !== s && (r(l), n(l)), d && o.find((p) => p.dataset.value === l).focus();
|
|
933
1033
|
}
|
|
934
1034
|
return a.addEventListener("keydown", (l) => {
|
|
935
|
-
const
|
|
936
|
-
if (
|
|
937
|
-
const
|
|
938
|
-
let
|
|
939
|
-
if (
|
|
940
|
-
else if (l.key === "Home")
|
|
941
|
-
else if (l.key === "End")
|
|
1035
|
+
const d = o.indexOf(l.target);
|
|
1036
|
+
if (d < 0) return;
|
|
1037
|
+
const p = { ArrowRight: 1, ArrowDown: 1, ArrowLeft: -1, ArrowUp: -1 }[l.key];
|
|
1038
|
+
let u;
|
|
1039
|
+
if (p) u = (d + p + o.length) % o.length;
|
|
1040
|
+
else if (l.key === "Home") u = 0;
|
|
1041
|
+
else if (l.key === "End") u = o.length - 1;
|
|
942
1042
|
else return;
|
|
943
|
-
l.preventDefault(), i(o[
|
|
944
|
-
}),
|
|
1043
|
+
l.preventDefault(), i(o[u].dataset.value, !0);
|
|
1044
|
+
}), r(null), { el: a, setValue: r };
|
|
945
1045
|
}
|
|
946
|
-
const
|
|
947
|
-
function*
|
|
948
|
-
for (const
|
|
949
|
-
|
|
1046
|
+
const In = /var\(\s*(--twd-[\w-]+)/g, Ea = /::?[\w-]+(\([^)]*\))?/g;
|
|
1047
|
+
function* _e(e) {
|
|
1048
|
+
for (const t of e)
|
|
1049
|
+
t.selectorText != null && (yield t), t.cssRules && (yield* _e(t.cssRules));
|
|
950
1050
|
}
|
|
951
|
-
function
|
|
952
|
-
const
|
|
953
|
-
for (const
|
|
954
|
-
if (
|
|
1051
|
+
function Ca() {
|
|
1052
|
+
const e = [];
|
|
1053
|
+
for (const t of document.styleSheets) {
|
|
1054
|
+
if (t.ownerNode?.id === "twiddle-scoped") continue;
|
|
955
1055
|
let n;
|
|
956
1056
|
try {
|
|
957
|
-
n =
|
|
1057
|
+
n = t.cssRules;
|
|
958
1058
|
} catch {
|
|
959
1059
|
continue;
|
|
960
1060
|
}
|
|
961
|
-
for (const a of
|
|
962
|
-
const o = [...a.style.cssText.matchAll(
|
|
963
|
-
o.length &&
|
|
1061
|
+
for (const a of _e(n)) {
|
|
1062
|
+
const o = [...a.style.cssText.matchAll(In)].map((s) => s[1]);
|
|
1063
|
+
o.length && e.push({ selectors: a.selectorText.split(","), vars: o });
|
|
964
1064
|
}
|
|
965
1065
|
}
|
|
966
|
-
return
|
|
1066
|
+
return e;
|
|
967
1067
|
}
|
|
968
|
-
function
|
|
969
|
-
const n =
|
|
1068
|
+
function Ma(e, t) {
|
|
1069
|
+
const n = t.replace(Ea, "").trim() || "*";
|
|
970
1070
|
try {
|
|
971
|
-
return
|
|
1071
|
+
return e.matches(n);
|
|
972
1072
|
} catch {
|
|
973
1073
|
return !1;
|
|
974
1074
|
}
|
|
975
1075
|
}
|
|
976
|
-
function
|
|
977
|
-
const
|
|
978
|
-
return
|
|
979
|
-
const
|
|
980
|
-
|
|
981
|
-
}), [...(
|
|
1076
|
+
function Dn(e) {
|
|
1077
|
+
const t = /* @__PURE__ */ new Set(), n = [];
|
|
1078
|
+
return Ca().forEach(({ selectors: a, vars: o }) => {
|
|
1079
|
+
const s = a.find((r) => Ma(e, r));
|
|
1080
|
+
s && (o.forEach((r) => t.add(r)), n.push(s.trim()));
|
|
1081
|
+
}), [...(e.getAttribute("style") || "").matchAll(In)].forEach((a) => t.add(a[1])), { names: t, selectors: n };
|
|
982
1082
|
}
|
|
983
|
-
const
|
|
984
|
-
function
|
|
985
|
-
const
|
|
986
|
-
let n =
|
|
1083
|
+
const yt = (e) => window.CSS && CSS.escape ? CSS.escape(e) : e;
|
|
1084
|
+
function Ta(e) {
|
|
1085
|
+
const t = [];
|
|
1086
|
+
let n = e;
|
|
987
1087
|
for (; n && n.nodeType === 1 && n !== document.body && n !== document.documentElement; ) {
|
|
988
|
-
if (n.id && document.querySelectorAll(`#${
|
|
989
|
-
return
|
|
990
|
-
const a = n.localName, o = [...n.parentElement.children].filter((
|
|
991
|
-
|
|
1088
|
+
if (n.id && document.querySelectorAll(`#${yt(n.id)}`).length === 1)
|
|
1089
|
+
return t.unshift(`#${yt(n.id)}`), t.join(" > ");
|
|
1090
|
+
const a = n.localName, o = [...n.parentElement.children].filter((s) => s.localName === a);
|
|
1091
|
+
t.unshift(o.length > 1 ? `${a}:nth-of-type(${o.indexOf(n) + 1})` : a), n = n.parentElement;
|
|
992
1092
|
}
|
|
993
|
-
return ["body", ...
|
|
1093
|
+
return ["body", ...t].join(" > ");
|
|
994
1094
|
}
|
|
995
|
-
function
|
|
996
|
-
const n =
|
|
1095
|
+
function Aa(e, t = Dn(e).selectors) {
|
|
1096
|
+
const n = e.getAttribute("data-twd");
|
|
997
1097
|
if (n) return `[data-twd="${n.replace(/"/g, "")}"]`;
|
|
998
|
-
const a = [...
|
|
999
|
-
return
|
|
1098
|
+
const a = [...e.classList], s = a.find((r) => t.some((i) => new RegExp(`\\.${r.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}(?![\\w-])`).test(i))) || a[0];
|
|
1099
|
+
return s ? `.${yt(s)}` : null;
|
|
1000
1100
|
}
|
|
1001
|
-
function
|
|
1002
|
-
const
|
|
1101
|
+
function La(e) {
|
|
1102
|
+
const t = Dn(e), n = Aa(e, t.selectors), a = (e.getAttribute("aria-label") || e.placeholder || e.textContent || "").trim().replace(/\s+/g, " ");
|
|
1003
1103
|
return {
|
|
1004
|
-
el:
|
|
1005
|
-
tag:
|
|
1006
|
-
classes: [...
|
|
1104
|
+
el: e,
|
|
1105
|
+
tag: e.localName,
|
|
1106
|
+
classes: [...e.classList],
|
|
1007
1107
|
text: a.length > 40 ? `${a.slice(0, 40)}…` : a,
|
|
1008
|
-
tokens:
|
|
1009
|
-
elementSelector:
|
|
1108
|
+
tokens: t.names,
|
|
1109
|
+
elementSelector: Ta(e),
|
|
1010
1110
|
classSelector: n,
|
|
1011
1111
|
classCount: n ? document.querySelectorAll(n).length : 0
|
|
1012
1112
|
};
|
|
1013
1113
|
}
|
|
1014
|
-
const
|
|
1015
|
-
function
|
|
1016
|
-
const a =
|
|
1017
|
-
let
|
|
1114
|
+
const vt = (e) => e.localName + [...e.classList].map((t) => `.${t}`).join(""), Pa = { radius: "px", intensity: "%", base: "px" }, Un = (e) => e.charAt(0).toUpperCase() + e.slice(1), Oa = (e, t) => typeof t == "number" || /^\d+$/.test(t) ? `${t}${Pa[e] || ""}` : Un(String(t));
|
|
1115
|
+
function sn(e, t, n) {
|
|
1116
|
+
const a = jn(() => e.tokens({ [e.id]: t }) || {}), o = Object.keys(a);
|
|
1117
|
+
let s = {};
|
|
1018
1118
|
if (o.length) {
|
|
1019
|
-
const
|
|
1020
|
-
|
|
1119
|
+
const r = n.selector ? document.querySelector(n.selector) : void 0;
|
|
1120
|
+
s = xa(o, r || void 0);
|
|
1021
1121
|
}
|
|
1022
1122
|
return {
|
|
1023
1123
|
...n,
|
|
1024
|
-
sectionId:
|
|
1025
|
-
title:
|
|
1026
|
-
settings: Object.entries(
|
|
1027
|
-
tokens: o.map((
|
|
1124
|
+
sectionId: e.id,
|
|
1125
|
+
title: e.title,
|
|
1126
|
+
settings: Object.entries(t).map(([r, i]) => ({ key: r, label: Un(r), value: Oa(r, i), ...e.describe?.(r, i) || {} })),
|
|
1127
|
+
tokens: o.map((r) => ({ name: r, from: s[r] || "", to: a[r] }))
|
|
1028
1128
|
};
|
|
1029
1129
|
}
|
|
1030
|
-
function
|
|
1130
|
+
function ln(e, t) {
|
|
1031
1131
|
const n = [];
|
|
1032
|
-
return
|
|
1033
|
-
|
|
1034
|
-
}), Object.entries(
|
|
1035
|
-
const
|
|
1036
|
-
|
|
1037
|
-
o[
|
|
1132
|
+
return t.forEach((a) => {
|
|
1133
|
+
e[a.id] && n.push(sn(a, e[a.id], { kind: "global", selector: null }));
|
|
1134
|
+
}), Object.entries(e.scopes || {}).forEach(([a, o]) => {
|
|
1135
|
+
const s = xt(a) ? "class" : "element";
|
|
1136
|
+
t.forEach((r) => {
|
|
1137
|
+
o[r.id] && n.push(sn(r, o[r.id], { kind: s, selector: a }));
|
|
1038
1138
|
});
|
|
1039
1139
|
}), n;
|
|
1040
1140
|
}
|
|
1041
|
-
function
|
|
1042
|
-
const
|
|
1043
|
-
return
|
|
1141
|
+
function Fa(e) {
|
|
1142
|
+
const t = (o) => Object.values(o || {}).reduce((s, r) => s + Object.keys(r || {}).length, 0), { scopes: n, ...a } = e;
|
|
1143
|
+
return t(a) + Object.values(n || {}).reduce((o, s) => o + t(s), 0);
|
|
1044
1144
|
}
|
|
1045
|
-
const
|
|
1046
|
-
function
|
|
1047
|
-
let
|
|
1145
|
+
const le = (e) => `\`${e}\``, et = (e) => e.map((t) => ` - ${le(t.name)}: ${t.from ? `${le(t.from)} → ` : ""}${le(t.to)}`), tt = (e) => `${e.title} › ${e.settings.map((t) => `${t.label}: ${t.value}`).join(", ")}`;
|
|
1146
|
+
function Na(e) {
|
|
1147
|
+
let t = null;
|
|
1048
1148
|
try {
|
|
1049
|
-
|
|
1149
|
+
t = document.querySelector(e);
|
|
1050
1150
|
} catch {
|
|
1051
1151
|
}
|
|
1052
|
-
if (!
|
|
1053
|
-
const n = (
|
|
1054
|
-
return `${
|
|
1152
|
+
if (!t) return "not found on the current page";
|
|
1153
|
+
const n = (t.getAttribute("aria-label") || t.placeholder || t.textContent || "").trim().replace(/\s+/g, " ").slice(0, 60);
|
|
1154
|
+
return `${le(vt(t))}${n ? ` with text “${n}”` : ""}`;
|
|
1055
1155
|
}
|
|
1056
|
-
function
|
|
1156
|
+
function dn(e, t) {
|
|
1057
1157
|
const n = /* @__PURE__ */ new Map();
|
|
1058
|
-
return
|
|
1059
|
-
const o = a[
|
|
1158
|
+
return e.forEach((a) => {
|
|
1159
|
+
const o = a[t];
|
|
1060
1160
|
n.has(o) || n.set(o, []), n.get(o).push(a);
|
|
1061
1161
|
}), [...n];
|
|
1062
1162
|
}
|
|
1063
|
-
function
|
|
1064
|
-
if (!
|
|
1065
|
-
const a =
|
|
1066
|
-
if (
|
|
1163
|
+
function ja(e, { link: t = location.href, warnings: n = [] } = {}) {
|
|
1164
|
+
if (!e.length) return "";
|
|
1165
|
+
const a = e.filter((i) => i.kind === "global"), o = e.filter((i) => i.kind === "class"), s = e.filter((i) => i.kind === "element"), r = [];
|
|
1166
|
+
if (r.push(
|
|
1067
1167
|
"# Apply design tweaks from Panel",
|
|
1068
1168
|
"",
|
|
1069
1169
|
"I explored design changes to this prototype with Panel (by Twiddle), a tweak panel that overrides the prototype's `--twd-*` CSS custom properties (design tokens) at runtime. Make these changes permanent in the source code.",
|
|
1070
1170
|
"",
|
|
1071
|
-
`Preview of the tweaked version: ${
|
|
1072
|
-
), a.length && (
|
|
1171
|
+
`Preview of the tweaked version: ${t}`
|
|
1172
|
+
), a.length && (r.push(
|
|
1073
1173
|
"",
|
|
1074
1174
|
"## 1. Global token changes",
|
|
1075
1175
|
"",
|
|
1076
1176
|
"Update the default value of each token where it is defined (usually a `:root` rule or a `tokens.css` / `twiddle-tokens.css` file). Change the definition, not the places that use it.",
|
|
1077
1177
|
""
|
|
1078
|
-
), a.forEach((i) =>
|
|
1178
|
+
), a.forEach((i) => r.push(`- **${tt(i)}**`, ...et(i.tokens)))), o.length && (r.push(
|
|
1079
1179
|
"",
|
|
1080
1180
|
`## ${a.length ? 2 : 1}. Component-wide changes`,
|
|
1081
1181
|
"",
|
|
1082
1182
|
"These apply to every element matching the selector. In the CSS rule for that component, redeclare the token, e.g. `.input { --twd-radius-control: 999px; }`. Don't replace the `var(--twd-…)` usages with fixed values.",
|
|
1083
1183
|
""
|
|
1084
|
-
),
|
|
1085
|
-
|
|
1086
|
-
})),
|
|
1184
|
+
), dn(o, "selector").forEach(([i, l]) => {
|
|
1185
|
+
r.push(`- ${le(i)} (${document.querySelectorAll(i).length} on this page)`), l.forEach((d) => r.push(` - **${tt(d)}**`, ...et(d.tokens).map((p) => ` ${p}`)));
|
|
1186
|
+
})), s.length) {
|
|
1087
1187
|
const i = 1 + (a.length ? 1 : 0) + (o.length ? 1 : 0);
|
|
1088
|
-
|
|
1188
|
+
r.push(
|
|
1089
1189
|
"",
|
|
1090
1190
|
`## ${i}. Single-element changes`,
|
|
1091
1191
|
"",
|
|
1092
1192
|
"These apply to one element only. Add a modifier class to that element (e.g. `input--pill`) and redeclare the token in a rule for that class. The selectors below are runtime paths to help you find the element; don't copy them into the stylesheet.",
|
|
1093
1193
|
""
|
|
1094
|
-
),
|
|
1095
|
-
|
|
1194
|
+
), dn(s, "selector").forEach(([l, d]) => {
|
|
1195
|
+
r.push(`- ${Na(l)}, found at ${le(l)}`), d.forEach((p) => r.push(` - **${tt(p)}**`, ...et(p.tokens).map((u) => ` ${u}`)));
|
|
1096
1196
|
});
|
|
1097
1197
|
}
|
|
1098
|
-
return n.length &&
|
|
1198
|
+
return n.length && r.push(
|
|
1099
1199
|
"",
|
|
1100
1200
|
"## Contrast to check",
|
|
1101
1201
|
"",
|
|
1102
1202
|
"Panel flagged these colour pairs as below WCAG AA with the new values:",
|
|
1103
1203
|
"",
|
|
1104
1204
|
...n.map((i) => `- ${i.text}`)
|
|
1105
|
-
),
|
|
1205
|
+
), r.push(
|
|
1106
1206
|
"",
|
|
1107
1207
|
"## Constraints",
|
|
1108
1208
|
"",
|
|
@@ -1110,88 +1210,88 @@ function Sa(t, { link: e = location.href, warnings: n = [] } = {}) {
|
|
|
1110
1210
|
"- Change only what is listed above.",
|
|
1111
1211
|
"- If a value uses `color-mix()` and the project targets older browsers, ask before converting it.",
|
|
1112
1212
|
"- When done, list the files you changed."
|
|
1113
|
-
),
|
|
1213
|
+
), r.join(`
|
|
1114
1214
|
`);
|
|
1115
1215
|
}
|
|
1116
|
-
const
|
|
1216
|
+
const Ue = 4.5, Ra = 3, za = [
|
|
1117
1217
|
["--twd-text", "--twd-bg", "Text on background"],
|
|
1118
1218
|
["--twd-text", "--twd-surface", "Text on surface"],
|
|
1119
1219
|
["--twd-text-muted", "--twd-surface", "Muted text on surface"],
|
|
1120
1220
|
["--twd-on-primary", "--twd-primary", "Text on brand"]
|
|
1121
1221
|
];
|
|
1122
|
-
let
|
|
1123
|
-
function
|
|
1124
|
-
if (!
|
|
1125
|
-
|
|
1126
|
-
const [
|
|
1127
|
-
return [
|
|
1128
|
-
}
|
|
1129
|
-
function
|
|
1130
|
-
const
|
|
1131
|
-
|
|
1132
|
-
const n = getComputedStyle(
|
|
1133
|
-
return
|
|
1134
|
-
}
|
|
1135
|
-
const
|
|
1222
|
+
let Y;
|
|
1223
|
+
function Mt(e) {
|
|
1224
|
+
if (!e || (Y ||= Object.assign(document.createElement("canvas"), { width: 1, height: 1 }).getContext("2d", { willReadFrequently: !0 }), !Y)) return null;
|
|
1225
|
+
Y.clearRect(0, 0, 1, 1), Y.fillStyle = "#000", Y.fillStyle = e, Y.fillRect(0, 0, 1, 1);
|
|
1226
|
+
const [t, n, a, o] = Y.getImageData(0, 0, 1, 1).data;
|
|
1227
|
+
return [t, n, a, o / 255];
|
|
1228
|
+
}
|
|
1229
|
+
function cn(e) {
|
|
1230
|
+
const t = document.createElement("i");
|
|
1231
|
+
t.style.cssText = `position:absolute;visibility:hidden;color:var(${e})`, document.documentElement.appendChild(t);
|
|
1232
|
+
const n = getComputedStyle(t).color;
|
|
1233
|
+
return t.remove(), Mt(n);
|
|
1234
|
+
}
|
|
1235
|
+
const pn = ([e, t, n]) => {
|
|
1136
1236
|
const a = (o) => (o /= 255, o <= 0.03928 ? o / 12.92 : ((o + 0.055) / 1.055) ** 2.4);
|
|
1137
|
-
return 0.2126 * a(
|
|
1138
|
-
},
|
|
1139
|
-
function
|
|
1140
|
-
const n =
|
|
1237
|
+
return 0.2126 * a(e) + 0.7152 * a(t) + 0.0722 * a(n);
|
|
1238
|
+
}, Bn = (e, t) => e.slice(0, 3).map((n, a) => n * e[3] + t[a] * (1 - e[3])).concat(1);
|
|
1239
|
+
function qn(e, t) {
|
|
1240
|
+
const n = pn(e[3] < 1 ? Bn(e, t) : e), a = pn(t);
|
|
1141
1241
|
return (Math.max(n, a) + 0.05) / (Math.min(n, a) + 0.05);
|
|
1142
1242
|
}
|
|
1143
|
-
const
|
|
1144
|
-
function
|
|
1145
|
-
const t = [];
|
|
1146
|
-
return Ea.forEach(([e, n, a]) => {
|
|
1147
|
-
const o = nn(e), r = nn(n);
|
|
1148
|
-
if (!o || !r || r[3] === 0) return;
|
|
1149
|
-
const s = Nn(o, r);
|
|
1150
|
-
s < Re && t.push({ label: a, ratio: s, needed: Re, text: `${a}: ${jn(s)} · AA needs ${Re}` });
|
|
1151
|
-
}), t;
|
|
1152
|
-
}
|
|
1153
|
-
function Ca(t) {
|
|
1243
|
+
const Hn = (e) => `${e.toFixed(1)}:1`;
|
|
1244
|
+
function nt() {
|
|
1154
1245
|
const e = [];
|
|
1155
|
-
|
|
1156
|
-
const
|
|
1157
|
-
if (
|
|
1246
|
+
return za.forEach(([t, n, a]) => {
|
|
1247
|
+
const o = cn(t), s = cn(n);
|
|
1248
|
+
if (!o || !s || s[3] === 0) return;
|
|
1249
|
+
const r = qn(o, s);
|
|
1250
|
+
r < Ue && e.push({ label: a, ratio: r, needed: Ue, text: `${a}: ${Hn(r)} · AA needs ${Ue}` });
|
|
1251
|
+
}), e;
|
|
1252
|
+
}
|
|
1253
|
+
function Ia(e) {
|
|
1254
|
+
const t = [];
|
|
1255
|
+
for (let n = e; n && n.nodeType === 1; n = n.parentElement) {
|
|
1256
|
+
const a = Mt(getComputedStyle(n).backgroundColor);
|
|
1257
|
+
if (a && a[3] > 0 && (t.push(a), a[3] >= 1))
|
|
1158
1258
|
break;
|
|
1159
1259
|
}
|
|
1160
|
-
return
|
|
1260
|
+
return t.reduceRight((n, a) => Bn(a, n), [255, 255, 255, 1]);
|
|
1161
1261
|
}
|
|
1162
|
-
function
|
|
1163
|
-
if (!
|
|
1164
|
-
const
|
|
1262
|
+
function Da(e) {
|
|
1263
|
+
if (!e.textContent.trim() && !e.placeholder) return null;
|
|
1264
|
+
const t = getComputedStyle(e), n = Mt(t.color);
|
|
1165
1265
|
if (!n) return null;
|
|
1166
|
-
const a = parseFloat(
|
|
1167
|
-
return
|
|
1168
|
-
}
|
|
1169
|
-
const
|
|
1170
|
-
function
|
|
1171
|
-
return
|
|
1172
|
-
const n =
|
|
1173
|
-
return n && !n.startsWith("--") &&
|
|
1266
|
+
const a = parseFloat(t.fontSize), s = a >= 24 || a >= 18.66 && parseInt(t.fontWeight, 10) >= 700 ? Ra : Ue, r = qn(n, Ia(e));
|
|
1267
|
+
return r < s ? { label: "This element", ratio: r, needed: s, text: `This element’s text: ${Hn(r)} · AA needs ${s}` } : null;
|
|
1268
|
+
}
|
|
1269
|
+
const un = "twiddle-panel", Ua = "0.8.0", hn = "https://panel.twiddle.in", Ba = "--twd-radius-control", qa = /var\(\s*--twd-/;
|
|
1270
|
+
function Ha(e) {
|
|
1271
|
+
return e.style.cssText.split(";").some((t) => {
|
|
1272
|
+
const n = t.slice(0, t.indexOf(":")).trim();
|
|
1273
|
+
return n && !n.startsWith("--") && qa.test(t);
|
|
1174
1274
|
});
|
|
1175
1275
|
}
|
|
1176
|
-
function
|
|
1177
|
-
if (!getComputedStyle(document.documentElement).getPropertyValue(
|
|
1178
|
-
let
|
|
1179
|
-
for (const
|
|
1180
|
-
if (
|
|
1276
|
+
function Va() {
|
|
1277
|
+
if (!getComputedStyle(document.documentElement).getPropertyValue(Ba).trim()) return "no-tokens";
|
|
1278
|
+
let e = !1;
|
|
1279
|
+
for (const t of document.styleSheets) {
|
|
1280
|
+
if (t.ownerNode?.id === "twiddle-scoped") continue;
|
|
1181
1281
|
let n;
|
|
1182
1282
|
try {
|
|
1183
|
-
n =
|
|
1283
|
+
n = t.cssRules;
|
|
1184
1284
|
} catch {
|
|
1185
|
-
|
|
1285
|
+
e = !0;
|
|
1186
1286
|
continue;
|
|
1187
1287
|
}
|
|
1188
|
-
for (const a of
|
|
1288
|
+
for (const a of _e(n)) if (Ha(a)) return "ok";
|
|
1189
1289
|
}
|
|
1190
|
-
return document.querySelector('[style*="var(--twd-"]') ? "ok" :
|
|
1290
|
+
return document.querySelector('[style*="var(--twd-"]') ? "ok" : e ? "unknown" : "unused";
|
|
1191
1291
|
}
|
|
1192
|
-
function
|
|
1193
|
-
const
|
|
1194
|
-
for (const a of n.matchAll(/var\(\s*(--twd-[\w-]+)/g))
|
|
1292
|
+
function _a() {
|
|
1293
|
+
const e = /* @__PURE__ */ new Set(), t = (n) => {
|
|
1294
|
+
for (const a of n.matchAll(/var\(\s*(--twd-[\w-]+)/g)) e.add(a[1]);
|
|
1195
1295
|
};
|
|
1196
1296
|
for (const n of document.styleSheets) {
|
|
1197
1297
|
if (n.ownerNode?.id === "twiddle-scoped") continue;
|
|
@@ -1201,19 +1301,19 @@ function Fa() {
|
|
|
1201
1301
|
} catch {
|
|
1202
1302
|
continue;
|
|
1203
1303
|
}
|
|
1204
|
-
for (const o of
|
|
1205
|
-
o.style.cssText.split(";").forEach((
|
|
1206
|
-
const
|
|
1207
|
-
|
|
1304
|
+
for (const o of _e(a))
|
|
1305
|
+
o.style.cssText.split(";").forEach((s) => {
|
|
1306
|
+
const r = s.slice(0, s.indexOf(":")).trim();
|
|
1307
|
+
r && !r.startsWith("--") && t(s);
|
|
1208
1308
|
});
|
|
1209
1309
|
}
|
|
1210
|
-
return document.querySelectorAll('[style*="var(--twd-"]').forEach((n) =>
|
|
1310
|
+
return document.querySelectorAll('[style*="var(--twd-"]').forEach((n) => t(n.getAttribute("style"))), e;
|
|
1211
1311
|
}
|
|
1212
|
-
const
|
|
1213
|
-
{ label: "With a build tool, in your entry file", code: `import '${
|
|
1312
|
+
const fn = [
|
|
1313
|
+
{ label: "With a build tool, in your entry file", code: `import '${un}/tokens.css';` },
|
|
1214
1314
|
// Pinned to this build's version: unversioned jsDelivr links can lag a release by days, and unpkg once failed for a fresh one.
|
|
1215
|
-
{ label: "Plain HTML, in <head>", code: `<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/${
|
|
1216
|
-
],
|
|
1315
|
+
{ label: "Plain HTML, in <head>", code: `<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/${un}@${Ua}/dist/twiddle-tokens.css">` }
|
|
1316
|
+
], Ka = `Convert this prototype's styles to use the Panel design tokens (the --twd-* CSS custom properties from twiddle-panel), so the panel can tweak them live.
|
|
1217
1317
|
|
|
1218
1318
|
1. Keep the current look. In a :root rule loaded after twiddle-tokens.css, set each token below to the value this project already uses (for example, if buttons use #0f766e, set --twd-primary: #0f766e). Only set the tokens the project actually needs.
|
|
1219
1319
|
2. Replace fixed values in component styles with var(--twd-…). Map each value to the closest token by role:
|
|
@@ -1231,205 +1331,205 @@ const sn = [
|
|
|
1231
1331
|
3. Leave values that don't match a role as they are (layout widths, one-off illustrations, icon sizes). Don't invent new --twd-* tokens.
|
|
1232
1332
|
4. Don't change the layout or how the page looks; after the change it should look the same until someone uses the panel.
|
|
1233
1333
|
5. When done, list the files you changed and any values you left fixed, with the reason.`;
|
|
1234
|
-
function
|
|
1235
|
-
const
|
|
1236
|
-
|
|
1334
|
+
function U(e) {
|
|
1335
|
+
const t = document.createElement("div");
|
|
1336
|
+
t.className = "field";
|
|
1237
1337
|
const n = document.createElement("div");
|
|
1238
1338
|
n.className = "field-head";
|
|
1239
1339
|
const a = document.createElement("span");
|
|
1240
|
-
a.className = "field-label", a.textContent =
|
|
1340
|
+
a.className = "field-label", a.textContent = e;
|
|
1241
1341
|
const o = document.createElement("span");
|
|
1242
|
-
return o.className = "field-value", o.hidden = !0, n.append(a, o),
|
|
1243
|
-
el:
|
|
1244
|
-
setValue(
|
|
1245
|
-
o.textContent =
|
|
1342
|
+
return o.className = "field-value", o.hidden = !0, n.append(a, o), t.append(n), {
|
|
1343
|
+
el: t,
|
|
1344
|
+
setValue(s) {
|
|
1345
|
+
o.textContent = s || "", o.hidden = !s;
|
|
1246
1346
|
},
|
|
1247
|
-
append(...
|
|
1248
|
-
|
|
1347
|
+
append(...s) {
|
|
1348
|
+
t.append(...s);
|
|
1249
1349
|
}
|
|
1250
1350
|
};
|
|
1251
1351
|
}
|
|
1252
|
-
function
|
|
1253
|
-
const
|
|
1254
|
-
|
|
1255
|
-
const
|
|
1256
|
-
const
|
|
1257
|
-
return
|
|
1352
|
+
function Wa({ label: e, ends: t, cols: n = 9, rows: a = 5, onInput: o }) {
|
|
1353
|
+
const s = document.createElement("div");
|
|
1354
|
+
s.className = "pad", s.tabIndex = 0, s.setAttribute("role", "group"), s.setAttribute("aria-roledescription", "2D slider");
|
|
1355
|
+
const r = (b, h) => {
|
|
1356
|
+
const y = document.createElement("span");
|
|
1357
|
+
return y.className = `pad-edge ${b}`, y.textContent = h, y.setAttribute("aria-hidden", "true"), y;
|
|
1258
1358
|
};
|
|
1259
|
-
|
|
1359
|
+
s.append(r("top", t.top), r("bottom", t.bottom), r("left", t.left), r("right", t.right));
|
|
1260
1360
|
const i = document.createElement("div");
|
|
1261
1361
|
i.className = "pad-area";
|
|
1262
1362
|
for (let b = 0; b < a; b++)
|
|
1263
|
-
for (let
|
|
1264
|
-
const
|
|
1265
|
-
|
|
1363
|
+
for (let h = 0; h < n; h++) {
|
|
1364
|
+
const y = document.createElement("i");
|
|
1365
|
+
y.className = "pad-dot", y.style.left = `${h / (n - 1) * 100}%`, y.style.top = `${b / (a - 1) * 100}%`, i.append(y);
|
|
1266
1366
|
}
|
|
1267
1367
|
const l = document.createElement("span");
|
|
1268
|
-
l.className = "pad-knob", i.append(l),
|
|
1269
|
-
let
|
|
1270
|
-
const
|
|
1271
|
-
function
|
|
1272
|
-
l.style.left = `${
|
|
1368
|
+
l.className = "pad-knob", i.append(l), s.append(i);
|
|
1369
|
+
let d = 0.5, p = 0.5;
|
|
1370
|
+
const u = (b, h) => Math.round(Math.min(1, Math.max(0, b)) * (h - 1)) / (h - 1);
|
|
1371
|
+
function v() {
|
|
1372
|
+
l.style.left = `${d * 100}%`, l.style.top = `${p * 100}%`;
|
|
1273
1373
|
}
|
|
1274
|
-
function f(b,
|
|
1275
|
-
b =
|
|
1374
|
+
function f(b, h, y) {
|
|
1375
|
+
b = y === "x" ? d : u(b, n), h = y === "y" ? p : u(h, a), !(b === d && h === p) && (d = b, p = h, v(), o(d, p));
|
|
1276
1376
|
}
|
|
1277
1377
|
const m = (b) => {
|
|
1278
|
-
const
|
|
1279
|
-
f((b.clientX -
|
|
1378
|
+
const h = i.getBoundingClientRect();
|
|
1379
|
+
f((b.clientX - h.left) / h.width, (b.clientY - h.top) / h.height);
|
|
1280
1380
|
};
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
}),
|
|
1284
|
-
|
|
1381
|
+
s.addEventListener("pointerdown", (b) => {
|
|
1382
|
+
s.setPointerCapture(b.pointerId), s.classList.add("dragging"), m(b);
|
|
1383
|
+
}), s.addEventListener("pointermove", (b) => {
|
|
1384
|
+
s.hasPointerCapture(b.pointerId) && m(b);
|
|
1285
1385
|
});
|
|
1286
1386
|
const x = (b) => {
|
|
1287
|
-
|
|
1387
|
+
s.hasPointerCapture(b.pointerId) && s.releasePointerCapture(b.pointerId), s.classList.remove("dragging");
|
|
1288
1388
|
};
|
|
1289
|
-
return
|
|
1290
|
-
const
|
|
1291
|
-
|
|
1389
|
+
return s.addEventListener("pointerup", x), s.addEventListener("pointercancel", x), s.addEventListener("keydown", (b) => {
|
|
1390
|
+
const h = { ArrowLeft: [-1, 0], ArrowRight: [1, 0], ArrowUp: [0, -1], ArrowDown: [0, 1] }[b.key];
|
|
1391
|
+
h && (b.preventDefault(), h[0] ? f(u(d, n) + h[0] / (n - 1), p, "y") : f(d, u(p, a) + h[1] / (a - 1), "x"));
|
|
1292
1392
|
}), {
|
|
1293
|
-
el:
|
|
1393
|
+
el: s,
|
|
1294
1394
|
/** Show a position without telling anyone (used to sync). Off-grid values are shown as they are. */
|
|
1295
|
-
setValue(b,
|
|
1296
|
-
|
|
1395
|
+
setValue(b, h, y) {
|
|
1396
|
+
d = Math.min(1, Math.max(0, b)), p = Math.min(1, Math.max(0, h)), v(), s.setAttribute("aria-label", y ? `${e}: ${y}` : e);
|
|
1297
1397
|
}
|
|
1298
1398
|
};
|
|
1299
1399
|
}
|
|
1300
|
-
function
|
|
1301
|
-
const
|
|
1302
|
-
|
|
1400
|
+
function Vn({ label: e, min: t, max: n, step: a = 1, format: o = String, onInput: s }) {
|
|
1401
|
+
const r = document.createElement("div");
|
|
1402
|
+
r.className = "slider";
|
|
1303
1403
|
const i = document.createElement("input");
|
|
1304
|
-
i.type = "range", i.min = String(
|
|
1404
|
+
i.type = "range", i.min = String(t), i.max = String(n), i.step = String(a), i.setAttribute("aria-label", e);
|
|
1305
1405
|
const l = document.createElement("span");
|
|
1306
|
-
l.className = "slider-value", l.setAttribute("aria-hidden", "true"),
|
|
1307
|
-
const
|
|
1308
|
-
const
|
|
1309
|
-
i.style.setProperty("--fill", `${
|
|
1406
|
+
l.className = "slider-value", l.setAttribute("aria-hidden", "true"), r.append(i, l);
|
|
1407
|
+
const d = (p) => {
|
|
1408
|
+
const u = (Number(i.value) - t) / (n - t) * 100;
|
|
1409
|
+
i.style.setProperty("--fill", `${u}%`), l.textContent = p ?? o(Number(i.value)), i.setAttribute("aria-valuetext", l.textContent);
|
|
1310
1410
|
};
|
|
1311
1411
|
return i.addEventListener("input", () => {
|
|
1312
|
-
|
|
1412
|
+
d(), s(Number(i.value));
|
|
1313
1413
|
}), {
|
|
1314
|
-
el:
|
|
1414
|
+
el: r,
|
|
1315
1415
|
/** Show a value without reporting it (sync after reset / URL load). */
|
|
1316
|
-
setValue(
|
|
1317
|
-
i.value = String(
|
|
1416
|
+
setValue(p, u) {
|
|
1417
|
+
i.value = String(p), d(u);
|
|
1318
1418
|
}
|
|
1319
1419
|
};
|
|
1320
1420
|
}
|
|
1321
|
-
function
|
|
1322
|
-
const
|
|
1323
|
-
|
|
1324
|
-
const
|
|
1325
|
-
const
|
|
1326
|
-
return
|
|
1421
|
+
function Ga({ label: e, options: t, columns: n = 3, className: a = "", onSelect: o }) {
|
|
1422
|
+
const s = document.createElement("div");
|
|
1423
|
+
s.className = `tiles ${a}`.trim(), s.style.setProperty("--cols", n), s.setAttribute("role", "radiogroup"), s.setAttribute("aria-label", e);
|
|
1424
|
+
const r = t.map((p) => {
|
|
1425
|
+
const u = document.createElement("button");
|
|
1426
|
+
return u.type = "button", u.className = "tile", p.wide && u.classList.add("wide"), u.setAttribute("role", "radio"), u.dataset.value = p.value, p.title && (u.title = p.title), u.innerHTML = `${p.preview ? `<span class="tile-preview" aria-hidden="true">${p.preview}</span>` : ""}<span class="tile-text"><span class="tile-label"></span>${p.sub ? '<span class="tile-sub"></span>' : ""}</span>`, u.querySelector(".tile-label").textContent = p.label, p.sub && (u.querySelector(".tile-sub").textContent = p.sub), u.addEventListener("click", () => d(p.value)), u;
|
|
1327
1427
|
});
|
|
1328
|
-
|
|
1428
|
+
s.append(...r);
|
|
1329
1429
|
let i = null;
|
|
1330
|
-
function l(
|
|
1331
|
-
i =
|
|
1332
|
-
const
|
|
1333
|
-
|
|
1334
|
-
|
|
1430
|
+
function l(p) {
|
|
1431
|
+
i = p, r.forEach((v) => v.setAttribute("aria-checked", String(v.dataset.value === p)));
|
|
1432
|
+
const u = Math.max(0, r.findIndex((v) => v.dataset.value === p));
|
|
1433
|
+
r.forEach((v, f) => {
|
|
1434
|
+
v.tabIndex = f === u ? 0 : -1;
|
|
1335
1435
|
});
|
|
1336
1436
|
}
|
|
1337
|
-
function p
|
|
1338
|
-
|
|
1437
|
+
function d(p, u) {
|
|
1438
|
+
p !== i && (l(p), o(p)), u && r.find((v) => v.dataset.value === p).focus();
|
|
1339
1439
|
}
|
|
1340
|
-
return
|
|
1341
|
-
const
|
|
1342
|
-
if (
|
|
1343
|
-
const
|
|
1440
|
+
return s.addEventListener("keydown", (p) => {
|
|
1441
|
+
const u = r.indexOf(p.target.closest(".tile"));
|
|
1442
|
+
if (u < 0) return;
|
|
1443
|
+
const v = { ArrowRight: 1, ArrowDown: 1, ArrowLeft: -1, ArrowUp: -1 }[p.key];
|
|
1344
1444
|
let f;
|
|
1345
|
-
if (
|
|
1346
|
-
else if (
|
|
1347
|
-
else if (
|
|
1445
|
+
if (v) f = (u + v + r.length) % r.length;
|
|
1446
|
+
else if (p.key === "Home") f = 0;
|
|
1447
|
+
else if (p.key === "End") f = r.length - 1;
|
|
1348
1448
|
else return;
|
|
1349
|
-
|
|
1350
|
-
}), l(null), { el:
|
|
1449
|
+
p.preventDefault(), d(r[f].dataset.value, !0);
|
|
1450
|
+
}), l(null), { el: s, setValue: l };
|
|
1351
1451
|
}
|
|
1352
|
-
const F = (
|
|
1353
|
-
function
|
|
1354
|
-
const n = /^(-?\d*\.?\d+)([a-z%]*)$/i.exec(String(
|
|
1452
|
+
const F = (e) => getComputedStyle(document.documentElement).getPropertyValue(e).trim();
|
|
1453
|
+
function Se(e, t) {
|
|
1454
|
+
const n = /^(-?\d*\.?\d+)([a-z%]*)$/i.exec(String(e).trim());
|
|
1355
1455
|
if (!n) return null;
|
|
1356
|
-
const a = parseFloat(n[1]) *
|
|
1456
|
+
const a = parseFloat(n[1]) * t, o = n[2] || "px";
|
|
1357
1457
|
return o === "px" ? `${Math.round(a)}px` : `${+a.toFixed(3)}${o}`;
|
|
1358
1458
|
}
|
|
1359
|
-
const
|
|
1360
|
-
function
|
|
1361
|
-
const
|
|
1459
|
+
const Ya = 999;
|
|
1460
|
+
function _n({ id: e, title: t, hint: n, token: a, uses: o = [], presets: s, max: r, extra: i = [], extraTokens: l = () => ({}), shape: d = "card" }) {
|
|
1461
|
+
const p = (f) => s.find((m) => m.value === f), u = (f) => f >= Ya ? "full" : `${f}px`, v = (f) => {
|
|
1362
1462
|
if (typeof f.radius == "number" && isFinite(f.radius))
|
|
1363
|
-
return { [a]: `${Math.min(Math.max(f.radius, 0),
|
|
1364
|
-
const m =
|
|
1463
|
+
return { [a]: `${Math.min(Math.max(f.radius, 0), r)}px` };
|
|
1464
|
+
const m = p(f.corners);
|
|
1365
1465
|
return m ? { [a]: `${m.radius}px` } : {};
|
|
1366
1466
|
};
|
|
1367
1467
|
return {
|
|
1368
|
-
id:
|
|
1369
|
-
title:
|
|
1468
|
+
id: e,
|
|
1469
|
+
title: t,
|
|
1370
1470
|
hint: n,
|
|
1371
|
-
presets:
|
|
1471
|
+
presets: s,
|
|
1372
1472
|
uses: [a, ...o],
|
|
1373
1473
|
tokens(f) {
|
|
1374
|
-
const m = f[
|
|
1375
|
-
return { ...
|
|
1474
|
+
const m = f[e] || {};
|
|
1475
|
+
return { ...v(m), ...l(m) };
|
|
1376
1476
|
},
|
|
1377
1477
|
// One render function per knob, so a layout can place them separately.
|
|
1378
1478
|
// Each takes an optional label (e.g. "Buttons" on the Corners page).
|
|
1379
1479
|
parts: {
|
|
1380
1480
|
corners(f, m, { label: x = "Corners" } = {}) {
|
|
1381
|
-
const b =
|
|
1382
|
-
label: `${
|
|
1383
|
-
options:
|
|
1384
|
-
onSelect: (E) => m.setSection(
|
|
1385
|
-
}),
|
|
1386
|
-
label: `${
|
|
1481
|
+
const b = U(x), h = ce({
|
|
1482
|
+
label: `${t} corners`,
|
|
1483
|
+
options: s.map((E) => ({ value: E.value, label: E.label, title: u(E.radius) })),
|
|
1484
|
+
onSelect: (E) => m.setSection(e, { corners: E, radius: null })
|
|
1485
|
+
}), y = Vn({
|
|
1486
|
+
label: `${t} corner radius`,
|
|
1387
1487
|
min: 0,
|
|
1388
|
-
max:
|
|
1389
|
-
format:
|
|
1390
|
-
onInput: (E) => m.setSection(
|
|
1488
|
+
max: r,
|
|
1489
|
+
format: u,
|
|
1490
|
+
onInput: (E) => m.setSection(e, { radius: E, corners: null })
|
|
1391
1491
|
});
|
|
1392
|
-
b.append(
|
|
1492
|
+
b.append(h.el, y.el), f.append(b.el);
|
|
1393
1493
|
const L = () => {
|
|
1394
|
-
const E = parseFloat((m.readToken || F)(a)),
|
|
1395
|
-
if (
|
|
1396
|
-
|
|
1494
|
+
const E = parseFloat((m.readToken || F)(a)), ee = m.getState()[e] || {}, K = p(ee.corners) || s.find((W) => W.radius === E);
|
|
1495
|
+
if (h.setValue(K ? K.value : null), isNaN(E)) {
|
|
1496
|
+
y.setValue(0, "—"), b.setValue("");
|
|
1397
1497
|
return;
|
|
1398
1498
|
}
|
|
1399
|
-
|
|
1499
|
+
y.setValue(Math.min(E, r), u(E)), b.setValue(u(E));
|
|
1400
1500
|
};
|
|
1401
1501
|
L(), m.onChange(L);
|
|
1402
1502
|
},
|
|
1403
|
-
...Object.fromEntries(i.map((f) => [f.key, (m, x, { label: b } = {}) => m.append(
|
|
1503
|
+
...Object.fromEntries(i.map((f) => [f.key, (m, x, { label: b } = {}) => m.append($e(x, { section: e, ...f, label: b || f.label }))])),
|
|
1404
1504
|
// Tabbed layout: presets as tiles showing the real rounding.
|
|
1405
1505
|
cornerTiles(f, m, { label: x = "Corners" } = {}) {
|
|
1406
|
-
const b = (
|
|
1506
|
+
const b = (h) => `<span class="shape shape-${d}" style="border-radius:${Math.min(h, d === "button" ? 12 : 16)}px"></span>`;
|
|
1407
1507
|
f.append(V(m, {
|
|
1408
|
-
section:
|
|
1508
|
+
section: e,
|
|
1409
1509
|
key: "corners",
|
|
1410
1510
|
label: x,
|
|
1411
|
-
columns:
|
|
1412
|
-
options:
|
|
1413
|
-
current: (
|
|
1414
|
-
const
|
|
1415
|
-
if (
|
|
1511
|
+
columns: s.length,
|
|
1512
|
+
options: s.map((h) => ({ value: h.value, label: h.label, title: u(h.radius), preview: b(h.radius) })),
|
|
1513
|
+
current: (h) => {
|
|
1514
|
+
const y = h[e] || {};
|
|
1515
|
+
if (y.radius != null) return null;
|
|
1416
1516
|
const L = parseFloat((m.readToken || F)(a));
|
|
1417
|
-
return (
|
|
1517
|
+
return (p(y.corners) || s.find((E) => E.radius === L))?.value || null;
|
|
1418
1518
|
},
|
|
1419
|
-
select: (
|
|
1519
|
+
select: (h) => m.setSection(e, { corners: h, radius: null }),
|
|
1420
1520
|
valueText: () => {
|
|
1421
|
-
const
|
|
1422
|
-
return isNaN(
|
|
1521
|
+
const h = parseFloat((m.readToken || F)(a));
|
|
1522
|
+
return isNaN(h) ? "" : u(h);
|
|
1423
1523
|
}
|
|
1424
1524
|
}));
|
|
1425
1525
|
},
|
|
1426
1526
|
...Object.fromEntries(i.filter((f) => f.preview).map((f) => [`${f.key}Tiles`, (m, x, { label: b } = {}) => m.append(V(x, {
|
|
1427
|
-
section:
|
|
1527
|
+
section: e,
|
|
1428
1528
|
key: f.key,
|
|
1429
1529
|
label: b || f.label,
|
|
1430
1530
|
fallback: f.fallback,
|
|
1431
1531
|
columns: f.columns || f.options.length,
|
|
1432
|
-
options: f.options.map((
|
|
1532
|
+
options: f.options.map((h) => ({ ...h, preview: f.preview(h.value) }))
|
|
1433
1533
|
}))]))
|
|
1434
1534
|
},
|
|
1435
1535
|
render(f, m) {
|
|
@@ -1437,50 +1537,50 @@ function zn({ id: t, title: e, hint: n, token: a, uses: o = [], presets: r, max:
|
|
|
1437
1537
|
}
|
|
1438
1538
|
};
|
|
1439
1539
|
}
|
|
1440
|
-
function
|
|
1441
|
-
const i =
|
|
1540
|
+
function $e(e, { section: t, key: n, label: a, options: o, fallback: s, valueText: r }) {
|
|
1541
|
+
const i = U(a), l = ce({
|
|
1442
1542
|
label: a,
|
|
1443
1543
|
options: o,
|
|
1444
|
-
onSelect: (
|
|
1544
|
+
onSelect: (p) => e.setSection(t, { [n]: p })
|
|
1445
1545
|
});
|
|
1446
1546
|
i.append(l.el);
|
|
1447
|
-
const
|
|
1448
|
-
const
|
|
1449
|
-
l.setValue(
|
|
1547
|
+
const d = () => {
|
|
1548
|
+
const p = (e.getState()[t] || {})[n] || s;
|
|
1549
|
+
l.setValue(p), i.setValue(r ? r(p) : "");
|
|
1450
1550
|
};
|
|
1451
|
-
return
|
|
1452
|
-
}
|
|
1453
|
-
function
|
|
1454
|
-
const l =
|
|
1455
|
-
l.append(
|
|
1456
|
-
const
|
|
1457
|
-
const
|
|
1458
|
-
|
|
1551
|
+
return d(), e.onChange(d), i.el;
|
|
1552
|
+
}
|
|
1553
|
+
function Xa(e, { section: t, key: n, label: a, min: o, max: s, fallback: r, format: i }) {
|
|
1554
|
+
const l = U(a), d = Vn({ label: a, min: o, max: s, format: i, onInput: (u) => e.setSection(t, { [n]: u }) });
|
|
1555
|
+
l.append(d.el);
|
|
1556
|
+
const p = () => {
|
|
1557
|
+
const u = (e.getState()[t] || {})[n];
|
|
1558
|
+
d.setValue(typeof u == "number" ? u : r);
|
|
1459
1559
|
};
|
|
1460
|
-
return
|
|
1560
|
+
return p(), e.onChange(p), l.el;
|
|
1461
1561
|
}
|
|
1462
|
-
function V(
|
|
1463
|
-
const
|
|
1562
|
+
function V(e, { section: t, key: n, label: a, options: o, columns: s = 3, className: r = "", fallback: i, valueText: l, current: d, select: p }) {
|
|
1563
|
+
const u = U(a), v = Ga({
|
|
1464
1564
|
label: a,
|
|
1465
1565
|
options: o,
|
|
1466
|
-
columns:
|
|
1467
|
-
className:
|
|
1468
|
-
onSelect: (m) =>
|
|
1566
|
+
columns: s,
|
|
1567
|
+
className: r,
|
|
1568
|
+
onSelect: (m) => p ? p(m) : e.setSection(t, { [n]: m })
|
|
1469
1569
|
});
|
|
1470
|
-
|
|
1570
|
+
u.append(v.el);
|
|
1471
1571
|
const f = () => {
|
|
1472
|
-
const m =
|
|
1473
|
-
|
|
1572
|
+
const m = e.getState(), x = d ? d(m) : (m[t] || {})[n] || i || null;
|
|
1573
|
+
v.setValue(x), u.setValue(l ? l(x, m) : "");
|
|
1474
1574
|
};
|
|
1475
|
-
return f(),
|
|
1575
|
+
return f(), e.onChange(f), u.el;
|
|
1476
1576
|
}
|
|
1477
|
-
const
|
|
1577
|
+
const gn = (e, t) => t.map((n) => `${e}${n}`), Kn = {
|
|
1478
1578
|
fonts: { label: "Tokens", tokens: ["font-heading", "font-body"], kind: "text" },
|
|
1479
|
-
type: { label: "Tokens", tokens:
|
|
1579
|
+
type: { label: "Tokens", tokens: gn("text-", ["xs", "sm", "base", "lg", "xl", "2xl", "3xl"]), kind: "length" },
|
|
1480
1580
|
leading: { label: "Line height", tokens: ["leading-tight", "leading-normal"], kind: "number" },
|
|
1481
1581
|
weights: { label: "Weights", tokens: ["weight-heading", "weight-strong"], kind: "number" },
|
|
1482
1582
|
colours: { label: "Tokens", tokens: ["bg", "surface", "surface-raised", "text", "text-muted", "border", "primary", "primary-hover", "on-primary", "focus", "success", "warning", "danger"], kind: "colour" },
|
|
1483
|
-
spacing: { label: "Steps", tokens:
|
|
1583
|
+
spacing: { label: "Steps", tokens: gn("space-", [1, 2, 3, 4, 5, 6, 7, 8]), kind: "length", bar: 64 },
|
|
1484
1584
|
controls: { label: "Controls", tokens: ["control-height", "control-pad-x"], kind: "length" },
|
|
1485
1585
|
corners: { label: "Tokens", tokens: ["radius-control", "radius-container"], kind: "length" },
|
|
1486
1586
|
borders: { label: "Tokens", tokens: ["border-width", "border-width-container"], kind: "length" },
|
|
@@ -1488,124 +1588,124 @@ const ln = (t, e) => e.map((n) => `${t}${n}`), In = {
|
|
|
1488
1588
|
buttonStyle: { label: "Button", tokens: ["button-bg", "button-fg", "button-border", "button-hover"], kind: "colour" },
|
|
1489
1589
|
cardStyle: { label: "Card", tokens: ["card-bg", "card-border"], kind: "colour" },
|
|
1490
1590
|
cardShadow: { label: "Card shadow", tokens: ["card-shadow"], kind: "text" }
|
|
1491
|
-
}, _ = (
|
|
1492
|
-
function
|
|
1493
|
-
const n = /^(-?\d*\.?\d+)(px|rem)?$/.exec(String(
|
|
1494
|
-
return !n ||
|
|
1591
|
+
}, _ = (e) => `--twd-${e}`, at = (e, t) => Object.prototype.hasOwnProperty.call(e.getState().custom || {}, t), mn = (e, t) => Kn[t].tokens.some((n) => (e.custom || {})[n] != null);
|
|
1592
|
+
function Za(e, t) {
|
|
1593
|
+
const n = /^(-?\d*\.?\d+)(px|rem)?$/.exec(String(e).trim());
|
|
1594
|
+
return !n || t === "text" || t === "colour" ? null : n[2] === "rem" ? { n: +(parseFloat(n[1]) * 16).toFixed(1), unit: "px" } : { n: +parseFloat(n[1]).toFixed(3), unit: n[2] || (t === "length" ? "px" : "") };
|
|
1495
1595
|
}
|
|
1496
|
-
function
|
|
1497
|
-
const n =
|
|
1498
|
-
return n ?
|
|
1596
|
+
function Ja(e, t) {
|
|
1597
|
+
const n = e.trim();
|
|
1598
|
+
return n ? t === "length" && /^-?\d*\.?\d+$/.test(n) ? `${n}px` : n : null;
|
|
1499
1599
|
}
|
|
1500
|
-
function
|
|
1600
|
+
function Qa(e, t, n, a) {
|
|
1501
1601
|
const o = document.createElement("div");
|
|
1502
1602
|
o.className = `token-row kind-${n}`, o.innerHTML = `
|
|
1503
|
-
<code class="token-name" title="${_(
|
|
1603
|
+
<code class="token-name" title="${_(t)}">${t}</code>
|
|
1504
1604
|
${a ? '<span class="token-bar"><span></span></span>' : '<span class="token-gap"></span>'}
|
|
1505
1605
|
<span class="custom-tag" hidden>Custom</span>
|
|
1506
1606
|
<label class="token-value">
|
|
1507
1607
|
${n === "colour" ? '<span class="token-swatch"></span>' : ""}
|
|
1508
|
-
<input type="text" spellcheck="false" aria-label="${_(
|
|
1608
|
+
<input type="text" spellcheck="false" aria-label="${_(t)}" ${n === "length" || n === "number" ? 'inputmode="decimal"' : ""}>
|
|
1509
1609
|
<span class="token-unit"></span>
|
|
1510
1610
|
</label>
|
|
1511
|
-
<button class="icon-btn token-reset" title="Back to the page’s value" aria-label="Reset ${_(
|
|
1611
|
+
<button class="icon-btn token-reset" title="Back to the page’s value" aria-label="Reset ${_(t)}" hidden>${A.reset}</button>
|
|
1512
1612
|
`;
|
|
1513
|
-
const
|
|
1514
|
-
let
|
|
1515
|
-
const
|
|
1516
|
-
let m =
|
|
1517
|
-
|
|
1518
|
-
const x =
|
|
1613
|
+
const s = o.querySelector("input"), r = o.querySelector(".token-unit"), i = o.querySelector(".token-bar span"), l = o.querySelector(".token-swatch"), d = o.querySelector(".custom-tag"), p = o.querySelector(".token-reset");
|
|
1614
|
+
let u = "";
|
|
1615
|
+
const v = () => {
|
|
1616
|
+
let m = s.value;
|
|
1617
|
+
u && /^-?\d*\.?\d+$/.test(m.trim()) && (m = `${m.trim()}${u}`);
|
|
1618
|
+
const x = Ja(m, n), b = F(_(t));
|
|
1519
1619
|
if (x === null) {
|
|
1520
|
-
|
|
1620
|
+
at(e, t) ? e.setSection("custom", { [t]: null }) : f();
|
|
1521
1621
|
return;
|
|
1522
1622
|
}
|
|
1523
|
-
if (x === b && !
|
|
1524
|
-
|
|
1623
|
+
if (x === b && !at(e, t)) return f();
|
|
1624
|
+
e.setSection("custom", { [t]: x });
|
|
1525
1625
|
};
|
|
1526
|
-
|
|
1527
|
-
m.key === "Enter" && (m.preventDefault(),
|
|
1528
|
-
}),
|
|
1626
|
+
s.addEventListener("change", v), s.addEventListener("blur", () => f()), s.addEventListener("keydown", (m) => {
|
|
1627
|
+
m.key === "Enter" && (m.preventDefault(), s.blur()), m.key === "Escape" && (m.stopPropagation(), f(), s.blur());
|
|
1628
|
+
}), p.addEventListener("click", () => e.setSection("custom", { [t]: null }));
|
|
1529
1629
|
function f() {
|
|
1530
|
-
const m = F(_(
|
|
1531
|
-
|
|
1630
|
+
const m = F(_(t)), x = at(e, t), b = Za(m, n);
|
|
1631
|
+
u = b ? b.unit : "", document.activeElement !== s && o.getRootNode().activeElement !== s && (s.value = b ? String(b.n) : m), s.title = m, r.textContent = u, r.hidden = !u, i && (i.style.width = `${Math.max(2, Math.min(100, (b ? b.n : 0) / a * 100))}%`), l && (l.style.background = m), o.classList.toggle("edited", x), d.hidden = !x, p.hidden = !x;
|
|
1532
1632
|
}
|
|
1533
|
-
return f(),
|
|
1633
|
+
return f(), e.onChange(f), o;
|
|
1534
1634
|
}
|
|
1535
|
-
const
|
|
1635
|
+
const eo = {
|
|
1536
1636
|
id: "custom",
|
|
1537
1637
|
title: "Custom tokens",
|
|
1538
1638
|
hint: "Any token, set to a fixed value",
|
|
1539
1639
|
uses: [],
|
|
1540
1640
|
// not offered for a single picked element (yet)
|
|
1541
|
-
tokens(
|
|
1542
|
-
return Object.fromEntries(Object.entries(
|
|
1641
|
+
tokens(e) {
|
|
1642
|
+
return Object.fromEntries(Object.entries(e.custom || {}).map(([t, n]) => [_(t), n]));
|
|
1543
1643
|
},
|
|
1544
|
-
describe: (
|
|
1644
|
+
describe: (e, t) => ({ label: _(e), value: String(t) }),
|
|
1545
1645
|
// One part per Edit page group, e.g. parts.spacing → rows for --twd-space-1 … 8.
|
|
1546
|
-
parts: Object.fromEntries(Object.entries(
|
|
1547
|
-
const
|
|
1548
|
-
|
|
1549
|
-
const
|
|
1550
|
-
|
|
1646
|
+
parts: Object.fromEntries(Object.entries(Kn).map(([e, t]) => [e, (n, a, { label: o = t.label } = {}) => {
|
|
1647
|
+
const s = U(o);
|
|
1648
|
+
s.el.classList.add("token-field");
|
|
1649
|
+
const r = t.tokens[0], i = t.tokens[t.tokens.length - 1];
|
|
1650
|
+
s.setValue(t.tokens.length > 2 && /-\w+$/.test(r) ? `${_(r)} … ${i.split("-").pop()}` : "");
|
|
1551
1651
|
const l = document.createElement("div");
|
|
1552
|
-
l.className = "token-rows",
|
|
1652
|
+
l.className = "token-rows", t.tokens.forEach((d) => l.append(Qa(a, d, t.kind, t.bar))), s.append(l), n.append(s.el);
|
|
1553
1653
|
}]))
|
|
1554
|
-
},
|
|
1555
|
-
function
|
|
1556
|
-
return typeof
|
|
1654
|
+
}, ve = { compact: 0.75, regular: 1, comfy: 1.25 }, kt = [0.75, 1.25], bn = [1, 2, 3, 4, 5, 6, 7, 8].map((e) => `--twd-space-${e}`);
|
|
1655
|
+
function se(e = {}) {
|
|
1656
|
+
return typeof e.factor == "number" && isFinite(e.factor) ? Math.min(kt[1], Math.max(kt[0], e.factor)) : ve[e.spacing] || 1;
|
|
1557
1657
|
}
|
|
1558
|
-
const
|
|
1658
|
+
const ot = (e) => `${Math.round(e * 100)}%`, to = {
|
|
1559
1659
|
id: "density",
|
|
1560
1660
|
title: "Density",
|
|
1561
1661
|
hint: "Compact · regular · comfortable",
|
|
1562
|
-
uses:
|
|
1563
|
-
tokens(
|
|
1564
|
-
const
|
|
1565
|
-
if (
|
|
1662
|
+
uses: bn,
|
|
1663
|
+
tokens(e) {
|
|
1664
|
+
const t = se(e.density);
|
|
1665
|
+
if (t === 1) return {};
|
|
1566
1666
|
const n = {};
|
|
1567
|
-
return
|
|
1568
|
-
const o =
|
|
1667
|
+
return bn.forEach((a) => {
|
|
1668
|
+
const o = Se(F(a), t);
|
|
1569
1669
|
o && (n[a] = o);
|
|
1570
1670
|
}), n;
|
|
1571
1671
|
},
|
|
1572
1672
|
/** How the Changes list and prompt describe a setting. */
|
|
1573
|
-
describe: (
|
|
1673
|
+
describe: (e, t) => e === "factor" ? { label: "Spacing", value: ot(t) } : null,
|
|
1574
1674
|
parts: {
|
|
1575
1675
|
// Tabbed layout: presets as tiles with a tiny layout sketch.
|
|
1576
|
-
spacingTiles(
|
|
1577
|
-
const a = (o,
|
|
1578
|
-
|
|
1676
|
+
spacingTiles(e, t, { label: n = "Density" } = {}) {
|
|
1677
|
+
const a = (o, s) => `<span class="sketch" style="padding:${s}px;gap:${o}px"><i></i><i></i><i></i></span>`;
|
|
1678
|
+
e.append(V(t, {
|
|
1579
1679
|
section: "density",
|
|
1580
1680
|
key: "spacing",
|
|
1581
1681
|
label: n,
|
|
1582
1682
|
options: [{ value: "compact", label: "Compact", preview: a(2, 5) }, { value: "regular", label: "Regular", preview: a(4, 8) }, { value: "comfy", label: "Comfy", preview: a(6, 11) }],
|
|
1583
1683
|
current: (o) => {
|
|
1584
|
-
const
|
|
1585
|
-
return Object.keys(
|
|
1684
|
+
const s = se(o.density);
|
|
1685
|
+
return Object.keys(ve).find((r) => ve[r] === s) || null;
|
|
1586
1686
|
},
|
|
1587
|
-
select: (o) =>
|
|
1588
|
-
valueText: (o,
|
|
1687
|
+
select: (o) => t.setSection("density", { spacing: o, factor: null }),
|
|
1688
|
+
valueText: (o, s) => mn(s, "spacing") ? "Custom" : o ? "" : ot(se(s.density))
|
|
1589
1689
|
}));
|
|
1590
1690
|
},
|
|
1591
|
-
spacing(
|
|
1592
|
-
const a =
|
|
1691
|
+
spacing(e, t, { label: n = "Spacing" } = {}) {
|
|
1692
|
+
const a = U(n), o = ce({
|
|
1593
1693
|
label: n,
|
|
1594
1694
|
options: [{ value: "compact", label: "Compact" }, { value: "regular", label: "Regular" }, { value: "comfy", label: "Comfy" }],
|
|
1595
|
-
onSelect: (
|
|
1695
|
+
onSelect: (r) => t.setSection("density", { spacing: r, factor: null })
|
|
1596
1696
|
});
|
|
1597
1697
|
a.append(o.el);
|
|
1598
|
-
const
|
|
1599
|
-
const
|
|
1600
|
-
o.setValue(l || null), a.setValue(
|
|
1698
|
+
const s = () => {
|
|
1699
|
+
const r = t.getState().density || {}, i = se(r), l = Object.keys(ve).find((d) => ve[d] === i);
|
|
1700
|
+
o.setValue(l || null), a.setValue(mn(t.getState(), "spacing") ? "Custom" : l ? "" : ot(i));
|
|
1601
1701
|
};
|
|
1602
|
-
|
|
1702
|
+
s(), t.onChange(s), e.append(a.el);
|
|
1603
1703
|
}
|
|
1604
1704
|
},
|
|
1605
|
-
render(
|
|
1606
|
-
Object.entries(this.parts).filter(([n]) => !n.endsWith("Tiles")).forEach(([, n]) => n(
|
|
1705
|
+
render(e, t) {
|
|
1706
|
+
Object.entries(this.parts).filter(([n]) => !n.endsWith("Tiles")).forEach(([, n]) => n(e, t));
|
|
1607
1707
|
}
|
|
1608
|
-
},
|
|
1708
|
+
}, no = '<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M14 6l4 4M4 20l1-4 9.5-9.5a2.1 2.1 0 013 3L8 19l-4 1z"/></svg>', ao = [
|
|
1609
1709
|
[50, "white", 10],
|
|
1610
1710
|
[100, "white", 20],
|
|
1611
1711
|
[200, "white", 36],
|
|
@@ -1617,35 +1717,35 @@ const et = (t) => `${Math.round(t * 100)}%`, Va = {
|
|
|
1617
1717
|
[800, "black", 58],
|
|
1618
1718
|
[900, "black", 45],
|
|
1619
1719
|
[950, "black", 30]
|
|
1620
|
-
],
|
|
1621
|
-
let
|
|
1622
|
-
return /^[0-9a-f]{3}$/i.test(
|
|
1720
|
+
], rt = (e) => {
|
|
1721
|
+
let t = String(e).trim().replace(/^#/, "");
|
|
1722
|
+
return /^[0-9a-f]{3}$/i.test(t) && (t = t.split("").map((n) => n + n).join("")), /^[0-9a-f]{6}$/i.test(t) ? `#${t.toUpperCase()}` : null;
|
|
1623
1723
|
};
|
|
1624
|
-
function
|
|
1724
|
+
function oo({ label: e, onInput: t }) {
|
|
1625
1725
|
const n = document.createElement("div");
|
|
1626
1726
|
n.className = "colour";
|
|
1627
1727
|
const a = document.createElement("div");
|
|
1628
1728
|
a.className = "colour-field";
|
|
1629
1729
|
const o = document.createElement("input");
|
|
1630
1730
|
o.type = "color", o.className = "colour-picker", o.tabIndex = -1, o.setAttribute("aria-hidden", "true");
|
|
1631
|
-
const
|
|
1632
|
-
|
|
1633
|
-
const
|
|
1634
|
-
|
|
1731
|
+
const s = document.createElement("button");
|
|
1732
|
+
s.type = "button", s.className = "colour-swatch", s.setAttribute("aria-label", `${e}: open colour picker`);
|
|
1733
|
+
const r = document.createElement("input");
|
|
1734
|
+
r.type = "text", r.className = "colour-hex", r.spellcheck = !1, r.maxLength = 7, r.setAttribute("aria-label", `${e} hex value`);
|
|
1635
1735
|
const i = document.createElement("button");
|
|
1636
|
-
i.type = "button", i.className = "colour-pipette", i.innerHTML =
|
|
1736
|
+
i.type = "button", i.className = "colour-pipette", i.innerHTML = no, i.title = "Pick a colour from the screen", i.setAttribute("aria-label", "Pick a colour from the screen"), a.append(o, s, r, i);
|
|
1637
1737
|
const l = document.createElement("div");
|
|
1638
|
-
l.className = "ramp", l.setAttribute("aria-hidden", "true"), l.innerHTML =
|
|
1738
|
+
l.className = "ramp", l.setAttribute("aria-hidden", "true"), l.innerHTML = ao.map(([f, m, x]) => {
|
|
1639
1739
|
const b = m ? `color-mix(in oklch, var(--brand) ${x}%, ${m})` : "var(--brand)";
|
|
1640
1740
|
return `<span title="${f}" style="background:${b}"></span>`;
|
|
1641
1741
|
}).join(""), n.append(a, l);
|
|
1642
|
-
let
|
|
1643
|
-
const
|
|
1644
|
-
|
|
1645
|
-
},
|
|
1646
|
-
f !==
|
|
1742
|
+
let d = "#000000";
|
|
1743
|
+
const p = (f) => {
|
|
1744
|
+
d = f, o.value = f.toLowerCase(), s.style.background = f, n.getRootNode().activeElement !== r && (r.value = f), n.style.setProperty("--brand", f);
|
|
1745
|
+
}, u = (f) => {
|
|
1746
|
+
f !== d && (p(f), t(f));
|
|
1647
1747
|
};
|
|
1648
|
-
|
|
1748
|
+
s.addEventListener("click", () => {
|
|
1649
1749
|
if (o.showPicker)
|
|
1650
1750
|
try {
|
|
1651
1751
|
o.showPicker();
|
|
@@ -1653,64 +1753,64 @@ function Ka({ label: t, onInput: e }) {
|
|
|
1653
1753
|
} catch {
|
|
1654
1754
|
}
|
|
1655
1755
|
o.click();
|
|
1656
|
-
}), o.addEventListener("input", () =>
|
|
1657
|
-
const f =
|
|
1658
|
-
f &&
|
|
1756
|
+
}), o.addEventListener("input", () => u(o.value.toUpperCase())), r.addEventListener("input", () => {
|
|
1757
|
+
const f = rt(r.value);
|
|
1758
|
+
f && u(f);
|
|
1659
1759
|
});
|
|
1660
|
-
const
|
|
1661
|
-
|
|
1760
|
+
const v = () => {
|
|
1761
|
+
r.value = d;
|
|
1662
1762
|
};
|
|
1663
|
-
return
|
|
1664
|
-
f.key === "Enter" && (
|
|
1763
|
+
return r.addEventListener("blur", v), r.addEventListener("keydown", (f) => {
|
|
1764
|
+
f.key === "Enter" && (v(), r.select());
|
|
1665
1765
|
}), i.addEventListener("click", async () => {
|
|
1666
1766
|
if ("EyeDropper" in window)
|
|
1667
1767
|
try {
|
|
1668
|
-
const { sRGBHex: f } = await new window.EyeDropper().open(), m =
|
|
1669
|
-
m &&
|
|
1768
|
+
const { sRGBHex: f } = await new window.EyeDropper().open(), m = rt(f);
|
|
1769
|
+
m && u(m);
|
|
1670
1770
|
} catch {
|
|
1671
1771
|
}
|
|
1672
1772
|
else
|
|
1673
|
-
|
|
1674
|
-
}), { el: n, setValue: (f) =>
|
|
1773
|
+
s.click();
|
|
1774
|
+
}), { el: n, setValue: (f) => p(rt(f) || d) };
|
|
1675
1775
|
}
|
|
1676
|
-
function
|
|
1776
|
+
function ro({ label: e, options: t, onSelect: n }) {
|
|
1677
1777
|
const a = document.createElement("div");
|
|
1678
|
-
a.className = "swatches", a.setAttribute("role", "radiogroup"), a.setAttribute("aria-label",
|
|
1679
|
-
const o =
|
|
1680
|
-
const
|
|
1681
|
-
return
|
|
1778
|
+
a.className = "swatches", a.setAttribute("role", "radiogroup"), a.setAttribute("aria-label", e);
|
|
1779
|
+
const o = t.map((l) => {
|
|
1780
|
+
const d = document.createElement("button");
|
|
1781
|
+
return d.type = "button", d.className = "swatch", d.setAttribute("role", "radio"), d.dataset.value = l.value, d.innerHTML = `<span class="swatch-ring"><span class="swatch-dot" style="background:${l.colour}"></span></span><span class="swatch-label">${l.label}</span>`, d.addEventListener("click", () => i(l.value)), d;
|
|
1682
1782
|
});
|
|
1683
1783
|
a.append(...o);
|
|
1684
|
-
let
|
|
1685
|
-
function
|
|
1686
|
-
|
|
1687
|
-
const
|
|
1688
|
-
o.forEach((
|
|
1689
|
-
|
|
1784
|
+
let s = null;
|
|
1785
|
+
function r(l) {
|
|
1786
|
+
s = l, o.forEach((p) => p.setAttribute("aria-checked", String(p.dataset.value === l)));
|
|
1787
|
+
const d = Math.max(0, o.findIndex((p) => p.dataset.value === l));
|
|
1788
|
+
o.forEach((p, u) => {
|
|
1789
|
+
p.tabIndex = u === d ? 0 : -1;
|
|
1690
1790
|
});
|
|
1691
1791
|
}
|
|
1692
|
-
function i(l,
|
|
1693
|
-
l !==
|
|
1792
|
+
function i(l, d) {
|
|
1793
|
+
l !== s && (r(l), n(l)), d && o.find((p) => p.dataset.value === l).focus();
|
|
1694
1794
|
}
|
|
1695
1795
|
return a.addEventListener("keydown", (l) => {
|
|
1696
|
-
const
|
|
1697
|
-
if (
|
|
1698
|
-
const
|
|
1699
|
-
let
|
|
1700
|
-
if (
|
|
1701
|
-
else if (l.key === "Home")
|
|
1702
|
-
else if (l.key === "End")
|
|
1796
|
+
const d = o.indexOf(l.target.closest(".swatch"));
|
|
1797
|
+
if (d < 0) return;
|
|
1798
|
+
const p = { ArrowRight: 1, ArrowDown: 1, ArrowLeft: -1, ArrowUp: -1 }[l.key];
|
|
1799
|
+
let u;
|
|
1800
|
+
if (p) u = (d + p + o.length) % o.length;
|
|
1801
|
+
else if (l.key === "Home") u = 0;
|
|
1802
|
+
else if (l.key === "End") u = o.length - 1;
|
|
1703
1803
|
else return;
|
|
1704
|
-
l.preventDefault(), i(o[
|
|
1705
|
-
}),
|
|
1706
|
-
}
|
|
1707
|
-
const
|
|
1708
|
-
slate:
|
|
1709
|
-
gray:
|
|
1710
|
-
zinc:
|
|
1711
|
-
neutral:
|
|
1712
|
-
stone:
|
|
1713
|
-
}, N = (
|
|
1804
|
+
l.preventDefault(), i(o[u].dataset.value, !0);
|
|
1805
|
+
}), r(null), { el: a, setValue: r };
|
|
1806
|
+
}
|
|
1807
|
+
const so = [50, 100, 200, 300, 400, 500, 600, 700, 800, 900, 950], xe = (e) => Object.fromEntries(e.split(" ").map((t, n) => [so[n], `#${t}`])), st = {
|
|
1808
|
+
slate: xe("f8fafc f1f5f9 e2e8f0 cbd5e1 94a3b8 64748b 475569 334155 1e293b 0f172a 020617"),
|
|
1809
|
+
gray: xe("f9fafb f3f4f6 e5e7eb d1d5db 9ca3af 6b7280 4b5563 374151 1f2937 111827 030712"),
|
|
1810
|
+
zinc: xe("fafafa f4f4f5 e4e4e7 d4d4d8 a1a1aa 71717a 52525b 3f3f46 27272a 18181b 09090b"),
|
|
1811
|
+
neutral: xe("fafafa f5f5f5 e5e5e5 d4d4d4 a3a3a3 737373 525252 404040 262626 171717 0a0a0a"),
|
|
1812
|
+
stone: xe("fafaf9 f5f5f4 e7e5e4 d6d3d1 a8a29e 78716c 57534e 44403c 292524 1c1917 0c0a09")
|
|
1813
|
+
}, N = (e) => Object.fromEntries(e.split(" ").map((t, n) => [400 + 100 * n, `#${t}`])), it = {
|
|
1714
1814
|
red: N("f87171 ef4444 dc2626 b91c1c 991b1b"),
|
|
1715
1815
|
orange: N("fb923c f97316 ea580c c2410c 9a3412"),
|
|
1716
1816
|
amber: N("fbbf24 f59e0b d97706 b45309 92400e"),
|
|
@@ -1728,17 +1828,17 @@ const Wa = [50, 100, 200, 300, 400, 500, 600, 700, 800, 900, 950], me = (t) => O
|
|
|
1728
1828
|
fuchsia: N("e879f9 d946ef c026d3 a21caf 86198f"),
|
|
1729
1829
|
pink: N("f472b6 ec4899 db2777 be185d 9d174d"),
|
|
1730
1830
|
rose: N("fb7185 f43f5e e11d48 be123c 9f1239")
|
|
1731
|
-
},
|
|
1831
|
+
}, Be = [
|
|
1732
1832
|
{ value: "indigo-zinc", label: "Indigo · Zinc", primary: "indigo", neutral: "zinc" },
|
|
1733
1833
|
{ value: "emerald-stone", label: "Emerald · Stone", primary: "emerald", neutral: "stone" },
|
|
1734
1834
|
{ value: "rose-slate", label: "Rose · Slate", primary: "rose", neutral: "slate" },
|
|
1735
1835
|
{ value: "amber-neutral", label: "Amber · Neutral", primary: "amber", neutral: "neutral" }
|
|
1736
|
-
],
|
|
1836
|
+
], xn = "#4F46E5", io = "#A0743C", lo = [
|
|
1737
1837
|
// Figma Swatch row sample colours
|
|
1738
1838
|
{ value: "pure", label: "Pure", colour: "#8E8E93" },
|
|
1739
1839
|
{ value: "subtle", label: "Subtle", colour: "#8A89A6" },
|
|
1740
1840
|
{ value: "warm", label: "Warm", colour: "#9C9389" }
|
|
1741
|
-
],
|
|
1841
|
+
], wn = ["bg", "surface", "surface-raised", "text", "text-muted", "border"], co = {
|
|
1742
1842
|
// proposal
|
|
1743
1843
|
bg: "#0F0F11",
|
|
1744
1844
|
surface: "#18181B",
|
|
@@ -1746,118 +1846,118 @@ const Wa = [50, 100, 200, 300, 400, 500, 600, 700, 800, 900, 950], me = (t) => O
|
|
|
1746
1846
|
text: "#F4F4F5",
|
|
1747
1847
|
"text-muted": "#A1A1AA",
|
|
1748
1848
|
border: "#2E2E33"
|
|
1749
|
-
},
|
|
1750
|
-
function
|
|
1751
|
-
const
|
|
1752
|
-
return
|
|
1849
|
+
}, po = { bg: 4, surface: 2, "surface-raised": 2, text: 5, "text-muted": 12, border: 10 }, X = (e) => /^#[0-9a-f]{6}$/i.test(e || ""), lt = (e) => [500, 600, 700].includes(Number(e.shade)) ? Number(e.shade) : 600, dt = (e) => e[0].toUpperCase() + e.slice(1);
|
|
1850
|
+
function yn(e) {
|
|
1851
|
+
const t = (l) => (l /= 255, l <= 0.03928 ? l / 12.92 : ((l + 0.055) / 1.055) ** 2.4), [n, a, o] = [1, 3, 5].map((l) => t(parseInt(e.slice(l, l + 2), 16))), s = 0.2126 * n + 0.7152 * a + 0.0722 * o, r = 1.05 / (s + 0.05), i = (s + 0.05) / (98e-4 + 0.05);
|
|
1852
|
+
return r >= i ? "#FFFFFF" : "#18181B";
|
|
1753
1853
|
}
|
|
1754
|
-
const
|
|
1854
|
+
const He = {
|
|
1755
1855
|
id: "colour",
|
|
1756
1856
|
title: "Colour",
|
|
1757
1857
|
hint: "Brand colour · neutral tint · mode",
|
|
1758
|
-
uses: ["--twd-primary", "--twd-primary-hover", "--twd-on-primary", "--twd-focus", ...
|
|
1759
|
-
tokens(
|
|
1760
|
-
const
|
|
1761
|
-
if (!
|
|
1762
|
-
const i =
|
|
1763
|
-
n["--twd-primary"] = o[i], n["--twd-primary-hover"] = o[a ? i - 100 : i + 100], n["--twd-on-primary"] =
|
|
1858
|
+
uses: ["--twd-primary", "--twd-primary-hover", "--twd-on-primary", "--twd-focus", ...wn.map((e) => `--twd-${e}`)],
|
|
1859
|
+
tokens(e) {
|
|
1860
|
+
const t = e.colour || {}, n = {}, a = t.mode === "dark", o = it[t.primary];
|
|
1861
|
+
if (!X(t.brand) && o) {
|
|
1862
|
+
const i = lt(t);
|
|
1863
|
+
n["--twd-primary"] = o[i], n["--twd-primary-hover"] = o[a ? i - 100 : i + 100], n["--twd-on-primary"] = yn(o[i]), n["--twd-focus"] = o[500];
|
|
1764
1864
|
}
|
|
1765
|
-
|
|
1766
|
-
const
|
|
1767
|
-
if (
|
|
1768
|
-
const i = a ? { bg:
|
|
1769
|
-
Object.entries(i).forEach(([l,
|
|
1770
|
-
n[`--twd-${l}`] =
|
|
1865
|
+
X(t.brand) && (n["--twd-primary"] = t.brand, n["--twd-primary-hover"] = a ? `color-mix(in oklch, ${t.brand} 85%, white)` : `color-mix(in oklch, ${t.brand} 86%, black)`, n["--twd-on-primary"] = yn(t.brand), n["--twd-focus"] = t.brand);
|
|
1866
|
+
const s = t.tint === "subtle" ? X(t.brand) ? t.brand : F("--twd-primary") || xn : t.tint === "warm" ? io : null, r = st[t.neutral];
|
|
1867
|
+
if (r) {
|
|
1868
|
+
const i = a ? { bg: r[950], surface: r[900], "surface-raised": r[800], text: r[100], "text-muted": r[400], border: r[800] } : { bg: r[50], surface: "#ffffff", "surface-raised": "#ffffff", text: r[900], "text-muted": r[500], border: r[200] };
|
|
1869
|
+
Object.entries(i).forEach(([l, d]) => {
|
|
1870
|
+
n[`--twd-${l}`] = d;
|
|
1771
1871
|
});
|
|
1772
|
-
} else (a ||
|
|
1773
|
-
const l = a ?
|
|
1774
|
-
l && (n[`--twd-${i}`] =
|
|
1872
|
+
} else (a || s) && wn.forEach((i) => {
|
|
1873
|
+
const l = a ? co[i] : F(`--twd-${i}`);
|
|
1874
|
+
l && (n[`--twd-${i}`] = s ? `color-mix(in oklch, ${l} ${100 - po[i]}%, ${s})` : l);
|
|
1775
1875
|
});
|
|
1776
1876
|
return n;
|
|
1777
1877
|
},
|
|
1778
1878
|
parts: {
|
|
1779
1879
|
// ---- Tabbed layout (Colour tab): themes, then Tailwind fine-tuning ----
|
|
1780
|
-
themeTiles(
|
|
1880
|
+
themeTiles(e, t, { label: n = "Theme" } = {}) {
|
|
1781
1881
|
const a = (o) => {
|
|
1782
|
-
const
|
|
1783
|
-
return `<span class="theme" style="--bg:${
|
|
1882
|
+
const s = st[o.neutral], r = it[o.primary];
|
|
1883
|
+
return `<span class="theme" style="--bg:${s[50]};--line:${s[200]};--ink:${s[900]};--soft:${s[500]};--pri:${r[600]}"><i><b></b><s></s><u></u></i></span><span class="palette">${[r[400], r[500], r[600], r[700], r[800], s[200], s[500], s[900]].map((i) => `<i style="background:${i}"></i>`).join("")}</span>`;
|
|
1784
1884
|
};
|
|
1785
|
-
|
|
1885
|
+
e.append(V(t, {
|
|
1786
1886
|
section: "colour",
|
|
1787
1887
|
key: "theme",
|
|
1788
1888
|
label: n,
|
|
1789
1889
|
columns: 2,
|
|
1790
1890
|
className: "tiles-themes",
|
|
1791
|
-
options:
|
|
1891
|
+
options: Be.map((o) => ({ value: o.value, label: o.label, preview: a(o) })),
|
|
1792
1892
|
current: (o) => {
|
|
1793
|
-
const
|
|
1794
|
-
return
|
|
1893
|
+
const s = o.colour || {};
|
|
1894
|
+
return X(s.brand) ? null : Be.find((r) => r.primary === s.primary && r.neutral === s.neutral)?.value || null;
|
|
1795
1895
|
},
|
|
1796
1896
|
select: (o) => {
|
|
1797
|
-
const
|
|
1798
|
-
|
|
1897
|
+
const s = Be.find((r) => r.value === o);
|
|
1898
|
+
t.setSection("colour", { primary: s.primary, neutral: s.neutral, shade: null, brand: null, tint: null });
|
|
1799
1899
|
},
|
|
1800
|
-
valueText: (o,
|
|
1801
|
-
const
|
|
1802
|
-
return o ? "" :
|
|
1900
|
+
valueText: (o, s) => {
|
|
1901
|
+
const r = s.colour || {};
|
|
1902
|
+
return o ? "" : r.primary || r.neutral || r.brand ? "Custom" : "Prototype colours";
|
|
1803
1903
|
}
|
|
1804
1904
|
}));
|
|
1805
1905
|
},
|
|
1806
|
-
primary(
|
|
1807
|
-
const a =
|
|
1906
|
+
primary(e, t, { label: n = "Primary" } = {}) {
|
|
1907
|
+
const a = U(n), o = V(t, {
|
|
1808
1908
|
section: "colour",
|
|
1809
1909
|
key: "primary",
|
|
1810
1910
|
label: `${n} colour family`,
|
|
1811
1911
|
columns: 9,
|
|
1812
1912
|
className: "tiles-swatches",
|
|
1813
|
-
options: Object.entries(
|
|
1913
|
+
options: Object.entries(it).map(([i, l]) => ({ value: i, label: dt(i), title: dt(i), preview: `<span class="dot" style="background:${l[500]}"></span>` })),
|
|
1814
1914
|
current: (i) => {
|
|
1815
1915
|
const l = i.colour || {};
|
|
1816
|
-
return
|
|
1916
|
+
return X(l.brand) ? null : l.primary || null;
|
|
1817
1917
|
},
|
|
1818
|
-
select: (i) =>
|
|
1918
|
+
select: (i) => t.setSection("colour", { primary: i, brand: null })
|
|
1819
1919
|
});
|
|
1820
1920
|
o.querySelector(".field-head").remove();
|
|
1821
|
-
const
|
|
1822
|
-
a.append(o,
|
|
1823
|
-
const
|
|
1824
|
-
const i =
|
|
1825
|
-
|
|
1921
|
+
const s = ce({ label: "Shade", options: [500, 600, 700].map((i) => ({ value: String(i), label: String(i) })), onSelect: (i) => t.setSection("colour", { shade: Number(i) }) });
|
|
1922
|
+
a.append(o, s.el);
|
|
1923
|
+
const r = () => {
|
|
1924
|
+
const i = t.getState().colour || {};
|
|
1925
|
+
s.setValue(String(lt(i))), a.setValue(X(i.brand) ? i.brand.toLowerCase() : i.primary ? `${i.primary}-${lt(i)}` : "");
|
|
1826
1926
|
};
|
|
1827
|
-
|
|
1927
|
+
r(), t.onChange(r), e.append(a.el);
|
|
1828
1928
|
},
|
|
1829
|
-
neutralTiles(
|
|
1830
|
-
|
|
1929
|
+
neutralTiles(e, t, { label: n = "Neutral" } = {}) {
|
|
1930
|
+
e.append(V(t, {
|
|
1831
1931
|
section: "colour",
|
|
1832
1932
|
key: "neutral",
|
|
1833
1933
|
label: n,
|
|
1834
1934
|
columns: 5,
|
|
1835
1935
|
className: "tiles-greys",
|
|
1836
|
-
options: Object.entries(
|
|
1837
|
-
select: (a) =>
|
|
1936
|
+
options: Object.entries(st).map(([a, o]) => ({ value: a, label: dt(a), preview: `<span class="greys">${[o[50], o[200], o[500], o[900]].map((s) => `<i style="background:${s}"></i>`).join("")}</span>` })),
|
|
1937
|
+
select: (a) => t.setSection("colour", { neutral: a, tint: null }),
|
|
1838
1938
|
valueText: (a) => a || ""
|
|
1839
1939
|
}));
|
|
1840
1940
|
},
|
|
1841
|
-
custom(
|
|
1941
|
+
custom(e, t) {
|
|
1842
1942
|
const n = document.createElement("details");
|
|
1843
1943
|
n.className = "more more-custom", n.innerHTML = '<summary><span class="rainbow" aria-hidden="true"></span><span>Custom colour…</span><span class="muted">any hex</span></summary>';
|
|
1844
1944
|
const a = document.createElement("div");
|
|
1845
|
-
a.className = "more-body", n.append(a),
|
|
1945
|
+
a.className = "more-body", n.append(a), He.parts.brand(a, t, { label: "Brand" }), X((t.getState().colour || {}).brand) && (n.open = !0), e.append(n);
|
|
1846
1946
|
},
|
|
1847
|
-
brand(
|
|
1848
|
-
const a =
|
|
1947
|
+
brand(e, t, { label: n = "Brand" } = {}) {
|
|
1948
|
+
const a = U(n), o = oo({ label: "Brand colour", onInput: (r) => t.setSection("colour", { brand: r }) });
|
|
1849
1949
|
a.append(o.el);
|
|
1850
|
-
const
|
|
1851
|
-
|
|
1950
|
+
const s = () => o.setValue((t.getState().colour || {}).brand || (t.readToken || F)("--twd-primary") || xn);
|
|
1951
|
+
s(), t.onChange(s), e.append(a.el);
|
|
1852
1952
|
},
|
|
1853
|
-
neutral(
|
|
1854
|
-
const a =
|
|
1953
|
+
neutral(e, t, { label: n = "Neutral" } = {}) {
|
|
1954
|
+
const a = U(n), o = ro({ label: "Neutral tint", options: lo, onSelect: (r) => t.setSection("colour", { tint: r }) });
|
|
1855
1955
|
a.append(o.el);
|
|
1856
|
-
const
|
|
1857
|
-
|
|
1956
|
+
const s = () => o.setValue((t.getState().colour || {}).tint || "pure");
|
|
1957
|
+
s(), t.onChange(s), e.append(a.el);
|
|
1858
1958
|
},
|
|
1859
|
-
mode(
|
|
1860
|
-
|
|
1959
|
+
mode(e, t, { label: n = "Mode" } = {}) {
|
|
1960
|
+
e.append($e(t, {
|
|
1861
1961
|
section: "colour",
|
|
1862
1962
|
key: "mode",
|
|
1863
1963
|
label: n,
|
|
@@ -1866,15 +1966,15 @@ const De = {
|
|
|
1866
1966
|
}));
|
|
1867
1967
|
}
|
|
1868
1968
|
},
|
|
1869
|
-
render(
|
|
1870
|
-
["brand", "neutral", "mode"].forEach((n) => this.parts[n](
|
|
1969
|
+
render(e, t) {
|
|
1970
|
+
["brand", "neutral", "mode"].forEach((n) => this.parts[n](e, t));
|
|
1871
1971
|
}
|
|
1872
|
-
},
|
|
1972
|
+
}, Ne = (e, t = "var(--twd-surface)") => `color-mix(in oklch, var(--twd-primary) ${e}%, ${t})`, uo = {
|
|
1873
1973
|
// proposal recipes; Solid leaves tokens.css defaults
|
|
1874
|
-
soft: { bg:
|
|
1875
|
-
outline: { bg: "transparent", fg: "var(--twd-primary)", border: "var(--twd-primary)", hover:
|
|
1876
|
-
ghost: { bg: "transparent", fg: "var(--twd-primary)", border: "transparent", hover:
|
|
1877
|
-
},
|
|
1974
|
+
soft: { bg: Ne(14), fg: "var(--twd-primary)", border: "transparent", hover: Ne(22) },
|
|
1975
|
+
outline: { bg: "transparent", fg: "var(--twd-primary)", border: "var(--twd-primary)", hover: Ne(8, "transparent") },
|
|
1976
|
+
ghost: { bg: "transparent", fg: "var(--twd-primary)", border: "transparent", hover: Ne(10, "transparent") }
|
|
1977
|
+
}, ho = { small: { height: 0.8, pad: 0.75 }, large: { height: 1.2, pad: 1.25 } }, Wn = _n({
|
|
1878
1978
|
id: "buttons",
|
|
1879
1979
|
title: "Buttons",
|
|
1880
1980
|
hint: "Style · corners · size",
|
|
@@ -1893,15 +1993,15 @@ const De = {
|
|
|
1893
1993
|
// Figma: "8px"
|
|
1894
1994
|
{ value: "pill", label: "Pill", radius: 999 }
|
|
1895
1995
|
],
|
|
1896
|
-
extraTokens(
|
|
1897
|
-
const
|
|
1898
|
-
n && Object.entries(n).forEach(([
|
|
1899
|
-
|
|
1996
|
+
extraTokens(e) {
|
|
1997
|
+
const t = {}, n = uo[e.style];
|
|
1998
|
+
n && Object.entries(n).forEach(([r, i]) => {
|
|
1999
|
+
t[`--twd-button-${r}`] = i;
|
|
1900
2000
|
});
|
|
1901
|
-
const a =
|
|
1902
|
-
if (!a) return
|
|
1903
|
-
const o =
|
|
1904
|
-
return o && (
|
|
2001
|
+
const a = ho[e.size];
|
|
2002
|
+
if (!a) return t;
|
|
2003
|
+
const o = Se(F("--twd-control-height"), a.height), s = Se(F("--twd-control-pad-x"), a.pad);
|
|
2004
|
+
return o && (t["--twd-control-height"] = o), s && (t["--twd-control-pad-x"] = s), t;
|
|
1905
2005
|
},
|
|
1906
2006
|
extra: [
|
|
1907
2007
|
{
|
|
@@ -1909,23 +2009,23 @@ const De = {
|
|
|
1909
2009
|
label: "Style",
|
|
1910
2010
|
fallback: "solid",
|
|
1911
2011
|
columns: 4,
|
|
1912
|
-
preview: (
|
|
2012
|
+
preview: (e) => `<span class="shape shape-button style-${e}"></span>`,
|
|
1913
2013
|
options: [{ value: "solid", label: "Solid" }, { value: "soft", label: "Soft" }, { value: "outline", label: "Outline" }, { value: "ghost", label: "Ghost" }]
|
|
1914
2014
|
},
|
|
1915
2015
|
{
|
|
1916
2016
|
key: "size",
|
|
1917
2017
|
label: "Size",
|
|
1918
2018
|
fallback: "medium",
|
|
1919
|
-
preview: (
|
|
2019
|
+
preview: (e) => `<span class="shape shape-button size-${e}"></span>`,
|
|
1920
2020
|
options: [{ value: "small", label: "Small" }, { value: "medium", label: "Medium" }, { value: "large", label: "Large" }]
|
|
1921
2021
|
}
|
|
1922
2022
|
]
|
|
1923
|
-
}),
|
|
2023
|
+
}), fo = {
|
|
1924
2024
|
// proposal recipes; Filled leaves tokens.css defaults
|
|
1925
2025
|
outlined: { bg: "transparent", border: "color-mix(in oklch, var(--twd-border) 60%, var(--twd-text))", shadow: "none" },
|
|
1926
2026
|
elevated: { bg: "var(--twd-surface-raised)", border: "transparent", shadow: "var(--twd-shadow-md)" },
|
|
1927
2027
|
tinted: { bg: "color-mix(in oklch, var(--twd-primary) 8%, var(--twd-surface))", border: "color-mix(in oklch, var(--twd-primary) 25%, var(--twd-surface))", shadow: "none" }
|
|
1928
|
-
},
|
|
2028
|
+
}, St = _n({
|
|
1929
2029
|
id: "cards",
|
|
1930
2030
|
title: "Cards",
|
|
1931
2031
|
hint: "Style · corners · border",
|
|
@@ -1944,15 +2044,15 @@ const De = {
|
|
|
1944
2044
|
{ value: "large", label: "Large", radius: 20 }
|
|
1945
2045
|
// proposal (cards don't go fully pill)
|
|
1946
2046
|
],
|
|
1947
|
-
extraTokens(
|
|
1948
|
-
const
|
|
2047
|
+
extraTokens(e) {
|
|
2048
|
+
const t = {}, n = fo[e.style];
|
|
1949
2049
|
if (n && Object.entries(n).forEach(([a, o]) => {
|
|
1950
|
-
|
|
1951
|
-
}),
|
|
1952
|
-
const a =
|
|
1953
|
-
a && (
|
|
2050
|
+
t[`--twd-card-${a}`] = o;
|
|
2051
|
+
}), e.border === "none" && (t["--twd-border-width-container"] = "0px"), e.border === "thick") {
|
|
2052
|
+
const a = Se(F("--twd-border-width-container"), 2);
|
|
2053
|
+
a && (t["--twd-border-width-container"] = a);
|
|
1954
2054
|
}
|
|
1955
|
-
return
|
|
2055
|
+
return t;
|
|
1956
2056
|
},
|
|
1957
2057
|
extra: [
|
|
1958
2058
|
{
|
|
@@ -1960,49 +2060,49 @@ const De = {
|
|
|
1960
2060
|
label: "Style",
|
|
1961
2061
|
fallback: "filled",
|
|
1962
2062
|
columns: 4,
|
|
1963
|
-
preview: (
|
|
2063
|
+
preview: (e) => `<span class="shape shape-card style-${e}"></span>`,
|
|
1964
2064
|
options: [{ value: "filled", label: "Filled" }, { value: "outlined", label: "Outlined" }, { value: "elevated", label: "Elevated" }, { value: "tinted", label: "Tinted" }]
|
|
1965
2065
|
},
|
|
1966
2066
|
{
|
|
1967
2067
|
key: "border",
|
|
1968
2068
|
label: "Border",
|
|
1969
2069
|
fallback: "thin",
|
|
1970
|
-
preview: (
|
|
2070
|
+
preview: (e) => `<span class="shape shape-card border-${e}"></span>`,
|
|
1971
2071
|
options: [{ value: "none", label: "None" }, { value: "thin", label: "Thin" }, { value: "thick", label: "Thick" }]
|
|
1972
2072
|
}
|
|
1973
2073
|
]
|
|
1974
|
-
}),
|
|
2074
|
+
}), ie = 60, ct = {
|
|
1975
2075
|
soft: { sm: [0, 1, 2, 0, 0.06], md: [0, 4, 12, 0, 0.08], lg: [0, 12, 32, 0, 0.12] },
|
|
1976
2076
|
// = tokens.css
|
|
1977
2077
|
crisp: { sm: [0, 1, 1, 0, 0.12], md: [0, 2, 4, 0, 0.14], lg: [0, 4, 8, 0, 0.16] },
|
|
1978
2078
|
bold: { sm: [2, 2, 0, 0, 0.85], md: [3, 3, 0, 0, 0.85], lg: [5, 5, 0, 0, 0.85] }
|
|
1979
|
-
},
|
|
1980
|
-
const
|
|
1981
|
-
return
|
|
1982
|
-
},
|
|
2079
|
+
}, je = ([e, t, n, a, o], s) => {
|
|
2080
|
+
const r = Math.min(1, +(o * s).toFixed(3));
|
|
2081
|
+
return r <= 0 ? "none" : `${e}px ${t}px ${n}px ${a}px rgb(0 0 0 / ${r})`;
|
|
2082
|
+
}, $t = {
|
|
1983
2083
|
id: "depth",
|
|
1984
2084
|
title: "Depth",
|
|
1985
2085
|
hint: "Flat · soft · crisp · bold",
|
|
1986
2086
|
uses: ["--twd-shadow-sm", "--twd-shadow-md", "--twd-shadow-lg"],
|
|
1987
|
-
tokens(
|
|
1988
|
-
const
|
|
2087
|
+
tokens(e) {
|
|
2088
|
+
const t = e.depth || {}, n = t.style || "soft", a = typeof t.intensity == "number" ? Math.min(100, Math.max(0, t.intensity)) : ie;
|
|
1989
2089
|
if (n === "flat")
|
|
1990
2090
|
return { "--twd-shadow-sm": "none", "--twd-shadow-md": "none", "--twd-shadow-lg": "none" };
|
|
1991
|
-
const o =
|
|
2091
|
+
const o = ct[n];
|
|
1992
2092
|
if (!o) return {};
|
|
1993
|
-
if (n === "soft" && a ===
|
|
1994
|
-
const
|
|
2093
|
+
if (n === "soft" && a === ie) return {};
|
|
2094
|
+
const s = a / ie;
|
|
1995
2095
|
return {
|
|
1996
|
-
"--twd-shadow-sm":
|
|
1997
|
-
"--twd-shadow-md":
|
|
1998
|
-
"--twd-shadow-lg":
|
|
2096
|
+
"--twd-shadow-sm": je(o.sm, s),
|
|
2097
|
+
"--twd-shadow-md": je(o.md, s),
|
|
2098
|
+
"--twd-shadow-lg": je(o.lg, s)
|
|
1999
2099
|
};
|
|
2000
2100
|
},
|
|
2001
2101
|
parts: {
|
|
2002
2102
|
// Tabbed layout: each style as a small card wearing that shadow.
|
|
2003
|
-
styleTiles(
|
|
2004
|
-
const a = (o) => `<span class="shape shape-card shadow-${o}" style="box-shadow:${
|
|
2005
|
-
|
|
2103
|
+
styleTiles(e, t, { label: n = "Style" } = {}) {
|
|
2104
|
+
const a = (o) => `<span class="shape shape-card shadow-${o}" style="box-shadow:${ct[o] ? je(ct[o].md, 1) : "none"}"></span>`;
|
|
2105
|
+
e.append(V(t, {
|
|
2006
2106
|
section: "depth",
|
|
2007
2107
|
key: "style",
|
|
2008
2108
|
label: n,
|
|
@@ -2011,8 +2111,8 @@ const De = {
|
|
|
2011
2111
|
options: ["flat", "soft", "crisp", "bold"].map((o) => ({ value: o, label: o[0].toUpperCase() + o.slice(1), preview: a(o) }))
|
|
2012
2112
|
}));
|
|
2013
2113
|
},
|
|
2014
|
-
style(
|
|
2015
|
-
|
|
2114
|
+
style(e, t, { label: n = "Style" } = {}) {
|
|
2115
|
+
e.append($e(t, {
|
|
2016
2116
|
section: "depth",
|
|
2017
2117
|
key: "style",
|
|
2018
2118
|
label: n,
|
|
@@ -2020,160 +2120,160 @@ const De = {
|
|
|
2020
2120
|
options: [{ value: "flat", label: "Flat" }, { value: "soft", label: "Soft" }, { value: "crisp", label: "Crisp" }, { value: "bold", label: "Bold" }]
|
|
2021
2121
|
}));
|
|
2022
2122
|
},
|
|
2023
|
-
intensity(
|
|
2024
|
-
|
|
2123
|
+
intensity(e, t, { label: n = "Intensity" } = {}) {
|
|
2124
|
+
e.append(Xa(t, { section: "depth", key: "intensity", label: n, min: 0, max: 100, fallback: ie, format: (a) => `${a}%` }));
|
|
2025
2125
|
}
|
|
2026
2126
|
},
|
|
2027
|
-
render(
|
|
2028
|
-
Object.entries(this.parts).filter(([n]) => !n.endsWith("Tiles")).forEach(([, n]) => n(
|
|
2127
|
+
render(e, t) {
|
|
2128
|
+
Object.entries(this.parts).filter(([n]) => !n.endsWith("Tiles")).forEach(([, n]) => n(e, t));
|
|
2029
2129
|
}
|
|
2030
|
-
},
|
|
2031
|
-
function
|
|
2032
|
-
return typeof
|
|
2130
|
+
}, go = (e) => Math.min(1, Math.max(0, e)), vn = (e) => Math.round(e / 50) * 50, pt = (e) => `${+e.toFixed(1)}%`;
|
|
2131
|
+
function Tt(e = {}) {
|
|
2132
|
+
return typeof e.bold == "number" && isFinite(e.bold) && e.bold !== 0.5 ? go(e.bold) : null;
|
|
2033
2133
|
}
|
|
2034
|
-
const
|
|
2134
|
+
const Re = (e, t, n) => (e.tokens(t) || {})[n] || F(n), mo = {
|
|
2035
2135
|
id: "character",
|
|
2036
2136
|
title: "Character",
|
|
2037
2137
|
hint: "Subtle ↔ Bold",
|
|
2038
2138
|
uses: [],
|
|
2039
2139
|
// set from the Simple page only
|
|
2040
|
-
tokens(
|
|
2041
|
-
const
|
|
2042
|
-
if (
|
|
2043
|
-
const n = {}, a = 2 *
|
|
2044
|
-
n["--twd-weight-heading"] = String(
|
|
2045
|
-
const
|
|
2046
|
-
if (
|
|
2047
|
-
n["--twd-border"] = `color-mix(in oklch, ${
|
|
2048
|
-
const l =
|
|
2049
|
-
n["--twd-text-muted"] = `color-mix(in oklch, ${l} ${
|
|
2050
|
-
const
|
|
2051
|
-
isNaN(
|
|
2052
|
-
}), [[
|
|
2053
|
-
const
|
|
2054
|
-
!isNaN(
|
|
2140
|
+
tokens(e) {
|
|
2141
|
+
const t = Tt(e.character);
|
|
2142
|
+
if (t === null) return {};
|
|
2143
|
+
const n = {}, a = 2 * t - 1, o = Math.max(0, a), s = Math.max(0, -a);
|
|
2144
|
+
n["--twd-weight-heading"] = String(vn(600 + 200 * a)), n["--twd-weight-strong"] = String(vn(600 + 100 * a));
|
|
2145
|
+
const r = Re(He, e, "--twd-border");
|
|
2146
|
+
if (s && (n["--twd-border"] = `color-mix(in oklch, ${r} ${pt(100 - 50 * s)}, var(--twd-bg))`), o) {
|
|
2147
|
+
n["--twd-border"] = `color-mix(in oklch, ${r} ${pt(100 - 90 * o)}, var(--twd-text))`;
|
|
2148
|
+
const l = Re(He, e, "--twd-text-muted");
|
|
2149
|
+
n["--twd-text-muted"] = `color-mix(in oklch, ${l} ${pt(100 - 35 * o)}, var(--twd-text))`, ["--twd-border-width", "--twd-border-width-container"].forEach((d) => {
|
|
2150
|
+
const p = d === "--twd-border-width-container" ? Re(St, e, d) : F(d), u = parseFloat(p);
|
|
2151
|
+
isNaN(u) || (n[d] = `${Math.round(u * (1 + o))}px`);
|
|
2152
|
+
}), [[Wn, "--twd-radius-control"], [St, "--twd-radius-container"]].forEach(([d, p]) => {
|
|
2153
|
+
const u = parseFloat(Re(d, e, p));
|
|
2154
|
+
!isNaN(u) && u < 999 && (n[p] = Se(`${u}px`, 1 - 0.5 * o));
|
|
2055
2155
|
});
|
|
2056
2156
|
}
|
|
2057
|
-
const i =
|
|
2157
|
+
const i = e.depth || {};
|
|
2058
2158
|
if (!i.style) {
|
|
2059
|
-
const l = typeof i.intensity == "number" ? i.intensity :
|
|
2060
|
-
Object.assign(n,
|
|
2159
|
+
const l = typeof i.intensity == "number" ? i.intensity : ie, d = s ? $t.tokens({ depth: { style: "soft", intensity: l * (1 - s) } }) : $t.tokens({ depth: { style: "bold", intensity: ie * o } });
|
|
2160
|
+
Object.assign(n, d);
|
|
2061
2161
|
}
|
|
2062
2162
|
return n;
|
|
2063
2163
|
},
|
|
2064
|
-
describe: (
|
|
2065
|
-
},
|
|
2066
|
-
let
|
|
2067
|
-
function
|
|
2068
|
-
const
|
|
2069
|
-
|
|
2164
|
+
describe: (e, t) => e === "bold" ? { label: "Subtle ↔ Bold", value: `${Math.round(t * 100)}%` } : null
|
|
2165
|
+
}, bo = '<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M6 9l6 6 6-6"/></svg>', xo = '<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M5 12.5l4.5 4.5L19 7.5"/></svg>';
|
|
2166
|
+
let wo = 0;
|
|
2167
|
+
function yo({ label: e, options: t, onSelect: n, placeholder: a, onOpen: o }) {
|
|
2168
|
+
const s = `twd-fonts-${++wo}`, r = document.createElement("div");
|
|
2169
|
+
r.className = "font-select";
|
|
2070
2170
|
const i = document.createElement("button");
|
|
2071
|
-
i.type = "button", i.className = "font-trigger", i.setAttribute("aria-haspopup", "listbox"), i.setAttribute("aria-expanded", "false"), i.setAttribute("aria-controls",
|
|
2171
|
+
i.type = "button", i.className = "font-trigger", i.setAttribute("aria-haspopup", "listbox"), i.setAttribute("aria-expanded", "false"), i.setAttribute("aria-controls", s), i.innerHTML = `
|
|
2072
2172
|
<span class="font-aa" aria-hidden="true">Aa</span>
|
|
2073
2173
|
<span class="font-name"></span>
|
|
2074
2174
|
<span class="font-fonts"></span>
|
|
2075
|
-
<span class="font-chevron">${
|
|
2175
|
+
<span class="font-chevron">${bo}</span>`;
|
|
2076
2176
|
const l = document.createElement("div");
|
|
2077
|
-
l.className = "font-menu", l.id =
|
|
2078
|
-
const
|
|
2079
|
-
const
|
|
2080
|
-
return
|
|
2081
|
-
<span class="font-aa" style="font-family:${
|
|
2082
|
-
<span class="font-text"><span class="font-name">${
|
|
2083
|
-
<span class="font-check">${
|
|
2177
|
+
l.className = "font-menu", l.id = s, l.setAttribute("role", "listbox"), l.setAttribute("aria-label", e), l.hidden = !0;
|
|
2178
|
+
const d = t.map((h) => {
|
|
2179
|
+
const y = document.createElement("div");
|
|
2180
|
+
return y.className = "font-option", y.setAttribute("role", "option"), y.tabIndex = -1, y.dataset.value = h.value, y.innerHTML = `
|
|
2181
|
+
<span class="font-aa" style="font-family:${h.preview}" aria-hidden="true">Aa</span>
|
|
2182
|
+
<span class="font-text"><span class="font-name">${h.name}</span><span class="font-fonts">${h.fonts}</span></span>
|
|
2183
|
+
<span class="font-check">${xo}</span>`, y.addEventListener("click", () => b(h.value)), y;
|
|
2084
2184
|
});
|
|
2085
|
-
l.append(...
|
|
2086
|
-
let
|
|
2087
|
-
function h
|
|
2088
|
-
const
|
|
2089
|
-
|
|
2185
|
+
l.append(...d), r.append(i, l);
|
|
2186
|
+
let p = null;
|
|
2187
|
+
function u(h) {
|
|
2188
|
+
const y = t.find((E) => E.value === h), L = y || a || t[0];
|
|
2189
|
+
p = y ? y.value : a ? null : t[0].value, i.querySelector(".font-name").textContent = L.name, i.querySelector(".font-fonts").textContent = L.fonts, i.querySelector(".font-aa").style.fontFamily = L.preview, i.setAttribute("aria-label", `${e}: ${L.name}, ${L.fonts}`), d.forEach((E) => E.setAttribute("aria-selected", String(E.dataset.value === p)));
|
|
2090
2190
|
}
|
|
2091
|
-
function
|
|
2092
|
-
const
|
|
2093
|
-
l.style.top = `${E}px`, l.style.left = `${
|
|
2191
|
+
function v() {
|
|
2192
|
+
const h = i.getBoundingClientRect(), y = l.offsetHeight, E = window.innerHeight - h.bottom - 8 >= y ? h.bottom + 4 : Math.max(8, h.top - 4 - y);
|
|
2193
|
+
l.style.top = `${E}px`, l.style.left = `${h.left}px`, l.style.width = `${h.width}px`;
|
|
2094
2194
|
}
|
|
2095
2195
|
function f() {
|
|
2096
|
-
l.hidden && (l.hidden = !1, i.setAttribute("aria-expanded", "true"), o && o(),
|
|
2196
|
+
l.hidden && (l.hidden = !1, i.setAttribute("aria-expanded", "true"), o && o(), v(), (d.find((h) => h.dataset.value === p) || d[0]).focus(), setTimeout(() => document.addEventListener("pointerdown", x, !0)), window.addEventListener("resize", m), r.closest(".body")?.addEventListener("scroll", m, { once: !0 }));
|
|
2097
2197
|
}
|
|
2098
|
-
function m(
|
|
2099
|
-
l.hidden || (l.hidden = !0, i.setAttribute("aria-expanded", "false"), document.removeEventListener("pointerdown", x, !0), window.removeEventListener("resize", m),
|
|
2198
|
+
function m(h) {
|
|
2199
|
+
l.hidden || (l.hidden = !0, i.setAttribute("aria-expanded", "false"), document.removeEventListener("pointerdown", x, !0), window.removeEventListener("resize", m), h === !0 && i.focus());
|
|
2100
2200
|
}
|
|
2101
|
-
function x(
|
|
2102
|
-
|
|
2201
|
+
function x(h) {
|
|
2202
|
+
h.composedPath().includes(r) || m();
|
|
2103
2203
|
}
|
|
2104
|
-
function b(
|
|
2105
|
-
|
|
2204
|
+
function b(h) {
|
|
2205
|
+
h !== p && (u(h), n(h)), m(!0);
|
|
2106
2206
|
}
|
|
2107
|
-
return i.addEventListener("click", () => l.hidden ? f() : m(!0)), i.addEventListener("keydown", (
|
|
2108
|
-
(
|
|
2109
|
-
}), l.addEventListener("keydown", (
|
|
2110
|
-
const
|
|
2207
|
+
return i.addEventListener("click", () => l.hidden ? f() : m(!0)), i.addEventListener("keydown", (h) => {
|
|
2208
|
+
(h.key === "ArrowDown" || h.key === "ArrowUp") && (h.preventDefault(), f());
|
|
2209
|
+
}), l.addEventListener("keydown", (h) => {
|
|
2210
|
+
const y = d.indexOf(h.target);
|
|
2111
2211
|
let L = null;
|
|
2112
|
-
if (
|
|
2113
|
-
else if (
|
|
2114
|
-
else if (
|
|
2115
|
-
else if (
|
|
2116
|
-
else if (
|
|
2117
|
-
|
|
2212
|
+
if (h.key === "ArrowDown") L = Math.min(d.length - 1, y + 1);
|
|
2213
|
+
else if (h.key === "ArrowUp") L = Math.max(0, y - 1);
|
|
2214
|
+
else if (h.key === "Home") L = 0;
|
|
2215
|
+
else if (h.key === "End") L = d.length - 1;
|
|
2216
|
+
else if (h.key === "Enter" || h.key === " ") {
|
|
2217
|
+
h.preventDefault(), b(h.target.dataset.value);
|
|
2118
2218
|
return;
|
|
2119
|
-
} else if (
|
|
2120
|
-
|
|
2219
|
+
} else if (h.key === "Escape") {
|
|
2220
|
+
h.preventDefault(), h.stopPropagation(), m(!0);
|
|
2121
2221
|
return;
|
|
2122
|
-
} else if (
|
|
2222
|
+
} else if (h.key === "Tab") {
|
|
2123
2223
|
m();
|
|
2124
2224
|
return;
|
|
2125
2225
|
}
|
|
2126
|
-
L !== null && (
|
|
2127
|
-
}),
|
|
2226
|
+
L !== null && (h.preventDefault(), d[L].focus());
|
|
2227
|
+
}), u(a ? null : t[0].value), { el: r, setValue: u };
|
|
2128
2228
|
}
|
|
2129
|
-
const I = 'ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, sans-serif',
|
|
2229
|
+
const I = 'ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, sans-serif', ze = 'Georgia, "Times New Roman", serif', J = [
|
|
2130
2230
|
{ value: "neutral", name: "Neutral", fonts: "Inter · Inter", heading: `"Inter", ${I}`, body: `"Inter", ${I}`, load: ["Inter:wght@400;500;600;700"] },
|
|
2131
2231
|
{ value: "modern", name: "Modern", fonts: "Geist · Geist", heading: `"Geist", ${I}`, body: `"Geist", ${I}`, load: ["Geist:wght@400;500;600;700"] },
|
|
2132
|
-
{ value: "editorial", name: "Editorial", fonts: "Fraunces · Inter", heading: `"Fraunces", ${
|
|
2232
|
+
{ value: "editorial", name: "Editorial", fonts: "Fraunces · Inter", heading: `"Fraunces", ${ze}`, body: `"Inter", ${I}`, load: ["Fraunces:wght@400;600;700", "Inter:wght@400;500;600;700"] },
|
|
2133
2233
|
{ value: "technical", name: "Technical", fonts: "Space Grotesk · DM Sans", heading: `"Space Grotesk", ${I}`, body: `"DM Sans", ${I}`, load: ["Space+Grotesk:wght@500;600;700", "DM+Sans:wght@400;500;600;700"] },
|
|
2134
|
-
{ value: "classic", name: "Classic", fonts: "Playfair · DM Sans", heading: `"Playfair Display", ${
|
|
2135
|
-
{ value: "plex", name: "Plex", fonts: "Plex Serif · Plex Sans", heading: `"IBM Plex Serif", ${
|
|
2136
|
-
{ value: "elegant", name: "Elegant", fonts: "Instrument Serif · Inter", heading: `"Instrument Serif", ${
|
|
2234
|
+
{ value: "classic", name: "Classic", fonts: "Playfair · DM Sans", heading: `"Playfair Display", ${ze}`, body: `"DM Sans", ${I}`, load: ["Playfair+Display:wght@600;700", "DM+Sans:wght@400;500;600;700"] },
|
|
2235
|
+
{ value: "plex", name: "Plex", fonts: "Plex Serif · Plex Sans", heading: `"IBM Plex Serif", ${ze}`, body: `"IBM Plex Sans", ${I}`, load: ["IBM+Plex+Serif:wght@500;600;700", "IBM+Plex+Sans:wght@400;500;600"] },
|
|
2236
|
+
{ value: "elegant", name: "Elegant", fonts: "Instrument Serif · Inter", heading: `"Instrument Serif", ${ze}`, body: `"Inter", ${I}`, load: ["Instrument+Serif", "Inter:wght@400;500;600;700"] },
|
|
2137
2237
|
{ value: "friendly", name: "Friendly", fonts: "Manrope · Manrope", heading: `"Manrope", ${I}`, body: `"Manrope", ${I}`, load: ["Manrope:wght@400;500;600;700"] }
|
|
2138
|
-
],
|
|
2139
|
-
function
|
|
2140
|
-
const
|
|
2141
|
-
if (!
|
|
2142
|
-
|
|
2238
|
+
], we = { compact: 1.125, balanced: 1.2, expressive: 1.333 }, kn = { xs: -2, sm: -1, base: 0, lg: 1, xl: 2, "2xl": 3, "3xl": 4 }, Sn = /* @__PURE__ */ new Set();
|
|
2239
|
+
function ut(e) {
|
|
2240
|
+
const t = e.filter((a) => !Sn.has(a));
|
|
2241
|
+
if (!t.length) return;
|
|
2242
|
+
t.forEach((a) => Sn.add(a));
|
|
2143
2243
|
const n = document.createElement("link");
|
|
2144
|
-
n.rel = "stylesheet", n.href = `https://fonts.googleapis.com/css2?${
|
|
2244
|
+
n.rel = "stylesheet", n.href = `https://fonts.googleapis.com/css2?${t.map((a) => `family=${a}`).join("&")}&display=swap`, n.dataset.twiddle = "fonts", document.head.appendChild(n);
|
|
2145
2245
|
}
|
|
2146
|
-
const
|
|
2246
|
+
const vo = {
|
|
2147
2247
|
id: "typography",
|
|
2148
2248
|
title: "Typography",
|
|
2149
2249
|
hint: "Font pairing · base size · scale",
|
|
2150
|
-
uses: ["--twd-font-heading", "--twd-font-body", ...Object.keys(
|
|
2151
|
-
tokens(
|
|
2152
|
-
const
|
|
2153
|
-
if (a && (
|
|
2154
|
-
const o = Number(
|
|
2155
|
-
Object.entries(
|
|
2156
|
-
n[`--twd-text-${
|
|
2250
|
+
uses: ["--twd-font-heading", "--twd-font-body", ...Object.keys(kn).map((e) => `--twd-text-${e}`)],
|
|
2251
|
+
tokens(e) {
|
|
2252
|
+
const t = e.typography || {}, n = {}, a = J.find((o) => o.value === t.font);
|
|
2253
|
+
if (a && (ut(a.load), n["--twd-font-heading"] = a.heading, n["--twd-font-body"] = a.body), t.base || t.scale) {
|
|
2254
|
+
const o = Number(t.base) || 16, s = we[t.scale] || we.balanced;
|
|
2255
|
+
Object.entries(kn).forEach(([r, i]) => {
|
|
2256
|
+
n[`--twd-text-${r}`] = `${+(o * s ** i / 16).toFixed(3)}rem`;
|
|
2157
2257
|
});
|
|
2158
2258
|
}
|
|
2159
2259
|
return n;
|
|
2160
2260
|
},
|
|
2161
2261
|
parts: {
|
|
2162
|
-
fonts(
|
|
2163
|
-
const a =
|
|
2262
|
+
fonts(e, t, { label: n = "Fonts" } = {}) {
|
|
2263
|
+
const a = U(n), o = yo({
|
|
2164
2264
|
label: "Font pairing",
|
|
2165
|
-
options:
|
|
2265
|
+
options: J.map((r) => ({ value: r.value, name: r.name, fonts: r.fonts, preview: r.heading })),
|
|
2166
2266
|
placeholder: { name: "Default", fonts: "Prototype fonts", preview: I },
|
|
2167
2267
|
// Load every pairing's fonts when the list opens, so the "Aa" previews are real.
|
|
2168
|
-
onOpen: () =>
|
|
2169
|
-
onSelect: (
|
|
2268
|
+
onOpen: () => ut([...new Set(J.flatMap((r) => r.load))]),
|
|
2269
|
+
onSelect: (r) => t.setSection("typography", { font: r })
|
|
2170
2270
|
});
|
|
2171
2271
|
a.append(o.el);
|
|
2172
|
-
const
|
|
2173
|
-
|
|
2272
|
+
const s = () => o.setValue((t.getState().typography || {}).font || null);
|
|
2273
|
+
s(), t.onChange(s), e.append(a.el);
|
|
2174
2274
|
},
|
|
2175
|
-
base(
|
|
2176
|
-
|
|
2275
|
+
base(e, t, { label: n = "Base size" } = {}) {
|
|
2276
|
+
e.append($e(t, {
|
|
2177
2277
|
section: "typography",
|
|
2178
2278
|
key: "base",
|
|
2179
2279
|
label: n,
|
|
@@ -2184,8 +2284,8 @@ const lo = {
|
|
|
2184
2284
|
},
|
|
2185
2285
|
// Tabbed layout: pairings as tiles, each "Aa" in its own heading font.
|
|
2186
2286
|
// The fonts load (from Google Fonts) only once the tiles are on screen.
|
|
2187
|
-
fontTiles(
|
|
2188
|
-
const a = (i) => i.replace(/"/g, """), o = (i) => `<span class="tile-aa" style="font-family:${a(i)}">Aa</span>`,
|
|
2287
|
+
fontTiles(e, t, { label: n = "Fonts" } = {}) {
|
|
2288
|
+
const a = (i) => i.replace(/"/g, """), o = (i) => `<span class="tile-aa" style="font-family:${a(i)}">Aa</span>`, s = V(t, {
|
|
2189
2289
|
section: "typography",
|
|
2190
2290
|
key: "font",
|
|
2191
2291
|
label: n,
|
|
@@ -2193,165 +2293,165 @@ const lo = {
|
|
|
2193
2293
|
className: "tiles-fonts",
|
|
2194
2294
|
options: [
|
|
2195
2295
|
{ value: "default", label: "Prototype fonts", sub: "Default", preview: o(I), wide: !0 },
|
|
2196
|
-
...
|
|
2296
|
+
...J.map((i) => ({ value: i.value, label: i.name, sub: i.fonts, preview: o(i.heading) }))
|
|
2197
2297
|
],
|
|
2198
2298
|
current: (i) => (i.typography || {}).font || "default",
|
|
2199
|
-
select: (i) =>
|
|
2200
|
-
valueText: (i) => i === "default" ? "Default" :
|
|
2201
|
-
}),
|
|
2202
|
-
i.some((l) => l.isIntersecting) && (
|
|
2299
|
+
select: (i) => t.setSection("typography", { font: i === "default" ? null : i }),
|
|
2300
|
+
valueText: (i) => i === "default" ? "Default" : J.find((l) => l.value === i)?.name || ""
|
|
2301
|
+
}), r = new IntersectionObserver((i) => {
|
|
2302
|
+
i.some((l) => l.isIntersecting) && (ut([...new Set(J.flatMap((l) => l.load))]), r.disconnect());
|
|
2203
2303
|
});
|
|
2204
|
-
|
|
2304
|
+
r.observe(s), e.append(s);
|
|
2205
2305
|
},
|
|
2206
|
-
scaleTiles(
|
|
2207
|
-
const a = (o) => `<span class="ladder">${[-1, 0, 1, 2].map((
|
|
2208
|
-
|
|
2306
|
+
scaleTiles(e, t, { label: n = "Scale" } = {}) {
|
|
2307
|
+
const a = (o) => `<span class="ladder">${[-1, 0, 1, 2].map((s) => `<b style="font-size:${Math.round(13 * o ** s)}px">A</b>`).join("")}</span>`;
|
|
2308
|
+
e.append(V(t, {
|
|
2209
2309
|
section: "typography",
|
|
2210
2310
|
key: "scale",
|
|
2211
2311
|
label: n,
|
|
2212
2312
|
fallback: "balanced",
|
|
2213
|
-
options: Object.entries(
|
|
2214
|
-
valueText: (o) => String(
|
|
2313
|
+
options: Object.entries(we).map(([o, s]) => ({ value: o, label: o[0].toUpperCase() + o.slice(1), preview: a(s) })),
|
|
2314
|
+
valueText: (o) => String(we[o])
|
|
2215
2315
|
}));
|
|
2216
2316
|
},
|
|
2217
|
-
scale(
|
|
2218
|
-
|
|
2317
|
+
scale(e, t, { label: n = "Scale" } = {}) {
|
|
2318
|
+
e.append($e(t, {
|
|
2219
2319
|
section: "typography",
|
|
2220
2320
|
key: "scale",
|
|
2221
2321
|
label: n,
|
|
2222
2322
|
fallback: "balanced",
|
|
2223
2323
|
options: [{ value: "compact", label: "Compact" }, { value: "balanced", label: "Balanced" }, { value: "expressive", label: "Expressive" }],
|
|
2224
|
-
valueText: (a) => String(
|
|
2324
|
+
valueText: (a) => String(we[a])
|
|
2225
2325
|
}));
|
|
2226
2326
|
}
|
|
2227
2327
|
},
|
|
2228
|
-
render(
|
|
2229
|
-
Object.entries(this.parts).filter(([n]) => !n.endsWith("Tiles")).forEach(([, n]) => n(
|
|
2328
|
+
render(e, t) {
|
|
2329
|
+
Object.entries(this.parts).filter(([n]) => !n.endsWith("Tiles")).forEach(([, n]) => n(e, t));
|
|
2230
2330
|
}
|
|
2231
|
-
}, [
|
|
2232
|
-
function
|
|
2233
|
-
return
|
|
2331
|
+
}, [$n, Ie] = kt;
|
|
2332
|
+
function ko(e) {
|
|
2333
|
+
return e > 0.56 ? "Bold" : e < 0.44 ? "Subtle" : "Balanced";
|
|
2234
2334
|
}
|
|
2235
|
-
function
|
|
2236
|
-
return
|
|
2335
|
+
function Gn(e) {
|
|
2336
|
+
return e > 1.06 ? "Comfy" : e < 0.94 ? "Compact" : "Regular";
|
|
2237
2337
|
}
|
|
2238
|
-
const
|
|
2239
|
-
["colour", "Colour", (
|
|
2240
|
-
const
|
|
2241
|
-
return { text: `${
|
|
2338
|
+
const De = (e) => e && e[0].toUpperCase() + e.slice(1), En = [
|
|
2339
|
+
["colour", "Colour", (e) => {
|
|
2340
|
+
const t = e.colour || {}, n = Be.find((o) => o.primary === t.primary && o.neutral === t.neutral);
|
|
2341
|
+
return { text: `${t.brand ? t.brand.toUpperCase() : n && !t.shade ? n.label : t.primary ? `${De(t.primary)}${t.neutral ? ` · ${De(t.neutral)}` : ""}` : "Prototype colours"}${t.mode === "dark" ? " · Dark" : ""}`, swatch: F("--twd-primary") };
|
|
2242
2342
|
}],
|
|
2243
|
-
["type", "Type", (
|
|
2244
|
-
const
|
|
2245
|
-
return { text: `${
|
|
2343
|
+
["type", "Type", (e) => {
|
|
2344
|
+
const t = e.typography || {};
|
|
2345
|
+
return { text: `${J.find((n) => n.value === t.font)?.name || "Prototype fonts"} · ${t.base || 16}px` };
|
|
2246
2346
|
}],
|
|
2247
|
-
["spacing", "Spacing", (
|
|
2248
|
-
["shape", "Shape", (
|
|
2249
|
-
["shadows", "Shadows", (
|
|
2250
|
-
],
|
|
2251
|
-
summary(
|
|
2252
|
-
const o =
|
|
2253
|
-
|
|
2254
|
-
const
|
|
2255
|
-
|
|
2256
|
-
}), o.append(
|
|
2257
|
-
const
|
|
2258
|
-
const i =
|
|
2259
|
-
|
|
2260
|
-
const
|
|
2261
|
-
|
|
2262
|
-
const
|
|
2263
|
-
|
|
2347
|
+
["spacing", "Spacing", (e) => ({ text: Gn(se(e.density)) })],
|
|
2348
|
+
["shape", "Shape", (e) => ({ text: `${De((e.buttons || {}).corners) || "Default"} corners` })],
|
|
2349
|
+
["shadows", "Shadows", (e) => ({ text: De((e.depth || {}).style) || (Tt(e.character) !== null ? "From the pad" : "Default") })]
|
|
2350
|
+
], So = {
|
|
2351
|
+
summary(e, t, { label: n = "Current look", goTab: a } = {}) {
|
|
2352
|
+
const o = U(n), s = document.createElement("div");
|
|
2353
|
+
s.className = "summary", En.forEach(([i, l]) => {
|
|
2354
|
+
const d = document.createElement("button");
|
|
2355
|
+
d.type = "button", d.className = "summary-row", d.dataset.summary = i, d.innerHTML = `<span class="summary-name"></span><span class="summary-swatch" hidden></span><span class="summary-value"></span><span class="chev" aria-hidden="true">${A.chevron}</span>`, d.querySelector(".summary-name").textContent = l, d.addEventListener("click", () => a && a(i)), s.append(d);
|
|
2356
|
+
}), o.append(s);
|
|
2357
|
+
const r = () => {
|
|
2358
|
+
const i = t.getState();
|
|
2359
|
+
En.forEach(([l, , d]) => {
|
|
2360
|
+
const p = s.querySelector(`[data-summary="${l}"]`), u = d(i);
|
|
2361
|
+
p.querySelector(".summary-value").textContent = u.text;
|
|
2362
|
+
const v = p.querySelector(".summary-swatch");
|
|
2363
|
+
v.hidden = !u.swatch, u.swatch && (v.style.background = u.swatch);
|
|
2264
2364
|
});
|
|
2265
2365
|
};
|
|
2266
|
-
|
|
2366
|
+
r(), t.onChange(r), e.append(o.el);
|
|
2267
2367
|
},
|
|
2268
|
-
character(
|
|
2269
|
-
const a =
|
|
2368
|
+
character(e, t, { label: n = "Character" } = {}) {
|
|
2369
|
+
const a = U(n), o = Wa({
|
|
2270
2370
|
label: n,
|
|
2271
2371
|
ends: { top: "Spacious", bottom: "Compact", left: "Subtle", right: "Bold" },
|
|
2272
|
-
onInput: (
|
|
2273
|
-
const l = +(
|
|
2274
|
-
|
|
2372
|
+
onInput: (r, i) => {
|
|
2373
|
+
const l = +(Ie - i * (Ie - $n)).toFixed(3), d = +r.toFixed(3);
|
|
2374
|
+
t.setSections({
|
|
2275
2375
|
density: { factor: l === 1 ? null : l, spacing: null },
|
|
2276
|
-
character: { bold:
|
|
2376
|
+
character: { bold: d === 0.5 ? null : d }
|
|
2277
2377
|
}, "character");
|
|
2278
2378
|
}
|
|
2279
2379
|
});
|
|
2280
2380
|
a.append(o.el);
|
|
2281
|
-
const
|
|
2282
|
-
const
|
|
2283
|
-
o.setValue(
|
|
2381
|
+
const s = () => {
|
|
2382
|
+
const r = t.getState(), i = se(r.density), l = Tt(r.character), d = l === null ? 0.5 : l, p = `${Gn(i)} · ${ko(d)}`;
|
|
2383
|
+
o.setValue(d, (Ie - i) / (Ie - $n), p), a.setValue(p);
|
|
2284
2384
|
};
|
|
2285
|
-
|
|
2385
|
+
s(), t.onChange(s), e.append(a.el);
|
|
2286
2386
|
}
|
|
2287
|
-
},
|
|
2387
|
+
}, ht = [
|
|
2288
2388
|
{ key: "page-pad", label: "Page padding", step: 5 },
|
|
2289
2389
|
{ key: "section-gap", label: "Section gap", step: 6 },
|
|
2290
2390
|
{ key: "card-pad", label: "Card padding", step: 5 },
|
|
2291
2391
|
{ key: "card-gap", label: "Card gap", step: 2 },
|
|
2292
2392
|
{ key: "control-gap", label: "Control gap", step: 3 },
|
|
2293
2393
|
{ key: "control-pad-x", label: "Control padding", step: null }
|
|
2294
|
-
],
|
|
2295
|
-
const
|
|
2296
|
-
return
|
|
2394
|
+
], $o = [1, 2, 3, 4, 5, 6, 7, 8], Eo = /^var\(\s*--twd-space-(\d)\s*\)$/, ft = (e) => {
|
|
2395
|
+
const t = /^(-?\d*\.?\d+)(px|rem)?$/.exec(String(e).trim());
|
|
2396
|
+
return t ? +(parseFloat(t[1]) * (t[2] === "rem" ? 16 : 1)).toFixed(1) : null;
|
|
2297
2397
|
};
|
|
2298
|
-
function
|
|
2299
|
-
const a = `--twd-${
|
|
2398
|
+
function Co(e, t, n) {
|
|
2399
|
+
const a = `--twd-${t.key}`, o = document.createElement("div");
|
|
2300
2400
|
o.className = "role-row", n || o.classList.add("unused"), o.innerHTML = `
|
|
2301
|
-
<span class="role-label" title="${a}${n ? "" : " (not used on this page)"}">${
|
|
2302
|
-
<label class="token-value role-fixed" hidden><input type="text" inputmode="decimal" spellcheck="false" aria-label="${
|
|
2401
|
+
<span class="role-label" title="${a}${n ? "" : " (not used on this page)"}">${t.label}</span>
|
|
2402
|
+
<label class="token-value role-fixed" hidden><input type="text" inputmode="decimal" spellcheck="false" aria-label="${t.label}, fixed value"><span class="token-unit">px</span></label>
|
|
2303
2403
|
<span class="step-select">
|
|
2304
|
-
<select aria-label="${
|
|
2404
|
+
<select aria-label="${t.label}"></select>
|
|
2305
2405
|
<span class="step-chevron" aria-hidden="true">${A.down}</span>
|
|
2306
2406
|
</span>
|
|
2307
|
-
<button class="icon-btn token-reset" title="Back to the default" aria-label="Reset ${
|
|
2407
|
+
<button class="icon-btn token-reset" title="Back to the default" aria-label="Reset ${t.label}" hidden>${A.reset}</button>
|
|
2308
2408
|
`;
|
|
2309
|
-
const
|
|
2310
|
-
const x =
|
|
2311
|
-
if (x == null) return
|
|
2312
|
-
const b =
|
|
2409
|
+
const s = o.querySelector("select"), r = o.querySelector(".role-fixed"), i = r.querySelector("input"), l = o.querySelector(".token-reset"), d = () => (e.getState().custom || {})[t.key], p = () => {
|
|
2410
|
+
const x = d();
|
|
2411
|
+
if (x == null) return t.step ? { step: t.step } : { fixed: F(a) };
|
|
2412
|
+
const b = Eo.exec(x);
|
|
2313
2413
|
return b ? { step: +b[1] } : { fixed: x };
|
|
2314
|
-
},
|
|
2315
|
-
|
|
2316
|
-
if (
|
|
2317
|
-
const b =
|
|
2318
|
-
|
|
2414
|
+
}, u = (x) => e.setSection("custom", { [t.key]: x }), v = (x) => t.step ? x === `var(--twd-space-${t.step})` : !1;
|
|
2415
|
+
s.addEventListener("change", () => {
|
|
2416
|
+
if (s.value === "fixed") {
|
|
2417
|
+
const b = ft(F(a));
|
|
2418
|
+
u(b != null ? `${b}px` : F(a)), i.focus(), i.select();
|
|
2319
2419
|
return;
|
|
2320
2420
|
}
|
|
2321
|
-
const x = `var(--twd-space-${
|
|
2322
|
-
|
|
2421
|
+
const x = `var(--twd-space-${s.value})`;
|
|
2422
|
+
u(v(x) ? null : x);
|
|
2323
2423
|
});
|
|
2324
2424
|
const f = () => {
|
|
2325
2425
|
const x = i.value.trim();
|
|
2326
|
-
if (!x) return
|
|
2327
|
-
|
|
2426
|
+
if (!x) return u(null);
|
|
2427
|
+
u(/^-?\d*\.?\d+$/.test(x) ? `${x}px` : x);
|
|
2328
2428
|
};
|
|
2329
2429
|
i.addEventListener("change", f), i.addEventListener("blur", () => m()), i.addEventListener("keydown", (x) => {
|
|
2330
2430
|
x.key === "Enter" && (x.preventDefault(), i.blur()), x.key === "Escape" && (x.stopPropagation(), m(), i.blur());
|
|
2331
|
-
}), l.addEventListener("click", () =>
|
|
2431
|
+
}), l.addEventListener("click", () => u(null));
|
|
2332
2432
|
function m() {
|
|
2333
|
-
const x =
|
|
2334
|
-
if (
|
|
2335
|
-
const
|
|
2336
|
-
i.value =
|
|
2433
|
+
const x = p();
|
|
2434
|
+
if (s.innerHTML = $o.map((h) => `<option value="${h}">space-${h} · ${ft(F(`--twd-space-${h}`)) ?? "?"}</option>`).join("") + '<option value="fixed">Fixed</option>', s.value = x.step ? String(x.step) : "fixed", r.hidden = !x.fixed, o.classList.toggle("is-fixed", !!x.fixed), x.fixed && o.getRootNode().activeElement !== i) {
|
|
2435
|
+
const h = ft(x.fixed);
|
|
2436
|
+
i.value = h != null ? String(h) : x.fixed;
|
|
2337
2437
|
}
|
|
2338
|
-
const b =
|
|
2438
|
+
const b = d() != null;
|
|
2339
2439
|
o.classList.toggle("edited", b), l.hidden = !b;
|
|
2340
2440
|
}
|
|
2341
|
-
return m(),
|
|
2441
|
+
return m(), e.onChange(m), o;
|
|
2342
2442
|
}
|
|
2343
|
-
const
|
|
2344
|
-
spacing(
|
|
2345
|
-
const a =
|
|
2443
|
+
const Mo = {
|
|
2444
|
+
spacing(e, t, { label: n = "Roles" } = {}) {
|
|
2445
|
+
const a = _a(), o = U(n);
|
|
2346
2446
|
o.el.classList.add("token-field");
|
|
2347
|
-
const
|
|
2348
|
-
o.setValue(
|
|
2349
|
-
const
|
|
2350
|
-
|
|
2447
|
+
const s = ht.filter((l) => !a.has(`--twd-${l.key}`));
|
|
2448
|
+
o.setValue(s.length === ht.length ? "not used on this page" : "");
|
|
2449
|
+
const r = document.createElement("div");
|
|
2450
|
+
r.className = "token-rows", ht.forEach((l) => r.append(Co(t, l, a.has(`--twd-${l.key}`)))), o.append(r), e.append(o.el);
|
|
2351
2451
|
const i = document.createElement("p");
|
|
2352
|
-
i.className = "role-note", i.textContent =
|
|
2452
|
+
i.className = "role-note", i.textContent = s.length ? `Only elements styled with these tokens change. Not used on this page: ${s.map((l) => l.label).join(", ")}.` : "Each role points at a spacing step, so Density still scales it. Pick another step, or a fixed value.", e.append(i);
|
|
2353
2453
|
}
|
|
2354
|
-
},
|
|
2454
|
+
}, ye = [
|
|
2355
2455
|
{ id: "overview", title: "Overview", icon: "tabOverview", parts: [["simple", "character"], ["simple", "summary"]] },
|
|
2356
2456
|
{ id: "colour", title: "Colour", icon: "tabColour", parts: [
|
|
2357
2457
|
["colour", "themeTiles"],
|
|
@@ -2391,18 +2491,18 @@ const go = {
|
|
|
2391
2491
|
["depth", "intensity"],
|
|
2392
2492
|
{ more: "Shadow tokens", parts: [["custom", "shadows"]] }
|
|
2393
2493
|
] }
|
|
2394
|
-
], O = (
|
|
2395
|
-
function
|
|
2494
|
+
], O = (e) => String(e).replace(/[&<>"]/g, (t) => ({ "&": "&", "<": "<", ">": ">", '"': """ })[t]);
|
|
2495
|
+
function To({ sections: e, store: t, view: n }) {
|
|
2396
2496
|
const a = document.createElement("div");
|
|
2397
2497
|
a.id = "twiddle-root", document.body.appendChild(a);
|
|
2398
|
-
const o = a.attachShadow({ mode: "open" }),
|
|
2498
|
+
const o = a.attachShadow({ mode: "open" }), s = (c) => c.map((g) => `
|
|
2399
2499
|
<details data-section="${g.id}">
|
|
2400
2500
|
<summary>${g.title}<span class="chev">${A.chevron}</span></summary>
|
|
2401
2501
|
<div class="section-body"><div class="placeholder">${g.hint}</div></div>
|
|
2402
2502
|
</details>
|
|
2403
2503
|
`).join("");
|
|
2404
2504
|
o.innerHTML = `
|
|
2405
|
-
<style>${
|
|
2505
|
+
<style>${va}</style>
|
|
2406
2506
|
<div class="pick-box" hidden><span class="pick-tag"></span></div>
|
|
2407
2507
|
<div class="wrap">
|
|
2408
2508
|
<div class="toast" role="status"></div>
|
|
@@ -2413,6 +2513,7 @@ function mo({ sections: t, store: e, view: n }) {
|
|
|
2413
2513
|
<button class="icon-btn" data-action="compare" title="Hold to see the original (Alt+C toggles)" aria-label="Compare with original" aria-pressed="false">${A.compare}</button>
|
|
2414
2514
|
<button class="icon-btn" data-action="copy" title="Copy link to this version" aria-label="Copy link">${A.link}</button>
|
|
2415
2515
|
<button class="icon-btn" data-action="reset" title="Reset to defaults" aria-label="Reset">${A.reset}</button>
|
|
2516
|
+
<button class="icon-btn" data-action="minimise" title="Minimise to the tabs" aria-label="Minimise to the tabs">${A.minimise}</button>
|
|
2416
2517
|
<button class="icon-btn" data-action="close" title="Close (Alt+T)" aria-label="Close">${A.close}</button>
|
|
2417
2518
|
</div>
|
|
2418
2519
|
<div class="actions">
|
|
@@ -2431,15 +2532,15 @@ function mo({ sections: t, store: e, view: n }) {
|
|
|
2431
2532
|
</div>
|
|
2432
2533
|
<div class="original-pill" hidden>Showing original</div>
|
|
2433
2534
|
<div class="body" data-view="global"><div class="setup-hint" role="status" hidden></div>
|
|
2434
|
-
${
|
|
2535
|
+
${ye.map((c) => `<div class="page page-fields" data-page="${c.id}" hidden></div>`).join("")}
|
|
2435
2536
|
</div>
|
|
2436
2537
|
<div class="body" data-view="local" hidden></div>
|
|
2437
2538
|
<div class="body" data-view="changes" hidden></div>
|
|
2438
2539
|
<nav class="tabbar" aria-label="Areas">
|
|
2439
|
-
${
|
|
2540
|
+
${ye.map((c) => `<button class="tab" data-tab="${c.id}" aria-pressed="false">${A[c.icon]}<span>${c.title}</span></button>`).join("")}
|
|
2440
2541
|
</nav>
|
|
2441
2542
|
</div>
|
|
2442
|
-
<button class="launcher" aria-label="Open Panel" aria-expanded="false">${A.panelMark}</button>
|
|
2543
|
+
<button class="launcher" aria-label="Open Panel" aria-expanded="false" title="Panel · drag to move, double-click to reset">${A.panelMark}</button>
|
|
2443
2544
|
</div>
|
|
2444
2545
|
<div class="dialog-backdrop" hidden>
|
|
2445
2546
|
<div class="dialog" role="dialog" aria-modal="true" aria-labelledby="twd-dialog-title">
|
|
@@ -2455,160 +2556,168 @@ function mo({ sections: t, store: e, view: n }) {
|
|
|
2455
2556
|
</div>
|
|
2456
2557
|
</div>
|
|
2457
2558
|
`;
|
|
2458
|
-
const
|
|
2459
|
-
function m(
|
|
2559
|
+
const r = (c) => o.querySelector(c), i = r(".panel"), l = r(".launcher"), d = r(".toast"), p = { global: r('[data-view="global"]'), local: r('[data-view="local"]'), changes: r('[data-view="changes"]') }, u = r(".pick-box"), v = r(".pick-tag"), f = { getState: t.getState, setSection: t.setSection, setSections: t.setSections, onChange: t.onChange };
|
|
2560
|
+
function m(c, g, w) {
|
|
2460
2561
|
g.forEach((k) => {
|
|
2461
2562
|
if (typeof k.render != "function") return;
|
|
2462
|
-
const C =
|
|
2563
|
+
const C = c.querySelector(`[data-section="${k.id}"] .section-body`);
|
|
2463
2564
|
C.innerHTML = "", k.render(C, w);
|
|
2464
2565
|
});
|
|
2465
|
-
const $ = [...
|
|
2566
|
+
const $ = [...c.querySelectorAll("details")];
|
|
2466
2567
|
return $.forEach((k) => k.addEventListener("toggle", () => {
|
|
2467
2568
|
k.open && $.forEach((C) => {
|
|
2468
2569
|
C !== k && (C.open = !1);
|
|
2469
2570
|
});
|
|
2470
2571
|
})), $;
|
|
2471
2572
|
}
|
|
2472
|
-
const x = Object.fromEntries(
|
|
2473
|
-
function b(
|
|
2573
|
+
const x = Object.fromEntries(e.map((c) => [c.id, c]));
|
|
2574
|
+
function b(c, g) {
|
|
2474
2575
|
g.forEach((w) => {
|
|
2475
2576
|
if (w === "warnings") {
|
|
2476
2577
|
const S = document.createElement("div");
|
|
2477
|
-
S.className = "warnings page-warnings",
|
|
2578
|
+
S.className = "warnings page-warnings", c.append(S);
|
|
2478
2579
|
return;
|
|
2479
2580
|
}
|
|
2480
2581
|
if (w.heading) {
|
|
2481
2582
|
const S = document.createElement("div");
|
|
2482
|
-
S.className = "page-heading", S.innerHTML = `<span></span>${w.note ? '<span class="muted"></span>' : ""}`, S.firstChild.textContent = w.heading, w.note && (S.lastChild.textContent = w.note),
|
|
2583
|
+
S.className = "page-heading", S.innerHTML = `<span></span>${w.note ? '<span class="muted"></span>' : ""}`, S.firstChild.textContent = w.heading, w.note && (S.lastChild.textContent = w.note), c.append(S);
|
|
2483
2584
|
return;
|
|
2484
2585
|
}
|
|
2485
2586
|
if (w.more) {
|
|
2486
2587
|
const S = document.createElement("details");
|
|
2487
|
-
S.className = "more", S.innerHTML = `<summary><span class="chev" aria-hidden="true">${A.chevron}</span><span></span></summary><div class="more-body"></div>`, S.querySelector("summary span:last-child").textContent = w.more, b(S.querySelector(".more-body"), w.parts),
|
|
2588
|
+
S.className = "more", S.innerHTML = `<summary><span class="chev" aria-hidden="true">${A.chevron}</span><span></span></summary><div class="more-body"></div>`, S.querySelector("summary span:last-child").textContent = w.more, b(S.querySelector(".more-body"), w.parts), c.append(S);
|
|
2488
2589
|
return;
|
|
2489
2590
|
}
|
|
2490
2591
|
if (w.tabs) {
|
|
2491
2592
|
const S = w.tabs.map((j) => {
|
|
2492
2593
|
const z = document.createElement("div");
|
|
2493
2594
|
return z.className = "tab-pane", b(z, j.parts), z;
|
|
2494
|
-
}),
|
|
2595
|
+
}), be = ce({
|
|
2495
2596
|
label: "View",
|
|
2496
2597
|
options: w.tabs.map((j, z) => ({ value: String(z), label: j.label })),
|
|
2497
2598
|
onSelect: (j) => {
|
|
2498
|
-
|
|
2499
|
-
z.hidden = String(
|
|
2599
|
+
be.setValue(j), S.forEach((z, ra) => {
|
|
2600
|
+
z.hidden = String(ra) !== j;
|
|
2500
2601
|
});
|
|
2501
2602
|
}
|
|
2502
2603
|
});
|
|
2503
|
-
|
|
2604
|
+
be.setValue("0"), S.forEach((j, z) => {
|
|
2504
2605
|
j.hidden = z !== 0;
|
|
2505
|
-
}),
|
|
2606
|
+
}), c.append(be.el, ...S);
|
|
2506
2607
|
return;
|
|
2507
2608
|
}
|
|
2508
|
-
const [$, k, C = {}] = w, q = $ === "simple" ?
|
|
2509
|
-
q && q(
|
|
2510
|
-
|
|
2609
|
+
const [$, k, C = {}] = w, q = $ === "simple" ? So[k] : $ === "roles" ? Mo[k] : x[$]?.parts?.[k];
|
|
2610
|
+
q && q(c, f, { ...C, buttons: x.buttons, cards: x.cards, goTab: (S) => {
|
|
2611
|
+
W("global"), E(S);
|
|
2511
2612
|
} });
|
|
2512
2613
|
});
|
|
2513
2614
|
}
|
|
2514
|
-
|
|
2515
|
-
const
|
|
2516
|
-
let
|
|
2615
|
+
ye.forEach((c) => b(p.global.querySelector(`[data-page="${c.id}"]`), c.parts));
|
|
2616
|
+
const h = [...o.querySelectorAll(".tab")];
|
|
2617
|
+
let y = ye.some((c) => c.id === Z("tab")) ? Z("tab") : ye[0].id;
|
|
2517
2618
|
function L() {
|
|
2518
|
-
|
|
2619
|
+
h.forEach((c) => c.setAttribute("aria-pressed", String(K === "global" && c.dataset.tab === y)));
|
|
2519
2620
|
}
|
|
2520
|
-
function E(
|
|
2521
|
-
|
|
2522
|
-
g.hidden = g.dataset.page !==
|
|
2523
|
-
}),
|
|
2621
|
+
function E(c) {
|
|
2622
|
+
y = c, oe("tab", c), p.global.querySelectorAll(".page").forEach((g) => {
|
|
2623
|
+
g.hidden = g.dataset.page !== c;
|
|
2624
|
+
}), p.global.scrollTop = 0, L();
|
|
2524
2625
|
}
|
|
2525
|
-
const
|
|
2626
|
+
const ee = (c) => c.map((g) => `<div class="warning">${A.warn}<span>${O(g.text)}</span></div>`).join("");
|
|
2526
2627
|
let K = "global";
|
|
2527
|
-
function
|
|
2528
|
-
K =
|
|
2529
|
-
w.hidden = g !==
|
|
2530
|
-
}),
|
|
2531
|
-
}
|
|
2532
|
-
E(
|
|
2533
|
-
const
|
|
2534
|
-
function
|
|
2628
|
+
function W(c) {
|
|
2629
|
+
K = c, Object.entries(p).forEach(([g, w]) => {
|
|
2630
|
+
w.hidden = g !== c;
|
|
2631
|
+
}), r('[data-action="changes"]').setAttribute("aria-pressed", String(c === "changes")), c !== "local" && Qn(), c === "changes" && Wt(), L();
|
|
2632
|
+
}
|
|
2633
|
+
E(y);
|
|
2634
|
+
const Ke = "cubic-bezier(0.3, 0.7, 0.2, 1)", We = "cubic-bezier(0.2, 0.8, 0.2, 1)", Ee = 640, ue = 100, Ge = 50, Yn = () => l.querySelector(".mark-knob"), Xn = () => [l.querySelector(".mark-t1"), l.querySelector(".mark-t2")];
|
|
2635
|
+
function At(c) {
|
|
2535
2636
|
if (matchMedia("(prefers-reduced-motion: reduce)").matches || !l.animate) return;
|
|
2536
|
-
const g =
|
|
2537
|
-
if (
|
|
2538
|
-
g.animate([{ transform: k(32) }, { transform: k(-6), offset: 0.78 }, { transform: k(0) }], { duration:
|
|
2637
|
+
const g = Yn(), [w, $] = Xn(), k = (C) => `translateY(${C}px)`;
|
|
2638
|
+
if (c) {
|
|
2639
|
+
g.animate([{ transform: k(32) }, { transform: k(-6), offset: 0.78 }, { transform: k(0) }], { duration: Ee, easing: Ke });
|
|
2539
2640
|
const C = [{ transform: "scale(0.4)", opacity: 0 }, { transform: "scale(1)", opacity: 1 }];
|
|
2540
|
-
$.animate(C, { duration:
|
|
2641
|
+
$.animate(C, { duration: ue * 2, delay: Ee * 0.12, easing: Ke, fill: "backwards" }), w.animate(C, { duration: ue * 2, delay: Ee * 0.32 + Ge, easing: Ke, fill: "backwards" });
|
|
2541
2642
|
} else {
|
|
2542
2643
|
const C = [{ opacity: 1 }, { opacity: 0 }];
|
|
2543
|
-
w.animate(C, { duration:
|
|
2644
|
+
w.animate(C, { duration: ue, easing: We }), $.animate(C, { duration: ue, delay: Ge / 2, easing: We, fill: "backwards" }), g.animate([{ transform: k(0) }, { transform: k(34), offset: 0.8 }, { transform: k(32) }], { duration: Ee * 0.7, delay: ue + Ge / 2, easing: We, fill: "backwards" });
|
|
2544
2645
|
}
|
|
2545
2646
|
}
|
|
2546
|
-
|
|
2547
|
-
|
|
2647
|
+
const Ye = r(".wrap"), Lt = $a({ wrapEl: Ye, launcherEl: l });
|
|
2648
|
+
let Pt = !1;
|
|
2649
|
+
function Ce(c) {
|
|
2650
|
+
Pt = c, i.classList.toggle("minimised", c), i.hidden || oe("panel", c ? "tabs" : "open");
|
|
2651
|
+
}
|
|
2652
|
+
function G({ animate: c = !0 } = {}) {
|
|
2653
|
+
i.hidden && c && At(!0), i.hidden = !1, Ye.classList.add("is-open"), l.setAttribute("aria-expanded", "true"), l.setAttribute("aria-label", "Close Panel"), Ce(!1);
|
|
2548
2654
|
}
|
|
2549
|
-
function
|
|
2550
|
-
i.hidden
|
|
2655
|
+
function Zn() {
|
|
2656
|
+
i.hidden && G(), Ce(!0);
|
|
2551
2657
|
}
|
|
2552
|
-
function
|
|
2553
|
-
i.hidden
|
|
2658
|
+
function Xe() {
|
|
2659
|
+
i.hidden || At(!1), i.hidden = !0, Ye.classList.remove("is-open"), l.setAttribute("aria-expanded", "false"), l.setAttribute("aria-label", "Open Panel"), oe("panel", "closed"), Te();
|
|
2554
2660
|
}
|
|
2555
|
-
|
|
2556
|
-
|
|
2557
|
-
p.textContent = d, p.classList.add("show"), clearTimeout(Ct), Ct = setTimeout(() => p.classList.remove("show"), 1600);
|
|
2661
|
+
function Me() {
|
|
2662
|
+
i.hidden ? G() : Xe();
|
|
2558
2663
|
}
|
|
2559
|
-
|
|
2560
|
-
|
|
2664
|
+
let Ot;
|
|
2665
|
+
function H(c) {
|
|
2666
|
+
d.textContent = c, d.classList.add("show"), clearTimeout(Ot), Ot = setTimeout(() => d.classList.remove("show"), 1600);
|
|
2561
2667
|
}
|
|
2562
|
-
|
|
2563
|
-
|
|
2564
|
-
|
|
2565
|
-
|
|
2668
|
+
function he(c, g) {
|
|
2669
|
+
navigator.clipboard.writeText(c).then(() => H(g)).catch(() => H("Couldn’t copy — select the text and copy it instead"));
|
|
2670
|
+
}
|
|
2671
|
+
let Ze = null;
|
|
2672
|
+
function te(c, g) {
|
|
2673
|
+
if (Ze = c, !c) {
|
|
2674
|
+
u.hidden = !0;
|
|
2566
2675
|
return;
|
|
2567
2676
|
}
|
|
2568
|
-
const w =
|
|
2569
|
-
Object.assign(
|
|
2677
|
+
const w = c.getBoundingClientRect();
|
|
2678
|
+
Object.assign(u.style, { top: `${w.top}px`, left: `${w.left}px`, width: `${w.width}px`, height: `${w.height}px` }), v.textContent = g || vt(c), u.hidden = !1;
|
|
2570
2679
|
}
|
|
2571
|
-
const
|
|
2572
|
-
|
|
2680
|
+
const Ft = () => {
|
|
2681
|
+
Ze && !u.hidden && te(Ze, v.textContent);
|
|
2573
2682
|
};
|
|
2574
|
-
window.addEventListener("scroll",
|
|
2575
|
-
let
|
|
2576
|
-
const
|
|
2577
|
-
function
|
|
2578
|
-
if (
|
|
2579
|
-
const g =
|
|
2580
|
-
g && g.nodeType === 1 && g !== document.documentElement && g !== document.body &&
|
|
2581
|
-
}
|
|
2582
|
-
function
|
|
2583
|
-
if (
|
|
2584
|
-
const g =
|
|
2585
|
-
!g || g === document.documentElement || g === document.body || (
|
|
2586
|
-
}
|
|
2587
|
-
function
|
|
2588
|
-
|
|
2589
|
-
}
|
|
2590
|
-
const
|
|
2591
|
-
function
|
|
2592
|
-
if (!
|
|
2593
|
-
if (
|
|
2594
|
-
const
|
|
2595
|
-
|
|
2683
|
+
window.addEventListener("scroll", Ft, !0), window.addEventListener("resize", Ft);
|
|
2684
|
+
let fe = !1;
|
|
2685
|
+
const Nt = r('[data-action="pick"]'), jt = (c) => c.composedPath().includes(a);
|
|
2686
|
+
function Rt(c) {
|
|
2687
|
+
if (jt(c)) return;
|
|
2688
|
+
const g = c.target;
|
|
2689
|
+
g && g.nodeType === 1 && g !== document.documentElement && g !== document.body && te(g);
|
|
2690
|
+
}
|
|
2691
|
+
function zt(c) {
|
|
2692
|
+
if (jt(c) || (c.preventDefault(), c.stopPropagation(), c.type !== "click")) return;
|
|
2693
|
+
const g = c.target;
|
|
2694
|
+
!g || g === document.documentElement || g === document.body || (Te(), Bt(g));
|
|
2695
|
+
}
|
|
2696
|
+
function It(c) {
|
|
2697
|
+
c.key === "Escape" && (c.preventDefault(), Te(), T && te(T.el));
|
|
2698
|
+
}
|
|
2699
|
+
const Dt = ["pointerdown", "mousedown", "mouseup", "click"];
|
|
2700
|
+
function Ut() {
|
|
2701
|
+
if (!fe) {
|
|
2702
|
+
if (fe = !0, !document.getElementById("twiddle-picking")) {
|
|
2703
|
+
const c = document.createElement("style");
|
|
2704
|
+
c.id = "twiddle-picking", c.textContent = "html.twd-picking, html.twd-picking * { cursor: crosshair !important; }", document.head.appendChild(c);
|
|
2596
2705
|
}
|
|
2597
|
-
|
|
2706
|
+
G(), Nt.setAttribute("aria-pressed", "true"), document.documentElement.classList.add("twd-picking"), document.addEventListener("pointermove", Rt, !0), Dt.forEach((c) => document.addEventListener(c, zt, !0)), document.addEventListener("keydown", It, !0), H("Click an element · Esc to cancel");
|
|
2598
2707
|
}
|
|
2599
2708
|
}
|
|
2600
|
-
function
|
|
2601
|
-
|
|
2709
|
+
function Te() {
|
|
2710
|
+
fe && (fe = !1, Nt.setAttribute("aria-pressed", "false"), document.documentElement.classList.remove("twd-picking"), document.removeEventListener("pointermove", Rt, !0), Dt.forEach((c) => document.removeEventListener(c, zt, !0)), document.removeEventListener("keydown", It, !0), te(T ? T.el : null));
|
|
2602
2711
|
}
|
|
2603
|
-
let
|
|
2604
|
-
function
|
|
2712
|
+
let T = null;
|
|
2713
|
+
function Jn(c, g) {
|
|
2605
2714
|
const w = [];
|
|
2606
2715
|
return {
|
|
2607
2716
|
ctx: {
|
|
2608
|
-
getState: () => (
|
|
2609
|
-
setSection: ($, k) =>
|
|
2717
|
+
getState: () => (t.getState().scopes || {})[c] || {},
|
|
2718
|
+
setSection: ($, k) => t.setScope(c, $, k),
|
|
2610
2719
|
onChange: ($) => {
|
|
2611
|
-
const k =
|
|
2720
|
+
const k = t.onChange($);
|
|
2612
2721
|
return w.push(k), k;
|
|
2613
2722
|
},
|
|
2614
2723
|
readToken: ($) => getComputedStyle(g).getPropertyValue($).trim()
|
|
@@ -2616,81 +2725,81 @@ function mo({ sections: t, store: e, view: n }) {
|
|
|
2616
2725
|
dispose: () => w.forEach(($) => $())
|
|
2617
2726
|
};
|
|
2618
2727
|
}
|
|
2619
|
-
function
|
|
2620
|
-
const w =
|
|
2621
|
-
g || (g = !$[w.elementSelector] && w.classSelector && $[w.classSelector] ? "class" : "element"),
|
|
2622
|
-
} },
|
|
2728
|
+
function Bt(c, g) {
|
|
2729
|
+
const w = La(c), $ = t.getState().scopes || {};
|
|
2730
|
+
g || (g = !$[w.elementSelector] && w.classSelector && $[w.classSelector] ? "class" : "element"), T && T.dispose(), T = { el: c, info: w, target: g, dispose: () => {
|
|
2731
|
+
} }, Je(), W("local"), G(), te(c);
|
|
2623
2732
|
}
|
|
2624
|
-
const
|
|
2625
|
-
function
|
|
2626
|
-
|
|
2627
|
-
const { info:
|
|
2733
|
+
const qt = () => T.target === "class" ? T.info.classSelector : T.info.elementSelector;
|
|
2734
|
+
function Je() {
|
|
2735
|
+
T.dispose();
|
|
2736
|
+
const { info: c } = T, g = e.filter((j) => (j.uses || []).some((z) => c.tokens.has(z))), w = p.local;
|
|
2628
2737
|
if (w.innerHTML = `
|
|
2629
2738
|
<div class="scope-bar">
|
|
2630
2739
|
<div class="scope-head">
|
|
2631
2740
|
<button class="icon-btn" data-action="back" title="Back to the whole page" aria-label="Back to the whole page">${A.back}</button>
|
|
2632
2741
|
<div class="scope-name">
|
|
2633
|
-
<code>${O(
|
|
2634
|
-
${
|
|
2742
|
+
<code>${O(vt(c.el))}</code>
|
|
2743
|
+
${c.text ? `<span class="muted">“${O(c.text)}”</span>` : ""}
|
|
2635
2744
|
</div>
|
|
2636
2745
|
</div>
|
|
2637
2746
|
<div class="scope-target"></div>
|
|
2638
2747
|
<div class="warnings"></div>
|
|
2639
2748
|
</div>
|
|
2640
|
-
${g.length ?
|
|
2641
|
-
`,
|
|
2642
|
-
const j =
|
|
2749
|
+
${g.length ? s(g) : '<div class="empty">This element doesn’t use Panel tokens yet, so there’s nothing to tweak here. Style it with <code>var(--twd-…)</code> to make it tweakable.</div>'}
|
|
2750
|
+
`, c.classSelector) {
|
|
2751
|
+
const j = ce({
|
|
2643
2752
|
label: "Apply to",
|
|
2644
2753
|
options: [
|
|
2645
2754
|
{ value: "element", label: "This element" },
|
|
2646
|
-
{ value: "class", label: `All ${
|
|
2755
|
+
{ value: "class", label: `All ${c.classSelector} (${c.classCount})`, title: `Every element matching ${c.classSelector}` }
|
|
2647
2756
|
],
|
|
2648
2757
|
onSelect: (z) => {
|
|
2649
|
-
z === "class" &&
|
|
2758
|
+
z === "class" && t.moveScope(c.elementSelector, c.classSelector), T.target = z, Je();
|
|
2650
2759
|
}
|
|
2651
2760
|
});
|
|
2652
|
-
j.setValue(
|
|
2761
|
+
j.setValue(T.target), w.querySelector(".scope-target").append(j.el);
|
|
2653
2762
|
}
|
|
2654
|
-
const { ctx: $, dispose: k } =
|
|
2763
|
+
const { ctx: $, dispose: k } = Jn(qt(), c.el), C = m(w, g, $);
|
|
2655
2764
|
C[0] && (C[0].open = !0);
|
|
2656
2765
|
const q = w.querySelector(".warnings"), S = () => {
|
|
2657
|
-
const j =
|
|
2658
|
-
q.innerHTML = j ?
|
|
2766
|
+
const j = c.tokens.size ? Da(c.el) : null;
|
|
2767
|
+
q.innerHTML = j ? ee([j]) : "";
|
|
2659
2768
|
};
|
|
2660
2769
|
S();
|
|
2661
|
-
const
|
|
2662
|
-
|
|
2663
|
-
k(),
|
|
2770
|
+
const be = t.onChange(S);
|
|
2771
|
+
T.dispose = () => {
|
|
2772
|
+
k(), be();
|
|
2664
2773
|
};
|
|
2665
2774
|
}
|
|
2666
|
-
function
|
|
2667
|
-
if (!
|
|
2668
|
-
const
|
|
2669
|
-
!
|
|
2670
|
-
}
|
|
2671
|
-
const
|
|
2672
|
-
|
|
2673
|
-
},
|
|
2674
|
-
|
|
2775
|
+
function Ht() {
|
|
2776
|
+
if (!T || !T.info.classSelector) return;
|
|
2777
|
+
const c = t.getState().scopes || {}, g = T.target === "class" ? "element" : "class", w = g === "class" ? T.info.classSelector : T.info.elementSelector;
|
|
2778
|
+
!c[qt()] && c[w] && (T.target = g, Je());
|
|
2779
|
+
}
|
|
2780
|
+
const Vt = () => {
|
|
2781
|
+
t.undo(), Ht();
|
|
2782
|
+
}, _t = () => {
|
|
2783
|
+
t.redo(), Ht();
|
|
2675
2784
|
};
|
|
2676
|
-
function
|
|
2677
|
-
|
|
2678
|
-
}
|
|
2679
|
-
const
|
|
2680
|
-
function
|
|
2681
|
-
const
|
|
2682
|
-
if (!
|
|
2683
|
-
w.innerHTML = `<div class="empty">No changes yet. Tweak a section, or pick an element to change it on its own.</div>${
|
|
2785
|
+
function Qn() {
|
|
2786
|
+
T && (T.dispose(), T = null, p.local.innerHTML = "", te(null));
|
|
2787
|
+
}
|
|
2788
|
+
const Kt = (c) => c.kind === "global" ? "Whole page" : c.kind === "class" ? `All ${c.selector}` : "One element";
|
|
2789
|
+
function Wt() {
|
|
2790
|
+
const c = ln(t.getState(), e), g = nt(), w = p.changes;
|
|
2791
|
+
if (!c.length) {
|
|
2792
|
+
w.innerHTML = `<div class="empty">No changes yet. Tweak a section, or pick an element to change it on its own.</div>${ee(g)}`;
|
|
2684
2793
|
return;
|
|
2685
2794
|
}
|
|
2686
2795
|
const $ = /* @__PURE__ */ new Map();
|
|
2687
|
-
|
|
2796
|
+
c.forEach((k) => {
|
|
2688
2797
|
const C = k.selector || "";
|
|
2689
2798
|
$.has(C) || $.set(C, []), $.get(C).push(k);
|
|
2690
|
-
}), w.innerHTML = (g.length ? `<div class="warnings">${
|
|
2799
|
+
}), w.innerHTML = (g.length ? `<div class="warnings">${ee(g)}</div>` : "") + [...$].map(([k, C]) => `
|
|
2691
2800
|
<div class="change-group">
|
|
2692
2801
|
<div class="change-scope">
|
|
2693
|
-
${k ? `<button class="link-btn" data-select="${O(k)}" title="Select it on the page">${O(
|
|
2802
|
+
${k ? `<button class="link-btn" data-select="${O(k)}" title="Select it on the page">${O(Kt(C[0]))}</button><code>${O(k)}</code>` : `<span>${Kt(C[0])}</span>`}
|
|
2694
2803
|
</div>
|
|
2695
2804
|
${C.map((q) => `
|
|
2696
2805
|
<div class="change-row">
|
|
@@ -2702,65 +2811,65 @@ function mo({ sections: t, store: e, view: n }) {
|
|
|
2702
2811
|
</div>`).join("")}
|
|
2703
2812
|
</div>`).join("");
|
|
2704
2813
|
}
|
|
2705
|
-
function
|
|
2706
|
-
if (g) return
|
|
2707
|
-
const w =
|
|
2708
|
-
|
|
2814
|
+
function ea(c, g) {
|
|
2815
|
+
if (g) return t.removeScope(g, c);
|
|
2816
|
+
const w = t.getState()[c] || {};
|
|
2817
|
+
t.setSection(c, Object.fromEntries(Object.keys(w).map(($) => [$, null])));
|
|
2709
2818
|
}
|
|
2710
|
-
const
|
|
2711
|
-
function
|
|
2712
|
-
|
|
2819
|
+
const ge = r(".apply-menu"), Gt = r('[data-action="apply-menu"]'), Yt = () => Z("apply-mode") === "copy" ? "copy" : "preview";
|
|
2820
|
+
function ta() {
|
|
2821
|
+
ge.querySelectorAll("[data-apply-mode]").forEach((c) => c.setAttribute("aria-checked", String(c.dataset.applyMode === Yt())));
|
|
2713
2822
|
}
|
|
2714
|
-
function
|
|
2715
|
-
|
|
2823
|
+
function Ae(c) {
|
|
2824
|
+
ge.hidden = !c, Gt.setAttribute("aria-expanded", String(c)), c && ta();
|
|
2716
2825
|
}
|
|
2717
|
-
function
|
|
2718
|
-
return
|
|
2719
|
-
}
|
|
2720
|
-
const
|
|
2721
|
-
function
|
|
2722
|
-
const
|
|
2723
|
-
if (!
|
|
2724
|
-
if (
|
|
2725
|
-
|
|
2726
|
-
}
|
|
2727
|
-
function
|
|
2728
|
-
|
|
2729
|
-
}
|
|
2730
|
-
const
|
|
2731
|
-
function
|
|
2732
|
-
|
|
2733
|
-
}
|
|
2734
|
-
let
|
|
2735
|
-
|
|
2736
|
-
|
|
2737
|
-
|
|
2826
|
+
function Xt() {
|
|
2827
|
+
return ja(ln(t.getState(), e), { warnings: nt() });
|
|
2828
|
+
}
|
|
2829
|
+
const Le = r(".dialog-backdrop"), Zt = r(".prompt-text");
|
|
2830
|
+
function na() {
|
|
2831
|
+
const c = Xt();
|
|
2832
|
+
if (!c) return H("No changes to copy yet");
|
|
2833
|
+
if (Yt() === "copy") return he(c, "Prompt copied · paste it into your agent");
|
|
2834
|
+
Zt.value = c, Le.hidden = !1, r('[data-action="dialog-copy"]').focus();
|
|
2835
|
+
}
|
|
2836
|
+
function Qe() {
|
|
2837
|
+
Le.hidden = !0, r('[data-action="apply"]').focus();
|
|
2838
|
+
}
|
|
2839
|
+
const ne = r('[data-action="compare"]'), aa = r(".original-pill");
|
|
2840
|
+
function Pe(c) {
|
|
2841
|
+
c ? n.suspend() : n.resume(), ne.setAttribute("aria-pressed", String(c)), aa.hidden = !c;
|
|
2842
|
+
}
|
|
2843
|
+
let Jt = null, me = !1;
|
|
2844
|
+
ne.addEventListener("pointerdown", () => {
|
|
2845
|
+
me = !1, Jt = setTimeout(() => {
|
|
2846
|
+
me = !0, Pe(!0);
|
|
2738
2847
|
}, 180);
|
|
2739
2848
|
});
|
|
2740
|
-
const
|
|
2741
|
-
clearTimeout(
|
|
2849
|
+
const Qt = () => {
|
|
2850
|
+
clearTimeout(Jt), me && (Pe(!1), me = !1);
|
|
2742
2851
|
};
|
|
2743
|
-
|
|
2744
|
-
const
|
|
2852
|
+
ne.addEventListener("pointerup", Qt), ne.addEventListener("pointerleave", Qt);
|
|
2853
|
+
const Oe = r(".setup-hint"), en = `setup-hint-dismissed:${location.pathname}`, oa = {
|
|
2745
2854
|
"no-tokens": {
|
|
2746
2855
|
title: "The Panel tokens aren’t loaded",
|
|
2747
2856
|
text: "The knobs change the --twd-* variables, but this page doesn’t load them. Add the token stylesheet before your own CSS:",
|
|
2748
|
-
snippets:
|
|
2749
|
-
link: `${
|
|
2857
|
+
snippets: fn,
|
|
2858
|
+
link: `${hn}/#install`
|
|
2750
2859
|
},
|
|
2751
2860
|
unused: {
|
|
2752
2861
|
title: "Nothing here uses the Panel tokens yet",
|
|
2753
2862
|
text: "The tokens are loaded, but no style on this page uses var(--twd-…), so the knobs won’t change anything. Ask your coding agent to convert the styles:",
|
|
2754
2863
|
copy: "Copy prompt for your agent",
|
|
2755
2864
|
copied: "Prompt copied",
|
|
2756
|
-
text2copy:
|
|
2757
|
-
link: `${
|
|
2865
|
+
text2copy: Ka,
|
|
2866
|
+
link: `${hn}/#tokens`
|
|
2758
2867
|
}
|
|
2759
2868
|
};
|
|
2760
2869
|
let D = null;
|
|
2761
|
-
function
|
|
2762
|
-
const
|
|
2763
|
-
D =
|
|
2870
|
+
function Fe() {
|
|
2871
|
+
const c = Va();
|
|
2872
|
+
D = Z(en) === c ? null : oa[c] || null, l.toggleAttribute("data-hint", !!D), Oe.hidden = !D, D && (Oe.innerHTML = `
|
|
2764
2873
|
<div class="setup-head">${A.warn}<b>${O(D.title)}</b>
|
|
2765
2874
|
<button class="icon-btn" data-action="setup-dismiss" title="Hide this message" aria-label="Hide this message">${A.close}</button>
|
|
2766
2875
|
</div>
|
|
@@ -2773,103 +2882,106 @@ function mo({ sections: t, store: e, view: n }) {
|
|
|
2773
2882
|
<div class="setup-actions">
|
|
2774
2883
|
${D.copy ? `<button class="apply-btn" data-action="setup-copy">${O(D.copy)}</button>` : ""}
|
|
2775
2884
|
<a href="${D.link}" target="_blank" rel="noopener">Setup guide</a>
|
|
2776
|
-
</div>`,
|
|
2777
|
-
}
|
|
2778
|
-
|
|
2779
|
-
const
|
|
2780
|
-
function
|
|
2781
|
-
|
|
2782
|
-
const
|
|
2783
|
-
|
|
2784
|
-
const g =
|
|
2785
|
-
|
|
2885
|
+
</div>`, Oe.dataset.status = c);
|
|
2886
|
+
}
|
|
2887
|
+
Fe(), document.readyState !== "complete" && window.addEventListener("load", Fe, { once: !0 }), setTimeout(Fe, 1500);
|
|
2888
|
+
const tn = r(".badge");
|
|
2889
|
+
function nn() {
|
|
2890
|
+
r('[data-action="undo"]').disabled = !t.canUndo(), r('[data-action="redo"]').disabled = !t.canRedo();
|
|
2891
|
+
const c = Fa(t.getState());
|
|
2892
|
+
tn.textContent = c, tn.hidden = !c;
|
|
2893
|
+
const g = ee(nt());
|
|
2894
|
+
p.global.querySelectorAll(".page-warnings").forEach((w) => {
|
|
2786
2895
|
w.innerHTML = g;
|
|
2787
|
-
}), K === "changes" &&
|
|
2896
|
+
}), K === "changes" && Wt();
|
|
2788
2897
|
}
|
|
2789
|
-
return
|
|
2790
|
-
if (
|
|
2791
|
-
const g =
|
|
2898
|
+
return t.onChange(nn), nn(), o.addEventListener("click", (c) => {
|
|
2899
|
+
if (c.target === Le) return Qe();
|
|
2900
|
+
const g = c.target.closest("button");
|
|
2792
2901
|
if (g) {
|
|
2793
|
-
if (g !==
|
|
2902
|
+
if (g !== Gt && !ge.contains(g) && Ae(!1), g === l)
|
|
2903
|
+
return Lt.consumeClick() ? void 0 : c.detail === 2 ? (Me(), Lt.reset(), H("Panel moved back to the corner")) : Me();
|
|
2794
2904
|
if (g.dataset.select) {
|
|
2795
2905
|
const w = document.querySelector(g.dataset.select);
|
|
2796
|
-
return w ?
|
|
2906
|
+
return w ? Bt(w) : H("That element isn’t on this page");
|
|
2797
2907
|
}
|
|
2798
|
-
if (g.dataset.setupSnippet) return
|
|
2908
|
+
if (g.dataset.setupSnippet) return he(fn[g.dataset.setupSnippet].code, "Copied");
|
|
2799
2909
|
if (g.dataset.tab)
|
|
2800
|
-
return
|
|
2801
|
-
if (g.dataset.remove) return
|
|
2910
|
+
return W("global"), Pt && Ce(!1), E(g.dataset.tab);
|
|
2911
|
+
if (g.dataset.remove) return ea(g.dataset.remove, g.dataset.removeScope);
|
|
2802
2912
|
if (g.dataset.applyMode)
|
|
2803
|
-
return
|
|
2913
|
+
return oe("apply-mode", g.dataset.applyMode), Ae(!1), H(g.dataset.applyMode === "copy" ? "Copy prompt copies straight away" : "Copy prompt shows a preview first");
|
|
2804
2914
|
switch (g.dataset.action) {
|
|
2805
2915
|
case "close":
|
|
2806
|
-
return
|
|
2916
|
+
return Xe();
|
|
2917
|
+
case "minimise":
|
|
2918
|
+
return Zn(), (o.querySelector('.tab[aria-pressed="true"]') || h[0]).focus();
|
|
2807
2919
|
case "reset":
|
|
2808
|
-
return
|
|
2920
|
+
return t.resetState(), H("Reset to defaults · ⌘Z to undo");
|
|
2809
2921
|
case "copy":
|
|
2810
|
-
return
|
|
2922
|
+
return he(location.href, "Link copied");
|
|
2811
2923
|
case "pick":
|
|
2812
|
-
return
|
|
2924
|
+
return fe ? Te() : Ut();
|
|
2813
2925
|
case "compare":
|
|
2814
|
-
|
|
2926
|
+
me || Pe(ne.getAttribute("aria-pressed") !== "true");
|
|
2815
2927
|
return;
|
|
2816
2928
|
case "undo":
|
|
2817
|
-
return
|
|
2929
|
+
return Vt();
|
|
2818
2930
|
case "redo":
|
|
2819
|
-
return
|
|
2931
|
+
return _t();
|
|
2820
2932
|
case "back":
|
|
2821
|
-
return
|
|
2933
|
+
return W("global");
|
|
2822
2934
|
case "changes":
|
|
2823
|
-
return
|
|
2935
|
+
return W(K === "changes" ? "global" : "changes");
|
|
2824
2936
|
case "apply":
|
|
2825
|
-
return
|
|
2937
|
+
return na();
|
|
2826
2938
|
case "apply-menu":
|
|
2827
|
-
return
|
|
2939
|
+
return Ae(ge.hidden);
|
|
2828
2940
|
case "dialog-close":
|
|
2829
|
-
return
|
|
2941
|
+
return Qe();
|
|
2830
2942
|
case "dialog-copy":
|
|
2831
|
-
return
|
|
2943
|
+
return he(Zt.value, "Prompt copied · paste it into your agent");
|
|
2832
2944
|
case "setup-copy":
|
|
2833
|
-
return D &&
|
|
2945
|
+
return D && he(D.text2copy, D.copied);
|
|
2834
2946
|
case "setup-dismiss":
|
|
2835
|
-
return
|
|
2947
|
+
return oe(en, Oe.dataset.status), Fe();
|
|
2836
2948
|
}
|
|
2837
2949
|
}
|
|
2838
|
-
}), o.addEventListener("keydown", (
|
|
2839
|
-
if (
|
|
2840
|
-
if (!
|
|
2841
|
-
if (!
|
|
2950
|
+
}), o.addEventListener("keydown", (c) => {
|
|
2951
|
+
if (c.key === "Escape") {
|
|
2952
|
+
if (!Le.hidden) return Qe();
|
|
2953
|
+
if (!ge.hidden) return Ae(!1);
|
|
2842
2954
|
}
|
|
2843
|
-
!(
|
|
2844
|
-
}), document.addEventListener("keydown", (
|
|
2845
|
-
|
|
2846
|
-
}),
|
|
2955
|
+
!(c.metaKey || c.ctrlKey) || c.key.toLowerCase() !== "z" || c.target.matches?.('input[type="text"], textarea') || (c.preventDefault(), c.shiftKey ? _t() : Vt());
|
|
2956
|
+
}), document.addEventListener("keydown", (c) => {
|
|
2957
|
+
c.altKey && (c.code === "KeyT" && (c.preventDefault(), Me()), c.code === "KeyC" && (c.preventDefault(), Pe(ne.getAttribute("aria-pressed") !== "true")));
|
|
2958
|
+
}), Z("panel") === "open" ? G({ animate: !1 }) : Z("panel") === "tabs" && (G({ animate: !1 }), Ce(!0)), { open: G, close: Xe, toggle: Me, toast: H, pick: Ut, prompt: Xt };
|
|
2847
2959
|
}
|
|
2848
|
-
const
|
|
2849
|
-
function
|
|
2960
|
+
const Cn = [vo, He, Wn, St, to, $t, mo, eo], Ao = ':root{--twd-font-heading: ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;--twd-font-body: ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;--twd-text-xs: .694rem;--twd-text-sm: .833rem;--twd-text-base: 1rem;--twd-text-lg: 1.2rem;--twd-text-xl: 1.44rem;--twd-text-2xl: 1.728rem;--twd-text-3xl: 2.074rem;--twd-leading-tight: 1.2;--twd-leading-normal: 1.5;--twd-weight-heading: 600;--twd-weight-strong: 600;--twd-bg: #fafafa;--twd-surface: #ffffff;--twd-surface-raised: #ffffff;--twd-text: #18181b;--twd-text-muted: #71717a;--twd-border: #e4e4e7;--twd-primary: #4f46e5;--twd-primary-hover: #4338ca;--twd-on-primary: #ffffff;--twd-focus: #6366f1;--twd-success: #16a34a;--twd-warning: #d97706;--twd-danger: #dc2626;--twd-radius-control: 8px;--twd-radius-container: 12px;--twd-space-1: 4px;--twd-space-2: 8px;--twd-space-3: 12px;--twd-space-4: 16px;--twd-space-5: 24px;--twd-space-6: 32px;--twd-space-7: 48px;--twd-space-8: 64px;--twd-control-height: 40px;--twd-control-pad-x: 16px;--twd-page-pad: var(--twd-space-5);--twd-section-gap: var(--twd-space-6);--twd-card-pad: var(--twd-space-5);--twd-card-gap: var(--twd-space-2);--twd-control-gap: var(--twd-space-3);--twd-border-width: 1px;--twd-border-width-container: var(--twd-border-width);--twd-shadow-sm: 0 1px 2px rgb(0 0 0 / .06);--twd-shadow-md: 0 4px 12px rgb(0 0 0 / .08);--twd-shadow-lg: 0 12px 32px rgb(0 0 0 / .12);--twd-button-bg: var(--twd-primary);--twd-button-fg: var(--twd-on-primary);--twd-button-border: transparent;--twd-button-hover: var(--twd-primary-hover);--twd-card-bg: var(--twd-surface);--twd-card-border: var(--twd-border);--twd-card-shadow: var(--twd-shadow-sm)}';
|
|
2961
|
+
function Lo() {
|
|
2850
2962
|
if (getComputedStyle(document.documentElement).getPropertyValue("--twd-radius-control").trim() || document.getElementById("twiddle-tokens")) return;
|
|
2851
|
-
const
|
|
2852
|
-
|
|
2963
|
+
const t = document.createElement("style");
|
|
2964
|
+
t.id = "twiddle-tokens", t.textContent = Ao, document.head.prepend(t);
|
|
2853
2965
|
}
|
|
2854
2966
|
if (!window.Twiddle) {
|
|
2855
|
-
|
|
2856
|
-
let
|
|
2857
|
-
const
|
|
2858
|
-
|
|
2967
|
+
Lo(), Cn.forEach((n) => ga(n.tokens)), Pn(wt);
|
|
2968
|
+
let e;
|
|
2969
|
+
const t = () => {
|
|
2970
|
+
wt(mt()), e = To({ sections: Cn, store: fa, view: { suspend: wa, resume: ya } });
|
|
2859
2971
|
};
|
|
2860
|
-
document.readyState === "loading" ? document.addEventListener("DOMContentLoaded",
|
|
2861
|
-
open: () =>
|
|
2862
|
-
close: () =>
|
|
2863
|
-
toggle: () =>
|
|
2864
|
-
reset: () =>
|
|
2865
|
-
undo: () =>
|
|
2866
|
-
redo: () =>
|
|
2867
|
-
pick: () =>
|
|
2868
|
-
prompt: () =>
|
|
2869
|
-
getState:
|
|
2972
|
+
document.readyState === "loading" ? document.addEventListener("DOMContentLoaded", t) : t(), window.Twiddle = {
|
|
2973
|
+
open: () => e && e.open(),
|
|
2974
|
+
close: () => e && e.close(),
|
|
2975
|
+
toggle: () => e && e.toggle(),
|
|
2976
|
+
reset: () => Tn(),
|
|
2977
|
+
undo: () => An(),
|
|
2978
|
+
redo: () => Ln(),
|
|
2979
|
+
pick: () => e && e.pick(),
|
|
2980
|
+
prompt: () => e ? e.prompt() : "",
|
|
2981
|
+
getState: mt
|
|
2870
2982
|
};
|
|
2871
2983
|
}
|
|
2872
|
-
const
|
|
2984
|
+
const Po = window.Twiddle;
|
|
2873
2985
|
export {
|
|
2874
|
-
|
|
2986
|
+
Po as Twiddle
|
|
2875
2987
|
};
|