@threadlabs/looma 0.13.6 → 0.14.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.
Files changed (60) hide show
  1. package/components/ui-button/ui-button.html +2 -1
  2. package/components/ui-checkbox/ui-checkbox.html +35 -0
  3. package/components/ui-combobox/ui-combobox.html +38 -3
  4. package/components/ui-input/ui-input.html +27 -0
  5. package/components/ui-input-group/ui-input-group.html +15 -0
  6. package/components/ui-radio/ui-radio.html +35 -0
  7. package/components/ui-select/ui-select.html +29 -3
  8. package/components/ui-switch/ui-switch.html +36 -0
  9. package/components/ui-textarea/ui-textarea.html +20 -0
  10. package/dist/index.js +1 -1
  11. package/package.json +1 -1
  12. package/styles/ui-button.css +3 -1
  13. package/styles/ui-checkbox.css +38 -0
  14. package/styles/ui-combobox.css +38 -1
  15. package/styles/ui-input-group.css +20 -0
  16. package/styles/ui-input.css +29 -0
  17. package/styles/ui-radio.css +38 -0
  18. package/styles/ui-select.css +29 -3
  19. package/styles/ui-switch.css +41 -0
  20. package/styles/ui-textarea.css +21 -0
  21. package/vanilla/UiCheckbox.d.ts +1 -0
  22. package/vanilla/UiCheckbox.js +1 -1
  23. package/vanilla/UiCombobox.d.ts +1 -1
  24. package/vanilla/UiCombobox.js +1 -1
  25. package/vanilla/UiInput.d.ts +1 -0
  26. package/vanilla/UiInput.js +1 -1
  27. package/vanilla/UiRadio.d.ts +1 -0
  28. package/vanilla/UiRadio.js +1 -1
  29. package/vanilla/UiSelect.d.ts +1 -0
  30. package/vanilla/UiSelect.js +1 -1
  31. package/vanilla/UiSwitch.d.ts +1 -0
  32. package/vanilla/UiSwitch.js +1 -1
  33. package/vanilla/UiTextarea.d.ts +1 -0
  34. package/vanilla/UiTextarea.js +1 -1
  35. package/vue/UiButton.vue +2 -1
  36. package/vue/UiCheckbox.d.ts +2 -0
  37. package/vue/UiCheckbox.vue +34 -0
  38. package/vue/UiCombobox.d.ts +2 -2
  39. package/vue/UiCombobox.vue +39 -2
  40. package/vue/UiInput.d.ts +2 -0
  41. package/vue/UiInput.vue +41 -0
  42. package/vue/UiInputGroup.vue +15 -0
  43. package/vue/UiRadio.d.ts +2 -0
  44. package/vue/UiRadio.vue +34 -0
  45. package/vue/UiSelect.d.ts +2 -0
  46. package/vue/UiSelect.vue +34 -3
  47. package/vue/UiSwitch.d.ts +2 -0
  48. package/vue/UiSwitch.vue +35 -0
  49. package/vue/UiTextarea.d.ts +2 -0
  50. package/vue/UiTextarea.vue +27 -0
  51. package/vue/chunks/UiButton.js +1 -1
  52. package/vue/chunks/UiCheckbox.js +3 -2
  53. package/vue/chunks/UiCombobox.js +1 -1
  54. package/vue/chunks/UiInput.js +7 -2
  55. package/vue/chunks/UiInputGroup.js +1 -1
  56. package/vue/chunks/UiRadio.js +3 -2
  57. package/vue/chunks/UiSelect.js +7 -2
  58. package/vue/chunks/UiSwitch.js +3 -2
  59. package/vue/chunks/UiTextarea.js +7 -2
  60. package/vue/components.css +365 -165
@@ -33,7 +33,7 @@ const props = withDefaults(
33
33
  readonly?: boolean
34
34
  required?: boolean
35
35
  selectedValues?: readonly (string)[]
36
- size?: 'sm' | 'md'
36
+ size?: 'sm' | 'md' | 'lg'
37
37
  tokenSeparators?: readonly (string)[]
38
38
  value?: string | null
39
39
  }>(),
@@ -834,9 +834,18 @@ input {
834
834
  margin: 0;
835
835
  outline: none;
836
836
  }
