@threadlabs/looma 0.36.0 → 0.37.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/components/shared/icons.js +3 -0
- package/components/ui-avatar/ui-avatar.html +1 -1
- package/components/ui-avatar-group/ui-avatar-group.html +1 -1
- package/components/ui-button/ui-button.html +3 -12
- package/components/ui-card/ui-card.html +5 -5
- package/components/ui-checkbox/ui-checkbox.html +0 -6
- package/components/ui-color-picker/ui-color-picker.html +0 -9
- package/components/ui-combobox/ui-combobox.html +1 -13
- package/components/ui-cycle-button/ui-cycle-button.html +41 -0
- package/components/ui-cycle-button/ui-cycle-button.js +19 -0
- package/components/ui-dialog/ui-dialog.html +1 -1
- package/components/ui-icon/ui-icon.html +3 -0
- package/components/ui-icon-button/ui-icon-button.html +10 -14
- package/components/ui-input/ui-input.html +1 -16
- package/components/ui-input-group/ui-input-group.html +0 -10
- package/components/ui-menu/ui-menu.html +1 -1
- package/components/ui-popover/ui-popover.html +1 -1
- package/components/ui-radio/ui-radio.html +0 -6
- package/components/ui-select/ui-select.html +2 -8
- package/components/ui-switch/ui-switch.html +0 -6
- package/components/ui-tooltip/ui-tooltip.html +1 -1
- package/dist/index.js +47 -46
- package/docs/README.md +47 -8
- package/docs/card.html +2 -1
- package/docs/catalog.json +214 -39
- package/docs/customizer-render.mjs +8 -7
- package/docs/customizer.js +39 -21
- package/docs/theme.mjs +6 -5
- package/docs/value-editors.mjs +4 -3
- package/editor/icons.d.ts +3 -0
- package/editor/index.js +4 -1
- package/package.json +2 -2
- package/styles/ui-avatar-group.css +1 -1
- package/styles/ui-avatar.css +1 -1
- package/styles/ui-button.css +2 -14
- package/styles/ui-card.css +1 -1
- package/styles/ui-checkbox.css +0 -9
- package/styles/ui-color-picker.css +0 -13
- package/styles/ui-combobox.css +2 -28
- package/styles/ui-cycle-button.css +36 -0
- package/styles/ui-dialog.css +2 -2
- package/styles/ui-icon-button.css +6 -13
- package/styles/ui-input-group.css +0 -13
- package/styles/ui-input.css +2 -18
- package/styles/ui-menu.css +1 -1
- package/styles/ui-popover.css +1 -1
- package/styles/ui-radio.css +0 -9
- package/styles/ui-select.css +3 -10
- package/styles/ui-switch.css +0 -9
- package/styles/ui-tooltip.css +1 -1
- package/theme-system.css +19 -18
- package/tokens.css +20 -25
- package/vanilla/UiCycleButton.d.ts +29 -0
- package/vanilla/UiCycleButton.js +85 -0
- package/vanilla/UiIcon.js +1 -1
- package/vanilla/index.js +1 -0
- package/vue/UiAvatar.vue +1 -1
- package/vue/UiAvatarGroup.vue +1 -1
- package/vue/UiButton.vue +2 -11
- package/vue/UiCard.vue +5 -3
- package/vue/UiCheckbox.vue +0 -6
- package/vue/UiColorPicker.vue +0 -9
- package/vue/UiCombobox.vue +2 -26
- package/vue/UiCycleButton.d.ts +69 -0
- package/vue/UiCycleButton.js +2 -0
- package/vue/UiCycleButton.vue +403 -0
- package/vue/UiDialog.vue +6 -2
- package/vue/UiIcon.vue +22 -0
- package/vue/UiIconButton.vue +2 -16
- package/vue/UiInput.vue +1 -22
- package/vue/UiInputGroup.vue +0 -10
- package/vue/UiMenu.vue +3 -1
- package/vue/UiPopover.vue +3 -1
- package/vue/UiRadio.vue +0 -6
- package/vue/UiSelect.vue +2 -8
- package/vue/UiSwitch.vue +0 -6
- package/vue/UiTooltip.vue +3 -1
- package/vue/chunks/UiAffordanceScope.js +1 -1
- package/vue/chunks/UiAvatar.js +2 -2
- package/vue/chunks/UiAvatarGroup.js +2 -2
- package/vue/chunks/UiBadge.js +1 -1
- package/vue/chunks/UiBreadcrumbItem.js +1 -1
- package/vue/chunks/UiBreadcrumbs.js +1 -1
- package/vue/chunks/UiButton.js +3 -3
- package/vue/chunks/UiCallout.js +1 -1
- package/vue/chunks/UiCard.js +4 -4
- package/vue/chunks/UiCheckbox.js +2 -2
- package/vue/chunks/UiCluster.js +1 -1
- package/vue/chunks/UiColorPicker.js +2 -2
- package/vue/chunks/UiCombobox.js +2 -2
- package/vue/chunks/UiContainer.js +1 -1
- package/vue/chunks/UiContextMenu.js +1 -1
- package/vue/chunks/UiCover.js +1 -1
- package/vue/chunks/UiCurveEditor.js +1 -1
- package/vue/chunks/UiCycleButton.js +352 -0
- package/vue/chunks/UiDescriptionItem.js +1 -1
- package/vue/chunks/UiDescriptionList.js +1 -1
- package/vue/chunks/UiDialog.js +2 -2
- package/vue/chunks/UiDisclosure.js +1 -1
- package/vue/chunks/UiEditable.js +1 -1
- package/vue/chunks/UiEditorBlockActions.js +1 -1
- package/vue/chunks/UiEditorChipEditor.js +1 -1
- package/vue/chunks/UiEditorCodeLanguage.js +1 -1
- package/vue/chunks/UiEditorFootnoteEditor.js +1 -1
- package/vue/chunks/UiEditorImageToolbar.js +1 -1
- package/vue/chunks/UiEditorInsertTableGrid.js +1 -1
- package/vue/chunks/UiEditorLinkCard.js +1 -1
- package/vue/chunks/UiEditorLinkForm.js +1 -1
- package/vue/chunks/UiEditorMentionMenu.js +1 -1
- package/vue/chunks/UiEditorMentionMenuItem.js +1 -1
- package/vue/chunks/UiEditorSlashMenu.js +1 -1
- package/vue/chunks/UiEditorSlashMenuGroup.js +1 -1
- package/vue/chunks/UiEditorSlashMenuItem.js +1 -1
- package/vue/chunks/UiEditorSurface.js +1 -1
- package/vue/chunks/UiEditorTableContextMenu.js +1 -1
- package/vue/chunks/UiEditorTableOverlay.js +1 -1
- package/vue/chunks/UiEditorTableToolbar.js +1 -1
- package/vue/chunks/UiEditorToolbar.js +1 -1
- package/vue/chunks/UiFloatingActionButton.js +1 -1
- package/vue/chunks/UiFormField.js +1 -1
- package/vue/chunks/UiGrid.js +1 -1
- package/vue/chunks/UiIcon.js +70 -2
- package/vue/chunks/UiIconButton.js +4 -4
- package/vue/chunks/UiImage.js +1 -1
- package/vue/chunks/UiInput.js +2 -2
- package/vue/chunks/UiInputGroup.js +2 -2
- package/vue/chunks/UiLightEditor.js +1 -1
- package/vue/chunks/UiList.js +1 -1
- package/vue/chunks/UiListItem.js +1 -1
- package/vue/chunks/UiListbox.js +1 -1
- package/vue/chunks/UiMenu.js +2 -2
- package/vue/chunks/UiMenuGroup.js +1 -1
- package/vue/chunks/UiMenuItem.js +1 -1
- package/vue/chunks/UiMeter.js +1 -1
- package/vue/chunks/UiNavItem.js +1 -1
- package/vue/chunks/UiNumberInput.js +1 -1
- package/vue/chunks/UiPageHeader.js +1 -1
- package/vue/chunks/UiPopover.js +2 -2
- package/vue/chunks/UiRadio.js +2 -2
- package/vue/chunks/UiRadioGroup.js +1 -1
- package/vue/chunks/UiReel.js +1 -1
- package/vue/chunks/UiScrollArea.js +1 -1
- package/vue/chunks/UiSearchResultRow.js +1 -1
- package/vue/chunks/UiSearchShell.js +1 -1
- package/vue/chunks/UiSection.js +1 -1
- package/vue/chunks/UiSelect.js +2 -2
- package/vue/chunks/UiSeparator.js +1 -1
- package/vue/chunks/UiSidebar.js +1 -1
- package/vue/chunks/UiSlider.js +1 -1
- package/vue/chunks/UiSpinner.js +1 -1
- package/vue/chunks/UiStack.js +1 -1
- package/vue/chunks/UiStatusMessage.js +1 -1
- package/vue/chunks/UiSwatch.js +1 -1
- package/vue/chunks/UiSwitch.js +2 -2
- package/vue/chunks/UiSwitcher.js +1 -1
- package/vue/chunks/UiTable.js +1 -1
- package/vue/chunks/UiTabs.js +1 -1
- package/vue/chunks/UiText.js +1 -1
- package/vue/chunks/UiTextarea.js +1 -1
- package/vue/chunks/UiToast.js +1 -1
- package/vue/chunks/UiToastRegion.js +1 -1
- package/vue/chunks/UiTooltip.js +2 -2
- package/vue/chunks/UiTree.js +1 -1
- package/vue/chunks/UiTreeItem.js +1 -1
- package/vue/chunks/props.js +12 -1
- package/vue/components.css +76 -181
- package/vue/editor/index.js +68 -0
- package/vue/index.d.ts +1 -0
- package/vue/index.js +2 -1
package/docs/customizer.js
CHANGED
|
@@ -22,17 +22,18 @@ export function connect(host) {
|
|
|
22
22
|
let alive = true;
|
|
23
23
|
let selectedExample = '';
|
|
24
24
|
let refinementTrigger;
|
|
25
|
-
let previewFrame = 0, pending = false,
|
|
26
|
-
const entryRecipe = structuredClone(recipe);
|
|
25
|
+
let previewFrame = 0, pending = false, candidate, gestureStart;
|
|
27
26
|
const abort = new AbortController();
|
|
28
27
|
const say = (text, error = false) => { message.textContent = text; message.setAttribute('role', error ? 'alert' : 'status'); };
|
|
29
28
|
const sayApplied = text => say(theme?.storageAvailable ? text : 'Theme applies to this page. Browser storage is unavailable; download CSS or a recipe to keep it.');
|
|
30
29
|
const fields = new Map(themeFields.map(field => [field.id, field]));
|
|
31
30
|
const unitContext = () => ({ remPixels: parseFloat(view.getComputedStyle(document.documentElement).fontSize) });
|
|
31
|
+
const editorFor = (field, value) => valueEditor(field, value, { remPixels: parseFloat(recipe.values['text-size']) });
|
|
32
|
+
let boundsBase;
|
|
32
33
|
const updateControl = (element, value) => {
|
|
33
34
|
const field = fields.get(element.dataset.token);
|
|
34
35
|
if (!field || !element.dataset.editorKind) { componentRegistry.update(element, { value }); return; }
|
|
35
|
-
const editor =
|
|
36
|
+
const editor = editorFor(field, value);
|
|
36
37
|
if (element.dataset.editorKind === 'curve') {
|
|
37
38
|
element.closest('[data-curve-for]').hidden = editor.kind !== 'curve';
|
|
38
39
|
if (editor.kind === 'curve') componentRegistry.update(element, { value });
|
|
@@ -40,13 +41,33 @@ export function connect(host) {
|
|
|
40
41
|
};
|
|
41
42
|
const updateUnits = () => {
|
|
42
43
|
for (const control of root.querySelectorAll('[data-unit-for]')) {
|
|
43
|
-
const field = fields.get(control.dataset.unitFor), editor =
|
|
44
|
+
const field = fields.get(control.dataset.unitFor), editor = editorFor(field, recipe.values[field.id]);
|
|
44
45
|
if (![...control.options].some(option => option.value === editor.unit)) control.add(new view.Option(editor.unit, editor.unit));
|
|
45
46
|
componentRegistry.update(control, { value: editor.unit });
|
|
46
47
|
}
|
|
47
48
|
};
|
|
49
|
+
const updateSizes = () => {
|
|
50
|
+
const samples = [...root.querySelectorAll('[data-size-sample]')].filter(sample => sample.getClientRects().length);
|
|
51
|
+
if (!samples.length) return;
|
|
52
|
+
const base = parseFloat(view.getComputedStyle(document.documentElement).fontSize);
|
|
53
|
+
const rounded = value => Number(value.toFixed(2));
|
|
54
|
+
for (const sample of samples) {
|
|
55
|
+
const button = sample.querySelector('button');
|
|
56
|
+
if (button) {
|
|
57
|
+
const height = button.getBoundingClientRect().height;
|
|
58
|
+
sample.querySelector('[data-size-readout]').textContent = ` · ${rounded(height / base)}rem (${rounded(height)}px)`;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
};
|
|
48
62
|
const paint = () => {
|
|
49
63
|
const css = theme?.css || themeCSS(recipe);
|
|
64
|
+
if (boundsBase !== recipe.values['text-size']) {
|
|
65
|
+
boundsBase = recipe.values['text-size'];
|
|
66
|
+
for (const control of root.querySelectorAll('[data-token]')) {
|
|
67
|
+
const field = fields.get(control.dataset.token);
|
|
68
|
+
if (field?.type.includes('length') && field.id !== 'text-size') updateControl(control, recipe.values[field.id]);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
50
71
|
for (const family of ['material','palette']) {
|
|
51
72
|
const state = refinementState(recipe, family), presets = family === 'material' ? materialPresets : themePalettes;
|
|
52
73
|
for (const select of root.querySelectorAll(`[data-refinement-preset="${family}"]`)) {
|
|
@@ -73,6 +94,7 @@ export function connect(host) {
|
|
|
73
94
|
: 'Finish catches the light. Spread shapes its reflection; strength affects reflection, edge light and glow together.';
|
|
74
95
|
const gaps = root.querySelector('[data-spacing-samples]');
|
|
75
96
|
gaps.setAttribute('aria-label', `Spacing progression with curve ${recipe.values['space-curve']}`);
|
|
97
|
+
updateSizes();
|
|
76
98
|
};
|
|
77
99
|
const show = (next, persist = true) => {
|
|
78
100
|
recipe = validateRecipe(next);
|
|
@@ -83,16 +105,15 @@ export function connect(host) {
|
|
|
83
105
|
changed = JSON.stringify(recipe) !== JSON.stringify(saved);
|
|
84
106
|
paint();
|
|
85
107
|
};
|
|
86
|
-
const
|
|
87
|
-
if (!
|
|
88
|
-
|
|
89
|
-
root.querySelector('[data-action="compare"]').setAttribute('aria-pressed', 'false');
|
|
108
|
+
const clearPreview = () => {
|
|
109
|
+
if (!candidate) return;
|
|
110
|
+
candidate = undefined; theme?.compare();
|
|
90
111
|
root.querySelector('[data-variation-confirm]').hidden = true;
|
|
91
112
|
};
|
|
92
113
|
const preview = next => {
|
|
93
114
|
const validated = validateRecipe(next);
|
|
94
115
|
if (JSON.stringify(validated.values) === JSON.stringify(recipe.values)) return;
|
|
95
|
-
|
|
116
|
+
clearPreview();
|
|
96
117
|
if (!pending) gestureStart = structuredClone(recipe);
|
|
97
118
|
recipe = validated; pending = true;
|
|
98
119
|
if (!previewFrame) previewFrame = view.requestAnimationFrame(() => {
|
|
@@ -166,17 +187,13 @@ export function connect(host) {
|
|
|
166
187
|
if (!button) { view.requestAnimationFrame(paint); return; }
|
|
167
188
|
if (button.dataset.action === 'save' && saving) return;
|
|
168
189
|
flush();
|
|
169
|
-
if (!['
|
|
190
|
+
if (!['preview-variation', 'apply-variation', 'cancel-variation'].includes(button.dataset.action)) clearPreview();
|
|
170
191
|
try {
|
|
171
192
|
switch (button.dataset.action) {
|
|
172
193
|
case 'undo': case 'redo':
|
|
173
194
|
theme?.[button.dataset.action](); show(theme?.recipe ?? recipe, false); sayApplied('Previous theme decision restored.'); break;
|
|
174
|
-
case 'compare':
|
|
175
|
-
candidate = undefined; comparing = !comparing; theme?.compare(comparing ? entryRecipe : undefined);
|
|
176
|
-
button.setAttribute('aria-pressed', String(comparing)); root.querySelector('[data-variation-confirm]').hidden = true;
|
|
177
|
-
say(comparing ? 'Comparing with the theme when you entered. Your edits remain intact. Choose Compare again to return.' : 'Back to your edited theme.'); break;
|
|
178
195
|
case 'preview-variation': {
|
|
179
|
-
|
|
196
|
+
clearPreview();
|
|
180
197
|
const locks = [...root.querySelectorAll('[data-family-lock]')].filter(control => control.querySelector('input')?.checked).map(control => control.dataset.familyLock);
|
|
181
198
|
candidate = nearbyVariation(recipe, root.querySelector('[data-variation-choice]').value, locks);
|
|
182
199
|
theme?.compare(candidate); root.querySelector('[data-variation-confirm]').hidden = false;
|
|
@@ -185,10 +202,10 @@ export function connect(host) {
|
|
|
185
202
|
break;
|
|
186
203
|
}
|
|
187
204
|
case 'apply-variation': {
|
|
188
|
-
const selected = candidate;
|
|
205
|
+
const selected = candidate; clearPreview();
|
|
189
206
|
if (selected) show(selected); sayApplied('Variation applied. Undo returns to your previous theme.'); break;
|
|
190
207
|
}
|
|
191
|
-
case 'cancel-variation':
|
|
208
|
+
case 'cancel-variation': clearPreview(); paint(); say('Variation cancelled; your theme is unchanged.'); break;
|
|
192
209
|
case 'light-top': case 'light-left': case 'light-center': {
|
|
193
210
|
const positions = { 'light-top': [.5, 0], 'light-left': [.15, .15], 'light-center': [.5, .5] };
|
|
194
211
|
const [x, y] = positions[button.dataset.action];
|
|
@@ -252,7 +269,7 @@ export function connect(host) {
|
|
|
252
269
|
if (event.target.matches('[data-light-editor]') || event.target.closest?.('[data-token]')) {
|
|
253
270
|
input(event); flush(); sayApplied('Theme remembered in this browser. Undo restores the previous decision.'); return;
|
|
254
271
|
}
|
|
255
|
-
flush();
|
|
272
|
+
flush(); clearPreview();
|
|
256
273
|
if (event.target.matches('[data-unit-for]')) {
|
|
257
274
|
const field = fields.get(event.target.dataset.unitFor);
|
|
258
275
|
try {
|
|
@@ -280,7 +297,7 @@ export function connect(host) {
|
|
|
280
297
|
const cancel = event => { if (event.target.matches('[data-light-editor], [data-editor-kind="curve"]')) { flush(false); say('Drag cancelled; the previous value is restored.'); } };
|
|
281
298
|
const keyboard = event => {
|
|
282
299
|
if (!(event.ctrlKey || event.metaKey) || event.key.toLowerCase() !== 'z' || event.target.closest?.('input,textarea,select,[contenteditable="true"]')) return;
|
|
283
|
-
event.preventDefault(); flush();
|
|
300
|
+
event.preventDefault(); flush(); clearPreview(); theme?.[event.shiftKey ? 'redo' : 'undo'](); show(theme?.recipe ?? recipe, false);
|
|
284
301
|
};
|
|
285
302
|
const ready = event => {
|
|
286
303
|
if (event.data?.type === 'looma-preview-ready' && [...root.querySelectorAll('iframe')].some(frame => frame.contentWindow === event.source)) paint();
|
|
@@ -289,7 +306,7 @@ export function connect(host) {
|
|
|
289
306
|
let fontRequest = 0;
|
|
290
307
|
const selectFont = async event => {
|
|
291
308
|
const control = event.target.closest?.('[data-font-picker]'); if (!control) return;
|
|
292
|
-
flush();
|
|
309
|
+
flush(); clearPreview();
|
|
293
310
|
const id = control.dataset.fontPicker, family = event.detail.value;
|
|
294
311
|
const fonts = { ...recipe.fonts };
|
|
295
312
|
if (family === 'system') delete fonts[id]; else fonts[id] = family;
|
|
@@ -310,6 +327,7 @@ export function connect(host) {
|
|
|
310
327
|
selectedExample = event.detail.value ?? '';
|
|
311
328
|
filterExamples();
|
|
312
329
|
};
|
|
330
|
+
root.addEventListener('toggle', updateSizes, true);
|
|
313
331
|
root.addEventListener('looma-example-remove', remove);
|
|
314
332
|
root.addEventListener('input', input);
|
|
315
333
|
root.addEventListener('cancel', cancel);
|
|
@@ -334,5 +352,5 @@ export function connect(host) {
|
|
|
334
352
|
root.querySelector('[data-action="save"]').hidden = false;
|
|
335
353
|
}).catch(error => { if (alive && error.name !== 'AbortError') say(error.message, true); });
|
|
336
354
|
}
|
|
337
|
-
return () => { flush();
|
|
355
|
+
return () => { root.removeEventListener('toggle', updateSizes, true); flush(); clearPreview(); root.removeEventListener('cancel', cancel); root.removeEventListener('keydown', keyboard); alive = false; abort.abort(); root.removeEventListener('looma-example-remove', remove); root.removeEventListener('input', input); root.removeEventListener('value-change', selectExample); root.removeEventListener('value-change', selectFont); root.removeEventListener('blur', blur, true); root.removeEventListener('change', change); root.removeEventListener('click', click); view.removeEventListener('beforeunload', flush); view.removeEventListener('message', ready); view.removeEventListener(themeEvent, synchronize); };
|
|
338
356
|
}
|
package/docs/theme.mjs
CHANGED
|
@@ -19,7 +19,7 @@ export const themeFields = [
|
|
|
19
19
|
['Colour', 'palette-relationship', 'Palette relationship', null, 'choice', 'preserve', 'Keep seed hues, or relate quiet surfaces to the brand through neighboring or complementary hues.'],
|
|
20
20
|
['Type', 'body-font', 'Body font', '--ui-font-sans', 'font', 'system-ui, sans-serif', 'The shared interface typeface.'],
|
|
21
21
|
['Type', 'display-font', 'Display font', '--ui-font-display', 'font', 'system-ui, sans-serif', 'The heading typeface.'],
|
|
22
|
-
['Type', 'text-size', '
|
|
22
|
+
['Type', 'text-size', 'Base text size', '--ui-font-size', 'positive-length', '16px', 'Sets the document root in px. Type, spacing and layout use rem relationships and scale from this base once.'],
|
|
23
23
|
['Type', 'text-weight', 'Emphasis weight', '--ui-font-medium', 'weight', '500', 'Control-label emphasis; body and headings keep their roles.'],
|
|
24
24
|
['Size & spacing', 'control-roominess', 'Control roominess', '--ui-control-roominess', 'positive', '2.5', 'Shared control height as a multiple of base text size; buttons, fields and swatches stay aligned.'],
|
|
25
25
|
['Size & spacing', 'spacing', 'Spacing', '--ui-spacing', 'positive', '1', 'More or less breathing room throughout the interface.'],
|
|
@@ -29,12 +29,12 @@ export const themeFields = [
|
|
|
29
29
|
['Size & spacing', 'line-weight', 'Line weight', '--ui-border-width', 'length', '1px', 'Shared edge weight; focus stays independently visible.'],
|
|
30
30
|
['Material', 'outer-depth', 'Outer depth', '--ui-outer-depth', 'nonnegative', '0.6', 'How strongly controls and floating surfaces rise above the page.'],
|
|
31
31
|
['Material', 'inner-depth', 'Inner depth', '--ui-inner-depth', 'nonnegative', '0.3', 'How strongly fields and pressed controls sit into their surface.'],
|
|
32
|
-
['Material', 'glassiness', 'Glassiness', '--ui-glassiness', 'fraction', '0', '
|
|
32
|
+
['Material', 'glassiness', 'Glassiness', '--ui-glassiness', 'fraction', '0', 'Clear through richly frosted: the upper range adds stronger blur, saturation and glass edges. Content stays readable.'],
|
|
33
33
|
['Material', 'surface-separation', 'Surface separation', '--ui-surface-separation', 'fraction', '0', 'One material offset, scaled by popup purpose and actual size: quiet tooltips through substantial dialogs.'],
|
|
34
34
|
['Material', 'translucency', 'Overlay translucency', '--ui-overlay-translucency', 'fraction', '0', 'How much background comes through floating surfaces.'],
|
|
35
35
|
['Material', 'edge-light', 'Edge light', '--ui-edge-light', 'fraction', '0', 'Light caught by surface edges, independent of depth.'],
|
|
36
36
|
['Material', 'finish', 'Finish', '--ui-finish', 'fraction', '0', 'Matte through polished: how strongly the material catches a reflection.'],
|
|
37
|
-
['Material', 'glow', 'Glow', '--ui-glow', 'fraction', '0', '
|
|
37
|
+
['Material', 'glow', 'Glow', '--ui-glow', 'fraction', '0', 'Reflected light beyond a surface, from a quiet halo to a vivid bloom at the upper end.'],
|
|
38
38
|
['Material', 'light-x', 'Light horizontal position', '--ui-light-x', 'fraction', '0.5', 'The light source from left to right, relative to each surface.'],
|
|
39
39
|
['Material', 'light-y', 'Light vertical position', '--ui-light-y', 'fraction', '0', 'The light source from top to bottom, relative to each surface.'],
|
|
40
40
|
['Material', 'light-spread', 'Light spread', '--ui-light-spread', 'fraction', '0.5', 'A concentrated spot through broad illumination.'],
|
|
@@ -161,6 +161,7 @@ export function validateRecipe(input) {
|
|
|
161
161
|
if (field.type === 'weight') valid &&= number >= 1 && number <= 1000;
|
|
162
162
|
}
|
|
163
163
|
}
|
|
164
|
+
if (field.id === 'text-size' && valid && !value.endsWith('px')) throw new Error('Base text size: enter a px value for the document root.');
|
|
164
165
|
if (!valid) throw new Error(`${field.label}: enter a valid ${field.type.replaceAll('-', ' ')} value.`);
|
|
165
166
|
if (Object.hasOwn(bounds, field.id)) {
|
|
166
167
|
const number = boundedNumber(field, value), [min, max] = bounds[field.id];
|
|
@@ -186,7 +187,7 @@ const contrast = (a, b) => (Math.max(luminance(a), luminance(b)) + .05) / (Math.
|
|
|
186
187
|
// fill cannot wash out the label, even at maximum finish and light strength.
|
|
187
188
|
function reflection(fill, text) {
|
|
188
189
|
const labels = Array.isArray(text) ? text : [text];
|
|
189
|
-
let amount = .
|
|
190
|
+
let amount = .6;
|
|
190
191
|
while (amount > 0 && labels.some(label => contrast(mix('#ffffff', fill, amount), label) < 4.5)) amount = Math.max(0, amount - .005);
|
|
191
192
|
return mix('#ffffff', fill, amount);
|
|
192
193
|
}
|
|
@@ -210,7 +211,7 @@ export function deriveTheme(input, { mode } = {}) {
|
|
|
210
211
|
const tokens = Object.fromEntries(themeFields.filter(field => field.property).map(field => [field.property, values[field.id]]));
|
|
211
212
|
for (const field of themeFields.filter(field => field.type.includes('length') && Object.hasOwn(bounds, field.id))) {
|
|
212
213
|
if (boundedNumber(field, values[field.id]) === undefined) {
|
|
213
|
-
const [min, max] = bounds[field.id]; tokens[field.property] = `clamp(${min
|
|
214
|
+
const [min, max] = bounds[field.id]; tokens[field.property] = `clamp(${min}px, ${values[field.id]}, ${max}px)`;
|
|
214
215
|
}
|
|
215
216
|
}
|
|
216
217
|
const cacheKey = JSON.stringify([mode, ...paletteKeys.map(key => values[key])]);
|
package/docs/value-editors.mjs
CHANGED
|
@@ -3,14 +3,15 @@ const lengthUnits = { px: 1, rem: 16, em: 16, cm: 96 / 2.54, mm: 96 / 25.4, Q: 9
|
|
|
3
3
|
const timeUnits = { ms: 1, s: 1000 };
|
|
4
4
|
const rounded = value => String(Number(Number(value).toFixed(6)));
|
|
5
5
|
|
|
6
|
-
export function valueEditor(field, value) {
|
|
6
|
+
export function valueEditor(field, value, { remPixels = 16 } = {}) {
|
|
7
|
+
if (field.id === 'text-size') return { kind: 'number', value, unit: 'px', min: field.min, max: field.max, step: 1 };
|
|
7
8
|
if (field.type === 'choice') return { kind: 'choice', value };
|
|
8
9
|
if (field.type === 'easing') return { kind: /^step-/.test(value) ? 'text' : 'curve', value };
|
|
9
10
|
if (['colour', 'font'].includes(field.type)) return { kind: 'text', value };
|
|
10
11
|
if (field.type.includes('length') || field.type === 'time') {
|
|
11
12
|
const unit = value.match(/[a-z]+$/i)?.[0] ?? 'px';
|
|
12
|
-
//
|
|
13
|
-
const units = field.type === 'time' ? timeUnits : lengthUnits;
|
|
13
|
+
// Convert authoring limits against the same root as the displayed quantity.
|
|
14
|
+
const units = field.type === 'time' ? timeUnits : { ...lengthUnits, rem: remPixels };
|
|
14
15
|
const factor = units[unit];
|
|
15
16
|
return { kind: 'number', value: value === '0' ? '0px' : value, unit,
|
|
16
17
|
min: factor ? field.min / factor : .001, max: factor ? field.max / factor : 1000000,
|
package/editor/icons.d.ts
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import { type IconNode } from 'lucide';
|
|
2
2
|
/** The opinionated Lucide set used by Looma's shipped interaction surfaces. */
|
|
3
3
|
export declare const LOOMA_ICONS: {
|
|
4
|
+
monitor: IconNode;
|
|
5
|
+
sun: IconNode;
|
|
6
|
+
moon: IconNode;
|
|
4
7
|
'align-center': IconNode;
|
|
5
8
|
'align-left': IconNode;
|
|
6
9
|
'align-right': IconNode;
|
package/editor/index.js
CHANGED
|
@@ -2,10 +2,13 @@ import { n as TABLE_CELL_BACKGROUND_PRESETS, t as TABLE_CELL_BACKGROUND_OPTIONS
|
|
|
2
2
|
import { n as resolveTableCellAt, t as measureTableOverlayGeometry } from "./chunks/ui.js";
|
|
3
3
|
import { $ as LOOMA_ACTIVE_BLOCK_BLUR_GRACE_MS, A as filterLoomaMentionItems, B as LOOMA_FOOTNOTE_LABEL, C as loomaMarkdownToHtml, D as createLoomaMentionExtension, E as LoomaMentionSuggestionPluginKey, F as LoomaTableOfContents, G as LoomaFigcaption, H as LoomaFootnoteReference, I as getTableOfContentsEntries, J as revealLoomaExpandAt, K as LoomaFigure, L as normalizeTableOfContentsConfig, M as LoomaImage, N as normalizeImageDimension, O as DEFAULT_MENTION_RESULT_LIMIT, P as normalizeImagePlacement, Q as LoomaCallout, R as LoomaTab, S as loomaMarkdownFromDoc, T as siteRelativeHref, U as LoomaFootnotes, V as LoomaFootnote, W as findLoomaFootnote, X as LoomaChip, Y as LOOMA_CHIP_COLORS, Z as normalizeLoomaChipColor, _ as getActiveTableCellAlignment, a as getActiveTableUiState, b as setActiveTableCellBackground, c as insertTableAtRange, d as LoomaTableKit, et as LOOMA_ACTIVE_BLOCK_CLASS, f as getDefaultEditorExtensions, g as LoomaTableHeader, h as LoomaTableCell, i as getDefaultSlashCommands, j as normalizeMentionResultLimit, k as MAX_MENTION_RESULT_LIMIT, l as normalizeActiveTableColumnWidths, m as LoomaTable, n as createLoomaSlashCommandExtension, nt as LoomaActiveBlock, o as handleTableAction, p as getLoomaTableExtensions, q as LoomaExpand, r as filterLoomaSlashCommands, rt as LoomaActiveBlockPluginKey, s as handleTableOverlayAction, t as LoomaSlashCommand, tt as LOOMA_ACTIVE_BLOCK_TYPING_IDLE_MS, u as shouldShowTextFormattingToolbar, v as getActiveTableCellBackground, w as createSiteRelativeLinks, x as LoomaSmartPaste, y as setActiveTableCellAlignment, z as LoomaTabs } from "./chunks/extensions.js";
|
|
4
4
|
import { a as setReferenceDisplay, i as referenceDisplay, n as LoomaReferencePresentation, o as LOOMA_CALLOUT_TONES, r as getReferenceDisplay, t as LoomaReference } from "./chunks/reference-display.js";
|
|
5
|
-
import { AlignCenter, AlignLeft, AlignRight, ArrowLeft, Bell, Bold, BookUser, Braces, Calculator, Check, ChevronDown, ChevronLeft, ChevronRight, CircleCheck, CircleDot, CircleQuestionMark, CircleX, CodeXml, Columns3, Compass, CreditCard, Ellipsis, Eraser, ExternalLink, FileText, Folder, GripHorizontal, GripVertical, Heading1, Heading2, Heading3, Highlighter, History, Image, Info, Italic, LayoutDashboard, Lightbulb, Link, List, ListOrdered, ListTodo, LoaderCircle, Merge, MessageSquareWarning, Minus, NotebookPen, PaintBucket, PanelBottom, PanelLeft, PanelRight, PanelTop, Paperclip, Pencil, Pilcrow, Plus, Quote, Receipt, Redo2, Rows3, Search, Settings, Split, Strikethrough, Superscript, Table2, Tag, Trash2, TriangleAlert, Truck, Underline, Undo2, Unlink, UserRound, Users, X } from "lucide";
|
|
5
|
+
import { AlignCenter, AlignLeft, AlignRight, ArrowLeft, Bell, Bold, BookUser, Braces, Calculator, Check, ChevronDown, ChevronLeft, ChevronRight, CircleCheck, CircleDot, CircleQuestionMark, CircleX, CodeXml, Columns3, Compass, CreditCard, Ellipsis, Eraser, ExternalLink, FileText, Folder, GripHorizontal, GripVertical, Heading1, Heading2, Heading3, Highlighter, History, Image, Info, Italic, LayoutDashboard, Lightbulb, Link, List, ListOrdered, ListTodo, LoaderCircle, Merge, MessageSquareWarning, Minus, Monitor, Moon, NotebookPen, PaintBucket, PanelBottom, PanelLeft, PanelRight, PanelTop, Paperclip, Pencil, Pilcrow, Plus, Quote, Receipt, Redo2, Rows3, Search, Settings, Split, Strikethrough, Sun, Superscript, Table2, Tag, Trash2, TriangleAlert, Truck, Underline, Undo2, Unlink, UserRound, Users, X } from "lucide";
|
|
6
6
|
//#region src/editor/icons.ts
|
|
7
7
|
/** The opinionated Lucide set used by Looma's shipped interaction surfaces. */
|
|
8
8
|
var LOOMA_ICONS = {
|
|
9
|
+
monitor: Monitor,
|
|
10
|
+
sun: Sun,
|
|
11
|
+
moon: Moon,
|
|
9
12
|
"align-center": AlignCenter,
|
|
10
13
|
"align-left": AlignLeft,
|
|
11
14
|
"align-right": AlignRight,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@threadlabs/looma",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.37.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Looma components as HTML, Vue, and plain DOM, with design tokens.",
|
|
6
6
|
"license": "MIT",
|
|
@@ -127,7 +127,7 @@
|
|
|
127
127
|
"@tiptap/pm": ">=2 <3",
|
|
128
128
|
"@tiptap/vue-3": ">=2 <3",
|
|
129
129
|
"vue": "^3.5.43",
|
|
130
|
-
"markupress": "^0.8.
|
|
130
|
+
"markupress": "^0.8.7 || ^0.9.0",
|
|
131
131
|
"@nextwebwg/htmlkit": "1.0.0-alpha.69",
|
|
132
132
|
"vite": "^8.3.4"
|
|
133
133
|
},
|
package/styles/ui-avatar.css
CHANGED
|
@@ -60,7 +60,7 @@
|
|
|
60
60
|
inline-size: 1.25rem;
|
|
61
61
|
block-size: 1.25rem;
|
|
62
62
|
/* Below the type scale on purpose: two initials need room inside a 1.25rem circle. */
|
|
63
|
-
font-size: calc(var(--ui-font-size) * 0.5625);
|
|
63
|
+
font-size: calc(var(--ui-font-size-md) * 0.5625);
|
|
64
64
|
letter-spacing: 0.02em;
|
|
65
65
|
}
|
|
66
66
|
|
package/styles/ui-button.css
CHANGED
|
@@ -451,18 +451,9 @@
|
|
|
451
451
|
}
|
|
452
452
|
|
|
453
453
|
/* The size states outrank :host, so sm is named too, or a small button would stay small. */
|
|
454
|
-
@media (pointer: coarse) {
|
|
455
|
-
:scope:where([data-component~="ui-button"]) {
|
|
456
|
-
min-block-size: max(var(--ui-control-size-md), var(--ui-control-min-block-size));
|
|
457
|
-
}
|
|
458
|
-
:scope:where([data-component~="ui-button"])[data-ui-button-state~="size=sm"] {
|
|
459
|
-
min-block-size: max(var(--ui-control-size-sm), var(--ui-control-min-block-size));
|
|
460
|
-
}
|
|
461
|
-
}
|
|
462
454
|
|
|
463
455
|
/* Under touch, a link-style button's press lands within the control minimum: an invisible hit
|
|
464
|
-
area, centred on it, like IconButton's. A
|
|
465
|
-
a link-style one keeps its text size, so a standalone one ("See all" under a list) needs the
|
|
456
|
+
area, centred on it, like IconButton's. A link-style action keeps its text size, so a standalone one ("See all" under a list) needs the
|
|
466
457
|
area. xs sits inside a line of text the same way, so it keeps its size and gains the area too.
|
|
467
458
|
Only these need it: a hit area on every button would reach over its neighbours. */
|
|
468
459
|
html[data-ui-input-modality="touch"] :scope:where([data-component~="ui-button"])[data-appearance="link"],
|
|
@@ -567,12 +558,9 @@
|
|
|
567
558
|
/* Ghost keeps no border, but gains the same neutral surface as other disabled buttons. */
|
|
568
559
|
|
|
569
560
|
/* The size states outrank :host, so sm is named too, or a small button would stay small. */
|
|
570
|
-
@media (pointer: coarse) {
|
|
571
|
-
}
|
|
572
561
|
|
|
573
562
|
/* Under touch, a link-style button's press lands within the control minimum: an invisible hit
|
|
574
|
-
area, centred on it, like IconButton's. A
|
|
575
|
-
a link-style one keeps its text size, so a standalone one ("See all" under a list) needs the
|
|
563
|
+
area, centred on it, like IconButton's. A link-style action keeps its text size, so a standalone one ("See all" under a list) needs the
|
|
576
564
|
area. xs sits inside a line of text the same way, so it keeps its size and gains the area too.
|
|
577
565
|
Only these need it: a hit area on every button would reach over its neighbours. */
|
|
578
566
|
|
package/styles/ui-card.css
CHANGED
|
@@ -52,7 +52,7 @@
|
|
|
52
52
|
}
|
|
53
53
|
|
|
54
54
|
:scope:where([data-component~="ui-card"])[data-ui-card-state~="treatment=expressive"] {
|
|
55
|
-
--_ui-panel-glass: clamp(0, var(--ui-glassiness, 0), 1);
|
|
55
|
+
--_ui-panel-glass: calc(clamp(0, var(--ui-glassiness, 0), 1) + 2 * pow(clamp(0, var(--ui-glassiness, 0), 1), 4));
|
|
56
56
|
--_ui-panel-separation: clamp(0, var(--ui-surface-separation, 0), 1);
|
|
57
57
|
--_ui-panel-rim: calc((var(--_ui-panel-separation) + var(--_ui-panel-glass) * 0.25) * 0.65 * 8px);
|
|
58
58
|
border-width: calc(var(--ui-border-width) + var(--_ui-panel-rim));
|
package/styles/ui-checkbox.css
CHANGED
|
@@ -210,12 +210,6 @@
|
|
|
210
210
|
padding-block: max(0px, calc((var(--_row) - max(var(--_line), var(--_ui-checkbox-size, 1.125rem))) / 2)) !important;
|
|
211
211
|
}
|
|
212
212
|
|
|
213
|
-
@media (pointer: coarse) {
|
|
214
|
-
:scope:where([data-component~="ui-checkbox"])[data-ui-checkbox-state~="size=sm"] {
|
|
215
|
-
--_row: var(--ui-control-min-block-size);
|
|
216
|
-
}
|
|
217
|
-
}
|
|
218
|
-
|
|
219
213
|
/* A component that sets its display still honours the hidden attribute on its root. */
|
|
220
214
|
:scope:where([data-component~="ui-checkbox"])[hidden] {
|
|
221
215
|
display: none !important;
|
|
@@ -235,8 +229,5 @@
|
|
|
235
229
|
and the first line centres in that control height, so the label shares a row's baseline.
|
|
236
230
|
Multi-line labels and descriptions still grow downward from that first line. */
|
|
237
231
|
|
|
238
|
-
@media (pointer: coarse) {
|
|
239
|
-
}
|
|
240
|
-
|
|
241
232
|
/* A component that sets its display still honours the hidden attribute on its root. */
|
|
242
233
|
}
|
|
@@ -104,17 +104,4 @@
|
|
|
104
104
|
padding: 0 !important;
|
|
105
105
|
accent-color: var(--ui-accent-solid);
|
|
106
106
|
}
|
|
107
|
-
@media (pointer: coarse) {
|
|
108
|
-
:scope:where([data-component~="ui-color-picker"]) {
|
|
109
|
-
--_size: max(var(--ui-control-size-md), var(--ui-control-min-block-size));
|
|
110
|
-
}
|
|
111
|
-
:scope:where([data-component~="ui-color-picker"])[data-ui-color-picker-state~="size=sm"] {
|
|
112
|
-
--_size: max(var(--ui-control-size-sm), var(--ui-control-min-block-size));
|
|
113
|
-
--_font: var(--ui-font-size-md);
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
@scope ([data-component~="ui-color-picker"]) to ([data-component]) {
|
|
118
|
-
@media (pointer: coarse) {
|
|
119
|
-
}
|
|
120
107
|
}
|
package/styles/ui-combobox.css
CHANGED
|
@@ -310,20 +310,8 @@
|
|
|
310
310
|
white-space: nowrap;
|
|
311
311
|
}
|
|
312
312
|
@media (pointer: coarse) {
|
|
313
|
-
:scope:where([data-component~="ui-combobox"]) {
|
|
314
|
-
--_row: max(var(--ui-control-size-md), var(--ui-control-min-block-size));
|
|
315
|
-
}
|
|
316
|
-
button {
|
|
317
|
-
min-inline-size: 2.75rem;
|
|
318
|
-
min-block-size: calc(var(--_row) - 2 * var(--ui-border-width));
|
|
319
|
-
}
|
|
320
|
-
/* Under touch a small field is still a touch-sized target, as a small button is, and keeps
|
|
321
|
-
body-size text: below 16px, iOS zooms the page into the focused field. */
|
|
322
|
-
:scope:where([data-component~="ui-combobox"])[data-ui-combobox-state~="size=sm"] {
|
|
323
|
-
--_row: max(var(--ui-control-size-sm), var(--ui-control-min-block-size));
|
|
324
|
-
}
|
|
325
313
|
:scope:where([data-component~="ui-combobox"])[data-ui-combobox-state~="size=sm"] input {
|
|
326
|
-
font-size:
|
|
314
|
+
font-size: var(--ui-font-size-md);
|
|
327
315
|
}
|
|
328
316
|
}
|
|
329
317
|
@media (forced-colors: active) {
|
|
@@ -408,8 +396,6 @@
|
|
|
408
396
|
/* A ghost icon button: the affordance is the mark, not a box around it. */
|
|
409
397
|
/* A row with a tag or a description: the label and its tag on one line, the description under them. */
|
|
410
398
|
@media (pointer: coarse) {
|
|
411
|
-
/* Under touch a small field is still a touch-sized target, as a small button is, and keeps
|
|
412
|
-
body-size text: below 16px, iOS zooms the page into the focused field. */
|
|
413
399
|
}
|
|
414
400
|
@media (forced-colors: active) {
|
|
415
401
|
}
|
|
@@ -722,20 +708,8 @@
|
|
|
722
708
|
white-space: nowrap;
|
|
723
709
|
}
|
|
724
710
|
@media (pointer: coarse) {
|
|
725
|
-
:where([data-component~="ui-combobox"]):where(:scope, :not([data-component], [data-slotted]:not([data-slotted~="ui-combobox"]))) {
|
|
726
|
-
--_row: max(var(--ui-control-size-md), var(--ui-control-min-block-size));
|
|
727
|
-
}
|
|
728
|
-
button:where(:scope, :scope *) {
|
|
729
|
-
min-inline-size: 2.75rem;
|
|
730
|
-
min-block-size: calc(var(--_row) - 2 * var(--ui-border-width));
|
|
731
|
-
}
|
|
732
|
-
/* Under touch a small field is still a touch-sized target, as a small button is, and keeps
|
|
733
|
-
body-size text: below 16px, iOS zooms the page into the focused field. */
|
|
734
|
-
:where([data-component~="ui-combobox"])[data-ui-combobox-state~="size=sm"]:where(:scope, :not([data-component], [data-slotted]:not([data-slotted~="ui-combobox"]))) {
|
|
735
|
-
--_row: max(var(--ui-control-size-sm), var(--ui-control-min-block-size));
|
|
736
|
-
}
|
|
737
711
|
:where([data-component~="ui-combobox"])[data-ui-combobox-state~="size=sm"] input:where(:scope, :not([data-component], [data-slotted]:not([data-slotted~="ui-combobox"]))) {
|
|
738
|
-
font-size:
|
|
712
|
+
font-size: var(--ui-font-size-md);
|
|
739
713
|
}
|
|
740
714
|
}
|
|
741
715
|
@media (forced-colors: active) {
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/* Generated by HTML Next 1.0.0-alpha.69. Do not edit. */
|
|
2
|
+
@supports (-moz-appearance: none) {
|
|
3
|
+
:where([data-component], [data-slotted]) { --html-next-scope-owner: boundary; }
|
|
4
|
+
:where([data-slotted]:not([data-component])) { --html-next-scope-owner: projected !important; }
|
|
5
|
+
}
|
|
6
|
+
@supports (-moz-appearance: none) {
|
|
7
|
+
[data-component~="ui-cycle-button"] { --html-next-scope-owner: "ui-cycle-button" !important; }
|
|
8
|
+
}
|
|
9
|
+
@scope ([data-component~="ui-cycle-button"]) to ([data-component], [data-slotted]) {
|
|
10
|
+
:scope:where([data-component~="ui-cycle-button"]) {
|
|
11
|
+
display: inline-flex !important;
|
|
12
|
+
align-items: center !important;
|
|
13
|
+
justify-content: center !important;
|
|
14
|
+
gap: 0 !important;
|
|
15
|
+
margin: 0 !important;
|
|
16
|
+
padding: 0 !important;
|
|
17
|
+
vertical-align: middle;
|
|
18
|
+
}
|
|
19
|
+
:scope:where([data-component~="ui-cycle-button"])[hidden] {
|
|
20
|
+
display: none !important;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
@scope ([data-slotted~="ui-cycle-button"]:not([data-component])) to ([data-component], [data-slotted]) {
|
|
24
|
+
:where([data-component~="ui-cycle-button"]):where(:scope, :not([data-component], [data-slotted]:not([data-slotted~="ui-cycle-button"]))) {
|
|
25
|
+
display: inline-flex !important;
|
|
26
|
+
align-items: center !important;
|
|
27
|
+
justify-content: center !important;
|
|
28
|
+
gap: 0 !important;
|
|
29
|
+
margin: 0 !important;
|
|
30
|
+
padding: 0 !important;
|
|
31
|
+
vertical-align: middle;
|
|
32
|
+
}
|
|
33
|
+
:where([data-component~="ui-cycle-button"])[hidden]:where(:scope, :not([data-component], [data-slotted]:not([data-slotted~="ui-cycle-button"]))) {
|
|
34
|
+
display: none !important;
|
|
35
|
+
}
|
|
36
|
+
}
|
package/styles/ui-dialog.css
CHANGED
|
@@ -74,7 +74,7 @@
|
|
|
74
74
|
/* Purpose and measured size shape one shared separation lever. The content plane stays opaque;
|
|
75
75
|
transparency, saturation and edge light belong to the surrounding decorative material. */
|
|
76
76
|
:scope:where([data-component~="ui-dialog"]) {
|
|
77
|
-
--_ui-material-glassiness: clamp(0, var(--ui-glassiness, 0), 1);
|
|
77
|
+
--_ui-material-glassiness: calc(clamp(0, var(--ui-glassiness, 0), 1) + 2 * pow(clamp(0, var(--ui-glassiness, 0), 1), 4));
|
|
78
78
|
--_ui-material-separation: clamp(0, var(--ui-surface-separation, 0), 1);
|
|
79
79
|
--_ui-material-active: clamp(0, calc((var(--_ui-material-glassiness) + var(--_ui-material-separation)) * 100000), 1);
|
|
80
80
|
--_ui-material-rim: calc((var(--_ui-material-separation) + var(--_ui-material-glassiness) * 0.25) * var(--_ui-material-scale, 1) * 8px);
|
|
@@ -395,7 +395,7 @@
|
|
|
395
395
|
/* Purpose and measured size shape one shared separation lever. The content plane stays opaque;
|
|
396
396
|
transparency, saturation and edge light belong to the surrounding decorative material. */
|
|
397
397
|
:where([data-component~="ui-dialog"]):where(:scope, :not([data-component], [data-slotted]:not([data-slotted~="ui-dialog"]))) {
|
|
398
|
-
--_ui-material-glassiness: clamp(0, var(--ui-glassiness, 0), 1);
|
|
398
|
+
--_ui-material-glassiness: calc(clamp(0, var(--ui-glassiness, 0), 1) + 2 * pow(clamp(0, var(--ui-glassiness, 0), 1), 4));
|
|
399
399
|
--_ui-material-separation: clamp(0, var(--ui-surface-separation, 0), 1);
|
|
400
400
|
--_ui-material-active: clamp(0, calc((var(--_ui-material-glassiness) + var(--_ui-material-separation)) * 100000), 1);
|
|
401
401
|
--_ui-material-rim: calc((var(--_ui-material-separation) + var(--_ui-material-glassiness) * 0.25) * var(--_ui-material-scale, 1) * 8px);
|
|
@@ -331,14 +331,6 @@
|
|
|
331
331
|
--_size: var(--ui-control-size-lg);
|
|
332
332
|
}
|
|
333
333
|
|
|
334
|
-
@media (pointer: coarse) {
|
|
335
|
-
:scope:where([data-component~="ui-icon-button"])[data-ui-icon-button-state~="matchButton"] {
|
|
336
|
-
inline-size: max(var(--ui-icon-button-size, var(--_size)), var(--ui-control-min-block-size));
|
|
337
|
-
min-inline-size: max(var(--ui-icon-button-size, var(--_size)), var(--ui-control-min-block-size));
|
|
338
|
-
min-block-size: max(var(--ui-icon-button-size, var(--_size)), var(--ui-control-min-block-size));
|
|
339
|
-
}
|
|
340
|
-
}
|
|
341
|
-
|
|
342
334
|
:scope:where([data-component~="ui-icon-button"])[data-ui-icon-button-state~="round"] {
|
|
343
335
|
border-radius: var(--ui-radius-round);
|
|
344
336
|
}
|
|
@@ -348,14 +340,18 @@
|
|
|
348
340
|
background-color: var(--ui-icon-button-outline-surface, var(--_icon-button-outline-surface, var(--ui-surface-default)));
|
|
349
341
|
background-image: var(--ui-surface-highlight, none);
|
|
350
342
|
color: var(--ui-icon-button-outline-text, var(--_icon-button-text, var(--ui-text-primary)));
|
|
351
|
-
box-shadow:
|
|
343
|
+
box-shadow:
|
|
344
|
+
var(--ui-action-shadow, var(--_ui-action-shadow)),
|
|
345
|
+
var(--_tone-glow);
|
|
352
346
|
}
|
|
353
347
|
|
|
354
348
|
:scope:where([data-component~="ui-icon-button"])[data-appearance="solid"] {
|
|
355
349
|
background-color: var(--ui-icon-button-solid-surface, var(--_icon-button-solid-surface, var(--ui-accent-solid)));
|
|
356
350
|
background-image: var(--_tone-highlight);
|
|
357
351
|
color: var(--ui-icon-button-solid-text, var(--_icon-button-solid-text, var(--ui-text-on-accent)));
|
|
358
|
-
box-shadow:
|
|
352
|
+
box-shadow:
|
|
353
|
+
var(--ui-icon-button-solid-shadow, var(--ui-action-shadow, var(--_ui-action-shadow))),
|
|
354
|
+
var(--_tone-glow);
|
|
359
355
|
}
|
|
360
356
|
|
|
361
357
|
:scope:where([data-component~="ui-icon-button"]):hover:enabled:not([aria-disabled="true"]) {
|
|
@@ -509,9 +505,6 @@
|
|
|
509
505
|
|
|
510
506
|
/* A text button's companion uses control geometry; compact icon buttons keep their own density. */
|
|
511
507
|
|
|
512
|
-
@media (pointer: coarse) {
|
|
513
|
-
}
|
|
514
|
-
|
|
515
508
|
/* Native toggle state persists after pointer release, including during loading. */
|
|
516
509
|
|
|
517
510
|
@media (prefers-reduced-motion: reduce) {
|
|
@@ -89,16 +89,6 @@
|
|
|
89
89
|
min-block-size: var(--ui-control-size-lg);
|
|
90
90
|
}
|
|
91
91
|
|
|
92
|
-
@media (pointer: coarse) {
|
|
93
|
-
:scope:where([data-component~="ui-input-group"]) {
|
|
94
|
-
min-block-size: max(var(--ui-control-size-md), var(--ui-control-min-block-size));
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
:scope:where([data-component~="ui-input-group"]):has(.field [data-ui-input-state~="size=sm"]) {
|
|
98
|
-
min-block-size: var(--ui-control-min-block-size);
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
|
|
102
92
|
:scope:where([data-component~="ui-input-group"]):hover:not(:has(:is(input, textarea):focus, :is(input, textarea):disabled, :is(input, textarea):read-only, [aria-invalid="true"], :is(:user-invalid, [data-user-invalid]))) {
|
|
103
93
|
border-color: var(--ui-control-border-hover, var(--ui-text-secondary));
|
|
104
94
|
}
|
|
@@ -249,9 +239,6 @@
|
|
|
249
239
|
|
|
250
240
|
/* The frame takes its input's size, so a small group lines up with small buttons and fields. */
|
|
251
241
|
|
|
252
|
-
@media (pointer: coarse) {
|
|
253
|
-
}
|
|
254
|
-
|
|
255
242
|
/* The ring shows when the field's input would show its own: when it is focus-visible, which a
|
|
256
243
|
text input is for a pointer as well as a keyboard. A focused action button draws its own. */
|
|
257
244
|
|
package/styles/ui-input.css
CHANGED
|
@@ -100,28 +100,13 @@
|
|
|
100
100
|
font-size: var(--ui-font-size-md);
|
|
101
101
|
}
|
|
102
102
|
|
|
103
|
-
/*
|
|
104
|
-
body-size text: below 16px, iOS zooms the page into the focused field. */
|
|
103
|
+
/* Touch may use body-size text in a small native field to avoid focus zoom; this does not impose a larger visible control height. */
|
|
105
104
|
@media (pointer: coarse) {
|
|
106
|
-
:scope:where([data-component~="ui-input"]) {
|
|
107
|
-
min-block-size: var(
|
|
108
|
-
--ui-input-min-block-size,
|
|
109
|
-
var(--_ui-default-input-min-block-size, max(var(--ui-control-size-md), var(--ui-control-min-block-size)))
|
|
110
|
-
);
|
|
111
|
-
}
|
|
112
|
-
|
|
113
105
|
:scope:where([data-component~="ui-input"]),
|
|
114
106
|
:scope:where([data-component~="ui-input"])[data-ui-input-state~="size=sm"],
|
|
115
107
|
:scope:where([data-component~="ui-input"])[data-ui-input-state~="size=lg"] {
|
|
116
108
|
font-size: var(--ui-font-size-md);
|
|
117
109
|
}
|
|
118
|
-
|
|
119
|
-
:scope:where([data-component~="ui-input"])[data-ui-input-state~="size=sm"] {
|
|
120
|
-
min-block-size: var(
|
|
121
|
-
--ui-input-min-block-size,
|
|
122
|
-
var(--_ui-default-input-min-block-size, max(var(--ui-control-size-sm), var(--ui-control-min-block-size)))
|
|
123
|
-
);
|
|
124
|
-
}
|
|
125
110
|
}
|
|
126
111
|
|
|
127
112
|
/* Tabbing into a field selects its text; tint that selection softly instead of the page highlight. */
|
|
@@ -195,8 +180,7 @@
|
|
|
195
180
|
|
|
196
181
|
/* Each size reads the hook and then an Input Group's default first, so a group, which draws the frame, can zero it. */
|
|
197
182
|
|
|
198
|
-
/*
|
|
199
|
-
body-size text: below 16px, iOS zooms the page into the focused field. */
|
|
183
|
+
/* Touch may use body-size text in a small native field to avoid focus zoom; this does not impose a larger visible control height. */
|
|
200
184
|
@media (pointer: coarse) {
|
|
201
185
|
}
|
|
202
186
|
|
package/styles/ui-menu.css
CHANGED
|
@@ -103,7 +103,7 @@
|
|
|
103
103
|
/* Purpose and measured size shape one shared separation lever. The content plane stays opaque;
|
|
104
104
|
transparency, saturation and edge light belong to the surrounding decorative material. */
|
|
105
105
|
:scope:where([data-component~="ui-menu"]):not([inline]) .surface {
|
|
106
|
-
--_ui-material-glassiness: clamp(0, var(--ui-glassiness, 0), 1);
|
|
106
|
+
--_ui-material-glassiness: calc(clamp(0, var(--ui-glassiness, 0), 1) + 2 * pow(clamp(0, var(--ui-glassiness, 0), 1), 4));
|
|
107
107
|
--_ui-material-separation: clamp(0, var(--ui-surface-separation, 0), 1);
|
|
108
108
|
--_ui-material-active: clamp(0, calc((var(--_ui-material-glassiness) + var(--_ui-material-separation)) * 100000), 1);
|
|
109
109
|
--_ui-material-rim: calc((var(--_ui-material-separation) + var(--_ui-material-glassiness) * 0.25) * var(--_ui-material-scale, 0.28) * 8px);
|