@adia-ai/web-modules 0.8.12 → 0.8.13
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/CHANGELOG.md +17 -0
- package/dist/everything.min.js +73 -73
- package/dist/shell/admin-shell.min.css +1 -1
- package/dist/web-modules.min.css +1 -1
- package/dist/web-modules.sheet.js +1 -1
- package/package.json +1 -1
- package/shell/admin-shell/css/admin-shell.collapsed.css +46 -15
- package/shell/admin-shell/css/admin-shell.tokens.css +1 -1
- package/theme/theme-panel/theme-panel.a2ui.json +16 -8
- package/theme/theme-panel/theme-panel.css +20 -9
- package/theme/theme-panel/theme-panel.d.ts +28 -14
- package/theme/theme-panel/theme-panel.js +172 -95
- package/theme/theme-panel/theme-panel.test.js +191 -46
- package/theme/theme-panel/theme-panel.yaml +55 -23
|
@@ -57,13 +57,28 @@ describe('theme-panel', () => {
|
|
|
57
57
|
expect(customElements.get('theme-panel')).toBeDefined();
|
|
58
58
|
});
|
|
59
59
|
|
|
60
|
-
it('
|
|
60
|
+
it('renders a Theme select with the 12 default identities by default', async () => {
|
|
61
61
|
const tp = mount('<theme-panel></theme-panel>');
|
|
62
62
|
await tick();
|
|
63
|
-
const
|
|
64
|
-
expect(
|
|
65
|
-
|
|
66
|
-
expect(
|
|
63
|
+
const select = tp.querySelector('select-ui[part="themes"]');
|
|
64
|
+
expect(select).toBeTruthy();
|
|
65
|
+
const values = [...select.querySelectorAll('option')].map((o) => o.value);
|
|
66
|
+
expect(values).toEqual(['', 'atlas', 'signal', 'terrarium', 'ledger', 'arcade', 'meridian', 'solstice', 'glacier', 'ember', 'botanica', 'vector', 'velour']);
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
it('[themes=""] suppresses the Theme row entirely', async () => {
|
|
70
|
+
const tp = mount('<theme-panel themes=""></theme-panel>');
|
|
71
|
+
await tick();
|
|
72
|
+
expect(tp.querySelector('[part="themes"]')).toBeNull();
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
it('[themes="…"] renders a Theme select with exactly the given slugs plus Default', async () => {
|
|
76
|
+
const tp = mount('<theme-panel themes="terrarium vector"></theme-panel>');
|
|
77
|
+
await tick();
|
|
78
|
+
const select = tp.querySelector('select-ui[part="themes"]');
|
|
79
|
+
expect(select).toBeTruthy();
|
|
80
|
+
const values = [...select.querySelectorAll('option')].map((o) => o.value);
|
|
81
|
+
expect(values).toEqual(['', 'terrarium', 'vector']);
|
|
67
82
|
});
|
|
68
83
|
|
|
69
84
|
it('renders only minimum surface by default (no sliders, presets, scheme)', async () => {
|
|
@@ -96,37 +111,41 @@ describe('theme-panel', () => {
|
|
|
96
111
|
expect(tp.querySelector('[part="scheme"] segmented-ui')).toBeTruthy();
|
|
97
112
|
});
|
|
98
113
|
|
|
99
|
-
it('
|
|
100
|
-
const tp = mount('<theme-panel></theme-panel>');
|
|
114
|
+
it('selecting a theme sets [theme] on the target (defaults to <html>)', async () => {
|
|
115
|
+
const tp = mount('<theme-panel themes="atlas ledger terrarium"></theme-panel>');
|
|
101
116
|
await tick();
|
|
102
|
-
const
|
|
103
|
-
|
|
117
|
+
const select = tp.querySelector('select-ui[part="themes"]');
|
|
118
|
+
select.value = 'ledger';
|
|
119
|
+
select.dispatchEvent(new CustomEvent('change', { bubbles: true, detail: { value: 'ledger' } }));
|
|
104
120
|
await tick();
|
|
105
|
-
expect(document.documentElement.getAttribute('theme')).toBe('
|
|
106
|
-
expect(tp.getAttribute('active-theme')).toBe('
|
|
121
|
+
expect(document.documentElement.getAttribute('theme')).toBe('ledger');
|
|
122
|
+
expect(tp.getAttribute('active-theme')).toBe('ledger');
|
|
107
123
|
});
|
|
108
124
|
|
|
109
|
-
it('
|
|
110
|
-
document.documentElement.setAttribute('theme', '
|
|
111
|
-
const tp = mount('<theme-panel></theme-panel>');
|
|
125
|
+
it('selecting the Default option clears [theme]', async () => {
|
|
126
|
+
document.documentElement.setAttribute('theme', 'ledger');
|
|
127
|
+
const tp = mount('<theme-panel themes="atlas ledger"></theme-panel>');
|
|
112
128
|
await tick();
|
|
113
|
-
const
|
|
114
|
-
|
|
129
|
+
const select = tp.querySelector('select-ui[part="themes"]');
|
|
130
|
+
select.value = '';
|
|
131
|
+
select.dispatchEvent(new CustomEvent('change', { bubbles: true, detail: { value: '' } }));
|
|
115
132
|
await tick();
|
|
116
133
|
expect(document.documentElement.hasAttribute('theme')).toBe(false);
|
|
117
134
|
expect(tp.getAttribute('active-theme')).toBe('');
|
|
118
135
|
});
|
|
119
136
|
|
|
120
|
-
it('fires theme-change event with source: "theme" on
|
|
121
|
-
const tp = mount('<theme-panel></theme-panel>');
|
|
137
|
+
it('fires theme-change event with source: "theme" on selection', async () => {
|
|
138
|
+
const tp = mount('<theme-panel themes="terrarium"></theme-panel>');
|
|
122
139
|
await tick();
|
|
123
140
|
const onChange = vi.fn();
|
|
124
141
|
tp.addEventListener('theme-change', onChange);
|
|
125
|
-
tp.querySelector('
|
|
142
|
+
const select = tp.querySelector('select-ui[part="themes"]');
|
|
143
|
+
select.value = 'terrarium';
|
|
144
|
+
select.dispatchEvent(new CustomEvent('change', { bubbles: true, detail: { value: 'terrarium' } }));
|
|
126
145
|
await tick();
|
|
127
146
|
expect(onChange).toHaveBeenCalled();
|
|
128
147
|
const detail = onChange.mock.calls[0][0].detail;
|
|
129
|
-
expect(detail.theme).toBe('
|
|
148
|
+
expect(detail.theme).toBe('terrarium');
|
|
130
149
|
expect(detail.source).toBe('theme');
|
|
131
150
|
});
|
|
132
151
|
|
|
@@ -166,34 +185,55 @@ describe('theme-panel', () => {
|
|
|
166
185
|
expect(document.documentElement.style.getPropertyValue('--a-radius-k')).toBe('1.25');
|
|
167
186
|
});
|
|
168
187
|
|
|
169
|
-
it('preset "default"
|
|
170
|
-
document.documentElement.setAttribute('theme', '
|
|
188
|
+
it('preset "default" only sets density/radius — it does NOT clear [theme]', async () => {
|
|
189
|
+
document.documentElement.setAttribute('theme', 'ledger');
|
|
171
190
|
const tp = mount('<theme-panel parametric presets></theme-panel>');
|
|
172
191
|
await tick();
|
|
173
|
-
const
|
|
174
|
-
|
|
192
|
+
const defaultBtn = tp.querySelector('button-ui[data-preset="default"]');
|
|
193
|
+
expect(defaultBtn.getAttribute('text')).toBe('Default');
|
|
194
|
+
defaultBtn.dispatchEvent(new Event('click', { bubbles: true }));
|
|
195
|
+
await tick();
|
|
196
|
+
expect(document.documentElement.getAttribute('theme')).toBe('ledger');
|
|
197
|
+
expect(document.documentElement.style.getPropertyValue('--a-density')).toBe('1');
|
|
198
|
+
expect(document.documentElement.style.getPropertyValue('--a-radius-k')).toBe('1');
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
it('the Reset row is a dedicated bottom control, separate from the Preset row', async () => {
|
|
202
|
+
document.documentElement.setAttribute('theme', 'ledger');
|
|
203
|
+
document.documentElement.style.setProperty('--a-density', '1.2');
|
|
204
|
+
const tp = mount('<theme-panel presets></theme-panel>');
|
|
205
|
+
await tick();
|
|
206
|
+
const resetBtn = tp.querySelector('[part="reset-row"] button-ui[part="reset"]');
|
|
207
|
+
expect(resetBtn).toBeTruthy();
|
|
208
|
+
expect(resetBtn.getAttribute('text')).toBe('Reset');
|
|
209
|
+
resetBtn.dispatchEvent(new Event('click', { bubbles: true }));
|
|
175
210
|
await tick();
|
|
176
211
|
expect(document.documentElement.hasAttribute('theme')).toBe(false);
|
|
212
|
+
expect(document.documentElement.style.getPropertyValue('--a-density')).toBe('');
|
|
177
213
|
});
|
|
178
214
|
|
|
179
|
-
it('[persist] writes localStorage keys on theme
|
|
180
|
-
const tp = mount('<theme-panel persist></theme-panel>');
|
|
215
|
+
it('[persist] writes localStorage keys on theme selection', async () => {
|
|
216
|
+
const tp = mount('<theme-panel persist themes="atlas"></theme-panel>');
|
|
181
217
|
await tick();
|
|
182
|
-
tp.querySelector('
|
|
218
|
+
const select = tp.querySelector('select-ui[part="themes"]');
|
|
219
|
+
select.value = 'atlas';
|
|
220
|
+
select.dispatchEvent(new CustomEvent('change', { bubbles: true, detail: { value: 'atlas' } }));
|
|
183
221
|
await tick();
|
|
184
|
-
expect(localStorage.getItem('adia-theme-theme')).toBe('
|
|
222
|
+
expect(localStorage.getItem('adia-theme-theme')).toBe('atlas');
|
|
185
223
|
});
|
|
186
224
|
|
|
187
225
|
it('absence of [persist] does NOT write localStorage', async () => {
|
|
188
|
-
const tp = mount('<theme-panel></theme-panel>');
|
|
226
|
+
const tp = mount('<theme-panel themes="atlas"></theme-panel>');
|
|
189
227
|
await tick();
|
|
190
|
-
tp.querySelector('
|
|
228
|
+
const select = tp.querySelector('select-ui[part="themes"]');
|
|
229
|
+
select.value = 'atlas';
|
|
230
|
+
select.dispatchEvent(new CustomEvent('change', { bubbles: true, detail: { value: 'atlas' } }));
|
|
191
231
|
await tick();
|
|
192
232
|
expect(localStorage.getItem('adia-theme-theme')).toBeNull();
|
|
193
233
|
});
|
|
194
234
|
|
|
195
|
-
it('reset() clears all state and emits source: "reset"', async () => {
|
|
196
|
-
document.documentElement.setAttribute('theme', '
|
|
235
|
+
it('reset() clears all state, resets the Theme select to Default, and emits source: "reset"', async () => {
|
|
236
|
+
document.documentElement.setAttribute('theme', 'ledger');
|
|
197
237
|
document.documentElement.style.setProperty('--a-density', '1.2');
|
|
198
238
|
const tp = mount('<theme-panel parametric persist></theme-panel>');
|
|
199
239
|
await tick();
|
|
@@ -203,27 +243,31 @@ describe('theme-panel', () => {
|
|
|
203
243
|
await tick();
|
|
204
244
|
expect(document.documentElement.hasAttribute('theme')).toBe(false);
|
|
205
245
|
expect(document.documentElement.style.getPropertyValue('--a-density')).toBe('');
|
|
246
|
+
expect(tp.querySelector('select-ui[part="themes"]').value).toBe('');
|
|
206
247
|
expect(onChange).toHaveBeenCalled();
|
|
207
248
|
expect(onChange.mock.calls[0][0].detail.source).toBe('reset');
|
|
208
249
|
});
|
|
209
250
|
|
|
210
251
|
it('[storage-prefix] override changes LS key namespace', async () => {
|
|
211
|
-
const tp = mount('<theme-panel persist storage-prefix="my-app-"></theme-panel>');
|
|
252
|
+
const tp = mount('<theme-panel persist storage-prefix="my-app-" themes="atlas"></theme-panel>');
|
|
212
253
|
await tick();
|
|
213
|
-
tp.querySelector('
|
|
254
|
+
const select = tp.querySelector('select-ui[part="themes"]');
|
|
255
|
+
select.value = 'atlas';
|
|
256
|
+
select.dispatchEvent(new CustomEvent('change', { bubbles: true, detail: { value: 'atlas' } }));
|
|
214
257
|
await tick();
|
|
215
|
-
expect(localStorage.getItem('my-app-theme')).toBe('
|
|
258
|
+
expect(localStorage.getItem('my-app-theme')).toBe('atlas');
|
|
216
259
|
expect(localStorage.getItem('adia-theme-theme')).toBeNull();
|
|
217
260
|
});
|
|
218
261
|
|
|
219
|
-
it('apply({theme}) is equivalent to a theme
|
|
262
|
+
it('apply({theme}) is equivalent to a theme selection, syncs the select, and emits source: "programmatic"', async () => {
|
|
220
263
|
const tp = mount('<theme-panel></theme-panel>');
|
|
221
264
|
await tick();
|
|
222
265
|
const onChange = vi.fn();
|
|
223
266
|
tp.addEventListener('theme-change', onChange);
|
|
224
|
-
tp.apply({ theme: '
|
|
267
|
+
tp.apply({ theme: 'velour' });
|
|
225
268
|
await tick();
|
|
226
|
-
expect(document.documentElement.getAttribute('theme')).toBe('
|
|
269
|
+
expect(document.documentElement.getAttribute('theme')).toBe('velour');
|
|
270
|
+
expect(tp.querySelector('select-ui[part="themes"]').value).toBe('velour');
|
|
227
271
|
expect(onChange.mock.calls[0][0].detail.source).toBe('programmatic');
|
|
228
272
|
});
|
|
229
273
|
|
|
@@ -247,11 +291,13 @@ describe('theme-panel', () => {
|
|
|
247
291
|
const preview = document.createElement('div');
|
|
248
292
|
preview.id = 'preview';
|
|
249
293
|
document.body.appendChild(preview);
|
|
250
|
-
const tp = mount('<theme-panel target="#preview"></theme-panel>');
|
|
294
|
+
const tp = mount('<theme-panel target="#preview" themes="atlas"></theme-panel>');
|
|
251
295
|
await tick();
|
|
252
|
-
tp.querySelector('
|
|
296
|
+
const select = tp.querySelector('select-ui[part="themes"]');
|
|
297
|
+
select.value = 'atlas';
|
|
298
|
+
select.dispatchEvent(new CustomEvent('change', { bubbles: true, detail: { value: 'atlas' } }));
|
|
253
299
|
await tick();
|
|
254
|
-
expect(preview.getAttribute('theme')).toBe('
|
|
300
|
+
expect(preview.getAttribute('theme')).toBe('atlas');
|
|
255
301
|
expect(document.documentElement.getAttribute('theme')).toBeNull();
|
|
256
302
|
});
|
|
257
303
|
|
|
@@ -264,11 +310,110 @@ describe('theme-panel', () => {
|
|
|
264
310
|
expect(mqlListeners.length).toBe(0);
|
|
265
311
|
});
|
|
266
312
|
|
|
267
|
-
it('
|
|
268
|
-
const tp = mount('<theme-panel themes="
|
|
313
|
+
it('tolerates unknown slugs — renders an option and applies [theme] regardless', async () => {
|
|
314
|
+
const tp = mount('<theme-panel themes="brand-a"></theme-panel>');
|
|
315
|
+
await tick();
|
|
316
|
+
const select = tp.querySelector('select-ui[part="themes"]');
|
|
317
|
+
expect([...select.querySelectorAll('option')].map((o) => o.value)).toEqual(['', 'brand-a']);
|
|
318
|
+
select.value = 'brand-a';
|
|
319
|
+
select.dispatchEvent(new CustomEvent('change', { bubbles: true, detail: { value: 'brand-a' } }));
|
|
320
|
+
await tick();
|
|
321
|
+
expect(document.documentElement.getAttribute('theme')).toBe('brand-a');
|
|
322
|
+
});
|
|
323
|
+
|
|
324
|
+
it('[presets] labels its row "Preset", not "Density" — disambiguates from the Density slider row', async () => {
|
|
325
|
+
const tp = mount('<theme-panel parametric presets></theme-panel>');
|
|
326
|
+
await tick();
|
|
327
|
+
// The Density slider's label lives on <field-ui label="Density">; the
|
|
328
|
+
// section labels (Parametric/Preset/Scale) are standalone text-ui rows.
|
|
329
|
+
expect(tp.querySelector('field-ui[label="Density"] slider-ui[part="density"]')).toBeTruthy();
|
|
330
|
+
const sectionLabels = [...tp.querySelectorAll('text-ui[variant="label"]')].map((l) => l.textContent);
|
|
331
|
+
expect(sectionLabels).toContain('Preset');
|
|
332
|
+
expect(sectionLabels).not.toContain('Density');
|
|
333
|
+
});
|
|
334
|
+
});
|
|
335
|
+
|
|
336
|
+
describe('theme-panel — [register] Scale row (six [scale] tiers, supersedes verse/prose)', () => {
|
|
337
|
+
it('renders a select with the six canonical tiers plus a Default option', async () => {
|
|
338
|
+
const tp = mount('<theme-panel register></theme-panel>');
|
|
339
|
+
await tick();
|
|
340
|
+
const select = tp.querySelector('select-ui[part="scale"]');
|
|
341
|
+
expect(select).toBeTruthy();
|
|
342
|
+
const values = [...select.querySelectorAll('option')].map((o) => o.value);
|
|
343
|
+
expect(values).toEqual(['', 'ui-sm', 'ui-md', 'ui-lg', 'content-sm', 'content-md', 'content-lg']);
|
|
344
|
+
});
|
|
345
|
+
|
|
346
|
+
it('does not render the Scale row without [register]', async () => {
|
|
347
|
+
const tp = mount('<theme-panel presets></theme-panel>');
|
|
348
|
+
await tick();
|
|
349
|
+
expect(tp.querySelector('select-ui[part="scale"]')).toBeNull();
|
|
350
|
+
});
|
|
351
|
+
|
|
352
|
+
it('selecting a tier sets [scale="…"] on the target, never [verse]/[prose]', async () => {
|
|
353
|
+
const tp = mount('<theme-panel register></theme-panel>');
|
|
354
|
+
await tick();
|
|
355
|
+
const select = tp.querySelector('select-ui[part="scale"]');
|
|
356
|
+
select.value = 'content-md';
|
|
357
|
+
select.dispatchEvent(new CustomEvent('change', { bubbles: true, detail: { value: 'content-md' } }));
|
|
358
|
+
await tick();
|
|
359
|
+
expect(document.documentElement.getAttribute('scale')).toBe('content-md');
|
|
360
|
+
expect(document.documentElement.hasAttribute('verse')).toBe(false);
|
|
361
|
+
expect(document.documentElement.hasAttribute('prose')).toBe(false);
|
|
362
|
+
expect(tp.getAttribute('active-scale')).toBe('content-md');
|
|
363
|
+
});
|
|
364
|
+
|
|
365
|
+
it('selecting the Default option clears [scale] on the target', async () => {
|
|
366
|
+
const tp = mount('<theme-panel register></theme-panel>');
|
|
367
|
+
await tick();
|
|
368
|
+
const select = tp.querySelector('select-ui[part="scale"]');
|
|
369
|
+
select.value = 'ui-lg';
|
|
370
|
+
select.dispatchEvent(new CustomEvent('change', { bubbles: true, detail: { value: 'ui-lg' } }));
|
|
371
|
+
await tick();
|
|
372
|
+
expect(document.documentElement.getAttribute('scale')).toBe('ui-lg');
|
|
373
|
+
|
|
374
|
+
select.value = '';
|
|
375
|
+
select.dispatchEvent(new CustomEvent('change', { bubbles: true, detail: { value: '' } }));
|
|
376
|
+
await tick();
|
|
377
|
+
expect(document.documentElement.hasAttribute('scale')).toBe(false);
|
|
378
|
+
expect(tp.getAttribute('active-scale')).toBe('');
|
|
379
|
+
});
|
|
380
|
+
|
|
381
|
+
it('apply({scale}) writes [scale] and emits source: "scale"', async () => {
|
|
382
|
+
const tp = mount('<theme-panel register></theme-panel>');
|
|
383
|
+
await tick();
|
|
384
|
+
const onChange = vi.fn();
|
|
385
|
+
tp.addEventListener('theme-change', onChange);
|
|
386
|
+
tp.apply({ scale: 'ui-sm' });
|
|
387
|
+
await tick();
|
|
388
|
+
expect(document.documentElement.getAttribute('scale')).toBe('ui-sm');
|
|
389
|
+
expect(onChange.mock.calls[0][0].detail.scale).toBe('ui-sm');
|
|
390
|
+
expect(onChange.mock.calls[0][0].detail.source).toBe('programmatic');
|
|
391
|
+
});
|
|
392
|
+
|
|
393
|
+
it('[persist] round-trips the scale tier through localStorage on reconnect', async () => {
|
|
394
|
+
const first = mount('<theme-panel register persist></theme-panel>');
|
|
395
|
+
await tick();
|
|
396
|
+
const select = first.querySelector('select-ui[part="scale"]');
|
|
397
|
+
select.value = 'content-lg';
|
|
398
|
+
select.dispatchEvent(new CustomEvent('change', { bubbles: true, detail: { value: 'content-lg' } }));
|
|
399
|
+
await tick();
|
|
400
|
+
expect(localStorage.getItem('adia-theme-scale')).toBe('content-lg');
|
|
401
|
+
|
|
402
|
+
first.remove();
|
|
403
|
+
document.documentElement.removeAttribute('scale');
|
|
404
|
+
const second = mount('<theme-panel register persist></theme-panel>');
|
|
405
|
+
await tick();
|
|
406
|
+
expect(document.documentElement.getAttribute('scale')).toBe('content-lg');
|
|
407
|
+
expect(second.getAttribute('active-scale')).toBe('content-lg');
|
|
408
|
+
});
|
|
409
|
+
|
|
410
|
+
it('reset() clears [scale] and resets the select to Default', async () => {
|
|
411
|
+
document.documentElement.setAttribute('scale', 'ui-md');
|
|
412
|
+
const tp = mount('<theme-panel register></theme-panel>');
|
|
413
|
+
await tick();
|
|
414
|
+
tp.reset();
|
|
269
415
|
await tick();
|
|
270
|
-
|
|
271
|
-
expect(
|
|
272
|
-
expect(buttons.map((b) => b.getAttribute('data-theme-slug'))).toEqual(['default', 'ocean']);
|
|
416
|
+
expect(document.documentElement.hasAttribute('scale')).toBe(false);
|
|
417
|
+
expect(tp.querySelector('select-ui[part="scale"]').value).toBe('');
|
|
273
418
|
});
|
|
274
419
|
});
|
|
@@ -14,9 +14,12 @@ description: |
|
|
|
14
14
|
small attribute API.
|
|
15
15
|
|
|
16
16
|
Drops into any consumer's <popover-ui slot="content"> (the canonical
|
|
17
|
-
composition) or directly into a sidebar section.
|
|
18
|
-
ship by default
|
|
19
|
-
|
|
17
|
+
composition) or directly into a sidebar section. The 12 named identities
|
|
18
|
+
in styles/themes.css ship by default (a <select-ui> Theme row); pass
|
|
19
|
+
[themes="slug1 slug2"] to restrict, or "" to suppress the row entirely.
|
|
20
|
+
Section visibility flips on/off via boolean attributes ([parametric],
|
|
21
|
+
[presets], [register], [scheme-toggle]); a Reset row (own button, bottom
|
|
22
|
+
of the panel) always renders and clears everything via .reset().
|
|
20
23
|
|
|
21
24
|
Promoted from the duplicated <div id="theme-panel"> block in site/ and
|
|
22
25
|
playgrounds/admin-shell/ — see .claude/docs/specs/theme-panel-module.md.
|
|
@@ -26,6 +29,7 @@ description: |
|
|
|
26
29
|
composes:
|
|
27
30
|
- segmented-ui
|
|
28
31
|
- segment-ui
|
|
32
|
+
- select-ui
|
|
29
33
|
- text-ui
|
|
30
34
|
- button-ui
|
|
31
35
|
- divider-ui
|
|
@@ -34,12 +38,17 @@ composes:
|
|
|
34
38
|
props:
|
|
35
39
|
themes:
|
|
36
40
|
description: |
|
|
37
|
-
Space-separated list of theme slugs to render
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
+
Space-separated list of theme slugs to render in the Theme
|
|
42
|
+
<select-ui>. Defaults to the 12 named identities shipped in
|
|
43
|
+
styles/themes.css — atlas, signal, terrarium, ledger, arcade,
|
|
44
|
+
meridian, solstice, glacier, ember, botanica, vector, velour
|
|
45
|
+
(each a real recolor + real Google/system font pairing, not a
|
|
46
|
+
decorative hue nudge — see that file's header). Pass "" to
|
|
47
|
+
suppress the Theme row entirely. Tolerant — unknown slugs still
|
|
48
|
+
render as an option; selecting one applies [theme=slug] regardless
|
|
49
|
+
of whether themes.css has a matching block.
|
|
41
50
|
type: string
|
|
42
|
-
default: "
|
|
51
|
+
default: "atlas signal terrarium ledger arcade meridian solstice glacier ember botanica vector velour"
|
|
43
52
|
reflect: true
|
|
44
53
|
|
|
45
54
|
parametric:
|
|
@@ -52,19 +61,24 @@ props:
|
|
|
52
61
|
|
|
53
62
|
presets:
|
|
54
63
|
description: |
|
|
55
|
-
Renders the compact /
|
|
56
|
-
applies a (density, radius) pair
|
|
57
|
-
[
|
|
64
|
+
Renders the compact / default / spacious preset row. Each preset
|
|
65
|
+
applies a (density, radius) pair only — it does not touch [theme],
|
|
66
|
+
[scheme], or [scale]. Full state clearing lives in the panel's
|
|
67
|
+
always-rendered Reset row (bottom, own button, calls .reset()).
|
|
58
68
|
type: boolean
|
|
59
69
|
default: false
|
|
60
70
|
reflect: true
|
|
61
71
|
|
|
62
72
|
register:
|
|
63
73
|
description: |
|
|
64
|
-
Renders a "Scale
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
74
|
+
Renders a "Scale" row — a <select-ui> offering the six ancestor
|
|
75
|
+
[scale] tiers (ui-sm | ui-md | ui-lg | content-sm | content-md |
|
|
76
|
+
content-lg, plus a Default option that clears the attribute) —
|
|
77
|
+
writing [scale="<tier>"] directly on the target element, matching
|
|
78
|
+
theme-provider.class.js's own `scale` prop values exactly.
|
|
79
|
+
Supersedes the old verse/regular/prose register (deprecated
|
|
80
|
+
[verse]/[prose] aliases); this control never writes them.
|
|
81
|
+
Persisted like the other knobs when [persist] is set.
|
|
68
82
|
type: boolean
|
|
69
83
|
default: false
|
|
70
84
|
reflect: true
|
|
@@ -90,9 +104,9 @@ props:
|
|
|
90
104
|
|
|
91
105
|
storage-prefix:
|
|
92
106
|
description: |
|
|
93
|
-
Namespace for localStorage keys when [persist] is set.
|
|
107
|
+
Namespace for localStorage keys when [persist] is set. Five keys
|
|
94
108
|
are written: {prefix}theme, {prefix}scheme, {prefix}density,
|
|
95
|
-
{prefix}radius.
|
|
109
|
+
{prefix}radius, {prefix}scale.
|
|
96
110
|
type: string
|
|
97
111
|
default: "adia-theme-"
|
|
98
112
|
reflect: true
|
|
@@ -155,14 +169,14 @@ props:
|
|
|
155
169
|
reflect: true
|
|
156
170
|
attribute: active-radius
|
|
157
171
|
|
|
158
|
-
active-
|
|
172
|
+
active-scale:
|
|
159
173
|
description: |
|
|
160
|
-
Reflected — the active
|
|
161
|
-
|
|
174
|
+
Reflected — the active [scale] tier on the target element; empty
|
|
175
|
+
for the unscaled default (no [scale] attribute set).
|
|
162
176
|
type: string
|
|
163
177
|
default: ""
|
|
164
178
|
reflect: true
|
|
165
|
-
attribute: active-
|
|
179
|
+
attribute: active-scale
|
|
166
180
|
|
|
167
181
|
events:
|
|
168
182
|
theme-change:
|
|
@@ -175,6 +189,7 @@ events:
|
|
|
175
189
|
scheme: string
|
|
176
190
|
density: number
|
|
177
191
|
radius: number
|
|
192
|
+
scale: string
|
|
178
193
|
source: string
|
|
179
194
|
|
|
180
195
|
slots: {}
|
|
@@ -182,6 +197,21 @@ slots: {}
|
|
|
182
197
|
states:
|
|
183
198
|
- name: idle
|
|
184
199
|
description: Default — panel rendered, awaiting input.
|
|
200
|
+
- name: fixed-scale
|
|
201
|
+
description: |
|
|
202
|
+
Always carries [scale="ui-md"] on itself (set in connected(), not
|
|
203
|
+
a consumer-configurable prop) — pins the panel's OWN chrome to the
|
|
204
|
+
base register, independent of whatever [scale] the Scale row is
|
|
205
|
+
driving on the target. Target defaults to :root, a DOM ancestor of
|
|
206
|
+
theme-panel, so [scale]'s inherited custom properties would
|
|
207
|
+
otherwise cascade into the panel's own controls too. A visual
|
|
208
|
+
no-op under the unscaled default (ui-md is byte-identical to it,
|
|
209
|
+
scale.css's own invariant) — only matters once an ancestor sets a
|
|
210
|
+
different tier. --a-density / --a-radius-k are pinned the same
|
|
211
|
+
way, declared directly in theme-panel.css (the "compact" preset's
|
|
212
|
+
values) rather than via an attribute — no [density] mechanism
|
|
213
|
+
exists in the framework the way [scale] does.
|
|
214
|
+
attribute: scale
|
|
185
215
|
- name: persisted
|
|
186
216
|
description: |
|
|
187
217
|
[persist] is set; selections write to localStorage under
|
|
@@ -207,8 +237,9 @@ a2ui:
|
|
|
207
237
|
[persist] for playgrounds and embedded demos so state stays
|
|
208
238
|
ephemeral. The default is ephemeral.
|
|
209
239
|
- >-
|
|
210
|
-
Add boolean attributes [parametric], [presets], [
|
|
211
|
-
to enable sections; the minimum panel is the theme
|
|
240
|
+
Add boolean attributes [parametric], [presets], [register],
|
|
241
|
+
[scheme-toggle] to enable sections; the minimum panel is the theme
|
|
242
|
+
button grid (empty until [themes] is supplied — no default list).
|
|
212
243
|
|
|
213
244
|
keywords:
|
|
214
245
|
- theme
|
|
@@ -216,6 +247,7 @@ keywords:
|
|
|
216
247
|
- palette
|
|
217
248
|
- density
|
|
218
249
|
- radius
|
|
250
|
+
- scale
|
|
219
251
|
- scheme
|
|
220
252
|
- dark-mode
|
|
221
253
|
- light-mode
|