837
+ [data-component~="ui-combobox"][data-ui-combobox-state~="size=sm"] {
838
+ --_row: var(--ui-control-size-sm);
839
+ }
837
840
  [data-component~="ui-combobox"][data-ui-combobox-state~="size=sm"] input {
838
- min-block-size: calc(var(--ui-control-size-sm) - 2px);
841
+ min-block-size: calc(var(--_row) - 2px);
839
842
  padding: var(--ui-space-1) var(--ui-space-2);
843
+ font-size: var(--ui-font-size-sm);
844
+ }
845
+ [data-component~="ui-combobox"][data-ui-combobox-state~="size=lg"] input {
846
+ min-block-size: calc(var(--ui-control-size-lg) - 2px);
847
+ padding: var(--ui-space-3) var(--ui-space-4);
848
+ font-size: var(--ui-font-size-md);
840
849
  }
841
850
  button {
842
851
  appearance: none;
@@ -937,6 +946,14 @@ input:disabled, button:disabled {
937
946
  min-inline-size: 2.75rem;
938
947
  min-block-size: 2.75rem;
939
948
  }
949
+ /* Under touch a small field is still a touch-sized target, as a small button is, and keeps
950
+ body-size text: below 16px, iOS zooms the page into the focused field. */
951
+ [data-component~="ui-combobox"][data-ui-combobox-state~="size=sm"] {
952
+ --_row: var(--ui-control-min-block-size);
953
+ }
954
+ [data-component~="ui-combobox"][data-ui-combobox-state~="size=sm"] input {
955
+ font-size: inherit;
956
+ }
940
957
  }
