@threadlabs/looma 0.11.3 → 0.11.5

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@threadlabs/looma",
3
- "version": "0.11.3",
3
+ "version": "0.11.5",
4
4
  "type": "module",
5
5
  "description": "Looma components as HTML, Vue, and plain DOM, with design tokens.",
6
6
  "license": "MIT",
@@ -222,6 +222,22 @@
222
222
  }
223
223
  }
224
224
 
225
+ /* Under touch, every button's press lands within the control minimum: an invisible hit area,
226
+ centred on it, like IconButton's. A standalone link-style button (a "See all" beside a
227
+ heading) is a target of its own, not text, so it gets one too. */
228
+ html[data-ui-input-modality="touch"] :scope {
229
+ position: relative;
230
+ }
231
+
232
+ html[data-ui-input-modality="touch"] :scope::after {
233
+ position: absolute;
234
+ inset: 50% auto auto 50%;
235
+ inline-size: max(100%, var(--ui-control-min-block-size));
236
+ block-size: max(100%, var(--ui-control-min-block-size));
237
+ transform: translate(-50%, -50%);
238
+ content: "";
239
+ }
240
+
225
241
  /* Link: an inline text action. No box, padding, or minimum height (inline targets are exempt under
226
242
  WCAG 2.5.8); the inherited font; an underline that strengthens on hover; a visible focus ring. */
227
243
  :scope[data-ui-button-state~="variant=link"] {
@@ -302,6 +318,10 @@
302
318
  @media (pointer: coarse) {
303
319
  }
304
320
 
321
+ /* Under touch, every button's press lands within the control minimum: an invisible hit area,
322
+ centred on it, like IconButton's. A standalone link-style button (a "See all" beside a
323
+ heading) is a target of its own, not text, so it gets one too. */
324
+
305
325
  /* Link: an inline text action. No box, padding, or minimum height (inline targets are exempt under
306
326
  WCAG 2.5.8); the inherited font; an underline that strengthens on hover; a visible focus ring. */
307
327
 
@@ -64,6 +64,8 @@
64
64
  --_callout-icon: var(--ui-danger-solid);
65
65
  }
66
66
 
67
+ /* A link in the callout's text is underlined, so it reads as a link by more than colour. */
68
+
67
69
  /* A component that sets its display still honours the hidden attribute on its root. */
68
70
  :scope[hidden] {
69
71
  display: none;
@@ -72,5 +74,13 @@
72
74
  @scope ([data-component~="ui-callout"]) to ([data-component]) {
73
75
  /* One line box tall, so the icon centres on the first line of text. */
74
76
 
77
+ /* A link in the callout's text is underlined, so it reads as a link by more than colour. */
78
+ .content :where([data-slotted], [data-slotted] *):is(a) {
79
+ color: var(--ui-accent-active);
80
+ text-decoration: underline;
81
+ text-decoration-thickness: 0.06em;
82
+ text-underline-offset: 0.18em;
83
+ }
84
+
75
85
  /* A component that sets its display still honours the hidden attribute on its root. */
76
86
  }
@@ -27,6 +27,8 @@
27
27
  line-height: var(--ui-line-height-md);
28
28
  }
29
29
 
30
+ /* An error reads in the danger colour, the one the invalid control's border takes. */
31
+
30
32
  /* A component that sets its display still honours the hidden attribute on its root. */
31
33
  :scope[hidden] {
32
34
  display: none;
@@ -40,9 +42,10 @@
40
42
  min-block-size: var(--ui-form-field-help-min-block-size, 0);
41
43
  }
42
44
 
45
+ /* An error reads in the danger colour, the one the invalid control's border takes. */
43
46
  .error,
44
47
  :where([data-slotted], [data-slotted] *):is([role="alert"]) {
45
- color: var(--ui-accent-active);
48
+ color: var(--ui-danger);
46
49
  font-size: var(--ui-font-size-sm);
47
50
  }
48
51
 
@@ -10,6 +10,8 @@
10
10
  --ui-input-border: transparent;
11
11
  --ui-input-border-hover: transparent;
12
12
  --ui-input-focus-border: transparent;
13
+ --ui-input-invalid-border: transparent;
14
+ --ui-input-invalid-focus-shadow: none;
13
15
  --ui-input-shadow: none;
14
16
  --ui-input-focus-shadow: none;
15
17
  --ui-input-radius: 0;
@@ -46,13 +46,15 @@
46
46
  /* The app's invalid flag, or the browser's own verdict once the user has left an invalid field. */
47
47
  :scope[aria-invalid="true"],
48
48
  :scope:is(:user-invalid, [data-user-invalid]) {
49
- border-color: var(--ui-danger-solid);
49
+ border-color: var(--ui-input-invalid-border, var(--ui-danger-solid));
50
50
  }
51
51
 
