@keenmate/web-multiselect 1.10.0 → 1.12.0-rc01
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 +73 -28
- package/component-variables.manifest.json +20 -5
- package/dist/index.d.ts +13 -0
- package/dist/multiselect.js +391 -335
- package/dist/multiselect.umd.js +10 -10
- package/dist/style.css +1 -1
- package/package.json +2 -1
- package/src/css/animations.css +1 -0
- package/src/css/{_badges-display.css → badges.css} +11 -110
- package/src/css/{_base.css → base.css} +3 -3
- package/src/css/{_input-dropdown.css → controls.css} +16 -108
- package/src/css/count-display.css +108 -0
- package/src/css/dark-mode.css +58 -0
- package/src/css/{_debug.css → debug.css} +18 -18
- package/src/css/{_tooltips-popover.css → floating.css} +94 -10
- package/src/css/main.css +27 -22
- package/src/css/{_options.css → options.css} +1 -1
- package/src/css/{_modifiers.css → states.css} +5 -8
- package/src/css/{_variables.css → variables.css} +69 -23
- /package/src/css/{_rtl.css → rtl.css} +0 -0
package/README.md
CHANGED
|
@@ -7,27 +7,27 @@ A lightweight, accessible multiselect web component with typeahead search, RTL l
|
|
|
7
7
|
|
|
8
8
|
> **⚠️ Security Notice:** This component intentionally allows raw HTML in rendering callbacks to give developers full control over content display. If you display user-generated content, you must sanitize it yourself. See [HTML Injection (XSS) Notice](#html-injection-xss-notice) for the complete list of affected callbacks.
|
|
9
9
|
|
|
10
|
-
## What's New in v1.
|
|
11
|
-
|
|
12
|
-
-
|
|
13
|
-
-
|
|
14
|
-
- **
|
|
15
|
-
-
|
|
16
|
-
- **
|
|
17
|
-
- **
|
|
18
|
-
-
|
|
19
|
-
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
- **
|
|
26
|
-
- **
|
|
27
|
-
-
|
|
28
|
-
- **
|
|
29
|
-
-
|
|
30
|
-
-
|
|
10
|
+
## What's New in v1.12.0-rc01
|
|
11
|
+
|
|
12
|
+
- **Dark mode now responds to framework theme classes** — `data-bs-theme="dark"` (Bootstrap 5.3+), `.dark` (Tailwind), and `data-theme="dark"` on any ancestor flip the component to dark, even on apps that flip classes without declaring `color-scheme`. Your own `--base-*` overrides on `:root` flow through unchanged — the component picks the dark branch of its built-in fallbacks without clobbering your theme. Symmetric `light` selectors let you force one widget back to light on a dark page. Per-instance `<web-multiselect data-theme="dark">` works as the highest-priority escape hatch.
|
|
13
|
+
- **CSS cascade layers (`@layer variables, component, overrides`)** — every internal rule lives in a named layer, so consumer CSS in the light DOM can override any component rule without `!important` or specificity arms races. Document any unlayered rule in your app's CSS and it wins automatically.
|
|
14
|
+
- **CSS file structure refactored into canonical Tier 1+2+3** — `controls.css` for input chrome, `floating.css` for dropdown/hint/tooltip/popover, `states.css` for block-level state modifiers, `badges.css` / `count-display.css` for the two display modes, etc. Each file now owns one logical concern; the old mixed-bag `input-dropdown.css`, `tooltips-popover.css`, `badges-display.css` are gone. Only affects deep CSS imports; bundle output unchanged.
|
|
15
|
+
- **CSS source files no longer have underscore prefix** — `_variables.css` → `variables.css`, etc. The underscore was a leftover SASS partial convention; these are plain CSS modules.
|
|
16
|
+
- **BEM-aligned class names** — `.ms-wrapper` → `.ms__wrapper`, `.ms-debug-info` → `.ms__debug-info`, `.ms-debug-stats` → `.ms__debug-stats`. The wrapper class is internal layout chrome; debug classes are dev-only. See CHANGELOG migration table if you'd styled any of these externally.
|
|
17
|
+
- **Debug panel now fully themeable** — 11 new `--ms-debug-*` variables (was hardcoded hex literals).
|
|
18
|
+
- **`"./manifest"` short-import** — `import manifest from '@keenmate/web-multiselect/manifest'` for tooling that reads the variable catalog.
|
|
19
|
+
- **`test/AUDIT.md`** — living compliance tracker against the BlissFramework web-component guidelines (CSS structure, base variables, color scheme).
|
|
20
|
+
|
|
21
|
+
## What's New in v1.11.0
|
|
22
|
+
|
|
23
|
+
- **OS-aware light/dark defaults via `light-dark()`** — set `color-scheme: dark` on your page (`:root`, `body`, etc.) and the multiselect picks readable dark text/background colors automatically. No more enumerating ~15 `--base-*` overrides just to get usable defaults on a dark theme.
|
|
24
|
+
- **Drift-detection warning for positioning edge cases** — if an exotic ancestor CSS property (e.g. `contain: paint`, or `container-type` in certain shadow-DOM layouts) makes the dropdown land somewhere other than where the library told the browser to put it, a `console.warn` fires once with the likely culprit element and an actionable fix suggestion.
|
|
25
|
+
- **Dropdown no longer stranded to the side when an ancestor uses `container-type`** — Floating UI was walking up to a `container-type: inline-size` ancestor (notably pure-admin's `.pa-layout__main`), but the browser wouldn't actually anchor the fixed panel there. The library now uses a custom `getOffsetParent` that only walks up properties browsers reliably honor for fixed positioning.
|
|
26
|
+
- **Dropdown no longer opens shifted to the side of its input** — when the dropdown's natural content was wider than the input, Floating UI's `shift()` middleware was measuring the unclamped panel and pushing it left, then the subsequent width clamp left it stranded next to the input. Panel sizing now happens before positioning. Same fix applies to the selected-items popover.
|
|
27
|
+
- **`--base-*` taxonomy aligned with KeenMate cross-component naming** (theming change — see CHANGELOG migration table): `--base-primary-bg` → `--base-hover-bg`, `--base-primary-bg-hover` → `--base-active-bg`. `--base-dropdown-bg` and `--base-tooltip-bg` continue to work; new chain fallbacks to `--base-elevated-bg` / `--base-inverse-bg`.
|
|
28
|
+
- **Option hover stays visible on dark themes** — `--ms-primary-bg` now mixes 8% of the text color into the main background by default, so the hover is always a visible step toward the text. No more invisible hover when the consumer forgets to override `--base-hover-bg`.
|
|
29
|
+
- **New `examples-positioning.html`** — walks through baseline / transformed-ancestor / container-type / drift-detection scenarios.
|
|
30
|
+
- **New dark-mode e2e suite** — 4 specs verifying WCAG-AA option contrast on a dark page across fully-themed, minimal-override, and pure OS-inheritance configurations.
|
|
31
31
|
|
|
32
32
|
## Features
|
|
33
33
|
|
|
@@ -1662,8 +1662,9 @@ KeenMate components support a **two-layer theming architecture**:
|
|
|
1662
1662
|
:root {
|
|
1663
1663
|
/* Base layer - single source of truth */
|
|
1664
1664
|
--base-accent-color: #3b82f6;
|
|
1665
|
-
--base-
|
|
1666
|
-
--base-
|
|
1665
|
+
--base-main-bg: #ffffff;
|
|
1666
|
+
--base-hover-bg: #f3f4f6;
|
|
1667
|
+
--base-text-color-1: #111827;
|
|
1667
1668
|
|
|
1668
1669
|
/* Components reference base layer */
|
|
1669
1670
|
--ms-accent-color: var(--base-accent-color);
|
|
@@ -1730,6 +1731,31 @@ You can customize the component using CSS variables even with just a `<script>`
|
|
|
1730
1731
|
</style>
|
|
1731
1732
|
```
|
|
1732
1733
|
|
|
1734
|
+
### Dark mode — supported signals
|
|
1735
|
+
|
|
1736
|
+
Since v1.12.0 the multiselect honors **five different signals** for switching to dark mode. Pick whichever fits your app; you don't need to wire them all up.
|
|
1737
|
+
|
|
1738
|
+
| # | Signal | Set by | Example |
|
|
1739
|
+
|---|--------|--------|---------|
|
|
1740
|
+
| 1 | OS preference + page `color-scheme` | App author | `html { color-scheme: light dark }` — multiselect picks the OS branch automatically. |
|
|
1741
|
+
| 2 | Page-level `color-scheme: dark` | App author | `body { color-scheme: dark }` — flips every multiselect on the page to dark. |
|
|
1742
|
+
| 3 | Framework data-attribute on ancestor | Bootstrap, Pure Admin, custom apps | `<html data-bs-theme="dark">` or `<div data-theme="dark">…</div>` |
|
|
1743
|
+
| 4 | Framework class on ancestor | Tailwind, hand-rolled toggles | `<html class="dark">` |
|
|
1744
|
+
| 5 | Per-instance attribute on host | App author, for one widget | `<web-multiselect data-theme="dark">` |
|
|
1745
|
+
|
|
1746
|
+
**Precedence** (highest wins): per-instance (#5) → framework ancestor (#3, #4) → page color-scheme (#1, #2).
|
|
1747
|
+
|
|
1748
|
+
#### Forcing a single widget back to light
|
|
1749
|
+
|
|
1750
|
+
If your page is dark but you want one multiselect to render light:
|
|
1751
|
+
|
|
1752
|
+
```html
|
|
1753
|
+
<!-- on a body { color-scheme: dark } page -->
|
|
1754
|
+
<web-multiselect data-theme="light"></web-multiselect>
|
|
1755
|
+
```
|
|
1756
|
+
|
|
1757
|
+
This works for any of signals #3–#5. The symmetric `data-theme="light"`, `data-bs-theme="light"`, `.light` selectors restore the light palette inside the affected scope.
|
|
1758
|
+
|
|
1733
1759
|
### Available CSS Variables
|
|
1734
1760
|
|
|
1735
1761
|
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.
|
|
@@ -1756,7 +1782,7 @@ All CSS custom properties are now defined at the `:host` level in the compiled C
|
|
|
1756
1782
|
```
|
|
1757
1783
|
|
|
1758
1784
|
For the complete list of all available CSS variables, see:
|
|
1759
|
-
- [
|
|
1785
|
+
- [variables.css](./src/css/variables.css) - All 150+ CSS custom properties at `:host` level
|
|
1760
1786
|
|
|
1761
1787
|
#### Colors
|
|
1762
1788
|
|
|
@@ -1788,7 +1814,7 @@ For the complete list of all available CSS variables, see:
|
|
|
1788
1814
|
|
|
1789
1815
|
| Variable | Default | Description |
|
|
1790
1816
|
|----------|---------|-------------|
|
|
1791
|
-
| `--ms-dropdown-bg` | `var(--base-dropdown-bg, #ffffff)` | Dropdown background |
|
|
1817
|
+
| `--ms-dropdown-bg` | `var(--base-dropdown-bg, var(--base-elevated-bg, light-dark(#ffffff, #1a1a1a)))` | Dropdown background (auto-adapts to OS dark mode) |
|
|
1792
1818
|
| `--ms-dropdown-border` | `var(--ms-border-color)` | Dropdown border color |
|
|
1793
1819
|
| `--ms-dropdown-shadow` | (box shadow) | Dropdown shadow |
|
|
1794
1820
|
| `--ms-dropdown-max-height` | `20rem` | Max height of dropdown |
|
|
@@ -1860,7 +1886,7 @@ For the complete list of all available CSS variables, see:
|
|
|
1860
1886
|
|
|
1861
1887
|
| Variable | Default | Description |
|
|
1862
1888
|
|----------|---------|-------------|
|
|
1863
|
-
| `--ms-tooltip-bg` | `var(--base-tooltip-bg, #333333)` | Tooltip background
|
|
1889
|
+
| `--ms-tooltip-bg` | `var(--base-tooltip-bg, var(--base-inverse-bg, light-dark(#333333, #f5f5f5)))` | Tooltip background (auto-adapts to OS dark mode) |
|
|
1864
1890
|
| `--ms-tooltip-color` | `var(--ms-tooltip-text-color)` | Tooltip text color |
|
|
1865
1891
|
| `--ms-tooltip-padding` | `0.5rem 0.75rem` | Tooltip padding |
|
|
1866
1892
|
| `--ms-tooltip-border-radius` | `0.375rem` | Tooltip border radius |
|
|
@@ -1890,6 +1916,25 @@ For the complete list of all available CSS variables, see:
|
|
|
1890
1916
|
| `--ms-shadow-xl` | (box shadow) | Extra large shadow |
|
|
1891
1917
|
| `--ms-disabled-opacity` | `0.5` | Opacity for disabled state |
|
|
1892
1918
|
|
|
1919
|
+
### Cascade layers / override contract
|
|
1920
|
+
|
|
1921
|
+
Since v1.12.0, the component's internal CSS is organized into named `@layer`s:
|
|
1922
|
+
|
|
1923
|
+
```css
|
|
1924
|
+
@layer variables, component, overrides;
|
|
1925
|
+
```
|
|
1926
|
+
|
|
1927
|
+
This gives consumers a predictable escape hatch when they need to override a rule from outside the shadow DOM (e.g. via `web-multiselect ::part(...)` or descendant selectors that reach into composed light DOM):
|
|
1928
|
+
|
|
1929
|
+
| Where your rule lives | Wins against |
|
|
1930
|
+
|---|---|
|
|
1931
|
+
| Unlayered consumer rule | Every internal layer (no `!important` needed) |
|
|
1932
|
+
| Consumer `@layer overrides` block | Component's `overrides` layer if loaded later in the stylesheet stack |
|
|
1933
|
+
| `:root { --base-* }` declaration | Component's `variables` layer trivially |
|
|
1934
|
+
| `web-multiselect { --ms-* }` element selector | Same as above, with higher specificity |
|
|
1935
|
+
|
|
1936
|
+
In practice you rarely need to think about layers — variables-first theming (`--ms-*` and `--base-*` overrides) covers ~95% of customization. Layers exist for the residual 5% where you need to flip a property the variable system doesn't expose.
|
|
1937
|
+
|
|
1893
1938
|
### Advanced: Direct CSS Import
|
|
1894
1939
|
|
|
1895
1940
|
For users who want to import the raw CSS source files:
|
|
@@ -1899,8 +1944,8 @@ For users who want to import the raw CSS source files:
|
|
|
1899
1944
|
@import '@keenmate/web-multiselect/css';
|
|
1900
1945
|
|
|
1901
1946
|
/* Or import individual partials */
|
|
1902
|
-
@import '@keenmate/web-multiselect/src/css/
|
|
1903
|
-
@import '@keenmate/web-multiselect/src/css/
|
|
1947
|
+
@import '@keenmate/web-multiselect/src/css/variables.css';
|
|
1948
|
+
@import '@keenmate/web-multiselect/src/css/base.css';
|
|
1904
1949
|
/* ... etc */
|
|
1905
1950
|
```
|
|
1906
1951
|
|
|
@@ -14,7 +14,8 @@
|
|
|
14
14
|
{ "name": "base-text-color-4", "required": false, "usage": "Quaternary text (placeholders)" },
|
|
15
15
|
{ "name": "base-text-color-on-accent", "required": false, "usage": "Text on accent backgrounds (badges, checkboxes)" },
|
|
16
16
|
{ "name": "base-main-bg", "required": true, "usage": "Primary background, hint background, actions background" },
|
|
17
|
-
{ "name": "base-hover-bg", "required": false, "usage": "Hover
|
|
17
|
+
{ "name": "base-hover-bg", "required": false, "usage": "Hover background (drives --ms-primary-bg: option hover/focus, action-button hover, counter hover)" },
|
|
18
|
+
{ "name": "base-active-bg", "required": false, "usage": "Active/pressed background (drives --ms-primary-bg-hover: selected-active, counter badge)" },
|
|
18
19
|
{ "name": "base-disabled-bg", "required": false, "usage": "Disabled/readonly surface backgrounds" },
|
|
19
20
|
{ "name": "base-border-color", "required": true, "usage": "Input, dropdown, action button borders" },
|
|
20
21
|
{ "name": "base-border", "required": false, "usage": "Full border shorthand" },
|
|
@@ -25,10 +26,12 @@
|
|
|
25
26
|
{ "name": "base-input-border-focus", "required": false, "usage": "Input border when focused" },
|
|
26
27
|
{ "name": "base-input-placeholder-color", "required": false, "usage": "Placeholder text color" },
|
|
27
28
|
{ "name": "base-input-bg-disabled", "required": false, "usage": "Disabled input background" },
|
|
28
|
-
{ "name": "base-dropdown-bg", "required": false, "usage": "Dropdown and popover backgrounds" },
|
|
29
|
+
{ "name": "base-dropdown-bg", "required": false, "usage": "Dropdown and popover backgrounds (primary; falls back to base-elevated-bg)" },
|
|
30
|
+
{ "name": "base-elevated-bg", "required": false, "usage": "Elevated surface (fallback for dropdown and popover backgrounds)" },
|
|
29
31
|
{ "name": "base-dropdown-border", "required": false, "usage": "Dropdown border" },
|
|
30
32
|
{ "name": "base-dropdown-box-shadow", "required": false, "usage": "Dropdown shadow" },
|
|
31
|
-
{ "name": "base-tooltip-bg", "required": false, "usage": "Tooltip background" },
|
|
33
|
+
{ "name": "base-tooltip-bg", "required": false, "usage": "Tooltip background (primary; falls back to base-inverse-bg)" },
|
|
34
|
+
{ "name": "base-inverse-bg", "required": false, "usage": "Inverse surface (fallback for tooltip background)" },
|
|
32
35
|
{ "name": "base-tooltip-text-color", "required": false, "usage": "Tooltip text color" },
|
|
33
36
|
{ "name": "base-font-family", "required": false, "usage": "All text in component" },
|
|
34
37
|
{ "name": "base-font-size-2xs", "required": false, "usage": "Smallest text (multiplier)" },
|
|
@@ -69,8 +72,8 @@
|
|
|
69
72
|
{ "name": "ms-text-primary", "category": "text", "usage": "Legacy alias for text-color-1" },
|
|
70
73
|
{ "name": "ms-text-secondary", "category": "text", "usage": "Legacy alias for text-color-3" },
|
|
71
74
|
|
|
72
|
-
{ "name": "ms-primary-bg", "category": "surface", "usage": "
|
|
73
|
-
{ "name": "ms-primary-bg-hover", "category": "surface", "usage": "
|
|
75
|
+
{ "name": "ms-primary-bg", "category": "surface", "usage": "Hover/focus background (options, action buttons, counter); reads --base-hover-bg" },
|
|
76
|
+
{ "name": "ms-primary-bg-hover", "category": "surface", "usage": "Active/stronger background (counter badge text); reads --base-active-bg" },
|
|
74
77
|
|
|
75
78
|
{ "name": "ms-border-color", "category": "border", "usage": "Default border color" },
|
|
76
79
|
{ "name": "ms-border", "category": "border", "usage": "Full border shorthand" },
|
|
@@ -387,6 +390,18 @@
|
|
|
387
390
|
{ "name": "ms-scrollbar-thumb-bg-hover", "category": "scrollbar", "usage": "Scrollbar thumb hover background" },
|
|
388
391
|
{ "name": "ms-scrollbar-thumb-border-radius", "category": "scrollbar", "usage": "Scrollbar thumb border radius" },
|
|
389
392
|
|
|
393
|
+
{ "name": "ms-debug-bg", "category": "debug", "usage": "Debug panel background" },
|
|
394
|
+
{ "name": "ms-debug-border-color", "category": "debug", "usage": "Debug panel border" },
|
|
395
|
+
{ "name": "ms-debug-text-color", "category": "debug", "usage": "Debug panel text" },
|
|
396
|
+
{ "name": "ms-debug-border-radius", "category": "debug", "usage": "Debug panel corner radius" },
|
|
397
|
+
{ "name": "ms-debug-summary-color", "category": "debug", "usage": "Debug section heading color" },
|
|
398
|
+
{ "name": "ms-debug-summary-bg-hover", "category": "debug", "usage": "Debug section heading hover background" },
|
|
399
|
+
{ "name": "ms-debug-summary-outline-color", "category": "debug", "usage": "Debug section heading focus outline" },
|
|
400
|
+
{ "name": "ms-debug-summary-border-radius", "category": "debug", "usage": "Debug section heading corner radius" },
|
|
401
|
+
{ "name": "ms-debug-stats-bg", "category": "debug", "usage": "Debug stats nested panel background" },
|
|
402
|
+
{ "name": "ms-debug-stats-border-radius", "category": "debug", "usage": "Debug stats nested panel corner radius" },
|
|
403
|
+
{ "name": "ms-debug-bullet-color", "category": "debug", "usage": "Debug stat bullet marker color" },
|
|
404
|
+
|
|
390
405
|
{ "name": "ms-transform-center-y", "category": "transform", "usage": "Center Y transform" },
|
|
391
406
|
{ "name": "ms-transform-rotate-180", "category": "transform", "usage": "180 degree rotation" },
|
|
392
407
|
{ "name": "ms-transform-scale-hover", "category": "transform", "usage": "Hover scale transform" },
|
package/dist/index.d.ts
CHANGED
|
@@ -553,6 +553,7 @@ export declare class WebMultiSelect<T = any> {
|
|
|
553
553
|
private isRTL;
|
|
554
554
|
private effectiveBadgesPosition;
|
|
555
555
|
private justClosedViaClick;
|
|
556
|
+
private positioningDriftWarned;
|
|
556
557
|
private dropdownCleanup;
|
|
557
558
|
private hintCleanup;
|
|
558
559
|
private selectedPopoverCleanup;
|
|
@@ -665,6 +666,18 @@ export declare class WebMultiSelect<T = any> {
|
|
|
665
666
|
* compute then lock the resulting placement, optionally clamp by dropdownMin/MaxWidth.
|
|
666
667
|
*/
|
|
667
668
|
private anchorFloatingPanel;
|
|
669
|
+
/**
|
|
670
|
+
* Sanity-check that the browser placed the panel where we told it to. With `position: fixed`
|
|
671
|
+
* and no transformed/perspective/filter ancestor, `left: ${x}px` must render at viewport-x = x.
|
|
672
|
+
* If the rendered position drifts, the consumer has an ancestor that establishes a fixed
|
|
673
|
+
* containing block but isn't on our reliable-anchors list (likely `contain: paint|layout|strict`
|
|
674
|
+
* or `container-type` — which the spec says creates a CB but the browser's actual behavior
|
|
675
|
+
* varies across shadow-DOM scenarios). We can't fix it from inside the library, but we can
|
|
676
|
+
* surface a clear warning so the developer knows where to look.
|
|
677
|
+
*
|
|
678
|
+
* Fires at most once per multiselect instance to avoid flooding the console during autoUpdate.
|
|
679
|
+
*/
|
|
680
|
+
private verifyPanelLanded;
|
|
668
681
|
private positionDropdown;
|
|
669
682
|
private positionHint;
|
|
670
683
|
private parseInitialSelection;
|