941
958
  @media (forced-colors: active) {
942
959
  .field, .popup {
@@ -995,6 +1012,26 @@ input:disabled, button:disabled {
995
1012
  min-block-size: 1.75rem;
996
1013
  padding: var(--ui-space-0-5) var(--ui-space-1);
997
1014
  }
1015
+ /* A small multiple field starts at the small control height; chosen items still wrap it taller. */
1016
+ [data-component~="ui-combobox"][data-ui-combobox-state~="multiple"][data-component~="ui-combobox"][data-ui-combobox-state~="size=sm"]
1017
+ .field {
1018
+ min-block-size: 0;
1019
+ padding: var(--ui-space-0-5);
1020
+ }
1021
+ [data-component~="ui-combobox"][data-ui-combobox-state~="multiple"][data-component~="ui-combobox"][data-ui-combobox-state~="size=sm"]
1022
+ input {
1023
+ min-block-size: calc(var(--_row) - 2px - 2 * var(--ui-space-0-5));
1024
+ padding: 0 var(--ui-space-1);
1025
+ }
1026
+ /* A large multiple field starts at the large control height, as sm starts at the small one. */
1027
+ [data-component~="ui-combobox"][data-ui-combobox-state~="multiple"][data-component~="ui-combobox"][data-ui-combobox-state~="size=lg"]
1028
+ .field {
1029
+ min-block-size: 0;
1030
+ }
1031
+ [data-component~="ui-combobox"][data-ui-combobox-state~="multiple"][data-component~="ui-combobox"][data-ui-combobox-state~="size=lg"]
1032
+ input {
1033
+ min-block-size: calc(var(--ui-control-size-lg) - 2px - 2 * var(--ui-space-1));
1034
+ }
998
1035
  .item {
999
1036
  appearance: none;
1000
1037
  display: inline-flex;
package/vue/UiInput.d.ts CHANGED
@@ -3,6 +3,7 @@ type __VLS_Props = {
3
3
  invalid?: boolean;
4
4
  readonly?: boolean;
5
5
  required?: boolean;
6
+ size?: 'sm' | 'md' | 'lg';
6
7
  value?: string;
7
8
  modelValue?: string | null;
8
9
  };
@@ -12,6 +13,7 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {
12
13
  "onUpdate:modelValue"?: ((value: string) => any) | undefined;
13
14
  }>, {
14
15
  invalid: boolean;
16
+ size: "sm" | "md" | "lg";
15
17
  disabled: boolean;
16
18
  required: boolean;
17
19
  readonly: boolean;
package/vue/UiInput.vue CHANGED
@@ -12,6 +12,7 @@ const props = withDefaults(
12
12
  invalid?: boolean
13
13
  readonly?: boolean
14
14
  required?: boolean
15
+ size?: 'sm' | 'md' | 'lg'
15
16
  value?: string
16
17
  modelValue?: string | null
17
18
  }>(),
@@ -20,6 +21,7 @@ const props = withDefaults(
20
21
  invalid: false,
21
22
  readonly: false,
22
23
  required: false,
24
+ size: 'md',
23
25
  },
24
26
  )
25
27
  const emit = defineEmits<{
@@ -32,6 +34,13 @@ const model = computed({
32
34
 
33
35
  const root = ref<HTMLElement | null>(null)
34
36
 
37
+ /** The values the styles' :host-state() rules test. */
38
+ const hostState = computed(() =>
39
+ [
40
+ props.size && `size size=${props.size}`,
41
+ ].filter(Boolean).join(' ')
42
+ )
43
+
35
44
  const dispatch = createDispatch(root)
36
45
 
37
46
  useComponentHost(controllerModule.default, {
@@ -50,6 +59,7 @@ useComponentHost(controllerModule.default, {
50
59
  :required="required"
51
60
  :aria-invalid="invalid"
52
61
  v-model="model"
62
+ :data-ui-input-state="hostState || undefined"
53
63
  ref="root"
54
64
  >
55
65
  </template>
@@ -138,6 +148,37 @@ useComponentHost(controllerModule.default, {
138
148
  background-color var(--ui-motion-fast) var(--ui-motion-ease);
139
149
  }
140
150
 
151
+ /* Each size reads the hook and then an Input Group's default first, so a group, which draws the frame, can zero it. */
152
+ [data-component~="ui-input"][data-ui-input-state~="size=sm"] {
153
+ min-block-size: var(
154
+ --ui-input-min-block-size,
155
+ var(--_ui-default-input-min-block-size, var(--ui-control-size-sm))
156
+ );
157
+ padding: var(--ui-space-1) var(--ui-space-2);
158
+ font-size: var(--ui-font-size-sm);
159
+ }
160
+
161
+ [data-component~="ui-input"][data-ui-input-state~="size=lg"] {
162
+ min-block-size: var(
163
+ --ui-input-min-block-size,
164
+ var(--_ui-default-input-min-block-size, var(--ui-control-size-lg))
165
+ );
166
+ padding: var(--ui-space-3) var(--ui-space-4);
167
+ font-size: var(--ui-font-size-md);
168
+ }
169
+
170
+ /* Under touch a small field is still a touch-sized target, as a small button is, and keeps
171
+ body-size text: below 16px, iOS zooms the page into the focused field. */
172
+ @media (pointer: coarse) {
173
+ [data-component~="ui-input"][data-ui-input-state~="size=sm"] {
174
+ min-block-size: var(
175
+ --ui-input-min-block-size,
176
+ var(--_ui-default-input-min-block-size, var(--ui-control-min-block-size))
177
+ );
178
+ font-size: inherit;
179
+ }
180
+ }
181
+
141
182
  /* Tabbing into a field selects its text; tint that selection softly instead of the page highlight. */
142
183
  [data-component~="ui-input"]::selection {
143
184
  background: color-mix(in srgb, var(--ui-accent-solid) 20%, transparent);
@@ -91,6 +91,21 @@ useComponentHost(controllerModule.default, {
91
91
  box-shadow var(--ui-motion-fast) var(--ui-motion-ease);
92
92
  }
93
93
 
94
+ /* The frame takes its input's size, so a small group lines up with small buttons and fields. */
95
+ [data-component~="ui-input-group"]:has(.field [data-ui-input-state~="size=sm"]) {
96
+ min-block-size: var(--ui-control-size-sm);
97
+ }
98
+
99
+ [data-component~="ui-input-group"]:has(.field [data-ui-input-state~="size=lg"]) {
100
+ min-block-size: var(--ui-control-size-lg);
101
+ }
102
+
103
+ @media (pointer: coarse) {
104
+ [data-component~="ui-input-group"]:has(.field [data-ui-input-state~="size=sm"]) {
105
+ min-block-size: var(--ui-control-min-block-size);
106
+ }
107
+ }
108
+
94
109
  [data-component~="ui-input-group"]:hover:not(:has(input:focus, input:disabled)) {
95
110
  border-color: var(--ui-control-border-hover, var(--ui-text-secondary));
96
111
  }
package/vue/UiRadio.d.ts CHANGED
@@ -3,6 +3,7 @@ type __VLS_Props = {
3
3
  disabled?: boolean;
4
4
  name?: string;
5
5
  required?: boolean;
6
+ size?: 'sm' | 'md' | 'lg';
6
7
  value?: string;
7
8
  };
8
9
  declare var __VLS_1: {}, __VLS_3: {};
@@ -29,6 +30,7 @@ declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {}, {}, {},
29
30
  "onUpdate:value"?: ((value: string) => any) | undefined;
30
31
  }>, {
31
32
  name: string;
33
+ size: "sm" | "md" | "lg";
32
34
  value: string;
33
35
  disabled: boolean;
34
36
  checked: boolean;
package/vue/UiRadio.vue CHANGED
@@ -12,6 +12,7 @@ const props = withDefaults(
12
12
  disabled?: boolean
13
13
  name?: string
14
14
  required?: boolean
15
+ size?: 'sm' | 'md' | 'lg'
15
16
  value?: string
16
17
  }>(),
17
18
  {
@@ -19,6 +20,7 @@ const props = withDefaults(
19
20
  disabled: false,
20
21
  name: '',
21
22
  required: false,
23
+ size: 'md',
22
24
  value: 'on',
23
25
  },
24
26
  )
@@ -43,6 +45,7 @@ const internalChecked = ref(false)
43
45
  const hostState = computed(() =>
44
46
  [
45
47
  props.disabled && 'disabled',
48
+ props.size && `size size=${props.size}`,
46
49
  ].filter(Boolean).join(' ')
47
50
  )
48
51
 
@@ -159,6 +162,37 @@ input:focus-visible {
159
162
  cursor: not-allowed;
160
163
  }
161
164
 
165
+ /* A sized choice keeps its box and aligns instead: the label takes that size's field type size,
166
+ and the first line centres in that control height, so the label shares a row's baseline.
167
+ Multi-line labels and descriptions still grow downward from that first line. */
168
+ [data-component~="ui-radio"][data-ui-radio-state~="size=sm"] {
169
+ --_font: var(--ui-font-size-sm);
170
+ --_row: var(--ui-control-size-sm);
171
+ }
172
+
173
+ [data-component~="ui-radio"][data-ui-radio-state~="size=lg"] {
174
+ --_font: var(--ui-font-size-md);
175
+ --_row: var(--ui-control-size-lg);
176
+ }
177
+
178
+ [data-component~="ui-radio"][data-ui-radio-state~="size=sm"],
179
+ [data-component~="ui-radio"][data-ui-radio-state~="size=lg"] {
180
+ --_line: calc(var(--_font) * var(--ui-line-height-md));
181
+ font-size: var(--_font);
182
+ padding-block: calc((var(--_row) - var(--_line)) / 2);
183
+ }
184
+
185
+ [data-component~="ui-radio"][data-ui-radio-state~="size=sm"] input,
186
+ [data-component~="ui-radio"][data-ui-radio-state~="size=lg"] input {
187
+ margin-top: calc((var(--_line) - var(--_ui-radio-size, 1.125rem)) / 2);
188
+ }
189
+
190
+ @media (pointer: coarse) {
191
+ [data-component~="ui-radio"][data-ui-radio-state~="size=sm"] {
192
+ --_row: var(--ui-control-min-block-size);
193
+ }
194
+ }
195
+
162
196
  /* A component that sets its display still honours the hidden attribute on its root. */
163
197
  [data-component~="ui-radio"][hidden] {
164
198
  display: none;
package/vue/UiSelect.d.ts CHANGED
@@ -2,6 +2,7 @@ type __VLS_Props = {
2
2
  disabled?: boolean;
3
3
  invalid?: boolean;
4
4
  required?: boolean;
5
+ size?: 'sm' | 'md' | 'lg';
5
6
  value?: string;
6
7
  modelValue?: string | null;
7
8
  };
@@ -15,6 +16,7 @@ declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {}, {}, {},
15
16
  "onUpdate:modelValue"?: ((value: string) => any) | undefined;
16
17
  }>, {
17
18
  invalid: boolean;
19
+ size: "sm" | "md" | "lg";
18
20
  disabled: boolean;
19
21
  required: boolean;
20
22
  }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
package/vue/UiSelect.vue CHANGED
@@ -11,6 +11,7 @@ const props = withDefaults(
11
11
  disabled?: boolean
12
12
  invalid?: boolean
13
13
  required?: boolean
14
+ size?: 'sm' | 'md' | 'lg'
14
15
  value?: string
15
16
  modelValue?: string | null
16
17
  }>(),
@@ -18,6 +19,7 @@ const props = withDefaults(
18
19
  disabled: false,
19
20
  invalid: false,
20
21
  required: false,
22
+ size: 'md',
21
23
  },
22
24
  )
23
25
  const emit = defineEmits<{
@@ -30,6 +32,13 @@ const model = computed({
30
32
 
31
33
  const root = ref<HTMLElement | null>(null)
32
34
 
35
+ /** The values the styles' :host-state() rules test. */
36
+ const hostState = computed(() =>
37
+ [
38
+ props.size && `size size=${props.size}`,
39
+ ].filter(Boolean).join(' ')
40
+ )
41
+
33
42
  const dispatch = createDispatch(root)
34
43
 
35
44
  useComponentHost(controllerModule.default, {
@@ -47,6 +56,7 @@ useComponentHost(controllerModule.default, {
47
56
  :required="required"
48
57
  :aria-invalid="invalid"
49
58
  v-model="model"
59
+ :data-ui-select-state="hostState || undefined"
50
60
  ref="root"
51
61
  >
52
62
  <slot />
@@ -137,9 +147,30 @@ useComponentHost(controllerModule.default, {
137
147
  cursor: not-allowed;
138
148
  }
139
149
 
140
- [data-component~="ui-select"][size]:not([size="1"]) {
141
- min-block-size: 7rem;
142
- padding: var(--ui-space-2);
150
+ [data-component~="ui-select"][data-ui-select-state~="size=sm"] {
151
+ min-block-size: var(--ui-control-size-sm);
152
+ padding: var(--ui-space-1) var(--ui-space-2);
153
+ padding-inline-end: calc(var(--ui-space-2) + var(--ui-icon-size-sm) + var(--ui-space-1));
154
+ background-position: right var(--ui-space-2) center;
155
+ background-size: var(--ui-icon-size-sm) var(--ui-icon-size-sm);
156
+ font-size: var(--ui-font-size-sm);
157
+ }
158
+
159
+ [data-component~="ui-select"][data-ui-select-state~="size=lg"] {
160
+ min-block-size: var(--ui-control-size-lg);
161
+ padding: var(--ui-space-3) var(--ui-space-4);
162
+ padding-inline-end: calc(var(--ui-space-4) + 1.5rem);
163
+ background-position: right var(--ui-space-4) center;
164
+ font-size: var(--ui-font-size-md);
165
+ }
166
+
167
+ /* Under touch a small field is still a touch-sized target, as a small button is, and keeps
168
+ body-size text: below 16px, iOS zooms the page into the focused field. */
169
+ @media (pointer: coarse) {
170
+ [data-component~="ui-select"][data-ui-select-state~="size=sm"] {
171
+ min-block-size: var(--ui-control-min-block-size);
172
+ font-size: inherit;
173
+ }
143
174
  }
144
175
 
145
176
  /* A component that sets its display still honours the hidden attribute on its root. */
package/vue/UiSwitch.d.ts CHANGED
@@ -3,6 +3,7 @@ type __VLS_Props = {
3
3
  disabled?: boolean;
4
4
  name?: string;
5
5
  required?: boolean;
6
+ size?: 'sm' | 'md' | 'lg';
6
7
  value?: string;
7
8
  };
8
9
  declare var __VLS_1: {}, __VLS_3: {};
@@ -29,6 +30,7 @@ declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {}, {}, {},
29
30
  "onUpdate:value"?: ((value: string) => any) | undefined;
30
31
  }>, {
31
32
  name: string;
33
+ size: "sm" | "md" | "lg";
32
34
  value: string;
33
35
  disabled: boolean;
34
36
  checked: boolean;
package/vue/UiSwitch.vue CHANGED
@@ -12,6 +12,7 @@ const props = withDefaults(
12
12
  disabled?: boolean
13
13
  name?: string
14
14
  required?: boolean
15
+ size?: 'sm' | 'md' | 'lg'
15
16
  value?: string
16
17
  }>(),
17
18
  {
@@ -19,6 +20,7 @@ const props = withDefaults(
19
20
  disabled: false,
20
21
  name: '',
21
22
  required: false,
23
+ size: 'md',
22
24
  value: 'on',
23
25
  },
24
26
  )
@@ -43,6 +45,7 @@ const internalChecked = ref(false)
43
45
  const hostState = computed(() =>
44
46
  [
45
47
  props.disabled && 'disabled',
48
+ props.size && `size size=${props.size}`,
46
49
  ].filter(Boolean).join(' ')
47
50
  )
48
51
 
@@ -206,6 +209,38 @@ input:focus-visible {
206
209
  }
207
210
  }
208
211
 
212
+ /* A sized switch keeps its track and aligns instead: the label takes that size's field type size,
213
+ and the first line centres in that control height, so the label shares a row's baseline.
214
+ Multi-line labels and descriptions still grow downward from that first line. */
215
+ [data-component~="ui-switch"][data-ui-switch-state~="size=sm"] {
216
+ --_font: var(--ui-font-size-sm);
217
+ --_row: var(--ui-control-size-sm);
218
+ }
219
+
220
+ [data-component~="ui-switch"][data-ui-switch-state~="size=lg"] {
221
+ --_font: var(--ui-font-size-md);
222
+ --_row: var(--ui-control-size-lg);
223
+ }
224
+
225
+ [data-component~="ui-switch"][data-ui-switch-state~="size=sm"],
226
+ [data-component~="ui-switch"][data-ui-switch-state~="size=lg"] {
227
+ --_line: calc(var(--_font) * var(--ui-line-height-md));
228
+ font-size: var(--_font);
229
+ padding-block: calc((var(--_row) - var(--ui-switch-track-height, 1.5rem)) / 2);
230
+ }
231
+
232
+ /* The label centres on the track. */
233
+ [data-component~="ui-switch"][data-ui-switch-state~="size=sm"] .text,
234
+ [data-component~="ui-switch"][data-ui-switch-state~="size=lg"] .text {
235
+ margin-top: calc((var(--_ui-switch-track-height, 1.5rem) - var(--_line)) / 2);
236
+ }
237
+
238
+ @media (pointer: coarse) {
239
+ [data-component~="ui-switch"][data-ui-switch-state~="size=sm"] {
240
+ --_row: var(--ui-control-min-block-size);
241
+ }
242
+ }
243
+
209
244
  /* A component that sets its display still honours the hidden attribute on its root. */
210
245
  [data-component~="ui-switch"][hidden] {
211
246
  display: none;
@@ -4,6 +4,7 @@ type __VLS_Props = {
4
4
  readonly?: boolean;
5
5
  required?: boolean;
6
6
  rows?: number;
7
+ size?: 'sm' | 'md' | 'lg';
7
8
  value?: string;
8
9
  modelValue?: string | null;
9
10
  };
@@ -13,6 +14,7 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {
13
14
  "onUpdate:modelValue"?: ((value: string) => any) | undefined;
14
15
  }>, {
15
16
  invalid: boolean;
17
+ size: "sm" | "md" | "lg";
16
18
  disabled: boolean;
17
19
  required: boolean;
18
20
  readonly: boolean;
@@ -13,6 +13,7 @@ const props = withDefaults(
13
13
  readonly?: boolean
14
14
  required?: boolean
15
15
  rows?: number
16
+ size?: 'sm' | 'md' | 'lg'
16
17
  value?: string
17
18
  modelValue?: string | null
18
19
  }>(),
@@ -22,6 +23,7 @@ const props = withDefaults(
22
23
  readonly: false,
23
24
  required: false,
24
25
  rows: 4,
26
+ size: 'md',
25
27
  },
26
28
  )
27
29
  const emit = defineEmits<{
@@ -34,6 +36,13 @@ const model = computed({
34
36
 
35
37
  const root = ref<HTMLElement | null>(null)
36
38
 
39
+ /** The values the styles' :host-state() rules test. */
40
+ const hostState = computed(() =>
41
+ [
42
+ props.size && `size size=${props.size}`,
43
+ ].filter(Boolean).join(' ')
44
+ )
45
+
37
46
  const dispatch = createDispatch(root)
38
47
 
39
48
  useComponentHost(controllerModule.default, {
@@ -53,6 +62,7 @@ useComponentHost(controllerModule.default, {
53
62
  :aria-invalid="invalid"
54
63
  :rows="rows"
55
64
  v-model="model"
65
+ :data-ui-textarea-state="hostState || undefined"
56
66
  ref="root"
57
67
  ></textarea>
58
68
  </template>
@@ -139,6 +149,23 @@ useComponentHost(controllerModule.default, {
139
149
  cursor: not-allowed;
140
150
  }
141
151
 
152
+ [data-component~="ui-textarea"][data-ui-textarea-state~="size=sm"] {
153
+ padding: var(--ui-space-1) var(--ui-space-2);
154
+ font-size: var(--ui-font-size-sm);
155
+ }
156
+
157
+ [data-component~="ui-textarea"][data-ui-textarea-state~="size=lg"] {
158
+ padding: var(--ui-space-3) var(--ui-space-4);
159
+ font-size: var(--ui-font-size-md);
160
+ }
161
+
162
+ /* Below 16px, iOS zooms the page into the focused field, so touch keeps body-size text. */
163
+ @media (pointer: coarse) {
164
+ [data-component~="ui-textarea"][data-ui-textarea-state~="size=sm"] {
165
+ font-size: inherit;
166
+ }
167
+ }
168
+
142
169
  /* A component that sets its display still honours the hidden attribute on its root. */
143
170
  [data-component~="ui-textarea"][hidden] {
144
171
  display: none;
@@ -87,6 +87,6 @@ var UiButton_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PUR
87
87
  }), [renderSlot(_ctx.$slots, "default", {}, void 0, true)], 16, _hoisted_2));
88
88
  };
89
89
  }
90
- }), [["__scopeId", "data-v-a45835c7"]]);
90
+ }), [["__scopeId", "data-v-bd32a08d"]]);
91
91
  //#endregion
92
92
  export { UiButton_default as t };
@@ -42,6 +42,7 @@ var UiCheckbox_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__P
42
42
  type: Boolean,
43
43
  default: false
44
44
  },
45
+ size: { default: "md" },
45
46
  value: { default: "on" }
46
47
  },
47
48
  emits: [
@@ -57,7 +58,7 @@ var UiCheckbox_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__P
57
58
  const descriptionElement = useTemplateRef("description");
58
59
  const internalChecked = ref(false);
59
60
  /** The values the styles' :host-state() rules test. */
60
- const hostState = computed(() => [props.disabled && "disabled"].filter(Boolean).join(" "));
61
+ const hostState = computed(() => [props.disabled && "disabled", props.size && `size size=${props.size}`].filter(Boolean).join(" "));
61
62
  const isChangeDetail = (detail) => detail !== null && typeof detail === "object" && !Array.isArray(detail) && typeof detail["checked"] === "boolean" && typeof detail["value"] === "string" && (detail["trigger"] === "keyboard" || detail["trigger"] === "pointer" || detail["trigger"] === "programmatic") && Object.keys(detail).every((key) => [
62
63
  "checked",
63
64
  "value",
@@ -94,6 +95,6 @@ var UiCheckbox_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__P
94
95
  }, null, 40, _hoisted_2), createElementVNode("span", _hoisted_3, [createElementVNode("span", _hoisted_4, [renderSlot(_ctx.$slots, "default", {}, void 0, true)]), createElementVNode("span", _hoisted_5, [renderSlot(_ctx.$slots, "description", {}, void 0, true)], 512)])], 16, _hoisted_1);
95
96
  };
96
97
  }
97
- }), [["__scopeId", "data-v-e122df08"]]);
98
+ }), [["__scopeId", "data-v-bfcc98e1"]]);
98
99
  //#endregion
99
100
  export { UiCheckbox_default as t };
@@ -488,6 +488,6 @@ var UiCombobox_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__P
488
488
  ], 16, _hoisted_1);
489
489
  };
490
490
  }
491
- }), [["__scopeId", "data-v-1482f333"]]);
491
+ }), [["__scopeId", "data-v-fd4a871f"]]);
492
492
  //#endregion
