@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.
- package/ASSET_LICENSE.md +3 -1
- package/README.md +51 -123
- package/certification.json +14 -2
- package/dist/assets/css/moo-ui.css +55 -15
- package/dist/assets/css/moo-ui.min.css +3 -3
- package/dist/assets/css/moo.css +49 -15
- package/dist/assets/css/moo.min.css +3 -3
- package/dist/js/chart.js +1 -1
- package/dist/js/chart.min.js +1 -1
- package/dist/js/combobox.js +71 -18
- package/dist/js/context-menu.js +1 -1
- package/dist/js/datatable.js +1 -1
- package/dist/js/datepicker.js +1 -1
- package/dist/js/datepicker.min.js +1 -1
- package/dist/js/moo-ui.js +18950 -0
- package/dist/js/moo-ui.min.js +27 -0
- package/dist/js/sidebar.js +1 -1
- package/dist/js/slider.js +1 -1
- package/package.json +12 -4
- package/scss/_components.scss +73 -0
- package/scss/_settings.scss +8 -0
- package/scss/components/_accordion.scss +59 -0
- package/scss/components/_alert.scss +94 -0
- package/scss/components/_avatar.scss +154 -0
- package/scss/components/_badge.scss +33 -0
- package/scss/components/_breadcrumb.scss +130 -0
- package/scss/components/_button.scss +228 -0
- package/scss/components/_button_group.scss +51 -0
- package/scss/components/_card.scss +142 -0
- package/scss/components/_checkbox.scss +115 -0
- package/scss/components/_close_button.scss +59 -0
- package/scss/components/_collapsible.scss +43 -0
- package/scss/components/_combobox.scss +258 -0
- package/scss/components/_context-menu.scss +28 -0
- package/scss/components/_datatable.scss +1082 -0
- package/scss/components/_datepicker.scss +236 -0
- package/scss/components/_dialog.scss +66 -0
- package/scss/components/_dropdown.scss +202 -0
- package/scss/components/_field.scss +125 -0
- package/scss/components/_input.scss +35 -0
- package/scss/components/_input_group.scss +201 -0
- package/scss/components/_kbd.scss +10 -0
- package/scss/components/_menubar.scss +16 -0
- package/scss/components/_navigation.scss +41 -0
- package/scss/components/_pagination.scss +70 -0
- package/scss/components/_popover.scss +47 -0
- package/scss/components/_progress.scss +7 -0
- package/scss/components/_radio_group.scss +9 -0
- package/scss/components/_select.scss +7 -0
- package/scss/components/_separator.scss +15 -0
- package/scss/components/_sheet.scss +32 -0
- package/scss/components/_sidebar.scss +7 -0
- package/scss/components/_skeleton.scss +17 -0
- package/scss/components/_slider.scss +214 -0
- package/scss/components/_spinner.scss +17 -0
- package/scss/components/_switch.scss +25 -0
- package/scss/components/_table.scss +7 -0
- package/scss/components/_tabs.scss +139 -0
- package/scss/components/_textarea.scss +7 -0
- package/scss/components/_toast.scss +119 -0
- package/scss/components/_toggle_group.scss +19 -0
- package/scss/components/_tooltip.scss +83 -0
- package/scss/components/sidebar/_collapsed.scss +187 -0
- package/scss/components/sidebar/_identity.scss +72 -0
- package/scss/components/sidebar/_inset.scss +59 -0
- package/scss/components/sidebar/_layout.scss +176 -0
- package/scss/components/sidebar/_menus.scss +236 -0
- package/scss/foundations/_core_global_primitives.scss +5 -0
- package/scss/foundations/_core_state_layer.scss +25 -0
- package/scss/foundations/_focus.scss +139 -0
- package/scss/foundations/_overlay_backdrop.scss +19 -0
- package/scss/moo-core.scss +47 -0
- package/scss/moo-ui.scss +50 -0
- package/scss/settings/_bootstrap_overrides.scss +11 -0
- package/scss/settings/_component_variables.scss +479 -0
- package/scss/settings/_forms.scss +65 -0
- package/scss/settings/_palette.scss +132 -0
- package/scss/themes/_scoped_core.scss +239 -0
- package/scss/themes/_standalone_root.scss +117 -0
- package/scss/utilities/_scroll_fade.scss +193 -0
- package/scss/utilities/_scroll_fade_primitives.scss +43 -0
- package/THIRD_PARTY_NOTICES.md +0 -53
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
// Bootstrap owns button geometry through its --bs-btn-* consumption values.
|
|
2
|
+
// Moo adds only icon composition and the missing xs/icon-only variants.
|
|
3
|
+
.btn {
|
|
4
|
+
display: inline-flex;
|
|
5
|
+
align-items: center;
|
|
6
|
+
justify-content: center;
|
|
7
|
+
flex-shrink: 0;
|
|
8
|
+
gap: $icon-link-gap;
|
|
9
|
+
white-space: nowrap;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
.btn:not(.btn-icon, .btn-icon-xs, .btn-icon-sm, .btn-icon-lg):has(> [data-icon="inline-start"]) {
|
|
13
|
+
padding-inline-start: $spacer * 0.5;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
.btn:not(.btn-icon, .btn-icon-xs, .btn-icon-sm, .btn-icon-lg):has(> [data-icon="inline-end"]) {
|
|
17
|
+
padding-inline-end: $spacer * 0.5;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
button.btn:not(:disabled),
|
|
21
|
+
input.btn:not(:disabled),
|
|
22
|
+
label.btn {
|
|
23
|
+
cursor: default;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// Bootstrap provides sm/lg; xs and icon-only variants reuse the same
|
|
27
|
+
// consumption properties instead of introducing a parallel size system.
|
|
28
|
+
.btn-xs,
|
|
29
|
+
.btn-icon-xs {
|
|
30
|
+
--bs-btn-padding-y: #{$spacer * 0.1875};
|
|
31
|
+
--bs-btn-padding-x: #{$spacer * 0.5};
|
|
32
|
+
--bs-btn-font-size: #{$spacer * 0.75};
|
|
33
|
+
--bs-btn-line-height: #{$spacer};
|
|
34
|
+
--bs-btn-border-radius: var(--bs-border-radius-sm);
|
|
35
|
+
gap: #{$spacer * 0.25};
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
.btn-sm,
|
|
39
|
+
.btn-icon-sm,
|
|
40
|
+
.btn-group-sm > .btn,
|
|
41
|
+
.btn-group-sm > .btn-group > .btn {
|
|
42
|
+
--bs-btn-padding-y: #{$btn-padding-y-sm};
|
|
43
|
+
--bs-btn-padding-x: #{$btn-padding-x-sm};
|
|
44
|
+
--bs-btn-font-size: #{$btn-font-size-sm};
|
|
45
|
+
--bs-btn-line-height: #{$spacer};
|
|
46
|
+
--bs-btn-border-radius: var(--bs-border-radius-sm);
|
|
47
|
+
gap: #{$spacer * 0.25};
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
.btn-lg,
|
|
51
|
+
.btn-icon-lg,
|
|
52
|
+
.btn-group-lg > .btn,
|
|
53
|
+
.btn-group-lg > .btn-group > .btn {
|
|
54
|
+
--bs-btn-padding-y: #{$btn-padding-y-lg};
|
|
55
|
+
--bs-btn-padding-x: #{$btn-padding-x-lg};
|
|
56
|
+
--bs-btn-font-size: #{$btn-font-size-lg};
|
|
57
|
+
--bs-btn-line-height: #{$btn-line-height};
|
|
58
|
+
--bs-btn-border-radius: var(--bs-border-radius-lg);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
:is(.btn-xs, .btn-sm, .btn-group-sm > .btn, .btn-group-sm > .btn-group > .btn):not(.btn-icon, .btn-icon-xs, .btn-icon-sm, .btn-icon-lg):has(> [data-icon="inline-start"]) {
|
|
62
|
+
padding-inline-start: $icon-link-gap;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
:is(.btn-xs, .btn-sm, .btn-group-sm > .btn, .btn-group-sm > .btn-group > .btn):not(.btn-icon, .btn-icon-xs, .btn-icon-sm, .btn-icon-lg):has(> [data-icon="inline-end"]) {
|
|
66
|
+
padding-inline-end: $icon-link-gap;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// Icon-only buttons become squares whose side equals the token-driven
|
|
70
|
+
// button height (line-height + 2*padding-y + 2*border), so they always match
|
|
71
|
+
// the height of sibling text buttons. --bs-btn-line-height is a length in
|
|
72
|
+
// Moo, making this calc valid. No hard-coded dimensions.
|
|
73
|
+
.btn-icon {
|
|
74
|
+
// Icon-only buttons become squares whose side equals the token-driven
|
|
75
|
+
// button height (line-height + 2*padding-y + 2*border), so they always
|
|
76
|
+
// match the height of sibling text buttons. The default button exposes no
|
|
77
|
+
// --bs-btn-line-height / --bs-btn-padding-y custom properties (Bootstrap
|
|
78
|
+
// compiles those from Sass), so only the default size declares them here;
|
|
79
|
+
// xs/sm/lg already set their own values in their own blocks above and
|
|
80
|
+
// must not be overridden back to the default by this shared rule.
|
|
81
|
+
--bs-btn-line-height: #{$btn-line-height};
|
|
82
|
+
--bs-btn-padding-y: #{$btn-padding-y};
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
.btn-icon,
|
|
86
|
+
.btn-icon-xs,
|
|
87
|
+
.btn-icon-sm,
|
|
88
|
+
.btn-icon-lg {
|
|
89
|
+
// The side is computed once into --moo-btn-icon-size and consumed on both
|
|
90
|
+
// axes (and by .btn-group-vertical) so the two cannot diverge.
|
|
91
|
+
--moo-btn-icon-size: #{$moo-btn-icon-size};
|
|
92
|
+
padding-inline: 0;
|
|
93
|
+
inline-size: var(--moo-btn-icon-size);
|
|
94
|
+
block-size: var(--moo-btn-icon-size);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
:is(.btn-icon, .btn-icon-xs, .btn-icon-sm, .btn-icon-lg)::after {
|
|
98
|
+
display: none;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
.btn > [data-icon="inline-start"],
|
|
102
|
+
.btn > [data-icon="inline-end"] {
|
|
103
|
+
width: $spacer;
|
|
104
|
+
height: $spacer;
|
|
105
|
+
flex: 0 0 auto;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
:is(.btn-xs, .btn-icon-xs) > [data-icon] {
|
|
109
|
+
width: $spacer * 0.75;
|
|
110
|
+
height: $spacer * 0.75;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
:is(.btn-sm, .btn-icon-sm, .btn-group-sm > .btn, .btn-group-sm > .btn-group > .btn) > [data-icon] {
|
|
114
|
+
width: $font-size-base;
|
|
115
|
+
height: $font-size-base;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// The loading state swaps icon_start for the same loader-circle icon the
|
|
119
|
+
// standalone Spinner uses; aria-busy doubles as the accessible loading
|
|
120
|
+
// signal and the hook that scopes the spin to only that icon. The
|
|
121
|
+
// rotation uses Bootstrap's own spinner-border keyframe in the standalone
|
|
122
|
+
// build and the namespaced Moo keyframe in the scoped core build.
|
|
123
|
+
.btn[aria-busy="true"] > [data-icon="inline-start"] {
|
|
124
|
+
animation: $moo-spinner-animation-name $spinner-animation-speed linear infinite;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
.btn:disabled,
|
|
128
|
+
.btn.disabled,
|
|
129
|
+
.btn[aria-disabled="true"] {
|
|
130
|
+
pointer-events: none;
|
|
131
|
+
opacity: var(--moo-disabled-control-opacity);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
.btn-check:disabled + .btn {
|
|
135
|
+
pointer-events: none;
|
|
136
|
+
opacity: var(--moo-disabled-control-opacity);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// Replaces Bootstrap's native inset active shadow disabled in
|
|
140
|
+
// _bootstrap_overrides.scss; 0.5px keeps the press visible without shifting
|
|
141
|
+
// adjacent inline controls.
|
|
142
|
+
.btn:not(:disabled):not(.disabled):not([aria-disabled="true"]):active {
|
|
143
|
+
transform: translateY($moo-control-active-translate-y);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// Bootstrap consumes these properties; owned variants supply only exceptions.
|
|
147
|
+
:is(.btn-primary, .btn-secondary, .btn-outline-secondary, .btn-outline-primary, .btn-ghost, .btn-danger) {
|
|
148
|
+
--bs-btn-border-color: var(--bs-btn-bg);
|
|
149
|
+
--bs-btn-hover-color: var(--bs-btn-color);
|
|
150
|
+
--bs-btn-hover-border-color: var(--bs-btn-hover-bg);
|
|
151
|
+
--bs-btn-active-color: var(--bs-btn-hover-color);
|
|
152
|
+
--bs-btn-active-bg: var(--bs-btn-hover-bg);
|
|
153
|
+
--bs-btn-active-border-color: var(--bs-btn-active-bg);
|
|
154
|
+
--bs-btn-disabled-color: var(--bs-btn-color);
|
|
155
|
+
--bs-btn-disabled-border-color: var(--bs-btn-border-color);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
:is(.btn-primary, .btn-secondary, .btn-danger) {
|
|
159
|
+
--bs-btn-disabled-bg: var(--bs-btn-bg);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
:is(.btn-outline-secondary, .btn-outline-primary, .btn-ghost) {
|
|
163
|
+
--bs-btn-disabled-bg: transparent;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
.btn-primary {
|
|
167
|
+
--bs-btn-color: var(--moo-primary-foreground);
|
|
168
|
+
--bs-btn-bg: var(--moo-primary);
|
|
169
|
+
--bs-btn-hover-bg: color-mix(in srgb, var(--moo-primary) 80%, var(--moo-surface));
|
|
170
|
+
--bs-btn-active-bg: color-mix(in srgb, var(--moo-primary) 72%, var(--moo-surface));
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
.btn-secondary {
|
|
174
|
+
--bs-btn-color: var(--moo-foreground);
|
|
175
|
+
// The reference's secondary button is the surface color (white in light)
|
|
176
|
+
// with a hairline border (bg-background + border-border), not a tinted
|
|
177
|
+
// fill; the shared muted surface stays the hover/active step.
|
|
178
|
+
--bs-btn-bg: var(--moo-surface);
|
|
179
|
+
--bs-btn-border-color: var(--moo-border);
|
|
180
|
+
--bs-btn-hover-bg: color-mix(in srgb, var(--moo-muted-surface) 95%, var(--moo-foreground));
|
|
181
|
+
--bs-btn-hover-border-color: color-mix(in srgb, var(--moo-border) 90%, var(--moo-foreground));
|
|
182
|
+
--bs-btn-active-bg: color-mix(in srgb, var(--moo-muted-surface) 88%, var(--moo-foreground));
|
|
183
|
+
--bs-btn-active-border-color: color-mix(in srgb, var(--moo-border) 82%, var(--moo-foreground));
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
.btn-outline-secondary {
|
|
187
|
+
--bs-btn-color: var(--moo-foreground);
|
|
188
|
+
--bs-btn-border-color: var(--moo-border);
|
|
189
|
+
--bs-btn-hover-bg: var(--moo-muted-surface);
|
|
190
|
+
--bs-btn-hover-border-color: var(--moo-border);
|
|
191
|
+
--bs-btn-active-border-color: var(--moo-border);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
.btn-outline-primary {
|
|
195
|
+
--bs-btn-color: var(--moo-foreground);
|
|
196
|
+
--bs-btn-border-color: var(--moo-foreground);
|
|
197
|
+
--bs-btn-hover-color: var(--moo-surface);
|
|
198
|
+
--bs-btn-hover-bg: var(--moo-foreground);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
.btn-ghost {
|
|
202
|
+
--bs-btn-color: var(--moo-foreground);
|
|
203
|
+
--bs-btn-bg: transparent;
|
|
204
|
+
--bs-btn-hover-bg: var(--moo-muted-surface);
|
|
205
|
+
--bs-btn-hover-border-color: transparent;
|
|
206
|
+
--bs-btn-active-bg: var(--bs-btn-hover-bg);
|
|
207
|
+
--bs-btn-active-color: var(--bs-btn-color);
|
|
208
|
+
--bs-btn-active-border-color: transparent;
|
|
209
|
+
--bs-btn-disabled-color: var(--moo-disabled-foreground);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
.btn-danger {
|
|
213
|
+
--bs-btn-color: var(--moo-destructive-foreground);
|
|
214
|
+
--bs-btn-bg: var(--moo-destructive-surface);
|
|
215
|
+
--bs-btn-border-color: transparent;
|
|
216
|
+
--bs-btn-hover-color: var(--moo-destructive-foreground);
|
|
217
|
+
--bs-btn-hover-bg: var(--moo-destructive-surface-hover);
|
|
218
|
+
--bs-btn-hover-border-color: transparent;
|
|
219
|
+
--bs-btn-active-color: var(--moo-destructive-foreground);
|
|
220
|
+
--bs-btn-active-bg: var(--moo-destructive-surface-hover);
|
|
221
|
+
--bs-btn-active-border-color: transparent;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
.btn-link {
|
|
225
|
+
--bs-btn-color: var(--moo-foreground);
|
|
226
|
+
--bs-btn-hover-color: var(--moo-muted-foreground);
|
|
227
|
+
text-underline-offset: $spacer * 0.25;
|
|
228
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
// Button Group adds no visual tokens of its own: Bootstrap's extend chain
|
|
2
|
+
// (group sizes extend the button sizes) plus the grouped size selectors in
|
|
3
|
+
// the button partial deliver one shared scale. Only RTL geometry lives here:
|
|
4
|
+
// joined borders keep LTR order while labels render bidi-correctly.
|
|
5
|
+
[dir="rtl"] .btn-group,
|
|
6
|
+
[dir="rtl"] .btn-toolbar {
|
|
7
|
+
direction: ltr;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
[dir="rtl"] .btn-group > .btn,
|
|
11
|
+
[dir="rtl"] .btn-toolbar > .btn {
|
|
12
|
+
unicode-bidi: plaintext;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
// Icon buttons in button groups should stay square, not stretch to fill
|
|
16
|
+
// the group width (Bootstrap's flex: 1 1 auto makes them rectangular).
|
|
17
|
+
.btn-group > .btn-icon,
|
|
18
|
+
.btn-group > .btn-icon-xs,
|
|
19
|
+
.btn-group > .btn-icon-sm,
|
|
20
|
+
.btn-group > .btn-icon-lg,
|
|
21
|
+
.btn-group-vertical > .btn-icon,
|
|
22
|
+
.btn-group-vertical > .btn-icon-xs,
|
|
23
|
+
.btn-group-vertical > .btn-icon-sm,
|
|
24
|
+
.btn-group-vertical > .btn-icon-lg {
|
|
25
|
+
flex: 0 0 auto;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// Bootstrap's .btn-group-vertical > .btn { width: 100% } would stretch icon
|
|
29
|
+
// buttons; re-assert the token-derived square so vertical rails stay square.
|
|
30
|
+
.btn-group-vertical > .btn-icon,
|
|
31
|
+
.btn-group-vertical > .btn-icon-xs,
|
|
32
|
+
.btn-group-vertical > .btn-icon-sm,
|
|
33
|
+
.btn-group-vertical > .btn-icon-lg {
|
|
34
|
+
width: var(--moo-btn-icon-size);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Select inside a button group: compact and content-sized, matching the
|
|
38
|
+
// compact currency selector pattern. Bootstrap's flex defaults would shrink
|
|
39
|
+
// it below its longest option; flex: 0 0 auto keeps the intrinsic (content)
|
|
40
|
+
// width. The min-width floor derives from Bootstrap's form-select caret-space
|
|
41
|
+
// token, not magic numbers. Focus keeps Bootstrap's default visible treatment.
|
|
42
|
+
//
|
|
43
|
+
// The input-group override matches Bootstrap's own `.input-group > .form-select`
|
|
44
|
+
// specificity (0,2,0) which would otherwise stretch the select with flex: 1 1 auto.
|
|
45
|
+
.btn-group-select,
|
|
46
|
+
.input-group > .btn-group-select {
|
|
47
|
+
flex: 0 0 auto;
|
|
48
|
+
width: auto;
|
|
49
|
+
min-width: calc(#{$form-select-indicator-padding} + #{$spacer});
|
|
50
|
+
padding-inline-end: $form-select-indicator-padding;
|
|
51
|
+
}
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
// Card: bridge Bootstrap's consumption variables to shared primitives
|
|
2
|
+
// (UI-wide shadow and radius) and runtime theme tokens, then add the minimal
|
|
3
|
+
// structure that gives Bootstrap's native card the reference silhouette.
|
|
4
|
+
// The reference card is a rounded surface with a hairline ring and a
|
|
5
|
+
// theme-adaptive muted footer tint; its geometry is the source of the values
|
|
6
|
+
// chosen here (verified against the live reference page).
|
|
7
|
+
.card {
|
|
8
|
+
--bs-card-bg: color-mix(in srgb, var(--bs-secondary-bg) #{$moo-card-bg-mix}, var(--bs-body-bg));
|
|
9
|
+
--bs-card-color: var(--moo-foreground);
|
|
10
|
+
--bs-card-border-color: color-mix(in srgb, var(--bs-body-color) #{$moo-card-border-mix}, #{$moo-card-border-mix-base});
|
|
11
|
+
// The reference uses a 14px corner; Bootstrap's shared radius-xl is the
|
|
12
|
+
// closest token and wins the delta (12px here) per the token-first rule.
|
|
13
|
+
--bs-card-border-radius: var(--bs-border-radius-xl);
|
|
14
|
+
--bs-card-inner-border-radius: calc(var(--bs-card-border-radius) - var(--bs-card-border-width));
|
|
15
|
+
--bs-card-box-shadow: var(--bs-box-shadow-sm);
|
|
16
|
+
--bs-card-cap-bg: transparent;
|
|
17
|
+
--bs-card-cap-color: var(--moo-foreground);
|
|
18
|
+
--bs-card-title-color: var(--moo-foreground);
|
|
19
|
+
--bs-card-subtitle-color: var(--moo-muted-foreground);
|
|
20
|
+
--moo-card-footer-bg: #{$moo-card-footer-bg};
|
|
21
|
+
// The reference card lets consumers retune section rhythm and inset in one
|
|
22
|
+
// place; the default matches the 16px step Bootstrap's own spacer scale
|
|
23
|
+
// already carries. Declared on the selector that consumes it, like
|
|
24
|
+
// Bootstrap's own component custom properties.
|
|
25
|
+
--moo-card-spacing: #{$moo-card-spacing};
|
|
26
|
+
|
|
27
|
+
// One vertical rhythm owned by the card; sections add only inline padding.
|
|
28
|
+
gap: var(--moo-card-spacing);
|
|
29
|
+
padding-block: var(--moo-card-spacing);
|
|
30
|
+
|
|
31
|
+
// A footer strip owns its own bottom padding; with one present the card
|
|
32
|
+
// drops its trailing rhythm so the muted strip touches the card edge
|
|
33
|
+
// (mirrors the reference's has-data-[slot=card-footer]:pb-0).
|
|
34
|
+
&:has(.card-footer) {
|
|
35
|
+
padding-block-end: 0;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// A leading image frame owns the top edge; with one present the card drops
|
|
39
|
+
// its leading rhythm so the image touches the card corner (mirrors the
|
|
40
|
+
// reference's has-[>img:first-child]:pt-0).
|
|
41
|
+
&:has(> .card-image) {
|
|
42
|
+
padding-block-start: 0;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Keep the Bootstrap border box visible with a soft tokenized color, then
|
|
46
|
+
// let the shared shadow token supply the subtle card depth.
|
|
47
|
+
border: var(--bs-card-border-width) solid var(--bs-card-border-color);
|
|
48
|
+
box-shadow: var(--bs-card-box-shadow);
|
|
49
|
+
|
|
50
|
+
&[data-bs-theme="dark"],
|
|
51
|
+
:where([data-bs-theme="dark"]) &:not([data-bs-theme="light"]):not([data-bs-theme="light"] *) {
|
|
52
|
+
--bs-card-bg: color-mix(in srgb, var(--bs-secondary-bg) #{$moo-card-bg-mix-dark}, var(--bs-body-bg));
|
|
53
|
+
--moo-card-footer-bg: #{$moo-card-footer-bg-dark};
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
.card--sm {
|
|
58
|
+
--moo-card-spacing: #{$moo-card-spacing-sm};
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// The reference header lays title/description in a grid; an optional action
|
|
62
|
+
// occupies the trailing auto column on the same row as the title.
|
|
63
|
+
.card-header {
|
|
64
|
+
display: grid;
|
|
65
|
+
grid-template-columns: 1fr auto;
|
|
66
|
+
column-gap: var(--moo-card-spacing);
|
|
67
|
+
align-items: flex-start;
|
|
68
|
+
border-bottom: 0;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
.card-header__text {
|
|
72
|
+
display: flex;
|
|
73
|
+
flex-direction: column;
|
|
74
|
+
gap: $spacer * 0.25;
|
|
75
|
+
min-width: 0;
|
|
76
|
+
grid-column: 1;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
.card-action {
|
|
80
|
+
grid-column: 2;
|
|
81
|
+
grid-row: 1;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
.card-header,
|
|
85
|
+
.card-body,
|
|
86
|
+
.card-footer {
|
|
87
|
+
padding-block: 0;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
.card-header,
|
|
91
|
+
.card-body {
|
|
92
|
+
padding-inline: var(--moo-card-spacing);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// Use a separately mixed footer surface so tuning the main card fill does not
|
|
96
|
+
// collapse the footer contrast.
|
|
97
|
+
.card-footer {
|
|
98
|
+
display: flex;
|
|
99
|
+
align-items: center;
|
|
100
|
+
gap: $spacer * 0.5;
|
|
101
|
+
padding: var(--moo-card-spacing);
|
|
102
|
+
border-top: var(--bs-card-border-width) solid var(--bs-card-border-color);
|
|
103
|
+
background-color: var(--moo-card-footer-bg);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
.card-img-top {
|
|
107
|
+
// The reference clips a leading image to the card's top corners; the shared
|
|
108
|
+
// radius keeps it aligned with the rounded surface.
|
|
109
|
+
border-top-left-radius: var(--bs-card-border-radius);
|
|
110
|
+
border-top-right-radius: var(--bs-card-border-radius);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// A leading image sits in a 16:9 frame; the frame owns a theme-adaptive
|
|
114
|
+
// surface so a non-cover image never leaves an unstyled gap.
|
|
115
|
+
.card-image {
|
|
116
|
+
aspect-ratio: 16 / 9;
|
|
117
|
+
overflow: hidden;
|
|
118
|
+
border-top-left-radius: var(--bs-card-border-radius);
|
|
119
|
+
border-top-right-radius: var(--bs-card-border-radius);
|
|
120
|
+
background-color: var(--moo-muted-surface);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
.card-image .card-img-top {
|
|
124
|
+
width: 100%;
|
|
125
|
+
height: 100%;
|
|
126
|
+
object-fit: cover;
|
|
127
|
+
border-radius: 0;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
.card-title {
|
|
131
|
+
margin-bottom: 0;
|
|
132
|
+
font-size: $card-title-font-size;
|
|
133
|
+
font-weight: $headings-font-weight;
|
|
134
|
+
line-height: $line-height-sm;
|
|
135
|
+
letter-spacing: $card-title-letter-spacing;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
.card-subtitle {
|
|
139
|
+
margin-top: 0;
|
|
140
|
+
font-size: $font-size-base;
|
|
141
|
+
color: var(--moo-muted-foreground);
|
|
142
|
+
}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
// Bootstrap's margin-top (0.25rem = 4px) was computed for the old 1em
|
|
2
|
+
// checkbox (14px at the catalog's 0.875rem body font). The override to
|
|
3
|
+
// 1rem (16px) shifted the geometric center by 1px; tightening the top
|
|
4
|
+
// margin to 0.125rem (2px) re-centres the 16px control within the
|
|
5
|
+
// .form-check min-height so it aligns with the label text.
|
|
6
|
+
.form-check-input {
|
|
7
|
+
margin-top: 0.125rem;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
// Bootstrap's checkbox/radio glyphs are SVG data URIs whose stroke/fill color
|
|
11
|
+
// is fixed at compile time. Draw the checkbox mark as an overlay so it follows
|
|
12
|
+
// the runtime primary foreground when Theme color changes.
|
|
13
|
+
.form-check-input[type="checkbox"]:checked:not([role="switch"]) {
|
|
14
|
+
--#{$prefix}form-check-bg-image: none;
|
|
15
|
+
|
|
16
|
+
position: relative;
|
|
17
|
+
overflow: hidden;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
.form-check-input[type="checkbox"]:checked:not([role="switch"])::after {
|
|
21
|
+
position: absolute;
|
|
22
|
+
top: 45%;
|
|
23
|
+
left: 50%;
|
|
24
|
+
width: 0.32em;
|
|
25
|
+
height: 0.58em;
|
|
26
|
+
border-color: var(--moo-primary-foreground);
|
|
27
|
+
border-style: solid;
|
|
28
|
+
border-width: $moo-form-checkmark-border-width;
|
|
29
|
+
content: "";
|
|
30
|
+
transform: translate(-50%, -55%) rotate(45deg);
|
|
31
|
+
transform-origin: center;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
.form-check-input[type="checkbox"]:indeterminate:not([role="switch"]) {
|
|
35
|
+
--#{$prefix}form-check-bg-image: linear-gradient(var(--moo-primary-foreground), var(--moo-primary-foreground));
|
|
36
|
+
|
|
37
|
+
background-size: $moo-form-check-indeterminate-bg-size;
|
|
38
|
+
background-position: $moo-form-check-indeterminate-bg-position;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
.form-check-input[type="radio"]:checked {
|
|
42
|
+
--#{$prefix}form-check-bg-image: radial-gradient(circle at center, var(--moo-primary-foreground) 0 34%, transparent 36%);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Covers checkbox, switch (also type="checkbox"), and radio alike so
|
|
46
|
+
// mouse-triggered focus never shows a ring on any form-check control,
|
|
47
|
+
// matching Input/Textarea/Select's own :focus:not(:focus-visible) rule
|
|
48
|
+
// in foundations/_focus.scss. Invalid checkboxes are excluded — their
|
|
49
|
+
// persistent validation ring is restored by the rules at the bottom.
|
|
50
|
+
.form-check-input:is([type="checkbox"], [type="radio"]):not(.is-invalid):focus:active,
|
|
51
|
+
.form-check-input:is([type="checkbox"], [type="radio"]):not(.is-invalid):focus:not(:focus-visible) {
|
|
52
|
+
border-color: var(--bs-border-color);
|
|
53
|
+
box-shadow: none;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
.form-check-input:is([type="checkbox"], [type="radio"]):checked:not(.is-invalid):focus:active,
|
|
57
|
+
.form-check-input:is([type="checkbox"], [type="radio"]):checked:not(.is-invalid):focus:not(:focus-visible) {
|
|
58
|
+
border-color: $form-check-input-checked-border-color;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
.form-check-input[type="checkbox"]:not(.is-invalid):indeterminate:focus:active,
|
|
62
|
+
.form-check-input[type="checkbox"]:not(.is-invalid):indeterminate:focus:not(:focus-visible) {
|
|
63
|
+
border-color: $form-check-input-indeterminate-border-color;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Invalid ("destructive") checkboxes are an exception to the mouse-click
|
|
67
|
+
// focus-ring suppression above. The validation ring stays visible at all
|
|
68
|
+
// times — not just on :focus — so the error state is never silently
|
|
69
|
+
// dismissed. This matches the reference's aria-invalid ring pattern and
|
|
70
|
+
// mirrors the destructive ring that foundations/_focus.scss already
|
|
71
|
+
// paints on .form-control.is-invalid and .form-select.is-invalid. Border
|
|
72
|
+
// color is left to Bootstrap's own native .is-invalid rule (compiled from
|
|
73
|
+
// vendor/bootstrap/scss/mixins/_forms.scss's form-validation-state(),
|
|
74
|
+
// already themed to --bs-form-invalid-border-color); only the ring is
|
|
75
|
+
// added on top of it here.
|
|
76
|
+
.form-check-input.is-invalid {
|
|
77
|
+
box-shadow: 0 0 0 #{$moo-form-focus-ring-width} var(--moo-form-invalid-ring-color);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// The focus-suppression rules above have specificity (0,4,0) which
|
|
81
|
+
// beats .is-invalid's (0,2,0). These :focus variants sit after the
|
|
82
|
+
// suppression block and carry (0,5,0), so the destructive ring stays
|
|
83
|
+
// visible during mouse click and programmatic focus alike.
|
|
84
|
+
.form-check-input.is-invalid:focus:active,
|
|
85
|
+
.form-check-input.is-invalid:focus:not(:focus-visible) {
|
|
86
|
+
box-shadow: 0 0 0 #{$moo-form-focus-ring-width} var(--moo-form-invalid-ring-color);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
.form-check-input:is([type="checkbox"], [type="radio"]).is-invalid:checked:focus:active,
|
|
90
|
+
.form-check-input:is([type="checkbox"], [type="radio"]).is-invalid:checked:focus:not(:focus-visible) {
|
|
91
|
+
box-shadow: 0 0 0 #{$moo-form-focus-ring-width} var(--moo-form-invalid-ring-color);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
.form-check-input[type="checkbox"].is-invalid:indeterminate:focus:active,
|
|
95
|
+
.form-check-input[type="checkbox"].is-invalid:indeterminate:focus:not(:focus-visible) {
|
|
96
|
+
box-shadow: 0 0 0 #{$moo-form-focus-ring-width} var(--moo-form-invalid-ring-color);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// The reference keeps a checked control's own border and fill at the
|
|
100
|
+
// normal checked color even inside an invalid group -- only unchecked
|
|
101
|
+
// siblings' solid border and the ring (kept above) flag the error, so the
|
|
102
|
+
// user's actual selection never reads as "wrong" by itself. Overrides two
|
|
103
|
+
// separate Bootstrap native rules: the base .is-invalid border-color
|
|
104
|
+
// above (which does not itself distinguish :checked) and .is-invalid:
|
|
105
|
+
// checked's own background-color: var(--bs-form-invalid-color) (also
|
|
106
|
+
// compiled from form-validation-state(), which paints a checked+invalid
|
|
107
|
+
// control's fill destructive-red by default). Repeats the :focus variants
|
|
108
|
+
// verbatim so this wins the same specificity tie by source order, focused
|
|
109
|
+
// or not.
|
|
110
|
+
.form-check-input:is([type="checkbox"], [type="radio"]):not([role="switch"]).is-invalid:checked,
|
|
111
|
+
.form-check-input:is([type="checkbox"], [type="radio"]):not([role="switch"]).is-invalid:checked:focus:active,
|
|
112
|
+
.form-check-input:is([type="checkbox"], [type="radio"]):not([role="switch"]).is-invalid:checked:focus:not(:focus-visible) {
|
|
113
|
+
border-color: var(--moo-primary);
|
|
114
|
+
background-color: var(--moo-primary);
|
|
115
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
.btn-close {
|
|
2
|
+
--bs-btn-close-color: var(--moo-foreground);
|
|
3
|
+
--bs-btn-hover-color: var(--bs-btn-close-color);
|
|
4
|
+
--bs-btn-hover-bg: var(--moo-muted-surface);
|
|
5
|
+
--bs-btn-hover-border-color: #{$moo-close-button-border-color};
|
|
6
|
+
// Replace Bootstrap's filled-path X with a Lucide-style stroke-based icon
|
|
7
|
+
// (thinner, more delicate — matches the reference's feel).
|
|
8
|
+
--bs-btn-close-bg: #{escape-svg($moo-close-button-bg-image)};
|
|
9
|
+
|
|
10
|
+
display: inline-flex;
|
|
11
|
+
flex: 0 0 auto;
|
|
12
|
+
align-items: center;
|
|
13
|
+
justify-content: center;
|
|
14
|
+
border-width: var(--bs-border-width);
|
|
15
|
+
border-style: var(--bs-border-style);
|
|
16
|
+
border-color: $moo-close-button-border-color;
|
|
17
|
+
border-radius: var(--bs-border-radius-xl);
|
|
18
|
+
color: var(--bs-btn-close-color);
|
|
19
|
+
background-image: none;
|
|
20
|
+
transition: $btn-transition;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
.btn-close::before {
|
|
24
|
+
width: $btn-close-width;
|
|
25
|
+
height: $btn-close-height;
|
|
26
|
+
content: "";
|
|
27
|
+
background-color: currentColor;
|
|
28
|
+
-webkit-mask: var(--bs-btn-close-bg) center / contain no-repeat;
|
|
29
|
+
mask: var(--bs-btn-close-bg) center / contain no-repeat;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// Bootstrap's dark rule filters the whole element to recolor its background
|
|
33
|
+
// image. The glyph above uses currentColor through a mask, so filtering would
|
|
34
|
+
// incorrectly invert the token-driven hover surface as well.
|
|
35
|
+
[data-bs-theme="dark"] .btn-close {
|
|
36
|
+
filter: none;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
.btn-close:hover {
|
|
40
|
+
border-color: var(--bs-btn-hover-border-color);
|
|
41
|
+
color: var(--bs-btn-hover-color);
|
|
42
|
+
background-color: var(--bs-btn-hover-bg);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
.btn-close:focus-visible {
|
|
46
|
+
border-color: var(--moo-ring);
|
|
47
|
+
color: var(--bs-btn-hover-color);
|
|
48
|
+
background-color: var(--bs-btn-hover-bg);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
.btn-close:focus:not(:focus-visible) {
|
|
52
|
+
box-shadow: none;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
.btn-close:disabled,
|
|
56
|
+
.btn-close.disabled {
|
|
57
|
+
pointer-events: none;
|
|
58
|
+
opacity: var(--moo-disabled-control-opacity);
|
|
59
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
.collapsible {
|
|
2
|
+
display: flex;
|
|
3
|
+
flex-direction: column;
|
|
4
|
+
gap: $spacer * 0.5;
|
|
5
|
+
width: 100%;
|
|
6
|
+
max-width: 100%;
|
|
7
|
+
min-width: 0;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
.collapsible-header {
|
|
11
|
+
display: flex;
|
|
12
|
+
align-items: center;
|
|
13
|
+
justify-content: space-between;
|
|
14
|
+
padding-inline: $spacer * 0.5;
|
|
15
|
+
gap: $spacer * 0.5;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
.collapsible-title {
|
|
19
|
+
margin-bottom: 0;
|
|
20
|
+
font-size: $font-size-sm;
|
|
21
|
+
font-weight: $font-weight-medium;
|
|
22
|
+
color: var(--moo-foreground);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
.collapsible .btn[data-bs-toggle='collapse'] {
|
|
26
|
+
color: var(--moo-muted-foreground);
|
|
27
|
+
line-height: 1;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
.collapsible .btn[data-bs-toggle='collapse'] [data-icon] {
|
|
31
|
+
width: $font-size-base;
|
|
32
|
+
height: $font-size-base;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
.collapsible-content {
|
|
37
|
+
padding: $spacer * 0.5 $spacer * 0.75;
|
|
38
|
+
border: var(--bs-border-width) solid var(--bs-border-color);
|
|
39
|
+
border-radius: var(--bs-border-radius);
|
|
40
|
+
font-size: $font-size-sm;
|
|
41
|
+
color: var(--moo-muted-foreground);
|
|
42
|
+
width: 100%;
|
|
43
|
+
}
|