@keenmate/web-multiselect 1.0.0-rc04 → 1.0.0-rc06
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 +374 -8
- package/dist/multiselect.js +1066 -772
- package/dist/multiselect.umd.js +14 -14
- package/dist/style.css +1 -1
- package/package.json +1 -1
- package/src/scss/_css-variables.scss +377 -0
- package/src/scss/main.scss +2 -0
package/README.md
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
[](LICENSE)
|
|
4
4
|
[](https://www.npmjs.com/package/@keenmate/web-multiselect)
|
|
5
5
|
|
|
6
|
-
A lightweight, accessible multiselect web component with typeahead search,
|
|
6
|
+
A lightweight, accessible multiselect web component with typeahead search, RTL language support, rich content, and excellent keyboard navigation.
|
|
7
7
|
|
|
8
8
|
## Features
|
|
9
9
|
|
|
@@ -17,6 +17,7 @@ A lightweight, accessible multiselect web component with typeahead search, rich
|
|
|
17
17
|
- 📦 **Grouped Options** - Organize options into collapsible groups
|
|
18
18
|
- 🎉 **Smart Positioning** - Uses Floating UI for intelligent dropdown placement
|
|
19
19
|
- 🌍 **i18n Support** - Customizable callbacks for pluralization and localization
|
|
20
|
+
- 🌐 **RTL Support** - Full right-to-left language support (Arabic, Hebrew, Persian, Urdu, etc.)
|
|
20
21
|
- ✨ **Modern** - Web Component with Shadow DOM, TypeScript, bundled with Vite
|
|
21
22
|
- 🌐 **Framework Agnostic** - Works with any framework or vanilla JS
|
|
22
23
|
|
|
@@ -87,7 +88,7 @@ multiselect.setSelected(['js', 'ts']);
|
|
|
87
88
|
| `show-checkboxes` | `boolean` | `true` | Show checkboxes next to options |
|
|
88
89
|
| `close-on-select` | `boolean` | `false` | Close dropdown after selecting |
|
|
89
90
|
| `dropdown-min-width` | `string` | - | Min width for dropdown (e.g., '20rem') |
|
|
90
|
-
| `pills-display-mode` | `'pills' \| 'count' \| 'compact'` | `'pills'` | How to display selected items |
|
|
91
|
+
| `pills-display-mode` | `'pills' \| 'count' \| 'compact' \| 'partial'` | `'pills'` | How to display selected items |
|
|
91
92
|
| `pills-threshold` | `number` | - | Auto-switch mode when exceeded (see pills-threshold-mode) |
|
|
92
93
|
| `pills-threshold-mode` | `'count' \| 'partial'` | `'count'` | Mode after threshold: 'count' shows badge, 'partial' shows limited pills + more badge |
|
|
93
94
|
| `pills-max-visible` | `number` | `3` | Max pills shown in partial mode |
|
|
@@ -101,6 +102,20 @@ multiselect.setSelected(['js', 'ts']);
|
|
|
101
102
|
| `empty-message` | `string` | `'No results found'` | Message when no options found |
|
|
102
103
|
| `loading-message` | `string` | `'Loading...'` | Message while loading async data |
|
|
103
104
|
| `min-search-length` | `number` | `0` | Minimum search length for async |
|
|
105
|
+
| `sticky-actions` | `boolean` | `true` | Keep Select All/Clear All buttons fixed at top while scrolling |
|
|
106
|
+
| `lock-placement` | `boolean` | `true` | Lock dropdown placement after first open to prevent flipping |
|
|
107
|
+
| `enable-search` | `boolean` | `true` | Enable/disable search functionality |
|
|
108
|
+
| `search-input-mode` | `'normal' \| 'readonly' \| 'hidden'` | `'normal'` | Search input display mode |
|
|
109
|
+
| `allow-add-new` | `boolean` | `false` | Allow adding new options not in the list |
|
|
110
|
+
| `value-member` | `string` | - | Property name for value/ID extraction from custom objects |
|
|
111
|
+
| `display-value-member` | `string` | - | Property name for display text extraction from custom objects |
|
|
112
|
+
| `search-value-member` | `string` | - | Property name for search text extraction from custom objects |
|
|
113
|
+
| `icon-member` | `string` | - | Property name for icon extraction from custom objects |
|
|
114
|
+
| `subtitle-member` | `string` | - | Property name for subtitle extraction from custom objects |
|
|
115
|
+
| `group-member` | `string` | - | Property name for group extraction from custom objects |
|
|
116
|
+
| `disabled-member` | `string` | - | Property name for disabled state extraction from custom objects |
|
|
117
|
+
| `name` | `string` | - | HTML form field name for form integration (creates hidden input) |
|
|
118
|
+
| `value-format` | `'json' \| 'csv' \| 'array'` | `'json'` | Format for form value serialization |
|
|
104
119
|
| `initial-values` | `string` (JSON array) | - | Pre-selected values |
|
|
105
120
|
|
|
106
121
|
## Properties
|
|
@@ -131,6 +146,12 @@ multiselect.onChange = (selectedOptions) => {
|
|
|
131
146
|
console.log('Changed:', selectedOptions);
|
|
132
147
|
};
|
|
133
148
|
|
|
149
|
+
// Pill display customization (show different text in pills vs dropdown)
|
|
150
|
+
multiselect.getPillDisplayCallback = (item) => {
|
|
151
|
+
// Show shorter text in pills (e.g., just name instead of "name (email)")
|
|
152
|
+
return item.name; // Dropdown might show "John Doe (john@example.com)"
|
|
153
|
+
};
|
|
154
|
+
|
|
134
155
|
// Pill tooltip customization
|
|
135
156
|
multiselect.getPillTooltipCallback = (item) => {
|
|
136
157
|
return `${item.label} - ${item.subtitle}`;
|
|
@@ -143,14 +164,51 @@ multiselect.getCountPillCallback = (count, moreCount) => {
|
|
|
143
164
|
}
|
|
144
165
|
return `${count} selected`; // Count mode display
|
|
145
166
|
};
|
|
167
|
+
|
|
168
|
+
// Data extraction - Member properties (for simple property names)
|
|
169
|
+
multiselect.valueMember = 'id';
|
|
170
|
+
multiselect.displayValueMember = 'name';
|
|
171
|
+
multiselect.iconMember = 'icon';
|
|
172
|
+
multiselect.subtitleMember = 'description';
|
|
173
|
+
multiselect.groupMember = 'category';
|
|
174
|
+
multiselect.disabledMember = 'isDisabled';
|
|
175
|
+
|
|
176
|
+
// Data extraction - Callback functions (for complex logic)
|
|
177
|
+
multiselect.getValueCallback = (item) => item.id || item.value;
|
|
178
|
+
multiselect.getDisplayValueCallback = (item) => item.label || item.name;
|
|
179
|
+
multiselect.getSearchValueCallback = (item) => `${item.name} ${item.tags.join(' ')}`;
|
|
180
|
+
multiselect.getIconCallback = (item) => item.icon || '📄';
|
|
181
|
+
multiselect.getSubtitleCallback = (item) => `${item.price} - ${item.stock} in stock`;
|
|
182
|
+
multiselect.getGroupCallback = (item) => item.category;
|
|
183
|
+
multiselect.getDisabledCallback = (item) => item.stock === 0;
|
|
184
|
+
|
|
185
|
+
// Form integration
|
|
186
|
+
multiselect.name = 'selected_items';
|
|
187
|
+
multiselect.valueFormat = 'json'; // 'json' | 'csv' | 'array'
|
|
188
|
+
multiselect.getValueFormatCallback = (values) => values.join('|'); // Custom format
|
|
189
|
+
|
|
190
|
+
// Read-only properties
|
|
191
|
+
const selectedValue = multiselect.selectedValue; // string | number | array | null
|
|
192
|
+
const selectedItem = multiselect.selectedItem; // First selected item object
|
|
193
|
+
|
|
194
|
+
// Add new option callback
|
|
195
|
+
multiselect.addNewCallback = async (value) => {
|
|
196
|
+
// Validate and create new option
|
|
197
|
+
const newOption = await fetch('/api/options', {
|
|
198
|
+
method: 'POST',
|
|
199
|
+
body: JSON.stringify({ name: value })
|
|
200
|
+
}).then(r => r.json());
|
|
201
|
+
return newOption;
|
|
202
|
+
};
|
|
146
203
|
```
|
|
147
204
|
|
|
148
205
|
## Methods
|
|
149
206
|
|
|
150
207
|
| Method | Description |
|
|
151
208
|
|--------|-------------|
|
|
152
|
-
| `getSelected()` | Get currently selected options |
|
|
153
|
-
| `setSelected(values: string[])` | Set selected values |
|
|
209
|
+
| `getSelected()` | Get currently selected options as array of option objects |
|
|
210
|
+
| `setSelected(values: (string \| number)[])` | Set selected values by ID/value |
|
|
211
|
+
| `getValue()` | Get selected value(s) - returns single value in single-select mode, array in multi-select mode |
|
|
154
212
|
| `destroy()` | Clean up and destroy instance |
|
|
155
213
|
|
|
156
214
|
## Events
|
|
@@ -272,7 +330,7 @@ Perfect for different use cases and space constraints:
|
|
|
272
330
|
|
|
273
331
|
### Pills Positioning
|
|
274
332
|
|
|
275
|
-
Control where selected item badges appear:
|
|
333
|
+
Control where selected item badges appear relative to the input:
|
|
276
334
|
|
|
277
335
|
```html
|
|
278
336
|
<!-- Pills below input (default) -->
|
|
@@ -281,13 +339,15 @@ Control where selected item badges appear:
|
|
|
281
339
|
<!-- Pills above input -->
|
|
282
340
|
<multi-select pills-position="top"></multi-select>
|
|
283
341
|
|
|
284
|
-
<!-- Pills to the left
|
|
342
|
+
<!-- Pills to the left of input -->
|
|
285
343
|
<multi-select pills-position="left"></multi-select>
|
|
286
344
|
|
|
287
|
-
<!-- Pills to the right
|
|
345
|
+
<!-- Pills to the right of input -->
|
|
288
346
|
<multi-select pills-position="right"></multi-select>
|
|
289
347
|
```
|
|
290
348
|
|
|
349
|
+
**Note:** In RTL mode, left/right positions are automatically mirrored - `pills-position="left"` will appear on the physical right side in RTL languages.
|
|
350
|
+
|
|
291
351
|
### Pill Tooltips
|
|
292
352
|
|
|
293
353
|
Enable tooltips on selected item pills with customizable placement and delay:
|
|
@@ -342,6 +402,287 @@ Customize count pill text for proper pluralization and localization:
|
|
|
342
402
|
</script>
|
|
343
403
|
```
|
|
344
404
|
|
|
405
|
+
### Right-to-Left (RTL) Language Support
|
|
406
|
+
|
|
407
|
+
Full RTL support for Arabic, Hebrew, Persian, Urdu, and other right-to-left languages with automatic detection and complete UI mirroring:
|
|
408
|
+
|
|
409
|
+
```html
|
|
410
|
+
<!-- Automatic RTL detection from dir attribute -->
|
|
411
|
+
<multi-select dir="rtl" search-placeholder="ابحث..."></multi-select>
|
|
412
|
+
|
|
413
|
+
<!-- RTL inherited from parent element -->
|
|
414
|
+
<div dir="rtl">
|
|
415
|
+
<multi-select search-placeholder="חיפוש..."></multi-select>
|
|
416
|
+
</div>
|
|
417
|
+
|
|
418
|
+
<!-- RTL on page level -->
|
|
419
|
+
<html dir="rtl">
|
|
420
|
+
<!-- All multi-selects will auto-detect RTL -->
|
|
421
|
+
</html>
|
|
422
|
+
```
|
|
423
|
+
|
|
424
|
+
**RTL Features:**
|
|
425
|
+
- ✅ **Auto-detection** - Detects `dir="rtl"` on component or any ancestor element
|
|
426
|
+
- ✅ **Complete UI mirroring** - Toggle icon, text alignment, pills, dropdown, badges
|
|
427
|
+
- ✅ **Logical positioning** - `pills-position="left"` becomes physically right in RTL
|
|
428
|
+
- ✅ **Pills remove buttons** - Flip to left side in RTL mode
|
|
429
|
+
- ✅ **Text direction** - All text content properly right-aligned
|
|
430
|
+
- ✅ **No configuration needed** - Just set `dir="rtl"` attribute
|
|
431
|
+
|
|
432
|
+
### Flexible Data Handling
|
|
433
|
+
|
|
434
|
+
The component supports **any data structure** through a member/callback pattern, allowing you to work with custom objects, tuple arrays, or existing API responses without transformation.
|
|
435
|
+
|
|
436
|
+
#### Member Properties (Simple Property Names)
|
|
437
|
+
|
|
438
|
+
For objects with consistent property names, use member attributes:
|
|
439
|
+
|
|
440
|
+
```html
|
|
441
|
+
<multi-select
|
|
442
|
+
id="products"
|
|
443
|
+
value-member="productId"
|
|
444
|
+
display-value-member="productName"
|
|
445
|
+
icon-member="icon"
|
|
446
|
+
subtitle-member="description"
|
|
447
|
+
group-member="category">
|
|
448
|
+
</multi-select>
|
|
449
|
+
|
|
450
|
+
<script type="module">
|
|
451
|
+
const select = document.getElementById('products');
|
|
452
|
+
select.options = [
|
|
453
|
+
{
|
|
454
|
+
productId: 'p1',
|
|
455
|
+
productName: 'Laptop',
|
|
456
|
+
icon: '💻',
|
|
457
|
+
description: 'High-performance laptop',
|
|
458
|
+
category: 'Electronics'
|
|
459
|
+
},
|
|
460
|
+
{
|
|
461
|
+
productId: 'p2',
|
|
462
|
+
productName: 'Mouse',
|
|
463
|
+
icon: '🖱️',
|
|
464
|
+
description: 'Wireless mouse',
|
|
465
|
+
category: 'Electronics'
|
|
466
|
+
}
|
|
467
|
+
];
|
|
468
|
+
</script>
|
|
469
|
+
```
|
|
470
|
+
|
|
471
|
+
#### Callback Functions (Complex Logic)
|
|
472
|
+
|
|
473
|
+
For complex data extraction or conditional logic, use callbacks:
|
|
474
|
+
|
|
475
|
+
```javascript
|
|
476
|
+
const select = document.querySelector('multi-select');
|
|
477
|
+
|
|
478
|
+
// Custom value extraction
|
|
479
|
+
select.getValueCallback = (item) => item.id || item.code || item.value;
|
|
480
|
+
|
|
481
|
+
// Combine multiple fields for display
|
|
482
|
+
select.getDisplayValueCallback = (item) => {
|
|
483
|
+
return `${item.firstName} ${item.lastName}`;
|
|
484
|
+
};
|
|
485
|
+
|
|
486
|
+
// Include multiple fields in search
|
|
487
|
+
select.getSearchValueCallback = (item) => {
|
|
488
|
+
return `${item.name} ${item.sku} ${item.tags.join(' ')}`;
|
|
489
|
+
};
|
|
490
|
+
|
|
491
|
+
// Conditional icons
|
|
492
|
+
select.getIconCallback = (item) => {
|
|
493
|
+
return item.inStock ? '✅' : '❌';
|
|
494
|
+
};
|
|
495
|
+
|
|
496
|
+
// Dynamic subtitles
|
|
497
|
+
select.getSubtitleCallback = (item) => {
|
|
498
|
+
return `$${item.price} - ${item.stock} in stock`;
|
|
499
|
+
};
|
|
500
|
+
|
|
501
|
+
// Disable based on conditions
|
|
502
|
+
select.getDisabledCallback = (item) => {
|
|
503
|
+
return item.stock === 0 || item.discontinued;
|
|
504
|
+
};
|
|
505
|
+
|
|
506
|
+
// Customize pill display (show different text in pills vs dropdown)
|
|
507
|
+
select.getPillDisplayCallback = (item) => {
|
|
508
|
+
// Pills show just the name for space efficiency
|
|
509
|
+
return item.name;
|
|
510
|
+
// While dropdown can show full details: "Laptop - $999 - Electronics"
|
|
511
|
+
};
|
|
512
|
+
```
|
|
513
|
+
|
|
514
|
+
#### Tuple Array Auto-Detection
|
|
515
|
+
|
|
516
|
+
The component automatically detects `[key, value]` tuple arrays:
|
|
517
|
+
|
|
518
|
+
```javascript
|
|
519
|
+
select.options = [
|
|
520
|
+
['js', 'JavaScript'],
|
|
521
|
+
['ts', 'TypeScript'],
|
|
522
|
+
['py', 'Python']
|
|
523
|
+
];
|
|
524
|
+
// First element becomes value, second becomes display text
|
|
525
|
+
```
|
|
526
|
+
|
|
527
|
+
#### Priority Order
|
|
528
|
+
|
|
529
|
+
When multiple extraction methods are defined, the component uses this priority:
|
|
530
|
+
|
|
531
|
+
1. **Callbacks** (highest priority) - `getValueCallback`, `getDisplayValueCallback`, etc.
|
|
532
|
+
2. **Member properties** - `valueMember`, `displayValueMember`, etc.
|
|
533
|
+
3. **Default properties** (lowest priority) - Falls back to `value`, `label`, `name`, etc.
|
|
534
|
+
|
|
535
|
+
#### TypeScript Support
|
|
536
|
+
|
|
537
|
+
The component is fully typed with generics:
|
|
538
|
+
|
|
539
|
+
```typescript
|
|
540
|
+
import type { MultiSelectElement } from '@keenmate/web-multiselect';
|
|
541
|
+
|
|
542
|
+
interface Product {
|
|
543
|
+
id: string;
|
|
544
|
+
name: string;
|
|
545
|
+
price: number;
|
|
546
|
+
category: string;
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
const select = document.querySelector<MultiSelectElement<Product>>('multi-select');
|
|
550
|
+
select.options = [
|
|
551
|
+
{ id: 'p1', name: 'Laptop', price: 999, category: 'Electronics' }
|
|
552
|
+
];
|
|
553
|
+
```
|
|
554
|
+
|
|
555
|
+
### Form Integration
|
|
556
|
+
|
|
557
|
+
The component seamlessly integrates with standard HTML forms by automatically creating hidden inputs in the light DOM (outside Shadow DOM) so FormData can access them.
|
|
558
|
+
|
|
559
|
+
#### Basic Form Integration
|
|
560
|
+
|
|
561
|
+
```html
|
|
562
|
+
<form id="userForm" action="/submit" method="POST">
|
|
563
|
+
<label>Select Skills:</label>
|
|
564
|
+
<multi-select
|
|
565
|
+
name="skills"
|
|
566
|
+
value-format="json"
|
|
567
|
+
multiple="true">
|
|
568
|
+
</multi-select>
|
|
569
|
+
|
|
570
|
+
<button type="submit">Submit</button>
|
|
571
|
+
</form>
|
|
572
|
+
|
|
573
|
+
<script type="module">
|
|
574
|
+
import '@keenmate/web-multiselect';
|
|
575
|
+
|
|
576
|
+
const form = document.getElementById('userForm');
|
|
577
|
+
const select = form.querySelector('multi-select');
|
|
578
|
+
|
|
579
|
+
select.options = [
|
|
580
|
+
{ value: 'js', label: 'JavaScript' },
|
|
581
|
+
{ value: 'ts', label: 'TypeScript' },
|
|
582
|
+
{ value: 'py', label: 'Python' }
|
|
583
|
+
];
|
|
584
|
+
|
|
585
|
+
form.addEventListener('submit', (e) => {
|
|
586
|
+
e.preventDefault();
|
|
587
|
+
const formData = new FormData(form);
|
|
588
|
+
|
|
589
|
+
// Access the value
|
|
590
|
+
const skills = formData.get('skills');
|
|
591
|
+
console.log('Selected skills:', skills);
|
|
592
|
+
// Output: ["js","ts"] (JSON string)
|
|
593
|
+
});
|
|
594
|
+
</script>
|
|
595
|
+
```
|
|
596
|
+
|
|
597
|
+
#### Value Formats
|
|
598
|
+
|
|
599
|
+
Choose how selected values are serialized in forms:
|
|
600
|
+
|
|
601
|
+
**JSON Format** (default):
|
|
602
|
+
```html
|
|
603
|
+
<multi-select name="items" value-format="json"></multi-select>
|
|
604
|
+
<!-- FormData result: items = ["item1","item2","item3"] -->
|
|
605
|
+
```
|
|
606
|
+
|
|
607
|
+
**CSV Format**:
|
|
608
|
+
```html
|
|
609
|
+
<multi-select name="items" value-format="csv"></multi-select>
|
|
610
|
+
<!-- FormData result: items = "item1,item2,item3" -->
|
|
611
|
+
```
|
|
612
|
+
|
|
613
|
+
**Array Format** (multiple inputs):
|
|
614
|
+
```html
|
|
615
|
+
<multi-select name="items" value-format="array"></multi-select>
|
|
616
|
+
<!-- FormData result:
|
|
617
|
+
items[] = "item1"
|
|
618
|
+
items[] = "item2"
|
|
619
|
+
items[] = "item3"
|
|
620
|
+
-->
|
|
621
|
+
```
|
|
622
|
+
|
|
623
|
+
#### Custom Value Formatting
|
|
624
|
+
|
|
625
|
+
For advanced use cases, provide a custom formatting function:
|
|
626
|
+
|
|
627
|
+
```javascript
|
|
628
|
+
const select = document.querySelector('multi-select');
|
|
629
|
+
|
|
630
|
+
select.name = 'product_ids';
|
|
631
|
+
select.getValueFormatCallback = (values) => {
|
|
632
|
+
// Custom format: pipe-separated with prefix
|
|
633
|
+
return values.map(v => `ID:${v}`).join('|');
|
|
634
|
+
};
|
|
635
|
+
|
|
636
|
+
// When submitted, FormData will have:
|
|
637
|
+
// product_ids = "ID:123|ID:456|ID:789"
|
|
638
|
+
```
|
|
639
|
+
|
|
640
|
+
#### Using getValue() for JavaScript Submissions
|
|
641
|
+
|
|
642
|
+
For JavaScript-based form submissions (AJAX, fetch), use `getValue()`:
|
|
643
|
+
|
|
644
|
+
```javascript
|
|
645
|
+
// Single-select mode
|
|
646
|
+
const select = document.querySelector('multi-select[multiple="false"]');
|
|
647
|
+
const selectedId = select.getValue();
|
|
648
|
+
// Returns: "js" or null
|
|
649
|
+
|
|
650
|
+
// Multi-select mode
|
|
651
|
+
const multiSelect = document.querySelector('multi-select[multiple="true"]');
|
|
652
|
+
const selectedIds = multiSelect.getValue();
|
|
653
|
+
// Returns: ["js", "ts", "py"] or []
|
|
654
|
+
|
|
655
|
+
// Submit with fetch
|
|
656
|
+
const response = await fetch('/api/update', {
|
|
657
|
+
method: 'POST',
|
|
658
|
+
headers: { 'Content-Type': 'application/json' },
|
|
659
|
+
body: JSON.stringify({
|
|
660
|
+
skills: multiSelect.getValue()
|
|
661
|
+
})
|
|
662
|
+
});
|
|
663
|
+
```
|
|
664
|
+
|
|
665
|
+
#### Working with Numeric Values
|
|
666
|
+
|
|
667
|
+
The component handles both string and numeric values correctly:
|
|
668
|
+
|
|
669
|
+
```javascript
|
|
670
|
+
select.options = [
|
|
671
|
+
{ value: 1, label: 'Option 1' },
|
|
672
|
+
{ value: 2, label: 'Option 2' },
|
|
673
|
+
{ value: 3, label: 'Option 3' }
|
|
674
|
+
];
|
|
675
|
+
|
|
676
|
+
// getValue() preserves types
|
|
677
|
+
const values = select.getValue();
|
|
678
|
+
// Returns: [1, 2, 3] (numbers, not strings)
|
|
679
|
+
|
|
680
|
+
// FormData serialization
|
|
681
|
+
// JSON format: [1,2,3]
|
|
682
|
+
// CSV format: 1,2,3
|
|
683
|
+
// Array format: items[]=1, items[]=2, items[]=3
|
|
684
|
+
```
|
|
685
|
+
|
|
345
686
|
### Disabled Options
|
|
346
687
|
|
|
347
688
|
```javascript
|
|
@@ -409,7 +750,32 @@ You can customize the component using CSS variables even with just a `<script>`
|
|
|
409
750
|
|
|
410
751
|
### Available CSS Variables
|
|
411
752
|
|
|
412
|
-
|
|
753
|
+
The component exposes **150+ CSS custom properties** defined at the `:host` level, making them inspectable and overridable. Below are the **50+ most commonly customized variables** organized by category.
|
|
754
|
+
|
|
755
|
+
#### Inspecting Variables in DevTools
|
|
756
|
+
|
|
757
|
+
All CSS custom properties are now defined at the `:host` level in the compiled CSS, making them visible in browser DevTools:
|
|
758
|
+
|
|
759
|
+
1. Open DevTools (F12) and select the `<multi-select>` element
|
|
760
|
+
2. In the **Styles** panel, look for the `:host` selector
|
|
761
|
+
3. You'll see all 150+ variables with their default values
|
|
762
|
+
4. Edit values live to preview changes instantly
|
|
763
|
+
|
|
764
|
+
**CSS variables work with Shadow DOM** because they inherit through the shadow boundary. This means you can customize the component from outside:
|
|
765
|
+
|
|
766
|
+
```html
|
|
767
|
+
<style>
|
|
768
|
+
/* These variables will penetrate into the Shadow DOM */
|
|
769
|
+
multi-select {
|
|
770
|
+
--ml-accent-color: #10b981; /* Changes primary color */
|
|
771
|
+
--ml-input-border-radius: 0.5rem; /* Rounds input corners */
|
|
772
|
+
}
|
|
773
|
+
</style>
|
|
774
|
+
```
|
|
775
|
+
|
|
776
|
+
For the complete list of all available CSS variables, see:
|
|
777
|
+
- [_css-variables.scss](./src/scss/_css-variables.scss) - All 150+ CSS custom properties at `:host` level
|
|
778
|
+
- [_variables.scss](./src/scss/_variables.scss) - Foundation SCSS variables (colors, spacing, typography)
|
|
413
779
|
|
|
414
780
|
#### Colors
|
|
415
781
|
|