52
52
  :scope:is([aria-invalid="true"], :is(:user-invalid, [data-user-invalid])):focus-visible {
53
- box-shadow:
53
+ box-shadow: var(
54
+ --ui-input-invalid-focus-shadow,
54
55
  var(--ui-control-inset),
55
- 0 0 0 3px color-mix(in srgb, var(--ui-danger-solid) 22%, transparent);
56
+ 0 0 0 3px color-mix(in srgb, var(--ui-danger-solid) 22%, transparent)
57
+ );
56
58
  }
57
59
 
58
60
  :scope:disabled {
@@ -62,6 +62,9 @@
62
62
  font-family: var(--ui-font-family-mono);
63
63
  }
64
64
 
65
+ /* A link in running text is underlined, so it stands apart from the words around it by more than
66
+ colour. */
67
+
65
68
  :scope[data-ui-text-state~="truncate"] {
66
69
  overflow: hidden;
67
70
  text-overflow: ellipsis;
@@ -81,5 +84,14 @@
81
84
  /* Running text, set on the type scale and in a tone that says what it is, so a view never styles
82
85
  text of its own. The root is a paragraph unless as= names a span, div, small, strong, or label. */
83
86
 
87
+ /* A link in running text is underlined, so it stands apart from the words around it by more than
88
+ colour. */
89
+ :scope :where([data-slotted], [data-slotted] *):is(a) {
90
+ color: var(--ui-accent-active);
91
+ text-decoration: underline;
92
+ text-decoration-thickness: 0.06em;
93
+ text-underline-offset: 0.18em;
94
+ }
95
+
84
96
  /* A component that sets its display still honours the hidden attribute on its root. */
85
97
  }
package/vue/UiButton.vue CHANGED
@@ -299,6 +299,22 @@ function attribute(value: unknown): any {
299
299
  }
300
300
  }
301
301
 
302
+ /* Under touch, every button's press lands within the control minimum: an invisible hit area,
303
+ centred on it, like IconButton's. A standalone link-style button (a "See all" beside a
304
+ heading) is a target of its own, not text, so it gets one too. */
305
+ html[data-ui-input-modality="touch"] [data-component~="ui-button"] {
306
+ position: relative;
307
+ }
308
+
309
+ html[data-ui-input-modality="touch"] [data-component~="ui-button"]::after {
310
+ position: absolute;
311
+ inset: 50% auto auto 50%;
312
+ inline-size: max(100%, var(--ui-control-min-block-size));
313
+ block-size: max(100%, var(--ui-control-min-block-size));
314
+ transform: translate(-50%, -50%);
315
+ content: "";
316
+ }
317
+
302
318
  /* Link: an inline text action. No box, padding, or minimum height (inline targets are exempt under
303
319
  WCAG 2.5.8); the inherited font; an underline that strengthens on hover; a visible focus ring. */
304
320
  [data-component~="ui-button"][data-ui-button-state~="variant=link"] {
package/vue/UiCallout.vue CHANGED
@@ -164,6 +164,14 @@ const hostState = computed(() =>
164
164
  --_callout-icon: var(--ui-danger-solid);
165
165
  }
166
166
 
167
+ /* A link in the callout's text is underlined, so it reads as a link by more than colour. */
168
+ .content :slotted(a) {
169
+ color: var(--ui-accent-active);
170
+ text-decoration: underline;
171
+ text-decoration-thickness: 0.06em;
172
+ text-underline-offset: 0.18em;
173
+ }
174
+
167
175
  /* A component that sets its display still honours the hidden attribute on its root. */
168
176
  [data-component~="ui-callout"][hidden] {
169
177
  display: none;
@@ -71,9 +71,10 @@ useComponentHost(controllerModule.default, {
71
71
  min-block-size: var(--ui-form-field-help-min-block-size, 0);
72
72
  }
73
73
 
74
+ /* An error reads in the danger colour, the one the invalid control's border takes. */
74
75
  .error,
75
76
  :slotted([role="alert"]) {
76
- color: var(--ui-accent-active);
77
+ color: var(--ui-danger);
77
78
  font-size: var(--ui-font-size-sm);
78
79
  }
79
80
 
package/vue/UiInput.vue CHANGED
@@ -108,13 +108,15 @@ useComponentHost(controllerModule.default, {
108
108
  /* The app's invalid flag, or the browser's own verdict once the user has left an invalid field. */
109
109
  [data-component~="ui-input"][aria-invalid="true"],
110
110
  [data-component~="ui-input"]:is(:user-invalid, [data-user-invalid]) {
111
- border-color: var(--ui-danger-solid);
111
+ border-color: var(--ui-input-invalid-border, var(--ui-danger-solid));
112
112
  }
113
113
 
114
114
  [data-component~="ui-input"]:is([aria-invalid="true"], :is(:user-invalid, [data-user-invalid])):focus-visible {
115
- box-shadow:
115
+ box-shadow: var(
116
+ --ui-input-invalid-focus-shadow,
116
117
  var(--ui-control-inset),
117
- 0 0 0 3px color-mix(in srgb, var(--ui-danger-solid) 22%, transparent);
118
+ 0 0 0 3px color-mix(in srgb, var(--ui-danger-solid) 22%, transparent)
119
+ );
118
120
  }
119
121
 
120
122
  [data-component~="ui-input"]:disabled {
@@ -44,6 +44,8 @@ const hostState = computed(() =>
44
44
  --ui-input-border: transparent;
45
45
  --ui-input-border-hover: transparent;
46
46
  --ui-input-focus-border: transparent;
47
+ --ui-input-invalid-border: transparent;
48
+ --ui-input-invalid-focus-shadow: none;
47
49
  --ui-input-shadow: none;
48
50
  --ui-input-focus-shadow: none;
49
51
  --ui-input-radius: 0;
package/vue/UiText.vue CHANGED
@@ -108,6 +108,15 @@ const hostState = computed(() =>
108
108
  font-family: var(--ui-font-family-mono);
109
109
  }
110
110
 
111
+ /* A link in running text is underlined, so it stands apart from the words around it by more than
112
+ colour. */
113
+ [data-component~="ui-text"] :slotted(a) {
114
+ color: var(--ui-accent-active);
115
+ text-decoration: underline;
116
+ text-decoration-thickness: 0.06em;
117
+ text-underline-offset: 0.18em;
118
+ }
119
+
111
120
  [data-component~="ui-text"][data-ui-text-state~="truncate"] {
112
121
  overflow: hidden;
113
122
  text-overflow: ellipsis;
@@ -83,6 +83,6 @@ var UiButton_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PUR
83
83
  ]);
84
84
  };
85
85
  }
86
- }), [["__scopeId", "data-v-0c121308"]]);
86
+ }), [["__scopeId", "data-v-6c8750a9"]]);
87
87
  //#endregion
