@threadlabs/looma 0.37.0 → 0.37.2
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-button/ui-button.html +3 -12
- package/components/ui-card/ui-card.html +5 -5
- package/components/ui-checkbox/ui-checkbox.html +1 -7
- package/components/ui-color-picker/ui-color-picker.html +0 -9
- package/components/ui-combobox/ui-combobox.html +2 -14
- 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 +4 -19
- 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 +1 -7
- package/components/ui-select/ui-select.html +4 -10
- package/components/ui-switch/ui-switch.html +1 -7
- package/components/ui-tooltip/ui-tooltip.html +1 -1
- package/dist/index.js +47 -46
- package/docs/README.md +48 -7
- package/docs/card.html +2 -1
- package/docs/catalog.json +224 -49
- package/docs/customizer-render.mjs +7 -6
- package/docs/customizer.js +13 -19
- package/docs/theme.mjs +3 -3
- package/editor/icons.d.ts +3 -0
- package/editor/index.js +4 -1
- package/package.json +2 -2
- 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 +14 -14
- package/tokens.css +2 -10
- 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/UiButton.vue +2 -11
- package/vue/UiCard.vue +5 -3
- package/vue/UiCheckbox.vue +1 -7
- package/vue/UiColorPicker.vue +0 -9
- package/vue/UiCombobox.vue +3 -27
- 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 +2 -23
- package/vue/UiInputGroup.vue +0 -10
- package/vue/UiMenu.vue +3 -1
- package/vue/UiPopover.vue +3 -1
- package/vue/UiRadio.vue +1 -7
- package/vue/UiSelect.vue +3 -9
- package/vue/UiSwitch.vue +1 -7
- package/vue/UiTooltip.vue +3 -1
- package/vue/chunks/UiAffordanceScope.js +1 -1
- package/vue/chunks/UiAvatar.js +1 -1
- package/vue/chunks/UiAvatarGroup.js +1 -1
- 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 +3 -3
- package/vue/chunks/UiCluster.js +1 -1
- package/vue/chunks/UiColorPicker.js +2 -2
- package/vue/chunks/UiCombobox.js +3 -3
- 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 +3 -3
- 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 +3 -3
- 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 +3 -3
- 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 +3 -3
- 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 +74 -179
- package/vue/editor/index.js +68 -0
- package/vue/index.d.ts +1 -0
- package/vue/index.js +2 -1
|
@@ -11,9 +11,9 @@ export function customizerResource(site, input = defaultRecipe()) {
|
|
|
11
11
|
const toCard = example => Object.fromEntries(cardFields.map(key => [key, example[key]]));
|
|
12
12
|
const initial = ['ui-button', 'ui-input', 'ui-color-picker', 'ui-checkbox', 'ui-card', 'ui-switch', 'ui-text', 'ui-dialog', 'ui-popover'].map(tag => examples.find(example => example.tag === tag && (tag !== 'ui-button' || example.id.endsWith('/02-variant')) && (tag !== 'ui-text' || example.id.endsWith('/02-variants')) && (tag !== 'ui-card' || example.id.endsWith('/04-expressive-surface'))) ?? examples.find(example => example.tag === tag)).filter(Boolean).map(toCard);
|
|
13
13
|
if (!initial.length && examples.length) initial.push(toCard(examples[0]));
|
|
14
|
-
const documentationCard = (tag, suffix) => {
|
|
14
|
+
const documentationCard = (tag, suffix, previewOnly = false) => {
|
|
15
15
|
const example = examples.find(item => item.tag === tag && (!suffix || item.id.endsWith(suffix))) ?? examples.find(item => item.tag === tag);
|
|
16
|
-
return example ? `<looma-example-card ${cardFields.filter(key => key !== 'id').map(key => `${key.replace(/[A-Z]/g, letter => '-' + letter.toLowerCase())}="${escape(String(example[key]))}"`).join(' ')} identity="${escape(example.id)}" loading="lazy"></looma-example-card>` : '';
|
|
16
|
+
return example ? `<looma-example-card ${previewOnly ? 'preview-only' : ''} ${cardFields.filter(key => key !== 'id').map(key => `${key.replace(/[A-Z]/g, letter => '-' + letter.toLowerCase())}="${escape(String(example[key]))}"`).join(' ')} identity="${escape(example.id)}" loading="lazy"></looma-example-card>` : '';
|
|
17
17
|
};
|
|
18
18
|
const renderField = field => {
|
|
19
19
|
const value = recipe.values[field.id], editor = valueEditor(field, value, { remPixels: parseFloat(recipe.values['text-size']) });
|
|
@@ -45,7 +45,7 @@ export function customizerResource(site, input = defaultRecipe()) {
|
|
|
45
45
|
'Colour': `${renderPreset('palette', 'overview')}<ui-text variant="caption">Coordinated light and dark ranges.</ui-text><ui-button data-action="refine-palette">Refine palette</ui-button>`,
|
|
46
46
|
'Material': `${renderPreset('material', 'overview')}<ui-text variant="caption">Material and light, shared everywhere.</ui-text><ui-button data-action="refine-material">Refine material & light</ui-button>`,
|
|
47
47
|
'Motion': `<ui-text variant="caption">Open and close this real documentation example to feel the response pace and curve. Reduced motion follows your system preference.</ui-text>${documentationCard('ui-popover')}`,
|
|
48
|
-
'Size & spacing': `<ui-text variant="caption">Shared size ladder: buttons, fields and swatches align at each step
|
|
48
|
+
'Size & spacing': `<ui-text variant="caption">Shared size ladder: buttons, fields and swatches align at each step.</ui-text><ui-stack>${['sm','md','lg'].map((size,index) => `<ui-stack gap="xs" data-size-sample="${size}"><ui-text variant="caption">${['Small','Medium','Large'][index]}<span data-size-readout></span></ui-text><ui-cluster><ui-button size="${size}">Action</ui-button><ui-input size="${size}" style="--ui-input-inline-size: 10ch" label="${size} size sample" value="Frodo"></ui-input><ui-color-picker size="${size}" label="${size} swatch sample"></ui-color-picker></ui-cluster></ui-stack>`).join('')}</ui-stack><div class="spacing-samples" data-spacing-samples role="img" aria-label="Spacing progression"><span></span><span></span><span></span><span></span></div><ui-disclosure summary="How the scale grows"><ui-stack><p>Control roominess relates every field and button to base text. Size ratio separates small from large. Visible proportions stay the same on touch; hit areas are a separate design choice. Spacing and its curve shape page rhythm independently.</p>${themeFields.filter(item => ['scale-contrast','space-curve'].includes(item.id)).map(renderField).join('')}</ui-stack></ui-disclosure>`,
|
|
49
49
|
};
|
|
50
50
|
const families = [...new Set(themeFields.map(field => field.family))].filter(family => !['Colour', 'Material'].includes(family)).map(family => `<ui-disclosure summary="${family}"><ui-stack>${themeFields.filter(item => item.family === family && !['scale-contrast', 'space-curve'].includes(item.id)).map(renderField).join('')}${supplements[family] ?? ''}</ui-stack></ui-disclosure>`).join('');
|
|
51
51
|
const refinement = (family, content) => `<section data-refinement-view="${family}" hidden tabindex="-1" aria-label="${family === 'material' ? 'Material and light refinement' : 'Palette refinement'}"><ui-stack gap="xl"><ui-cluster justify="between"><ui-button data-action="back">Back to customization</ui-button><ui-button data-action="restore-${family}">Restore ${family} preset</ui-button></ui-cluster><ui-text variant="heading" as="h2">${family === 'material' ? 'Refine material & light' : 'Refine your palette'}</ui-text><p>These are advanced refinements of the same persistent theme. Every change applies throughout your documentation.</p>${renderPreset(family, 'refinement')}${content}</ui-stack></section>`;
|
|
@@ -59,7 +59,7 @@ export function customizerResource(site, input = defaultRecipe()) {
|
|
|
59
59
|
<div data-looma-customizer data-recipe="${escape(JSON.stringify(recipe))}" data-base="${escape(site.base)}" data-mode="${site.config.local ? 'local' : 'public'}" data-revision="${escape(site.config.revision ?? '')}">
|
|
60
60
|
<ui-stack gap="l"><ui-text>Choose a starting point, refine shared decisions, and see them on real documentation examples.</ui-text>
|
|
61
61
|
<ui-text variant="caption" role="status" aria-live="polite" data-message>Changes are remembered across the documentation.</ui-text>
|
|
62
|
-
<ui-cluster><ui-text variant="label" data-theme-origin></ui-text><ui-button size="sm" data-action="undo" disabled>Undo</ui-button><ui-button size="sm" data-action="redo" disabled>Redo</ui-button
|
|
62
|
+
<ui-cluster><ui-text variant="label" data-theme-origin></ui-text><ui-button size="sm" data-action="undo" disabled>Undo</ui-button><ui-button size="sm" data-action="redo" disabled>Redo</ui-button></ui-cluster>
|
|
63
63
|
<ui-disclosure summary="Explore nearby styles"><ui-stack><ui-text variant="caption">Preview a small change before applying it. Locks preserve your selected families.</ui-text><ui-cluster>${[['palette','Palette'],['material','Material'],['type','Type & spacing']].map(([id,label]) => `<ui-checkbox size="sm" data-family-lock="${id}">Lock ${label}</ui-checkbox>`).join('')}</ui-cluster><ui-form-field><label slot="label" for="nearby-style">Nearby style</label><ui-select id="nearby-style" data-variation-choice>${Object.entries(themeVariations).map(([id,label]) => `<option value="${id}">${label}</option>`).join('')}</ui-select></ui-form-field><ui-button data-action="preview-variation">Preview variation</ui-button><div data-variation-confirm hidden><ui-stack><ui-text data-variation-description role="status"></ui-text><ui-cluster><ui-button preset="primary" data-action="apply-variation">Apply variation</ui-button><ui-button data-action="cancel-variation">Cancel preview</ui-button></ui-cluster></ui-stack></div></ui-stack></ui-disclosure>
|
|
64
64
|
${materialView}${paletteView}
|
|
65
65
|
<div class="customizer-layout" data-customizer-overview>
|
|
@@ -73,6 +73,7 @@ export function customizerResource(site, input = defaultRecipe()) {
|
|
|
73
73
|
<ui-disclosure summary="Use your theme"><p>Every export includes light and dark. Leave data-theme off the page to follow system preference, or set it to light or dark on the page or a section. <a href="${escape(site.base)}design-system/">Installation and mode guidance</a></p></ui-disclosure>
|
|
74
74
|
<ui-disclosure summary="Theme CSS"><ui-textarea data-css readonly rows="12" aria-label="Generated theme CSS" value="${escape(themeCSS(recipe))}"></ui-textarea></ui-disclosure></ui-stack></ui-disclosure>
|
|
75
75
|
</ui-stack></aside>
|
|
76
|
+
<section data-theme-sampler><ui-stack gap="xs"><ui-text variant="heading" as="h2">Theme sampler</ui-text>${documentationCard('ui-cluster', '/99-theme-sampler', true)}</ui-stack></section>
|
|
76
77
|
<section class="examples"><ui-stack><ui-form-field><label slot="label" for="example-query">Filter previews</label><ui-input id="example-query" type="search" data-example-query placeholder="Name, tag or purpose"></ui-input></ui-form-field><ui-text variant="caption" role="status" aria-live="polite" data-example-count></ui-text><ui-disclosure summary="Add an example"><ui-stack><ui-combobox id="example-choice" data-add-example label="Choose a component example" placeholder="Search buttons, fields, dialogs…" disclosure clearable>${options}</ui-combobox><ui-button data-action="add" disabled>Add to preview</ui-button></ui-stack></ui-disclosure>
|
|
77
78
|
<ui-grid columns="2" min="sm"><section $each="card of $cards" $key="$card.id"><looma-example-card ${cardFields.filter(name => name !== 'id').map(name => `from:${name.replace(/[A-Z]/g, letter => '-' + letter.toLowerCase())}="$card.${name}"`).join(' ')} from:identity="$card.id" loading="eager" removable></looma-example-card></section></ui-grid>
|
|
78
79
|
<noscript>Enable JavaScript to edit the theme. The component reference and example previews remain available.</noscript>
|
|
@@ -80,7 +81,7 @@ export function customizerResource(site, input = defaultRecipe()) {
|
|
|
80
81
|
<style>
|
|
81
82
|
[data-customizer-overview][hidden], [data-refinement-view][hidden], [data-variation-confirm][hidden] { display: none !important; }
|
|
82
83
|
.customizer-layout { display: grid !important; grid-template-columns: minmax(16rem, 20rem) minmax(0, 1fr); gap: var(--ui-space-6) !important; align-items: start !important; }
|
|
83
|
-
aside { position: sticky; inset-block-start: 5rem; max-block-size: calc(100dvh - 6rem); overflow: auto; padding
|
|
84
|
+
aside { grid-row: 1 / span 2; position: sticky; inset-block-start: 5rem; max-block-size: calc(100dvh - 6rem); overflow: auto; padding: max(var(--ui-space-2), calc(var(--ui-focus-width) * 2)) !important; scroll-padding: max(var(--ui-space-2), calc(var(--ui-focus-width) * 2)); }
|
|
84
85
|
.examples { min-inline-size: 0; }
|
|
85
86
|
.refinement-layout { display: grid !important; grid-template-columns: minmax(0, 1.2fr) minmax(16rem, 1fr); gap: var(--ui-space-6) !important; align-items: start !important; }
|
|
86
87
|
[data-refinement-view] { scroll-margin-block-start: 5rem; }
|
|
@@ -94,6 +95,6 @@ export function customizerResource(site, input = defaultRecipe()) {
|
|
|
94
95
|
.palette-canvas { background: var(--ui-surface); }
|
|
95
96
|
.palette-content { background: var(--ui-surface-raised); }
|
|
96
97
|
.palette-action { background: var(--ui-accent); }
|
|
97
|
-
@media (max-width: 64rem) { .customizer-layout, .refinement-layout { grid-template-columns: minmax(0, 1fr); } aside { position: static; max-block-size: none; } }
|
|
98
|
+
@media (max-width: 64rem) { .customizer-layout, .refinement-layout { grid-template-columns: minmax(0, 1fr); } aside { grid-row: auto; order: 2; position: static; max-block-size: none; } [data-theme-sampler] { order: 1; } .examples { order: 3; } }
|
|
98
99
|
</style></template>`;
|
|
99
100
|
}
|
package/docs/customizer.js
CHANGED
|
@@ -22,8 +22,7 @@ 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.');
|
|
@@ -106,16 +105,15 @@ export function connect(host) {
|
|
|
106
105
|
changed = JSON.stringify(recipe) !== JSON.stringify(saved);
|
|
107
106
|
paint();
|
|
108
107
|
};
|
|
109
|
-
const
|
|
110
|
-
if (!
|
|
111
|
-
|
|
112
|
-
root.querySelector('[data-action="compare"]').setAttribute('aria-pressed', 'false');
|
|
108
|
+
const clearPreview = () => {
|
|
109
|
+
if (!candidate) return;
|
|
110
|
+
candidate = undefined; theme?.compare();
|
|
113
111
|
root.querySelector('[data-variation-confirm]').hidden = true;
|
|
114
112
|
};
|
|
115
113
|
const preview = next => {
|
|
116
114
|
const validated = validateRecipe(next);
|
|
117
115
|
if (JSON.stringify(validated.values) === JSON.stringify(recipe.values)) return;
|
|
118
|
-
|
|
116
|
+
clearPreview();
|
|
119
117
|
if (!pending) gestureStart = structuredClone(recipe);
|
|
120
118
|
recipe = validated; pending = true;
|
|
121
119
|
if (!previewFrame) previewFrame = view.requestAnimationFrame(() => {
|
|
@@ -189,17 +187,13 @@ export function connect(host) {
|
|
|
189
187
|
if (!button) { view.requestAnimationFrame(paint); return; }
|
|
190
188
|
if (button.dataset.action === 'save' && saving) return;
|
|
191
189
|
flush();
|
|
192
|
-
if (!['
|
|
190
|
+
if (!['preview-variation', 'apply-variation', 'cancel-variation'].includes(button.dataset.action)) clearPreview();
|
|
193
191
|
try {
|
|
194
192
|
switch (button.dataset.action) {
|
|
195
193
|
case 'undo': case 'redo':
|
|
196
194
|
theme?.[button.dataset.action](); show(theme?.recipe ?? recipe, false); sayApplied('Previous theme decision restored.'); break;
|
|
197
|
-
case 'compare':
|
|
198
|
-
candidate = undefined; comparing = !comparing; theme?.compare(comparing ? entryRecipe : undefined);
|
|
199
|
-
button.setAttribute('aria-pressed', String(comparing)); root.querySelector('[data-variation-confirm]').hidden = true;
|
|
200
|
-
say(comparing ? 'Comparing with the theme when you entered. Your edits remain intact. Choose Compare again to return.' : 'Back to your edited theme.'); break;
|
|
201
195
|
case 'preview-variation': {
|
|
202
|
-
|
|
196
|
+
clearPreview();
|
|
203
197
|
const locks = [...root.querySelectorAll('[data-family-lock]')].filter(control => control.querySelector('input')?.checked).map(control => control.dataset.familyLock);
|
|
204
198
|
candidate = nearbyVariation(recipe, root.querySelector('[data-variation-choice]').value, locks);
|
|
205
199
|
theme?.compare(candidate); root.querySelector('[data-variation-confirm]').hidden = false;
|
|
@@ -208,10 +202,10 @@ export function connect(host) {
|
|
|
208
202
|
break;
|
|
209
203
|
}
|
|
210
204
|
case 'apply-variation': {
|
|
211
|
-
const selected = candidate;
|
|
205
|
+
const selected = candidate; clearPreview();
|
|
212
206
|
if (selected) show(selected); sayApplied('Variation applied. Undo returns to your previous theme.'); break;
|
|
213
207
|
}
|
|
214
|
-
case 'cancel-variation':
|
|
208
|
+
case 'cancel-variation': clearPreview(); paint(); say('Variation cancelled; your theme is unchanged.'); break;
|
|
215
209
|
case 'light-top': case 'light-left': case 'light-center': {
|
|
216
210
|
const positions = { 'light-top': [.5, 0], 'light-left': [.15, .15], 'light-center': [.5, .5] };
|
|
217
211
|
const [x, y] = positions[button.dataset.action];
|
|
@@ -275,7 +269,7 @@ export function connect(host) {
|
|
|
275
269
|
if (event.target.matches('[data-light-editor]') || event.target.closest?.('[data-token]')) {
|
|
276
270
|
input(event); flush(); sayApplied('Theme remembered in this browser. Undo restores the previous decision.'); return;
|
|
277
271
|
}
|
|
278
|
-
flush();
|
|
272
|
+
flush(); clearPreview();
|
|
279
273
|
if (event.target.matches('[data-unit-for]')) {
|
|
280
274
|
const field = fields.get(event.target.dataset.unitFor);
|
|
281
275
|
try {
|
|
@@ -303,7 +297,7 @@ export function connect(host) {
|
|
|
303
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.'); } };
|
|
304
298
|
const keyboard = event => {
|
|
305
299
|
if (!(event.ctrlKey || event.metaKey) || event.key.toLowerCase() !== 'z' || event.target.closest?.('input,textarea,select,[contenteditable="true"]')) return;
|
|
306
|
-
event.preventDefault(); flush();
|
|
300
|
+
event.preventDefault(); flush(); clearPreview(); theme?.[event.shiftKey ? 'redo' : 'undo'](); show(theme?.recipe ?? recipe, false);
|
|
307
301
|
};
|
|
308
302
|
const ready = event => {
|
|
309
303
|
if (event.data?.type === 'looma-preview-ready' && [...root.querySelectorAll('iframe')].some(frame => frame.contentWindow === event.source)) paint();
|
|
@@ -312,7 +306,7 @@ export function connect(host) {
|
|
|
312
306
|
let fontRequest = 0;
|
|
313
307
|
const selectFont = async event => {
|
|
314
308
|
const control = event.target.closest?.('[data-font-picker]'); if (!control) return;
|
|
315
|
-
flush();
|
|
309
|
+
flush(); clearPreview();
|
|
316
310
|
const id = control.dataset.fontPicker, family = event.detail.value;
|
|
317
311
|
const fonts = { ...recipe.fonts };
|
|
318
312
|
if (family === 'system') delete fonts[id]; else fonts[id] = family;
|
|
@@ -358,5 +352,5 @@ export function connect(host) {
|
|
|
358
352
|
root.querySelector('[data-action="save"]').hidden = false;
|
|
359
353
|
}).catch(error => { if (alive && error.name !== 'AbortError') say(error.message, true); });
|
|
360
354
|
}
|
|
361
|
-
return () => { root.removeEventListener('toggle', updateSizes, true); 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); };
|
|
362
356
|
}
|
package/docs/theme.mjs
CHANGED
|
@@ -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.'],
|
|
@@ -187,7 +187,7 @@ const contrast = (a, b) => (Math.max(luminance(a), luminance(b)) + .05) / (Math.
|
|
|
187
187
|
// fill cannot wash out the label, even at maximum finish and light strength.
|
|
188
188
|
function reflection(fill, text) {
|
|
189
189
|
const labels = Array.isArray(text) ? text : [text];
|
|
190
|
-
let amount = .
|
|
190
|
+
let amount = .6;
|
|
191
191
|
while (amount > 0 && labels.some(label => contrast(mix('#ffffff', fill, amount), label) < 4.5)) amount = Math.max(0, amount - .005);
|
|
192
192
|
return mix('#ffffff', fill, amount);
|
|
193
193
|
}
|
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.37.
|
|
3
|
+
"version": "0.37.2",
|
|
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.7",
|
|
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-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);
|
package/styles/ui-popover.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
|
.surface {
|
|
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, 0.55) * 8px);
|
package/styles/ui-radio.css
CHANGED
|
@@ -110,12 +110,6 @@
|
|
|
110
110
|
padding-block: max(0px, calc((var(--_row) - max(var(--_line), var(--_ui-radio-size, 1.125rem))) / 2)) !important;
|
|
111
111
|
}
|
|
112
112
|
|
|
113
|
-
@media (pointer: coarse) {
|
|
114
|
-
:scope:where([data-component~="ui-radio"])[data-ui-radio-state~="size=sm"] {
|
|
115
|
-
--_row: var(--ui-control-min-block-size);
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
|
|
119
113
|
/* A component that sets its display still honours the hidden attribute on its root. */
|
|
120
114
|
:scope:where([data-component~="ui-radio"])[hidden] {
|
|
121
115
|
display: none !important;
|
|
@@ -130,8 +124,5 @@
|
|
|
130
124
|
and the first line centres in that control height, so the label shares a row's baseline.
|
|
131
125
|
Multi-line labels and descriptions still grow downward from that first line. */
|
|
132
126
|
|
|
133
|
-
@media (pointer: coarse) {
|
|
134
|
-
}
|
|
135
|
-
|
|
136
127
|
/* A component that sets its display still honours the hidden attribute on its root. */
|
|
137
128
|
}
|