493
493
  export { UiCombobox_default as t };
@@ -7,7 +7,8 @@ var _hoisted_1 = [
7
7
  "disabled",
8
8
  "readonly",
9
9
  "required",
10
- "aria-invalid"
10
+ "aria-invalid",
11
+ "data-ui-input-state"
11
12
  ];
12
13
  //#endregion
13
14
  //#region .build/vue/UiInput.vue
@@ -31,6 +32,7 @@ var UiInput_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PURE
31
32
  type: Boolean,
32
33
  default: false
33
34
  },
35
+ size: { default: "md" },
34
36
  value: {},
35
37
  modelValue: {}
36
38
  },
@@ -43,6 +45,8 @@ var UiInput_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PURE
43
45
  set: (value) => emit("update:modelValue", value)
44
46
  });
45
47
  const root = ref(null);
48
+ /** The values the styles' :host-state() rules test. */
49
+ const hostState = computed(() => [props.size && `size size=${props.size}`].filter(Boolean).join(" "));
46
50
  const dispatch = createDispatch(root);
47
51
  useComponentHost(controllerModule.default, {
48
52
  root,
@@ -56,11 +60,12 @@ var UiInput_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PURE
56
60
  required: __props.required,
57
61
  "aria-invalid": __props.invalid,
58
62
  "onUpdate:modelValue": _cache[0] || (_cache[0] = ($event) => model.value = $event),
63
+ "data-ui-input-state": hostState.value || void 0,
59
64
  ref_key: "root",
60
65
  ref: root
61
66
  }), null, 16, _hoisted_1)), [[vModelDynamic, model.value]]);
