@latty-ds/web 0.6.0 → 0.8.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 (48) hide show
  1. package/custom-elements.json +1226 -995
  2. package/dist/components/accordion/index.js +4 -4
  3. package/dist/components/alert/index.js +39 -5
  4. package/dist/components/avatar/index.js +1 -1
  5. package/dist/components/badge/index.js +6 -2
  6. package/dist/components/breadcrumb/index.js +1 -1
  7. package/dist/components/button/button.d.ts +7 -2
  8. package/dist/components/button/button.types.d.ts +3 -2
  9. package/dist/components/button/index.js +83 -4
  10. package/dist/components/calendar/index.js +3 -3
  11. package/dist/components/checkbox/index.js +2 -1
  12. package/dist/components/chip/index.js +6 -2
  13. package/dist/components/color-input/index.js +6 -3
  14. package/dist/components/combobox/index.js +4 -4
  15. package/dist/components/date-input/index.js +6 -6
  16. package/dist/components/datepicker/index.js +3 -3
  17. package/dist/components/dialog/index.js +40 -6
  18. package/dist/components/divider/index.js +1 -1
  19. package/dist/components/dropdown/index.js +5 -5
  20. package/dist/components/icon-button/icon-button.d.ts +4 -0
  21. package/dist/components/icon-button/index.js +35 -1
  22. package/dist/components/image/image.d.ts +32 -1
  23. package/dist/components/image/image.types.d.ts +7 -0
  24. package/dist/components/image/index.js +41 -2
  25. package/dist/components/list/index.js +94 -7
  26. package/dist/components/list/list-item.d.ts +35 -0
  27. package/dist/components/nav/index.js +19 -5
  28. package/dist/components/nav/nav-item.d.ts +7 -1
  29. package/dist/components/pagination/index.js +1 -1
  30. package/dist/components/radio/index.js +2 -1
  31. package/dist/components/radio-group/index.js +2 -2
  32. package/dist/components/select/index.js +6 -6
  33. package/dist/components/shared/aria-forwarding.d.ts +43 -0
  34. package/dist/components/sidepanel/index.js +36 -2
  35. package/dist/components/slider/index.js +2 -2
  36. package/dist/components/snackbar/index.js +4 -4
  37. package/dist/components/surface/index.js +3 -3
  38. package/dist/components/switch/index.js +2 -1
  39. package/dist/components/tab/index.js +1 -1
  40. package/dist/components/tab-group/index.js +2 -2
  41. package/dist/components/table/index.js +3 -3
  42. package/dist/components/text/index.js +1 -1
  43. package/dist/components/textfield/index.js +7 -3
  44. package/dist/components/tooltip/index.js +1 -1
  45. package/dist/index.cjs +302 -67
  46. package/dist/index.js +344 -106
  47. package/dist/manifest.json +126 -84
  48. package/package.json +3 -3
