@threadlabs/looma 0.37.6 → 0.39.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/EXPRESSIVE-MARKDOWN.md +2 -0
- package/README.md +1 -1
- package/components/ui-button/ui-button.html +20 -5
- package/components/ui-combobox/ui-combobox.html +24 -3
- package/components/ui-combobox/ui-combobox.js +18 -3
- package/components/ui-icon-button/ui-icon-button.html +17 -8
- package/components/ui-menu-item/ui-menu-item.html +6 -1
- package/components/ui-select/ui-select.html +12 -0
- package/dist/index.js +1 -1
- package/docs/README.md +25 -12
- package/docs/catalog.json +78 -31
- package/docs/cli.mjs +1 -1
- package/docs/commands.mjs +2 -2
- package/docs/customizer-render.mjs +36 -20
- package/docs/customizer.js +76 -25
- package/docs/documentation.css +2 -0
- package/docs/index.d.ts +3 -3
- package/docs/local-server.mjs +1 -1
- package/docs/palette.mjs +3 -3
- package/docs/site.mjs +7 -7
- package/docs/theme-session.mjs +1 -0
- package/docs/theme.mjs +11 -10
- package/editor/expressmark.js +12 -4
- package/package.json +6 -6
- package/styles/ui-button.css +19 -9
- package/styles/ui-combobox.css +40 -2
- package/styles/ui-icon-button.css +12 -2
- package/styles/ui-menu-item.css +6 -1
- package/styles/ui-select.css +9 -0
- package/theme-system.css +7 -2
- package/vanilla/UiButton.js +1 -1
- package/vanilla/UiCombobox.d.ts +1 -0
- package/vanilla/UiCombobox.js +38 -37
- package/vanilla/UiIconButton.d.ts +1 -1
- package/vanilla/UiIconButton.js +1 -1
- package/vanilla/UiSelect.d.ts +1 -0
- package/vanilla/UiSelect.js +7 -7
- package/vue/UiButton.vue +30 -12
- package/vue/UiCombobox.d.ts +6 -0
- package/vue/UiCombobox.vue +70 -6
- package/vue/UiIconButton.d.ts +2 -2
- package/vue/UiIconButton.vue +23 -18
- package/vue/UiMenuItem.vue +6 -1
- package/vue/UiSelect.d.ts +6 -0
- package/vue/UiSelect.vue +30 -1
- package/vue/chunks/UiButton.js +4 -4
- package/vue/chunks/UiCombobox.js +72 -46
- package/vue/chunks/UiIconButton.js +6 -7
- package/vue/chunks/UiMenuItem.js +1 -1
- package/vue/chunks/UiSelect.js +27 -2
- package/vue/components.css +126 -25
- package/vue/editor/index.js +2 -3
package/docs/theme.mjs
CHANGED
|
@@ -3,7 +3,7 @@ import { mixColour as mix, paletteSeeds, companionSurface, toLch, fromLch } from
|
|
|
3
3
|
|
|
4
4
|
const bounds = {
|
|
5
5
|
'text-size': [14, 24], 'control-roominess': [2, 4], 'corner-roundness': [0, 32],
|
|
6
|
-
'line-weight': [.5, 3], spacing: [.75, 1.5], 'scale-contrast': [1, 1.5],
|
|
6
|
+
'line-weight': [.5, 3], rhythm: [.75, 1.5], spacing: [.75, 1.5], 'scale-contrast': [1, 1.5],
|
|
7
7
|
'space-curve': [.5, 2], 'text-weight': [400, 700], 'outer-depth': [0, 2], 'inner-depth': [0, 2],
|
|
8
8
|
'response-time': [0, 600],
|
|
9
9
|
};
|
|
@@ -16,12 +16,13 @@ export const themeFields = [
|
|
|
16
16
|
['Colour', 'text', 'Text', '--ui-text', 'colour', '#1a1a1a', 'Readable content; supporting text follows this and Surface.'],
|
|
17
17
|
['Colour', 'warmth', 'Surface warmth', null, 'fraction', '0.5', '0% cool, 50% original, 100% warm paper and ink; your brand hue stays independent.'],
|
|
18
18
|
['Colour', 'color-intensity', 'Color intensity', null, 'fraction', '0.5', '0% quiet, 50% original, 100% vivid, coordinated across surfaces, actions and status colors.'],
|
|
19
|
-
['Colour', 'palette-relationship', 'Palette relationship', null, 'choice', 'preserve', 'Keep
|
|
20
|
-
['Type', 'body-font', '
|
|
21
|
-
['Type', 'display-font', '
|
|
19
|
+
['Colour', 'palette-relationship', 'Palette relationship', null, 'choice', 'preserve', 'Keep base hues, or relate quiet surfaces to the brand through neighboring or complementary hues.'],
|
|
20
|
+
['Type', 'body-font', 'Font', '--ui-font-sans', 'font', 'system-ui, sans-serif', 'The shared interface typeface.'],
|
|
21
|
+
['Type', 'display-font', 'Heading font', '--ui-font-display', 'font', 'system-ui, sans-serif', 'The heading typeface.'],
|
|
22
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
|
+
['Size & spacing', 'rhythm', 'Rhythm', '--ui-rhythm', 'positive', '1', 'More or less breathing room between lines and groups. Spacing fine-tunes gaps independently.'],
|
|
25
26
|
['Size & spacing', 'spacing', 'Spacing', '--ui-spacing', 'positive', '1', 'More or less breathing room throughout the interface.'],
|
|
26
27
|
['Size & spacing', 'scale-contrast', 'Size ratio', '--ui-scale-ratio', 'ratio', '1.2', 'The multiplier between small, medium and large controls. At 1 the sizes are equal; higher values separate them.'],
|
|
27
28
|
['Size & spacing', 'space-curve', 'Spacing curve', '--ui-space-curve', 'nonnegative', '1', 'Spacing growth: 1 is linear, 2 is quadratic. Independent of control sizes.'],
|
|
@@ -31,7 +32,7 @@ export const themeFields = [
|
|
|
31
32
|
['Material', 'inner-depth', 'Inner depth', '--ui-inner-depth', 'nonnegative', '0.3', 'How strongly fields and pressed controls sit into their surface.'],
|
|
32
33
|
['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
34
|
['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
|
-
['Material', 'translucency', 'Overlay translucency', '--ui-overlay-translucency', 'fraction', '0', 'How much background comes through floating
|
|
35
|
+
['Material', 'translucency', 'Overlay translucency', '--ui-overlay-translucency', 'fraction', '0', 'How much background comes through floating material around opaque content.'],
|
|
35
36
|
['Material', 'edge-light', 'Edge light', '--ui-edge-light', 'fraction', '0', 'Light caught by surface edges, independent of depth.'],
|
|
36
37
|
['Material', 'finish', 'Finish', '--ui-finish', 'fraction', '0', 'Matte through polished: how strongly the material catches a reflection.'],
|
|
37
38
|
['Material', 'glow', 'Glow', '--ui-glow', 'fraction', '0', 'Reflected light beyond a surface, from a quiet halo to a vivid bloom at the upper end.'],
|
|
@@ -47,11 +48,11 @@ export const defaultRecipe = () => ({ schemaVersion: 1, values: Object.fromEntri
|
|
|
47
48
|
const starting = values => ({ schemaVersion: 1, values: { ...defaultRecipe().values, ...values } });
|
|
48
49
|
export const startingPoints = {
|
|
49
50
|
clear: defaultRecipe(),
|
|
50
|
-
bookish: starting({ action: '#2e1228', canvas: '#f7f7f5', surface: '#fffefd', text: '#0d0c0c', 'body-font': 'Arial, sans-serif', 'display-font': 'Georgia, serif', 'text-weight': '400', 'control-roominess': '2.625', 'scale-contrast': '1.18', 'outer-depth': '1', 'inner-depth': '1', glassiness: '0.8', 'surface-separation': '0.65', translucency: '0.18', 'edge-light': '1', finish: '0.85', glow: '0.3', 'light-spread': '0.3', 'light-strength': '0.9', 'response-time': '150ms', 'response-curve': 'ease' }),
|
|
51
|
-
wiki: starting({ action: '#4f46e5', canvas: '#ffffff', surface: '#ffffff', text: '#1a1a1a', 'control-roominess': '2.125', spacing: '0.85', 'scale-contrast': '1.15', 'corner-roundness': '6px', 'outer-depth': '0.35', 'inner-depth': '0', 'response-time': '110ms' }),
|
|
52
|
-
editorial: starting({ action: '#182c24', canvas: '#eee9dc', surface: '#fffdf5', text: '#182c24', 'corner-roundness': '0px', 'outer-depth': '0', 'inner-depth': '0', 'body-font': 'Georgia, serif', 'display-font': 'Georgia, serif', 'text-weight': '400', 'line-weight': '2px', 'response-time': '90ms', 'response-curve': 'linear' }),
|
|
53
|
-
night: starting({ action: '#a7b8ff', canvas: '#11141c', surface: '#202634', text: '#edf1fb', 'corner-roundness': '12px', 'outer-depth': '1.4', 'inner-depth': '0.45', translucency: '0.2', 'edge-light': '0.65', 'response-time': '210ms', 'response-curve': 'cubic-bezier(0.2,0.8,0.2,1)' }),
|
|
54
|
-
garden: starting({ action: '#256342', canvas: '#
|
|
51
|
+
bookish: starting({ rhythm: '1.05', action: '#2e1228', canvas: '#f7f7f5', surface: '#fffefd', text: '#0d0c0c', 'body-font': 'Arial, sans-serif', 'display-font': 'Georgia, serif', 'text-weight': '400', 'control-roominess': '2.625', 'scale-contrast': '1.18', 'outer-depth': '1', 'inner-depth': '1', glassiness: '0.8', 'surface-separation': '0.65', translucency: '0.18', 'edge-light': '1', finish: '0.85', glow: '0.3', 'light-spread': '0.3', 'light-strength': '0.9', 'response-time': '150ms', 'response-curve': 'ease' }),
|
|
52
|
+
wiki: starting({ rhythm: '0.8', 'text-weight': '400', 'space-curve': '0.8', action: '#4f46e5', canvas: '#ffffff', surface: '#ffffff', text: '#1a1a1a', 'control-roominess': '2.125', spacing: '0.85', 'scale-contrast': '1.15', 'corner-roundness': '6px', 'outer-depth': '0.35', 'inner-depth': '0', 'response-time': '110ms' }),
|
|
53
|
+
editorial: starting({ rhythm: '1.3', 'text-size': '17px', spacing: '1.12', 'space-curve': '1.4', 'scale-contrast': '1.3', action: '#182c24', canvas: '#eee9dc', surface: '#fffdf5', text: '#182c24', 'corner-roundness': '0px', 'outer-depth': '0', 'inner-depth': '0', 'body-font': 'Georgia, serif', 'display-font': 'Georgia, serif', 'text-weight': '400', 'line-weight': '2px', 'response-time': '90ms', 'response-curve': 'linear' }),
|
|
54
|
+
night: starting({ rhythm: '0.9', 'display-font': 'ui-monospace, monospace', 'text-weight': '600', 'control-roominess': '2.375', spacing: '0.9', 'scale-contrast': '1.25', 'space-curve': '1.2', finish: '0.6', glow: '0.45', glassiness: '0.45', 'surface-separation': '0.25', 'light-x': '0.25', 'light-spread': '0.65', action: '#a7b8ff', canvas: '#11141c', surface: '#202634', text: '#edf1fb', 'corner-roundness': '12px', 'outer-depth': '1.4', 'inner-depth': '0.45', translucency: '0.2', 'edge-light': '0.65', 'response-time': '210ms', 'response-curve': 'cubic-bezier(0.2,0.8,0.2,1)' }),
|
|
55
|
+
garden: starting({ rhythm: '1.2', 'body-font': 'ui-rounded, system-ui, sans-serif', 'display-font': 'Georgia, serif', 'text-weight': '400', 'space-curve': '1.15', finish: '0.15', 'light-spread': '1', action: '#256342', canvas: '#f4edda', surface: '#fff8e7', text: '#173121', 'corner-roundness': '22px', 'outer-depth': '0.35', 'inner-depth': '0', 'edge-light': '0.2', 'control-roominess': '2.875', 'scale-contrast': '1.24', spacing: '1.05', 'response-time': '180ms', 'response-curve': 'cubic-bezier(0.16,1,0.3,1)' }),
|
|
55
56
|
};
|
|
56
57
|
export const startingPointLabels = { clear: 'Clear', bookish: 'Bookish', wiki: 'Wiki', editorial: 'Editorial', night: 'Night', garden: 'Garden' };
|
|
57
58
|
const paletteKeys = themeFields.filter(field => field.family === 'Colour').map(field => field.id);
|
package/editor/expressmark.js
CHANGED
|
@@ -109,7 +109,7 @@ function safeBackground(value, path) {
|
|
|
109
109
|
value: "color",
|
|
110
110
|
attributes: { style }
|
|
111
111
|
}]
|
|
112
|
-
})), (node) => typeof node.attributes?.style === "string")?.attributes?.style;
|
|
112
|
+
}), { syntax: "compatible" }), (node) => typeof node.attributes?.style === "string")?.attributes?.style;
|
|
113
113
|
if (typeof accepted !== "string" || accepted.replace(/\s/g, "").replace(/;$/, "") !== style.replace(/\s/g, "")) fail(path, "unsafe or unsupported table background");
|
|
114
114
|
}
|
|
115
115
|
function safeNavigation(value, image, path) {
|
|
@@ -129,7 +129,7 @@ function safeNavigation(value, image, path) {
|
|
|
129
129
|
value: "link"
|
|
130
130
|
}]
|
|
131
131
|
}]
|
|
132
|
-
})), (child) => child.type === (image ? "image" : "link"))?.url !== value) fail(path, "unsafe or unsupported navigation URL");
|
|
132
|
+
}), { syntax: "compatible" }), (child) => child.type === (image ? "image" : "link"))?.url !== value) fail(path, "unsafe or unsupported navigation URL");
|
|
133
133
|
}
|
|
134
134
|
var MARKS = {
|
|
135
135
|
bold: "strong",
|
|
@@ -605,6 +605,7 @@ function editorDocumentToExpressmark(document, options = {}) {
|
|
|
605
605
|
return {
|
|
606
606
|
type: "document",
|
|
607
607
|
version: 1,
|
|
608
|
+
syntax: options.syntax ?? "compatible",
|
|
608
609
|
body: (document.content ?? []).map((node, index) => {
|
|
609
610
|
const child = toNode(node, `document.body[${index}]`, context);
|
|
610
611
|
return child.type === "image" ? {
|
|
@@ -1007,7 +1008,11 @@ function expressmarkToEditorDocument(document) {
|
|
|
1007
1008
|
* @failure Unsupported editor data raises EditorMarkdownConversionError before any input is changed.
|
|
1008
1009
|
*/
|
|
1009
1010
|
function editorDocumentToMarkdown(document, options = {}) {
|
|
1010
|
-
|
|
1011
|
+
const conversion = {
|
|
1012
|
+
syntax: "compatible",
|
|
1013
|
+
...options
|
|
1014
|
+
};
|
|
1015
|
+
return stringify(editorDocumentToExpressmark(document, conversion), conversion);
|
|
1011
1016
|
}
|
|
1012
1017
|
/**
|
|
1013
1018
|
* Serializes the current editor, including its current display cache and concrete TOC entries.
|
|
@@ -1024,7 +1029,10 @@ function editorToMarkdown(editor, options = {}) {
|
|
|
1024
1029
|
* @failure Unsupported content or root metadata stops conversion instead of being silently flattened.
|
|
1025
1030
|
*/
|
|
1026
1031
|
function markdownToEditorDocument(markdown, options = {}) {
|
|
1027
|
-
return expressmarkToEditorDocument(parse(markdown,
|
|
1032
|
+
return expressmarkToEditorDocument(parse(markdown, {
|
|
1033
|
+
syntax: "compatible",
|
|
1034
|
+
...options
|
|
1035
|
+
}));
|
|
1028
1036
|
}
|
|
1029
1037
|
//#endregion
|
|
1030
1038
|
export { EditorMarkdownConversionError, editorDocumentToExpressmark, editorDocumentToMarkdown, editorToMarkdown, expressmarkToEditorDocument, markdownToEditorDocument };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@threadlabs/looma",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.39.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Looma components as HTML, Vue, and plain DOM, with design tokens.",
|
|
6
6
|
"license": "MIT",
|
|
@@ -119,7 +119,7 @@
|
|
|
119
119
|
"lowlight": "^3.3.0",
|
|
120
120
|
"markdown-it": "14.3.1",
|
|
121
121
|
"lucide": "~0.554.0",
|
|
122
|
-
"expressmark": "0.1
|
|
122
|
+
"expressmark": ">=0.2.0 <1"
|
|
123
123
|
},
|
|
124
124
|
"peerDependencies": {
|
|
125
125
|
"@nextwebwg/html-next": "1.0.0-alpha.69",
|
|
@@ -127,9 +127,9 @@
|
|
|
127
127
|
"@tiptap/pm": ">=2 <3",
|
|
128
128
|
"@tiptap/vue-3": ">=2 <3",
|
|
129
129
|
"vue": "^3.5.43",
|
|
130
|
-
"markupress": "
|
|
131
|
-
"@
|
|
132
|
-
"vite": "
|
|
130
|
+
"markupress": ">=0.10.0 <1",
|
|
131
|
+
"@threadlabs/markupkit": ">=0.2.0 <1",
|
|
132
|
+
"vite": ">=7.2.7 <9"
|
|
133
133
|
},
|
|
134
134
|
"peerDependenciesMeta": {
|
|
135
135
|
"@tiptap/core": {
|
|
@@ -147,7 +147,7 @@
|
|
|
147
147
|
"markupress": {
|
|
148
148
|
"optional": true
|
|
149
149
|
},
|
|
150
|
-
"@
|
|
150
|
+
"@threadlabs/markupkit": {
|
|
151
151
|
"optional": true
|
|
152
152
|
},
|
|
153
153
|
"vite": {
|
package/styles/ui-button.css
CHANGED
|
@@ -268,7 +268,6 @@
|
|
|
268
268
|
background-color: var(--ui-button-hover-surface, oklch(from var(--_tone) l c h / 12%));
|
|
269
269
|
}
|
|
270
270
|
|
|
271
|
-
/* A neutral card encloses content quietly; ordinary outlines keep their action-tone edge. */
|
|
272
271
|
:scope:where([data-component~="ui-button"])[data-appearance="card"][data-tone="neutral"] {
|
|
273
272
|
border-color: var(--ui-button-border, var(--ui-border));
|
|
274
273
|
}
|
|
@@ -277,6 +276,12 @@
|
|
|
277
276
|
border-color: var(--ui-button-hover-border, var(--ui-border-strong));
|
|
278
277
|
}
|
|
279
278
|
|
|
279
|
+
:scope:where([data-component~="ui-button"])[data-appearance="card"][aria-pressed="true"] {
|
|
280
|
+
background-color: var(--ui-selection-surface);
|
|
281
|
+
border-color: var(--ui-selection-border);
|
|
282
|
+
color: var(--ui-selection-text);
|
|
283
|
+
}
|
|
284
|
+
|
|
280
285
|
/* Pressed reads as pressed in: the highlight goes, and the shadow moves inside. */
|
|
281
286
|
:scope:where([data-component~="ui-button"]):active:not(:disabled, [aria-disabled="true"]) {
|
|
282
287
|
scale: var(--ui-press-scale, 1);
|
|
@@ -324,6 +329,18 @@
|
|
|
324
329
|
background-color: var(--ui-button-surface, var(--ui-neutral-surface));
|
|
325
330
|
color: var(--ui-button-text, var(--ui-text));
|
|
326
331
|
}
|
|
332
|
+
:scope:where([data-component~="ui-button"])[data-tone="neutral"][data-appearance="outline"] {
|
|
333
|
+
border-color: var(--ui-button-border, var(--ui-control-border));
|
|
334
|
+
background-color: var(--ui-button-surface, transparent);
|
|
335
|
+
}
|
|
336
|
+
:scope:where([data-component~="ui-button"])[data-tone="neutral"][data-appearance="outline"]:hover:not(:disabled, [aria-disabled="true"]) {
|
|
337
|
+
border-color: var(--ui-button-hover-border, var(--ui-control-border-hover, var(--ui-control-border)));
|
|
338
|
+
background-color: var(--ui-button-hover-surface, var(--ui-neutral-hover));
|
|
339
|
+
}
|
|
340
|
+
:scope:where([data-component~="ui-button"])[data-tone="neutral"][data-appearance="outline"]:active:not(:disabled, [aria-disabled="true"]) {
|
|
341
|
+
border-color: var(--ui-button-hover-border, var(--ui-control-border-hover, var(--ui-control-border)));
|
|
342
|
+
background-color: var(--ui-button-active-surface, var(--ui-neutral-active));
|
|
343
|
+
}
|
|
327
344
|
:scope:where([data-component~="ui-button"])[data-tone="neutral"][data-appearance="solid"]:hover:not(:disabled, [aria-disabled="true"]) {
|
|
328
345
|
background-color: var(--ui-button-hover-surface, var(--ui-neutral-hover));
|
|
329
346
|
}
|
|
@@ -411,10 +428,8 @@
|
|
|
411
428
|
border-color: transparent;
|
|
412
429
|
}
|
|
413
430
|
|
|
414
|
-
/* Pressed, or holding its menu open, a ghost button sinks slightly: a light surface with a faint
|
|
415
|
-
edge and a soft inner shadow. Lighter than hover, so an open menu's trigger never reads as heavy. */
|
|
416
431
|
:scope:where([data-component~="ui-button"])[data-appearance="ghost"]:is(:active, [aria-expanded="true"]):not(:disabled, [aria-disabled="true"]) {
|
|
417
|
-
background-color: var(--ui-button-ghost-active-surface,
|
|
432
|
+
background-color: var(--ui-button-ghost-active-surface, oklch(from var(--_tone) l c h / 18%));
|
|
418
433
|
border-color: transparent;
|
|
419
434
|
box-shadow: inset 0 0 0 1px oklch(from var(--_tone) l c h / 10%), inset 0 1px 2px oklch(from var(--_tone) l c h / 8%);
|
|
420
435
|
}
|
|
@@ -548,8 +563,6 @@
|
|
|
548
563
|
/* A link is never :enabled, so interactive states test "not unavailable" instead: for a button that
|
|
549
564
|
is exactly :enabled, at the same specificity, and it also matches a link that can be followed. */
|
|
550
565
|
|
|
551
|
-
/* A neutral card encloses content quietly; ordinary outlines keep their action-tone edge. */
|
|
552
|
-
|
|
553
566
|
/* Pressed reads as pressed in: the highlight goes, and the shadow moves inside. */
|
|
554
567
|
|
|
555
568
|
/* Tone is the colour, variant is the volume. Every pairing works: a danger outline is a
|
|
@@ -559,9 +572,6 @@
|
|
|
559
572
|
|
|
560
573
|
/* Tints derive from the text colour, not a surface token, so hover and press show on any background. */
|
|
561
574
|
|
|
562
|
-
/* Pressed, or holding its menu open, a ghost button sinks slightly: a light surface with a faint
|
|
563
|
-
edge and a soft inner shadow. Lighter than hover, so an open menu's trigger never reads as heavy. */
|
|
564
|
-
|
|
565
575
|
/* A soft halo that transitions in with the other box-shadow changes, layered on the raised shadow. */
|
|
566
576
|
|
|
567
577
|
/* A loading action stays visually active; only an explicitly disabled action takes the
|
package/styles/ui-combobox.css
CHANGED
|
@@ -136,6 +136,21 @@
|
|
|
136
136
|
flex: 1 1 auto;
|
|
137
137
|
min-inline-size: 0;
|
|
138
138
|
}
|
|
139
|
+
.start {
|
|
140
|
+
display: flex !important;
|
|
141
|
+
align-items: center !important;
|
|
142
|
+
flex: none;
|
|
143
|
+
padding-inline-start: var(--ui-space-3) !important;
|
|
144
|
+
}
|
|
145
|
+
.start:empty {
|
|
146
|
+
display: none !important;
|
|
147
|
+
}
|
|
148
|
+
:scope:where([data-component~="ui-combobox"])[data-ui-combobox-state~="size=sm"] .start {
|
|
149
|
+
padding-inline-start: var(--ui-space-2) !important;
|
|
150
|
+
}
|
|
151
|
+
:scope:where([data-component~="ui-combobox"])[data-ui-combobox-state~="size=lg"] .start {
|
|
152
|
+
padding-inline-start: var(--ui-space-4) !important;
|
|
153
|
+
}
|
|
139
154
|
/* A ghost icon button: the affordance is the mark, not a box around it. */
|
|
140
155
|
.help {
|
|
141
156
|
flex: none;
|
|
@@ -174,6 +189,10 @@
|
|
|
174
189
|
block-size: var(--ui-icon-size-sm);
|
|
175
190
|
}
|
|
176
191
|
|
|
192
|
+
:scope:where([data-component~="ui-combobox"])[data-ui-combobox-state~="muted"] input:not(:disabled) {
|
|
193
|
+
color: var(--ui-text-secondary);
|
|
194
|
+
}
|
|
195
|
+
|
|
177
196
|
input:where(:not([data-component], [data-slotted]))::selection {
|
|
178
197
|
background: color-mix(in srgb, var(--ui-accent-solid) 20%, transparent);
|
|
179
198
|
color: inherit;
|
|
@@ -258,7 +277,7 @@
|
|
|
258
277
|
padding: var(--ui-space-1) !important;
|
|
259
278
|
border: var(--ui-border-width) solid var(--_ui-combobox-popup-border, var(--ui-overlay-border, var(--ui-border-strong)));
|
|
260
279
|
border-radius: var(--_ui-combobox-popup-radius, var(--ui-overlay-radius, var(--ui-radius-md)));
|
|
261
|
-
background: var(--_ui-combobox-popup-surface, var(--ui-
|
|
280
|
+
background: var(--_ui-combobox-popup-surface, var(--ui-material-content, var(--ui-surface-elevated)));
|
|
262
281
|
color: var(--ui-text-primary);
|
|
263
282
|
box-shadow: var(--_ui-combobox-popup-shadow, var(--ui-overlay-shadow, var(--ui-elevation-overlay, var(--ui-shadow-lg))));
|
|
264
283
|
}
|
|
@@ -537,6 +556,21 @@
|
|
|
537
556
|
flex: 1 1 auto;
|
|
538
557
|
min-inline-size: 0;
|
|
539
558
|
}
|
|
559
|
+
.start:where(:scope, :scope *) {
|
|
560
|
+
display: flex !important;
|
|
561
|
+
align-items: center !important;
|
|
562
|
+
flex: none;
|
|
563
|
+
padding-inline-start: var(--ui-space-3) !important;
|
|
564
|
+
}
|
|
565
|
+
.start:empty:where(:scope, :scope *) {
|
|
566
|
+
display: none !important;
|
|
567
|
+
}
|
|
568
|
+
:where([data-component~="ui-combobox"])[data-ui-combobox-state~="size=sm"] .start:where(:scope, :not([data-component], [data-slotted]:not([data-slotted~="ui-combobox"]))) {
|
|
569
|
+
padding-inline-start: var(--ui-space-2) !important;
|
|
570
|
+
}
|
|
571
|
+
:where([data-component~="ui-combobox"])[data-ui-combobox-state~="size=lg"] .start:where(:scope, :not([data-component], [data-slotted]:not([data-slotted~="ui-combobox"]))) {
|
|
572
|
+
padding-inline-start: var(--ui-space-4) !important;
|
|
573
|
+
}
|
|
540
574
|
/* A ghost icon button: the affordance is the mark, not a box around it. */
|
|
541
575
|
.help:where(:scope, :scope *) {
|
|
542
576
|
flex: none;
|
|
@@ -574,6 +608,10 @@
|
|
|
574
608
|
block-size: var(--ui-icon-size-sm);
|
|
575
609
|
}
|
|
576
610
|
|
|
611
|
+
:where([data-component~="ui-combobox"])[data-ui-combobox-state~="muted"] input:not(:disabled):where(:scope, :not([data-component], [data-slotted]:not([data-slotted~="ui-combobox"]))) {
|
|
612
|
+
color: var(--ui-text-secondary);
|
|
613
|
+
}
|
|
614
|
+
|
|
577
615
|
input:where(:not([data-component], [data-slotted]:not([data-slotted~="ui-combobox"]))):where(:scope, :scope *)::selection {
|
|
578
616
|
background: color-mix(in srgb, var(--ui-accent-solid) 20%, transparent);
|
|
579
617
|
color: inherit;
|
|
@@ -657,7 +695,7 @@
|
|
|
657
695
|
padding: var(--ui-space-1) !important;
|
|
658
696
|
border: var(--ui-border-width) solid var(--_ui-combobox-popup-border, var(--ui-overlay-border, var(--ui-border-strong)));
|
|
659
697
|
border-radius: var(--_ui-combobox-popup-radius, var(--ui-overlay-radius, var(--ui-radius-md)));
|
|
660
|
-
background: var(--_ui-combobox-popup-surface, var(--ui-
|
|
698
|
+
background: var(--_ui-combobox-popup-surface, var(--ui-material-content, var(--ui-surface-elevated)));
|
|
661
699
|
color: var(--ui-text-primary);
|
|
662
700
|
box-shadow: var(--_ui-combobox-popup-shadow, var(--ui-overlay-shadow, var(--ui-elevation-overlay, var(--ui-shadow-lg))));
|
|
663
701
|
}
|
|
@@ -172,10 +172,14 @@
|
|
|
172
172
|
color: var(--_icon-button-text);
|
|
173
173
|
}
|
|
174
174
|
|
|
175
|
-
/*
|
|
175
|
+
/* Neutral actions share the same quiet surface and border roles as fields. */
|
|
176
176
|
:scope:where([data-component~="ui-icon-button"])[data-tone="neutral"] {
|
|
177
177
|
--_tone-highlight: var(--ui-neutral-highlight, none);
|
|
178
178
|
--_tone-glow: var(--ui-neutral-glow, 0 0 transparent);
|
|
179
|
+
--_icon-button-outline-surface: transparent;
|
|
180
|
+
--_icon-button-border: var(--ui-control-border);
|
|
181
|
+
--_icon-button-hover-border: var(--ui-control-border-hover, var(--ui-control-border));
|
|
182
|
+
--_icon-button-text: var(--ui-text);
|
|
179
183
|
--_icon-button-solid-surface: var(--ui-neutral-surface);
|
|
180
184
|
--_icon-button-solid-hover-surface: var(--ui-neutral-hover);
|
|
181
185
|
--_icon-button-solid-text: var(--ui-text);
|
|
@@ -363,6 +367,12 @@
|
|
|
363
367
|
:scope:where([data-component~="ui-icon-button"])[data-appearance="outline"]:hover:enabled:not([aria-disabled="true"]) {
|
|
364
368
|
border-color: var(--ui-icon-button-hover-border, var(--_icon-button-hover-border, var(--ui-control-border-hover, var(--ui-control-border))));
|
|
365
369
|
}
|
|
370
|
+
:scope:where([data-component~="ui-icon-button"])[data-tone="neutral"][data-appearance="outline"]:hover:enabled:not([aria-disabled="true"]) {
|
|
371
|
+
background-color: var(--ui-icon-button-hover-surface, var(--ui-neutral-hover));
|
|
372
|
+
}
|
|
373
|
+
:scope:where([data-component~="ui-icon-button"])[data-tone="neutral"][data-appearance="outline"]:active:enabled:not([aria-disabled="true"]) {
|
|
374
|
+
background-color: var(--ui-icon-button-active-surface, var(--ui-neutral-active));
|
|
375
|
+
}
|
|
366
376
|
|
|
367
377
|
:scope:where([data-component~="ui-icon-button"])[data-appearance="solid"]:hover:enabled:not([aria-disabled="true"]) {
|
|
368
378
|
background-color: var(--ui-icon-button-solid-hover-surface, var(--_icon-button-solid-hover-surface, var(--ui-accent-hover)));
|
|
@@ -487,7 +497,7 @@
|
|
|
487
497
|
@scope ([data-component~="ui-icon-button"]) to ([data-component]) {
|
|
488
498
|
/* This component's hooks style the element they are set on, and never a nested one. */
|
|
489
499
|
|
|
490
|
-
/*
|
|
500
|
+
/* Neutral actions share the same quiet surface and border roles as fields. */
|
|
491
501
|
|
|
492
502
|
/* At rest an anticipatory button shows only its guide dot. The icon grows into place when
|
|
493
503
|
proximity, hover, or focus reveals it; the button's hit area and guide remain stable. */
|
package/styles/ui-menu-item.css
CHANGED
|
@@ -131,7 +131,12 @@
|
|
|
131
131
|
}
|
|
132
132
|
|
|
133
133
|
:scope:where([data-component~="ui-menu-item"]):is(:hover, :focus-visible):not([aria-disabled="true"]) {
|
|
134
|
-
background: var(--ui-menu-item-hover-surface, var(--ui-
|
|
134
|
+
background: var(--ui-menu-item-hover-surface, var(--ui-accent-subtle));
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
:scope:where([data-component~="ui-menu-item"])[aria-checked="true"]:not([aria-disabled="true"]) {
|
|
138
|
+
background: var(--ui-selection-surface);
|
|
139
|
+
color: var(--ui-selection-text);
|
|
135
140
|
}
|
|
136
141
|
|
|
137
142
|
:scope:where([data-component~="ui-menu-item"]):focus-visible {
|
package/styles/ui-select.css
CHANGED
|
@@ -88,6 +88,10 @@
|
|
|
88
88
|
background-size: 1.125rem 1.125rem, 100% 100%;
|
|
89
89
|
}
|
|
90
90
|
|
|
91
|
+
:scope:where([data-component~="ui-select"])[data-ui-select-state~="muted"] {
|
|
92
|
+
color: var(--ui-text-secondary);
|
|
93
|
+
}
|
|
94
|
+
|
|
91
95
|
:scope:where([data-component~="ui-select"]):hover:not(:focus):not(:disabled):not([aria-invalid="true"]):not(:is(:user-invalid, [data-user-invalid])) {
|
|
92
96
|
border-color: var(--ui-select-border-hover, var(--ui-control-border-hover, var(--ui-text-secondary)));
|
|
93
97
|
}
|
|
@@ -160,6 +164,11 @@
|
|
|
160
164
|
|
|
161
165
|
/* The select draws its own chevron: the native caret cannot be inset or sized consistently.
|
|
162
166
|
* The root stays a native <s-lect>, so the icon is a background image rather than an element. */
|
|
167
|
+
:scope:where([data-component~="ui-select"])[data-ui-select-state~="muted"] :where([data-slotted], [data-slotted] *):is(option),
|
|
168
|
+
:scope:where([data-component~="ui-select"])[data-ui-select-state~="muted"] :where([data-slotted], [data-slotted] *):is(optgroup) {
|
|
169
|
+
color: var(--ui-text-primary);
|
|
170
|
+
background-color: var(--ui-control-surface);
|
|
171
|
+
}
|
|
163
172
|
|
|
164
173
|
/* The app's invalid flag, or the browser's own verdict once the user has left an invalid field. */
|
|
165
174
|
|
package/theme-system.css
CHANGED
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
@layer theme {
|
|
15
15
|
:root {
|
|
16
16
|
--ui-spacing: 1;
|
|
17
|
+
--ui-rhythm: 1;
|
|
17
18
|
--ui-control-roominess: 2.5;
|
|
18
19
|
--ui-scale-ratio: 1.2;
|
|
19
20
|
--ui-space-curve: 1;
|
|
@@ -36,7 +37,11 @@
|
|
|
36
37
|
contrast cannot unexpectedly turn linear spacing into quadratic spacing. */
|
|
37
38
|
--ui-control-size: calc(1rem * var(--ui-control-roominess));
|
|
38
39
|
--_ui-theme-ratio: max(1, var(--ui-scale-ratio));
|
|
39
|
-
--_ui-theme-unit: calc(1rem * 0.25 * max(0, var(--ui-spacing)));
|
|
40
|
+
--_ui-theme-unit: calc(1rem * 0.25 * max(0, var(--ui-spacing)) * clamp(0.75, var(--ui-rhythm), 1.5));
|
|
41
|
+
--ui-line-height-tight: clamp(1.1, calc(1.2 + (var(--ui-rhythm) - 1) * 0.2), 1.3);
|
|
42
|
+
--ui-line-height: clamp(1.35, calc(1.4 + (var(--ui-rhythm) - 1) * 0.4), 1.6);
|
|
43
|
+
--ui-line-height-md: var(--ui-line-height);
|
|
44
|
+
--ui-line-height-relaxed: calc(var(--ui-line-height) * 1.15);
|
|
40
45
|
--_ui-theme-curve: max(0, var(--ui-space-curve));
|
|
41
46
|
--ui-control-size-sm: calc(var(--ui-control-size) / var(--_ui-theme-ratio));
|
|
42
47
|
--ui-control-size-md: var(--ui-control-size);
|
|
@@ -89,7 +94,7 @@
|
|
|
89
94
|
--ui-elevation-dialog: var(--ui-shadow-lg);
|
|
90
95
|
--ui-control-inset: inset calc(var(--_ui-light-inline) * var(--_ui-theme-inner)) calc(var(--_ui-light-block) * var(--_ui-theme-inner)) calc(3px * var(--_ui-theme-inner)) rgb(0 0 0 / 0.15), inset calc(-1 * var(--_ui-light-inline)) calc(-1 * var(--_ui-light-block)) 0 rgb(255 255 255 / calc(var(--_ui-theme-edge) * var(--ui-light-strength) * 0.18)), var(--ui-material-glow);
|
|
91
96
|
--ui-pressed: inset calc(2 * var(--_ui-light-inline) * var(--_ui-theme-inner)) calc(2 * var(--_ui-light-block) * var(--_ui-theme-inner)) calc(4px * var(--_ui-theme-inner)) rgb(0 0 0 / 0.2);
|
|
92
|
-
--ui-overlay-surface:
|
|
97
|
+
--ui-overlay-surface: var(--ui-surface-raised);
|
|
93
98
|
/* Decorative outer material is allowed to transmit the scene. Content keeps
|
|
94
99
|
its own opaque plane; changing the scene cannot undermine text contrast. */
|
|
95
100
|
--ui-material-content: var(--ui-surface-raised);
|
package/vanilla/UiButton.js
CHANGED
|
@@ -27,7 +27,7 @@ let P0, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P
|
|
|
27
27
|
const S = { n: ["appearance","colorTone"], t: ["?","?"], f: import.meta.url, g: "ui-button", k: 0, r: readonlyView, a: (host, I) => C(host, I), b: generatedInputs };
|
|
28
28
|
const A = [["a",{},[],"",[],["data-ui-button-state","data-ui-button-prop-align","data-ui-button-prop-as","data-ui-button-prop-disabled","data-ui-button-prop-href","data-ui-button-prop-loading","data-ui-button-prop-pending","data-ui-button-prop-preset","data-ui-button-prop-rel","data-ui-button-prop-shape","data-ui-button-prop-size","data-ui-button-prop-square","data-ui-button-prop-stretch","data-ui-button-prop-target","data-ui-button-prop-tone","data-ui-button-prop-type","data-ui-button-prop-variant","data-appearance","data-tone","href","target","rel","aria-disabled","aria-busy","tabindex"]],["button",{},[],"",[],["data-ui-button-state","data-ui-button-prop-align","data-ui-button-prop-as","data-ui-button-prop-disabled","data-ui-button-prop-href","data-ui-button-prop-loading","data-ui-button-prop-pending","data-ui-button-prop-preset","data-ui-button-prop-rel","data-ui-button-prop-shape","data-ui-button-prop-size","data-ui-button-prop-square","data-ui-button-prop-stretch","data-ui-button-prop-target","data-ui-button-prop-tone","data-ui-button-prop-type","data-ui-button-prop-variant","data-appearance","data-tone","type","disabled","aria-disabled","aria-busy"]],["a",{},[],"",[],["data-ui-button-state","data-ui-button-prop-align","data-ui-button-prop-as","data-ui-button-prop-disabled","data-ui-button-prop-href","data-ui-button-prop-loading","data-ui-button-prop-pending","data-ui-button-prop-preset","data-ui-button-prop-rel","data-ui-button-prop-shape","data-ui-button-prop-size","data-ui-button-prop-square","data-ui-button-prop-stretch","data-ui-button-prop-target","data-ui-button-prop-tone","data-ui-button-prop-type","data-ui-button-prop-variant","data-appearance","data-tone","href","target","rel","aria-disabled","aria-busy","tabindex"]],["button",{},[],"",[],["data-ui-button-state","data-ui-button-prop-align","data-ui-button-prop-as","data-ui-button-prop-disabled","data-ui-button-prop-href","data-ui-button-prop-loading","data-ui-button-prop-pending","data-ui-button-prop-preset","data-ui-button-prop-rel","data-ui-button-prop-shape","data-ui-button-prop-size","data-ui-button-prop-square","data-ui-button-prop-stretch","data-ui-button-prop-target","data-ui-button-prop-tone","data-ui-button-prop-type","data-ui-button-prop-variant","data-appearance","data-tone","type","disabled","aria-disabled","aria-busy"]]];
|
|
29
29
|
const Q = [[],[],[],[]];
|
|
30
|
-
const D = { g: "ui-button", props: { "align": { "required":false,"values":["center","start"],"default":"center", c: checkFormat("keyword", keywordFormat), t: "keyword", j: 0, m: ["p",formatOf(keywordFormat)] }, "as": { "required":false,"values":["button","a"],"default":"button", c: checkFormat("keyword", keywordFormat), t: "keyword", j: 0, m: ["p",formatOf(keywordFormat)] }, "disabled": { "required":false,"default":false, c: checkBoolean, t: "boolean", j: 0, m: "b" }, "href": { "required":false, c: checkString, t: "string", j: 0, m: "s" }, "loading": { "required":false,"default":false, c: checkBoolean, t: "boolean", j: 0, m: "b" }, "pending": { "required":false,"default":false, c: checkBoolean, t: "boolean", j: 0, m: "b" }, "preset": { "required":false,"values":["none","primary","standard","quiet","destructive"],"default":"none", c: checkFormat("keyword", keywordFormat), t: "keyword", j: 0, m: ["p",formatOf(keywordFormat)] }, "rel": { "required":false, c: checkString, t: "string", j: 0, m: "s" }, "shape": { "required":false,"values":["rounded","pill"],"default":"rounded", c: checkFormat("keyword", keywordFormat), t: "keyword", j: 0, m: ["p",formatOf(keywordFormat)] }, "size": { "required":false,"values":["xs","sm","md","lg"],"default":"md", c: checkFormat("keyword", keywordFormat), t: "keyword", j: 0, m: ["p",formatOf(keywordFormat)] }, "square": { "required":false,"default":false, c: checkBoolean, t: "boolean", j: 0, m: "b" }, "stretch": { "required":false,"default":false, c: checkBoolean, t: "boolean", j: 0, m: "b" }, "target": { "required":false, c: checkString, t: "string", j: 0, m: "s" }, "tone": { "required":false,"values":["accent","neutral","danger","success","warning","info"],"default":"
|
|
30
|
+
const D = { g: "ui-button", props: { "align": { "required":false,"values":["center","start"],"default":"center", c: checkFormat("keyword", keywordFormat), t: "keyword", j: 0, m: ["p",formatOf(keywordFormat)] }, "as": { "required":false,"values":["button","a"],"default":"button", c: checkFormat("keyword", keywordFormat), t: "keyword", j: 0, m: ["p",formatOf(keywordFormat)] }, "disabled": { "required":false,"default":false, c: checkBoolean, t: "boolean", j: 0, m: "b" }, "href": { "required":false, c: checkString, t: "string", j: 0, m: "s" }, "loading": { "required":false,"default":false, c: checkBoolean, t: "boolean", j: 0, m: "b" }, "pending": { "required":false,"default":false, c: checkBoolean, t: "boolean", j: 0, m: "b" }, "preset": { "required":false,"values":["none","primary","standard","quiet","destructive"],"default":"none", c: checkFormat("keyword", keywordFormat), t: "keyword", j: 0, m: ["p",formatOf(keywordFormat)] }, "rel": { "required":false, c: checkString, t: "string", j: 0, m: "s" }, "shape": { "required":false,"values":["rounded","pill"],"default":"rounded", c: checkFormat("keyword", keywordFormat), t: "keyword", j: 0, m: ["p",formatOf(keywordFormat)] }, "size": { "required":false,"values":["xs","sm","md","lg"],"default":"md", c: checkFormat("keyword", keywordFormat), t: "keyword", j: 0, m: ["p",formatOf(keywordFormat)] }, "square": { "required":false,"default":false, c: checkBoolean, t: "boolean", j: 0, m: "b" }, "stretch": { "required":false,"default":false, c: checkBoolean, t: "boolean", j: 0, m: "b" }, "target": { "required":false, c: checkString, t: "string", j: 0, m: "s" }, "tone": { "required":false,"values":["accent","neutral","danger","success","warning","info"],"default":"neutral", c: checkFormat("keyword", keywordFormat), t: "keyword", j: 0, m: ["p",formatOf(keywordFormat)] }, "type": { "required":false,"values":["button","submit","reset"],"default":"button", c: checkFormat("keyword", keywordFormat), t: "keyword", j: 0, m: ["p",formatOf(keywordFormat)] }, "variant": { "required":false,"values":["outline","solid","danger","ghost","link","card","field"],"default":"outline", c: checkFormat("keyword", keywordFormat), t: "keyword", j: 0, m: ["p",formatOf(keywordFormat)] } } };
|
|
31
31
|
const C = (host) => activateModule(controller, host);
|
|
32
32
|
|
|
33
33
|
export function createUiButton(options = {}, html) {
|
package/vanilla/UiCombobox.d.ts
CHANGED