@vialiq/web-components 0.18.0 → 0.20.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.
package/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # @vialiq/web-components
1
+ ,# @vialiq/web-components
2
2
 
3
3
  Buildable and publishable Lit web component library for the Vi design system.
4
4
 
@@ -20,6 +20,7 @@ npm install @vialiq/web-components lit
20
20
  Since these components are built using standard Custom Elements APIs, they are compatible with any web framework or vanilla web stack.
21
21
 
22
22
  ### Subpath Exports & Tree-Shaking
23
+
23
24
  To keep your bundle sizes minimal, import only the components you need:
24
25
 
25
26
  ```ts
@@ -39,9 +40,11 @@ import { registerIcons, ViButton } from '@vialiq/web-components';
39
40
  ### Framework Guides
40
41
 
41
42
  #### 1. React
43
+
42
44
  React 19 supports Custom Elements natively. If you are using React <19, you must set properties and custom events manually via `ref` or use custom wrapper packages.
43
45
 
44
46
  **React 19 Example:**
47
+
45
48
  ```tsx
46
49
  import React from 'react';
47
50
  import '@vialiq/web-components/button';
@@ -49,38 +52,43 @@ import '@vialiq/web-components/input';
49
52
 
50
53
  export function SearchForm() {
51
54
  return (
52
- <form onSubmit={(e) => { e.preventDefault(); console.log('Submitted'); }}>
53
- <vi-input
54
- name="query"
55
- placeholder="Search..."
56
- required
57
- onvialiq-input={(e: any) => console.log(e.detail.value)}
58
- />
59
- <vi-button type="submit" variant="primary">Search</vi-button>
55
+ <form
56
+ onSubmit={(e) => {
57
+ e.preventDefault();
58
+ console.log('Submitted');
59
+ }}
60
+ >
61
+ <vi-input name="query" placeholder="Search..." required onvialiq-input={(e: any) => console.log(e.detail.value)} />
62
+ <vi-button type="submit" variant="primary">
63
+ Search
64
+ </vi-button>
60
65
  </form>
61
66
  );
62
67
  }
63
68
  ```
64
69
 
65
70
  #### 2. Vue
71
+
66
72
  Vue supports custom elements seamlessly out-of-the-box. Register the tags so Vue's compiler knows not to treat them as Vue components:
67
73
 
68
74
  **vite.config.ts:**
75
+
69
76
  ```ts
70
77
  export default defineConfig({
71
78
  plugins: [
72
79
  vue({
73
80
  template: {
74
81
  compilerOptions: {
75
- isCustomElement: (tag) => tag.startsWith('vi-')
76
- }
77
- }
78
- })
79
- ]
82
+ isCustomElement: (tag) => tag.startsWith('vi-'),
83
+ },
84
+ },
85
+ }),
86
+ ],
80
87
  });
81
88
  ```
82
89
 
83
90
  **Vue Component Template:**
91
+
84
92
  ```html
85
93
  <template>
86
94
  <div>