62
67
  };
63
68
  }
64
- }), [["__scopeId", "data-v-e1edefb2"]]);
69
+ }), [["__scopeId", "data-v-4ba095d3"]]);
65
70
  //#endregion
66
71
  export { UiInput_default as t };
@@ -41,6 +41,6 @@ var UiInputGroup_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @_
41
41
  ], 16, _hoisted_1);
42
42
  };
43
43
  }
44
- }), [["__scopeId", "data-v-d57b2ead"]]);
44
+ }), [["__scopeId", "data-v-26a5b56d"]]);
45
45
  //#endregion
46
46
  export { UiInputGroup_default as t };
@@ -37,6 +37,7 @@ var UiRadio_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PURE
37
37
  type: Boolean,
38
38
  default: false
39
39
  },
40
+ size: { default: "md" },
40
41
  value: { default: "on" }
41
42
  },
42
43
  emits: [
@@ -52,7 +53,7 @@ var UiRadio_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PURE
52
53
  const descriptionElement = useTemplateRef("description");
53
54
  const internalChecked = ref(false);
54
55
  /** The values the styles' :host-state() rules test. */
55
- const hostState = computed(() => [props.disabled && "disabled"].filter(Boolean).join(" "));
56
+ const hostState = computed(() => [props.disabled && "disabled", props.size && `size size=${props.size}`].filter(Boolean).join(" "));
56
57
  const isChangeDetail = (detail) => detail !== null && typeof detail === "object" && !Array.isArray(detail) && typeof detail["checked"] === "boolean" && typeof detail["value"] === "string" && (detail["trigger"] === "keyboard" || detail["trigger"] === "pointer" || detail["trigger"] === "programmatic") && Object.keys(detail).every((key) => [
57
58
  "checked",
58
59
  "value",
@@ -88,6 +89,6 @@ var UiRadio_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PURE
88
89
  }, null, 40, _hoisted_2), createElementVNode("span", _hoisted_3, [createElementVNode("span", _hoisted_4, [renderSlot(_ctx.$slots, "default", {}, void 0, true)]), createElementVNode("span", _hoisted_5, [renderSlot(_ctx.$slots, "description", {}, void 0, true)], 512)])], 16, _hoisted_1);
