@appius-fr/apx 2.6.2 → 2.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. package/APX.mjs +2 -0
  2. package/README.md +207 -203
  3. package/dist/2ab50e700c8fbddb45e0.svg +10 -0
  4. package/dist/2e967d8dd752e0bed703.svg +10 -0
  5. package/dist/5ddaeefe5dfbc8e09652.svg +7 -0
  6. package/dist/6dc2907ba3bbb232601d.svg +10 -0
  7. package/dist/6e1e61dfca176a885b8d.svg +3 -0
  8. package/dist/6f3a0a27a260bb2c221b.svg +9 -0
  9. package/dist/8b07a8bf719a38262b7d.svg +10 -0
  10. package/dist/APX.dev.mjs +781 -227
  11. package/dist/APX.mjs +1 -1
  12. package/dist/APX.prod.mjs +1 -1
  13. package/dist/APX.standalone.js +677 -75
  14. package/dist/APX.standalone.js.map +1 -1
  15. package/dist/bdfa755a1cdb872368c7.svg +3 -0
  16. package/dist/c9da177f7663f9fcd023.svg +10 -0
  17. package/dist/ce9ef5fceb78e17e68c9.svg +8 -0
  18. package/dist/ed5af5163957b04bc6cc.svg +7 -0
  19. package/modules/listen/README.md +242 -235
  20. package/modules/listen/listen.mjs +1 -3
  21. package/modules/scrollableTable/README.md +52 -0
  22. package/modules/scrollableTable/css/scrollableTable.css +60 -0
  23. package/modules/scrollableTable/scrollableTable.mjs +198 -0
  24. package/modules/toast/README.md +186 -153
  25. package/modules/tristate/CHANGELOG.md +34 -0
  26. package/modules/tristate/README.md +157 -94
  27. package/modules/tristate/assets/tristate-checked.svg +3 -0
  28. package/modules/tristate/assets/tristate-cross.svg +10 -0
  29. package/modules/tristate/assets/tristate-crossed.svg +3 -0
  30. package/modules/tristate/assets/tristate-indeterminate-dash.svg +9 -0
  31. package/modules/tristate/assets/tristate-tick.svg +10 -0
  32. package/modules/tristate/assets/tristate-unchecked.svg +7 -0
  33. package/modules/tristate/css/tristate.css +91 -24
  34. package/modules/tristate/tristate.mjs +292 -171
  35. package/package.json +5 -1