@@ -91,9 +99,11 @@ export default defineConfig({
91
99
  ```
92
100
 
93
101
  #### 3. Angular
102
+
94
103
  To use Custom Elements in Angular, you must add the `CUSTOM_ELEMENTS_SCHEMA` to the `schemas` array of the `@Component` (for standalone components) or `@NgModule` where they are consumed.
95
104
 
96
105
  **Standalone Component Setup:**
106
+
97
107
  ```typescript
98
108
  import { Component, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
99
109
 
@@ -101,7 +111,7 @@ import { Component, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
101
111
  selector: 'app-search-form',
102
112
  standalone: true,
103
113
  templateUrl: './search-form.component.html',
104
- schemas: [CUSTOM_ELEMENTS_SCHEMA]
114
+ schemas: [CUSTOM_ELEMENTS_SCHEMA],
105
115
  })
106
116
  export class SearchFormComponent {
107
117
  searchQuery = '';
@@ -114,19 +124,17 @@ export class SearchFormComponent {
114
124
  ```
115
125
 
116
126
  **search-form.component.html Template:**
127
+
117
128
  ```html
118
129
  <div>
119
- <vi-input
120
- [value]="searchQuery"
121
- (vialiq-input)="onInput($event)"
122
- placeholder="Search catalog..."
123
- ></vi-input>
124
-
130
+ <vi-input [value]="searchQuery" (vialiq-input)="onInput($event)" placeholder="Search catalog..."></vi-input>
131
+
125
132
  <vi-button variant="primary">Search</vi-button>
126
133
  </div>
127
134
  ```
128
135
 
129
136
  #### 4. Next.js (SSR / React Server Components)
137
+
130
138
  Custom Elements must register on the browser's `window` object. Next.js and server-side rendering environments require lazy-loading or dynamic imports to ensure registration occurs client-side.
131
139
 
132
140
  ```tsx
@@ -144,29 +152,32 @@ export default function MyClientComponent() {
144
152
  }
145
153
  ```
146
154
 
147
-
148
155
  ---
149
156
 
150
157
  ## Component API & Detailed Examples
151
158
 
152
159
  ### Button ([vi-button](./src/button/vi-button.ts))
160
+
153
161
  The [ViButton](./src/button/vi-button.ts) is a versatile button component that wraps a native `<button>` element with keyboard interaction, focus indicators, visual variations, and slot options.
154
162
 
155
163
  #### Properties & Attributes
156
- | Attribute | Property | Type | Default | Description |
157
- | :--- | :--- | :--- | :--- | :--- |
158
- | `variant` | `variant` | `'primary'\|'secondary'\|'danger'\|'success'\|'info'\|'ghost'` | `'primary'` | Visual design style. |
159
- | `size` | `size` | `'xs'\|'sm'\|'md'\|'lg'` | `'md'` | Sizing scale. |
160
- | `icon-placement` | `iconPlacement` | `'start'\|'end'` | `'start'` | Location of the icon relative to the label. |
161
- | `full-width` | `fullWidth` | `boolean` | `false` | Sets width to 100% of container. |
162
- | `icon-only` | `iconOnly` | `boolean` | `false` | Squares padding and matches dimensions for an icon-only layout. |
163
- | `disabled` | `disabled` | `boolean` | `false` | Disables button interactions and sets `tabindex="-1"`. |
164
+
165
+ | Attribute | Property | Type | Default | Description |
166
+ | :--------------- | :-------------- | :------------------------------------------------------------- | :---------- | :-------------------------------------------------------------- |
167
+ | `variant` | `variant` | `'primary'\|'secondary'\|'danger'\|'success'\|'info'\|'ghost'` | `'primary'` | Visual design style. |
168
+ | `size` | `size` | `'xs'\|'sm'\|'md'\|'lg'` | `'md'` | Sizing scale. |
169
+ | `icon-placement` | `iconPlacement` | `'start'\|'end'` | `'start'` | Location of the icon relative to the label. |
170
+ | `full-width` | `fullWidth` | `boolean` | `false` | Sets width to 100% of container. |
171
+ | `icon-only` | `iconOnly` | `boolean` | `false` | Squares padding and matches dimensions for an icon-only layout. |
172
+ | `disabled` | `disabled` | `boolean` | `false` | Disables button interactions and sets `tabindex="-1"`. |
164
173
 
165
174
  #### Slots
175
+
166
176
  - **Default Slot**: Button label (text/content).
167
177
  - **`icon` Slot**: Container for standard icons.
168
178
 
169
179
  #### CSS Parts
180
+
170
181
  - `button`: The native internal `<button>` element.
171
182
  - `icon`: The icon wrapper element.
172
183
  - `label`: The text label span wrapper.
@@ -174,6 +185,7 @@ The [ViButton](./src/button/vi-button.ts) is a versatile button component that w
174
185
  #### Snippets
175
186
 
176
187
  **Standard Button Variants:**
188
+
177
189
  ```html
178
190
  <vi-button variant="primary">Primary Action</vi-button>
179
191
  <vi-button variant="secondary">Secondary Action</vi-button>
@@ -182,6 +194,7 @@ The [ViButton](./src/button/vi-button.ts) is a versatile button component that w
182
194
  ```
183
195
 
184
196
  **Sizes & Layouts:**
197
+
185
198
  ```html
186
199
  <vi-button size="xs">Extra Small</vi-button>
187
200
  <vi-button size="sm">Small</vi-button>
@@ -193,6 +206,7 @@ The [ViButton](./src/button/vi-button.ts) is a versatile button component that w
193
206
  ```
194
207
 
195
208
  **Icons Support:**
209
+
196
210
  ```html
197
211
  <!-- Icon at start (default) -->
198
212
  <vi-button>
@@ -215,34 +229,40 @@ The [ViButton](./src/button/vi-button.ts) is a versatile button component that w
215
229
  ---
216
230
 
217
231
  ### Input ([vi-input](./src/input/vi-input.ts))
232
+
218
233
  The [ViInput](./src/input/vi-input.ts) component is a form-associated custom text input control. It wraps a native single-line input field and automatically supports accessibility features, validation states, helper text slots, and custom style configuration.
219
234
 
220
235
  #### Properties & Attributes
221
- | Attribute | Property | Type | Default | Description |
222
- | :--- | :--- | :--- | :--- | :--- |
223
- | `type` | `type` | `'text'\|'email'\|'password'\|'search'\|'tel'\|'url'\|'number'` | `'text'` | Renders appropriate input format. |
224
- | `placeholder` | `placeholder` | `string` | `''` | Input placeholder text. |
225
- | `name` | `name` | `string` | `''` | Form participation field name. |
226
- | `value` | `value` | `string` | `''` | Controlled input value. |
227
- | `disabled` | `disabled` | `boolean` | `false` | Disables field interactions. |
228
- | `readonly` | `readonly` | `boolean` | `false` | Disables keyboard editing. |
229
- | `required` | `required` | `boolean` | `false` | Marks field validation as mandatory. |
230
- | `status` | `status` | `'default'\|'valid'\|'invalid'` | `'default'` | Controls validation visual presentation. |
231
- | `validity-message` | `validityMessage` | `string` | `''` | Native or custom error message to display in UI. |
232
- | `size` | `size` | `'xs'\|'sm'\|'md'\|'lg'` | `'md'` | Controls font sizes and paddings. |
233
- | `aria-label` | `ariaLabel` | `string` | `''` | Accessibility label. |
234
- | `aria-labelledby` | `ariaLabelledby` | `string` | `''` | ID reference of accessible label. |
236
+
237
+ | Attribute | Property | Type | Default | Description |
238
+ | :----------------- | :---------------- | :-------------------------------------------------------------- | :---------- | :----------------------------------------------- |
239
+ | `type` | `type` | `'text'\|'email'\|'password'\|'search'\|'tel'\|'url'\|'number'` | `'text'` | Renders appropriate input format. |
240
+ | `placeholder` | `placeholder` | `string` | `''` | Input placeholder text. |
241
+ | `name` | `name` | `string` | `''` | Form participation field name. |
242
+ | `value` | `value` | `string` | `''` | Controlled input value. |
243
+ | `disabled` | `disabled` | `boolean` | `false` | Disables field interactions. |
244
+ | `readonly` | `readonly` | `boolean` | `false` | Disables keyboard editing. |
245
+ | `required` | `required` | `boolean` | `false` | Marks field validation as mandatory. |
246
+ | `status` | `status` | `'default'\|'valid'\|'invalid'` | `'default'` | Controls validation visual presentation. |
247
+ | `validity-message` | `validityMessage` | `string` | `''` | Native or custom error message to display in UI. |
248
+ | `size` | `size` | `'xs'\|'sm'\|'md'\|'lg'` | `'md'` | Controls font sizes and paddings. |
249
+ | `aria-label` | `ariaLabel` | `string` | `''` | Accessibility label. |
250
+ | `aria-labelledby` | `ariaLabelledby` | `string` | `''` | ID reference of accessible label. |
235
251
 
236
252
  #### Slots
253
+
237
254
  - **`helper` Slot**: Location to insert description text below the input field.
238
255
 
239
256
  #### Events
257
+
240
258
  - `vialiq-input`: Fires on every keypress. Detail: `{ value: string }`.
241
259
  - `vialiq-change`: Fires when element loses focus (blur). Detail: `{ value: string }`.
242
260
  - `invalid`: Native HTML5 validation failed event.
243
261
 
244
262
  #### CSS Custom Properties
263
+
245
264
  Exposes variables for custom theme styling:
265
+
246
266
  ```css
247
267
  vi-input {
248
268
  --vi-input-border-color: #d1d5db;
@@ -260,6 +280,7 @@ vi-input {
260
280
  #### Snippets
261
281
 
262
282
  **Basic Text & Password Inputs:**
283
+
263
284
  ```html
264
285
  <vi-input name="username" placeholder="Enter username"></vi-input>
265
286
 
@@ -268,40 +289,164 @@ vi-input {
268
289
  ```
269
290
 
270
291
  **Required with Helper Text & Validation:**
292
+
271
293
  ```html
272
- <vi-input
273
- type="email"
274
- name="email"
275
- placeholder="you@vialiq.com"
276
- required
277
- >
294
+ <vi-input type="email" name="email" placeholder="you@vialiq.com" required>
278
295
  <span slot="helper">We will never share your email address.</span>
279
296
  </vi-input>
280
297
  ```
281
298
 
282
299
  ---
283
300
 
301
+ ### Select ([vi-select](./src/select/vi-select.ts))
302
+
303
+ > [!NOTE]
304
+ > **Performance Notice:** The `vi-select` component automatically calculates its width to perfectly fit its content by measuring all rendered options. Because it renders all options directly into the DOM without virtualization, it is **not intended for large lists** (e.g., thousands of items). For massive datasets, use a combobox with virtualization and async filtering instead.
305
+
306
+ The [ViSelect](./src/select/vi-select.ts) is a custom listbox component that replaces the native `<select>` element. It supports custom templating, dynamic wrapping, form integration, and floating dropdowns using Floating UI.
307
+
308
+ #### Properties & Attributes
309
+
310
+ | Attribute | Property | Type | Default | Description |
311
+ | :----------------- | :---------------- | :------------------------------ | :---------- | :----------------------------------------------- |
312
+ | `value` | `value` | `string` | `''` | The currently selected option's value. |
313
+ | `placeholder` | `placeholder` | `string` | `'Select...'` | Text shown when no option is selected. |
314
+ | `name` | `name` | `string` | `''` | Form field name. |
315
+ | `disabled` | `disabled` | `boolean` | `false` | Disables the select component entirely. |
316
+ | `required` | `required` | `boolean` | `false` | Makes selecting an option mandatory. |
317
+ | `clearable` | `clearable` | `boolean` | `false` | Shows a clear button ("×") when an option is selected. |
318
+ | `status` | `status` | `'default'\|'valid'\|'invalid'` | `'default'` | Controls validation visual border colors. |
319
+ | `validity-message` | `validityMessage` | `string` | `''` | Custom error message to display in UI. |
320
+ | `wrap-text` | `wrapText` | `boolean` | `false` | Allows text within the dropdown options to wrap instead of truncating. |
321
+ | `match-width` | `matchWidth` | `boolean` | `true` | Forces the listbox dropdown to match the trigger's width. |
322
+ | `placement` | `placement` | `DropdownPlacement` | `'bottom-start'` | Preferred positioning of the listbox dropdown. |
323
+ | `hoist` | `hoist` | `boolean` | `true` | Uses a fixed positioning strategy to escape clipping containers. |
324
+ | `flip-boundary` | `flipBoundary` | `string` | `''` | CSS selector for a boundary element used in collision detection. |
325
+
326
+ #### Slots
327
+
328
+ - **Default Slot**: Used for placing `<vi-select-option>` and `<vi-select-group>` child elements.
329
+ - **`helper` Slot**: Location to insert description text below the select component.
330
+
331
+ #### Child Components (`<vi-select-option>` and `<vi-select-group>`)
332
+
333
+ Options are created using the `<vi-select-option>` tag inside the select.
334
+ - **`value`**: The value to submit when selected.
335
+ - **`label`**: The plain text label used for the trigger and type-ahead filtering.
336
+ - **Default Slot**: You can place any complex HTML template inside the option (e.g., avatars, icons, badges) and it will render inside the listbox!
337
+
338
+ You can group options logically using `<vi-select-group>`:
339
+ - **`label`**: The heading text for the option group.
340
+
341
+ #### CSS Custom Properties
342
+
343
+ - `--vi-select-width`: Controls the width of the select trigger and the dropdown (default: `100%`).
344
+ - `--vi-typeahead-highlight-bg`: Background color of the text match when using keyboard type-ahead (default: `#ebf5ff` / `blue-100`).
345
+ - `--vi-typeahead-highlight-color`: Text color of the type-ahead match (default: `#3676d0` / `primary`).
346
+
347
+ #### Events
348
+
349
+ - `vialiq-change`: Fires when an option is selected. Detail: `{ value: string, label: string }`.
350
+ - `vialiq-clear`: Fires when the clear button is clicked.
351
+
352
+ #### Snippets
353
+
354
+ **Basic Usage:**
355
+
356
+ ```html
357
+ <vi-select name="country" placeholder="Select a country...">
358
+ <vi-select-option value="us" label="United States"></vi-select-option>
359
+ <vi-select-option value="uk" label="United Kingdom"></vi-select-option>
360
+ <vi-select-option value="ca" label="Canada"></vi-select-option>
361
+ </vi-select>
362
+ ```
363
+
364
+ **Custom Templates & Width:**
365
+
366
+ ```html
367
+ <vi-select style="--vi-select-width: 300px;" placeholder="Select a user...">
368
+ <vi-select-option value="user1" label="Jane Doe">
369
+ <div style="display: flex; gap: 8px;">
370
+ <img src="avatar.png" alt="" />
371
+ <span>Jane Doe</span>
372
+ </div>
373
+ </vi-select-option>
374
+ </vi-select>
375
+ ```
376
+
377
+ **Option Groups:**
378
+
379
+ ```html
380
+ <vi-select name="fruits" placeholder="Select a fruit...">
381
+ <vi-select-group label="Citrus">
382
+ <vi-select-option value="orange" label="Orange"></vi-select-option>
383
+ <vi-select-option value="lemon" label="Lemon"></vi-select-option>
384
+ </vi-select-group>
385
+ <vi-select-group label="Berries">
386
+ <vi-select-option value="strawberry" label="Strawberry"></vi-select-option>
387
+ <vi-select-option value="blueberry" label="Blueberry"></vi-select-option>
388
+ </vi-select-group>
389
+ </vi-select>
390
+ ```
391
+
392
+ **Placement & Fit Width (`match-width="false"`):**
393
+
394
+ By default, the dropdown matches the width of the trigger. To allow the dropdown to grow based on its content width, and change the floating direction:
395
+
396
+ ```html
397
+ <vi-select placement="top-start" match-width="false" placeholder="Select a user...">
398
+ <vi-select-option value="user1" label="Jane Doe">
399
+ <div style="display: flex; white-space: nowrap;">
400
+ Jane Doe (jane.doe@example.com - Senior Software Engineer)
401
+ </div>
402
+ </vi-select-option>
403
+ </vi-select>
404
+ ```
405
+
406
+ **Form Integration & Reset:**
407
+
408
+ The `<vi-select>` responds properly to native form resets and submit interactions.
409
+
410
+ ```html
411
+ <form>
412
+ <vi-select name="status" value="pending" required>
413
+ <vi-select-option value="pending" label="Pending"></vi-select-option>
414
+ <vi-select-option value="approved" label="Approved"></vi-select-option>
415
+ </vi-select>
416
+
417
+ <!-- This native reset will restore the select back to "pending" -->
418
+ <vi-button type="reset" variant="ghost">Clear Changes</vi-button>
419
+ <vi-button type="submit" variant="primary">Submit</vi-button>
420
+ </form>
421
+ ```
422
+
423
+ ---
424
+
284
425
  ### Checkbox ([vi-checkbox](./src/checkbox/vi-checkbox.ts))
426
+
285
427
  The [ViCheckbox](./src/checkbox/vi-checkbox.ts) is a customizable form-associated checkbox control using SVG graphics for checkmarks and supporting the indeterminate (mixed) validation state.
286
428
 
287
429
  #### Properties & Attributes
288
- | Attribute | Property | Type | Default | Description |
289
- | :--- | :--- | :--- | :--- | :--- |
290
- | `checked` | `checked` | `boolean` | `false` | Checked state. |
291
- | `indeterminate` | `indeterminate` | `boolean` | `false` | Indeterminate (mixed) dash state. |
292
- | `value` | `value` | `string` | `'on'` | Submitted form value. |
293
- | `name` | `name` | `string` | `''` | Form field identifier. |
294
- | `disabled` | `disabled` | `boolean` | `false` | Disables checkbox toggles. |
295
- | `required` | `required` | `boolean` | `false` | Makes checking field mandatory. |
296
- | `status` | `status` | `'default'\|'valid'\|'invalid'` | `'default'` | Controls validation visual border colors. |
297
- | `size` | `size` | `'xs'\|'sm'\|'md'\|'lg'` | `'md'` | Controls dimension metrics. |
430
+
431
+ | Attribute | Property | Type | Default | Description |
432
+ | :-------------- | :-------------- | :------------------------------ | :---------- | :---------------------------------------- |
433
+ | `checked` | `checked` | `boolean` | `false` | Checked state. |
434
+ | `indeterminate` | `indeterminate` | `boolean` | `false` | Indeterminate (mixed) dash state. |
435
+ | `value` | `value` | `string` | `'on'` | Submitted form value. |
436
+ | `name` | `name` | `string` | `''` | Form field identifier. |
437
+ | `disabled` | `disabled` | `boolean` | `false` | Disables checkbox toggles. |
438
+ | `required` | `required` | `boolean` | `false` | Makes checking field mandatory. |
439
+ | `status` | `status` | `'default'\|'valid'\|'invalid'` | `'default'` | Controls validation visual border colors. |
440
+ | `size` | `size` | `'xs'\|'sm'\|'md'\|'lg'` | `'md'` | Controls dimension metrics. |
298
441
 
299
442
  #### Events
443
+
300
444
  - `vialiq-change`: Fired on user toggle. Detail: `{ checked: boolean, value: string }`.
301
445
 
302
446
  #### Snippets
303
447
 
304
448
  **Simple Configurations:**
449
+
305
450
  ```html
306
451
  <vi-checkbox name="agree" required>I accept the terms and conditions</vi-checkbox>
307
452
 
@@ -309,6 +454,7 @@ The [ViCheckbox](./src/checkbox/vi-checkbox.ts) is a customizable form-associate
309
454
  ```
310
455
 
311
456
  **Indeterminate State (Parent/Child controls):**
457
+
312
458
  ```html
313
459
  <vi-checkbox id="select-all" indeterminate>Select All Modules</vi-checkbox>
314
460
  ```
@@ -316,29 +462,33 @@ The [ViCheckbox](./src/checkbox/vi-checkbox.ts) is a customizable form-associate
316
462
  ---
317
463
 
318
464
  ### Radio Group & Radio ([vi-radio-group](./src/radio/vi-radio-group.ts) & [vi-radio](./src/radio/vi-radio.ts))
465
+
319
466
  The [ViRadioGroup](./src/radio/vi-radio-group.ts) and [ViRadio](./src/radio/vi-radio.ts) work in tandem. The group container handles form-association, propagation of attributes (`name`, `disabled`, `size`), roving tabindexes, and WAI-ARIA compliant keyboard navigation via arrow keys.
320
467
 
321
468
  #### `<vi-radio-group>` Properties & Attributes
322
- | Attribute | Property | Type | Default | Description |
323
- | :--- | :--- | :--- | :--- | :--- |
324
- | `value` | `value` | `string` | `''` | Selection value. |
325
- | `name` | `name` | `string` | `''` | Shared name propagated to children. |
326
- | `disabled` | `disabled` | `boolean` | `false` | Disables entire selection array. |
327
- | `required` | `required` | `boolean` | `false` | Marks group validation as mandatory. |
328
- | `status` | `status` | `'default'\|'valid'\|'invalid'` | `'default'` | Group visual status. |
329
- | `validity-message` | `validity-message` | `string` | `''` | Helper error label when validation triggers. |
330
- | `orientation` | `orientation` | `'vertical'\|'horizontal'` | `'vertical'` | Direction grid layout. |
331
- | `size` | `size` | `'xs'\|'sm'\|'md'\|'lg'` | `'md'` | Shared size propagated to children. |
332
- | `allow-dblclick-clear` | `allowDblclickClear` | `boolean` | `false` | Double clicking a selected radio deselects it. |
469
+
470
+ | Attribute | Property | Type | Default | Description |
471
+ | :--------------------- | :------------------- | :------------------------------ | :----------- | :--------------------------------------------- |
472
+ | `value` | `value` | `string` | `''` | Selection value. |
473
+ | `name` | `name` | `string` | `''` | Shared name propagated to children. |
474
+ | `disabled` | `disabled` | `boolean` | `false` | Disables entire selection array. |
475
+ | `required` | `required` | `boolean` | `false` | Marks group validation as mandatory. |
476
+ | `status` | `status` | `'default'\|'valid'\|'invalid'` | `'default'` | Group visual status. |
477
+ | `validity-message` | `validity-message` | `string` | `''` | Helper error label when validation triggers. |
478
+ | `orientation` | `orientation` | `'vertical'\|'horizontal'` | `'vertical'` | Direction grid layout. |
479
+ | `size` | `size` | `'xs'\|'sm'\|'md'\|'lg'` | `'md'` | Shared size propagated to children. |
480
+ | `allow-dblclick-clear` | `allowDblclickClear` | `boolean` | `false` | Double clicking a selected radio deselects it. |
333
481
 
334
482
  #### `<vi-radio>` Properties & Attributes
335
- | Attribute | Property | Type | Default | Description |
336
- | :--- | :--- | :--- | :--- | :--- |
337
- | `value` | `value` | `string` | `''` | Value this radio represents. |
338
- | `checked` | `checked` | `boolean` | `false` | Checked selection status. |
483
+
484
+ | Attribute | Property | Type | Default | Description |
485
+ | :--------- | :--------- | :-------- | :------ | :--------------------------- |
486
+ | `value` | `value` | `string` | `''` | Value this radio represents. |
487
+ | `checked` | `checked` | `boolean` | `false` | Checked selection status. |
339
488
  | `disabled` | `disabled` | `boolean` | `false` | Local disable flag override. |
340
489
 
341
490
  #### Slots (`<vi-radio-group>`)
491
+
342
492
  - **Default Slot**: Holds the list of `<vi-radio>` child tags.
343
493
  - **`label` Slot**: Legend label displayed above the list.
344
494
  - **`helper` Slot**: Support text shown below the components.
@@ -346,6 +496,7 @@ The [ViRadioGroup](./src/radio/vi-radio-group.ts) and [ViRadio](./src/radio/vi-r
346
496
  #### Snippets
347
497
 
348
498
  **Vertical Layout (Default):**
499
+
349
500
  ```html
350
501
  <vi-radio-group name="shipping" value="standard">
351
502
  <span slot="label">Choose Shipping Method</span>
@@ -357,13 +508,9 @@ The [ViRadioGroup](./src/radio/vi-radio-group.ts) and [ViRadio](./src/radio/vi-r
357
508
  ```
358
509
 
359
510
  **Horizontal Layout with Double-Click Clear:**
511
+
360
512
  ```html
361
- <vi-radio-group
362
- name="rating"
363
- orientation="horizontal"
364
- size="lg"
365
- allow-dblclick-clear
366
- >
513
+ <vi-radio-group name="rating" orientation="horizontal" size="lg" allow-dblclick-clear>
367
514
  <span slot="label">Score rating (Double-click to clear)</span>
368
515
  <vi-radio value="1">1 Star</vi-radio>
369
516
  <vi-radio value="2">2 Stars</vi-radio>
@@ -376,31 +523,36 @@ The [ViRadioGroup](./src/radio/vi-radio-group.ts) and [ViRadio](./src/radio/vi-r
376
523
  ---
377
524
 
378
525
  ### Tooltip ([vi-tooltip](./src/tooltip/vi-tooltip.ts))
526
+
379
527
  The [ViTooltip](./src/tooltip/vi-tooltip.ts) manages floating help text. It leverages `@floating-ui/dom` under the hood for collision-detection, auto-flipping, dynamic viewport alignments, and manual trigger controls.
380
528
 
381
529
  #### Properties & Attributes
382
- | Attribute | Property | Type | Default | Description |
383
- | :--- | :--- | :--- | :--- | :--- |
384
- | `content` | `content` | `string` | `''` | Plain text tooltip label. Overridden if the `content` slot is populated. |
385
- | `placement` | `placement` | `TooltipPlacement` | `'top'` | Direction: `top`\|`top-start`\|`top-end`\|`bottom`\|`bottom-start`\|`bottom-end`\|`left`\|`right`. |
386
- | `trigger` | `trigger` | `TooltipTrigger` | `'hover focus'` | Trigger events: `hover focus`\|`hover`\|`focus`\|`click`. |
387
- | `delay` | `delay` | `number` | `500` | Wait delay before displaying in ms. |
388
- | `hide-delay` | `hide-delay` | `number` | `100` | Hide delay after trigger lost in ms. |
389
- | `max-width` | `max-width` | `number` | `240` | Max width bounds size in pixels. |
390
- | `disabled` | `disabled` | `boolean` | `false` | Prevents rendering/open operations. |
391
- | `popper-options` | `popperOptions` | `object` | `{}` | Custom options passed directly to Floating UI's `computePosition`. |
530
+
531
+ | Attribute | Property | Type | Default | Description |
532
+ | :--------------- | :-------------- | :----------------- | :-------------- | :------------------------------------------------------------------------------------------------- |
533
+ | `content` | `content` | `string` | `''` | Plain text tooltip label. Overridden if the `content` slot is populated. |
534
+ | `placement` | `placement` | `TooltipPlacement` | `'top'` | Direction: `top`\|`top-start`\|`top-end`\|`bottom`\|`bottom-start`\|`bottom-end`\|`left`\|`right`. |
535
+ | `trigger` | `trigger` | `TooltipTrigger` | `'hover focus'` | Trigger events: `hover focus`\|`hover`\|`focus`\|`click`. |
536
+ | `delay` | `delay` | `number` | `500` | Wait delay before displaying in ms. |
537
+ | `hide-delay` | `hide-delay` | `number` | `100` | Hide delay after trigger lost in ms. |
538
+ | `max-width` | `max-width` | `number` | `240` | Max width bounds size in pixels. |
539
+ | `disabled` | `disabled` | `boolean` | `false` | Prevents rendering/open operations. |
540
+ | `popper-options` | `popperOptions` | `object` | `{}` | Custom options passed directly to Floating UI's `computePosition`. |
392
541
 
393
542
  #### Slots
543
+
394
544
  - **Default Slot**: The target anchor element (e.g. `<vi-button>`).
395
545
  - **`content` Slot**: Holds rich interactive HTML content. (When used, automatically updates ARIA parameters from `aria-describedby` to `aria-details` for screen readers).
396
546
 
397
547
  #### Methods
548
+
398
549
  - `show()`: Force displays the tooltip pane.
399
550
  - `hide(immediate = false)`: Force hides the tooltip pane.
400
551
 
401
552
  #### Snippets
402
553
 
403
554
  **Basic Text Tooltip:**
555
+
404
556
  ```html
405
557
  <vi-tooltip content="Press to permanently remove configuration" placement="right">
406
558
  <vi-button variant="danger">Delete Account</vi-button>
@@ -408,6 +560,7 @@ The [ViTooltip](./src/tooltip/vi-tooltip.ts) manages floating help text. It leve
408
560
  ```
409
561
 
410
562
  **Rich Interactive Content (Aria-Details compliant):**
563
+
411
564
  ```html
412
565
  <vi-tooltip placement="bottom-start" trigger="click">
413
566
  <vi-button>View Pricing Plan</vi-button>
@@ -434,24 +587,17 @@ If you want the tooltip to dynamically choose the absolute best side (e.g., auto
434
587
 
435
588
  ```html
436
589
  <!-- Passing custom autoPlacement middleware via popper-options property -->
437
- <vi-tooltip
438
- id="auto-placement-tooltip"
439
- content="Dynamic placement based on available viewport space"
440
- >
590
+ <vi-tooltip id="auto-placement-tooltip" content="Dynamic placement based on available viewport space">
441
591
  <vi-button>Auto placement</vi-button>
442
592
  </vi-tooltip>
443
593
 
444
594
  <script>
445
595
  import { autoPlacement, offset, shift } from '@floating-ui/dom';
446
-
596
+
447
597
  const tooltip = document.getElementById('auto-placement-tooltip');
448
598
  // Configure custom options directly to override default middleware list
449
599
  tooltip.popperOptions = {
450
- middleware: [
451
- offset(12),
452
- autoPlacement({ padding: 8 }),
453
- shift({ padding: 8 })
454
- ]
600
+ middleware: [offset(12), autoPlacement({ padding: 8 }), shift({ padding: 8 })],
455
601
  };
456
602
  </script>
457
603
  ```
@@ -459,16 +605,19 @@ If you want the tooltip to dynamically choose the absolute best side (e.g., auto
459
605
  ---
460
606
 
461
607
  ### Icon ([vi-icon](./src/icons/vi-icon.ts))
608
+
462
609
  The [ViIcon](./src/icons/vi-icon.ts) component renders named inline SVG elements. It depends on a dynamic map store, registering only the icons utilized in your project to allow bundlers to prune unused assets (tree-shaking).
463
610
 
464
611
  #### Properties & Attributes
465
- | Attribute | Property | Type | Default | Description |
466
- | :--- | :--- | :--- | :--- | :--- |
467
- | `name` | `name` | `string` | `''` | Name identifier inside the registry. |
468
- | `size` | `size` | `number` | `24` | Width/height footprint dimension in pixels. |
469
- | `label` | `label` | `string` | `''` | Accessibility label. When set, renders as an interactive image with `role="img"`. When empty, marks element as `aria-hidden="true"`. |
612
+
613
+ | Attribute | Property | Type | Default | Description |
614
+ | :-------- | :------- | :------- | :------ | :----------------------------------------------------------------------------------------------------------------------------------- |
615
+ | `name` | `name` | `string` | `''` | Name identifier inside the registry. |
616
+ | `size` | `size` | `number` | `24` | Width/height footprint dimension in pixels. |
617
+ | `label` | `label` | `string` | `''` | Accessibility label. When set, renders as an interactive image with `role="img"`. When empty, marks element as `aria-hidden="true"`. |
470
618
 
471
619
  #### Icon Registration API
620
+
472
621
  Before rendering `<vi-icon>`, you must register the required icon definitions using the [registerIcons](./src/icons/registry.ts) function:
473
622
 
474
623
  ```ts
@@ -480,11 +629,12 @@ import { settingsIcon } from '@vialiq/icons/settings';
480
629
  registerIcons([checkIcon, settingsIcon]);
481
630
  ```
482
631
 
483
- *Note: For security, `registerIcons` includes internal sanity checks that filter out inline `<script>` tags or event handlers (e.g. `onload=`) to prevent XSS exploits.*
632
+ _Note: For security, `registerIcons` includes internal sanity checks that filter out inline `<script>` tags or event handlers (e.g. `onload=`) to prevent XSS exploits._
484
633
 
485
634
  #### Snippets
486
635
 
487
636
  **Usage Example:**
637
+
488
638
  ```html
489
639
  <!-- Decorative usage (aria-hidden is true) -->
490
640
  <vi-icon name="check" size="20"></vi-icon>
@@ -493,6 +643,89 @@ registerIcons([checkIcon, settingsIcon]);
493
643
  <vi-icon name="settings" size="32" label="Open workspace settings"></vi-icon>
494
644
  ```
495
645
 
646
+ ### Modal ([vi-modal](./src/modal/vi-modal.ts))
647
+
648
+ The [ViModal](./src/modal/vi-modal.ts) is a focus-trapping dialog that dynamically teleports itself to `document.body` to guarantee stacking context above all other elements. It supports multiple layout variants (default, drawer, alert), draggable boundaries, resize handles, and stacking multiple modals automatically using an internal OverlayManager.
649
+
650
+ #### Properties & Attributes
651
+
652
+ | Attribute | Property | Type | Default | Description |
653
+ | :----------------- | :---------------- | :--------------------------------------------------------- | :---------- | :------------------------------------------------------------------------ |
654
+ | `open` | `open` | `boolean` | `false` | Controls the visibility of the modal. |
655
+ | `variant` | `variant` | `'default'\|'drawer'\|'alert'` | `'default'` | Core layout mode. |
656
+ | `size` | `size` | `'xs'\|'sm'\|'md'\|'lg'\|'xl'\|'full-width'\|'fullscreen'` | `'md'` | Dialog dimensions. |
657
+ | `position` | `position` | `'center'\|'top'\|'bottom'\|'left'\|'right'\|...` | `'center'` | Screen positioning for default variant. |
658
+ | `persistent` | `persistent` | `boolean` | `false` | Prevent closing on backdrop click or Escape key (triggers shake instead). |
659
+ | `no-backdrop` | `no-backdrop` | `boolean` | `false` | Hides the backdrop overlay and allows background interaction (modeless). |
660
+ | `draggable` | `draggable` | `boolean` | `false` | Allows dragging the modal by its header. |
661
+ | `resizable` | `resizable` | `boolean` | `false` | Adds 8-point resize handles to the modal edges. |
662
+ | `scrollable` | `scrollable` | `boolean` | `true` | Body content scrolls while header/footer stay fixed. |
663
+ | `drawer-placement` | `drawerPlacement` | `'left'\|'right'` | `'right'` | Edge attachment for the drawer variant. |
664
+ | `append-to` | `appendTo` | `string\|HTMLElement` | `'body'` | Selector or Element to teleport the modal into. |
665
+
666
+ #### Slots
667
+
668
+ - **Default Slot**: The main body content.
669
+ - **`header` Slot**: Overrides the title text.
670
+ - **`header-actions` Slot**: Insert custom buttons next to close/maximize.
671
+ - **`footer` Slot**: Bottom action bar content.
672
+ - **`icon` Slot**: (Alert variant only) Override the default status icon.
673
+
674
+ #### Events
675
+
676
+ - `vi-modal-before-open`: Fires immediately on `open=true` (cancelable).
677
+ - `vi-modal-open`: Fires as the modal begins opening.
678
+ - `vi-modal-after-open`: Fires after enter animations complete.
679
+ - `vi-modal-before-close`: Fires immediately on `open=false` (cancelable). Detail: `{ reason: string }`
680
+ - `vi-modal-close`: Fires as the modal begins closing. Detail: `{ reason: string }`
681
+ - `vi-modal-after-close`: Fires after exit animations complete and modal is removed.
682
+ - `vi-modal-request-close`: Fires when user attempts to close via backdrop/escape. Detail: `{ reason: 'escape' \| 'backdrop' }`
683
+
684
+ #### Snippets
685
+
686
+ **Standard Modal:**
687
+
688
+ ```html
689
+ <vi-modal>
690
+ <vi-modal-header slot="header" closable>Title</vi-modal-header>
691
+ <p>Content</p>
692
+ <vi-modal-footer slot="footer">
693
+ <vi-button>Save</vi-button>
694
+ </vi-modal-footer>
695
+ </vi-modal>
696
+ ```
697
+
698
+ **Draggable & Resizable Modeless Dialog:**
699
+
700
+ ```html
701
+ <vi-modal no-backdrop draggable resizable position="bottom-right">
702
+ <vi-modal-header slot="header" closable>Floating Tool</vi-modal-header>
703
+ <p>This modal doesn't block interaction with the page behind it!</p>
704
+ </vi-modal>
705
+ ```
706
+
707
+ **Right-Side Drawer:**
708
+
709
+ ```html
710
+ <vi-modal variant="drawer" drawer-placement="right" size="sm">
711
+ <vi-modal-header slot="header" closable>Settings</vi-modal-header>
712
+ <p>Drawer contents here...</p>
713
+ </vi-modal>
714
+ ```
715
+
716
+ **Destructive Alert:**
717
+
718
+ ```html
719
+ <vi-modal variant="alert" persistent>
720
+ <vi-modal-header slot="header" alert-variant="danger" closable>Delete Workspace?</vi-modal-header>
721
+ <p>This action cannot be undone. All data will be permanently lost.</p>
722
+ <vi-modal-footer slot="footer">
723
+ <vi-button variant="ghost">Cancel</vi-button>
724
+ <vi-button variant="danger">Confirm Deletion</vi-button>
725
+ </vi-modal-footer>
726
+ </vi-modal>
727
+ ```
728
+
496
729
  ---
497
730
 
498
731
  ## Form Validation & ElementInternals
@@ -500,20 +733,14 @@ registerIcons([checkIcon, settingsIcon]);
500
733
  Custom controls inside this library utilize the native browser [ValidityMixin](./src/base/validity-mixin.ts) to integrate with standard `<form>` features like `.elements`, `.checkValidity()`, and `.reportValidity()`.
501
734
 
502
735
  ### Handling Submit Validation
736
+
503
737
  ```html
504
738
  <form id="profile-form">
505
- <vi-input
506
- type="text"
507
- name="fullname"
508
- placeholder="John Doe"
509
- required
510
- >
739
+ <vi-input type="text" name="fullname" placeholder="John Doe" required>
511
740
  <span slot="helper">Enter your full name.</span>
512
741
  </vi-input>
513
742
 
514
- <vi-checkbox name="newsletter" required>
515
- Confirm subscription policy
516
- </vi-checkbox>
743
+ <vi-checkbox name="newsletter" required> Confirm subscription policy </vi-checkbox>
517
744
 
518
745
  <vi-button type="submit" variant="primary">Submit</vi-button>
519
746
  </form>
@@ -522,7 +749,7 @@ Custom controls inside this library utilize the native browser [ValidityMixin](.
522
749
  const form = document.getElementById('profile-form');
523
750
  form.addEventListener('submit', (e) => {
524
751
  e.preventDefault();
525
-
752
+
526
753
  // Checks validity for all elements in the form
527
754
  if (form.checkValidity()) {
528
755
  const formData = new FormData(form);
@@ -535,6 +762,7 @@ Custom controls inside this library utilize the native browser [ValidityMixin](.
535
762
  ```
536
763
 
537
764
  ### Custom Error Reporting
765
+
538
766
  Use `setCustomValidity` to configure custom error messages or run manual server-side validation responses:
539
767
 
540
768
  ```javascript
@@ -542,7 +770,7 @@ const emailField = document.querySelector('vi-input[name="email"]');
542
770
 
543
771
  emailField.addEventListener('vialiq-change', (e) => {
544
772
  const email = e.detail.value;
545
-
773
+
546
774
  if (email.endsWith('@forbidden-domain.com')) {
547
775
  emailField.setCustomValidity('Registrations from this domain are forbidden.');
548
776
  emailField.reportValidity(); // Displays the custom validation message tooltip
@@ -559,6 +787,7 @@ emailField.addEventListener('vialiq-change', (e) => {
559
787
  Web Components utilize CSS Shadow Roots to encapsulate logic and layout styles. To override designs safely without bleeding global configurations, use **CSS Shadow Parts** or **CSS Variables**.
560
788
 
561
789
  ### CSS Shadow Parts (`::part`)
790
+
562
791
  Elements expose internal sub-nodes through the `part="..."` syntax. Customize these nodes using the CSS `::part()` selector:
563
792
 
564
793
  ```css
@@ -580,6 +809,7 @@ vi-checkbox::part(box) {
580
809
  ```
581
810
 
582
811
  ### CSS Variables Custom Properties
812
+
583
813
  For variables used repeatedly, custom elements offer direct CSS property bindings. Customize them globally or on single layouts:
584
814
 
585
815
  ```css
@@ -617,6 +847,174 @@ npx nx run web-components:storybook
617
847
  npx nx run web-components:test-wdio
618
848
  ```
619
849
 
850
+ ---
851
+
852
+ ### Badge ([vi-badge](./src/badge/vi-badge.ts))
853
+
854
+ The `<vi-badge>` is a compact inline indicator used to communicate status, category, or count. It supports various color semantics, sizes, pill vs. square shapes, dot-only mode, and numeric count capping.
855
+
856
+ #### Properties & Attributes
857
+
858
+ | Attribute | Property | Type | Default | Description |
859
+ | :---------- | :--------- | :------------------------------------------------------------- | :---------- | :---------------------------------------------------------------------- |
860
+ | `variant` | `variant` | `'neutral'\|'primary'\|'success'\|'warning'\|'danger'\|'info'` | `'neutral'` | Color semantic. |
861
+ | `size` | `size` | `'sm'\|'md'\|'lg'` | `'md'` | Size of the badge. |
862
+ | `dot` | `dot` | `boolean` | `false` | Renders a small colored dot instead of text. |
863
+ | `pill` | `pill` | `boolean` | `true` | Renders fully rounded ends (pill) vs slightly rounded corners (square). |
864
+ | `count` | `count` | `number` | `undefined` | Numeric count to display. Overrides default slot content. |
865
+ | `show-zero` | `showZero` | `boolean` | `false` | Displays the badge even if the count is zero. |
866
+ | `max` | `max` | `number` | `99` | Max count before showing `{max}+`. |
867
+ | `outline` | `outline` | `boolean` | `false` | Renders the badge with an outline/ghost style. |
868
+
869
+ #### Slots
870
+
871
+ - **Default Slot**: Badge text content (ignored if `count` is set).
872
+ - **`icon` Slot**: Optional leading icon.
873
+
874
+ #### Snippets
875
+
876
+ **Standard Variants:**
877
+
878
+ ```html
879
+ <vi-badge variant="neutral">Draft</vi-badge>
880
+ <vi-badge variant="primary">Submitted</vi-badge>
881
+ <vi-badge variant="success">Locked</vi-badge>
882
+ <vi-badge variant="warning">In Review</vi-badge>
883
+ <vi-badge variant="danger">Query Open</vi-badge>
884
+ ```
885
+
886
+ **Outlined & Square Styles:**
887
+
888
+ ```html
889
+ <vi-badge variant="info" outline>Outline Mode</vi-badge> <vi-badge variant="primary" :pill="false">Square Badge</vi-badge>
890
+ ```
891
+
892
+ **Numeric Counts:**
893
+
894
+ ```html
895
+ <vi-badge count="5" variant="danger"></vi-badge> <vi-badge count="120" max="99" variant="danger"></vi-badge>
896
+ ```
897
+
898
+ **Dot Indicators:**
899
+
900
+ ```html
901
+ <vi-badge dot variant="success"></vi-badge>
902
+ ```
903
+
904
+ ---
905
+
906
+ ### Chip & Chip Group ([vi-chip](./src/chip/vi-chip.ts), [vi-chip-group](./src/chip/vi-chip-group.ts))
907
+
908
+ The `<vi-chip>` is an interactive pill-shaped element used for selection, filtering, or categorisation. Chips are usually managed inside a `<vi-chip-group>`, which provides multi-select or single-select logic and arrow-key navigation.
909
+
910
+ #### `<vi-chip>` Properties & Attributes
911
+
912
+ | Attribute | Property | Type | Default | Description |
913
+ | :------------------ | :---------------- | :------------------------------------------------------------- | :---------- | :--------------------------------------------------- |
914
+ | `value` | `value` | `string` | `''` | Value used for selection tracking within a group. |
915
+ | `selected` | `selected` | `boolean` | `false` | Sets the active/selected visual state and checkmark. |
916
+ | `disabled` | `disabled` | `boolean` | `false` | Makes the chip non-interactive. |
917
+ | `removable` | `removable` | `boolean` | `false` | Shows a trailing "×" remove button. |
918
+ | `variant` | `variant` | `'neutral'\|'primary'\|'success'\|'warning'\|'danger'\|'info'` | `'neutral'` | Color semantic. |
919
+ | `size` | `size` | `'sm'\|'md'\|'lg'` | `'md'` | Size of the chip. |
920
+ | `remove-aria-label` | `removeAriaLabel` | `string` | `'Remove'` | Accessible label for the remove button. |
921
+
922
+ **`<vi-chip>` Slots:**
923
+
924
+ - **Default Slot**: Label text content.
925
+ - **`avatar` Slot**: Leading avatar image or initials.
926
+ - **`icon` Slot**: Leading icon (used when no avatar).
927
+ - **`trailing-icon` Slot**: Trailing icon (separate from the remove button).
928
+
929
+ #### `<vi-chip-group>` Properties & Attributes
930
+
931
+ | Attribute | Property | Type | Default | Description |
932
+ | :--------- | :--------- | :--------- | :------ | :----------------------------------------------------------- |
933
+ | `value` | `value` | `string[]` | `[]` | Array of currently selected chip values. |
934
+ | `multi` | `multi` | `boolean` | `true` | Allows multiple selections vs. single selection. |
935
+ | `name` | `name` | `string` | `''` | Form field name for native form participation. |
936
+ | `required` | `required` | `boolean` | `false` | Requires at least one chip to be selected for form validity. |
937
+ | `disabled` | `disabled` | `boolean` | `false` | Disables all child chips. |
938
+ | `wrap` | `wrap` | `boolean` | `true` | Controls whether chips wrap to the next line. |
939
+ | `gap` | `gap` | `string` | `'8px'` | Gap spacing between chips. |
940
+
941
+ **`<vi-chip-group>` Events:**
942
+
943
+ - `vi-chip-group-change`: Fired when the selection changes (event detail contains `value: string[]`).
944
+
945
+ #### Snippets
946
+
947
+ **Multi-Select Group:**
948
+
949
+ ```html
950
+ <vi-chip-group multi name="grades" value='["grade-1"]'>
951
+ <vi-chip value="grade-1">Grade 1</vi-chip>
952
+ <vi-chip value="grade-2">Grade 2</vi-chip>
953
+ <vi-chip value="grade-3" variant="warning">Grade 3</vi-chip>
954
+ <vi-chip value="grade-4" variant="danger">Grade 4</vi-chip>
955
+ </vi-chip-group>
956
+ ```
957
+
958
+ **Single-Select Group:**
959
+
960
+ ```html
961
+ <vi-chip-group :multi="false" name="visit">
962
+ <vi-chip value="1" variant="primary">Visit 1</vi-chip>
963
+ <vi-chip value="2" variant="primary">Visit 2</vi-chip>
964
+ </vi-chip-group>
965
+ ```
966
+
967
+ ---
968
+
969
+ ---
970
+
971
+ ---
972
+
973
+ ### Switch ([vi-switch](./src/switch/vi-switch.ts))
974
+
975
+ The `<vi-switch>` component is a form-associated toggle switch used for boolean settings. It supports size variants, custom label placements, and native HTML form participation.
976
+
977
+ #### Properties & Attributes
978
+
979
+ | Attribute | Property | Type | Default | Description |
980
+ | :---------------- | :--------------- | :----------------- | :------ | :---------------------------------------------------- |
981
+ | `checked` | `checked` | `boolean` | `false` | Checked state of the switch. |
982
+ | `disabled` | `disabled` | `boolean` | `false` | Disables the switch. |
983
+ | `size` | `size` | `'sm'\|'md'\|'lg'` | `'md'` | Visual size scale. |
984
+ | `label-placement` | `labelPlacement` | `'start'\|'end'` | `'end'` | Placement of the label relative to the switch toggle. |
985
+ | `name` | `name` | `string` | `''` | Form field name. |
986
+ | `value` | `value` | `string` | `'on'` | Form submission value when checked. |
987
+
988
+ **Slots:**
989
+
990
+ - **Default Slot**: Label text/content.
991
+ - **`on-label` Slot**: Optional text displayed inside the track when checked.
992
+ - **`off-label` Slot**: Optional text displayed inside the track when unchecked.
993
+
994
+ **Events:**
995
+
996
+ - `vi-switch-change`: Fires when the user toggles the checked state. (Detail: `{ checked: boolean }`).
997
+
998
+ #### Snippets
999
+
1000
+ **Basic Usage:**
1001
+
1002
+ ```html
1003
+ <vi-switch>Enable Notifications</vi-switch>
1004
+ <vi-switch checked>Marketing Emails</vi-switch>
1005
+ <vi-switch disabled>Disabled Toggle</vi-switch>
1006
+ ```
1007
+
1008
+ **Custom Label Placement & Sizes:**
1009
+
1010
+ ```html
1011
+ <!-- Label to the left of the switch -->
1012
+ <vi-switch label-placement="start" size="lg">Auto-save</vi-switch>
1013
+ <vi-switch size="sm">Compact Mode</vi-switch>
1014
+ ```
1015
+
1016
+ ---
1017
+
620
1018
  ## Storybook
621
1019
 
622
1020
  The project uses **Storybook 10** with the `@storybook/web-components-vite` framework.