@duskmoon-dev/core 1.19.3 → 1.19.5

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 (121) hide show
  1. package/README.md +1 -1
  2. package/dist/base.css +484 -0
  3. package/dist/components/accordion.css +33 -9
  4. package/dist/components/alert.css +81 -3
  5. package/dist/components/autocomplete.css +67 -13
  6. package/dist/components/avatar.css +39 -15
  7. package/dist/components/badge.css +15 -3
  8. package/dist/components/card.css +12 -0
  9. package/dist/components/carousel.css +28 -0
  10. package/dist/components/chat.css +15 -11
  11. package/dist/components/checkbox.css +83 -13
  12. package/dist/components/collapse.css +67 -16
  13. package/dist/components/countdown.css +22 -0
  14. package/dist/components/datepicker.css +76 -0
  15. package/dist/components/diff.css +33 -0
  16. package/dist/components/file-input.css +107 -0
  17. package/dist/components/filter-group.css +452 -0
  18. package/dist/components/form-group.css +95 -204
  19. package/dist/components/form.css +1551 -421
  20. package/dist/components/index.css +6248 -5799
  21. package/dist/components/input.css +114 -31
  22. package/dist/components/kbd.css +28 -0
  23. package/dist/components/list.css +22 -3
  24. package/dist/components/loading.css +105 -0
  25. package/dist/components/modal.css +62 -1
  26. package/dist/components/otp-input.css +124 -6
  27. package/dist/components/progress.css +74 -0
  28. package/dist/components/radial-progress.css +47 -0
  29. package/dist/components/radio.css +70 -0
  30. package/dist/components/range.css +41 -0
  31. package/dist/components/rating.css +39 -12
  32. package/dist/components/select.css +74 -0
  33. package/dist/components/skeleton.css +13 -1
  34. package/dist/components/snackbar.css +48 -18
  35. package/dist/components/stat.css +40 -0
  36. package/dist/components/switch.css +70 -0
  37. package/dist/components/table.css +8 -0
  38. package/dist/components/textarea.css +74 -0
  39. package/dist/components/timeline.css +40 -25
  40. package/dist/components/toast.css +188 -34
  41. package/dist/components/toggle-switch.css +165 -0
  42. package/dist/components/toggle.css +198 -32
  43. package/dist/components/tooltip.css +2 -47
  44. package/dist/components/validator.css +66 -0
  45. package/dist/effects/aura.css +22 -0
  46. package/dist/effects/hover-3d.css +8 -0
  47. package/dist/effects/hover-gallery.css +14 -0
  48. package/dist/effects/index.css +65 -0
  49. package/dist/effects/text-rotate.css +16 -0
  50. package/dist/esm/components/accordion.js +33 -9
  51. package/dist/esm/components/alert.js +81 -3
  52. package/dist/esm/components/autocomplete.js +67 -13
  53. package/dist/esm/components/avatar.js +39 -15
  54. package/dist/esm/components/badge.js +15 -3
  55. package/dist/esm/components/card.js +12 -0
  56. package/dist/esm/components/carousel.d.ts +5 -0
  57. package/dist/esm/components/carousel.js +37 -0
  58. package/dist/esm/components/chat.js +15 -11
  59. package/dist/esm/components/checkbox.js +84 -14
  60. package/dist/esm/components/collapse.js +67 -16
  61. package/dist/esm/components/countdown.d.ts +5 -0
  62. package/dist/esm/components/countdown.js +31 -0
  63. package/dist/esm/components/datepicker.js +77 -1
  64. package/dist/esm/components/diff.d.ts +5 -0
  65. package/dist/esm/components/diff.js +42 -0
  66. package/dist/esm/components/file-input.d.ts +5 -0
  67. package/dist/esm/components/file-input.js +116 -0
  68. package/dist/esm/components/filter-group.d.ts +5 -0
  69. package/dist/esm/components/filter-group.js +461 -0
  70. package/dist/esm/components/form-group.js +96 -205
  71. package/dist/esm/components/form.js +1539 -409
  72. package/dist/esm/components/input.js +115 -32
  73. package/dist/esm/components/kbd.d.ts +5 -0
  74. package/dist/esm/components/kbd.js +37 -0
  75. package/dist/esm/components/list.js +22 -3
  76. package/dist/esm/components/loading.d.ts +5 -0
  77. package/dist/esm/components/loading.js +114 -0
  78. package/dist/esm/components/modal.js +62 -1
  79. package/dist/esm/components/otp-input.js +125 -7
  80. package/dist/esm/components/progress.js +74 -0
  81. package/dist/esm/components/radial-progress.d.ts +5 -0
  82. package/dist/esm/components/radial-progress.js +56 -0
  83. package/dist/esm/components/radio.js +71 -1
  84. package/dist/esm/components/range.d.ts +5 -0
  85. package/dist/esm/components/range.js +50 -0
  86. package/dist/esm/components/rating.js +39 -12
  87. package/dist/esm/components/select.js +75 -1
  88. package/dist/esm/components/skeleton.js +13 -1
  89. package/dist/esm/components/snackbar.js +48 -18
  90. package/dist/esm/components/stat.d.ts +5 -0
  91. package/dist/esm/components/stat.js +49 -0
  92. package/dist/esm/components/switch.js +71 -1
  93. package/dist/esm/components/table.js +8 -0
  94. package/dist/esm/components/textarea.js +75 -1
  95. package/dist/esm/components/timeline.js +40 -25
  96. package/dist/esm/components/toast.js +188 -34
  97. package/dist/esm/components/toggle-switch.d.ts +5 -0
  98. package/dist/esm/components/toggle-switch.js +174 -0
  99. package/dist/esm/components/toggle.js +198 -32
  100. package/dist/esm/components/tooltip.js +2 -47
  101. package/dist/esm/components/validator.d.ts +5 -0
  102. package/dist/esm/components/validator.js +75 -0
  103. package/dist/esm/effects/aura.d.ts +5 -0
  104. package/dist/esm/effects/aura.js +31 -0
  105. package/dist/esm/effects/hover-3d.d.ts +5 -0
  106. package/dist/esm/effects/hover-3d.js +17 -0
  107. package/dist/esm/effects/hover-gallery.d.ts +5 -0
  108. package/dist/esm/effects/hover-gallery.js +23 -0
  109. package/dist/esm/effects/index.d.ts +5 -0
  110. package/dist/esm/effects/index.js +74 -0
  111. package/dist/esm/effects/text-rotate.d.ts +5 -0
  112. package/dist/esm/effects/text-rotate.js +25 -0
  113. package/dist/index.css +7652 -7204
  114. package/dist/index.min.css +1 -1
  115. package/dist/standalone/duskmoonui-themes.css +1 -1
  116. package/dist/standalone/duskmoonui.css +7598 -7141
  117. package/dist/standalone/duskmoonui.js +18 -16
  118. package/dist/standalone/duskmoonui.mjs +18 -16
  119. package/dist/types/types/plugin.d.ts +1 -1
  120. package/dist/types/types/plugin.d.ts.map +1 -1
  121. package/package.json +110 -1
