@threadlabs/looma 0.11.4 → 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.4",
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
 
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"] {
@@ -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 };
@@ -368,7 +368,7 @@ ol[data-v-0d86d58e] {
368
368
  display: none;
369
369
  }
370
370
 
371
- [data-component~="ui-button"][data-v-0c121308] {
371
+ [data-component~="ui-button"][data-v-6c8750a9] {
372
372
  --_hue: var(--ui-accent);
373
373
  --_hue-hover: var(--ui-accent-hover);
374
374
  --_hue-active: var(--ui-accent-active);
@@ -387,15 +387,15 @@ ol[data-v-0d86d58e] {
387
387
  vertical-align: middle;
388
388
  color: var(--_tone-text);
389
389
  }
390
- [data-component~="ui-button"][data-ui-button-state~="align=start"][data-v-0c121308] {
390
+ [data-component~="ui-button"][data-ui-button-state~="align=start"][data-v-6c8750a9] {
391
391
  justify-content: flex-start;
392
392
  text-align: start;
393
393
  }
394
- [data-component~="ui-button"][data-ui-button-state~="stretch"][data-v-0c121308] {
394
+ [data-component~="ui-button"][data-ui-button-state~="stretch"][data-v-6c8750a9] {
395
395
  display: flex;
396
396
  inline-size: 100%;
397
397
  }
398
- [data-component~="ui-button"][data-v-0c121308] {
398
+ [data-component~="ui-button"][data-v-6c8750a9] {
399
399
  appearance: none;
400
400
  min-block-size: var(--ui-button-min-block-size, var(--ui-control-size-md));
401
401
  border-style: solid;
@@ -420,17 +420,17 @@ ol[data-v-0d86d58e] {
420
420
  margin: 0;
421
421
  }
422
422
  @supports (text-box-trim: trim-both) {
423
- [data-component~="ui-button"][data-v-0c121308] {
423
+ [data-component~="ui-button"][data-v-6c8750a9] {
424
424
  text-box-trim: trim-both;
425
425
  text-box-edge: cap alphabetic;
426
426
  }
427
427
  }
428
- [data-component~="ui-button"][data-ui-button-state~="size=sm"][data-v-0c121308] {
428
+ [data-component~="ui-button"][data-ui-button-state~="size=sm"][data-v-6c8750a9] {
429
429
  min-block-size: var(--ui-control-size-sm);
430
430
  padding: var(--ui-space-1) var(--ui-space-2);
431
431
  font-size: var(--ui-font-size-sm);
432
432
  }
433
- [data-component~="ui-button"][data-ui-button-state~="size=lg"][data-v-0c121308] {
433
+ [data-component~="ui-button"][data-ui-button-state~="size=lg"][data-v-6c8750a9] {
434
434
  min-block-size: var(--ui-control-size-lg);
435
435
  padding: var(--ui-space-3) var(--ui-space-4);
436
436
  font-size: var(--ui-font-size-md);
@@ -438,29 +438,29 @@ ol[data-v-0d86d58e] {
438
438
 
439
439
  /* A link is never :enabled, so interactive states test "not unavailable" instead: for a button that
440
440
  is exactly :enabled, at the same specificity, and it also matches a link that can be followed. */
441
- [data-component~="ui-button"][data-v-0c121308]:hover:not(:disabled, [aria-disabled="true"]) {
441
+ [data-component~="ui-button"][data-v-6c8750a9]:hover:not(:disabled, [aria-disabled="true"]) {
442
442
  border-color: var(--ui-button-hover-border, var(--_tone-hover));
443
443
  background-color: var(--ui-button-hover-surface, oklch(from var(--_tone) l c h / 12%));
444
444
  }
445
445
 
446
446
  /* Pressed reads as pressed in: the highlight goes, and the shadow moves inside. */
447
- [data-component~="ui-button"][data-v-0c121308]:active:not(:disabled, [aria-disabled="true"]) {
447
+ [data-component~="ui-button"][data-v-6c8750a9]:active:not(:disabled, [aria-disabled="true"]) {
448
448
  background-color: var(--ui-button-active-surface, oklch(from var(--_tone) l c h / 18%));
449
449
  background-image: none;
450
450
  box-shadow: var(--ui-pressed);
451
451
  }
452
- [data-component~="ui-button"][data-ui-button-state~="variant=solid"][data-v-0c121308],
453
- [data-component~="ui-button"][data-ui-button-state~="variant=danger"][data-v-0c121308] {
452
+ [data-component~="ui-button"][data-ui-button-state~="variant=solid"][data-v-6c8750a9],
453
+ [data-component~="ui-button"][data-ui-button-state~="variant=danger"][data-v-6c8750a9] {
454
454
  border-color: var(--_tone);
455
455
  background-color: var(--_tone);
456
456
  color: var(--_on-tone);
457
457
  box-shadow: var(--ui-button-solid-shadow, var(--ui-raised));
458
458
  }
459
- :is([data-component~="ui-button"][data-ui-button-state~="variant=solid"][data-v-0c121308], [data-component~="ui-button"][data-ui-button-state~="variant=danger"][data-v-0c121308]):hover:not(:disabled, [aria-disabled="true"]) {
459
+ :is([data-component~="ui-button"][data-ui-button-state~="variant=solid"][data-v-6c8750a9], [data-component~="ui-button"][data-ui-button-state~="variant=danger"][data-v-6c8750a9]):hover:not(:disabled, [aria-disabled="true"]) {
460
460
  background-color: var(--_tone-hover);
461
461
  border-color: var(--_tone-hover);
462
462
  }
463
- :is([data-component~="ui-button"][data-ui-button-state~="variant=solid"][data-v-0c121308], [data-component~="ui-button"][data-ui-button-state~="variant=danger"][data-v-0c121308]):active:not(:disabled, [aria-disabled="true"]) {
463
+ :is([data-component~="ui-button"][data-ui-button-state~="variant=solid"][data-v-6c8750a9], [data-component~="ui-button"][data-ui-button-state~="variant=danger"][data-v-6c8750a9]):active:not(:disabled, [aria-disabled="true"]) {
464
464
  background-color: var(--_tone-active);
465
465
  border-color: var(--_tone-active);
466
466
  background-image: none;
@@ -469,43 +469,43 @@ ol[data-v-0d86d58e] {
469
469
 
470
470
  /* Tone is the colour, variant is the volume. Every pairing works: a danger outline is a
471
471
  destructive secondary action, not a special case. */
472
- [data-component~="ui-button"][data-ui-button-state~="tone=neutral"][data-v-0c121308] {
472
+ [data-component~="ui-button"][data-ui-button-state~="tone=neutral"][data-v-6c8750a9] {
473
473
  --_hue: var(--ui-text);
474
474
  --_hue-hover: var(--ui-text);
475
475
  --_hue-active: var(--ui-text);
476
476
  --_hue-text: var(--ui-text);
477
477
  --_on-hue: var(--ui-surface);
478
478
  }
479
- [data-component~="ui-button"][data-ui-button-state~="tone=danger"][data-v-0c121308],
480
- [data-component~="ui-button"][data-ui-button-state~="variant=danger"][data-v-0c121308] {
479
+ [data-component~="ui-button"][data-ui-button-state~="tone=danger"][data-v-6c8750a9],
480
+ [data-component~="ui-button"][data-ui-button-state~="variant=danger"][data-v-6c8750a9] {
481
481
  --_hue: var(--ui-danger);
482
482
  --_hue-hover: var(--ui-danger-hover);
483
483
  --_hue-active: var(--ui-danger-hover);
484
484
  --_hue-text: var(--ui-danger-hover);
485
485
  --_on-hue: var(--ui-on-danger);
486
486
  }
487
- [data-component~="ui-button"][data-ui-button-state~="tone=success"][data-v-0c121308] {
487
+ [data-component~="ui-button"][data-ui-button-state~="tone=success"][data-v-6c8750a9] {
488
488
  --_hue: var(--ui-success);
489
489
  --_hue-hover: color-mix(in srgb, var(--ui-success) 85%, #000);
490
490
  --_hue-active: color-mix(in srgb, var(--ui-success) 75%, #000);
491
491
  --_hue-text: color-mix(in srgb, var(--ui-success) 85%, #000);
492
492
  --_on-hue: var(--ui-surface);
493
493
  }
494
- [data-component~="ui-button"][data-ui-button-state~="tone=warning"][data-v-0c121308] {
494
+ [data-component~="ui-button"][data-ui-button-state~="tone=warning"][data-v-6c8750a9] {
495
495
  --_hue: var(--ui-warning);
496
496
  --_hue-hover: color-mix(in srgb, var(--ui-warning) 85%, #000);
497
497
  --_hue-active: color-mix(in srgb, var(--ui-warning) 75%, #000);
498
498
  --_hue-text: color-mix(in srgb, var(--ui-warning) 85%, #000);
499
499
  --_on-hue: var(--ui-surface);
500
500
  }
501
- [data-component~="ui-button"][data-ui-button-state~="tone=info"][data-v-0c121308] {
501
+ [data-component~="ui-button"][data-ui-button-state~="tone=info"][data-v-6c8750a9] {
502
502
  --_hue: var(--ui-info);
503
503
  --_hue-hover: color-mix(in srgb, var(--ui-info) 85%, #000);
504
504
  --_hue-active: color-mix(in srgb, var(--ui-info) 75%, #000);
505
505
  --_hue-text: color-mix(in srgb, var(--ui-info) 85%, #000);
506
506
  --_on-hue: var(--ui-surface);
507
507
  }
508
- [data-component~="ui-button"][data-ui-button-state~="variant=ghost"][data-v-0c121308] {
508
+ [data-component~="ui-button"][data-ui-button-state~="variant=ghost"][data-v-6c8750a9] {
509
509
  border-color: transparent;
510
510
  background: transparent;
511
511
  box-shadow: none;
@@ -513,24 +513,24 @@ ol[data-v-0d86d58e] {
513
513
  }
514
514
 
515
515
  /* Tints derive from the text colour, not a surface token, so hover and press show on any background. */
516
- [data-component~="ui-button"][data-ui-button-state~="variant=ghost"][data-v-0c121308]:hover:not(:disabled, [aria-disabled="true"]) {
516
+ [data-component~="ui-button"][data-ui-button-state~="variant=ghost"][data-v-6c8750a9]:hover:not(:disabled, [aria-disabled="true"]) {
517
517
  background-color: var(--ui-button-ghost-hover-surface, oklch(from var(--_tone) l c h / 10%));
518
518
  border-color: transparent;
519
519
  }
520
- [data-component~="ui-button"][data-ui-button-state~="variant=ghost"][data-v-0c121308]:active:not(:disabled, [aria-disabled="true"]) {
520
+ [data-component~="ui-button"][data-ui-button-state~="variant=ghost"][data-v-6c8750a9]:active:not(:disabled, [aria-disabled="true"]) {
521
521
  background-color: var(--ui-button-ghost-active-surface, oklch(from var(--_tone) l c h / 16%));
522
522
  border-color: transparent;
523
523
  box-shadow: var(--ui-pressed);
524
524
  }
525
525
 
526
526
  /* A soft halo that transitions in with the other box-shadow changes, layered on the raised shadow. */
527
- [data-component~="ui-button"][data-v-0c121308]:focus-visible {
527
+ [data-component~="ui-button"][data-v-6c8750a9]:focus-visible {
528
528
  outline: none;
529
529
  box-shadow:
530
530
  var(--ui-button-shadow, var(--ui-raised)),
531
531
  var(--ui-focus-halo);
532
532
  }
533
- [data-component~="ui-button"][data-ui-button-state~="variant=solid"][data-v-0c121308]:focus-visible {
533
+ [data-component~="ui-button"][data-ui-button-state~="variant=solid"][data-v-6c8750a9]:focus-visible {
534
534
  box-shadow:
535
535
  var(--ui-button-solid-shadow, var(--ui-raised)),
536
536
  var(--ui-focus-halo);
@@ -540,7 +540,7 @@ ol[data-v-0d86d58e] {
540
540
  disabled outline is still an outline and a disabled ghost still has no edge. Intent goes — an
541
541
  unavailable action says "unavailable", not "unavailable, and destructive" — and so do the
542
542
  highlight and shadow, because nothing that cannot be pressed should look raised. */
543
- [data-component~="ui-button"][data-v-0c121308]:is(:disabled, [aria-disabled="true"]) {
543
+ [data-component~="ui-button"][data-v-6c8750a9]:is(:disabled, [aria-disabled="true"]) {
544
544
  /* Derived from the tone itself, not from a separate disabled palette: faded toward the
545
545
  page, which desaturates it at the same time. Toward the page rather than toward white,
546
546
  so a dark theme dims where a light one lightens — the same rule, both schemes. */
@@ -557,7 +557,7 @@ ol[data-v-0d86d58e] {
557
557
  }
558
558
 
559
559
  /* A filled button keeps its fill, faded further so its text stays legible on it. */
560
- :is([data-component~="ui-button"][data-ui-button-state~="variant=solid"][data-v-0c121308], [data-component~="ui-button"][data-ui-button-state~="variant=danger"][data-v-0c121308]):is(:disabled, [aria-disabled="true"]) {
560
+ :is([data-component~="ui-button"][data-ui-button-state~="variant=solid"][data-v-6c8750a9], [data-component~="ui-button"][data-ui-button-state~="variant=danger"][data-v-6c8750a9]):is(:disabled, [aria-disabled="true"]) {
561
561
  --_tone: color-mix(in oklab, var(--_hue) 34%, var(--ui-surface));
562
562
  }
563
563
 
@@ -565,19 +565,34 @@ ol[data-v-0d86d58e] {
565
565
  it takes a surface rather than reading as plain text. The surface is a wash of the faded tone,
566
566
  the way hover washes the live one, so it stays light in every tone. An opaque mix toward the
567
567
  hue came out a mid-grey slab for neutral, whose hue is the ink: louder than the enabled button. */
568
- [data-component~="ui-button"][data-ui-button-state~="variant=ghost"][data-v-0c121308]:is(:disabled, [aria-disabled="true"]) {
568
+ [data-component~="ui-button"][data-ui-button-state~="variant=ghost"][data-v-6c8750a9]:is(:disabled, [aria-disabled="true"]) {
569
569
  background-color: oklch(from var(--_tone) l c h / 14%);
570
570
  border-color: transparent;
571
571
  }
572
572
  @media (pointer: coarse) {
573
- [data-component~="ui-button"][data-v-0c121308] {
573
+ [data-component~="ui-button"][data-v-6c8750a9] {
574
574
  min-block-size: var(--ui-control-min-block-size);
575
575
  }
576
576
  }
577
577
 
578
+ /* Under touch, every button's press lands within the control minimum: an invisible hit area,
579
+ centred on it, like IconButton's. A standalone link-style button (a "See all" beside a
580
+ heading) is a target of its own, not text, so it gets one too. */
581
+ html[data-ui-input-modality="touch"] [data-component~="ui-button"][data-v-6c8750a9] {
582
+ position: relative;
583
+ }
584
+ html[data-ui-input-modality="touch"] [data-component~="ui-button"][data-v-6c8750a9]::after {
585
+ position: absolute;
586
+ inset: 50% auto auto 50%;
587
+ inline-size: max(100%, var(--ui-control-min-block-size));
588
+ block-size: max(100%, var(--ui-control-min-block-size));
589
+ transform: translate(-50%, -50%);
590
+ content: "";
591
+ }
592
+
578
593
  /* Link: an inline text action. No box, padding, or minimum height (inline targets are exempt under
579
594
  WCAG 2.5.8); the inherited font; an underline that strengthens on hover; a visible focus ring. */
580
- [data-component~="ui-button"][data-ui-button-state~="variant=link"][data-v-0c121308] {
595
+ [data-component~="ui-button"][data-ui-button-state~="variant=link"][data-v-6c8750a9] {
581
596
  display: inline;
582
597
  min-block-size: 0;
583
598
  padding: 0;
@@ -595,22 +610,22 @@ ol[data-v-0d86d58e] {
595
610
  color var(--ui-motion-fast) var(--ui-motion-ease),
596
611
  text-decoration-color var(--ui-motion-fast) var(--ui-motion-ease);
597
612
  }
598
- [data-component~="ui-button"][data-ui-button-state~="variant=link"][data-v-0c121308]:hover:not(:disabled, [aria-disabled="true"]) {
613
+ [data-component~="ui-button"][data-ui-button-state~="variant=link"][data-v-6c8750a9]:hover:not(:disabled, [aria-disabled="true"]) {
599
614
  color: var(--ui-button-link-hover-text, var(--ui-text-primary));
600
615
  text-decoration-color: currentColor;
601
616
  background: none;
602
617
  transform: none;
603
618
  }
604
- [data-component~="ui-button"][data-ui-button-state~="variant=link"][data-v-0c121308]:active:not(:disabled, [aria-disabled="true"]) {
619
+ [data-component~="ui-button"][data-ui-button-state~="variant=link"][data-v-6c8750a9]:active:not(:disabled, [aria-disabled="true"]) {
605
620
  background: none;
606
621
  transform: none;
607
622
  }
608
- [data-component~="ui-button"][data-ui-button-state~="variant=link"][data-v-0c121308]:is(:disabled, [aria-disabled="true"]) {
623
+ [data-component~="ui-button"][data-ui-button-state~="variant=link"][data-v-6c8750a9]:is(:disabled, [aria-disabled="true"]) {
609
624
  background: none;
610
625
  border-color: transparent;
611
626
  color: var(--ui-button-disabled-text, var(--ui-disabled-text));
612
627
  }
613
- [data-component~="ui-button"][data-ui-button-state~="variant=link"][data-v-0c121308]:focus-visible {
628
+ [data-component~="ui-button"][data-ui-button-state~="variant=link"][data-v-6c8750a9]:focus-visible {
614
629
  box-shadow: none;
615
630
  /* Fallback keeps the ring if the token is missing: an unresolved var() would void the outline. */
616
631
  outline: 2px solid var(--ui-focus-ring);
@@ -618,7 +633,7 @@ ol[data-v-0d86d58e] {
618
633
  }
619
634
 
620
635
  /* A component that sets its display still honours the hidden attribute on its root. */
621
- [data-component~="ui-button"][hidden][data-v-0c121308] {
636
+ [data-component~="ui-button"][hidden][data-v-6c8750a9] {
622
637
  display: none;
623
638
  }
624
639