@abstraks-dev/ui-library 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (158) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +708 -0
  3. package/dist/__tests__/Anchor.test.js +145 -0
  4. package/dist/__tests__/ArrowRight.test.js +91 -0
  5. package/dist/__tests__/Avatar.test.js +123 -0
  6. package/dist/__tests__/Button.test.js +82 -0
  7. package/dist/__tests__/Card.test.js +198 -0
  8. package/dist/__tests__/CheckCircle.test.js +98 -0
  9. package/dist/__tests__/Checkbox.test.js +161 -0
  10. package/dist/__tests__/ChevronDown.test.js +73 -0
  11. package/dist/__tests__/Close.test.js +98 -0
  12. package/dist/__tests__/EditSquare.test.js +99 -0
  13. package/dist/__tests__/Error.test.js +74 -0
  14. package/dist/__tests__/Footer.test.js +66 -0
  15. package/dist/__tests__/Heading.test.js +227 -0
  16. package/dist/__tests__/Hero.test.js +74 -0
  17. package/dist/__tests__/Label.test.js +123 -0
  18. package/dist/__tests__/Loader.test.js +115 -0
  19. package/dist/__tests__/MenuHover.test.js +137 -0
  20. package/dist/__tests__/Paragraph.test.js +93 -0
  21. package/dist/__tests__/PlusCircle.test.js +99 -0
  22. package/dist/__tests__/Radio.test.js +153 -0
  23. package/dist/__tests__/Select.test.js +187 -0
  24. package/dist/__tests__/Tabs.test.js +162 -0
  25. package/dist/__tests__/TextArea.test.js +127 -0
  26. package/dist/__tests__/TextInput.test.js +181 -0
  27. package/dist/__tests__/Toggle.test.js +120 -0
  28. package/dist/__tests__/TrashX.test.js +99 -0
  29. package/dist/__tests__/useHeadingAccessibility.test.js +144 -0
  30. package/dist/components/Anchor.js +131 -0
  31. package/dist/components/Animation.js +129 -0
  32. package/dist/components/AnimationGroup.js +207 -0
  33. package/dist/components/AnimationToggle.js +216 -0
  34. package/dist/components/Avatar.js +153 -0
  35. package/dist/components/Button.js +218 -0
  36. package/dist/components/Card.js +222 -0
  37. package/dist/components/Checkbox.js +305 -0
  38. package/dist/components/Crud.js +564 -0
  39. package/dist/components/DragAndDrop.js +337 -0
  40. package/dist/components/Error.js +206 -0
  41. package/dist/components/Footer.js +99 -0
  42. package/dist/components/Form.js +412 -0
  43. package/dist/components/Header.js +372 -0
  44. package/dist/components/Heading.js +134 -0
  45. package/dist/components/Hero.js +181 -0
  46. package/dist/components/Label.js +256 -0
  47. package/dist/components/Loader.js +302 -0
  48. package/dist/components/MenuHover.js +114 -0
  49. package/dist/components/Paragraph.js +128 -0
  50. package/dist/components/Prompt.js +61 -0
  51. package/dist/components/Radio.js +254 -0
  52. package/dist/components/Select.js +422 -0
  53. package/dist/components/SideMenu.js +313 -0
  54. package/dist/components/Tabs.js +297 -0
  55. package/dist/components/TextArea.js +370 -0
  56. package/dist/components/TextInput.js +286 -0
  57. package/dist/components/Toggle.js +186 -0
  58. package/dist/components/crudFiles/CrudEditBase.js +150 -0
  59. package/dist/components/crudFiles/CrudViewBase.js +39 -0
  60. package/dist/components/crudFiles/crudDevelopment.js +118 -0
  61. package/dist/components/crudFiles/crudEditHandlers.js +50 -0
  62. package/dist/constants/animation.js +30 -0
  63. package/dist/icons/ArrowIcon.js +32 -0
  64. package/dist/icons/ArrowRight.js +33 -0
  65. package/dist/icons/CheckCircle.js +33 -0
  66. package/dist/icons/ChevronDown.js +28 -0
  67. package/dist/icons/Close.js +33 -0
  68. package/dist/icons/EditSquare.js +33 -0
  69. package/dist/icons/Ellipses.js +34 -0
  70. package/dist/icons/Hamburger.js +39 -0
  71. package/dist/icons/LoadingSpinner.js +42 -0
  72. package/dist/icons/PlusCircle.js +33 -0
  73. package/dist/icons/SaveIcon.js +32 -0
  74. package/dist/icons/TrashX.js +33 -0
  75. package/dist/icons/__tests__/CheckCircle.test.js +9 -0
  76. package/dist/icons/__tests__/ChevronDown.test.js +9 -0
  77. package/dist/icons/__tests__/Close.test.js +9 -0
  78. package/dist/icons/__tests__/EditSquare.test.js +9 -0
  79. package/dist/icons/__tests__/PlusCircle.test.js +9 -0
  80. package/dist/icons/__tests__/TrashX.test.js +9 -0
  81. package/dist/icons/index.js +89 -0
  82. package/dist/index.js +332 -0
  83. package/dist/setupTests.js +3 -0
  84. package/dist/styles/_variables.scss +286 -0
  85. package/dist/styles/anchor.scss +40 -0
  86. package/dist/styles/animation-accessibility.scss +96 -0
  87. package/dist/styles/animation-toggle.scss +233 -0
  88. package/dist/styles/animation.scss +3781 -0
  89. package/dist/styles/avatar.scss +285 -0
  90. package/dist/styles/button.scss +430 -0
  91. package/dist/styles/card.scss +210 -0
  92. package/dist/styles/checkbox.scss +160 -0
  93. package/dist/styles/crud.scss +474 -0
  94. package/dist/styles/dragAndDrop.scss +312 -0
  95. package/dist/styles/error.scss +232 -0
  96. package/dist/styles/footer.scss +58 -0
  97. package/dist/styles/form.scss +420 -0
  98. package/dist/styles/grid.scss +29 -0
  99. package/dist/styles/header.scss +276 -0
  100. package/dist/styles/heading.scss +118 -0
  101. package/dist/styles/hero.scss +185 -0
  102. package/dist/styles/htmlElements.scss +20 -0
  103. package/dist/styles/image.scss +9 -0
  104. package/dist/styles/label.scss +340 -0
  105. package/dist/styles/list-item.scss +5 -0
  106. package/dist/styles/loader.scss +354 -0
  107. package/dist/styles/logo.scss +19 -0
  108. package/dist/styles/main.css +9056 -0
  109. package/dist/styles/main.css.map +1 -0
  110. package/dist/styles/main.scss +0 -0
  111. package/dist/styles/menu-hover.scss +30 -0
  112. package/dist/styles/paragraph.scss +88 -0
  113. package/dist/styles/prompt.scss +51 -0
  114. package/dist/styles/radio.scss +202 -0
  115. package/dist/styles/select.scss +363 -0
  116. package/dist/styles/side-menu.scss +334 -0
  117. package/dist/styles/tabs.scss +540 -0
  118. package/dist/styles/text-area.scss +388 -0
  119. package/dist/styles/text-input.scss +171 -0
  120. package/dist/styles/toggle.scss +0 -0
  121. package/dist/styles/unordered-list.scss +8 -0
  122. package/dist/utils/ScrollHandler.js +30 -0
  123. package/dist/utils/accessibility.js +128 -0
  124. package/dist/utils/heroUtils.js +316 -0
  125. package/dist/utils/index.js +104 -0
  126. package/dist/utils/inputValidation.js +29 -0
  127. package/dist/utils/keyboardNavigation.js +536 -0
  128. package/dist/utils/labelUtils.js +708 -0
  129. package/dist/utils/loaderUtils.js +387 -0
  130. package/dist/utils/menuUtils.js +575 -0
  131. package/dist/utils/useHeadingAccessibility.js +298 -0
  132. package/dist/utils/useRadioGroup.js +260 -0
  133. package/dist/utils/useSelectAccessibility.js +426 -0
  134. package/dist/utils/useTabsAccessibility.js +278 -0
  135. package/dist/utils/useTextAreaAccessibility.js +255 -0
  136. package/dist/utils/useTextInputAccessibility.js +295 -0
  137. package/dist/utils/useTypographyAccessibility.js +168 -0
  138. package/dist/utils/useWindowSize.js +32 -0
  139. package/dist/utils/utils/ScrollHandler.js +26 -0
  140. package/dist/utils/utils/accessibility.js +133 -0
  141. package/dist/utils/utils/heroUtils.js +348 -0
  142. package/dist/utils/utils/index.js +9 -0
  143. package/dist/utils/utils/inputValidation.js +22 -0
  144. package/dist/utils/utils/keyboardNavigation.js +664 -0
  145. package/dist/utils/utils/labelUtils.js +772 -0
  146. package/dist/utils/utils/loaderUtils.js +436 -0
  147. package/dist/utils/utils/menuUtils.js +651 -0
  148. package/dist/utils/utils/useHeadingAccessibility.js +334 -0
  149. package/dist/utils/utils/useRadioGroup.js +311 -0
  150. package/dist/utils/utils/useSelectAccessibility.js +498 -0
  151. package/dist/utils/utils/useTabsAccessibility.js +316 -0
  152. package/dist/utils/utils/useTextAreaAccessibility.js +303 -0
  153. package/dist/utils/utils/useTextInputAccessibility.js +338 -0
  154. package/dist/utils/utils/useTypographyAccessibility.js +180 -0
  155. package/dist/utils/utils/useWindowSize.js +26 -0
  156. package/dist/utils/utils/validation.js +131 -0
  157. package/dist/utils/validation.js +139 -0
  158. package/package.json +90 -0