@@ -3,6 +3,172 @@
3
3
  * DuskMoonUI - Material Design 3 inspired toggle button
4
4
  * Used for selecting between multiple options or toggling states
5
5
  */
6
+ /** Opt-in native presentation. Applications own errors, messages and announcements. */
7
+ @layer components {
8
+ /* Compatibility wrapper states are intentionally immediate. */
9
+ :is(.form-group-info, .form-control.info) { --dm-validation-color: var(--color-info); }
10
+ :is(.form-group-warning, .form-control.warning) { --dm-validation-color: var(--color-warning); }
11
+ :is(.form-group-success, .form-control.success) { --dm-validation-color: var(--color-success); }
12
+
13
+ :is(.input, .select, .textarea, .checkbox, .radio, .switch, .toggle, .file-input, .otp-input-field, .otp-code, .datepicker-input):is(.validate :valid, .validate:valid, .validator.validator-success:user-valid):not([aria-invalid="true"]):not(:where(.form-group-error *, .form-control.error *)):not(:disabled) {
14
+ --dm-validation-color: var(--color-success);
15
+ --dm-validation-content: var(--color-success-content);
16
+ }
17
+
18
+ :is(.input, .select, .textarea, .checkbox, .radio, .switch, .toggle, .file-input, .otp-input-field, .otp-code, .datepicker-input):is(.validate :invalid, .validate:invalid, .validator:user-invalid):not(:disabled) {
19
+ --dm-validation-color: var(--color-error);
20
+ --dm-validation-content: var(--color-error-content);
21
+ }
22
+
23
+ :is(.form-group-error, .form-control.error) {
24
+ --dm-validation-color: var(--color-error);
25
+ --dm-validation-content: var(--color-error-content);
26
+ }
27
+
28
+ /* Higher specificity than native states, including server-invalid + user-valid. */
29
+ :is(.input, .select, .textarea, .checkbox, .radio, .switch, .toggle, .file-input, .otp-input-field, .otp-code, .datepicker-input):is([aria-invalid="true"], .form-group-error *, .form-control.error *):not(:disabled):is(:focus, :not(:focus)) {
30
+ --dm-validation-color: var(--color-error);
31
+ --dm-validation-content: var(--color-error-content);
32
+ }
33
+
34
+ /* This shared selector wins over filled/ghost focus and semantic modifiers. */
35
+ :is(.input, .select, .textarea, .checkbox, .radio, .switch, .toggle, .file-input, .otp-input-field, .otp-code, .datepicker-input):is([aria-invalid="true"], .validator:user-invalid, .validator.validator-success:user-valid, .validate :invalid, .validate :valid, .validate:invalid, .validate:valid, .form-group-error *, .form-group-success *, .form-group-warning *, .form-group-info *, .form-control.error *, .form-control.success *, .form-control.warning *, .form-control.info *):not(:disabled) {
36
+ color: var(--dm-validation-color);
37
+ border-color: var(--dm-validation-color);
38
+ --checkbox-color: var(--dm-validation-color);
39
+ --radio-color: var(--dm-validation-color);
40
+ --switch-color: var(--dm-validation-color);
41
+ --switch-content-color: var(--dm-validation-content, var(--color-primary-content));
42
+ --toggle-color: var(--dm-validation-color);
43
+ }
44
+
45
+ :is(.input, .select, .textarea, .checkbox, .radio, .switch, .toggle, .file-input, .otp-input-field, .otp-code, .datepicker-input):is([aria-invalid="true"], .validator:user-invalid, .validator.validator-success:user-valid, .validate :invalid, .validate :valid, .validate:invalid, .validate:valid, .form-group-error *, .form-group-success *, .form-group-warning *, .form-group-info *, .form-control.error *, .form-control.success *, .form-control.warning *, .form-control.info *):not(:disabled):focus {
46
+ border-color: var(--dm-validation-color);
47
+ box-shadow: 0 0 0 3px color-mix(in oklch, var(--dm-validation-color) 20%, transparent);
48
+ }
49
+
50
+ :is(.form-group-error, .form-control.error) :is(.form-label, .label-text) { color: var(--color-error); }
51
+ :is(.form-group-success, .form-control.success) :is(.form-label, .label-text) { color: var(--color-success); }
52
+ :is(.form-group-warning, .form-control.warning) :is(.form-label, .label-text) { color: var(--color-warning); }
53
+ :is(.form-group-info, .form-control.info) :is(.form-label, .label-text) { color: var(--color-info); }
54
+
55
+ .validator-hint { font-size: 0.75rem; color: var(--color-on-surface-variant); }
56
+ .validator-error { display: none; font-size: 0.75rem; color: var(--color-error); }
57
+ /* Keep OTP actions stationary when blur reveals an error during a click. */
58
+ .otp-code ~ .validator-error { display: block; visibility: hidden; }
59
+ :is(.form-group, .form-control):has(:is([aria-invalid="true"], .validator:user-invalid, .validate :invalid, .validate:invalid):not(:disabled)) > .validator-error,
60
+ :is(.form-group-error, .form-control.error) > .validator-error { display: block; visibility: visible; }
61
+
62
+ @media (forced-colors: active) {
63
+ :is(input, select, textarea):is(.input, .select, .textarea, .checkbox, .radio, .switch, .toggle, .file-input, .otp-input-field, .otp-code, .datepicker-input):focus-visible {
64
+ outline: 2px solid Highlight;
65
+ outline-offset: 2px;
66
+ }
67
+ input:is(.checkbox, .radio, .switch, .toggle) { appearance: auto; }
68
+ input:is(.checkbox, .radio, .switch, .toggle)::before,
69
+ input:is(.checkbox, .radio, .switch, .toggle)::after { content: none; }
70
+ }
71
+ }
72
+
73
+
74
+ @layer components {
75
+ /* Toggle/Switch */
76
+ .toggle {
77
+ --toggle-color: var(--color-primary);
78
+ box-sizing: border-box;
79
+ color: var(--color-on-surface);
80
+ border: 1px solid currentColor;
81
+ appearance: none;
82
+ width: 3rem;
83
+ height: 1.5rem;
84
+ border-radius: var(--radius-lg);
85
+ background-color: var(--color-surface-container-highest);
86
+ cursor: pointer;
87
+ transition: all 150ms ease-in-out;
88
+ position: relative;
89
+ }
90
+
91
+ .toggle::after {
92
+ content: '';
93
+ position: absolute;
94
+ inset-inline-start: 0.25rem;
95
+ top: 50%;
96
+ transform: translateY(-50%);
97
+ width: 1rem;
98
+ height: 1rem;
99
+ background-color: var(--color-on-surface-variant);
100
+ border-radius: var(--radius-full);
101
+ transition: all 150ms ease-in-out;
102
+ }
103
+
104
+ .toggle:checked {
105
+ background-color: var(--toggle-color);
106
+ }
107
+
108
+ .toggle:checked::after {
109
+ inset-inline-start: calc(100% - 1.25rem);
110
+ background-color: var(--dm-validation-content, var(--color-primary-content));
111
+ }
112
+
113
+ .toggle:focus-visible {
114
+ outline: none;
115
+ box-shadow: 0 0 0 3px color-mix(in oklch, currentColor 20%, transparent);
116
+ }
117
+
118
+ .toggle:disabled {
119
+ cursor: not-allowed;
120
+ opacity: 0.5;
121
+ }
122
+
123
+ /* Toggle Sizes */
124
+ .toggle-xs {
125
+ width: 2rem;
126
+ height: 1rem;
127
+ }
128
+
129
+ .toggle-xs::after {
130
+ width: 0.625rem;
131
+ height: 0.625rem;
132
+ inset-inline-start: 0.1875rem;
133
+ }
134
+
135
+ .toggle-xs:checked::after {
136
+ inset-inline-start: calc(100% - 0.8125rem);
137
+ }
138
+
139
+ .toggle-sm {
140
+ width: 2.5rem;
141
+ height: 1.25rem;
142
+ }
143
+
144
+ .toggle-sm::after {
145
+ width: 0.75rem;
146
+ height: 0.75rem;
147
+ }
148
+
149
+ .toggle-lg {
150
+ width: 4rem;
151
+ height: 2rem;
152
+ }
153
+
154
+ .toggle-lg::after {
155
+ width: 1.5rem;
156
+ height: 1.5rem;
157
+ }
158
+
159
+ .toggle-sm:checked::after { inset-inline-start: calc(100% - 1rem); }
160
+ .toggle-lg:checked::after { inset-inline-start: calc(100% - 1.75rem); }
161
+ .toggle-primary { --toggle-color: var(--color-primary); }
162
+ .toggle-secondary { --toggle-color: var(--color-secondary); }
163
+ .toggle-tertiary { --toggle-color: var(--color-tertiary); }
164
+ .toggle-info { --toggle-color: var(--color-info); }
165
+ .toggle-success { --toggle-color: var(--color-success); }
166
+ .toggle-warning { --toggle-color: var(--color-warning); }
167
+ .toggle-error { --toggle-color: var(--color-error); }
168
+ .toggle-ghost { border-color: transparent; }
169
+ @media (prefers-reduced-motion: reduce) { .toggle, .toggle::after { transition: none; } }
170
+ }
171
+
6
172
 