89
90
  };
90
91
  }
91
- }), [["__scopeId", "data-v-293c00d6"]]);
92
+ }), [["__scopeId", "data-v-02111ebf"]]);
92
93
  //#endregion
93
94
  export { UiRadio_default as t };
@@ -6,7 +6,8 @@ import * as controllerModule from "@threadlabs/looma/components/ui-select/ui-sel
6
6
  var _hoisted_1 = [
7
7
  "disabled",
8
8
  "required",
9
- "aria-invalid"
9
+ "aria-invalid",
10
+ "data-ui-select-state"
10
11
  ];
11
12
  //#endregion
12
13
  //#region .build/vue/UiSelect.vue
@@ -26,6 +27,7 @@ var UiSelect_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PUR
26
27
  type: Boolean,
27
28
  default: false
28
29
  },
30
+ size: { default: "md" },
29
31
  value: {},
30
32
  modelValue: {}
31
33
  },
@@ -38,6 +40,8 @@ var UiSelect_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PUR
38
40
  set: (value) => emit("update:modelValue", value)
39
41
  });
40
42
  const root = ref(null);
43
+ /** The values the styles' :host-state() rules test. */
44
+ const hostState = computed(() => [props.size && `size size=${props.size}`].filter(Boolean).join(" "));
41
45
  const dispatch = createDispatch(root);
42
46
  useComponentHost(controllerModule.default, {
43
47
  root,
@@ -50,11 +54,12 @@ var UiSelect_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PUR
50
54
  required: __props.required,
51
55
  "aria-invalid": __props.invalid,
52
56
  "onUpdate:modelValue": _cache[0] || (_cache[0] = ($event) => model.value = $event),
57
+ "data-ui-select-state": hostState.value || void 0,
53
58
  ref_key: "root",
54
59
  ref: root
55
60
  }), [renderSlot(_ctx.$slots, "default", {}, void 0, true)], 16, _hoisted_1)), [[vModelSelect, model.value]]);
56
61
  };
57
62
  }
58
- }), [["__scopeId", "data-v-57618b8b"]]);
63
+ }), [["__scopeId", "data-v-c81ec3d9"]]);
59
64
  //#endregion
60
65
  export { UiSelect_default as t };