@wpmoo/ui 1.0.0-rc.3 → 1.0.0-rc.4

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 (82) hide show
  1. package/ASSET_LICENSE.md +3 -1
  2. package/README.md +51 -123
  3. package/certification.json +14 -2
  4. package/dist/assets/css/moo-ui.css +55 -15
  5. package/dist/assets/css/moo-ui.min.css +3 -3
  6. package/dist/assets/css/moo.css +49 -15
  7. package/dist/assets/css/moo.min.css +3 -3
  8. package/dist/js/chart.js +1 -1
  9. package/dist/js/chart.min.js +1 -1
  10. package/dist/js/combobox.js +71 -18
  11. package/dist/js/context-menu.js +1 -1
  12. package/dist/js/datatable.js +1 -1
  13. package/dist/js/datepicker.js +1 -1
  14. package/dist/js/datepicker.min.js +1 -1
  15. package/dist/js/moo-ui.js +18950 -0
  16. package/dist/js/moo-ui.min.js +27 -0
  17. package/dist/js/sidebar.js +1 -1
  18. package/dist/js/slider.js +1 -1
  19. package/package.json +12 -4
  20. package/scss/_components.scss +73 -0
  21. package/scss/_settings.scss +8 -0
  22. package/scss/components/_accordion.scss +59 -0
  23. package/scss/components/_alert.scss +94 -0
  24. package/scss/components/_avatar.scss +154 -0
  25. package/scss/components/_badge.scss +33 -0
  26. package/scss/components/_breadcrumb.scss +130 -0
  27. package/scss/components/_button.scss +228 -0
  28. package/scss/components/_button_group.scss +51 -0
  29. package/scss/components/_card.scss +142 -0
  30. package/scss/components/_checkbox.scss +115 -0
  31. package/scss/components/_close_button.scss +59 -0
  32. package/scss/components/_collapsible.scss +43 -0
  33. package/scss/components/_combobox.scss +258 -0
  34. package/scss/components/_context-menu.scss +28 -0
  35. package/scss/components/_datatable.scss +1082 -0
  36. package/scss/components/_datepicker.scss +236 -0
  37. package/scss/components/_dialog.scss +66 -0
  38. package/scss/components/_dropdown.scss +202 -0
  39. package/scss/components/_field.scss +125 -0
  40. package/scss/components/_input.scss +35 -0
  41. package/scss/components/_input_group.scss +201 -0
  42. package/scss/components/_kbd.scss +10 -0
  43. package/scss/components/_menubar.scss +16 -0
  44. package/scss/components/_navigation.scss +41 -0
  45. package/scss/components/_pagination.scss +70 -0
  46. package/scss/components/_popover.scss +47 -0
  47. package/scss/components/_progress.scss +7 -0
  48. package/scss/components/_radio_group.scss +9 -0
  49. package/scss/components/_select.scss +7 -0
  50. package/scss/components/_separator.scss +15 -0
  51. package/scss/components/_sheet.scss +32 -0
  52. package/scss/components/_sidebar.scss +7 -0
  53. package/scss/components/_skeleton.scss +17 -0
  54. package/scss/components/_slider.scss +214 -0
  55. package/scss/components/_spinner.scss +17 -0
  56. package/scss/components/_switch.scss +25 -0
  57. package/scss/components/_table.scss +7 -0
  58. package/scss/components/_tabs.scss +139 -0
  59. package/scss/components/_textarea.scss +7 -0
  60. package/scss/components/_toast.scss +119 -0
  61. package/scss/components/_toggle_group.scss +19 -0
  62. package/scss/components/_tooltip.scss +83 -0
  63. package/scss/components/sidebar/_collapsed.scss +187 -0
  64. package/scss/components/sidebar/_identity.scss +72 -0
  65. package/scss/components/sidebar/_inset.scss +59 -0
  66. package/scss/components/sidebar/_layout.scss +176 -0
  67. package/scss/components/sidebar/_menus.scss +236 -0
  68. package/scss/foundations/_core_global_primitives.scss +5 -0
  69. package/scss/foundations/_core_state_layer.scss +25 -0
  70. package/scss/foundations/_focus.scss +139 -0
  71. package/scss/foundations/_overlay_backdrop.scss +19 -0
  72. package/scss/moo-core.scss +47 -0
  73. package/scss/moo-ui.scss +50 -0
  74. package/scss/settings/_bootstrap_overrides.scss +11 -0
  75. package/scss/settings/_component_variables.scss +479 -0
  76. package/scss/settings/_forms.scss +65 -0
  77. package/scss/settings/_palette.scss +132 -0
  78. package/scss/themes/_scoped_core.scss +239 -0
  79. package/scss/themes/_standalone_root.scss +117 -0
  80. package/scss/utilities/_scroll_fade.scss +193 -0
  81. package/scss/utilities/_scroll_fade_primitives.scss +43 -0
  82. package/THIRD_PARTY_NOTICES.md +0 -53
