@lmvz-ds/components 0.26.0 → 0.27.1

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 (130) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/cjs/{reactive-controller-host-DrtMkMd7.js → aria-validation-controller-BMV2tv9-.js} +0 -41
  3. package/cjs/{ds.constants-8fh6ItAF.js → ds.constants-BbWUmoDv.js} +2 -0
  4. package/cjs/index.cjs.js +2 -1
  5. package/cjs/list-keyboard-controller-CzZdPBeH.js +173 -0
  6. package/cjs/lmvz-button_2.cjs.entry.js +4 -3
  7. package/cjs/lmvz-checkbox.cjs.entry.js +8 -4
  8. package/cjs/lmvz-chip.cjs.entry.js +4 -3
  9. package/cjs/lmvz-components.cjs.js +1 -1
  10. package/cjs/lmvz-header_2.cjs.entry.js +7 -51
  11. package/cjs/lmvz-input.cjs.entry.js +7 -5
  12. package/cjs/lmvz-menuitem.cjs.entry.js +3 -2
  13. package/cjs/lmvz-modal.cjs.entry.js +3 -2
  14. package/cjs/lmvz-radio.cjs.entry.js +3 -2
  15. package/cjs/lmvz-select.cjs.entry.js +3 -2
  16. package/cjs/lmvz-tab.cjs.entry.js +31 -0
  17. package/cjs/lmvz-tabs.cjs.entry.js +256 -0
  18. package/cjs/lmvz-toggle.cjs.entry.js +4 -3
  19. package/cjs/loader.cjs.js +1 -1
  20. package/cjs/reactive-controller-host-B_lZtcA6.js +43 -0
  21. package/collection/api/ds.constants.js +1 -0
  22. package/collection/collection-manifest.json +2 -0
  23. package/collection/components/lmvz-checkbox/lmvz-checkbox.css +10 -8
  24. package/collection/components/lmvz-checkbox/lmvz-checkbox.js +4 -1
  25. package/collection/components/lmvz-header/lmvz-header.js +3 -3
  26. package/collection/components/lmvz-input/lmvz-input.css +40 -25
  27. package/collection/components/lmvz-input/lmvz-input.js +33 -2
  28. package/collection/components/lmvz-tab/lmvz-tab.css +148 -0
  29. package/collection/components/lmvz-tab/lmvz-tab.js +125 -0
  30. package/collection/components/lmvz-tabs/lmvz-tabs.css +58 -0
  31. package/collection/components/lmvz-tabs/lmvz-tabs.js +399 -0
  32. package/collection/components/lmvz-toggle/lmvz-toggle.js +1 -1
  33. package/collection/index.js +2 -2
  34. package/collection/integration/header-integration/header-integration.js +1 -1
  35. package/collection/utils/aria/list-keyboard-controller.js +151 -28
  36. package/components/index.d.ts +4 -0
  37. package/components/index.d.ts.bak +4 -0
  38. package/components/index.js +1 -1
  39. package/components/lmvz-button-group.js +1 -1
  40. package/components/lmvz-button.js +1 -1
  41. package/components/lmvz-checkbox.js +1 -1
  42. package/components/lmvz-chip.js +1 -1
  43. package/components/lmvz-header.js +1 -1
  44. package/components/lmvz-icon.js +1 -1
  45. package/components/lmvz-input.js +1 -1
  46. package/components/lmvz-menuitem.js +1 -1
  47. package/components/lmvz-modal.js +1 -1
  48. package/components/lmvz-radio.js +1 -1
  49. package/components/lmvz-select.js +1 -1
  50. package/components/lmvz-snackbar.js +1 -1
  51. package/components/lmvz-tab.d.ts +11 -0
  52. package/components/lmvz-tab.d.ts.bak +11 -0
  53. package/components/lmvz-tab.js +1 -0
  54. package/components/lmvz-tabs.d.ts +11 -0
  55. package/components/lmvz-tabs.d.ts.bak +11 -0
  56. package/components/lmvz-tabs.js +1 -0
  57. package/components/lmvz-toggle.js +1 -1
  58. package/components/p-0P2Wb3pE.js +1 -0
  59. package/components/p-2VdcUIrr.js +1 -0
  60. package/components/p-BJEQw1Sz.js +1 -0
  61. package/components/p-Cs7RCOHZ.js +1 -0
  62. package/components/p-CtaMrBNE.js +1 -0
  63. package/components/{p-JAKQdFhF.js → p-DOTK1OW3.js} +1 -1
  64. package/components/p-DjvbwRyl.js +1 -0
  65. package/esm/{reactive-controller-host-ZrGf1F2-.js → aria-validation-controller-D-KO0Asb.js} +1 -41
  66. package/esm/{ds.constants-BOOwq5dE.js → ds.constants-HeV_qTCr.js} +2 -1
  67. package/esm/index.js +1 -1
  68. package/esm/list-keyboard-controller-Coi8XfUH.js +171 -0
  69. package/esm/lmvz-button_2.entry.js +2 -1
  70. package/esm/lmvz-checkbox.entry.js +7 -3
  71. package/esm/lmvz-chip.entry.js +3 -2
  72. package/esm/lmvz-components.js +1 -1
  73. package/esm/lmvz-header_2.entry.js +6 -50
  74. package/esm/lmvz-input.entry.js +6 -4
  75. package/esm/lmvz-menuitem.entry.js +2 -1
  76. package/esm/lmvz-modal.entry.js +2 -1
  77. package/esm/lmvz-radio.entry.js +2 -1
  78. package/esm/lmvz-select.entry.js +2 -1
  79. package/esm/lmvz-tab.entry.js +29 -0
  80. package/esm/lmvz-tabs.entry.js +254 -0
  81. package/esm/lmvz-toggle.entry.js +3 -2
  82. package/esm/loader.js +1 -1
  83. package/esm/reactive-controller-host-FBuCCcFC.js +41 -0
  84. package/hydrate/index.js +467 -36
  85. package/hydrate/index.mjs +467 -36
  86. package/lmvz-components/index.esm.js +1 -1
  87. package/lmvz-components/lmvz-components.esm.js +1 -1
  88. package/lmvz-components/p-43b463bf.entry.js +1 -0
  89. package/lmvz-components/p-6484fbc6.entry.js +1 -0
  90. package/lmvz-components/p-6988c3ea.entry.js +1 -0
  91. package/lmvz-components/p-758dbb82.entry.js +1 -0
  92. package/lmvz-components/p-7b15cdce.entry.js +1 -0
  93. package/lmvz-components/p-8874ff19.entry.js +1 -0
  94. package/lmvz-components/p-CtaMrBNE.js +1 -0
  95. package/lmvz-components/p-FBuCCcFC.js +1 -0
  96. package/lmvz-components/p-HeV_qTCr.js +1 -0
  97. package/lmvz-components/p-a5c921dc.entry.js +1 -0
  98. package/lmvz-components/p-bd23eab3.entry.js +1 -0
  99. package/lmvz-components/p-c5c064f1.entry.js +1 -0
  100. package/lmvz-components/p-c6228cee.entry.js +1 -0
  101. package/lmvz-components/p-cb756f95.entry.js +1 -0
  102. package/lmvz-components/p-da16ff58.entry.js +1 -0
  103. package/lmvz-components/p-hRb38wX6.js +1 -0
  104. package/manifest.json +440 -2
  105. package/package.json +9 -1
  106. package/types/api/ds.constants.d.ts +2 -0
  107. package/types/components/lmvz-checkbox/lmvz-checkbox.d.ts +1 -0
  108. package/types/components/lmvz-header/lmvz-header.d.ts +2 -2
  109. package/types/components/lmvz-input/lmvz-input.d.ts +1 -0
  110. package/types/components/lmvz-tab/lmvz-tab.d.ts +11 -0
  111. package/types/components/lmvz-tabs/lmvz-tabs.d.ts +43 -0
  112. package/types/components.d.ts +165 -0
  113. package/types/index.d.ts +2 -2
  114. package/types/utils/aria/list-keyboard-controller.d.ts +28 -5
  115. package/components/p-BOzeYzKk.js +0 -1
  116. package/components/p-DYa3zcGE.js +0 -1
  117. package/components/p-WLZ7VWNX.js +0 -1
  118. package/components/p-lsUdmjdw.js +0 -1
  119. package/lmvz-components/p-01aeca60.entry.js +0 -1
  120. package/lmvz-components/p-14c3d837.entry.js +0 -1
  121. package/lmvz-components/p-24e63b0a.entry.js +0 -1
  122. package/lmvz-components/p-3da301a6.entry.js +0 -1
  123. package/lmvz-components/p-40228d48.entry.js +0 -1
  124. package/lmvz-components/p-4da9073a.entry.js +0 -1
  125. package/lmvz-components/p-758078db.entry.js +0 -1
  126. package/lmvz-components/p-8dae99f1.entry.js +0 -1
  127. package/lmvz-components/p-BOOwq5dE.js +0 -1
  128. package/lmvz-components/p-CwX1wKkM.js +0 -1
  129. package/lmvz-components/p-e1eaa7a2.entry.js +0 -1
  130. package/lmvz-components/p-f5cece32.entry.js +0 -1