package/dist/index.cjs CHANGED
@@ -1331,7 +1331,7 @@ __decorateClass([
1331
1331
  var accordionStyles = i`
1332
1332
  :host {
1333
1333
  display: block;
1334
- font-family: 'Hanken Grotesk', sans-serif;
1334
+ font-family: var(--lt-typography-fontFamily, 'Hanken Grotesk', sans-serif);
1335
1335
  width: 100%;
1336
1336
  }
1337
1337
 
@@ -1416,7 +1416,7 @@ var accordionStyles = i`
1416
1416
  /* Variant: default */
1417
1417
  :host([appearance='default']) details {
1418
1418
  background: var(--lt-bg-subtle);
1419
- border: 1px solid var(--lt-border-default);
1419
+ border: var(--lt-border-width, 1px) solid var(--lt-border-default);
1420
1420
  }
1421
1421
 
1422
1422
  :host([appearance='default']) details:hover {
@@ -1436,7 +1436,7 @@ var accordionStyles = i`
1436
1436
  /* Variant: outlined */
1437
1437
  :host([appearance='outlined']) details {
1438
1438
  background: transparent;
1439
- border: 1px solid var(--lt-border-default);
1439
+ border: var(--lt-border-width, 1px) solid var(--lt-border-default);
1440
1440
  }
1441
1441
 
1442
1442
  :host([appearance='outlined']) details:hover {
@@ -1508,7 +1508,7 @@ var textStyles = i`
1508
1508
  [part='base'] {
1509
1509
  margin: 0;
1510
1510
  color: inherit;
1511
- font-family: var(--lt-typography-fontfamily);
1511
+ font-family: var(--lt-typography-fontFamily, 'Hanken Grotesk', sans-serif);
1512
1512
  }
1513
1513
 
1514
1514
  /* ── Display ─────────────────────────────────────────────────────────── */
@@ -1781,7 +1781,7 @@ var o8 = (o10) => o10 ?? A;
1781
1781
  var buttonStyles = i`
1782
1782
  :host {
1783
1783
  display: inline-block;
1784
- font-family: 'Hanken Grotesk', sans-serif;
1784
+ font-family: var(--lt-typography-fontFamily, 'Hanken Grotesk', sans-serif);
1785
1785
 
1786
1786
  /* Internal state props — set per variant/appearance below */
1787
1787
  --_color: var(--lt-text-on-primary);
@@ -1804,7 +1804,7 @@ var buttonStyles = i`
1804
1804
  align-items: center;
1805
1805
  background: var(--lt-button-bg, var(--_bg));
1806
1806
  border-radius: var(--lt-border-radius);
1807
- border: 1px solid var(--lt-button-border-color, var(--_border-color));
1807
+ border: var(--lt-border-width, 1px) solid var(--lt-button-border-color, var(--_border-color));
1808
1808
  box-sizing: border-box;
1809
1809
  color: var(--lt-button-color, var(--_color));
1810
1810
  cursor: pointer;
@@ -1814,7 +1814,7 @@ var buttonStyles = i`
1814
1814
  gap: var(--lt-spacing-2);
1815
1815
  justify-content: center;
1816
1816
  line-height: 1;
1817
- min-width: 88px;
1817
+ min-width: var(--lt-button-min-width, 88px);
1818
1818
  user-select: none;
1819
1819
  transition:
1820
1820
  background-color 120ms ease,
@@ -1966,7 +1966,52 @@ var buttonStyles = i`
1966
1966
  --_active-bg: var(--lt-bg-info-subtle);
1967
1967
  }
1968
1968
 
1969
- :host([appearance='outlined']) [part='base']:active:not([disabled]):not([aria-disabled='true']) {
1969
+ /* ghost appearance — transparent background, no border, low-emphasis text button */
1970
+ :host([appearance='ghost'][variant='primary']) {
1971
+ --_bg: transparent;
1972
+ --_color: var(--lt-text-primary);
1973
+ --_hover-bg: var(--lt-bg-primary-subtle);
1974
+ --_active-bg: var(--lt-bg-primary-subtle);
1975
+ }
1976
+ :host([appearance='ghost'][variant='secondary']) {
1977
+ --_bg: transparent;
1978
+ --_color: var(--lt-text-secondary);
1979
+ --_hover-bg: var(--lt-bg-secondary-subtle);
1980
+ --_active-bg: var(--lt-bg-secondary-subtle);
1981
+ }
1982
+ :host([appearance='ghost'][variant='neutral']) {
1983
+ --_bg: transparent;
1984
+ --_color: var(--lt-text-neutral);
1985
+ --_hover-bg: var(--lt-bg-subtle);
1986
+ --_active-bg: var(--lt-bg-surface);
1987
+ }
1988
+ :host([appearance='ghost'][variant='success']) {
1989
+ --_bg: transparent;
1990
+ --_color: var(--lt-text-success);
1991
+ --_hover-bg: var(--lt-bg-success-subtle);
1992
+ --_active-bg: var(--lt-bg-success-subtle);
1993
+ }
1994
+ :host([appearance='ghost'][variant='warning']) {
1995
+ --_bg: transparent;
1996
+ --_color: var(--lt-text-warning);
1997
+ --_hover-bg: var(--lt-bg-warning-subtle);
1998
+ --_active-bg: var(--lt-bg-warning-subtle);
1999
+ }
2000
+ :host([appearance='ghost'][variant='error']) {
2001
+ --_bg: transparent;
2002
+ --_color: var(--lt-text-error);
2003
+ --_hover-bg: var(--lt-bg-error-subtle);
2004
+ --_active-bg: var(--lt-bg-error-subtle);
2005
+ }
2006
+ :host([appearance='ghost'][variant='info']) {
2007
+ --_bg: transparent;
2008
+ --_color: var(--lt-text-info);
2009
+ --_hover-bg: var(--lt-bg-info-subtle);
2010
+ --_active-bg: var(--lt-bg-info-subtle);
2011
+ }
2012
+
2013
+ :host([appearance='outlined']) [part='base']:active:not([disabled]):not([aria-disabled='true']),
2014
+ :host([appearance='ghost']) [part='base']:active:not([disabled]):not([aria-disabled='true']) {
1970
2015
  opacity: 0.8;
1971
2016
  }
1972
2017
 
@@ -2000,6 +2045,39 @@ var buttonStyles = i`
2000
2045
  }
2001
2046
  `;
2002
2047
 
2048
+ // src/components/shared/aria-forwarding.ts
2049
+ var FORWARDED_ARIA_ATTRIBUTES = [
2050
+ "aria-pressed",
2051
+ "aria-expanded",
2052
+ "aria-haspopup",
2053
+ "aria-controls",
2054
+ "aria-current"
2055
+ ];
2056
+ var AriaForwardingController = class {
2057
+ constructor(host, getTarget) {
2058
+ this._host = host;
2059
+ this._getTarget = getTarget;
2060
+ this._observer = new MutationObserver(() => this._host.requestUpdate());
2061
+ host.addController(this);
2062
+ }
2063
+ hostConnected() {
2064
+ this._observer.observe(this._host, { attributes: true, attributeFilter: [...FORWARDED_ARIA_ATTRIBUTES] });
2065
+ this._host.requestUpdate();
2066
+ }
2067
+ hostDisconnected() {
2068
+ this._observer.disconnect();
2069
+ }
2070
+ hostUpdated() {
2071
+ const target = this._getTarget();
2072
+ if (!target) return;
2073
+ for (const attr of FORWARDED_ARIA_ATTRIBUTES) {
2074
+ const value = this._host.getAttribute(attr);
2075
+ if (value === null) target.removeAttribute(attr);
2076
+ else if (target.getAttribute(attr) !== value) target.setAttribute(attr, value);
2077
+ }
2078
+ }
2079
+ };
2080
+
2003
2081
  // src/components/spinner/spinner.styles.ts
2004
2082
  var spinnerStyles = i`
2005
2083
  :host {
@@ -2094,6 +2172,7 @@ var import_icons2 = require("@latty-ds/icons");
2094
2172
  var Button = class extends ThemeableElement {
2095
2173
  constructor() {
2096
2174
  super();
2175
+ this._ariaForwarding = new AriaForwardingController(this, () => this.shadowRoot?.querySelector('[part="base"]'));
2097
2176
  this.variant = "primary";
2098
2177
  this.appearance = "filled";
2099
2178
  this.size = "md";
@@ -2248,7 +2327,7 @@ var o9 = e5(class extends i4 {
2248
2327
  var chipStyles = i`
2249
2328
  :host {
2250
2329
  display: inline-flex;
2251
- font-family: 'Hanken Grotesk', sans-serif;
2330
+ font-family: var(--lt-typography-fontFamily, 'Hanken Grotesk', sans-serif);
2252
2331
  vertical-align: middle;
2253
2332
  }
2254
2333
 
@@ -2256,7 +2335,11 @@ var chipStyles = i`
2256
2335
  align-items: center;
2257
2336
  background: var(--lt-chip-bg);
2258
2337
  border-radius: 999px;
2259
- border: 1px solid var(--lt-chip-border-color, transparent);
2338
+ /* Chips are small pills (as short as 20px tall at size="sm") — a "thick"
2339
+ (4px) themed border eats too much of that height and reads as
2340
+ compressed/squished, so cap what the chip actually renders at "medium"
2341
+ (2px) regardless of the theme's configured border width. */
2342
+ border: min(var(--lt-border-width, 1px), 2px) solid var(--lt-chip-border-color, transparent);
2260
2343
  color: var(--lt-chip-color);
2261
2344
  box-sizing: border-box;
2262
2345
  display: inline-flex;
@@ -2498,7 +2581,7 @@ Chip = __decorateClass([
2498
2581
  var checkboxStyles = i`
2499
2582
  :host {
2500
2583
  display: inline-block;
2501
- font-family: 'Hanken Grotesk', sans-serif;
2584
+ font-family: var(--lt-typography-fontFamily, 'Hanken Grotesk', sans-serif);
2502
2585
  }
2503
2586
 
2504
2587
  .checkbox-wrapper {
@@ -2698,6 +2781,7 @@ var Checkbox = class extends ThemeableElement {
2698
2781
  * @private
2699
2782
  */
2700
2783
  handleChange(e7) {
2784
+ e7.stopPropagation();
2701
2785
  const target = e7.target;
2702
2786
  this.checked = target.checked;
2703
2787
  this.indeterminate = false;
@@ -2788,7 +2872,7 @@ Checkbox = __decorateClass([
2788
2872
  // src/components/dialog/dialog.styles.ts
2789
2873
  var dialogStyles = i`
2790
2874
  :host {
2791
- font-family: 'Hanken Grotesk', sans-serif;
2875
+ font-family: var(--lt-typography-fontFamily, 'Hanken Grotesk', sans-serif);
2792
2876
  }
2793
2877
 
2794
2878
  /* Backdrop */
@@ -2940,7 +3024,7 @@ var iconButtonStyles = i`
2940
3024
  align-items: center;
2941
3025
  background: var(--lt-icon-button-bg, var(--_bg));
2942
3026
  border-radius: var(--lt-border-radius);
2943
- border: 1px solid var(--lt-icon-button-border-color, var(--_border-color));
3027
+ border: var(--lt-border-width, 1px) solid var(--lt-icon-button-border-color, var(--_border-color));
2944
3028
  box-sizing: border-box;
2945
3029
  color: var(--lt-icon-button-color, var(--_color));
2946
3030
  cursor: pointer;
@@ -3151,6 +3235,7 @@ var import_icons5 = require("@latty-ds/icons");
3151
3235
  var IconButton = class extends ThemeableElement {
3152
3236
  constructor() {
3153
3237
  super(...arguments);
3238
+ this._ariaForwarding = new AriaForwardingController(this, () => this.shadowRoot?.querySelector('[part="base"]'));
3154
3239
  this.icon = "";
3155
3240
  this.label = "";
3156
3241
  this.variant = "neutral";
@@ -3227,7 +3312,7 @@ IconButton = __decorateClass([
3227
3312
  var surfaceStyles = i`
3228
3313
  :host {
3229
3314
  display: block;
3230
- font-family: 'Hanken Grotesk', sans-serif;
3315
+ font-family: var(--lt-typography-fontFamily, 'Hanken Grotesk', sans-serif);
3231
3316
  }
3232
3317
 
3233
3318
  .surface {
@@ -3266,7 +3351,7 @@ var surfaceStyles = i`
3266
3351
 
3267
3352
  :host([appearance='outlined']) .surface {
3268
3353
  background: var(--lt-surface-bg, transparent);
3269
- border: 1px solid var(--lt-border-default);
3354
+ border: var(--lt-border-width, 1px) solid var(--lt-border-default);
3270
3355
  }
3271
3356
 
3272
3357
  :host([appearance='outlined']:hover) .surface {
@@ -3283,7 +3368,7 @@ var Surface = class extends ThemeableElement {
3283
3368
  this.background = "";
3284
3369
  }
3285
3370
  render() {
3286
- const style = this.background ? { "--_surface-bg": resolveColorValue(this.background) } : {};
3371
+ const style = this.background ? { "--lt-surface-bg": resolveColorValue(this.background) } : {};
3287
3372
  return T`
3288
3373
  <div class="surface" part="surface" style=${o9(style)}>
3289
3374
  <slot></slot>
@@ -3472,7 +3557,7 @@ Dialog = __decorateClass([
3472
3557
  var listStyles = i`
3473
3558
  :host {
3474
3559
  display: block;
3475
- font-family: 'Hanken Grotesk', sans-serif;
3560
+ font-family: var(--lt-typography-fontFamily, 'Hanken Grotesk', sans-serif);
3476
3561
  --list-marker-color: var(--lt-color-neutral-500);
3477
3562
  }
3478
3563
 
@@ -3593,11 +3678,60 @@ var listItemStyles = i`
3593
3678
  gap: var(--lt-spacing-2);
3594
3679
  }
3595
3680
 
3681
+ /* Keep the slotted content as a single flex item so rich inline markup
3682
+ (strong, code, multiple text runs) flows as normal prose instead of each
3683
+ node becoming its own shrunk flex item. min-width: 0 lets it wrap. */
3684
+ .content {
3685
+ flex: 1 1 auto;
3686
+ min-width: 0;
3687
+ }
3688
+
3596
3689
  lt-icon {
3597
3690
  flex-shrink: 0;
3598
3691
  color: var(--lt-color-neutral-500);
3599
3692
  font-size: inherit;
3600
3693
  }
3694
+
3695
+ /* interactive rows — native button/anchor reset + states */
3696
+ a.inner,
3697
+ button.inner {
3698
+ background: transparent;
3699
+ border: none;
3700
+ border-radius: var(--lt-border-radius);
3701
+ box-sizing: border-box;
3702
+ color: inherit;
3703
+ cursor: pointer;
3704
+ font: inherit;
3705
+ margin: 0;
3706
+ padding: var(--lt-spacing-2);
3707
+ text-align: start;
3708
+ text-decoration: none;
3709
+ transition: background-color 120ms ease;
3710
+ width: 100%;
3711
+ }
3712
+
3713
+ a.inner:hover:not([aria-disabled='true']),
3714
+ button.inner:hover:not([disabled]) {
3715
+ background: var(--lt-list-item-hover-bg, var(--lt-bg-subtle));
3716
+ }
3717
+
3718
+ a.inner:active:not([aria-disabled='true']),
3719
+ button.inner:active:not([disabled]) {
3720
+ background: var(--lt-list-item-active-bg, var(--lt-bg-surface));
3721
+ }
3722
+
3723
+ a.inner:focus-visible,
3724
+ button.inner:focus-visible {
3725
+ outline: 3px solid var(--lt-border-focus);
3726
+ outline-offset: -3px;
3727
+ }
3728
+
3729
+ button.inner[disabled],
3730
+ a.inner[aria-disabled='true'] {
3731
+ cursor: not-allowed;
3732
+ opacity: 0.6;
3733
+ pointer-events: none;
3734
+ }
3601
3735
  `;
3602
3736
 
3603
3737
  // src/components/list/list-item.ts
@@ -3607,19 +3741,41 @@ var ListItem = class extends ThemeableElement {
3607
3741
  super(...arguments);
3608
3742
  this.iconStart = "";
3609
3743
  this.iconEnd = "";
3744
+ this.clickable = false;
3745
+ this.href = "";
3746
+ this.target = "";
3747
+ this.rel = "";
3748
+ this.disabled = false;
3610
3749
  }
3611
3750
  connectedCallback() {
3612
3751
  super.connectedCallback();
3613
3752
  if (!this.hasAttribute("role")) this.setAttribute("role", "listitem");
3614
3753
  }
3615
3754
  render() {
3616
- return T`
3617
- <div class="inner">
3618
- ${this.iconStart ? T`<lt-icon class="icon-start" name=${this.iconStart}></lt-icon>` : A}
3619
- <slot></slot>
3620
- ${this.iconEnd ? T`<lt-icon class="icon-end" name=${this.iconEnd}></lt-icon>` : A}
3621
- </div>
3755
+ const inner = T`
3756
+ ${this.iconStart ? T`<lt-icon class="icon-start" name=${this.iconStart}></lt-icon>` : A}
3757
+ <span class="content"><slot></slot></span>
3758
+ ${this.iconEnd ? T`<lt-icon class="icon-end" name=${this.iconEnd}></lt-icon>` : A}
3622
3759
  `;
3760
+ if (this.href) {
3761
+ const rel = this.rel || (this.target === "_blank" ? "noopener noreferrer" : void 0);
3762
+ return T`
3763
+ <a
3764
+ class="inner"
3765
+ part="base"
3766
+ href=${this.href}
3767
+ target=${o8(this.target || void 0)}
3768
+ rel=${o8(rel)}
3769
+ aria-disabled=${this.disabled ? "true" : A}
3770
+ tabindex=${this.disabled ? "-1" : A}
3771
+ >${inner}</a
3772
+ >
3773
+ `;
3774
+ }
3775
+ if (this.clickable) {
3776
+ return T`<button class="inner" part="base" type="button" ?disabled=${this.disabled}>${inner}</button>`;
3777
+ }
3778
+ return T`<div class="inner" part="base">${inner}</div>`;
3623
3779
  }
3624
3780
  };
3625
3781
  ListItem.styles = listItemStyles;
@@ -3629,6 +3785,21 @@ __decorateClass([
3629
3785
  __decorateClass([
3630
3786
  n4({ attribute: "icon-end" })
3631
3787
  ], ListItem.prototype, "iconEnd", 2);
3788
+ __decorateClass([
3789
+ n4({ type: Boolean, reflect: true })
3790
+ ], ListItem.prototype, "clickable", 2);
3791
+ __decorateClass([
3792
+ n4()
3793
+ ], ListItem.prototype, "href", 2);
3794
+ __decorateClass([
3795
+ n4()
3796
+ ], ListItem.prototype, "target", 2);
3797
+ __decorateClass([
3798
+ n4()
3799
+ ], ListItem.prototype, "rel", 2);
3800
+ __decorateClass([
3801
+ n4({ type: Boolean, reflect: true })
3802
+ ], ListItem.prototype, "disabled", 2);
3632
3803
  ListItem = __decorateClass([
3633
3804
  t3("lt-list-item")
3634
3805
  ], ListItem);
@@ -3677,7 +3848,7 @@ List = __decorateClass([
3677
3848
  var radioStyles = i`
3678
3849
  :host {
3679
3850
  display: inline-block;
3680
- font-family: 'Hanken Grotesk', sans-serif;
3851
+ font-family: var(--lt-typography-fontFamily, 'Hanken Grotesk', sans-serif);
3681
3852
  }
3682
3853
 
3683
3854
  .radio-wrapper {
@@ -3881,6 +4052,7 @@ var Radio = class extends ThemeableElement {
3881
4052
  * @private
3882
4053
  */
3883
4054
  handleChange(e7) {
4055
+ e7.stopPropagation();
3884
4056
  const target = e7.target;
3885
4057
  this.checked = target.checked;
3886
4058
  this.dispatchEvent(
@@ -3949,7 +4121,7 @@ Radio = __decorateClass([
3949
4121
  var radioGroupStyles = i`
3950
4122
  :host {
3951
4123
  display: block;
3952
- font-family: 'Hanken Grotesk', sans-serif;
4124
+ font-family: var(--lt-typography-fontFamily, 'Hanken Grotesk', sans-serif);
3953
4125
  }
3954
4126
 
3955
4127
  .radio-group {
@@ -5506,7 +5678,7 @@ var selectStyles = [
5506
5678
  i`
5507
5679
  :host {
5508
5680
  display: inline-block;
5509
- font-family: 'Hanken Grotesk', sans-serif;
5681
+ font-family: var(--lt-typography-fontFamily, 'Hanken Grotesk', sans-serif);
5510
5682
  position: relative;
5511
5683
  width: 100%;
5512
5684
  }
@@ -5535,7 +5707,7 @@ var selectStyles = [
5535
5707
  align-items: center;
5536
5708
  background: var(--lt-bg-subtle);
5537
5709
  border-radius: var(--lt-border-radius);
5538
- border: 1px solid var(--lt-border-default);
5710
+ border: var(--lt-border-width, 1px) solid var(--lt-border-default);
5539
5711
  box-sizing: border-box;
5540
5712
  cursor: pointer;
5541
5713
  display: flex;
@@ -5938,7 +6110,7 @@ Select = __decorateClass([
5938
6110
  var switchStyles = i`
5939
6111
  :host {
5940
6112
  display: inline-block;
5941
- font-family: 'Hanken Grotesk', sans-serif;
6113
+ font-family: var(--lt-typography-fontFamily, 'Hanken Grotesk', sans-serif);
5942
6114
  }
5943
6115
 
5944
6116
  .switch-wrapper {
@@ -6139,6 +6311,7 @@ var Switch = class extends ThemeableElement {
6139
6311
  return this._internals.reportValidity();
6140
6312
  }
6141
6313
  handleChange(e7) {
6314
+ e7.stopPropagation();
6142
6315
  const target = e7.target;
6143
6316
  this.checked = target.checked;
6144
6317
  this.dispatchEvent(
@@ -6209,7 +6382,7 @@ Switch = __decorateClass([
6209
6382
  var tabStyles = i`
6210
6383
  :host {
6211
6384
  display: inline-block;
6212
- font-family: 'Hanken Grotesk', sans-serif;
6385
+ font-family: var(--lt-typography-fontFamily, 'Hanken Grotesk', sans-serif);
6213
6386
  }
6214
6387
 
6215
6388
  .tab {
@@ -6413,7 +6586,7 @@ TabPanel = __decorateClass([
6413
6586
  var tabGroupStyles = i`
6414
6587
  :host {
6415
6588
  display: block;
6416
- font-family: 'Hanken Grotesk', sans-serif;
6589
+ font-family: var(--lt-typography-fontFamily, 'Hanken Grotesk', sans-serif);
6417
6590
  }
6418
6591
 
6419
6592
  .tab-group {
@@ -6708,7 +6881,7 @@ var c5 = e5(class extends i4 {
6708
6881
  var tableStyles = i`
6709
6882
  :host {
6710
6883
  display: block;
6711
- font-family: 'Hanken Grotesk', sans-serif;
6884
+ font-family: var(--lt-typography-fontFamily, 'Hanken Grotesk', sans-serif);
6712
6885
  overflow: hidden;
6713
6886
  width: 100%;
6714
6887
  }
@@ -6869,7 +7042,7 @@ var tableStyles = i`
6869
7042
 
6870
7043
  /* Border styling */
6871
7044
  :host([bordered]) table {
6872
- border: 1px solid var(--lt-border-default);
7045
+ border: var(--lt-border-width, 1px) solid var(--lt-border-default);
6873
7046
  }
6874
7047
 
6875
7048
  :host([bordered]) th,
@@ -6925,7 +7098,7 @@ var tableStyles = i`
6925
7098
  :host([responsive-mode='stack']) tr {
6926
7099
  background: var(--lt-bg-subtle);
6927
7100
  border-radius: var(--lt-border-radius);
6928
- border: 1px solid var(--lt-border-default);
7101
+ border: var(--lt-border-width, 1px) solid var(--lt-border-default);
6929
7102
  margin-bottom: var(--lt-spacing-3);
6930
7103
  padding: var(--lt-spacing-4);
6931
7104
  }
@@ -7245,7 +7418,7 @@ Table = __decorateClass([
7245
7418
  var textfieldStyles = i`
7246
7419
  :host {
7247
7420
  display: inline-block;
7248
- font-family: 'Hanken Grotesk', sans-serif;
7421
+ font-family: var(--lt-typography-fontFamily, 'Hanken Grotesk', sans-serif);
7249
7422
  width: 100%;
7250
7423
  }
7251
7424
 
@@ -7315,7 +7488,7 @@ var textfieldStyles = i`
7315
7488
  textarea {
7316
7489
  background: var(--lt-bg-subtle);
7317
7490
  border-radius: var(--lt-border-radius);
7318
- border: 1px solid var(--lt-border-default);
7491
+ border: var(--lt-border-width, 1px) solid var(--lt-border-default);
7319
7492
  box-sizing: border-box;
7320
7493
  color: var(--lt-text-default);
7321
7494
  font-family: inherit;
@@ -7547,6 +7720,7 @@ var Textfield = class extends ThemeableElement {
7547
7720
  return true;
7548
7721
  }
7549
7722
  handleInput(e7) {
7723
+ e7.stopPropagation();
7550
7724
  const input = e7.target;
7551
7725
  this.value = input.value;
7552
7726
  if (this._touched) this._autoError = !this._validate();
@@ -7566,6 +7740,7 @@ var Textfield = class extends ThemeableElement {
7566
7740
  * @private
7567
7741
  */
7568
7742
  handleChange(e7) {
7743
+ e7.stopPropagation();
7569
7744
  const input = e7.target;
7570
7745
  this.value = input.value;
7571
7746
  this.dispatchEvent(
@@ -7652,6 +7827,7 @@ var Textfield = class extends ThemeableElement {
7652
7827
  ${isMultiline ? T`
7653
7828
  <textarea
7654
7829
  .value=${this.value}
7830
+ name=${o8(this.name || void 0)}
7655
7831
  placeholder=${this.placeholder}
7656
7832
  rows=${this.rows}
7657
7833
  minlength=${o8(this.min !== null ? this.min : void 0)}
@@ -7671,6 +7847,7 @@ var Textfield = class extends ThemeableElement {
7671
7847
  <input
7672
7848
  type=${actualInputType}
7673
7849
  .value=${this.value}
7850
+ name=${o8(this.name || void 0)}
7674
7851
  placeholder=${this.placeholder}
7675
7852
  min=${o8(isNumber && this.min !== null ? this.min : void 0)}
7676
7853
  max=${o8(isNumber && this.max !== null ? this.max : void 0)}
@@ -7790,7 +7967,7 @@ var avatarStyles = i`
7790
7967
  }
7791
7968
 
7792
7969
  .initials {
7793
- font-family: 'Hanken Grotesk', sans-serif;
7970
+ font-family: var(--lt-typography-fontFamily, 'Hanken Grotesk', sans-serif);
7794
7971
  font-weight: 600;
7795
7972
  letter-spacing: 0.02em;
7796
7973
  line-height: 1;
@@ -7948,7 +8125,7 @@ Avatar = __decorateClass([
7948
8125
  var badgeStyles = i`
7949
8126
  :host {
7950
8127
  display: inline-flex;
7951
- font-family: 'Hanken Grotesk', sans-serif;
8128
+ font-family: var(--lt-typography-fontFamily, 'Hanken Grotesk', sans-serif);
7952
8129
  vertical-align: middle;
7953
8130
  }
7954
8131
 
@@ -7956,7 +8133,11 @@ var badgeStyles = i`
7956
8133
  align-items: center;
7957
8134
  background: var(--lt-badge-bg);
7958
8135
  border-radius: 999px;
7959
- border: 1px solid var(--lt-badge-border-color, transparent);
8136
+ /* Badges are even smaller than chips (as short as 18px at size="sm") — a
8137
+ "thick" (4px) themed border reads as compressed, so cap what the badge
8138
+ actually renders at "medium" (2px) regardless of the theme's configured
8139
+ border width. */
8140
+ border: min(var(--lt-border-width, 1px), 2px) solid var(--lt-badge-border-color, transparent);
7960
8141
  color: var(--lt-badge-color);
7961
8142
  box-sizing: border-box;
7962
8143
  display: inline-flex;
@@ -8371,7 +8552,7 @@ var snackbarStyles = i`
8371
8552
  align-items: center;
8372
8553
  color: var(--lt-text-inverse);
8373
8554
  display: flex;
8374
- font-family: 'Hanken Grotesk', sans-serif;
8555
+ font-family: var(--lt-typography-fontFamily, 'Hanken Grotesk', sans-serif);
8375
8556
  font-size: 0.875rem;
8376
8557
  gap: var(--lt-spacing-2);
8377
8558
  line-height: 1.4;
@@ -8564,7 +8745,7 @@ Snackbar = __decorateClass([
8564
8745
  var alertStyles = i`
8565
8746
  :host {
8566
8747
  display: block;
8567
- font-family: 'Hanken Grotesk', sans-serif;
8748
+ font-family: var(--lt-typography-fontFamily, 'Hanken Grotesk', sans-serif);
8568
8749
  opacity: 1;
8569
8750
  transform: translateY(0);
8570
8751
  transition:
@@ -8634,14 +8815,14 @@ var alertStyles = i`
8634
8815
  :host([appearance='filled']) .inner,
8635
8816
  :host(:not([appearance])) .inner {
8636
8817
  background: var(--_bg-subtle);
8637
- border: 1px solid var(--_border);
8818
+ border: var(--lt-border-width, 1px) solid var(--_border);
8638
8819
  color: var(--_color);
8639
8820
  }
8640
8821
 
8641
8822
  /* outlined — white bg + colored border */
8642
8823
  :host([appearance='outlined']) .inner {
8643
8824
  background: var(--lt-bg-default);
8644
- border: 1px solid var(--_border);
8825
+ border: var(--lt-border-width, 1px) solid var(--_border);
8645
8826
  color: var(--_color);
8646
8827
  }
8647
8828
 
@@ -8782,7 +8963,7 @@ Alert = __decorateClass([
8782
8963
  var sliderStyles = i`
8783
8964
  :host {
8784
8965
  display: block;
8785
- font-family: 'Hanken Grotesk', sans-serif;
8966
+ font-family: var(--lt-typography-fontFamily, 'Hanken Grotesk', sans-serif);
8786
8967
  width: 100%;
8787
8968
  }
8788
8969
 
@@ -9048,7 +9229,7 @@ var dropdownStyles = [
9048
9229
  i`
9049
9230
  :host {
9050
9231
  display: inline-block;
9051
- font-family: 'Hanken Grotesk', sans-serif;
9232
+ font-family: var(--lt-typography-fontFamily, 'Hanken Grotesk', sans-serif);
9052
9233
  }
9053
9234
 
9054
9235
  .trigger-wrap {
@@ -9081,7 +9262,7 @@ var dropdownItemStyles = i`
9081
9262
  color: var(--lt-text-default);
9082
9263
  cursor: pointer;
9083
9264
  display: flex;
9084
- font-family: 'Hanken Grotesk', sans-serif;
9265
+ font-family: var(--lt-typography-fontFamily, 'Hanken Grotesk', sans-serif);
9085
9266
  font-size: 0.875rem;
9086
9267
  gap: var(--lt-spacing-2);
9087
9268
  padding: var(--lt-spacing-2) var(--lt-spacing-4);
@@ -9476,7 +9657,7 @@ var navItemStyles = i`
9476
9657
  min-width: 160px;
9477
9658
  background: var(--lt-bg-default);
9478
9659
  color: var(--lt-color-neutral-700);
9479
- border: 1px solid var(--lt-color-neutral-200);
9660
+ border: var(--lt-border-width, 1px) solid var(--lt-color-neutral-200);
9480
9661
  border-radius: 8px;
9481
9662
  box-shadow:
9482
9663
  0 4px 6px -1px rgb(0 0 0 / 0.08),
@@ -9541,13 +9722,15 @@ var NavItem = class extends ThemeableElement {
9541
9722
  }
9542
9723
  _handleTriggerClick(e7) {
9543
9724
  if (this.disabled) return;
9544
- this.dispatchEvent(
9725
+ const proceed = this.dispatchEvent(
9545
9726
  new CustomEvent("nav-item-click", {
9546
9727
  detail: { href: this.href, label: this.label },
9547
9728
  bubbles: true,
9548
- composed: true
9729
+ composed: true,
9730
+ cancelable: true
9549
9731
  })
9550
9732
  );
9733
+ if (!proceed) e7.preventDefault();
9551
9734
  if (this._hasChildren) {
9552
9735
  e7.preventDefault();
9553
9736
  this.open = !this.open;
@@ -9567,10 +9750,22 @@ var NavItem = class extends ThemeableElement {
9567
9750
  <span class="label">${this.label}</span>
9568
9751
  ${showChevron ? T`<lt-icon class="chevron" name="caret-down" size="sm"></lt-icon>` : A}
9569
9752
  `;
9753
+ const ariaCurrent = this.active ? "page" : A;
9570
9754
  if (this.href && !this._hasChildren) {
9571
- return T`<a class="item-trigger" href=${this.href} @click=${this._handleTriggerClick}>${inner}</a>`;
9572
- }
9573
- return T`<button class="item-trigger" @click=${this._handleTriggerClick} ?disabled=${this.disabled}>
9755
+ return T`<a
9756
+ class="item-trigger"
9757
+ href=${this.href}
9758
+ aria-current=${ariaCurrent}
9759
+ @click=${this._handleTriggerClick}
9760
+ >${inner}</a
9761
+ >`;
9762
+ }
9763
+ return T`<button
9764
+ class="item-trigger"
9765
+ aria-current=${ariaCurrent}
9766
+ @click=${this._handleTriggerClick}
9767
+ ?disabled=${this.disabled}
9768
+ >
9574
9769
  ${inner}
9575
9770
  </button>`;
9576
9771
  }
@@ -9743,7 +9938,7 @@ var breadcrumbItemStyles = i`
9743
9938
  :host {
9744
9939
  align-items: center;
9745
9940
  display: inline-flex;
9746
- font-family: var(--lt-typography-fontfamily);
9941
+ font-family: var(--lt-typography-fontFamily, 'Hanken Grotesk', sans-serif);
9747
9942
  font-size: 0.875rem;
9748
9943
  }
9749
9944
 
@@ -9898,7 +10093,7 @@ var dividerStyles = i`
9898
10093
 
9899
10094
  [part='label'] {
9900
10095
  color: var(--lt-text-subtle);
9901
- font-family: var(--lt-typography-fontfamily);
10096
+ font-family: var(--lt-typography-fontFamily, 'Hanken Grotesk', sans-serif);
9902
10097
  font-size: 0.75rem;
9903
10098
  font-weight: 600;
9904
10099
  padding-inline: var(--lt-spacing-3);
@@ -10432,15 +10627,47 @@ var Image = class extends ThemeableElement {
10432
10627
  this.alt = "";
10433
10628
  this.rounded = null;
10434
10629
  this.responsive = false;
10630
+ this.loading = "";
10631
+ this._errored = false;
10435
10632
  }
10436
10633
  get _borderRadius() {
10437
10634
  if (this.rounded === null) return void 0;
10438
10635
  return this.rounded || "var(--lt-border-radius)";
10439
10636
  }
10637
+ willUpdate(changed) {
10638
+ if (changed.has("src")) this._errored = false;
10639
+ }
10640
+ _dispatch(name) {
10641
+ this.dispatchEvent(
10642
+ new CustomEvent(name, {
10643
+ detail: { src: this.src },
10644
+ bubbles: true,
10645
+ composed: true
10646
+ })
10647
+ );
10648
+ }
10649
+ _handleLoad() {
10650
+ this._dispatch("lt-load");
10651
+ }
10652
+ _handleError() {
10653
+ this._errored = true;
10654
+ this._dispatch("lt-error");
10655
+ }
10440
10656
  render() {
10657
+ if (this._errored) {
10658
+ return T`<slot name="fallback"></slot>`;
10659
+ }
10441
10660
  const radius = this._borderRadius;
10442
10661
  return T`
10443
- <img part="image" src=${this.src} alt=${this.alt} style=${radius ? `border-radius: ${radius}` : ""} />
10662
+ <img
10663
+ part="image"
10664
+ src=${this.src}
10665
+ alt=${this.alt}
10666
+ loading=${o8(this.loading || void 0)}
10667
+ style=${radius ? `border-radius: ${radius}` : ""}
10668
+ @load=${this._handleLoad}
10669
+ @error=${this._handleError}
10670
+ />
10444
10671
  `;
10445
10672
  }
10446
10673
  };
@@ -10457,6 +10684,12 @@ __decorateClass([
10457
10684
  __decorateClass([
10458
10685
  n4({ type: Boolean, reflect: true })
10459
10686
  ], Image.prototype, "responsive", 2);
10687
+ __decorateClass([
10688
+ n4({ reflect: true })
10689
+ ], Image.prototype, "loading", 2);
10690
+ __decorateClass([
10691
+ r5()
10692
+ ], Image.prototype, "_errored", 2);
10460
10693
  Image = __decorateClass([
10461
10694
  t3("lt-image")
10462
10695
  ], Image);
@@ -10465,7 +10698,7 @@ Image = __decorateClass([
10465
10698
  var paginationStyles = i`
10466
10699
  :host {
10467
10700
  display: inline-flex;
10468
- font-family: 'Hanken Grotesk', sans-serif;
10701
+ font-family: var(--lt-typography-fontFamily, 'Hanken Grotesk', sans-serif);
10469
10702
  }
10470
10703
 
10471
10704
  nav {
@@ -10630,7 +10863,7 @@ Pagination = __decorateClass([
10630
10863
  var datepickerStyles = i`
10631
10864
  :host {
10632
10865
  display: block;
10633
- font-family: 'Hanken Grotesk', sans-serif;
10866
+ font-family: var(--lt-typography-fontFamily, 'Hanken Grotesk', sans-serif);
10634
10867
  }
10635
10868
 
10636
10869
  .wrapper {
@@ -10653,7 +10886,7 @@ var datepickerStyles = i`
10653
10886
  input {
10654
10887
  background: var(--lt-bg-surface);
10655
10888
  border-radius: var(--lt-border-radius);
10656
- border: 1px solid var(--lt-border-default);
10889
+ border: var(--lt-border-width, 1px) solid var(--lt-border-default);
10657
10890
  color: var(--lt-text-default);
10658
10891
  font-family: inherit;
10659
10892
  font-size: 0.9375rem;
@@ -10855,13 +11088,13 @@ var import_icons16 = require("@latty-ds/icons");
10855
11088
  var calendarStyles = i`
10856
11089
  :host {
10857
11090
  display: inline-block;
10858
- font-family: 'Hanken Grotesk', sans-serif;
11091
+ font-family: var(--lt-typography-fontFamily, 'Hanken Grotesk', sans-serif);
10859
11092
  }
10860
11093
 
10861
11094
  .calendar {
10862
11095
  background: var(--lt-bg-default);
10863
11096
  border-radius: var(--lt-border-radius);
10864
- border: 1px solid var(--lt-border-default);
11097
+ border: var(--lt-border-width, 1px) solid var(--lt-border-default);
10865
11098
  display: flex;
10866
11099
  flex-direction: column;
10867
11100
  gap: var(--lt-spacing-2);
@@ -11204,7 +11437,7 @@ var calendarStyles = i`
11204
11437
  .today-btn {
11205
11438
  background: transparent;
11206
11439
  border-radius: var(--lt-border-radius);
11207
- border: 1px solid var(--lt-border-default);
11440
+ border: var(--lt-border-width, 1px) solid var(--lt-border-default);
11208
11441
  color: var(--lt-text-subtle);
11209
11442
  cursor: pointer;
11210
11443
  font-family: inherit;
@@ -11769,7 +12002,7 @@ var dateInputStyles = [
11769
12002
  i`
11770
12003
  :host {
11771
12004
  display: block;
11772
- font-family: 'Hanken Grotesk', sans-serif;
12005
+ font-family: var(--lt-typography-fontFamily, 'Hanken Grotesk', sans-serif);
11773
12006
  }
11774
12007
 
11775
12008
  .wrapper {
@@ -11798,7 +12031,7 @@ var dateInputStyles = [
11798
12031
  align-items: center;
11799
12032
  background: var(--lt-bg-surface);
11800
12033
  border-radius: var(--lt-border-radius);
11801
- border: 1px solid var(--lt-border-default);
12034
+ border: var(--lt-border-width, 1px) solid var(--lt-border-default);
11802
12035
  color: var(--lt-text-default);
11803
12036
  cursor: pointer;
11804
12037
  display: flex;
@@ -12119,7 +12352,7 @@ var comboboxStyles = [
12119
12352
  i`
12120
12353
  :host {
12121
12354
  display: block;
12122
- font-family: 'Hanken Grotesk', sans-serif;
12355
+ font-family: var(--lt-typography-fontFamily, 'Hanken Grotesk', sans-serif);
12123
12356
  position: relative;
12124
12357
  }
12125
12358
 
@@ -12142,7 +12375,7 @@ var comboboxStyles = [
12142
12375
  align-items: center;
12143
12376
  background: var(--lt-bg-surface);
12144
12377
  border-radius: var(--lt-border-radius);
12145
- border: 1px solid var(--lt-border-default);
12378
+ border: var(--lt-border-width, 1px) solid var(--lt-border-default);
12146
12379
  cursor: text;
12147
12380
  display: flex;
12148
12381
  gap: var(--lt-spacing-2);
@@ -12237,7 +12470,7 @@ var comboboxStyles = [
12237
12470
  .dropdown {
12238
12471
  background: var(--lt-bg-surface);
12239
12472
  border-radius: var(--lt-border-radius);
12240
- border: 1px solid var(--lt-border-default);
12473
+ border: var(--lt-border-width, 1px) solid var(--lt-border-default);
12241
12474
  box-shadow: var(--lt-shadow-md);
12242
12475
  max-height: 240px;
12243
12476
  overflow-y: auto;
@@ -12508,7 +12741,7 @@ Combobox = __decorateClass([
12508
12741
  var colorInputStyles = i`
12509
12742
  :host {
12510
12743
  display: inline-block;
12511
- font-family: 'Hanken Grotesk', sans-serif;
12744
+ font-family: var(--lt-typography-fontFamily, 'Hanken Grotesk', sans-serif);
12512
12745
  width: 100%;
12513
12746
  }
12514
12747
 
@@ -12533,7 +12766,7 @@ var colorInputStyles = i`
12533
12766
  display: flex;
12534
12767
  align-items: center;
12535
12768
  background: var(--lt-bg-subtle);
12536
- border: 1px solid var(--lt-border-default);
12769
+ border: var(--lt-border-width, 1px) solid var(--lt-border-default);
12537
12770
  border-radius: var(--lt-border-radius);
12538
12771
  box-sizing: border-box;
12539
12772
  transition:
@@ -12745,6 +12978,7 @@ var ColorInput = class extends ThemeableElement {
12745
12978
  return this._internals.reportValidity();
12746
12979
  }
12747
12980
  _handleChange(e7) {
12981
+ e7.stopPropagation();
12748
12982
  const pickerHex = e7.target.value;
12749
12983
  this.value = formatColor(pickerHex, this.format);
12750
12984
  this.dispatchEvent(
@@ -12768,6 +13002,7 @@ var ColorInput = class extends ThemeableElement {
12768
13002
  type="color"
12769
13003
  class="native-picker"
12770
13004
  .value=${hex || "#000000"}
13005
+ name=${o8(this.name || void 0)}
12771
13006
  ?disabled=${this.disabled}
12772
13007
  aria-label=${this.label || this.placeholder}
12773
13008
  @change=${this._handleChange}