@@ -0,0 +1,214 @@
1
+ $slider-thumb-size: $form-range-thumb-width * 0.75;
2
+ $slider-thumb-margin-top: ($progress-height - $slider-thumb-size) * 0.5;
3
+
4
+ .slider {
5
+ display: grid;
6
+ gap: $form-text-margin-top;
7
+ width: 100%;
8
+ }
9
+
10
+ .slider-track {
11
+ position: relative;
12
+ display: flex;
13
+ align-items: center;
14
+ width: $form-range-track-width;
15
+ height: $slider-thumb-size;
16
+ cursor: $form-range-track-cursor;
17
+ touch-action: none;
18
+ }
19
+
20
+ .slider-track::before,
21
+ .slider-range {
22
+ position: absolute;
23
+ inset-block-start: 50%;
24
+ height: $progress-height;
25
+ border-radius: var(--bs-border-radius-pill);
26
+ transform: translateY(-50%);
27
+ pointer-events: none;
28
+ }
29
+
30
+ .slider-track::before {
31
+ content: "";
32
+ inset-inline: 0;
33
+ background-color: $form-range-track-bg;
34
+ box-shadow: var(--bs-box-shadow-inset);
35
+ }
36
+
37
+ .slider-range {
38
+ inset-inline-start: var(--moo-slider-start, 0%);
39
+ inset-inline-end: calc(100% - var(--moo-slider-end, 0%));
40
+ background-color: var(--moo-primary);
41
+ }
42
+
43
+ .slider-input {
44
+ position: absolute;
45
+ inset: 0;
46
+ margin: 0;
47
+ width: 100%;
48
+ height: 100%;
49
+ pointer-events: none;
50
+ }
51
+
52
+ .slider-input::-webkit-slider-runnable-track {
53
+ height: $progress-height;
54
+ background-color: transparent;
55
+ box-shadow: none;
56
+ }
57
+
58
+ .slider-input::-moz-range-track {
59
+ height: $progress-height;
60
+ background-color: transparent;
61
+ box-shadow: none;
62
+ }
63
+
64
+ .slider-input::-webkit-slider-thumb {
65
+ position: relative;
66
+ z-index: 1;
67
+ width: $slider-thumb-size;
68
+ height: $slider-thumb-size;
69
+ margin-top: $slider-thumb-margin-top;
70
+ background-color: var(--bs-body-bg);
71
+ border-width: var(--bs-border-width);
72
+ border-style: solid;
73
+ border-color: var(--moo-primary);
74
+ box-shadow: var(--bs-box-shadow-sm);
75
+ pointer-events: auto;
76
+ }
77
+
78
+ .slider-input::-moz-range-thumb {
79
+ position: relative;
80
+ z-index: 1;
81
+ width: $slider-thumb-size;
82
+ height: $slider-thumb-size;
83
+ background-color: var(--bs-body-bg);
84
+ border-width: var(--bs-border-width);
85
+ border-style: solid;
86
+ border-color: var(--moo-primary);
87
+ box-shadow: var(--bs-box-shadow-sm);
88
+ pointer-events: auto;
89
+ }
90
+
91
+ .slider-input::-webkit-slider-thumb:active {
92
+ background-color: var(--bs-body-bg);
93
+ border-color: var(--moo-primary);
94
+ }
95
+
96
+ .slider-input::-moz-range-thumb:active {
97
+ background-color: var(--bs-body-bg);
98
+ border-color: var(--moo-primary);
99
+ }
100
+
101
+ .slider-input:focus-visible::-webkit-slider-thumb {
102
+ box-shadow: 0 0 0 #{$moo-form-focus-ring-width} color-mix(in srgb, #{$moo-form-focus-ring-color} 50%, transparent);
103
+ }
104
+
105
+ .slider-input:focus-visible::-moz-range-thumb {
106
+ box-shadow: 0 0 0 #{$moo-form-focus-ring-width} color-mix(in srgb, #{$moo-form-focus-ring-color} 50%, transparent);
107
+ }
108
+
109
+ .slider[data-slider-dragging] .slider-input::-webkit-slider-thumb {
110
+ box-shadow: 0 0 0 #{$moo-form-focus-ring-width} color-mix(in srgb, #{$moo-form-focus-ring-color} 50%, transparent);
111
+ }
112
+
113
+ .slider[data-slider-dragging] .slider-input::-moz-range-thumb {
114
+ box-shadow: 0 0 0 #{$moo-form-focus-ring-width} color-mix(in srgb, #{$moo-form-focus-ring-color} 50%, transparent);
115
+ }
116
+
117
+ // Safari can keep pointer-driven range focus out of :focus-visible, while
118
+ // Bootstrap's native .form-range:focus thumb halo still applies.
119
+ .slider[data-slider-pointer-focus]:not([data-slider-dragging]) .slider-input:focus::-webkit-slider-thumb {
120
+ box-shadow: var(--bs-box-shadow-sm);
121
+ }
122
+
123
+ .slider[data-slider-pointer-focus]:not([data-slider-dragging]) .slider-input:focus::-moz-range-thumb {
124
+ box-shadow: var(--bs-box-shadow-sm);
125
+ }
126
+
127
+ .slider-input:disabled {
128
+ opacity: 1;
129
+ }
130
+
131
+ .slider-input:disabled::-webkit-slider-thumb {
132
+ background-color: var(--moo-muted-surface);
133
+ border-color: var(--moo-disabled-foreground);
134
+ box-shadow: none;
135
+ cursor: default;
136
+ }
137
+
138
+ .slider-input:disabled::-moz-range-thumb {
139
+ background-color: var(--moo-muted-surface);
140
+ border-color: var(--moo-disabled-foreground);
141
+ box-shadow: none;
142
+ cursor: default;
143
+ }
144
+
145
+ .slider-input:disabled:focus-visible::-webkit-slider-thumb,
146
+ .slider[data-slider-dragging] .slider-input:disabled::-webkit-slider-thumb,
147
+ .slider[data-slider-pointer-focus] .slider-input:disabled:focus-visible::-webkit-slider-thumb {
148
+ background-color: var(--moo-muted-surface);
149
+ border-color: var(--moo-disabled-foreground);
150
+ box-shadow: none;
151
+ }
152
+
153
+ .slider-input:disabled:focus-visible::-moz-range-thumb,
154
+ .slider[data-slider-dragging] .slider-input:disabled::-moz-range-thumb,
155
+ .slider[data-slider-pointer-focus] .slider-input:disabled:focus-visible::-moz-range-thumb {
156
+ background-color: var(--moo-muted-surface);
157
+ border-color: var(--moo-disabled-foreground);
158
+ box-shadow: none;
159
+ }
160
+
161
+ .slider:has(.slider-input:disabled) .slider-track {
162
+ cursor: default;
163
+ }
164
+
165
+ .slider:has(.slider-input:disabled) .slider-range {
166
+ background-color: var(--moo-disabled-foreground);
167
+ }
168
+
169
+ .slider--range .slider-input--upper {
170
+ z-index: 2;
171
+ }
172
+
173
+ .slider--vertical {
174
+ width: max-content;
175
+ }
176
+
177
+ .slider--vertical [data-slider-output] {
178
+ display: inline-block;
179
+ min-width: 3ch;
180
+ text-align: end;
181
+ font-variant-numeric: tabular-nums;
182
+ }
183
+
184
+ .slider--vertical .slider-track {
185
+ justify-content: center;
186
+ justify-self: center;
187
+ width: $slider-thumb-size;
188
+ height: $input-height * 4;
189
+ }
190
+
191
+ .slider--vertical .slider-track::before {
192
+ inset-block: 0;
193
+ inset-inline: auto;
194
+ width: $progress-height;
195
+ height: auto;
196
+ transform: none;
197
+ }
198
+
199
+ .slider--vertical .slider-range {
200
+ inset-block-start: calc(100% - var(--moo-slider-end, 0%));
201
+ inset-block-end: var(--moo-slider-start, 0%);
202
+ inset-inline-start: 50%;
203
+ inset-inline-end: auto;
204
+ width: $progress-height;
205
+ height: auto;
206
+ transform: translateX(-50%);
207
+ }
208
+
209
+ .slider--vertical .slider-input {
210
+ width: 100%;
211
+ height: 100%;
212
+ writing-mode: vertical-lr;
213
+ direction: rtl;
214
+ }
@@ -0,0 +1,17 @@
1
+ // The design reference's Spinner is the Lucide loader-circle icon spun with
2
+ // CSS, not Bootstrap's thicker spinner-border ring — render_icon already
3
+ // emits the same stroke-width and line-cap Lucide markup the reference
4
+ // uses, so only sizing
5
+ // and rotation are needed here. The animation name is supplied by the active
6
+ // build entry: Bootstrap's spinner-border for standalone, namespaced Moo for
7
+ // the scoped core.
8
+ .spinner [data-icon] {
9
+ width: $spacer;
10
+ height: $spacer;
11
+ animation: $moo-spinner-animation-name $spinner-animation-speed linear infinite;
12
+ }
13
+
14
+ .spinner-sm [data-icon] {
15
+ width: $spacer * 0.75;
16
+ height: $spacer * 0.75;
17
+ }
@@ -0,0 +1,25 @@
1
+ // Bootstrap's .form-switch only overrides width; height still comes from
2
+ // the shared $form-check-input-width (also used by Checkbox and Radio
3
+ // Group), so matching the design reference's taller track needs an
4
+ // explicit height here instead of a shared Bootstrap variable.
5
+ .form-switch .form-check-input {
6
+ height: $moo-switch-height;
7
+ }
8
+
9
+ :where([data-bs-theme="dark"]) .form-switch .form-check-input:checked {
10
+ --#{$prefix}form-switch-bg: #{escape-svg($moo-switch-checked-bg-image-dark)};
11
+
12
+ background-color: var(--moo-primary);
13
+ border-color: var(--moo-primary);
14
+ filter: none;
15
+ }
16
+
17
+ .form-switch .form-check-input:focus:active:not(:checked),
18
+ .form-switch .form-check-input:focus:not(:focus-visible):not(:checked) {
19
+ --#{$prefix}form-switch-bg: #{escape-svg($form-switch-bg-image)};
20
+ }
21
+
22
+ :where([data-bs-theme="dark"]) .form-switch .form-check-input:focus:active:not(:checked),
23
+ :where([data-bs-theme="dark"]) .form-switch .form-check-input:focus:not(:focus-visible):not(:checked) {
24
+ --#{$prefix}form-switch-bg: #{escape-svg($form-switch-bg-image-dark)};
25
+ }
@@ -0,0 +1,7 @@
1
+ .table-responsive:not(.scroll-fade-x):has(.table-row-actions > [aria-expanded="true"]) {
2
+ overflow: visible;
3
+ }
4
+
5
+ .table-row-actions:has(> [aria-expanded="true"]) {
6
+ z-index: $zindex-dropdown;
7
+ }
@@ -0,0 +1,139 @@
1
+ // The reference TabsList is a segmented control: a muted rounded track with an
2
+ // inset, shadowed pill marking the active tab. Bootstrap's pills nav has
3
+ // no shared track and colors the active pill with $primary directly, so
4
+ // this styling is scoped to the tabs-list wrapper instead of the shared
5
+ // pills class — Navigation's own pills variant already owns that class
6
+ // and must keep its plain Bootstrap look.
7
+ .tabs-list {
8
+ display: inline-flex;
9
+ gap: $moo-tabs-list-gap;
10
+ padding: $moo-tabs-list-padding;
11
+ background-color: var(--moo-muted-surface);
12
+ border-radius: var(--bs-border-radius-lg);
13
+ }
14
+
15
+ .tabs-list .nav-link {
16
+ padding: $moo-tabs-trigger-padding-y $moo-tabs-trigger-padding-x;
17
+ font-size: $font-size-base;
18
+ font-weight: 500;
19
+ // var(--moo-muted-foreground) on the track's own var(--moo-muted-surface)
20
+ // background measures 4.40:1 in light mode, under the 4.5:1 WCAG AA
21
+ // minimum for normal text (axe caught this in the certification fixture).
22
+ // var(--bs-secondary-color) is not a usable substitute here -- Moo UI's
23
+ // own theme sets --bs-secondary-color: var(--moo-muted-foreground)
24
+ // (scss/themes/_standalone_root.scss), so it resolves to the exact same
25
+ // failing color. var(--bs-secondary-text-emphasis) is a different,
26
+ // untouched Bootstrap token (Moo UI does not override it) built for
27
+ // accessible colored text against a tinted "subtle" surface -- the same
28
+ // category of fix the Kbd contrast defect used -- and measures 5.55:1
29
+ // light / 14.03:1 dark against this track's own background.
30
+ color: var(--bs-secondary-text-emphasis);
31
+ border-radius: var(--bs-border-radius);
32
+ cursor: default;
33
+ @include transition($transition-base);
34
+ }
35
+
36
+ .tabs-list .nav-link:hover {
37
+ color: var(--moo-foreground);
38
+ }
39
+
40
+ .tabs-list .nav-link:not(.active, .disabled):hover {
41
+ background-color: transparent;
42
+ box-shadow: none;
43
+ }
44
+
45
+ .tabs-list .nav-link:focus:not(:focus-visible) {
46
+ outline: 0;
47
+ box-shadow: none;
48
+ }
49
+
50
+ .tabs-list .nav-link.active {
51
+ color: var(--moo-foreground);
52
+ background-color: var(--moo-surface);
53
+ box-shadow: var(--bs-box-shadow-sm);
54
+ }
55
+
56
+ .tabs-list .nav-link.active:focus-visible {
57
+ border-color: var(--moo-ring);
58
+ outline: 0;
59
+ box-shadow: 0 0 0 #{$moo-form-focus-ring-width} color-mix(in srgb, #{$moo-form-focus-ring-color} 50%, transparent);
60
+ }
61
+
62
+ .tabs-list:has(.nav-link:hover) .nav-link.active:not(:hover) {
63
+ background-color: var(--moo-surface);
64
+ box-shadow: var(--bs-box-shadow-sm);
65
+ }
66
+
67
+ .tabs--vertical {
68
+ display: flex;
69
+ align-items: flex-start;
70
+ gap: $spacer;
71
+ width: 100%;
72
+ max-width: 100%;
73
+ }
74
+
75
+ .tabs--vertical .tabs-list {
76
+ flex: 0 0 auto;
77
+ }
78
+
79
+ .tabs--vertical .nav-link {
80
+ justify-content: flex-start;
81
+ text-align: start;
82
+ }
83
+
84
+ .tabs--vertical .tab-content {
85
+ flex: 1 1 auto;
86
+ min-width: 0;
87
+ margin-top: 0;
88
+ }
89
+
90
+ @include media-breakpoint-down(sm) {
91
+ .tabs--vertical {
92
+ flex-direction: column;
93
+ }
94
+
95
+ .tabs--vertical .tabs-list {
96
+ flex-direction: row !important;
97
+ }
98
+ }
99
+
100
+ .tabs-list .nav-link:disabled,
101
+ .tabs-list .nav-link.disabled {
102
+ color: var(--moo-disabled-foreground);
103
+ opacity: var(--moo-disabled-control-opacity);
104
+ pointer-events: none;
105
+ }
106
+
107
+ // Bootstrap hides inactive panes with display:none, so the pane height
108
+ // changes the moment a shorter or taller tab becomes active — the
109
+ // surrounding page jumps to the new height on every switch. Stacking every
110
+ // pane in the same grid cell sizes the row to the tallest pane once, so
111
+ // switching only toggles visibility, never the container's height.
112
+ .tab-content {
113
+ display: grid;
114
+ width: 100%;
115
+ max-width: 100%;
116
+ min-width: 0;
117
+ margin-top: $moo-tabs-content-gap;
118
+ }
119
+
120
+ .tab-content > .tab-pane {
121
+ grid-area: 1 / 1;
122
+ display: block;
123
+ width: 100%;
124
+ max-width: 100%;
125
+ min-width: 0;
126
+ opacity: 0;
127
+ visibility: hidden;
128
+ pointer-events: none;
129
+ // Slightly longer than Bootstrap's .15s fade so the content handoff feels
130
+ // intentional without adding any positional movement.
131
+ transition: opacity 0.16s ease, visibility 0s linear 0.16s;
132
+ }
133
+
134
+ .tab-content > .active {
135
+ opacity: 1;
136
+ visibility: visible;
137
+ pointer-events: auto;
138
+ transition-delay: 0s;
139
+ }
@@ -0,0 +1,7 @@
1
+ textarea.form-control:not(.form-control-sm, .form-control-lg) {
2
+ padding: $input-btn-padding-y-lg $input-btn-padding-x;
3
+ }
4
+
5
+ textarea.form-control:disabled {
6
+ resize: none;
7
+ }
@@ -0,0 +1,119 @@
1
+ // Bootstrap owns Toast markup, lifecycle classes, placement utilities, and
2
+ // autohide behavior. This partial only retunes the native surface and opts
3
+ // an explicitly indexed bottom-end container into the catalog deck layout.
4
+ .toast {
5
+ --bs-toast-border-radius: var(--bs-border-radius-lg);
6
+ --bs-toast-border-color: var(--moo-border);
7
+ --bs-toast-bg: var(--bs-body-bg);
8
+ --bs-toast-box-shadow: var(--bs-box-shadow-lg);
9
+ --bs-toast-header-bg: var(--bs-body-bg);
10
+ --bs-toast-padding-x: #{$spacer};
11
+ }
12
+
13
+ .toast-container {
14
+ z-index: var(--bs-toast-zindex, #{$zindex-toast});
15
+ }
16
+
17
+ .toast-header strong {
18
+ font-weight: $font-weight-medium;
19
+ }
20
+
21
+ .toast-status-icon {
22
+ display: inline-flex;
23
+ flex: 0 0 auto;
24
+ margin-inline-end: $moo-toast-status-icon-gap;
25
+ color: var(--bs-secondary-color);
26
+ }
27
+
28
+ .toast-status-icon [data-icon] {
29
+ width: $moo-toast-status-icon-size;
30
+ height: $moo-toast-status-icon-size;
31
+ }
32
+
33
+ .toast[data-toast-variant="success"] .toast-status-icon {
34
+ color: var(--moo-success);
35
+ }
36
+
37
+ .toast[data-toast-variant="info"] .toast-status-icon {
38
+ color: var(--moo-info);
39
+ }
40
+
41
+ .toast[data-toast-variant="warning"] .toast-status-icon {
42
+ color: var(--moo-warning);
43
+ }
44
+
45
+ .toast[data-toast-variant="destructive"] .toast-status-icon {
46
+ color: var(--moo-destructive);
47
+ }
48
+
49
+ .toast[data-toast-variant="loading"] .toast-status-icon [data-icon] {
50
+ animation: $moo-spinner-animation-name $spinner-animation-speed linear infinite;
51
+ }
52
+
53
+ .toast-container--stacked[data-toast-stack="deck"] {
54
+ --moo-toast-stack-gap: #{$moo-toast-stack-gap};
55
+ --moo-toast-stack-peek: #{$moo-toast-stack-peek};
56
+ --moo-toast-stack-scale-step: #{$moo-toast-stack-scale-step};
57
+ --moo-toast-stack-min-scale: #{$moo-toast-stack-min-scale};
58
+ --moo-toast-stack-enter-y: #{$moo-toast-stack-enter-y};
59
+ }
60
+
61
+ // Without runtime indexes, the container remains readable Bootstrap flow.
62
+ .toast-container--stacked[data-toast-stack="deck"][data-toast-stack-active] {
63
+ bottom: 1rem !important;
64
+ inset-inline-end: 1rem !important;
65
+ height: 0;
66
+ max-width: calc(100vw - 2rem);
67
+ padding: 0 !important;
68
+ width: unquote("min(24rem, calc(100vw - 2rem))");
69
+ }
70
+
71
+ .toast-container--stacked[data-toast-stack="deck"][data-toast-stack-active] > .toast[data-toast-stack-index] {
72
+ inset-block-end: 0;
73
+ inset-inline-end: 0;
74
+ max-width: none;
75
+ margin-bottom: 0;
76
+ position: absolute;
77
+ width: 100%;
78
+ transform: translateY(var(--moo-toast-stack-collapsed-y, 0px))
79
+ scale(var(--moo-toast-stack-scale, 1));
80
+ transform-origin: bottom center;
81
+ z-index: var(--moo-toast-stack-z, 1);
82
+ transition:
83
+ transform $moo-toast-stack-transition-duration $moo-toast-stack-transition-timing-function,
84
+ opacity $moo-toast-stack-transition-duration ease,
85
+ height $moo-toast-stack-height-transition-duration ease;
86
+ }
87
+
88
+ .toast-container--stacked[data-toast-stack="deck"][data-toast-stack-active] > .toast[data-toast-stack-entering] {
89
+ transform: translateY(calc(var(--moo-toast-stack-collapsed-y, 0px) + var(--moo-toast-stack-enter-y, 150%)))
90
+ scale(var(--moo-toast-stack-scale, 1));
91
+ }
92
+
93
+ .toast-container--stacked[data-toast-stack="deck"][data-toast-stack-active] > .toast[data-toast-stack-limited] {
94
+ opacity: 0;
95
+ pointer-events: none;
96
+ }
97
+
98
+ .toast-container--stacked[data-toast-stack="deck"][data-toast-stack-active]:hover > .toast[data-toast-stack-index],
99
+ .toast-container--stacked[data-toast-stack="deck"][data-toast-stack-active]:focus-within > .toast[data-toast-stack-index],
100
+ .toast-container--stacked[data-toast-stack="deck"][data-toast-stack-active][data-toast-stack-hovering] > .toast[data-toast-stack-index] {
101
+ transform: translateY(var(--moo-toast-stack-expanded-y, 0px)) scale(1);
102
+ }
103
+
104
+ .toast-container--stacked[data-toast-stack="deck"][data-toast-stack-active]:hover > .toast[data-toast-stack-entering],
105
+ .toast-container--stacked[data-toast-stack="deck"][data-toast-stack-active]:focus-within > .toast[data-toast-stack-entering],
106
+ .toast-container--stacked[data-toast-stack="deck"][data-toast-stack-active][data-toast-stack-hovering] > .toast[data-toast-stack-entering] {
107
+ transform: translateY(calc(var(--moo-toast-stack-collapsed-y, 0px) + var(--moo-toast-stack-enter-y, 150%)))
108
+ scale(var(--moo-toast-stack-scale, 1));
109
+ }
110
+
111
+ @media (prefers-reduced-motion: reduce) {
112
+ .toast[data-toast-variant="loading"] .toast-status-icon [data-icon] {
113
+ animation: none;
114
+ }
115
+
116
+ .toast-container--stacked[data-toast-stack="deck"][data-toast-stack-active] > .toast[data-toast-stack-index] {
117
+ transition: none;
118
+ }
119
+ }
@@ -0,0 +1,19 @@
1
+ // Toggle Group uses Bootstrap's native .btn-check + label.btn contract, but
2
+ // selected state should change in place instead of using the generic button
3
+ // press nudge.
4
+ .toggle-group > .btn:not(:disabled):not(.disabled):not([aria-disabled="true"]):active {
5
+ transform: none;
6
+ }
7
+
8
+ .toggle-group > .btn-check:checked + .btn {
9
+ --bs-btn-active-color: var(--moo-primary-foreground);
10
+ --bs-btn-active-bg: var(--moo-primary);
11
+ --bs-btn-active-border-color: var(--moo-primary);
12
+ }
13
+
14
+ .toggle-group > .btn-check:focus-visible + .btn,
15
+ .toggle-group > .btn-check:checked:focus-visible + .btn {
16
+ border-color: var(--moo-ring);
17
+ outline: 0;
18
+ box-shadow: 0 0 0 #{$moo-form-focus-ring-width} color-mix(in srgb, #{$moo-form-focus-ring-color} 50%, transparent);
19
+ }
@@ -0,0 +1,83 @@
1
+ // Retuned against the design reference's tooltip (bg-foreground
2
+ // text-background, px-3 py-1.5, text-xs, rounded-md, fully opaque,
3
+ // max-w-xs): Bootstrap's own
4
+ // defaults are narrower, tighter, and dimmed to 90% opacity. All four
5
+ // overrides route through Bootstrap's existing custom properties, so no new
6
+ // selectors or hardcoded colors are introduced.
7
+ .tooltip {
8
+ --bs-tooltip-max-width: 20rem;
9
+ --bs-tooltip-padding-x: 0.75rem;
10
+ --bs-tooltip-padding-y: 0.375rem;
11
+ --bs-tooltip-font-size: 0.75rem;
12
+ --bs-tooltip-opacity: 1;
13
+ --moo-tooltip-motion-duration: #{$moo-tooltip-motion-duration};
14
+ --moo-tooltip-motion-easing: #{$moo-tooltip-motion-easing};
15
+ --moo-tooltip-motion-scale: #{$moo-tooltip-motion-scale};
16
+ --moo-tooltip-motion-translate: #{$moo-tooltip-motion-translate};
17
+ line-height: 1.333;
18
+ }
19
+
20
+ // Popper owns the wrapper transform for placement. Animate only the bubble
21
+ // surface so the tooltip opens with the reference's subtle zoom/slide without
22
+ // disturbing Bootstrap positioning.
23
+ .tooltip.fade .tooltip-inner {
24
+ transition: transform var(--moo-tooltip-motion-duration) var(--moo-tooltip-motion-easing);
25
+ transform: scale(var(--moo-tooltip-motion-scale));
26
+ transform-origin: center;
27
+ will-change: transform;
28
+ }
29
+
30
+ .tooltip.show .tooltip-inner {
31
+ transform: translate3d(0, 0, 0) scale(1);
32
+ }
33
+
34
+ .bs-tooltip-top.fade:not(.show) .tooltip-inner,
35
+ .bs-tooltip-auto[data-popper-placement^="top"].fade:not(.show) .tooltip-inner {
36
+ transform: translate3d(0, var(--moo-tooltip-motion-translate), 0) scale(var(--moo-tooltip-motion-scale));
37
+ }
38
+
39
+ .bs-tooltip-bottom.fade:not(.show) .tooltip-inner,
40
+ .bs-tooltip-auto[data-popper-placement^="bottom"].fade:not(.show) .tooltip-inner {
41
+ transform: translate3d(0, calc(var(--moo-tooltip-motion-translate) * -1), 0) scale(var(--moo-tooltip-motion-scale));
42
+ }
43
+
44
+ .bs-tooltip-start.fade:not(.show) .tooltip-inner,
45
+ .bs-tooltip-auto[data-popper-placement^="left"].fade:not(.show) .tooltip-inner {
46
+ transform: translate3d(var(--moo-tooltip-motion-translate), 0, 0) scale(var(--moo-tooltip-motion-scale));
47
+ }
48
+
49
+ .bs-tooltip-end.fade:not(.show) .tooltip-inner,
50
+ .bs-tooltip-auto[data-popper-placement^="right"].fade:not(.show) .tooltip-inner {
51
+ transform: translate3d(calc(var(--moo-tooltip-motion-translate) * -1), 0, 0) scale(var(--moo-tooltip-motion-scale));
52
+ }
53
+
54
+ @media (prefers-reduced-motion: reduce) {
55
+ .tooltip.fade .tooltip-inner {
56
+ transition: none;
57
+ transform: none;
58
+ will-change: auto;
59
+ }
60
+ }
61
+
62
+ // Bootstrap's tooltip bubble is fully token-driven already (see the vendored
63
+ // Bootstrap tooltip partial), so this partial only owns the plain link
64
+ // trigger's own text color. Unstyled, it falls through to Bootstrap's
65
+ // un-themed --bs-link-color, which reads too low-contrast on dark surfaces;
66
+ // a dotted underline also reads as "hover for more info" rather than a real
67
+ // navigational link.
68
+ .tooltip-trigger {
69
+ color: var(--moo-foreground);
70
+ text-decoration-line: underline;
71
+ text-decoration-style: dotted;
72
+ text-decoration-color: var(--moo-muted-foreground);
73
+ text-underline-offset: 0.2em;
74
+ }
75
+
76
+ // Tooltip shortcut hints render the Kbd component's native <kbd> element.
77
+ // The catalog tooltip initializer extends Bootstrap's sanitizer allowlist
78
+ // with the kbd tag so it survives data-bs-html sanitization; styling comes
79
+ // from the shared Kbd component and Bootstrap's own reboot. A shortcut chip
80
+ // after trailing prose keeps a readable gap from the surrounding text.
81
+ .tooltip-inner kbd {
82
+ margin-inline-start: $kbd-padding-x;
83
+ }