@@ -45,24 +45,45 @@
45
45
  --input-bg: var(--lmvz-semantic-color-surface-input-primary, #ffffff);
46
46
  --input-border-color: var(--lmvz-semantic-color-border-default, #e0e0e0);
47
47
  --input-border-color-hover: var(--lmvz-semantic-color-border-hover, #c7c7c7);
48
- --input-border-color-focus: var(--lmvz-semantic-color-status-on-active, #0e7ab4);
48
+ --input-border-color-focus: var(--lmvz-semantic-color-border-active, #0f8acc);
49
49
  --input-border-width: var(--lmvz-semantic-border-width-default, 1px);
50
- --input-height: 48px; /* From Figma input-size=48 */
51
- --input-padding-x: 16px;
50
+
51
+ /* Size defaults (md) */
52
+ --input-height: var(--lmvz-component-input-size-md, clamp(2.5rem, 2.44rem + 0.26vw, 2.75rem));
53
+ --input-padding-x: var(--lmvz-component-input-padding-md, clamp(0.88rem, 0.84rem + 0.13vw, 1rem));
54
+ --input-gap: var(--lmvz-component-input-gap-md, clamp(0.5rem, 0.44rem + 0.26vw, 0.75rem));
55
+ --input-font-size: var(--lmvz-component-body-md-font-size, clamp(0.88rem, 0.84rem + 0.13vw, 1rem));
52
56
 
53
57
  --label-color: var(--lmvz-semantic-color-on-surface-input-secondary, #545454);
54
58
  --input-text-color: var(--lmvz-semantic-color-on-surface-input-primary, #000000);
55
59
  --helper-text-color: var(--lmvz-semantic-color-on-surface-input-secondary, #545454);
56
60
  --error-text-color: var(--lmvz-semantic-color-status-on-danger, #e52a31);
57
61
 
58
- --label-floating-scale: 0.85; /* 14px -> 12px approx */
59
- --label-minimized-top: -6px;
60
62
  --label-minimized-bg: var(--input-bg);
61
63
  --label-minimized-padding-x: 4px;
62
64
 
63
65
  /* Disabled state uses opacity token */
64
66
  --input-disabled-opacity: var(--lmvz-component-input-disabled-opacity, 40%);
65
67
  }
68
+ /* Size variants */
69
+ :host([size='sm']) {
70
+ --input-height: var(--lmvz-component-input-size-sm, clamp(2rem, 1.94rem + 0.26vw, 2.25rem));
71
+ --input-padding-x: var(--lmvz-component-input-padding-sm, clamp(0.75rem, 0.72rem + 0.13vw, 0.88rem));
72
+ --input-gap: var(--lmvz-component-input-gap-sm, clamp(0.38rem, 0.31rem + 0.26vw, 0.63rem));
73
+ --input-font-size: var(--lmvz-component-body-sm-font-size, clamp(0.69rem, 0.67rem + 0.06vw, 0.75rem));
74
+ }
75
+ :host([size='md']) {
76
+ --input-height: var(--lmvz-component-input-size-md, clamp(2.5rem, 2.44rem + 0.26vw, 2.75rem));
77
+ --input-padding-x: var(--lmvz-component-input-padding-md, clamp(0.88rem, 0.84rem + 0.13vw, 1rem));
78
+ --input-gap: var(--lmvz-component-input-gap-md, clamp(0.5rem, 0.44rem + 0.26vw, 0.75rem));
79
+ --input-font-size: var(--lmvz-component-body-md-font-size, clamp(0.88rem, 0.84rem + 0.13vw, 1rem));
80
+ }
81
+ :host([size='lg']) {
82
+ --input-height: var(--lmvz-component-input-size-lg, clamp(2.75rem, 2.69rem + 0.26vw, 3rem));
83
+ --input-padding-x: var(--lmvz-component-input-padding-lg, clamp(1rem, 0.94rem + 0.26vw, 1.25rem));
84
+ --input-gap: var(--lmvz-component-input-gap-lg, clamp(0.75rem, 0.69rem + 0.26vw, 1rem));
85
+ --input-font-size: var(--lmvz-component-body-lg-font-size, clamp(1rem, 0.97rem + 0.13vw, 1.13rem));
86
+ }
66
87
  .input-container {
67
88
  display: flex;
68
89
  flex-direction: column;
@@ -79,7 +100,7 @@
79
100
  padding: 0 var(--input-padding-x);
80
101
  position: relative;
81
102
  transition: border-color 0.2s ease;
82
- gap: 8px; /* input-gap-x */
103
+ gap: var(--input-gap);
83
104
  }
84
105
  :host([disabled]) .input-wrapper {
85
106
  opacity: var(--input-disabled-opacity);
@@ -87,7 +108,7 @@
87
108
  pointer-events: none;
88
109
  }
89
110
  .input-container:focus-within .input-wrapper {
90
- /* border-color: var(--input-border-color-focus); */
111
+ border-color: var(--input-border-color-focus);
91
112
  outline: var(--lmvz-ds-outline, 1px solid #0e7ab4);
92
113
  outline-offset: var(--lmvz-ds-outline-offset, clamp(0.25rem, 0.19rem + 0.26vw, 0.5rem));
93
114
  }
@@ -95,10 +116,6 @@
95
116
  :host([error]) .input-wrapper {
96
117
  border-color: var(--error-text-color);
97
118
  }
98
- :host([error]):focus-within .input-wrapper {
99
- border-color: var(--error-text-color);
100
- outline-color: var(--error-text-color);
101
- }
102
119
  /* Hover State - Use CSS pseudo-selector instead of class */
103
120
  :host(:not([disabled])) .input-wrapper:hover {
104
121
  border-color: var(--input-border-color-hover);
@@ -116,26 +133,26 @@ label {
116
133
  top: 50%;
117
134
  transform: translateY(-50%);
118
135
  color: var(--label-color);
119
- font-size: 14px; /* body-md size from Figma */
120
- font-family: Router, sans-serif;
136
+ font-size: var(--input-font-size);
121
137
  font-weight: 400;
122
138
  pointer-events: none;
123
139
  transition: all 0.2s ease-out;
124
140
  background-color: transparent;
125
141
  padding: 0;
126
142
  margin: 0;
127
- line-height: normal;
143
+ line-height: 1.5;
128
144
  white-space: nowrap;
129
145
  }
130
146
  label.floating {
131
- top: 0; /* Align to top border area */
132
- transform: translateY(-50%) scale(0.85); /* Scale down */
147
+ top: 0.5em;
148
+ transform: translateY(-100%);
133
149
  transform-origin: left top;
150
+ font-size: var(--lmvz-component-body-2xs-font-size, clamp(0.5rem, 0.47rem + 0.13vw, 0.63rem));
134
151
  background-color: var(--label-minimized-bg);
135
152
  padding: 0 var(--label-minimized-padding-x);
136
153
  left: -2px; /* Adjust for padding/border visual alignment */
137
154
  color: var(--input-text-color); /* Darker when floating */
138
- font-weight: 500; /* Medium weight when minimized */
155
+ font-weight: 400;
139
156
  }
140
157
  .required-indicator {
141
158
  color: var(--error-text-color);
@@ -148,11 +165,11 @@ input {
148
165
  height: 100%;
149
166
  color: var(--input-text-color);
150
167
  font-family: inherit;
151
- font-size: 14px;
168
+ font-size: var(--input-font-size);
152
169
  outline: none;
153
170
  padding: 0;
154
171
  margin: 0;
155
- font-weight: 400; /* Regular weight */
172
+ font-weight: 400;
156
173
  }
157
174
  input::placeholder {
158
175
  color: var(--label-color);
@@ -162,19 +179,17 @@ div:empty {
162
179
  }
163
180
  [role='status'] {
164
181
  padding-top: 8px; /* space-gap-component-input-helper-text */
165
- padding-left: 12px; /* input-md-padding-x for helper text indent */
182
+ padding-left: var(--input-padding-x);
166
183
  font-size: 12px;
167
184
  color: var(--helper-text-color);
168
- font-family: Router, sans-serif;
169
- font-weight: 500; /* Medium weight */
185
+ font-weight: 400;
170
186
  }
171
187
  [role='alert'] {
172
188
  padding-top: 8px;
173
- padding-left: 12px;
189
+ padding-left: var(--input-padding-x);
174
190
  font-size: 12px;
175
191
  color: var(--error-text-color);
176
- font-family: Router, sans-serif;
177
- font-weight: 500;
192
+ font-weight: 400;
178
193
  }
179
194
  ::slotted(lmvz-button) {
180
195
  /* Minimize padding for (icon) buttons due to height constraints */
@@ -56,6 +56,7 @@ export class LmvzInput extends ReactiveControllerHost {
56
56
  max;
57
57
  step;
58
58
  form;
59
+ size = 'md';
59
60
  get error() {
60
61
  return this.errorFromProp || this.nativeError;
61
62
  }
@@ -150,9 +151,9 @@ export class LmvzInput extends ReactiveControllerHost {
150
151
  render() {
151
152
  const hasValue = Boolean(this.value);
152
153
  const shouldFloatLabel = hasValue || Boolean(this.placeholder);
153
- return (h("div", { key: 'b8c3aaccafb89725aaa157a72633c67ae8b4d58c', class: classNames('input-container', {
154
+ return (h("div", { key: '4be6846147fec950acad0c0cb8643bfa69233935', class: classNames('input-container', {
154
155
  'interaction-filled': hasValue,
155
- }) }, h("div", { key: '80261c486d3bfe61aa060d50e9a6774c961339a1', class: "input-wrapper" }, h("slot", { key: '6856dd64991251adc63d8064ed63e9cf7b1773a2', name: "before-input" }), h("div", { key: 'c975357079efef465c56fb50cbb404bd1cbe3fb1', class: "label-input-group" }, h("label", { key: '798417172f8411be97866edbffbd2bb98c308c69', htmlFor: this.inputId, class: classNames({ floating: shouldFloatLabel }) }, this.label, this.required && (h("span", { key: '9fd59f3d93c984304e6c8ae6531df82bc0057243', class: "required-indicator", "aria-hidden": "true" }, ' ', "*"))), h("input", { key: 'a4e1133b4891c01044365eb3c97ee234c84c9bac', id: this.inputId, ref: (el) => (this.nativeInputElement = el), type: this.type, min: this.min, max: this.max, step: this.step, value: this.value, name: this.name, placeholder: this.placeholder, disabled: this.disabled, readOnly: this.readonly, required: this.required, form: this.form, autocomplete: this.autocomplete, inputmode: this.inputmode, autocorrect: this.autocorrect, autocapitalize: this.autocapitalize, spellcheck: this.spellcheck, autofocus: this.autofocus, minlength: this.minlength, maxlength: this.maxlength, pattern: this.pattern, "aria-invalid": this.error ? 'true' : 'false', "aria-required": this.required ? 'true' : 'false', "aria-describedby": this.describedBy, "aria-errormessage": this.errorId, onInput: this.handleInput, onChange: this.handleChange, onFocus: this.handleFocus, onBlur: this.handleBlur })), h("slot", { key: 'c774f03e704889b524f305c00d60c0902abdff5f', name: "after-input" })), h("div", { key: 'f513746541c71ce0d67c26eb220b54408fc3f136', id: this.helperId, role: "status" }, this.helperText || null), h("div", { key: '197683e55807bf100ca1543f5b84b14e910e721f', id: this.errorId, role: "alert" }, (this.showErrorMessage && this.errorMessage) || null)));
156
+ }) }, h("div", { key: 'ae9bf8ffd91b73a86c2ebd21af051efe26a1d5fd', class: "input-wrapper" }, h("slot", { key: '36b3f7d97a11296aeead44c4d8ab4f0f8edf62d1', name: "before-input" }), h("div", { key: '4320f461ffa908730cd898863f43644aa3677081', class: "label-input-group" }, h("label", { key: '1dd51cf16f064303fd9c5249cbe3a357c7515ddc', htmlFor: this.inputId, class: classNames({ floating: shouldFloatLabel }) }, this.label, this.required && (h("span", { key: '74bb8124b1126501201eb7c3361c6cf7f17dda5c', class: "required-indicator", "aria-hidden": "true" }, ' ', "*"))), h("input", { key: '9cff77bea65c50331a6804d7039672bbe01e710c', id: this.inputId, ref: (el) => (this.nativeInputElement = el), type: this.type, min: this.min, max: this.max, step: this.step, value: this.value, name: this.name, placeholder: this.placeholder, disabled: this.disabled, readOnly: this.readonly, required: this.required, form: this.form, autocomplete: this.autocomplete, inputmode: this.inputmode, autocorrect: this.autocorrect, autocapitalize: this.autocapitalize, spellcheck: this.spellcheck, autofocus: this.autofocus, minlength: this.minlength, maxlength: this.maxlength, pattern: this.pattern, "aria-invalid": this.error ? 'true' : 'false', "aria-required": this.required ? 'true' : 'false', "aria-describedby": this.describedBy, "aria-errormessage": this.errorId, onInput: this.handleInput, onChange: this.handleChange, onFocus: this.handleFocus, onBlur: this.handleBlur })), h("slot", { key: '689a3aa53e22f89173893adbc72eaf4953a2c7f6', name: "after-input" })), h("div", { key: 'd29b9ea209c138d0de09f71042f539a56ed4f9fa', id: this.helperId, role: "status" }, this.helperText || null), h("div", { key: '8be227367abadb0e001d9475f0793aed31dbb64a', id: this.errorId, role: "alert" }, (this.showErrorMessage && this.errorMessage) || null)));
156
157
  }
157
158
  static get is() { return "lmvz-input"; }
158
159
  static get encapsulation() { return "scoped"; }
@@ -663,6 +664,36 @@ export class LmvzInput extends ReactiveControllerHost {
663
664
  "reflect": false,
664
665
  "attribute": "form"
665
666
  },
667
+ "size": {
668
+ "type": "string",
669
+ "mutable": false,
670
+ "complexType": {
671
+ "original": "Input.Size",
672
+ "resolved": "\"lg\" | \"md\" | \"sm\"",
673
+ "references": {
674
+ "Input": {
675
+ "location": "import",
676
+ "path": "../../api",
677
+ "id": "src/api/index.d.ts::Input",
678
+ "referenceLocation": "Input"
679
+ }
680
+ }
681
+ },
682
+ "required": false,
683
+ "optional": false,
684
+ "docs": {
685
+ "tags": [{
686
+ "name": "default",
687
+ "text": "'md'"
688
+ }],
689
+ "text": "Size variant of the input"
690
+ },
691
+ "getter": false,
692
+ "setter": false,
693
+ "reflect": true,
694
+ "attribute": "size",
695
+ "defaultValue": "'md'"
696
+ },
666
697
  "error": {
667
698
  "type": "boolean",
668
699
  "mutable": false,
@@ -0,0 +1,148 @@
1
+
2
+ @layer lmvz-ds.reset, lmvz-ds.theme, lmvz-ds.components, lmvz-ds.overrides;
3
+ /**
4
+ * This defines the order of our lmvz-ds' CSS layers. See readme.md for details.
5
+ * Important: Always import this file _before_ layering your own styles!
6
+ */
7
+ @layer lmvz-ds.reset {
8
+ body {
9
+ margin: 0;
10
+ }
11
+
12
+ h1,
13
+ h2,
14
+ h3,
15
+ h4,
16
+ h5,
17
+ h6 {
18
+ margin: 0;
19
+ }
20
+
21
+ *[hidden] {
22
+ display: none !important;
23
+ }
24
+
25
+ }
26
+ :host {
27
+ display: inline-block;
28
+ flex: none;
29
+ }
30
+ :host([disabled]) {
31
+ cursor: not-allowed;
32
+ pointer-events: none;
33
+ }
34
+ button {
35
+ --lmvz-tab-color: var(--lmvz-semantic-color-int-on-secondary, #000000);
36
+ --lmvz-tab-background: var(--lmvz-semantic-color-int-secondary, #f0f0f0);
37
+
38
+ position: relative;
39
+ display: inline-flex;
40
+ flex-direction: column;
41
+ align-items: center;
42
+ justify-content: center;
43
+ gap: var(--lmvz-component-component-spacing-0-default, 4px);
44
+
45
+ padding-top: var(--lmvz-dimension-8-12, clamp(0.5rem, 0.44rem + 0.26vw, 0.75rem));
46
+ padding-bottom: var(--lmvz-dimension-8-14, clamp(0.5rem, 0.41rem + 0.39vw, 0.88rem));
47
+ padding-inline: var(--lmvz-dimension-16-24, clamp(1rem, 0.88rem + 0.52vw, 1.5rem));
48
+
49
+ border: none;
50
+ border-radius: var(--lmvz-semantic-border-radius-round, 999px);
51
+ background: var(--lmvz-tab-background);
52
+ color: var(--lmvz-tab-color);
53
+
54
+ font: var(--lmvz-typography-body-md-strong, 500 clamp(0.88rem, 0.84rem + 0.13vw, 1rem) / 1.5
55
+ Router);
56
+ cursor: pointer;
57
+ white-space: nowrap;
58
+ transition:
59
+ background-color 0.15s ease,
60
+ color 0.15s ease;
61
+ }
62
+ button:focus-visible {
63
+ outline: var(--lmvz-ds-outline, 1px solid #0e7ab4);
64
+ outline-offset: var(--lmvz-ds-outline-offset, clamp(0.25rem, 0.19rem + 0.26vw, 0.5rem));
65
+ z-index: 1;
66
+ }
67
+ button:disabled {
68
+ opacity: var(--lmvz-component-input-disabled-opacity, 40%);
69
+ pointer-events: none;
70
+ cursor: not-allowed;
71
+ }
72
+ button:hover {
73
+ --lmvz-tab-background: var(--lmvz-semantic-color-int-tertiary-hover, #f0f0f0);
74
+ --lmvz-tab-color: var(--lmvz-semantic-color-int-on-secondary-hover, #000000);
75
+ }
76
+ button:active {
77
+ --lmvz-tab-background: var(--lmvz-semantic-color-int-tertiary-active, #e0e0e0);
78
+ --lmvz-tab-color: var(--lmvz-semantic-color-int-on-secondary-active, #000000);
79
+ }
80
+ :host([has-media]) {
81
+ --lmvz-tab-color: var(--lmvz-semantic-color-int-on-tertiary, #000000);
82
+ --lmvz-tab-background: var(--lmvz-semantic-color-int-tertiary, #ffffff);
83
+ }
84
+ :host([has-media]) button {
85
+ padding-top: var(--lmvz-dimension-16-18, clamp(1rem, 0.97rem + 0.13vw, 1.13rem));
86
+ padding-bottom: var(--lmvz-dimension-12-14, clamp(0.75rem, 0.72rem + 0.13vw, 0.88rem));
87
+ padding-inline: var(--lmvz-dimension-40-48, clamp(2.5rem, 2.38rem + 0.52vw, 3rem));
88
+ border-radius: var(--lmvz-semantic-border-radius-lg, 14px);
89
+ }
90
+ :host([has-media]) button:hover {
91
+ --lmvz-tab-color: var(--lmvz-semantic-color-int-on-tertiary-hover, #000000);
92
+ }
93
+ :host([has-media]) button:active {
94
+ --lmvz-tab-color: var(--lmvz-semantic-color-int-on-tertiary-active, #000000);
95
+ }
96
+ :host([selected]) button {
97
+ --lmvz-tab-color: var(--lmvz-semantic-color-status-on-active, #0e7ab4);
98
+ --lmvz-tab-background: var(--lmvz-semantic-color-status-active, #f6fbfe);
99
+ }
100
+ :host([selected][has-media]) button:hover,
101
+ :host([selected][has-media]) button:active {
102
+ --lmvz-tab-color: var(--lmvz-semantic-color-status-on-active, #0e7ab4);
103
+ }
104
+ .indicator {
105
+ /* TODO: replace with a design token if one is introduced for tab indicator border */
106
+ --lmvz-tab-indicator-size: 4px;
107
+
108
+ position: absolute;
109
+ background: linear-gradient(to right, var(--lmvz-semantic-color-gradient-main-1, #0e7ab4), var(--lmvz-semantic-color-gradient-main-2, #0e7ab4));
110
+ opacity: 0;
111
+ transition: opacity 0.15s ease;
112
+
113
+ /* Horizontal: full-width bar at bottom */
114
+ inset-block-end: 0;
115
+ inset-inline: 0;
116
+ block-size: var(--lmvz-tab-indicator-size);
117
+ border-start-start-radius: 0;
118
+ border-start-end-radius: 0;
119
+ border-end-start-radius: var(--lmvz-tab-indicator-size);
120
+ border-end-end-radius: var(--lmvz-tab-indicator-size);
121
+ }
122
+ :host([selected][has-media]) .indicator {
123
+ opacity: 1;
124
+ }
125
+ :host([vertical]) button {
126
+ border-radius: var(--lmvz-semantic-border-radius-round, 999px);
127
+ }
128
+ :host([vertical]) .indicator {
129
+ /* Vertical: narrow bar at inline-start edge */
130
+ inset-block: 0;
131
+ inset-inline: 0 auto;
132
+ inline-size: var(--lmvz-tab-indicator-size);
133
+ block-size: auto;
134
+ border-radius: 0 var(--lmvz-tab-indicator-size) var(--lmvz-tab-indicator-size) 0;
135
+ background: linear-gradient(to bottom, var(--lmvz-semantic-color-gradient-main-1, #0e7ab4), var(--lmvz-semantic-color-gradient-main-2, #0e7ab4));
136
+ }
137
+ ::slotted(*) {
138
+ --lmvz-component-color: var(--lmvz-tab-color);
139
+ }
140
+ ::slotted(lmvz-icon) {
141
+ --lmvz-component-size: var(--lmvz-dimension-16-18, clamp(1rem, 0.97rem + 0.13vw, 1.13rem));
142
+ }
143
+ :host([has-media]) ::slotted(lmvz-icon) {
144
+ --lmvz-component-size: var(--lmvz-component-icon-size-lg, clamp(1.5rem, 1.44rem + 0.26vw, 1.75rem));
145
+ }
146
+ .label {
147
+ display: contents;
148
+ }
@@ -0,0 +1,125 @@
1
+ import { Host, h } from "@stencil/core";
2
+ export class LmvzTab {
3
+ el;
4
+ mediaSlotEl;
5
+ value;
6
+ disabled = false;
7
+ selected = false;
8
+ vertical = false;
9
+ handleMediaSlotChange = () => {
10
+ this.el.toggleAttribute('has-media', (this.mediaSlotEl?.assignedElements() ?? []).length > 0);
11
+ };
12
+ componentDidLoad() {
13
+ this.handleMediaSlotChange();
14
+ this.el.dispatchEvent(new CustomEvent('lmvzTabReady', { bubbles: true, composed: false }));
15
+ }
16
+ render() {
17
+ return (h(Host, { key: 'f02b0624ac752f25e3996071ab88be1364dd27c3' }, h("button", { key: '1666fea6a68c66d5bf88141660067a66e0164891', part: "button", role: "tab", disabled: this.disabled, "aria-selected": String(this.selected), "aria-disabled": this.disabled ? 'true' : undefined, tabIndex: 0 }, h("slot", { key: '23d1ef98da2a72efa3a5ec1563298fc4c08da441', name: "media", ref: (el) => (this.mediaSlotEl = el), onSlotchange: this.handleMediaSlotChange }), h("span", { key: 'bd96765d2228817c621a2ad4fd4f3b05fcdb3a21', class: "label" }, h("slot", { key: 'ef51c25169a1d993797a86b89fa296218e264969' })), h("span", { key: '28dda1631d62325a29e285909ee3f1c0ea9da3ad', "aria-hidden": "true", part: "indicator", class: "indicator" }))));
18
+ }
19
+ static get is() { return "lmvz-tab"; }
20
+ static get encapsulation() { return "shadow"; }
21
+ static get delegatesFocus() { return true; }
22
+ static get originalStyleUrls() {
23
+ return {
24
+ "$": ["lmvz-tab.css"]
25
+ };
26
+ }
27
+ static get styleUrls() {
28
+ return {
29
+ "$": ["lmvz-tab.css"]
30
+ };
31
+ }
32
+ static get properties() {
33
+ return {
34
+ "value": {
35
+ "type": "string",
36
+ "mutable": false,
37
+ "complexType": {
38
+ "original": "string",
39
+ "resolved": "string",
40
+ "references": {}
41
+ },
42
+ "required": true,
43
+ "optional": false,
44
+ "docs": {
45
+ "tags": [],
46
+ "text": "Unique value that identifies this tab within its `lmvz-tabs` parent.\nThe parent uses this to track the selected tab."
47
+ },
48
+ "getter": false,
49
+ "setter": false,
50
+ "reflect": true,
51
+ "attribute": "value"
52
+ },
53
+ "disabled": {
54
+ "type": "boolean",
55
+ "mutable": false,
56
+ "complexType": {
57
+ "original": "boolean",
58
+ "resolved": "boolean",
59
+ "references": {}
60
+ },
61
+ "required": false,
62
+ "optional": false,
63
+ "docs": {
64
+ "tags": [{
65
+ "name": "default",
66
+ "text": "false"
67
+ }],
68
+ "text": "Whether the tab is disabled.\nDisabled tabs are not activatable and are skipped by arrow-key navigation in `lmvz-tabs`."
69
+ },
70
+ "getter": false,
71
+ "setter": false,
72
+ "reflect": true,
73
+ "attribute": "disabled",
74
+ "defaultValue": "false"
75
+ },
76
+ "selected": {
77
+ "type": "boolean",
78
+ "mutable": false,
79
+ "complexType": {
80
+ "original": "boolean",
81
+ "resolved": "boolean",
82
+ "references": {}
83
+ },
84
+ "required": false,
85
+ "optional": false,
86
+ "docs": {
87
+ "tags": [{
88
+ "name": "default",
89
+ "text": "false"
90
+ }],
91
+ "text": "Whether this tab is currently selected.\nSet by the parent `lmvz-tabs` component \u2014 do not set manually."
92
+ },
93
+ "getter": false,
94
+ "setter": false,
95
+ "reflect": true,
96
+ "attribute": "selected",
97
+ "defaultValue": "false"
98
+ },
99
+ "vertical": {
100
+ "type": "boolean",
101
+ "mutable": false,
102
+ "complexType": {
103
+ "original": "boolean",
104
+ "resolved": "boolean",
105
+ "references": {}
106
+ },
107
+ "required": false,
108
+ "optional": false,
109
+ "docs": {
110
+ "tags": [{
111
+ "name": "default",
112
+ "text": "false"
113
+ }],
114
+ "text": "Vertical orientation context.\nSet by the parent `lmvz-tabs` when `orientation=\"vertical\"` \u2014 do not set manually.\nWhen true the selection indicator moves to the inline-start edge."
115
+ },
116
+ "getter": false,
117
+ "setter": false,
118
+ "reflect": true,
119
+ "attribute": "vertical",
120
+ "defaultValue": "false"
121
+ }
122
+ };
123
+ }
124
+ static get elementRef() { return "el"; }
125
+ }
@@ -0,0 +1,58 @@
1
+
2
+ @layer lmvz-ds.reset, lmvz-ds.theme, lmvz-ds.components, lmvz-ds.overrides;
3
+ /**
4
+ * This defines the order of our lmvz-ds' CSS layers. See readme.md for details.
5
+ * Important: Always import this file _before_ layering your own styles!
6
+ */
7
+ @layer lmvz-ds.reset {
8
+ body {
9
+ margin: 0;
10
+ }
11
+
12
+ h1,
13
+ h2,
14
+ h3,
15
+ h4,
16
+ h5,
17
+ h6 {
18
+ margin: 0;
19
+ }
20
+
21
+ *[hidden] {
22
+ display: none !important;
23
+ }
24
+
25
+ }
26
+ :host {
27
+ display: inline-block;
28
+ }
29
+ .tablist {
30
+ display: inline-flex;
31
+ flex-direction: row;
32
+ background: var(--lmvz-semantic-color-int-secondary, #f0f0f0);
33
+ border-radius: var(--lmvz-semantic-border-radius-round, 999px);
34
+ /* Horizontal overflow: tabs scroll when they overflow the container */
35
+ overflow: auto hidden;
36
+ /* Hide scrollbar visually while keeping scroll functionality */
37
+ scrollbar-width: none; /* Firefox */
38
+ }
39
+ .tablist::-webkit-scrollbar {
40
+ display: none; /* Chrome/Safari */
41
+ }
42
+ :host([has-icon-tabs]) .tablist {
43
+ gap: var(--lmvz-dimension-6-8, clamp(0.38rem, 0.34rem + 0.13vw, 0.5rem));
44
+ background: none;
45
+ border-radius: 0;
46
+ }
47
+ /* ── Vertical orientation ────────────────────────────────────────────────── */
48
+ :host([orientation='vertical']) .tablist {
49
+ flex-direction: column;
50
+ overflow: visible auto;
51
+ }
52
+ /* The panels slot renders inline; individual panels control their own display */
53
+ ::slotted([role='tabpanel']) {
54
+ display: block;
55
+ }
56
+ ::slotted([role='tabpanel'][hidden]) {
57
+ display: none;
58
+ }