@@ -1,95 +1,158 @@
1
- # APX Tri-State Checkbox Module
2
-
3
- The APX Tri-State Checkbox module provides an augmentation to the `APX` object. This functionality allows you to easily transform standard checkboxes within an `APX` object into tri-state checkboxes.
4
-
5
- ## Installation
6
-
7
- Ensure you have the core `APX` object integrated into your project.
8
-
9
- Next, to use this augmentation, make sure you've imported and added the `tristate` module to your project:
10
-
11
- ```javascript
12
- import APX from 'path-to-APX';
13
- // Assuming tristate is part of APX's module structure
14
- ```
15
-
16
- Also, link the required CSS:
17
-
18
- ```html
19
- <link rel="stylesheet" href="path-to-css/tristate.css">
20
- ```
21
-
22
- ## Usage
23
-
24
- To transform a group of checkboxes into tri-state checkboxes using the `APX` object, first create an `APX` object containing the checkboxes you wish to transform, then simply call the `tristate` method:
25
-
26
- ```javascript
27
- const myApxCheckboxes = APX('.my-checkbox-class');
28
- myApxCheckboxes.tristate();
29
- ```
30
-
31
- ### Parameters
32
-
33
- The `tristate` method accepts an `options` parameter which can have the following properties:
34
-
35
- - `size`: An object that defines the `width` and `height` of the tri-state checkbox.
36
- - `width`: Width of the checkbox in pixels (e.g., `50` for 50px width).
37
- - `height`: Height of the checkbox in pixels.
38
-
39
- - `classes`: A string or an array of strings representing classes to be added to the custom checkbox. The string can contain class names separated by spaces or commas.
40
-
41
- - `defaultState`: A string that defines the default state of the tri-state checkbox. Possible values are 'checked', 'unchecked', and 'crossed'.
42
-
43
- - `callbacks`: An object that contains callback functions.
44
- - `after`: A function that is called after the tri-state checkbox is created.
45
- - `change`: A function that is called when the tri-state checkbox state changes.
46
-
47
- - `bubbleEvents`: A boolean that determines whether events (`click`, `keyup`, `change`) on the custom checkbox should bubble up to the original checkbox.
48
-
49
- Example:
50
-
51
- ```javascript
52
- const options = {
53
- size: {
54
- width: 50,
55
- height: 30
56
- },
57
- classes: 'myClass1 myClass2',
58
- defaultState: 'checked',
59
- callbacks: {
60
- after: function(originalCheckbox, tristateDom, hiddenInput) {
61
- console.log("Checkbox created!");
62
- },
63
- change: function(originalCheckbox, tristateDom, hiddenInput) {
64
- console.log("Checkbox state changed!");
65
- }
66
- },
67
- bubbleEvents: true
68
- };
69
-
70
- const myApxCheckboxes = APX('.my-checkbox-class');
71
- myApxCheckboxes.tristate(options);
72
- ```
73
-
74
- ## Features
75
-
76
- ### Tri-State Logic
77
-
78
- The checkbox will cycle through three states:
79
-
80
- 1. Checked (represented by a tick)
81
- 2. Crossed (represented by a cross)
82
- 3. Unchecked (default checkbox state)
83
-
84
- ### Accessibility
85
-
86
- The tri-state checkbox retains the `tabIndex` property of the original checkbox, ensuring the accessibility of the control.
87
-
88
- ### Event Bubbling
89
-
90
- Any `click`, `keyup`, or `change` events attached to the original checkbox will be bubbled up from the custom checkbox if the `bubbleEvents` option is set to true. This ensures that your existing event listeners will still work as expected and that custom callbacks can also be executed.
91
-
92
-
93
- ## License
94
-
1
+ # APX Tri-State Checkbox Module
2
+
3
+ The APX Tri-State Checkbox module provides an augmentation to the `APX` object. This functionality allows you to easily transform standard checkboxes within an `APX` object into tri-state checkboxes.
4
+
5
+ ### When to use this module
6
+
7
+ This module is designed for **whitelist/blacklist** and **inheritance-based policy** UIs: permissions, feature flags, or rule matrices where each row can be explicitly allowed, explicitly denied, or left unspecified (inherit/default). Three distinct states avoid forcing a binary choice and keep policy intent clear both in the UI and when submitting to the backend.
8
+
9
+ ## Installation
10
+
11
+ Ensure you have the core `APX` object integrated into your project.
12
+
13
+ Next, to use this augmentation, make sure you've imported and added the `tristate` module to your project:
14
+
15
+ ```javascript
16
+ import APX from 'path-to-APX';
17
+ // Assuming tristate is part of APX's module structure
18
+ ```
19
+
20
+ Also, link the required CSS:
21
+
22
+ ```html
23
+ <link rel="stylesheet" href="path-to-css/tristate.css">
24
+ ```
25
+
26
+ ## Usage
27
+
28
+ To transform a group of checkboxes into tri-state checkboxes using the `APX` object, first create an `APX` object containing the checkboxes you wish to transform, then simply call the `tristate` method:
29
+
30
+ ```javascript
31
+ const myApxCheckboxes = APX('.my-checkbox-class');
32
+ myApxCheckboxes.tristate();
33
+ ```
34
+
35
+ **Example: policy row (whitelist/blacklist)** one checkbox per permission, submitted as allow/deny/inherit:
36
+
37
+ ```html
38
+ <form>
39
+ <label>Can edit <input type="checkbox" name="perm_edit" class="policy-cb"></label>
40
+ <label>Can delete <input type="checkbox" name="perm_delete" class="policy-cb"></label>
41
+ </form>
42
+ <script>
43
+ APX('.policy-cb').tristate({
44
+ defaultState: 'unchecked',
45
+ colors: { tick: '#fff' },
46
+ callbacks: { change: (_, el, hidden) => console.log(hidden.name, hidden.value || 'inherit'); }
47
+ });
48
+ </script>
49
+ ```
50
+
51
+ ### Parameters
52
+
53
+ The `tristate` method accepts an `options` parameter which can have the following properties:
54
+
55
+ - `size`: An object that defines the `width` and `height` of the tri-state checkbox.
56
+ - `width`: Numeric value is treated as pixels (e.g. `50` → `50px`); string values are used as-is (e.g. `'1.25rem'`, `'20px'`).
57
+ - `height`: Same as `width` (number = px, string = any CSS length).
58
+
59
+ - `classes`: A string or an array of strings representing classes to be added to the custom checkbox. The string can contain class names separated by spaces or commas.
60
+
61
+ - `defaultState`: A string that defines the default state of the tri-state checkbox. Possible values are 'checked', 'unchecked', and 'crossed'.
62
+
63
+ - `colors`: An object grouping color targets (recommended). Omitted keys keep CSS defaults.
64
+ - `tick`: CSS color for the check/cross symbol. Default is `transparent`.
65
+ - `checked`: CSS color for the checked state background. Default is `#0654ba`.
66
+ - `crossed`: CSS color for the crossed state background. Default is `#f00`.
67
+ - For backward compatibility, the flat options `tickColor`, `checkedColor`, and `crossedColor` are still supported; they are overridden by `colors` when both are present.
68
+
69
+ - `callbacks`: An object that contains callback functions.
70
+ - `after`: A function that is called after the tri-state checkbox is created.
71
+ - `change`: A function that is called when the tri-state checkbox state changes.
72
+
73
+ - `bubbleEvents`: A boolean that determines whether events (`click`, `keyup`, `change`) on the custom checkbox should bubble up to the original checkbox.
74
+
75
+ ### Parent + sub-checkboxes: `setChildren(childrenApx)`
76
+
77
+ `tristate()` returns a chainable object with a `setChildren` method. Use it to link one parent tristate to its child checkboxes. The parent then reflects the children (all checked → parent checked, all crossed → parent crossed, all unchecked → parent empty, **mixed children → parent shows indeterminate dash**). Clicking the parent applies its new state to all children.
78
+
79
+ The indeterminate (dash) appearance is **automatically** applied to the parent when `setChildren` is used: the dash is shown only when the children are in a mixed state; when all are unchecked the parent shows an empty frame.
80
+
81
+ ```javascript
82
+ APX('#policyParent').tristate({ size: { width: 28, height: 28 }, colors: { tick: '#fff' }, defaultState: 'unchecked' })
83
+ .setChildren(APX('#policyEmail, #policySms, #policyPush'));
84
+ ```
85
+
86
+ - Only the **first** parent in the APX selection is linked to **all** children in the given APX. For multiple parent/child groups, call `tristate().setChildren()` per parent.
87
+ - Children are initialized as tristates with the same options as the parent (plus internal change callbacks). If they are already tristates, they are just linked.
88
+
89
+ Example:
90
+
91
+ ```javascript
92
+ const options = {
93
+ size: {
94
+ width: 50,
95
+ height: 30
96
+ },
97
+ classes: 'myClass1 myClass2',
98
+ defaultState: 'checked',
99
+ callbacks: {
100
+ after: function(originalCheckbox, tristateDom, hiddenInput) {
101
+ console.log("Checkbox created!");
102
+ },
103
+ change: function(originalCheckbox, tristateDom, hiddenInput) {
104
+ console.log("Checkbox state changed!");
105
+ }
106
+ },
107
+ colors: {
108
+ tick: '#ffffff',
109
+ checked: '#0654ba',
110
+ crossed: '#ff0000'
111
+ },
112
+ bubbleEvents: true
113
+ };
114
+
115
+ const myApxCheckboxes = APX('.my-checkbox-class');
116
+ myApxCheckboxes.tristate(options);
117
+ ```
118
+
119
+ ## Features
120
+
121
+ ### Tri-State Logic
122
+
123
+ The checkbox will cycle through three states:
124
+
125
+ 1. Checked (represented by a tick)
126
+ 2. Crossed (represented by a cross)
127
+ 3. Unchecked (default checkbox state)
128
+
129
+ ### Whitelist/Blacklist semantics
130
+
131
+ For policy UIs, map the three states as follows:
132
+
133
+ | State | Visual | Typical meaning | Backend value (via hidden input) |
134
+ |-----------|--------|-------------------------|----------------------------------|
135
+ | Checked | Tick | Allow / whitelist | `true` (or your chosen value) |
136
+ | Crossed | Cross | Deny / blacklist | `false` (or your chosen value) |
137
+ | Unchecked | Empty | Inherit / default / N/A | Not submitted (no value) |
138
+
139
+ This gives you an explicit **deny vs not-set** distinction: users can leave a row unspecified instead of being forced to choose allow or deny.
140
+
141
+ ### Benefits
142
+
143
+ - **Explicit allow/deny vs inherit** — Unchecked means “no override”; checked and crossed mean explicit policy decisions.
144
+ - **Cleaner inheritance** — Hierarchical rules (e.g. org → team → user) can override only where needed; other levels inherit.
145
+ - **Auditability** — It is clear in the UI and in submitted data whether a rule was explicitly denied or simply not set.
146
+
147
+ ### Accessibility
148
+
149
+ The tri-state checkbox retains the `tabIndex` property of the original checkbox, ensuring the accessibility of the control.
150
+
151
+ ### Event Bubbling
152
+
153
+ Any `click`, `keyup`, or `change` events attached to the original checkbox will be bubbled up from the custom checkbox if the `bubbleEvents` option is set to true. This ensures that your existing event listeners will still work as expected and that custom callbacks can also be executed.
154
+
155
+
156
+ ## License
157
+
95
158
  Copyright Appius SARL