88
88
  export { UiButton_default as t };
@@ -100,9 +100,9 @@ var UiCallout_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PU
100
100
  createElementVNode("path", { d: "m15 9-6 6" }, null, -1),
101
101
  createElementVNode("path", { d: "m9 9 6 6" }, null, -1)
102
102
  ])])) : createCommentVNode("v-if", true)
103
- ]), createElementVNode("div", _hoisted_8, [renderSlot(_ctx.$slots, "default", {}, void 0, true)])], 16, _hoisted_1);
103
+ ]), createElementVNode("div", _hoisted_8, [renderSlot(_ctx.$slots, "default")])], 16, _hoisted_1);
104
104
  };
105
105
  }
106
- }), [["__scopeId", "data-v-2fd6ab30"]]);
106
+ }), [["__scopeId", "data-v-36fb7e55"]]);
107
107
  //#endregion
108
108
  export { UiCallout_default as t };
@@ -46,6 +46,6 @@ var UiFormField_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__
46
46
  ], 16);
47
47
  };
48
48
  }
49
- }), [["__scopeId", "data-v-eba1e76b"]]);
49
+ }), [["__scopeId", "data-v-4df6c2a6"]]);
50
50
  //#endregion
51
51
  export { UiFormField_default as t };
@@ -61,6 +61,6 @@ var UiInput_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PURE
61
61
  }), null, 16, _hoisted_1)), [[vModelDynamic, model.value]]);
62
62
  };
63
63
  }
64
- }), [["__scopeId", "data-v-12ffae5a"]]);
64
+ }), [["__scopeId", "data-v-c19f8322"]]);
65
65
  //#endregion
66
66
  export { UiInput_default as t };
@@ -26,6 +26,6 @@ var UiInputGroup_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @_
26
26
  ], 16, _hoisted_1);
27
27
  };
28
28
  }
29
- }), [["__scopeId", "data-v-0a135fc1"]]);
29
+ }), [["__scopeId", "data-v-42ff173c"]]);
30
30
  //#endregion
31
31
  export { UiInputGroup_default as t };
@@ -30,11 +30,11 @@ var UiText_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PURE_
30
30
  ].filter(Boolean).join(" "));
31
31
  return (_ctx, _cache) => {
32
32
  return openBlock(), createBlock(resolveDynamicComponent(__props.as), mergeProps({ "data-component": "ui-text" }, _ctx.$attrs, { "data-ui-text-state": hostState.value || void 0 }), {
33
- default: withCtx(() => [renderSlot(_ctx.$slots, "default", {}, void 0, true)]),
33
+ default: withCtx(() => [renderSlot(_ctx.$slots, "default")]),
34
34
  _: 3
35
35
  }, 16, ["data-ui-text-state"]);
36
36
  };
37
37
  }
38
- }), [["__scopeId", "data-v-70a75450"]]);
38
+ }), [["__scopeId", "data-v-b83a3ab9"]]);
39
39
  //#endregion
40
40
  export { UiText_default as t };