@threadlabs/looma 0.39.2 → 0.39.3
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/ui-combobox/ui-combobox.html +8 -3
- package/components/ui-combobox/ui-combobox.js +3 -2
- package/components/ui-dialog/ui-dialog.html +6 -52
- package/components/ui-input/ui-input.html +21 -3
- package/components/ui-input-group/ui-input-group.html +1 -0
- package/components/ui-select/ui-select.html +10 -3
- package/dist/index.js +1 -1
- package/docs/README.md +1 -1
- package/docs/catalog.json +1 -25
- package/docs/site.mjs +9 -5
- package/docs/theme.mjs +8 -2
- package/package.json +1 -1
- package/styles/ui-combobox.css +16 -6
- package/styles/ui-dialog.css +6 -107
- package/styles/ui-input-group.css +1 -0
- package/styles/ui-input.css +24 -3
- package/styles/ui-select.css +10 -3
- package/theme-system.css +4 -1
- package/vanilla/UiDialog.js +2 -2
- package/vue/UiCombobox.vue +22 -6
- package/vue/UiDialog.d.ts +3 -3
- package/vue/UiDialog.vue +18 -111
- package/vue/UiInput.vue +30 -3
- package/vue/UiInputGroup.vue +1 -0
- package/vue/UiSelect.vue +16 -3
- package/vue/chunks/UiCombobox.js +1 -1
- package/vue/chunks/UiDialog.js +22 -14
- package/vue/chunks/UiInput.js +1 -1
- package/vue/chunks/UiInputGroup.js +1 -1
- package/vue/chunks/UiSelect.js +1 -1
- package/vue/components.css +105 -118
package/docs/README.md
CHANGED
|
@@ -256,7 +256,7 @@ Use the `@threadlabs/looma/docs` entrypoint for programmatic discovery, catalog
|
|
|
256
256
|
|
|
257
257
|
## Saving and recovery
|
|
258
258
|
|
|
259
|
-
The local editor writes only the configured recipe and CSS inside the project. It rejects symlink paths, generated or dependency directories, cross-origin save requests and stale file revisions. A competing edit leaves your unsaved preview in place; reload and reconcile it before saving again. Examples run in isolated frames and receive theme CSS, never the local editing capability.
|
|
259
|
+
The local editor writes only the configured recipe and CSS inside the project. It rejects symlink paths, generated or dependency directories, cross-origin save requests and stale file revisions. A competing edit leaves your unsaved preview in place; reload and reconcile it before saving again. Examples run in isolated frames and receive theme CSS, never the local editing capability. Each preview's compiled runtime is embedded after its content and authorized by an exact content hash in the generated content policy. This keeps previews interactive in Safari while retaining their opaque origin and parent-page isolation.
|
|
260
260
|
|
|
261
261
|
The writer stages complete files, serializes saves with a project lock, and rolls back the recipe if replacing the CSS fails. If a process stops during a save or build, close the old editor before removing its `.looma/theme-write.lock` or `.looma/docs-build.lock`; restart and save the loaded recipe to regenerate a consistent pair. Change the server base, output directory or theme-file paths by restarting `dev`.
|
|
262
262
|
|
package/docs/catalog.json
CHANGED
|
@@ -458,7 +458,7 @@
|
|
|
458
458
|
],
|
|
459
459
|
"schemaVersion": 1,
|
|
460
460
|
"package": "@threadlabs/looma",
|
|
461
|
-
"version": "0.39.
|
|
461
|
+
"version": "0.39.3",
|
|
462
462
|
"groups": [
|
|
463
463
|
{
|
|
464
464
|
"id": "layout",
|
|
@@ -7430,24 +7430,12 @@
|
|
|
7430
7430
|
{
|
|
7431
7431
|
"name": "--ui-border-width"
|
|
7432
7432
|
},
|
|
7433
|
-
{
|
|
7434
|
-
"name": "--ui-control-min-block-size"
|
|
7435
|
-
},
|
|
7436
|
-
{
|
|
7437
|
-
"name": "--ui-control-size-sm"
|
|
7438
|
-
},
|
|
7439
7433
|
{
|
|
7440
7434
|
"name": "--ui-elevation-dialog",
|
|
7441
7435
|
"fallbacks": [
|
|
7442
7436
|
"var(--ui-shadow-xl)"
|
|
7443
7437
|
]
|
|
7444
7438
|
},
|
|
7445
|
-
{
|
|
7446
|
-
"name": "--ui-focus-ring"
|
|
7447
|
-
},
|
|
7448
|
-
{
|
|
7449
|
-
"name": "--ui-focus-width"
|
|
7450
|
-
},
|
|
7451
7439
|
{
|
|
7452
7440
|
"name": "--ui-font-semibold"
|
|
7453
7441
|
},
|
|
@@ -7493,9 +7481,6 @@
|
|
|
7493
7481
|
{
|
|
7494
7482
|
"name": "--ui-motion-ease"
|
|
7495
7483
|
},
|
|
7496
|
-
{
|
|
7497
|
-
"name": "--ui-motion-fast"
|
|
7498
|
-
},
|
|
7499
7484
|
{
|
|
7500
7485
|
"name": "--ui-overlay-backdrop"
|
|
7501
7486
|
},
|
|
@@ -7527,9 +7512,6 @@
|
|
|
7527
7512
|
{
|
|
7528
7513
|
"name": "--ui-radius-dialog"
|
|
7529
7514
|
},
|
|
7530
|
-
{
|
|
7531
|
-
"name": "--ui-radius-round"
|
|
7532
|
-
},
|
|
7533
7515
|
{
|
|
7534
7516
|
"name": "--ui-shadow-xl"
|
|
7535
7517
|
},
|
|
@@ -7557,9 +7539,6 @@
|
|
|
7557
7539
|
"linear-gradient(transparent, transparent)"
|
|
7558
7540
|
]
|
|
7559
7541
|
},
|
|
7560
|
-
{
|
|
7561
|
-
"name": "--ui-surface-muted"
|
|
7562
|
-
},
|
|
7563
7542
|
{
|
|
7564
7543
|
"name": "--ui-surface-separation",
|
|
7565
7544
|
"fallbacks": [
|
|
@@ -7568,9 +7547,6 @@
|
|
|
7568
7547
|
},
|
|
7569
7548
|
{
|
|
7570
7549
|
"name": "--ui-text-primary"
|
|
7571
|
-
},
|
|
7572
|
-
{
|
|
7573
|
-
"name": "--ui-text-secondary"
|
|
7574
7550
|
}
|
|
7575
7551
|
]
|
|
7576
7552
|
},
|
package/docs/site.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { cp, mkdir, readFile, readdir, realpath, rename, rm, symlink, unlink, writeFile } from 'node:fs/promises';
|
|
2
2
|
import path from 'node:path';
|
|
3
|
-
import { randomUUID } from 'node:crypto';
|
|
3
|
+
import { createHash, randomUUID } from 'node:crypto';
|
|
4
4
|
import { createRequire } from 'node:module';
|
|
5
5
|
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
6
6
|
import { discoverComponents, escapeHtml, mergeCatalogs, readCatalog, readJson } from './catalog.mjs';
|
|
@@ -220,7 +220,7 @@ export async function buildDocumentation({ root = process.cwd(), config = {}, ca
|
|
|
220
220
|
});
|
|
221
221
|
}
|
|
222
222
|
|
|
223
|
-
async function isolatePreviewScripts(site, { outDir
|
|
223
|
+
async function isolatePreviewScripts(site, { outDir }) {
|
|
224
224
|
const { build } = await tool('vite', site.root);
|
|
225
225
|
// Preview entries and outputs are independent. Keep compiler memory bounded
|
|
226
226
|
// and retain the build lock until every in-flight bundle has settled.
|
|
@@ -236,9 +236,13 @@ async function isolatePreviewScripts(site, { outDir, writeFile: writeOutput }) {
|
|
|
236
236
|
const input = path.join(outDir, url.slice(site.base.length));
|
|
237
237
|
const bundle = await build({ configFile: false, logLevel: 'silent', build: { write: false, minify: true, lib: { entry: input, name: 'LoomaPreview', formats: ['iife'] }, rollupOptions: { output: { codeSplitting: false } } } });
|
|
238
238
|
const code = (Array.isArray(bundle) ? bundle[0] : bundle).output.find(output => output.type === 'chunk').code;
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
239
|
+
// A sandbox has an opaque origin: Safari cannot authorize this runtime via 'self'.
|
|
240
|
+
// Hash the exact inline bytes and run after the preview DOM, preserving isolation.
|
|
241
|
+
const script = code.replace(/<\/script/gi, '<\\/script');
|
|
242
|
+
const hash = createHash('sha256').update(script).digest('base64');
|
|
243
|
+
if (!/<meta\b[^>]*http-equiv="Content-Security-Policy"[^>]*content="[^"]*\bscript-src\b/.test(html)) throw new Error(`Missing script policy for isolated preview ${example.id}.`);
|
|
244
|
+
html = html.replace(/(<meta\b[^>]*http-equiv="Content-Security-Policy"[^>]*content="[^"]*\bscript-src)\b/, `$1 'sha256-${hash}'`);
|
|
245
|
+
html = html.replace(tag, '').replace('</body>', () => `<script>${script}</script></body>`);
|
|
242
246
|
}
|
|
243
247
|
html = html.replace(/<link\b[^>]*rel="modulepreload"[^>]*>/g, '');
|
|
244
248
|
await writeFile(htmlFile, html);
|
package/docs/theme.mjs
CHANGED
|
@@ -273,15 +273,21 @@ export function themeCSS(input, systemCSS = '', { scope = 'portable' } = {}) {
|
|
|
273
273
|
const selector = scope === 'documentation'
|
|
274
274
|
? ':root[data-looma-documentation-theme], :root[data-looma-documentation-theme] [data-theme], :root[data-looma-documentation-theme] [data-contrast]'
|
|
275
275
|
: ':root, [data-theme], [data-contrast]';
|
|
276
|
-
const
|
|
276
|
+
const recipe = validateRecipe(input);
|
|
277
|
+
const fontURL = googleFontURL(recipe.fonts);
|
|
277
278
|
const imports = fontURL ? `@import url("${fontURL}");\n` : '';
|
|
278
279
|
const light = deriveTheme(input, { mode: 'light' }).tokens;
|
|
279
280
|
const dark = deriveTheme(input, { mode: 'dark' }).tokens;
|
|
280
281
|
const palette = [], geometry = [];
|
|
282
|
+
const relativeLengths = new Map(themeFields.filter(field => field.property && field.type.includes('length') && recipe.values[field.id].endsWith('rem')).map(field => [field.property, recipe.values[field.id]]));
|
|
281
283
|
for (const [name, value] of Object.entries(light)) {
|
|
282
284
|
if (name === 'color-scheme') continue;
|
|
283
285
|
if (/^#[\da-f]{6}$/i.test(value)) palette.push(` ${name}: light-dark(${value}, ${dark[name]});`);
|
|
284
|
-
else
|
|
286
|
+
else {
|
|
287
|
+
const relative = relativeLengths.get(name);
|
|
288
|
+
const resolved = relative ? value.replace(relative, `calc(${parseFloat(relative)} * var(--_ui-root-font-unit, 1rem))`) : value;
|
|
289
|
+
geometry.push(` ${name}: ${resolved};`);
|
|
290
|
+
}
|
|
285
291
|
}
|
|
286
292
|
const geometrySelector = scope === 'documentation' ? ':root[data-looma-documentation-theme]' : ':root';
|
|
287
293
|
const chevron = color => `url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='${color.replace('#', '%23')}' stroke-width='2.25' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E")`;
|
package/package.json
CHANGED
package/styles/ui-combobox.css
CHANGED
|
@@ -198,10 +198,12 @@
|
|
|
198
198
|
color: inherit;
|
|
199
199
|
}
|
|
200
200
|
input {
|
|
201
|
+
--_ui-combobox-inset-block: var(--ui-control-inset-block-md, var(--ui-space-2));
|
|
201
202
|
appearance: none;
|
|
202
203
|
width: 100%;
|
|
203
204
|
min-inline-size: 0;
|
|
204
205
|
min-block-size: calc(var(--_row) - 2 * var(--ui-border-width));
|
|
206
|
+
max-block-size: max(calc(var(--_row) - 2 * var(--ui-border-width)), calc(1em * var(--ui-line-height-tight) + 2 * var(--_ui-combobox-inset-block)));
|
|
205
207
|
box-sizing: border-box !important;
|
|
206
208
|
border: 0;
|
|
207
209
|
border-radius: inherit;
|
|
@@ -209,7 +211,7 @@
|
|
|
209
211
|
color: inherit;
|
|
210
212
|
font: inherit;
|
|
211
213
|
line-height: var(--ui-line-height-tight);
|
|
212
|
-
padding: var(--
|
|
214
|
+
padding: var(--_ui-combobox-inset-block) var(--ui-space-3) !important;
|
|
213
215
|
margin: 0 !important;
|
|
214
216
|
outline: none;
|
|
215
217
|
}
|
|
@@ -217,14 +219,16 @@
|
|
|
217
219
|
--_row: var(--ui-control-size-sm);
|
|
218
220
|
}
|
|
219
221
|
:scope:where([data-component~="ui-combobox"])[data-ui-combobox-state~="size=sm"] input {
|
|
220
|
-
|
|
222
|
+
--_ui-combobox-inset-block: var(--ui-control-inset-block-sm, var(--ui-space-1));
|
|
223
|
+
padding: var(--_ui-combobox-inset-block) var(--ui-space-2) !important;
|
|
221
224
|
font-size: var(--ui-font-size-sm);
|
|
222
225
|
}
|
|
223
226
|
:scope:where([data-component~="ui-combobox"])[data-ui-combobox-state~="size=lg"] {
|
|
224
227
|
--_row: var(--ui-control-size-lg);
|
|
225
228
|
}
|
|
226
229
|
:scope:where([data-component~="ui-combobox"])[data-ui-combobox-state~="size=lg"] input {
|
|
227
|
-
|
|
230
|
+
--_ui-combobox-inset-block: var(--ui-control-inset-block-lg, var(--ui-space-3));
|
|
231
|
+
padding: var(--_ui-combobox-inset-block) var(--ui-space-4) !important;
|
|
228
232
|
font-size: var(--ui-font-size-md);
|
|
229
233
|
}
|
|
230
234
|
button {
|
|
@@ -359,6 +363,7 @@
|
|
|
359
363
|
display: none !important;
|
|
360
364
|
}
|
|
361
365
|
:scope:where([data-component~="ui-combobox"])[data-ui-combobox-state~="multiple"] input {
|
|
366
|
+
max-block-size: none;
|
|
362
367
|
flex: 1 0 var(--_ui-combobox-item-input-min-inline-size, 5rem);
|
|
363
368
|
width: auto;
|
|
364
369
|
min-block-size: 1.75rem;
|
|
@@ -617,10 +622,12 @@
|
|
|
617
622
|
color: inherit;
|
|
618
623
|
}
|
|
619
624
|
input:where(:scope, :scope *) {
|
|
625
|
+
--_ui-combobox-inset-block: var(--ui-control-inset-block-md, var(--ui-space-2));
|
|
620
626
|
appearance: none;
|
|
621
627
|
width: 100%;
|
|
622
628
|
min-inline-size: 0;
|
|
623
629
|
min-block-size: calc(var(--_row) - 2 * var(--ui-border-width));
|
|
630
|
+
max-block-size: max(calc(var(--_row) - 2 * var(--ui-border-width)), calc(1em * var(--ui-line-height-tight) + 2 * var(--_ui-combobox-inset-block)));
|
|
624
631
|
box-sizing: border-box !important;
|
|
625
632
|
border: 0;
|
|
626
633
|
border-radius: inherit;
|
|
@@ -628,7 +635,7 @@
|
|
|
628
635
|
color: inherit;
|
|
629
636
|
font: inherit;
|
|
630
637
|
line-height: var(--ui-line-height-tight);
|
|
631
|
-
padding: var(--
|
|
638
|
+
padding: var(--_ui-combobox-inset-block) var(--ui-space-3) !important;
|
|
632
639
|
margin: 0 !important;
|
|
633
640
|
outline: none;
|
|
634
641
|
}
|
|
@@ -636,14 +643,16 @@
|
|
|
636
643
|
--_row: var(--ui-control-size-sm);
|
|
637
644
|
}
|
|
638
645
|
:where([data-component~="ui-combobox"])[data-ui-combobox-state~="size=sm"] input:where(:scope, :not([data-component], [data-slotted]:not([data-slotted~="ui-combobox"]))) {
|
|
639
|
-
|
|
646
|
+
--_ui-combobox-inset-block: var(--ui-control-inset-block-sm, var(--ui-space-1));
|
|
647
|
+
padding: var(--_ui-combobox-inset-block) var(--ui-space-2) !important;
|
|
640
648
|
font-size: var(--ui-font-size-sm);
|
|
641
649
|
}
|
|
642
650
|
:where([data-component~="ui-combobox"])[data-ui-combobox-state~="size=lg"]:where(:scope, :not([data-component], [data-slotted]:not([data-slotted~="ui-combobox"]))) {
|
|
643
651
|
--_row: var(--ui-control-size-lg);
|
|
644
652
|
}
|
|
645
653
|
:where([data-component~="ui-combobox"])[data-ui-combobox-state~="size=lg"] input:where(:scope, :not([data-component], [data-slotted]:not([data-slotted~="ui-combobox"]))) {
|
|
646
|
-
|
|
654
|
+
--_ui-combobox-inset-block: var(--ui-control-inset-block-lg, var(--ui-space-3));
|
|
655
|
+
padding: var(--_ui-combobox-inset-block) var(--ui-space-4) !important;
|
|
647
656
|
font-size: var(--ui-font-size-md);
|
|
648
657
|
}
|
|
649
658
|
button:where(:scope, :scope *) {
|
|
@@ -776,6 +785,7 @@
|
|
|
776
785
|
display: none !important;
|
|
777
786
|
}
|
|
778
787
|
:where([data-component~="ui-combobox"])[data-ui-combobox-state~="multiple"] input:where(:scope, :not([data-component], [data-slotted]:not([data-slotted~="ui-combobox"]))) {
|
|
788
|
+
max-block-size: none;
|
|
779
789
|
flex: 1 0 var(--_ui-combobox-item-input-min-inline-size, 5rem);
|
|
780
790
|
width: auto;
|
|
781
791
|
min-block-size: 1.75rem;
|
package/styles/ui-dialog.css
CHANGED
|
@@ -193,58 +193,10 @@
|
|
|
193
193
|
}
|
|
194
194
|
|
|
195
195
|
.close {
|
|
196
|
-
display:
|
|
197
|
-
|
|
198
|
-
flex: none;
|
|
196
|
+
display: flex !important;
|
|
197
|
+
align-items: center !important;
|
|
198
|
+
flex: none !important;
|
|
199
199
|
margin-inline-start: auto !important;
|
|
200
|
-
inline-size: var(--ui-control-size-sm);
|
|
201
|
-
block-size: var(--ui-control-size-sm);
|
|
202
|
-
padding: 0 !important;
|
|
203
|
-
border: 0;
|
|
204
|
-
border-radius: var(--ui-radius-round);
|
|
205
|
-
background: transparent;
|
|
206
|
-
color: var(--ui-text-secondary);
|
|
207
|
-
cursor: pointer;
|
|
208
|
-
transition: background-color var(--ui-motion-fast) var(--ui-motion-ease), color var(--ui-motion-fast) var(--ui-motion-ease);
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
/* A CSS-drawn X. */
|
|
212
|
-
.close:where(:not([data-component], [data-slotted]))::before,
|
|
213
|
-
.close:where(:not([data-component], [data-slotted]))::after {
|
|
214
|
-
content: "";
|
|
215
|
-
grid-area: 1 / 1;
|
|
216
|
-
inline-size: 0.875rem;
|
|
217
|
-
block-size: 1.5px;
|
|
218
|
-
border-radius: 1px;
|
|
219
|
-
background: currentColor;
|
|
220
|
-
transform: rotate(45deg);
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
.close:where(:not([data-component], [data-slotted]))::after {
|
|
224
|
-
transform: rotate(-45deg);
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
.close:hover {
|
|
228
|
-
background: var(--ui-surface-muted);
|
|
229
|
-
color: var(--ui-text-primary);
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
.close:focus-visible {
|
|
233
|
-
outline: var(--ui-focus-width) solid var(--ui-focus-ring);
|
|
234
|
-
outline-offset: var(--ui-focus-width);
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
/* A touch target, like Button on coarse pointers. */
|
|
238
|
-
@media (pointer: coarse) {
|
|
239
|
-
.close {
|
|
240
|
-
inline-size: var(--ui-control-min-block-size);
|
|
241
|
-
block-size: var(--ui-control-min-block-size);
|
|
242
|
-
}
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
html[data-ui-input-modality="touch"] :scope:where([data-component~="ui-dialog"]) .close {
|
|
246
|
-
inline-size: var(--ui-control-min-block-size);
|
|
247
|
-
block-size: var(--ui-control-min-block-size);
|
|
248
200
|
}
|
|
249
201
|
|
|
250
202
|
.body {
|
|
@@ -320,12 +272,6 @@
|
|
|
320
272
|
/* Alerts have no close target to add height above the title. */
|
|
321
273
|
|
|
322
274
|
/* :host-qualified so page heading rules that reach in cannot outrank the dialog chrome. */
|
|
323
|
-
|
|
324
|
-
/* A CSS-drawn X. */
|
|
325
|
-
|
|
326
|
-
/* A touch target, like Button on coarse pointers. */
|
|
327
|
-
@media (pointer: coarse) {
|
|
328
|
-
}
|
|
329
275
|
}
|
|
330
276
|
@scope ([data-slotted~="ui-dialog"]:not([data-component])) to ([data-component], [data-slotted]) {
|
|
331
277
|
/* This component's hooks style the element they are set on, and never a nested one. */
|
|
@@ -514,57 +460,10 @@
|
|
|
514
460
|
}
|
|
515
461
|
|
|
516
462
|
.close:where(:scope, :scope *) {
|
|
517
|
-
display:
|
|
518
|
-
|
|
519
|
-
flex: none;
|
|
463
|
+
display: flex !important;
|
|
464
|
+
align-items: center !important;
|
|
465
|
+
flex: none !important;
|
|
520
466
|
margin-inline-start: auto !important;
|
|
521
|
-
inline-size: var(--ui-control-size-sm);
|
|
522
|
-
block-size: var(--ui-control-size-sm);
|
|
523
|
-
padding: 0 !important;
|
|
524
|
-
border: 0;
|
|
525
|
-
border-radius: var(--ui-radius-round);
|
|
526
|
-
background: transparent;
|
|
527
|
-
color: var(--ui-text-secondary);
|
|
528
|
-
cursor: pointer;
|
|
529
|
-
transition: background-color var(--ui-motion-fast) var(--ui-motion-ease), color var(--ui-motion-fast) var(--ui-motion-ease);
|
|
530
|
-
}
|
|
531
|
-
|
|
532
|
-
/* A CSS-drawn X. */
|
|
533
|
-
.close:where(:not([data-component], [data-slotted]:not([data-slotted~="ui-dialog"]))):where(:scope, :scope *)::before, .close:where(:not([data-component], [data-slotted]:not([data-slotted~="ui-dialog"]))):where(:scope, :scope *)::after {
|
|
534
|
-
content: "";
|
|
535
|
-
grid-area: 1 / 1;
|
|
536
|
-
inline-size: 0.875rem;
|
|
537
|
-
block-size: 1.5px;
|
|
538
|
-
border-radius: 1px;
|
|
539
|
-
background: currentColor;
|
|
540
|
-
transform: rotate(45deg);
|
|
541
|
-
}
|
|
542
|
-
|
|
543
|
-
.close:where(:not([data-component], [data-slotted]:not([data-slotted~="ui-dialog"]))):where(:scope, :scope *)::after {
|
|
544
|
-
transform: rotate(-45deg);
|
|
545
|
-
}
|
|
546
|
-
|
|
547
|
-
.close:hover:where(:scope, :scope *) {
|
|
548
|
-
background: var(--ui-surface-muted);
|
|
549
|
-
color: var(--ui-text-primary);
|
|
550
|
-
}
|
|
551
|
-
|
|
552
|
-
.close:focus-visible:where(:scope, :scope *) {
|
|
553
|
-
outline: var(--ui-focus-width) solid var(--ui-focus-ring);
|
|
554
|
-
outline-offset: var(--ui-focus-width);
|
|
555
|
-
}
|
|
556
|
-
|
|
557
|
-
/* A touch target, like Button on coarse pointers. */
|
|
558
|
-
@media (pointer: coarse) {
|
|
559
|
-
.close:where(:scope, :scope *) {
|
|
560
|
-
inline-size: var(--ui-control-min-block-size);
|
|
561
|
-
block-size: var(--ui-control-min-block-size);
|
|
562
|
-
}
|
|
563
|
-
}
|
|
564
|
-
|
|
565
|
-
html[data-ui-input-modality="touch"] :where([data-component~="ui-dialog"]) .close:where(:scope, :not([data-component], [data-slotted]:not([data-slotted~="ui-dialog"]))) {
|
|
566
|
-
inline-size: var(--ui-control-min-block-size);
|
|
567
|
-
block-size: var(--ui-control-min-block-size);
|
|
568
467
|
}
|
|
569
468
|
|
|
570
469
|
.body:where(:scope, :scope *) {
|
|
@@ -32,6 +32,7 @@
|
|
|
32
32
|
Hooks do not inherit, so these are defaults for the input; its own hooks still win. */
|
|
33
33
|
--_ui-default-input-border-width: 0;
|
|
34
34
|
--_ui-default-input-min-block-size: 0;
|
|
35
|
+
--_ui-default-input-frame-width: calc(2 * var(--ui-border-width));
|
|
35
36
|
--_ui-default-input-border: transparent;
|
|
36
37
|
--_ui-default-input-border-hover: transparent;
|
|
37
38
|
--_ui-default-input-focus-border: transparent;
|
package/styles/ui-input.css
CHANGED
|
@@ -61,15 +61,24 @@
|
|
|
61
61
|
syntax: "*";
|
|
62
62
|
inherits: false;
|
|
63
63
|
}
|
|
64
|
+
/* Normalize a unitless zero border to a length before the box calculation. */
|
|
65
|
+
@property --_ui-input-stroke {
|
|
66
|
+
syntax: "<length>";
|
|
67
|
+
inherits: false;
|
|
68
|
+
initial-value: 1px;
|
|
69
|
+
}
|
|
64
70
|
|
|
65
71
|
:scope:where([data-component~="ui-input"]) {
|
|
72
|
+
--_ui-input-size: var(--ui-control-size-md);
|
|
73
|
+
--_ui-input-inset-block: var(--ui-control-inset-block-md, var(--ui-space-2));
|
|
74
|
+
--_ui-input-stroke: var(--ui-input-border-width, var(--_ui-default-input-border-width, var(--ui-border-width)));
|
|
66
75
|
box-sizing: border-box !important;
|
|
67
76
|
display: inline-block !important;
|
|
68
77
|
inline-size: var(--ui-input-inline-size, 100%);
|
|
69
78
|
min-inline-size: 0;
|
|
70
79
|
min-block-size: var(--ui-input-min-block-size, var(--_ui-default-input-min-block-size, var(--ui-control-size-md)));
|
|
71
80
|
margin: 0 !important;
|
|
72
|
-
padding: var(--
|
|
81
|
+
padding: var(--_ui-input-inset-block) var(--ui-space-3) !important;
|
|
73
82
|
appearance: none;
|
|
74
83
|
padding-inline-start: var(--_ui-default-input-padding-inline-start, var(--ui-space-3)) !important;
|
|
75
84
|
border: var(--ui-input-border-width, var(--_ui-default-input-border-width, var(--ui-border-width))) solid
|
|
@@ -87,16 +96,25 @@
|
|
|
87
96
|
background-color var(--ui-motion-fast) var(--ui-motion-ease);
|
|
88
97
|
}
|
|
89
98
|
|
|
99
|
+
/* Cap Safari's excess text metrics without changing native date, file, color or range widgets. */
|
|
100
|
+
:scope:where([data-component~="ui-input"]):is([type="text"], [type="search"], [type="tel"], [type="url"], [type="email"], [type="password"], [type="number"]) {
|
|
101
|
+
max-block-size: max(calc(var(--_ui-input-size) - var(--_ui-default-input-frame-width, 0px)), calc(1em * var(--ui-line-height-tight) + 2 * var(--_ui-input-inset-block) + 2 * var(--_ui-input-stroke)));
|
|
102
|
+
}
|
|
103
|
+
|
|
90
104
|
/* Each size reads the hook and then an Input Group's default first, so a group, which draws the frame, can zero it. */
|
|
91
105
|
:scope:where([data-component~="ui-input"])[data-ui-input-state~="size=sm"] {
|
|
106
|
+
--_ui-input-size: var(--ui-control-size-sm);
|
|
107
|
+
--_ui-input-inset-block: var(--ui-control-inset-block-sm, var(--ui-space-1));
|
|
92
108
|
min-block-size: var(--ui-input-min-block-size, var(--_ui-default-input-min-block-size, var(--ui-control-size-sm)));
|
|
93
|
-
padding: var(--
|
|
109
|
+
padding: var(--_ui-input-inset-block) var(--ui-space-2) !important;
|
|
94
110
|
font-size: var(--ui-font-size-sm);
|
|
95
111
|
}
|
|
96
112
|
|
|
97
113
|
:scope:where([data-component~="ui-input"])[data-ui-input-state~="size=lg"] {
|
|
114
|
+
--_ui-input-size: var(--ui-control-size-lg);
|
|
115
|
+
--_ui-input-inset-block: var(--ui-control-inset-block-lg, var(--ui-space-3));
|
|
98
116
|
min-block-size: var(--ui-input-min-block-size, var(--_ui-default-input-min-block-size, var(--ui-control-size-lg)));
|
|
99
|
-
padding: var(--
|
|
117
|
+
padding: var(--_ui-input-inset-block) var(--ui-space-4) !important;
|
|
100
118
|
font-size: var(--ui-font-size-md);
|
|
101
119
|
}
|
|
102
120
|
|
|
@@ -177,6 +195,9 @@
|
|
|
177
195
|
@scope ([data-component~="ui-input"]) to ([data-component]) {
|
|
178
196
|
/* This component's hooks style the element they are set on, and never a nested one. An Input
|
|
179
197
|
Group sets --_ui-default-input-* for the input inside it, below the input's own hooks. */
|
|
198
|
+
/* Normalize a unitless zero border to a length before the box calculation. */
|
|
199
|
+
|
|
200
|
+
/* Cap Safari's excess text metrics without changing native date, file, color or range widgets. */
|
|
180
201
|
|
|
181
202
|
/* Each size reads the hook and then an Input Group's default first, so a group, which draws the frame, can zero it. */
|
|
182
203
|
|
package/styles/ui-select.css
CHANGED
|
@@ -50,13 +50,16 @@
|
|
|
50
50
|
}
|
|
51
51
|
|
|
52
52
|
:scope:where([data-component~="ui-select"]) {
|
|
53
|
+
--_ui-select-size: var(--ui-control-size-md);
|
|
54
|
+
--_ui-select-inset-block: var(--ui-control-inset-block-md, var(--ui-space-2));
|
|
53
55
|
box-sizing: border-box !important;
|
|
54
56
|
display: inline-block !important;
|
|
55
57
|
inline-size: var(--ui-select-inline-size, 100%);
|
|
56
58
|
min-inline-size: 0;
|
|
59
|
+
max-block-size: max(var(--_ui-select-size), calc(1em * var(--ui-line-height-tight) + 2 * var(--_ui-select-inset-block) + 2 * var(--ui-border-width)));
|
|
57
60
|
min-block-size: var(--ui-control-size-md);
|
|
58
61
|
margin: 0 !important;
|
|
59
|
-
padding: var(--
|
|
62
|
+
padding: var(--_ui-select-inset-block) var(--ui-space-3) !important;
|
|
60
63
|
border: var(--ui-border-width) solid var(--ui-select-border, var(--ui-control-border, var(--ui-border-strong)));
|
|
61
64
|
border-radius: var(--ui-select-radius, var(--ui-field-radius, var(--ui-radius-md)));
|
|
62
65
|
background-color: var(--ui-select-surface, var(--ui-control-surface, var(--ui-surface-elevated)));
|
|
@@ -129,8 +132,10 @@
|
|
|
129
132
|
}
|
|
130
133
|
|
|
131
134
|
:scope:where([data-component~="ui-select"])[data-ui-select-state~="size=sm"] {
|
|
135
|
+
--_ui-select-size: var(--ui-control-size-sm);
|
|
136
|
+
--_ui-select-inset-block: var(--ui-control-inset-block-sm, var(--ui-space-1));
|
|
132
137
|
min-block-size: var(--ui-control-size-sm);
|
|
133
|
-
padding: var(--
|
|
138
|
+
padding: var(--_ui-select-inset-block) var(--ui-space-2) !important;
|
|
134
139
|
padding-inline-end: calc(var(--ui-space-2) + var(--ui-icon-size-sm) + var(--ui-space-1)) !important;
|
|
135
140
|
background-position: right var(--ui-space-2) center, center;
|
|
136
141
|
background-size:
|
|
@@ -140,8 +145,10 @@
|
|
|
140
145
|
}
|
|
141
146
|
|
|
142
147
|
:scope:where([data-component~="ui-select"])[data-ui-select-state~="size=lg"] {
|
|
148
|
+
--_ui-select-size: var(--ui-control-size-lg);
|
|
149
|
+
--_ui-select-inset-block: var(--ui-control-inset-block-lg, var(--ui-space-3));
|
|
143
150
|
min-block-size: var(--ui-control-size-lg);
|
|
144
|
-
padding: var(--
|
|
151
|
+
padding: var(--_ui-select-inset-block) var(--ui-space-4) !important;
|
|
145
152
|
padding-inline-end: calc(var(--ui-space-4) + 1.5rem) !important;
|
|
146
153
|
background-position: right var(--ui-space-4) center, center;
|
|
147
154
|
font-size: var(--ui-font-size-md);
|
package/theme-system.css
CHANGED
|
@@ -8,11 +8,14 @@
|
|
|
8
8
|
/* Keep the root font seed unregistered: a registered rem length used by root
|
|
9
9
|
font-size creates a dependency cycle (CSS Properties and Values API 2.7.2). */
|
|
10
10
|
@property --ui-control-size { syntax: "<length>"; inherits: true; initial-value: 40px; }
|
|
11
|
+
@property --_ui-root-font-unit { syntax: "<length>"; inherits: true; initial-value: 16px; }
|
|
11
12
|
@property --ui-radius-md { syntax: "<length>"; inherits: true; initial-value: 8px; }
|
|
12
13
|
@property --ui-border-width { syntax: "<length>"; inherits: true; initial-value: 1px; }
|
|
13
14
|
|
|
14
15
|
@layer theme {
|
|
15
16
|
:root {
|
|
17
|
+
/* Resolve once on the root: WebKit can resolve registered rem lengths against the initial font size. */
|
|
18
|
+
--_ui-root-font-unit: 1em;
|
|
16
19
|
--ui-spacing: 1;
|
|
17
20
|
--ui-rhythm: 1;
|
|
18
21
|
--ui-control-roominess: 2.5;
|
|
@@ -35,7 +38,7 @@
|
|
|
35
38
|
:where(:root, [data-theme], [data-contrast]) {
|
|
36
39
|
/* Controls use a ratio. Spacing uses an independent curve, so increasing the size
|
|
37
40
|
contrast cannot unexpectedly turn linear spacing into quadratic spacing. */
|
|
38
|
-
--ui-control-size: calc(
|
|
41
|
+
--ui-control-size: calc(var(--_ui-root-font-unit) * var(--ui-control-roominess));
|
|
39
42
|
--_ui-theme-ratio: max(1, var(--ui-scale-ratio));
|
|
40
43
|
--_ui-theme-unit: calc(1rem * 0.25 * max(0, var(--ui-spacing)) * clamp(0.75, var(--ui-rhythm), 1.5));
|
|
41
44
|
--ui-line-height-tight: clamp(1.1, calc(1.2 + (var(--ui-rhythm) - 1) * 0.2), 1.3);
|
package/vanilla/UiDialog.js
CHANGED
|
@@ -3,7 +3,7 @@ import { attachGeneratedController, buildTemplate, clearRegion, toAttribute, toT
|
|
|
3
3
|
import * as controller from "../components/ui-dialog/ui-dialog.js";
|
|
4
4
|
import "../styles/ui-dialog.css";
|
|
5
5
|
|
|
6
|
-
const T0 = ["",[],"\n ",["header",[],"\n ",1,"\n ",["
|
|
6
|
+
const T0 = ["",[],"\n ",["header",[],"\n ",1,"\n ",["div",["class","close"],["ui-icon-button",["size","sm","round","","label","Close"],["ui-icon",["name","x"]]]],"\n "],"\n ",["div",["class","body"],["ui-scroll-area",[],4]],"\n ",["footer",[],4],"\n "];
|
|
7
7
|
const T1 = ["h2",["class","title"],0];
|
|
8
8
|
const T2 = ["",[]];
|
|
9
9
|
const T3 = ["",[]];
|
|
@@ -82,7 +82,7 @@ export function createUiDialog(options = {}, html) {
|
|
|
82
82
|
};
|
|
83
83
|
const t0 = element.firstChild.nextSibling;
|
|
84
84
|
const t1 = t0.firstChild.nextSibling;
|
|
85
|
-
const t2 = t1.nextSibling.nextSibling.nextSibling;
|
|
85
|
+
const t2 = t1.nextSibling.nextSibling.nextSibling.firstChild;
|
|
86
86
|
const t3 = t0.nextSibling.nextSibling;
|
|
87
87
|
const t4 = t3.firstChild;
|
|
88
88
|
const t5 = t4.firstChild;
|
package/vue/UiCombobox.vue
CHANGED
|
@@ -2276,10 +2276,15 @@ useComponentHost(() => import('../components/ui-combobox/ui-combobox.js'), {
|
|
|
2276
2276
|
color: inherit;
|
|
2277
2277
|
}
|
|
2278
2278
|
:global(input:where([data-hn-ui-combobox])) {
|
|
2279
|
+
--_ui-combobox-inset-block: var(--ui-control-inset-block-md, var(--ui-space-2));
|
|
2279
2280
|
appearance: none;
|
|
2280
2281
|
width: 100%;
|
|
2281
2282
|
min-inline-size: 0;
|
|
2282
2283
|
min-block-size: calc(var(--_row) - 2 * var(--ui-border-width));
|
|
2284
|
+
max-block-size: max(
|
|
2285
|
+
calc(var(--_row) - 2 * var(--ui-border-width)),
|
|
2286
|
+
calc(1em * var(--ui-line-height-tight) + 2 * var(--_ui-combobox-inset-block))
|
|
2287
|
+
);
|
|
2283
2288
|
box-sizing: border-box !important;
|
|
2284
2289
|
border: 0;
|
|
2285
2290
|
border-radius: inherit;
|
|
@@ -2287,7 +2292,7 @@ useComponentHost(() => import('../components/ui-combobox/ui-combobox.js'), {
|
|
|
2287
2292
|
color: inherit;
|
|
2288
2293
|
font: inherit;
|
|
2289
2294
|
line-height: var(--ui-line-height-tight);
|
|
2290
|
-
padding: var(--
|
|
2295
|
+
padding: var(--_ui-combobox-inset-block) var(--ui-space-3) !important;
|
|
2291
2296
|
margin: 0 !important;
|
|
2292
2297
|
outline: none;
|
|
2293
2298
|
}
|
|
@@ -2295,14 +2300,16 @@ useComponentHost(() => import('../components/ui-combobox/ui-combobox.js'), {
|
|
|
2295
2300
|
--_row: var(--ui-control-size-sm);
|
|
2296
2301
|
}
|
|
2297
2302
|
:global(:scope[data-ui-combobox-state~="size=sm"] input:where([data-hn-ui-combobox])) {
|
|
2298
|
-
|
|
2303
|
+
--_ui-combobox-inset-block: var(--ui-control-inset-block-sm, var(--ui-space-1));
|
|
2304
|
+
padding: var(--_ui-combobox-inset-block) var(--ui-space-2) !important;
|
|
2299
2305
|
font-size: var(--ui-font-size-sm);
|
|
2300
2306
|
}
|
|
2301
2307
|
:global(:scope[data-ui-combobox-state~="size=lg"]:where([data-hn-ui-combobox])) {
|
|
2302
2308
|
--_row: var(--ui-control-size-lg);
|
|
2303
2309
|
}
|
|
2304
2310
|
:global(:scope[data-ui-combobox-state~="size=lg"] input:where([data-hn-ui-combobox])) {
|
|
2305
|
-
|
|
2311
|
+
--_ui-combobox-inset-block: var(--ui-control-inset-block-lg, var(--ui-space-3));
|
|
2312
|
+
padding: var(--_ui-combobox-inset-block) var(--ui-space-4) !important;
|
|
2306
2313
|
font-size: var(--ui-font-size-md);
|
|
2307
2314
|
}
|
|
2308
2315
|
:global(button:where([data-hn-ui-combobox])) {
|
|
@@ -2448,6 +2455,7 @@ useComponentHost(() => import('../components/ui-combobox/ui-combobox.js'), {
|
|
|
2448
2455
|
display: none !important;
|
|
2449
2456
|
}
|
|
2450
2457
|
:global(:scope[data-ui-combobox-state~="multiple"] input:where([data-hn-ui-combobox])) {
|
|
2458
|
+
max-block-size: none;
|
|
2451
2459
|
flex: 1 0 var(--_ui-combobox-item-input-min-inline-size, 5rem);
|
|
2452
2460
|
width: auto;
|
|
2453
2461
|
min-block-size: 1.75rem;
|
|
@@ -2631,10 +2639,15 @@ useComponentHost(() => import('../components/ui-combobox/ui-combobox.js'), {
|
|
|
2631
2639
|
color: inherit;
|
|
2632
2640
|
}
|
|
2633
2641
|
:global(input:where(:scope, :scope *):where([data-hn-ui-combobox])) {
|
|
2642
|
+
--_ui-combobox-inset-block: var(--ui-control-inset-block-md, var(--ui-space-2));
|
|
2634
2643
|
appearance: none;
|
|
2635
2644
|
width: 100%;
|
|
2636
2645
|
min-inline-size: 0;
|
|
2637
2646
|
min-block-size: calc(var(--_row) - 2 * var(--ui-border-width));
|
|
2647
|
+
max-block-size: max(
|
|
2648
|
+
calc(var(--_row) - 2 * var(--ui-border-width)),
|
|
2649
|
+
calc(1em * var(--ui-line-height-tight) + 2 * var(--_ui-combobox-inset-block))
|
|
2650
|
+
);
|
|
2638
2651
|
box-sizing: border-box !important;
|
|
2639
2652
|
border: 0;
|
|
2640
2653
|
border-radius: inherit;
|
|
@@ -2642,7 +2655,7 @@ useComponentHost(() => import('../components/ui-combobox/ui-combobox.js'), {
|
|
|
2642
2655
|
color: inherit;
|
|
2643
2656
|
font: inherit;
|
|
2644
2657
|
line-height: var(--ui-line-height-tight);
|
|
2645
|
-
padding: var(--
|
|
2658
|
+
padding: var(--_ui-combobox-inset-block) var(--ui-space-3) !important;
|
|
2646
2659
|
margin: 0 !important;
|
|
2647
2660
|
outline: none;
|
|
2648
2661
|
}
|
|
@@ -2651,7 +2664,8 @@ useComponentHost(() => import('../components/ui-combobox/ui-combobox.js'), {
|
|
|
2651
2664
|
}
|
|
2652
2665
|
:global(:where(.ui-combobox)[data-ui-combobox-state~="size=sm"]
|
|
2653
2666
|
input:where(:scope, :not([data-hn], [data-slotted]:not([data-slotted~="ui-combobox"]))):where([data-hn-ui-combobox])) {
|
|
2654
|
-
|
|
2667
|
+
--_ui-combobox-inset-block: var(--ui-control-inset-block-sm, var(--ui-space-1));
|
|
2668
|
+
padding: var(--_ui-combobox-inset-block) var(--ui-space-2) !important;
|
|
2655
2669
|
font-size: var(--ui-font-size-sm);
|
|
2656
2670
|
}
|
|
2657
2671
|
:global(:where(.ui-combobox)[data-ui-combobox-state~="size=lg"]:where(:scope, :not([data-hn], [data-slotted]:not([data-slotted~="ui-combobox"]))):where([data-hn-ui-combobox])) {
|
|
@@ -2659,7 +2673,8 @@ useComponentHost(() => import('../components/ui-combobox/ui-combobox.js'), {
|
|
|
2659
2673
|
}
|
|
2660
2674
|
:global(:where(.ui-combobox)[data-ui-combobox-state~="size=lg"]
|
|
2661
2675
|
input:where(:scope, :not([data-hn], [data-slotted]:not([data-slotted~="ui-combobox"]))):where([data-hn-ui-combobox])) {
|
|
2662
|
-
|
|
2676
|
+
--_ui-combobox-inset-block: var(--ui-control-inset-block-lg, var(--ui-space-3));
|
|
2677
|
+
padding: var(--_ui-combobox-inset-block) var(--ui-space-4) !important;
|
|
2663
2678
|
font-size: var(--ui-font-size-md);
|
|
2664
2679
|
}
|
|
2665
2680
|
:global(button:where(:scope, :scope *):where([data-hn-ui-combobox])) {
|
|
@@ -2814,6 +2829,7 @@ useComponentHost(() => import('../components/ui-combobox/ui-combobox.js'), {
|
|
|
2814
2829
|
}
|
|
2815
2830
|
:global(:where(.ui-combobox)[data-ui-combobox-state~="multiple"]
|
|
2816
2831
|
input:where(:scope, :not([data-hn], [data-slotted]:not([data-slotted~="ui-combobox"]))):where([data-hn-ui-combobox])) {
|
|
2832
|
+
max-block-size: none;
|
|
2817
2833
|
flex: 1 0 var(--_ui-combobox-item-input-min-inline-size, 5rem);
|
|
2818
2834
|
width: auto;
|
|
2819
2835
|
min-block-size: 1.75rem;
|