@@ -0,0 +1,3 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
2
+ <rect width="32" height="32" fill="#0654ba" />
3
+ </svg>
@@ -0,0 +1,10 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
2
+ <path
3
+ d="M8 8L24 24M24 8L8 24"
4
+ fill="none"
5
+ stroke="#fff"
6
+ stroke-width="3"
7
+ stroke-linecap="round"
8
+ stroke-linejoin="round"
9
+ />
10
+ </svg>
@@ -0,0 +1,3 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
2
+ <rect width="32" height="32" fill="#f00" />
3
+ </svg>
@@ -0,0 +1,9 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
2
+ <path
3
+ d="M8 16h16"
4
+ fill="none"
5
+ stroke="#fff"
6
+ stroke-width="3"
7
+ stroke-linecap="round"
8
+ />
9
+ </svg>
@@ -0,0 +1,10 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
2
+ <path
3
+ d="M6 17L12 23L26 9"
4
+ fill="none"
5
+ stroke="#fff"
6
+ stroke-width="3"
7
+ stroke-linecap="round"
8
+ stroke-linejoin="round"
9
+ />
10
+ </svg>
@@ -0,0 +1,7 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
2
+ <mask id="frame-cutout">
3
+ <rect width="32" height="32" fill="#fff" />
4
+ <rect x="3" y="3" width="26" height="26" rx="4" ry="4" fill="#000" />
5
+ </mask>
6
+ <rect width="32" height="32" fill="#cccccc" mask="url(#frame-cutout)" />
7
+ </svg>
@@ -1,25 +1,92 @@
1
- .apx-tristate {
2
- display: inline-block;
3
- vertical-align: middle;
4
- text-align: center;
5
- cursor: pointer;
6
- position: relative;
7
- min-width:13px;
8
- min-height:13px;
9
- background-size: contain;
10
- background-color: white;
11
- border-radius:3px;
12
- }
13
- .apx-tristate.unchecked {
14
- background-image: url('data:image/svg+xml,%3Csvg%20xmlns%3D%27http%3A//www.w3.org/2000/svg%27%20width%3D%2732%27%20height%3D%2732%27%3E%3Cpath%20%20fill%3D%22%23cccccc%22%20d%3D%22M25.107%2032.030h-18.214c-3.456%200-6.268-2.81-6.268-6.264v-19.529c0-3.456%202.812-6.268%206.268-6.268h18.214c3.456%200%206.268%202.812%206.268%206.268v19.529c0%203.452-2.812%206.264-6.268%206.264zM6.893%201.85c-2.419%200-4.386%201.967-4.386%204.386v19.529c0%202.417%201.967%204.382%204.386%204.382h18.214c2.419%200%204.386-1.965%204.386-4.382v-19.529c0-2.419-1.967-4.386-4.386-4.386h-18.214z%22%3E%3C/path%3E%3C/svg%3E');
15
- }
16
- .apx-tristate.checked {
17
- background-image: url('data:image/svg+xml,%3Csvg%20xmlns%3D%27http%3A//www.w3.org/2000/svg%27%20width%3D%2732%27%20height%3D%2732%27%3E%3Cpath%20fill%3D%22%230654ba%22%20d%3D%22M0.543%205.647c0-3.119%202.531-5.647%205.65-5.647h19.309c3.12%200%205.65%202.511%205.65%205.647v20.705c0%203.119-2.531%205.647-5.65%205.647h-19.309c-3.12%200-5.65-2.511-5.65-5.647v-20.705zM5.313%2017.587l7.039%206.839%2013.831-13.439-2.636-2.561-10.929%2010.62-4.442-4.317-2.862%202.858z%22%3E%3C/path%3E%3C/svg%3E');
18
- background-repeat: no-repeat;
19
- background-position: center;
20
- }
21
- .apx-tristate.crossed {
22
- background-image: url('data:image/svg+xml,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22UTF-8%22%3F%3E%3Csvg%20viewBox%3D%220%200%2032%2032%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%3Cpath%20d%3D%22m0.543%205.647c0-3.119%202.531-5.647%205.65-5.647h19.309c3.12%200%205.65%202.511%205.65%205.647v20.705c0%203.119-2.531%205.647-5.65%205.647h-19.309c-3.12%200-5.65-2.511-5.65-5.647v-20.705z%22%20fill%3D%22%23f00%22%2F%3E%3Cpath%20d%3D%22m8%208%2016%2016%22%20stroke%3D%22%23fff%22%20stroke-width%3D%222%22%2F%3E%3Cpath%20d%3D%22M24%208L8%2024%22%20stroke%3D%22%23fff%22%20stroke-width%3D%222%22%2F%3E%3C%2Fsvg%3E');
23
- background-repeat: no-repeat;
24
- background-position: center;
1
+ .apx-tristate {
2
+ display: inline-block;
3
+ vertical-align: middle;
4
+ text-align: center;
5
+ cursor: pointer;
6
+ position: relative;
7
+ min-width:13px;
8
+ min-height:13px;
9
+ background-size: contain;
10
+ background-color: transparent;
11
+ border-radius:3px;
12
+ --apx-tristate-tick-color: transparent;
13
+ --apx-tristate-checked-color: #0654ba;
14
+ --apx-tristate-crossed-color: #f00;
15
+ }
16
+ .apx-tristate.unchecked {
17
+ background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='32' height='32' viewBox='0 0 32 32'%3E%3Cmask id='frame-cutout'%3E%3Crect width='32' height='32' fill='%23fff'/%3E%3Crect x='3' y='3' width='26' height='26' rx='4' ry='4' fill='%23000'/%3E%3C/mask%3E%3Crect width='32' height='32' fill='%23cccccc' mask='url(%23frame-cutout)'/%3E%3C/svg%3E");
18
+ }
19
+ .apx-tristate.unchecked.apx-tristate--unchecked-indeterminate::after {
20
+ content: '';
21
+ position: absolute;
22
+ inset: 0;
23
+ display: block;
24
+ pointer-events: none;
25
+ }
26
+ /* When --mixed is used (e.g. parent + sub-checkboxes), dash shows only when children are mixed */
27
+ .apx-tristate.unchecked.apx-tristate--indeterminate-dash:not(.apx-tristate--mixed)::before,
28
+ .apx-tristate.unchecked.apx-tristate--indeterminate-dash:not(.apx-tristate--mixed)::after {
29
+ display: none;
30
+ }
31
+ .apx-tristate.unchecked.apx-tristate--indeterminate-dash::before {
32
+ content: '';
33
+ position: absolute;
34
+ inset: 0;
35
+ display: block;
36
+ pointer-events: none;
37
+ background-color: var(--apx-tristate-checked-color);
38
+ border-radius: inherit;
39
+ }
40
+ .apx-tristate.unchecked.apx-tristate--indeterminate-dash::after {
41
+ background-color: var(--apx-tristate-tick-color);
42
+ -webkit-mask-repeat: no-repeat;
43
+ -webkit-mask-position: center;
44
+ -webkit-mask-size: 100% 100%;
45
+ -webkit-mask-mode: alpha;
46
+ mask-repeat: no-repeat;
47
+ mask-position: center;
48
+ mask-size: 100% 100%;
49
+ mask-mode: alpha;
50
+ -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='32' height='32' viewBox='0 0 32 32'%3E%3Cpath d='M8 16h16' fill='none' stroke='%23fff' stroke-width='3' stroke-linecap='round'/%3E%3C/svg%3E");
51
+ mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='32' height='32' viewBox='0 0 32 32'%3E%3Cpath d='M8 16h16' fill='none' stroke='%23fff' stroke-width='3' stroke-linecap='round'/%3E%3C/svg%3E");
52
+ }
53
+ .apx-tristate.unchecked.apx-tristate--indeterminate-filled-check::after {
54
+ background-color: var(--apx-tristate-checked-color);
55
+ border-radius: inherit;
56
+ }
57
+ .apx-tristate.checked {
58
+ background-image: none;
59
+ background-color: var(--apx-tristate-checked-color);
60
+ }
61
+ .apx-tristate.crossed {
62
+ background-image: none;
63
+ background-color: var(--apx-tristate-crossed-color);
64
+ }
65
+
66
+ .apx-tristate.checked::after,
67
+ .apx-tristate.crossed::after {
68
+ content: '';
69
+ position: absolute;
70
+ inset: 0;
71
+ display: block;
72
+ background-color: var(--apx-tristate-tick-color);
73
+ -webkit-mask-repeat: no-repeat;
74
+ -webkit-mask-position: center;
75
+ -webkit-mask-size: 100% 100%;
76
+ -webkit-mask-mode: alpha;
77
+ mask-repeat: no-repeat;
78
+ mask-position: center;
79
+ mask-size: 100% 100%;
80
+ mask-mode: alpha;
81
+ pointer-events: none;
82
+ }
83
+
84
+ .apx-tristate.checked::after {
85
+ -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='32' height='32' viewBox='0 0 32 32'%3E%3Cpath d='M6 17L12 23L26 9' fill='none' stroke='%23fff' stroke-width='3' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
86
+ mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='32' height='32' viewBox='0 0 32 32'%3E%3Cpath d='M6 17L12 23L26 9' fill='none' stroke='%23fff' stroke-width='3' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
87
+ }
88
+
89
+ .apx-tristate.crossed::after {
90
+ -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='32' height='32' viewBox='0 0 32 32'%3E%3Cpath d='M8 8L24 24M24 8L8 24' fill='none' stroke='%23fff' stroke-width='3' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
91
+ mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='32' height='32' viewBox='0 0 32 32'%3E%3Cpath d='M8 8L24 24M24 8L8 24' fill='none' stroke='%23fff' stroke-width='3' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
25
92
  }