@@ -0,0 +1,363 @@
1
+ @use 'variables' as *;
2
+
3
+ /* ==========================================================================
4
+ Select Component Styles
5
+ ========================================================================== */
6
+
7
+ /* ==========================================================================
8
+ SVG Arrow Variables and Mixins
9
+ ========================================================================== */
10
+
11
+ // Arrow color variations as URL-encoded hex values
12
+ // These ensure consistency across all select states and make color changes easy
13
+ $select-arrow-normal: '%236c757d'; // Gray (#6c757d) for normal state
14
+ $select-arrow-disabled: '%23adb5bd'; // Light gray (#adb5bd) for disabled state
15
+ $select-arrow-error: '%23dc3545'; // Red (#dc3545) for error state
16
+ $select-arrow-dark: '%23adb5bd'; // Light gray (#adb5bd) for dark mode
17
+
18
+ /**
19
+ * Mixin to generate dropdown arrow background with specified color
20
+ *
21
+ * Benefits:
22
+ * - Eliminates SVG duplication (4+ instances reduced to 1 mixin)
23
+ * - Centralizes arrow styling for easy maintenance
24
+ * - Makes color changes simple (just update variables)
25
+ * - Ensures consistent arrow styling across all states
26
+ *
27
+ * @param {string} $color - URL-encoded hex color (e.g., '%236c757d')
28
+ */
29
+ @mixin select-dropdown-arrow($color: $select-arrow-normal) {
30
+ background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='#{$color}' viewBox='0 0 16 16'%3E%3Cpath d='M8 11L3 6h10l-5 5z'/%3E%3C/svg%3E");
31
+ background-position: right $spacing-sm center;
32
+ background-repeat: no-repeat;
33
+ background-size: 16px 16px;
34
+ }
35
+
36
+ /**
37
+ * Select Wrapper
38
+ * Container for the entire select component including label, input, and error
39
+ */
40
+ .select-wrapper {
41
+ display: flex;
42
+ flex-direction: column;
43
+ position: relative;
44
+ width: 100%;
45
+ font-family: $font-family-body;
46
+
47
+ /* Spacing between elements */
48
+ gap: $spacing-xs;
49
+
50
+ /* No label variant */
51
+ &--no-label {
52
+ .select__label {
53
+ position: absolute;
54
+ width: 1px;
55
+ height: 1px;
56
+ padding: 0;
57
+ margin: -1px;
58
+ overflow: hidden;
59
+ clip: rect(0, 0, 0, 0);
60
+ white-space: nowrap;
61
+ border: 0;
62
+ }
63
+ }
64
+
65
+ /* High contrast mode support */
66
+ @media (prefers-contrast: high) {
67
+ .select {
68
+ border-width: 2px;
69
+ font-weight: $font-weight-medium;
70
+ }
71
+
72
+ .select:focus {
73
+ outline: 3px solid currentColor;
74
+ outline-offset: 2px;
75
+ }
76
+ }
77
+
78
+ /* Reduced motion support */
79
+ @media (prefers-reduced-motion: reduce) {
80
+ .select {
81
+ transition: none;
82
+ }
83
+ }
84
+ }
85
+
86
+ /**
87
+ * Select Label
88
+ * Associated label for the select input
89
+ */
90
+ .select__label {
91
+ color: $color-font-body;
92
+ font-size: $font-size-sm;
93
+ font-weight: $font-weight-medium;
94
+ line-height: $line-height-tight;
95
+ margin-bottom: $spacing-xs;
96
+
97
+ /* Required indicator */
98
+ &--required::after {
99
+ content: ' *';
100
+ color: $color-error;
101
+ font-weight: $font-weight-bold;
102
+ }
103
+
104
+ /* Responsive typography */
105
+ @media #{$medium} {
106
+ font-size: $font-size-base;
107
+ }
108
+ }
109
+
110
+ /**
111
+ * Help Text
112
+ * Descriptive text below the label
113
+ */
114
+ .select__help-text {
115
+ color: $color-font-body;
116
+ font-size: $font-size-xs;
117
+ line-height: $line-height-base;
118
+ margin-bottom: $spacing-xs;
119
+
120
+ @media #{$medium} {
121
+ font-size: $font-size-sm;
122
+ }
123
+ }
124
+
125
+ /**
126
+ * Main Select Element
127
+ * The native select input with custom styling
128
+ */
129
+ .select {
130
+ /* Reset and base styles */
131
+ appearance: none;
132
+ background-color: $color-white;
133
+ border: 1px solid $gray-300;
134
+ border-radius: $border-radius-sm;
135
+ color: $gray-600;
136
+ cursor: pointer;
137
+ font-family: $font-family-body;
138
+ font-size: $font-size-base;
139
+ font-weight: $font-weight-normal;
140
+ line-height: $line-height-base;
141
+ min-height: 2.5rem;
142
+ padding: $spacing-sm;
143
+ position: relative;
144
+ transition: border-color 0.2s ease, box-shadow 0.2s ease,
145
+ background-color 0.2s ease;
146
+ width: 100%;
147
+
148
+ /* Custom dropdown arrow */
149
+ @include select-dropdown-arrow($select-arrow-normal);
150
+ padding-right: calc(1rem + #{$spacing-lg});
151
+
152
+ /* States */
153
+ &:hover:not(:disabled) {
154
+ border-color: $gray-500;
155
+ background-color: $color-white;
156
+ }
157
+
158
+ &:focus {
159
+ border-color: $color-muted-blue;
160
+ box-shadow: 0 0 0 2px rgba(77, 144, 254, 0.2);
161
+ outline: none;
162
+ background-color: $color-white;
163
+ }
164
+
165
+ &:active:not(:disabled) {
166
+ border-color: $gray-600;
167
+ }
168
+
169
+ /* Disabled state */
170
+ &:disabled,
171
+ &--disabled {
172
+ background-color: $color-white;
173
+ border-color: $gray-300;
174
+ color: $gray-600;
175
+ cursor: not-allowed;
176
+ opacity: 0.6;
177
+
178
+ /* Custom arrow for disabled state */
179
+ @include select-dropdown-arrow($select-arrow-disabled);
180
+
181
+ &:hover {
182
+ border-color: $gray-300;
183
+ background-color: $color-white;
184
+ }
185
+ }
186
+
187
+ /* Error state */
188
+ &--error {
189
+ border-color: $color-error;
190
+ box-shadow: inset 0 1px 2px rgba($color-error, 0.1);
191
+
192
+ &:focus {
193
+ border-color: $color-error;
194
+ box-shadow: 0 0 0 2px rgba(244, 67, 54, 0.2);
195
+ }
196
+
197
+ /* Error arrow */
198
+ @include select-dropdown-arrow($select-arrow-error);
199
+ }
200
+
201
+ /* Multiple select styling */
202
+ &[multiple] {
203
+ background-image: none;
204
+ padding-right: $spacing-sm;
205
+ min-height: calc(2.5rem * 3);
206
+ resize: vertical;
207
+
208
+ option {
209
+ padding: $spacing-xs $spacing-sm;
210
+
211
+ &:checked {
212
+ background-color: $color-primary;
213
+ color: $color-white;
214
+ }
215
+ }
216
+ }
217
+
218
+ /* Size variants */
219
+ &[size] {
220
+ background-image: none;
221
+ padding-right: $spacing-sm;
222
+ min-height: auto;
223
+ height: auto;
224
+ }
225
+
226
+ /* Option styling */
227
+ option {
228
+ color: $gray-600;
229
+ background-color: $color-white;
230
+ padding: $spacing-xs $spacing-sm;
231
+
232
+ &:disabled {
233
+ color: $gray-600;
234
+ opacity: 0.6;
235
+ }
236
+
237
+ &:checked {
238
+ background-color: $color-primary;
239
+ color: $color-white;
240
+ }
241
+ }
242
+
243
+ /* Responsive sizing */
244
+ @media #{$medium} {
245
+ font-size: $font-size-base;
246
+ min-height: 2.75rem;
247
+ padding: $spacing-sm $spacing-md;
248
+
249
+ &[multiple] {
250
+ min-height: calc(2.75rem * 3);
251
+ }
252
+ }
253
+
254
+ @media #{$large} {
255
+ font-size: $font-size-lg;
256
+ min-height: 3rem;
257
+ padding: $spacing-md $spacing-lg;
258
+
259
+ &[multiple] {
260
+ min-height: calc(3rem * 3);
261
+ }
262
+ }
263
+
264
+ @media #{$xlarge} {
265
+ font-size: $font-size-lg;
266
+ padding: $spacing-md $spacing-lg;
267
+ }
268
+ }
269
+
270
+ /**
271
+ * Error Message
272
+ * Error text displayed below the select
273
+ */
274
+ .select__error {
275
+ color: $color-error;
276
+ font-size: $font-size-xs;
277
+ font-weight: $font-weight-medium;
278
+ line-height: $line-height-tight;
279
+ margin-top: $spacing-xs;
280
+ display: flex;
281
+ align-items: flex-start;
282
+ gap: $spacing-xs;
283
+
284
+ /* Error icon */
285
+ &::before {
286
+ content: '⚠';
287
+ flex-shrink: 0;
288
+ font-size: $font-size-sm;
289
+ line-height: 1;
290
+ }
291
+
292
+ @media #{$medium} {
293
+ font-size: $font-size-sm;
294
+ }
295
+ }
296
+
297
+ /* ==========================================================================
298
+ High Contrast Mode Support
299
+ ========================================================================== */
300
+ @media (prefers-contrast: high) {
301
+ .select-wrapper {
302
+ .select {
303
+ border-width: 2px;
304
+ font-weight: $font-weight-medium;
305
+
306
+ &:focus {
307
+ outline: 3px solid currentColor;
308
+ outline-offset: 2px;
309
+ box-shadow: none;
310
+ }
311
+
312
+ &--error {
313
+ border-color: $color-error;
314
+ border-width: 3px;
315
+ }
316
+ }
317
+
318
+ .select__error {
319
+ font-weight: $font-weight-bold;
320
+ text-decoration: underline;
321
+ }
322
+ }
323
+ }
324
+
325
+ /* ==========================================================================
326
+ Dark Mode Support
327
+ ========================================================================== */
328
+ // @media (prefers-color-scheme: dark) {
329
+ // .select-wrapper {
330
+ // .select {
331
+ // background-color: $gray-800;
332
+ // border-color: $gray-600;
333
+ // color: $gray-100;
334
+
335
+ // /* Dark mode dropdown arrow */
336
+ // @include select-dropdown-arrow($select-arrow-dark);
337
+
338
+ // &:hover:not(:disabled) {
339
+ // border-color: $gray-500;
340
+ // background-color: $gray-700;
341
+ // }
342
+
343
+ // &:focus {
344
+ // border-color: $focus-color;
345
+ // box-shadow: 0 0 0 2px rgba(77, 144, 254, 0.2);
346
+ // background-color: $gray-800;
347
+ // }
348
+
349
+ // option {
350
+ // background-color: $gray-800;
351
+ // color: $gray-100;
352
+ // }
353
+ // }
354
+
355
+ // .select__label {
356
+ // color: $gray-100;
357
+ // }
358
+
359
+ // .select__help-text {
360
+ // color: $gray-400;
361
+ // }
362
+ // }
363
+ // }
@@ -0,0 +1,334 @@
1
+ @use 'variables' as *;
2
+
3
+ .side-menu {
4
+ .overlay,
5
+ .menu {
6
+ height: 100%;
7
+ right: 0;
8
+ top: 0;
9
+ visibility: hidden;
10
+ z-index: $z-index-overlay;
11
+ position: fixed;
12
+ }
13
+
14
+ .overlay {
15
+ background-color: rgba(0, 0, 0, 0.5);
16
+ opacity: 0;
17
+ transition: visibility 0s linear 0.3s, opacity 0.3s ease-in-out;
18
+ width: 100%;
19
+ left: 0;
20
+
21
+ /* Enhanced backdrop for better accessibility */
22
+ backdrop-filter: blur(2px);
23
+
24
+ &.active {
25
+ opacity: 1;
26
+ visibility: visible;
27
+ transition: opacity 0.3s ease-in-out;
28
+ }
29
+ }
30
+
31
+ /* Direction-specific transforms */
32
+ .menu-x {
33
+ transform: translateX(100%);
34
+ transition: transform 0.3s ease-in-out;
35
+
36
+ &.active {
37
+ transform: translateX(0);
38
+ }
39
+
40
+ /* Horizontal position variants */
41
+ &.menu-x-left {
42
+ transform: translateX(-100%);
43
+ right: auto;
44
+ left: 0;
45
+
46
+ &.active {
47
+ transform: translateX(0);
48
+ }
49
+ }
50
+ }
51
+
52
+ .menu-y {
53
+ transform: translateY(-100%);
54
+ transition: transform 0.3s ease-in-out;
55
+ right: auto;
56
+ left: 0;
57
+ top: 0;
58
+ width: 100%;
59
+
60
+ &.active {
61
+ transform: translateY(0);
62
+ }
63
+
64
+ /* Vertical position variants */
65
+ &.menu-y-bottom {
66
+ transform: translateY(100%);
67
+ top: auto;
68
+ bottom: 0;
69
+
70
+ &.active {
71
+ transform: translateY(0);
72
+ }
73
+ }
74
+ }
75
+
76
+ .menu {
77
+ align-items: flex-end;
78
+ flex-direction: column;
79
+ width: 100%;
80
+ transition: transform 0.3s ease-in-out, visibility 0.3s ease-in-out;
81
+ overflow-x: hidden;
82
+ background: #ffffff;
83
+ box-shadow: -2px 0 10px rgba(0, 0, 0, 0.1);
84
+
85
+ /* Mobile-first responsive design */
86
+ @media #{$medium} {
87
+ width: 60%;
88
+ max-width: 400px;
89
+ }
90
+
91
+ @media #{$large} {
92
+ width: 50%;
93
+ max-width: 450px;
94
+ }
95
+
96
+ @media #{$xlarge} {
97
+ width: 40%;
98
+ max-width: 500px;
99
+ }
100
+
101
+ .wrapper-header,
102
+ .wrapper-content {
103
+ width: 100%;
104
+ padding: 16px;
105
+
106
+ @media #{$medium} {
107
+ padding: 20px;
108
+ }
109
+
110
+ @media #{$large} {
111
+ padding: 24px;
112
+ }
113
+
114
+ @media #{$xlarge} {
115
+ padding: 28px;
116
+ }
117
+ }
118
+
119
+ .wrapper-header {
120
+ display: flex;
121
+ align-items: center;
122
+ justify-content: space-between;
123
+ border-bottom: 1px solid #e0e0e0;
124
+ background: #f8f9fa;
125
+ min-height: 60px;
126
+
127
+ .menu-title {
128
+ margin: 0;
129
+ font-size: 1.1rem;
130
+ font-weight: 600;
131
+ color: #333;
132
+ flex: 1;
133
+ text-align: center;
134
+
135
+ @media #{$medium} {
136
+ font-size: 1.2rem;
137
+ }
138
+
139
+ @media #{$large} {
140
+ font-size: 1.3rem;
141
+ }
142
+ }
143
+
144
+ .icon-left,
145
+ .icon-right {
146
+ background: none;
147
+ border: none;
148
+ cursor: pointer;
149
+ padding: 8px;
150
+ border-radius: 4px;
151
+ display: flex;
152
+ align-items: center;
153
+ justify-content: center;
154
+ color: #666;
155
+ transition: background-color 0.2s, color 0.2s;
156
+ min-width: 40px;
157
+ min-height: 40px;
158
+
159
+ &:hover {
160
+ background-color: rgba(0, 0, 0, 0.1);
161
+ color: #333;
162
+ }
163
+
164
+ &:focus {
165
+ outline: 2px solid #007bff;
166
+ outline-offset: 2px;
167
+ }
168
+
169
+ /* Enhanced touch targets for mobile */
170
+ @media #{$medium} {
171
+ min-width: 44px;
172
+ min-height: 44px;
173
+ }
174
+ }
175
+
176
+ .icon-right {
177
+ margin-left: auto;
178
+ }
179
+ }
180
+
181
+ .wrapper-content {
182
+ height: calc(100% - 60px);
183
+ overflow-y: auto;
184
+ flex: 1;
185
+
186
+ /* Custom scrollbar for better UX */
187
+ &::-webkit-scrollbar {
188
+ width: 6px;
189
+ }
190
+
191
+ &::-webkit-scrollbar-track {
192
+ background: #f1f1f1;
193
+ }
194
+
195
+ &::-webkit-scrollbar-thumb {
196
+ background: #c1c1c1;
197
+ border-radius: 3px;
198
+
199
+ &:hover {
200
+ background: #a1a1a1;
201
+ }
202
+ }
203
+ }
204
+ }
205
+
206
+ /* Trigger styling */
207
+ .trigger {
208
+ cursor: pointer;
209
+ display: inline-block;
210
+
211
+ &:focus-within {
212
+ outline: 2px solid #007bff;
213
+ outline-offset: 2px;
214
+ border-radius: 4px;
215
+ }
216
+ }
217
+
218
+ /* Active state handling */
219
+ .active {
220
+ visibility: visible;
221
+ display: flex;
222
+ }
223
+
224
+ /* Accessibility and reduced motion support */
225
+ @media (prefers-reduced-motion: reduce) {
226
+ .overlay,
227
+ .menu-x,
228
+ .menu-y {
229
+ transition: none;
230
+ }
231
+
232
+ .overlay {
233
+ backdrop-filter: none;
234
+ }
235
+ }
236
+
237
+ /* High contrast mode support */
238
+ @media (prefers-contrast: high) {
239
+ .menu {
240
+ border: 2px solid #000;
241
+ box-shadow: none;
242
+ }
243
+
244
+ .wrapper-header {
245
+ border-bottom: 2px solid #000;
246
+ background: #fff;
247
+
248
+ .menu-title {
249
+ font-weight: bold;
250
+ color: #000;
251
+ }
252
+
253
+ .icon-left,
254
+ .icon-right {
255
+ border: 2px solid #000;
256
+ color: #000;
257
+
258
+ &:hover,
259
+ &:focus {
260
+ background: #000;
261
+ color: #fff;
262
+ }
263
+ }
264
+ }
265
+
266
+ .overlay {
267
+ background-color: rgba(0, 0, 0, 0.8);
268
+ }
269
+ }
270
+
271
+ /* Dark mode considerations */
272
+ @media (prefers-color-scheme: dark) {
273
+ .menu {
274
+ background: #1a1a1a;
275
+ color: #ffffff;
276
+ box-shadow: -2px 0 10px rgba(0, 0, 0, 0.5);
277
+ }
278
+
279
+ .wrapper-header {
280
+ background: #2a2a2a;
281
+ border-bottom-color: #404040;
282
+
283
+ .menu-title {
284
+ color: #ffffff;
285
+ }
286
+
287
+ .icon-left,
288
+ .icon-right {
289
+ color: #cccccc;
290
+
291
+ &:hover {
292
+ background-color: rgba(255, 255, 255, 0.1);
293
+ color: #ffffff;
294
+ }
295
+ }
296
+ }
297
+
298
+ .wrapper-content {
299
+ &::-webkit-scrollbar-track {
300
+ background: #2a2a2a;
301
+ }
302
+
303
+ &::-webkit-scrollbar-thumb {
304
+ background: #666;
305
+
306
+ &:hover {
307
+ background: #888;
308
+ }
309
+ }
310
+ }
311
+ }
312
+
313
+ /* Focus trap styling */
314
+ &:focus-within {
315
+ .menu {
316
+ &:focus {
317
+ outline: none;
318
+ }
319
+ }
320
+ }
321
+ }
322
+
323
+ /* Screen reader only utility class */
324
+ .sr-only {
325
+ position: absolute !important;
326
+ width: 1px !important;
327
+ height: 1px !important;
328
+ padding: 0 !important;
329
+ margin: -1px !important;
330
+ overflow: hidden !important;
331
+ clip: rect(0, 0, 0, 0) !important;
332
+ white-space: nowrap !important;
333
+ border: 0 !important;
334
+ }