7
173
  @layer components {
8
174
  /* Base toggle button */
@@ -30,93 +196,93 @@
30
196
  }
31
197
 
32
198
  /* Active/Selected state */
33
- .toggle-btn-active,
34
- .toggle-btn.active {
199
+ :is(.toggle-btn-active:not([aria-pressed]), .toggle-btn[aria-pressed="true"]),
200
+ .toggle-btn.active:not([aria-pressed]) {
35
201
  color: var(--color-on-primary-container);
36
202
  background-color: var(--color-primary-container);
37
203
  border-color: var(--color-primary);
38
204
  }
39
205
 
40
- .toggle-btn-active:hover:not(:disabled),
41
- .toggle-btn.active:hover:not(:disabled) {
206
+ :is(.toggle-btn-active:not([aria-pressed]), .toggle-btn[aria-pressed="true"]):hover:not(:disabled),
207
+ .toggle-btn.active:not([aria-pressed]):hover:not(:disabled) {
42
208
  background-color: color-mix(in oklch, var(--color-primary-container) 90%, transparent);
43
209
  }
44
210
 
45
211
  /* Primary variant */
46
- .toggle-btn-primary.toggle-btn-active,
47
- .toggle-btn-primary.active {
212
+ .toggle-btn-primary:is(.toggle-btn-active:not([aria-pressed]), .toggle-btn[aria-pressed="true"]),
213
+ .toggle-btn-primary.active:not([aria-pressed]) {
48
214
  color: var(--color-on-primary-container);
49
215
  background-color: var(--color-primary-container);
50
216
  border-color: var(--color-primary);
51
217
  }
52
218
 
53
219
  /* Secondary variant */
54
- .toggle-btn-secondary.toggle-btn-active,
55
- .toggle-btn-secondary.active {
220
+ .toggle-btn-secondary:is(.toggle-btn-active:not([aria-pressed]), .toggle-btn[aria-pressed="true"]),
221
+ .toggle-btn-secondary.active:not([aria-pressed]) {
56
222
  color: var(--color-on-secondary-container);
57
223
  background-color: var(--color-secondary-container);
58
224
  border-color: var(--color-secondary);
59
225
  }
60
226
 
61
227
  /* Tertiary variant */
62
- .toggle-btn-tertiary.toggle-btn-active,
63
- .toggle-btn-tertiary.active {
228
+ .toggle-btn-tertiary:is(.toggle-btn-active:not([aria-pressed]), .toggle-btn[aria-pressed="true"]),
229
+ .toggle-btn-tertiary.active:not([aria-pressed]) {
64
230
  color: var(--color-on-tertiary-container);
65
231
  background-color: var(--color-tertiary-container);
66
232
  border-color: var(--color-tertiary);
67
233
  }
68
234
 
69
235
  /* Info variant */
70
- .toggle-btn-info.toggle-btn-active,
71
- .toggle-btn-info.active {
236
+ .toggle-btn-info:is(.toggle-btn-active:not([aria-pressed]), .toggle-btn[aria-pressed="true"]),
237
+ .toggle-btn-info.active:not([aria-pressed]) {
72
238
  color: var(--color-on-info-container);
73
239
  background-color: var(--color-info-container);
74
240
  border-color: var(--color-info);
75
241
  }
76
242
 
77
243
  /* Success variant */
78
- .toggle-btn-success.toggle-btn-active,
79
- .toggle-btn-success.active {
244
+ .toggle-btn-success:is(.toggle-btn-active:not([aria-pressed]), .toggle-btn[aria-pressed="true"]),
245
+ .toggle-btn-success.active:not([aria-pressed]) {
80
246
  color: var(--color-on-success-container);
81
247
  background-color: var(--color-success-container);
82
248
  border-color: var(--color-success);
83
249
  }
84
250
 
85
251
  /* Warning variant */
86
- .toggle-btn-warning.toggle-btn-active,
87
- .toggle-btn-warning.active {
252
+ .toggle-btn-warning:is(.toggle-btn-active:not([aria-pressed]), .toggle-btn[aria-pressed="true"]),
253
+ .toggle-btn-warning.active:not([aria-pressed]) {
88
254
  color: var(--color-on-warning-container);
89
255
  background-color: var(--color-warning-container);
90
256
  border-color: var(--color-warning);
91
257
  }
92
258
 
93
259
  /* Error variant */
94
- .toggle-btn-error.toggle-btn-active,
95
- .toggle-btn-error.active {
260
+ .toggle-btn-error:is(.toggle-btn-active:not([aria-pressed]), .toggle-btn[aria-pressed="true"]),
261
+ .toggle-btn-error.active:not([aria-pressed]) {
96
262
  color: var(--color-on-error-container);
97
263
  background-color: var(--color-error-container);
98
264
  border-color: var(--color-error);
99
265
  }
100
266
 
101
267
  /* Accent variant */
102
- .toggle-btn-accent.toggle-btn-active,
103
- .toggle-btn-accent.active {
268
+ .toggle-btn-accent:is(.toggle-btn-active:not([aria-pressed]), .toggle-btn[aria-pressed="true"]),
269
+ .toggle-btn-accent.active:not([aria-pressed]) {
104
270
  color: var(--color-on-surface);
105
271
  background-color: color-mix(in oklch, var(--color-accent) 15%, var(--color-surface));
106
272
  border-color: var(--color-accent);
107
273
  }
108
274
 
109
275
  /* Neutral variant */
110
- .toggle-btn-neutral.toggle-btn-active,
111
- .toggle-btn-neutral.active {
276
+ .toggle-btn-neutral:is(.toggle-btn-active:not([aria-pressed]), .toggle-btn[aria-pressed="true"]),
277
+ .toggle-btn-neutral.active:not([aria-pressed]) {
112
278
  color: var(--color-on-surface);
113
279
  background-color: color-mix(in oklch, var(--color-neutral) 15%, var(--color-surface));
114
280
  border-color: var(--color-neutral);
115
281
  }
116
282
 
117
283
  /* Base variant */
118
- .toggle-btn-base.toggle-btn-active,
119
- .toggle-btn-base.active {
284
+ .toggle-btn-base:is(.toggle-btn-active:not([aria-pressed]), .toggle-btn[aria-pressed="true"]),
285
+ .toggle-btn-base.active:not([aria-pressed]) {
120
286
  color: var(--color-base-content);
121
287
  background-color: var(--color-base-200);
122
288
  border-color: var(--color-base-content);
@@ -245,8 +411,8 @@
245
411
  border-radius: var(--radius-xs);
246
412
  }
247
413
 
248
- .toggle-segmented .toggle-btn.toggle-btn-active,
249
- .toggle-segmented .toggle-btn.active {
414
+ .toggle-segmented .toggle-btn:is(.toggle-btn-active:not([aria-pressed]), .toggle-btn[aria-pressed="true"]),
415
+ .toggle-segmented .toggle-btn.active:not([aria-pressed]) {
250
416
  background-color: var(--color-surface-variant);
251
417
  border-color: transparent;
252
418
  box-shadow: var(--shadow-sm);
@@ -258,8 +424,8 @@
258
424
  padding: 0.5rem 1rem;
259
425
  }
260
426
 
261
- .toggle-chip.toggle-btn-active,
262
- .toggle-chip.active {
427
+ .toggle-chip:is(.toggle-btn-active:not([aria-pressed]), .toggle-btn[aria-pressed="true"]),
428
+ .toggle-chip.active:not([aria-pressed]) {
263
429
  background-color: var(--color-primary-container);
264
430
  border-color: var(--color-primary-container);
265
431
  }
@@ -269,8 +435,8 @@
269
435
  background-color: transparent;
270
436
  }
271
437
 
272
- .toggle-outlined.toggle-btn-active,
273
- .toggle-outlined.active {
438
+ .toggle-outlined:is(.toggle-btn-active:not([aria-pressed]), .toggle-btn[aria-pressed="true"]),
439
+ .toggle-outlined.active:not([aria-pressed]) {
274
440
  background-color: color-mix(in oklch, var(--color-primary) 10%, transparent);
275
441
  }
276
442
 
@@ -280,8 +446,8 @@
280
446
  border: none;
281
447
  }
282
448
 
283
- .toggle-filled.toggle-btn-active,
284
- .toggle-filled.active {
449
+ .toggle-filled:is(.toggle-btn-active:not([aria-pressed]), .toggle-btn[aria-pressed="true"]),
450
+ .toggle-filled.active:not([aria-pressed]) {
285
451
  background-color: var(--color-primary);
286
452
  color: var(--color-primary-content);
287
453
  }
@@ -76,53 +76,8 @@
76
76
  position-area: right;
77
77
  }
78
78
 
79
- /* ========================================
80
- * Arrow (preferred position only; may be wrong after flip)
81
- * ======================================== */
82
-
83
- .tooltip[popover]::before {
84
- content: '';
85
- position: absolute;
86
- width: 0;
87
- height: 0;
88
- border: 0.375rem solid transparent;
89
- }
90
-
91
- .tooltip-top[popover]::before,
92
- .tooltip[popover]:not(.tooltip-bottom):not(.tooltip-left):not(.tooltip-right)::before {
93
- top: 100%;
94
- left: 50%;
95
- transform: translateX(-50%);
96
- border-top-color: var(--tooltip-bg);
97
- }
98
-
99
- .tooltip-bottom[popover]::before {
100
- bottom: 100%;
101
- top: auto;
102
- left: 50%;
103
- transform: translateX(-50%);
104
- border-bottom-color: var(--tooltip-bg);
105
- border-top-color: transparent;
106
- }
107
-
108
- .tooltip-left[popover]::before {
109
- left: 100%;
110
- top: 50%;
111
- bottom: auto;
112
- transform: translateY(-50%);
113
- border-left-color: var(--tooltip-bg);
114
- border-top-color: transparent;
115
- }
116
-
117
- .tooltip-right[popover]::before {
118
- right: 100%;
119
- left: auto;
120
- top: 50%;
121
- bottom: auto;
122
- transform: translateY(-50%);
123
- border-right-color: var(--tooltip-bg);
124
- border-top-color: transparent;
125
- }
79
+ /* Automatic placement can flip independently of the preferred side. The
80
+ core stays arrowless instead of showing a misleading arrow. */
126
81
 
127
82
  /* ========================================
128
83
  * Color variants
@@ -0,0 +1,66 @@
1
+ /** Opt-in native presentation. Applications own errors, messages and announcements. */
2
+ @layer components {
3
+ /* Compatibility wrapper states are intentionally immediate. */
4
+ :is(.form-group-info, .form-control.info) { --dm-validation-color: var(--color-info); }
5
+ :is(.form-group-warning, .form-control.warning) { --dm-validation-color: var(--color-warning); }
6
+ :is(.form-group-success, .form-control.success) { --dm-validation-color: var(--color-success); }
7
+
8
+ :is(.input, .select, .textarea, .checkbox, .radio, .switch, .toggle, .file-input, .otp-input-field, .otp-code, .datepicker-input):is(.validate :valid, .validate:valid, .validator.validator-success:user-valid):not([aria-invalid="true"]):not(:where(.form-group-error *, .form-control.error *)):not(:disabled) {
9
+ --dm-validation-color: var(--color-success);
10
+ --dm-validation-content: var(--color-success-content);
11
+ }
12
+
13
+ :is(.input, .select, .textarea, .checkbox, .radio, .switch, .toggle, .file-input, .otp-input-field, .otp-code, .datepicker-input):is(.validate :invalid, .validate:invalid, .validator:user-invalid):not(:disabled) {
14
+ --dm-validation-color: var(--color-error);
15
+ --dm-validation-content: var(--color-error-content);
16
+ }
17
+
18
+ :is(.form-group-error, .form-control.error) {
19
+ --dm-validation-color: var(--color-error);
20
+ --dm-validation-content: var(--color-error-content);
21
+ }
22
+
23
+ /* Higher specificity than native states, including server-invalid + user-valid. */
24
+ :is(.input, .select, .textarea, .checkbox, .radio, .switch, .toggle, .file-input, .otp-input-field, .otp-code, .datepicker-input):is([aria-invalid="true"], .form-group-error *, .form-control.error *):not(:disabled):is(:focus, :not(:focus)) {
25
+ --dm-validation-color: var(--color-error);
26
+ --dm-validation-content: var(--color-error-content);
27
+ }
28
+
29
+ /* This shared selector wins over filled/ghost focus and semantic modifiers. */
30
+ :is(.input, .select, .textarea, .checkbox, .radio, .switch, .toggle, .file-input, .otp-input-field, .otp-code, .datepicker-input):is([aria-invalid="true"], .validator:user-invalid, .validator.validator-success:user-valid, .validate :invalid, .validate :valid, .validate:invalid, .validate:valid, .form-group-error *, .form-group-success *, .form-group-warning *, .form-group-info *, .form-control.error *, .form-control.success *, .form-control.warning *, .form-control.info *):not(:disabled) {
31
+ color: var(--dm-validation-color);
32
+ border-color: var(--dm-validation-color);
33
+ --checkbox-color: var(--dm-validation-color);
34
+ --radio-color: var(--dm-validation-color);
35
+ --switch-color: var(--dm-validation-color);
36
+ --switch-content-color: var(--dm-validation-content, var(--color-primary-content));
37
+ --toggle-color: var(--dm-validation-color);
38
+ }
39
+
40
+ :is(.input, .select, .textarea, .checkbox, .radio, .switch, .toggle, .file-input, .otp-input-field, .otp-code, .datepicker-input):is([aria-invalid="true"], .validator:user-invalid, .validator.validator-success:user-valid, .validate :invalid, .validate :valid, .validate:invalid, .validate:valid, .form-group-error *, .form-group-success *, .form-group-warning *, .form-group-info *, .form-control.error *, .form-control.success *, .form-control.warning *, .form-control.info *):not(:disabled):focus {
41
+ border-color: var(--dm-validation-color);
42
+ box-shadow: 0 0 0 3px color-mix(in oklch, var(--dm-validation-color) 20%, transparent);
43
+ }
44
+
45
+ :is(.form-group-error, .form-control.error) :is(.form-label, .label-text) { color: var(--color-error); }
46
+ :is(.form-group-success, .form-control.success) :is(.form-label, .label-text) { color: var(--color-success); }
47
+ :is(.form-group-warning, .form-control.warning) :is(.form-label, .label-text) { color: var(--color-warning); }
48
+ :is(.form-group-info, .form-control.info) :is(.form-label, .label-text) { color: var(--color-info); }
49
+
50
+ .validator-hint { font-size: 0.75rem; color: var(--color-on-surface-variant); }
51
+ .validator-error { display: none; font-size: 0.75rem; color: var(--color-error); }
52
+ /* Keep OTP actions stationary when blur reveals an error during a click. */
53
+ .otp-code ~ .validator-error { display: block; visibility: hidden; }
54
+ :is(.form-group, .form-control):has(:is([aria-invalid="true"], .validator:user-invalid, .validate :invalid, .validate:invalid):not(:disabled)) > .validator-error,
55
+ :is(.form-group-error, .form-control.error) > .validator-error { display: block; visibility: visible; }
56
+
57
+ @media (forced-colors: active) {
58
+ :is(input, select, textarea):is(.input, .select, .textarea, .checkbox, .radio, .switch, .toggle, .file-input, .otp-input-field, .otp-code, .datepicker-input):focus-visible {
59
+ outline: 2px solid Highlight;
60
+ outline-offset: 2px;
61
+ }
62
+ input:is(.checkbox, .radio, .switch, .toggle) { appearance: auto; }
63
+ input:is(.checkbox, .radio, .switch, .toggle)::before,
64
+ input:is(.checkbox, .radio, .switch, .toggle)::after { content: none; }
65
+ }
66
+ }
@@ -0,0 +1,22 @@
1
+ @layer components {
2
+ .aura {
3
+ position: relative;
4
+ isolation: isolate;
5
+ border-radius: var(--radius-md);
6
+ outline: 1px solid var(--color-primary);
7
+ }
8
+ .aura::before {
9
+ content: '';
10
+ position: absolute;
11
+ inset: 0;
12
+ z-index: -1;
13
+ border-radius: inherit;
14
+ box-shadow: 0 0 1rem color-mix(in oklch, var(--color-primary) 35%, transparent);
15
+ pointer-events: none;
16
+ }
17
+ @media (prefers-reduced-motion: no-preference) {
18
+ .aura-animated::before { animation: dm-aura 4s ease-in-out infinite; }
19
+ }
20
+ @keyframes dm-aura { 50% { opacity: 0.4; } }
21
+ @media print { .aura::before { display: none; } }
22
+ }
@@ -0,0 +1,8 @@
1
+ @layer components {
2
+ .hover-3d { border-radius: var(--radius-md); }
3
+ @media (hover: hover) and (pointer: fine) and (prefers-reduced-motion: no-preference) {
4
+ .hover-3d { transition: transform 200ms ease; }
5
+ .hover-3d:hover:not(:focus-within) { transform: perspective(60rem) rotateX(var(--hover-3d-x, 2deg)) rotateY(var(--hover-3d-y, -2deg)); }
6
+ }
7
+ @media print { .hover-3d { transform: none; } }
8
+ }
@@ -0,0 +1,14 @@
1
+ @layer components {
2
+ .hover-gallery { display: grid; gap: 0.5rem; min-inline-size: 0; color: var(--color-on-surface); }
3
+ .hover-gallery-stage { aspect-ratio: var(--hover-gallery-ratio, 4 / 3); overflow: hidden; border-radius: var(--radius-md); }
4
+ .hover-gallery-stage img { inline-size: 100%; block-size: 100%; object-fit: cover; }
5
+ .hover-gallery-controls { display: flex; flex-wrap: wrap; gap: 0.5rem; }
6
+ .hover-gallery-controls > button { border: 1px solid currentColor; color: inherit; background: var(--color-surface-container); padding: 0.25rem; border-radius: var(--radius-sm); cursor: pointer; }
7
+ .hover-gallery-controls img { display: block; inline-size: 3rem; block-size: 3rem; object-fit: cover; }
8
+ .hover-gallery-controls > button[aria-pressed="true"] { color: var(--color-primary); outline: 2px solid currentColor; }
9
+ .hover-gallery-controls > button:focus-visible { outline: 2px solid var(--color-primary); outline-offset: 2px; }
10
+ @media (hover: hover) and (prefers-reduced-motion: no-preference) {
11
+ .hover-gallery-controls > button:hover { background: var(--color-primary-container); }
12
+ }
13
+ @media print { .hover-gallery-controls { display: none; } }
14
+ }
@@ -0,0 +1,65 @@
1
+ /* Explicitly opt-in. Not imported by core or components/index.css. */
2
+ @layer components {
3
+ .aura {
4
+ position: relative;
5
+ isolation: isolate;
6
+ border-radius: var(--radius-md);
7
+ outline: 1px solid var(--color-primary);
8
+ }
9
+ .aura::before {
10
+ content: '';
11
+ position: absolute;
12
+ inset: 0;
13
+ z-index: -1;
14
+ border-radius: inherit;
15
+ box-shadow: 0 0 1rem color-mix(in oklch, var(--color-primary) 35%, transparent);
16
+ pointer-events: none;
17
+ }
18
+ @media (prefers-reduced-motion: no-preference) {
19
+ .aura-animated::before { animation: dm-aura 4s ease-in-out infinite; }
20
+ }
21
+ @keyframes dm-aura { 50% { opacity: 0.4; } }
22
+ @media print { .aura::before { display: none; } }
23
+ }
24
+
25
+ @layer components {
26
+ .hover-3d { border-radius: var(--radius-md); }
27
+ @media (hover: hover) and (pointer: fine) and (prefers-reduced-motion: no-preference) {
28
+ .hover-3d { transition: transform 200ms ease; }
29
+ .hover-3d:hover:not(:focus-within) { transform: perspective(60rem) rotateX(var(--hover-3d-x, 2deg)) rotateY(var(--hover-3d-y, -2deg)); }
30
+ }
31
+ @media print { .hover-3d { transform: none; } }
32
+ }
33
+
34
+ @layer components {
35
+ .hover-gallery { display: grid; gap: 0.5rem; min-inline-size: 0; color: var(--color-on-surface); }
36
+ .hover-gallery-stage { aspect-ratio: var(--hover-gallery-ratio, 4 / 3); overflow: hidden; border-radius: var(--radius-md); }
37
+ .hover-gallery-stage img { inline-size: 100%; block-size: 100%; object-fit: cover; }
38
+ .hover-gallery-controls { display: flex; flex-wrap: wrap; gap: 0.5rem; }
39
+ .hover-gallery-controls > button { border: 1px solid currentColor; color: inherit; background: var(--color-surface-container); padding: 0.25rem; border-radius: var(--radius-sm); cursor: pointer; }
40
+ .hover-gallery-controls img { display: block; inline-size: 3rem; block-size: 3rem; object-fit: cover; }
41
+ .hover-gallery-controls > button[aria-pressed="true"] { color: var(--color-primary); outline: 2px solid currentColor; }
42
+ .hover-gallery-controls > button:focus-visible { outline: 2px solid var(--color-primary); outline-offset: 2px; }
43
+ @media (hover: hover) and (prefers-reduced-motion: no-preference) {
44
+ .hover-gallery-controls > button:hover { background: var(--color-primary-container); }
45
+ }
46
+ @media print { .hover-gallery-controls { display: none; } }
47
+ }
48
+
49
+ @layer components {
50
+ .text-rotate { display: inline-grid; color: var(--color-on-surface); vertical-align: baseline; }
51
+ .text-rotate > span { grid-area: 1 / 1; visibility: hidden; }
52
+ .text-rotate > span:first-child { visibility: visible; }
53
+ @media (prefers-reduced-motion: no-preference) {
54
+ .text-rotate-animated > span { animation: dm-text-rotate var(--text-rotate-duration, 6s) steps(1, end) infinite; }
55
+ .text-rotate-animated > span:nth-child(2) { animation-delay: calc(var(--text-rotate-duration, 6s) / -3); }
56
+ .text-rotate-animated > span:nth-child(3) { animation-delay: calc(var(--text-rotate-duration, 6s) / -3 * 2); }
57
+ .text-rotate-animated:is(:hover, :focus-within, .text-rotate-paused) > span { animation-play-state: paused; }
58
+ }
59
+ @keyframes dm-text-rotate { 0%, 33.32% { visibility: visible; } 33.33%, 100% { visibility: hidden; } }
60
+ @media print {
61
+ .text-rotate > span { animation: none; visibility: hidden; }
62
+ .text-rotate > span:first-child { visibility: visible; }
63
+ }
64
+ }
65
+
@@ -0,0 +1,16 @@
1
+ @layer components {
2
+ .text-rotate { display: inline-grid; color: var(--color-on-surface); vertical-align: baseline; }
3
+ .text-rotate > span { grid-area: 1 / 1; visibility: hidden; }
4
+ .text-rotate > span:first-child { visibility: visible; }
5
+ @media (prefers-reduced-motion: no-preference) {
6
+ .text-rotate-animated > span { animation: dm-text-rotate var(--text-rotate-duration, 6s) steps(1, end) infinite; }
7
+ .text-rotate-animated > span:nth-child(2) { animation-delay: calc(var(--text-rotate-duration, 6s) / -3); }
8
+ .text-rotate-animated > span:nth-child(3) { animation-delay: calc(var(--text-rotate-duration, 6s) / -3 * 2); }
9
+ .text-rotate-animated:is(:hover, :focus-within, .text-rotate-paused) > span { animation-play-state: paused; }
10
+ }
11
+ @keyframes dm-text-rotate { 0%, 33.32% { visibility: visible; } 33.33%, 100% { visibility: hidden; } }
12
+ @media print {
13
+ .text-rotate > span { animation: none; visibility: hidden; }
14
+ .text-rotate > span:first-child { visibility: visible; }
15
+ }
16
+ }