@wpmoo/ui 1.0.0-rc.6 → 1.0.0-rc.8
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 +1 -1
- package/README.md +37 -6
- package/certification.json +6 -2
- package/dist/assets/css/moo-ui.css +7799 -8113
- package/dist/assets/css/moo-ui.min.css +5 -5
- package/dist/assets/css/moo.css +707 -685
- package/dist/assets/css/moo.min.css +5 -5
- package/dist/js/chart.js +57 -5
- package/dist/js/chart.min.js +3 -3
- package/dist/js/combobox.js +1 -1
- package/dist/js/context-menu.js +1 -1
- package/dist/js/datatable.js +114 -213
- package/dist/js/datepicker.js +79 -37
- package/dist/js/datepicker.min.js +2 -2
- package/dist/js/moo-ui.js +169 -52
- package/dist/js/moo-ui.min.js +3 -3
- package/dist/js/sidebar.js +122 -175
- package/dist/js/slider.js +1 -1
- package/dist/js/theme-prepaint.js +91 -0
- package/dist/release-manifest.json +24 -0
- package/package.json +12 -8
- package/scss/_components.scss +1 -4
- package/scss/components/_alert.scss +1 -2
- package/scss/components/_card.scss +5 -5
- package/scss/components/_close_button.scss +8 -3
- package/scss/components/_dialog.scss +16 -0
- package/scss/components/_dropdown.scss +12 -7
- package/scss/components/_search_trigger.scss +62 -0
- package/scss/components/_sidebar.scss +1 -2
- package/scss/components/_switch.scss +13 -5
- package/scss/components/_tabs.scss +1 -1
- package/scss/components/sidebar/_base.scss +93 -0
- package/scss/components/sidebar/_collapsed.scss +9 -90
- package/scss/foundations/_backdrop.scss +128 -0
- package/scss/foundations/_scope.scss +8 -0
- package/scss/layouts/_app.scss +77 -0
- package/scss/layouts/app/sidebar/_base.scss +117 -0
- package/scss/layouts/app/sidebar/_default.scss +26 -0
- package/scss/layouts/app/sidebar/_floating.scss +44 -0
- package/scss/layouts/app/sidebar/_inset.scss +29 -0
- package/scss/mixins/_banner.scss +3 -0
- package/scss/moo-core.scss +6 -34
- package/scss/moo-ui.scss +6 -6
- package/scss/settings/_bootstrap_overrides.scss +4 -0
- package/scss/settings/_component_variables.scss +20 -1
- package/scss/settings/_options.scss +2 -0
- package/scss/settings/_palette.scss +1 -1
- package/scss/themes/_forms.scss +15 -0
- package/scss/themes/_root.scss +16 -0
- package/scss/themes/_standalone.scss +5 -0
- package/scss/themes/{_scoped_core.scss → _theme.scss} +1 -0
- package/scss/components/sidebar/_inset.scss +0 -59
- package/scss/components/sidebar/_layout.scss +0 -176
- package/scss/foundations/_core_state_layer.scss +0 -25
- package/scss/foundations/_overlay_backdrop.scss +0 -19
- package/scss/themes/_standalone_root.scss +0 -119
- /package/scss/foundations/{_core_global_primitives.scss → _globals.scss} +0 -0
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* Moo UI theme prepaint
|
|
3
|
+
* Copyright 2026 WPMoo (https://wpmoo.org)
|
|
4
|
+
* Licensed under MIT (https://github.com/wpmoo-org/ui/blob/main/LICENSE)
|
|
5
|
+
*/
|
|
6
|
+
(() => {
|
|
7
|
+
const ownerDocument =
|
|
8
|
+
typeof document === "undefined"
|
|
9
|
+
? null
|
|
10
|
+
: document;
|
|
11
|
+
const owner = ownerDocument?.currentScript?.parentElement;
|
|
12
|
+
const ownerSelector =
|
|
13
|
+
'.moo-ui[data-bs-theme="light"], .moo-ui[data-bs-theme="dark"]';
|
|
14
|
+
|
|
15
|
+
if (!owner?.matches?.(ownerSelector)) return;
|
|
16
|
+
|
|
17
|
+
const body = ownerDocument?.body;
|
|
18
|
+
const documentOwner =
|
|
19
|
+
owner.dataset?.mooDocumentOwner === "true" &&
|
|
20
|
+
owner.parentElement === body &&
|
|
21
|
+
owner === body?.firstElementChild;
|
|
22
|
+
const view =
|
|
23
|
+
ownerDocument?.defaultView ||
|
|
24
|
+
(typeof window === "undefined" ? null : window);
|
|
25
|
+
const normalizeDirection = (value) =>
|
|
26
|
+
value === "ltr" || value === "rtl" ? value : null;
|
|
27
|
+
const ownerDirection = normalizeDirection(
|
|
28
|
+
owner.getAttribute?.("dir") || owner.dir,
|
|
29
|
+
);
|
|
30
|
+
const documentDirection = normalizeDirection(
|
|
31
|
+
ownerDocument?.documentElement?.getAttribute?.("dir") ||
|
|
32
|
+
ownerDocument?.documentElement?.dir,
|
|
33
|
+
);
|
|
34
|
+
owner.__mooPrepaintBaseline = {
|
|
35
|
+
theme: owner.dataset.bsTheme === "dark" ? "dark" : "light",
|
|
36
|
+
direction: documentOwner ? documentDirection || "ltr" : ownerDirection,
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
const readPreference = (axis, allowed) => {
|
|
40
|
+
const dataKey = axis === "theme" ? "mooThemeKey" : "mooDirectionKey";
|
|
41
|
+
const explicit = owner.dataset?.[dataKey]?.trim();
|
|
42
|
+
const key = explicit || (documentOwner ? `moo:${axis}` : null);
|
|
43
|
+
if (!key) return null;
|
|
44
|
+
|
|
45
|
+
try {
|
|
46
|
+
const value = view?.localStorage?.getItem(key);
|
|
47
|
+
return allowed.includes(value) ? value : null;
|
|
48
|
+
} catch (_) {
|
|
49
|
+
return null;
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
const systemPrefersDark = () => {
|
|
54
|
+
try {
|
|
55
|
+
const matchMedia = view?.matchMedia;
|
|
56
|
+
if (typeof matchMedia !== "function") return null;
|
|
57
|
+
return Boolean(
|
|
58
|
+
matchMedia.call(view, "(prefers-color-scheme: dark)")?.matches,
|
|
59
|
+
);
|
|
60
|
+
} catch (_) {
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
try {
|
|
66
|
+
const storedTheme = readPreference("theme", ["light", "dark", "system"]);
|
|
67
|
+
const mediaPrefersDark =
|
|
68
|
+
storedTheme === "system" ? systemPrefersDark() : null;
|
|
69
|
+
const resolvedTheme =
|
|
70
|
+
storedTheme === "dark"
|
|
71
|
+
? "dark"
|
|
72
|
+
: storedTheme === "light"
|
|
73
|
+
? "light"
|
|
74
|
+
: storedTheme === "system" && mediaPrefersDark !== null
|
|
75
|
+
? mediaPrefersDark
|
|
76
|
+
? "dark"
|
|
77
|
+
: "light"
|
|
78
|
+
: owner.dataset.bsTheme === "dark"
|
|
79
|
+
? "dark"
|
|
80
|
+
: "light";
|
|
81
|
+
owner.dataset.bsTheme = resolvedTheme;
|
|
82
|
+
|
|
83
|
+
const storedDirection = readPreference("direction", ["ltr", "rtl"]);
|
|
84
|
+
if (storedDirection) {
|
|
85
|
+
if (documentOwner) ownerDocument.documentElement.dir = storedDirection;
|
|
86
|
+
else owner.dir = storedDirection;
|
|
87
|
+
}
|
|
88
|
+
} finally {
|
|
89
|
+
owner.dataset.mooPrepaint = "ready";
|
|
90
|
+
}
|
|
91
|
+
})();
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
{
|
|
2
|
+
"artifacts": [
|
|
3
|
+
{
|
|
4
|
+
"export": "./moo.css",
|
|
5
|
+
"path": "dist/assets/css/moo.css",
|
|
6
|
+
"sha256": "29c92bd75db1e4990745796aeef4d6a0b3313d1b9d2e3c929f559f8687a5fe6f"
|
|
7
|
+
},
|
|
8
|
+
{
|
|
9
|
+
"export": "./moo-ui.css",
|
|
10
|
+
"path": "dist/assets/css/moo-ui.css",
|
|
11
|
+
"sha256": "00db81fad96811182874b9207746a3154adb0f457c965a08e639a04af83f95a4"
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
"export": "./theme-prepaint.js",
|
|
15
|
+
"path": "dist/js/theme-prepaint.js",
|
|
16
|
+
"sha256": "e40ce7caa83112498c0819719e63ee8d0f23f58236f02ec038ccabf7a128121a"
|
|
17
|
+
}
|
|
18
|
+
],
|
|
19
|
+
"package": {
|
|
20
|
+
"name": "@wpmoo/ui",
|
|
21
|
+
"version": "1.0.0-rc.8"
|
|
22
|
+
},
|
|
23
|
+
"schemaVersion": 1
|
|
24
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wpmoo/ui",
|
|
3
|
-
"version": "1.0.0-rc.
|
|
3
|
+
"version": "1.0.0-rc.8",
|
|
4
4
|
"description": "Bootstrap markup. shadcn feel. A frontend-framework-free component system for server-rendered apps.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "WPMoo (https://wpmoo.org)",
|
|
@@ -15,13 +15,13 @@
|
|
|
15
15
|
"private": false,
|
|
16
16
|
"type": "module",
|
|
17
17
|
"scripts": {
|
|
18
|
-
"build": ".venv/bin/
|
|
19
|
-
"dev": ".venv/bin/
|
|
20
|
-
"test": ".venv/bin/
|
|
21
|
-
"test:quick": ".venv/bin/
|
|
22
|
-
"test:browser-smoke": ".venv/bin/
|
|
23
|
-
"test:browser-full": ".venv/bin/
|
|
24
|
-
"test:release": ".venv/bin/
|
|
18
|
+
"build": ".venv/bin/python3 build.py",
|
|
19
|
+
"dev": ".venv/bin/python3 dev.py",
|
|
20
|
+
"test": ".venv/bin/python3 scripts/run-test-tier.py run release",
|
|
21
|
+
"test:quick": ".venv/bin/python3 scripts/run-test-tier.py run quick",
|
|
22
|
+
"test:browser-smoke": ".venv/bin/python3 scripts/run-test-tier.py run browser-smoke",
|
|
23
|
+
"test:browser-full": ".venv/bin/python3 scripts/run-test-tier.py run browser-full",
|
|
24
|
+
"test:release": ".venv/bin/python3 scripts/run-test-tier.py run release",
|
|
25
25
|
"deploy": "wrangler deploy",
|
|
26
26
|
"preview": "wrangler dev"
|
|
27
27
|
},
|
|
@@ -42,6 +42,8 @@
|
|
|
42
42
|
"./chart.min.js": "./dist/js/chart.min.js",
|
|
43
43
|
"./datepicker.js": "./dist/js/datepicker.js",
|
|
44
44
|
"./datepicker.min.js": "./dist/js/datepicker.min.js",
|
|
45
|
+
"./theme-prepaint.js": "./dist/js/theme-prepaint.js",
|
|
46
|
+
"./release-manifest.json": "./dist/release-manifest.json",
|
|
45
47
|
"./scss/config": "./scss/_config.scss",
|
|
46
48
|
"./scss/moo-ui": "./scss/moo-ui.scss",
|
|
47
49
|
"./scss/moo-core": "./scss/moo-core.scss",
|
|
@@ -66,6 +68,8 @@
|
|
|
66
68
|
"dist/js/chart.min.js",
|
|
67
69
|
"dist/js/datepicker.js",
|
|
68
70
|
"dist/js/datepicker.min.js",
|
|
71
|
+
"dist/js/theme-prepaint.js",
|
|
72
|
+
"dist/release-manifest.json",
|
|
69
73
|
"scss/*.scss",
|
|
70
74
|
"scss/**/*.scss",
|
|
71
75
|
"certification.json",
|
package/scss/_components.scss
CHANGED
|
@@ -25,10 +25,6 @@
|
|
|
25
25
|
@import "bootstrap/scss/offcanvas";
|
|
26
26
|
@import "bootstrap/scss/helpers/color-bg";
|
|
27
27
|
|
|
28
|
-
// Moo component support
|
|
29
|
-
@import "foundations/focus";
|
|
30
|
-
@import "utilities/scroll_fade";
|
|
31
|
-
|
|
32
28
|
// Moo components
|
|
33
29
|
@import "components/kbd";
|
|
34
30
|
@import "components/input";
|
|
@@ -36,6 +32,7 @@
|
|
|
36
32
|
@import "components/select";
|
|
37
33
|
@import "components/input_group";
|
|
38
34
|
@import "components/button";
|
|
35
|
+
@import "components/search_trigger";
|
|
39
36
|
@import "components/close_button";
|
|
40
37
|
@import "components/button_group";
|
|
41
38
|
@import "components/toggle_group";
|
|
@@ -24,10 +24,9 @@ $moo-alert-dismissible-close-space: $btn-close-width + ($btn-close-padding-x * 2
|
|
|
24
24
|
.alert-dismissible .btn-close {
|
|
25
25
|
top: auto;
|
|
26
26
|
right: auto;
|
|
27
|
-
inset-block-start:
|
|
27
|
+
inset-block-start: var(--bs-alert-padding-y);
|
|
28
28
|
inset-inline-end: var(--bs-alert-padding-x);
|
|
29
29
|
padding: $btn-close-padding-y $btn-close-padding-x;
|
|
30
|
-
transform: translateY(-50%);
|
|
31
30
|
}
|
|
32
31
|
|
|
33
32
|
// The reference's destructive alert keeps the same neutral surface and
|
|
@@ -47,11 +47,11 @@
|
|
|
47
47
|
border: var(--bs-card-border-width) solid var(--bs-card-border-color);
|
|
48
48
|
box-shadow: var(--bs-card-box-shadow);
|
|
49
49
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
:scope[data-bs-theme="dark"] .card {
|
|
53
|
+
--bs-card-bg: color-mix(in srgb, var(--bs-secondary-bg) #{$moo-card-bg-mix-dark}, var(--bs-body-bg));
|
|
54
|
+
--moo-card-footer-bg: #{$moo-card-footer-bg-dark};
|
|
55
55
|
}
|
|
56
56
|
|
|
57
57
|
.card--sm {
|
|
@@ -14,7 +14,9 @@
|
|
|
14
14
|
border-width: var(--bs-border-width);
|
|
15
15
|
border-style: var(--bs-border-style);
|
|
16
16
|
border-color: $moo-close-button-border-color;
|
|
17
|
-
|
|
17
|
+
// Keep the square hit area visually circular in every context, including
|
|
18
|
+
// modal and offcanvas headers.
|
|
19
|
+
border-radius: var(--bs-border-radius-pill);
|
|
18
20
|
color: var(--bs-btn-close-color);
|
|
19
21
|
background-image: none;
|
|
20
22
|
transition: $btn-transition;
|
|
@@ -31,8 +33,11 @@
|
|
|
31
33
|
|
|
32
34
|
// Bootstrap's dark rule filters the whole element to recolor its background
|
|
33
35
|
// image. The glyph above uses currentColor through a mask, so filtering would
|
|
34
|
-
// incorrectly invert the token-driven hover surface as well.
|
|
35
|
-
|
|
36
|
+
// incorrectly invert the token-driven hover surface as well. Both resolved
|
|
37
|
+
// owner modes override it so an outer dark owner cannot affect an inner light
|
|
38
|
+
// owner through Bootstrap's global descendant selector.
|
|
39
|
+
:scope[data-bs-theme="light"] .btn-close,
|
|
40
|
+
:scope[data-bs-theme="dark"] .btn-close {
|
|
36
41
|
filter: none;
|
|
37
42
|
}
|
|
38
43
|
|
|
@@ -34,6 +34,22 @@
|
|
|
34
34
|
gap: calc(var(--bs-modal-padding) * 0.75);
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
+
.modal-header:has(> .btn-close) {
|
|
38
|
+
// Keep the dismiss control in a stable top/end corner, like the reference
|
|
39
|
+
// dialog surface, while reserving room so long titles cannot run beneath it.
|
|
40
|
+
position: relative;
|
|
41
|
+
padding-inline-end: calc(
|
|
42
|
+
var(--bs-modal-padding) + #{$btn-close-width} + #{$btn-close-padding-x * 2} + 0.5rem
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
.modal-header:has(> .btn-close) > .btn-close {
|
|
47
|
+
position: absolute;
|
|
48
|
+
inset-block-start: 0.5rem;
|
|
49
|
+
inset-inline-end: 0.5rem;
|
|
50
|
+
margin: 0;
|
|
51
|
+
}
|
|
52
|
+
|
|
37
53
|
.modal--alert {
|
|
38
54
|
--bs-modal-width: 24rem;
|
|
39
55
|
--bs-modal-padding: 1rem;
|
|
@@ -167,6 +167,16 @@
|
|
|
167
167
|
min-width: #{unquote("max(100%, var(--moo-dropdown-sidebar-min-width))")};
|
|
168
168
|
}
|
|
169
169
|
|
|
170
|
+
// Header identity menus reuse the Sidebar account-menu body. Keep the same
|
|
171
|
+
// comfortable width when the shared body is hosted by the global navbar
|
|
172
|
+
// instead of a Sidebar slot; ordinary Header dropdowns remain content-sized.
|
|
173
|
+
// The trigger marker is the integration contract, so Dropdown does not reach
|
|
174
|
+
// into Sidebar's private class namespace to identify the menu body.
|
|
175
|
+
[data-identity-trigger="1"] + .dropdown-menu {
|
|
176
|
+
--moo-dropdown-sidebar-min-width: #{$moo-dropdown-sidebar-min-width};
|
|
177
|
+
min-width: var(--moo-dropdown-sidebar-min-width);
|
|
178
|
+
}
|
|
179
|
+
|
|
170
180
|
@include media-breakpoint-down(sm) {
|
|
171
181
|
[data-slot="sidebar-header"] .dropend > .dropdown-menu,
|
|
172
182
|
[data-slot="sidebar-footer"] .dropend > .dropdown-menu {
|
|
@@ -217,8 +227,8 @@
|
|
|
217
227
|
[data-slot="sidebar-footer"] [data-sidebar-dropdown-positioned] > .dropdown-menu {
|
|
218
228
|
position: fixed !important;
|
|
219
229
|
z-index: $zindex-dropdown;
|
|
220
|
-
|
|
221
|
-
|
|
230
|
+
left: var(--moo-sidebar-dropdown-left, auto) !important;
|
|
231
|
+
right: var(--moo-sidebar-dropdown-right, auto) !important;
|
|
222
232
|
width: var(--moo-dropdown-sidebar-min-width);
|
|
223
233
|
min-width: var(--moo-dropdown-sidebar-min-width);
|
|
224
234
|
margin: 0;
|
|
@@ -234,9 +244,4 @@
|
|
|
234
244
|
inset-block-end: var(--moo-sidebar-dropdown-block-end) !important;
|
|
235
245
|
}
|
|
236
246
|
|
|
237
|
-
[data-slot="sidebar"][data-side="right"] [data-slot="sidebar-header"] [data-sidebar-dropdown-positioned] > .dropdown-menu,
|
|
238
|
-
[data-slot="sidebar"][data-side="right"] [data-slot="sidebar-footer"] [data-sidebar-dropdown-positioned] > .dropdown-menu {
|
|
239
|
-
inset-inline-start: auto !important;
|
|
240
|
-
inset-inline-end: var(--moo-sidebar-dropdown-inline-end, auto) !important;
|
|
241
|
-
}
|
|
242
247
|
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
// Shared command/search trigger composition. The host supplies the semantic
|
|
2
|
+
// button markup and behavior; Core owns only the reusable visual contract.
|
|
3
|
+
.search-trigger {
|
|
4
|
+
width: $moo-search-trigger-width;
|
|
5
|
+
max-width: 100%;
|
|
6
|
+
flex: 0 1 $moo-search-trigger-width;
|
|
7
|
+
min-width: 0;
|
|
8
|
+
justify-content: space-between;
|
|
9
|
+
height: $moo-search-trigger-height;
|
|
10
|
+
padding: $moo-search-trigger-padding-y $moo-search-trigger-padding-x;
|
|
11
|
+
color: var(--bs-secondary-color);
|
|
12
|
+
border: var(--bs-border-width) solid $moo-search-trigger-border-color;
|
|
13
|
+
border-radius: var(--bs-border-radius);
|
|
14
|
+
background: color-mix(in srgb, var(--bs-secondary-bg) 55%, var(--bs-body-bg));
|
|
15
|
+
font-size: $font-size-base;
|
|
16
|
+
font-weight: $font-weight-normal;
|
|
17
|
+
box-shadow: none;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
.search-trigger:hover {
|
|
21
|
+
color: var(--bs-secondary-color);
|
|
22
|
+
border-color: $moo-search-trigger-border-color;
|
|
23
|
+
background: color-mix(in srgb, var(--bs-secondary-bg) 78%, var(--bs-body-bg));
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
.search-trigger:focus-visible {
|
|
27
|
+
color: var(--bs-secondary-color);
|
|
28
|
+
border-color: $moo-search-trigger-border-color;
|
|
29
|
+
background: color-mix(in srgb, var(--bs-secondary-bg) 78%, var(--bs-body-bg));
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
.search-trigger__label {
|
|
33
|
+
display: inline-flex;
|
|
34
|
+
min-width: 0;
|
|
35
|
+
align-items: center;
|
|
36
|
+
gap: $moo-search-trigger-label-gap;
|
|
37
|
+
overflow: hidden;
|
|
38
|
+
text-overflow: ellipsis;
|
|
39
|
+
white-space: nowrap;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
.search-trigger__shortcut {
|
|
43
|
+
display: inline-flex;
|
|
44
|
+
flex: 0 0 auto;
|
|
45
|
+
align-items: center;
|
|
46
|
+
gap: $moo-search-trigger-shortcut-gap;
|
|
47
|
+
margin-inline-start: auto;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
@include media-breakpoint-up(lg) {
|
|
51
|
+
.search-trigger {
|
|
52
|
+
width: $moo-search-trigger-width-lg;
|
|
53
|
+
flex-basis: $moo-search-trigger-width-lg;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
@include media-breakpoint-up(xxl) {
|
|
58
|
+
.search-trigger {
|
|
59
|
+
width: $moo-search-trigger-width-xxl;
|
|
60
|
+
flex-basis: $moo-search-trigger-width-xxl;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
// Bootstrap has no native sidebar, so this owned selector namespace is split
|
|
2
2
|
// by responsibility while preserving one deterministic emission order.
|
|
3
|
-
@import "sidebar/
|
|
3
|
+
@import "sidebar/base";
|
|
4
4
|
@import "sidebar/menus";
|
|
5
5
|
@import "sidebar/identity";
|
|
6
|
-
@import "sidebar/inset";
|
|
7
6
|
@import "sidebar/collapsed";
|
|
@@ -6,7 +6,15 @@
|
|
|
6
6
|
height: $moo-switch-height;
|
|
7
7
|
}
|
|
8
8
|
|
|
9
|
-
:
|
|
9
|
+
:scope[data-bs-theme="light"] .form-switch .form-check-input:checked {
|
|
10
|
+
--#{$prefix}form-switch-bg: #{escape-svg($form-switch-checked-bg-image)};
|
|
11
|
+
|
|
12
|
+
background-color: var(--moo-primary);
|
|
13
|
+
border-color: var(--moo-primary);
|
|
14
|
+
filter: none;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
:scope[data-bs-theme="dark"] .form-switch .form-check-input:checked {
|
|
10
18
|
--#{$prefix}form-switch-bg: #{escape-svg($moo-switch-checked-bg-image-dark)};
|
|
11
19
|
|
|
12
20
|
background-color: var(--moo-primary);
|
|
@@ -14,12 +22,12 @@
|
|
|
14
22
|
filter: none;
|
|
15
23
|
}
|
|
16
24
|
|
|
17
|
-
.form-switch .form-check-input:focus:active:not(:checked),
|
|
18
|
-
.form-switch .form-check-input:focus:not(:focus-visible):not(:checked) {
|
|
25
|
+
:scope[data-bs-theme="light"] .form-switch .form-check-input:focus:active:not(:checked),
|
|
26
|
+
:scope[data-bs-theme="light"] .form-switch .form-check-input:focus:not(:focus-visible):not(:checked) {
|
|
19
27
|
--#{$prefix}form-switch-bg: #{escape-svg($form-switch-bg-image)};
|
|
20
28
|
}
|
|
21
29
|
|
|
22
|
-
:
|
|
23
|
-
:
|
|
30
|
+
:scope[data-bs-theme="dark"] .form-switch .form-check-input:focus:active:not(:checked),
|
|
31
|
+
:scope[data-bs-theme="dark"] .form-switch .form-check-input:focus:not(:focus-visible):not(:checked) {
|
|
24
32
|
--#{$prefix}form-switch-bg: #{escape-svg($form-switch-bg-image-dark)};
|
|
25
33
|
}
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
// minimum for normal text (axe caught this in the certification fixture).
|
|
22
22
|
// var(--bs-secondary-color) is not a usable substitute here -- Moo UI's
|
|
23
23
|
// own theme sets --bs-secondary-color: var(--moo-muted-foreground)
|
|
24
|
-
// (scss/themes/
|
|
24
|
+
// (scss/themes/_root.scss), so it resolves to the exact same
|
|
25
25
|
// failing color. var(--bs-secondary-text-emphasis) is a different,
|
|
26
26
|
// untouched Bootstrap token (Moo UI does not override it) built for
|
|
27
27
|
// accessible colored text against a tinted "subtle" surface -- the same
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
// Bootstrap has no native sidebar, so this owned sidebar selector namespace
|
|
2
|
+
// provides the inner surface and anatomy. The App layout owns the outer
|
|
3
|
+
// <aside>, placement, width, visual variant, and collapse mode.
|
|
4
|
+
|
|
5
|
+
.sidebar-inner {
|
|
6
|
+
position: sticky;
|
|
7
|
+
top: 0;
|
|
8
|
+
display: flex;
|
|
9
|
+
flex-direction: column;
|
|
10
|
+
width: 100%;
|
|
11
|
+
height: 100svh;
|
|
12
|
+
max-height: 100svh;
|
|
13
|
+
background: var(--moo-sidebar);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
.sidebar-header,
|
|
17
|
+
.sidebar-footer {
|
|
18
|
+
display: flex;
|
|
19
|
+
flex-direction: column;
|
|
20
|
+
gap: $spacer * 0.25;
|
|
21
|
+
padding: $spacer * 0.5;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
.sidebar-content {
|
|
25
|
+
display: flex;
|
|
26
|
+
flex: 1 1 auto;
|
|
27
|
+
flex-direction: column;
|
|
28
|
+
gap: $spacer * 0.5;
|
|
29
|
+
min-height: 0;
|
|
30
|
+
overflow-y: auto;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
.sidebar-input {
|
|
34
|
+
width: 100%;
|
|
35
|
+
padding: $spacer * 0.25;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
.sidebar-separator {
|
|
39
|
+
display: flex;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
.sidebar-menu-skeleton {
|
|
43
|
+
display: flex;
|
|
44
|
+
align-items: center;
|
|
45
|
+
gap: $spacer * 0.25;
|
|
46
|
+
min-height: $spacer * 2;
|
|
47
|
+
padding-inline: $spacer * 0.5;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
.sidebar-menu-skeleton__icon {
|
|
51
|
+
width: $spacer;
|
|
52
|
+
height: $spacer;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
.sidebar-menu-skeleton__line {
|
|
56
|
+
flex: 1 1 auto;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
.sidebar-group {
|
|
60
|
+
position: relative;
|
|
61
|
+
display: flex;
|
|
62
|
+
flex-direction: column;
|
|
63
|
+
padding: $spacer * 0.5;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
.sidebar-group-content {
|
|
67
|
+
display: block;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
.sidebar-group-content--inline {
|
|
71
|
+
display: flex;
|
|
72
|
+
align-items: center;
|
|
73
|
+
flex-wrap: wrap;
|
|
74
|
+
gap: $spacer * 0.25;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
.sidebar-group-label {
|
|
78
|
+
display: flex;
|
|
79
|
+
align-items: center;
|
|
80
|
+
height: $spacer * 2;
|
|
81
|
+
padding-inline: $spacer * 0.5;
|
|
82
|
+
color: var(--moo-muted-foreground);
|
|
83
|
+
font-size: $font-size-base * 0.857;
|
|
84
|
+
font-weight: $font-weight-medium;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// Reserve room so a long label doesn't run under the absolutely positioned
|
|
88
|
+
// group action button (see .sidebar-group-action below). A descendant match
|
|
89
|
+
// covers both a group action placed directly in .sidebar-group and one
|
|
90
|
+
// nested inside .sidebar-group-content alongside the label.
|
|
91
|
+
.sidebar-group:has(.sidebar-group-action) .sidebar-group-label {
|
|
92
|
+
padding-inline-end: $spacer * 2.5;
|
|
93
|
+
}
|
|
@@ -1,64 +1,8 @@
|
|
|
1
|
-
//
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
inset-block: 0;
|
|
5
|
-
inset-inline-end: 0;
|
|
6
|
-
z-index: 20;
|
|
7
|
-
width: $spacer;
|
|
8
|
-
padding: 0;
|
|
9
|
-
transform: translateX(50%);
|
|
10
|
-
border: 0;
|
|
11
|
-
background: transparent;
|
|
12
|
-
cursor: pointer;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
[dir="rtl"] .sidebar-rail {
|
|
16
|
-
transform: translateX(-50%);
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
.sidebar-rail::after {
|
|
20
|
-
content: "";
|
|
21
|
-
position: absolute;
|
|
22
|
-
inset-block: 0;
|
|
23
|
-
inset-inline-start: 50%;
|
|
24
|
-
width: var(--bs-border-width);
|
|
25
|
-
background: transparent;
|
|
26
|
-
transition: background 0.15s ease;
|
|
27
|
-
}
|
|
1
|
+
// Collapsed-to-icon inner state (desktop). App owns the outer shell width,
|
|
2
|
+
// rail, stacking, and responsive placement; Sidebar owns the inner content
|
|
3
|
+
// visibility and flyout anatomy.
|
|
28
4
|
|
|
29
|
-
.sidebar-rail:hover::after {
|
|
30
|
-
background: var(--moo-sidebar-border);
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
// Collapsed-to-icon rail (desktop). Text, labels, and the rail's disclosure
|
|
34
|
-
// chevrons hide; every remaining control centers on its icon.
|
|
35
5
|
@include media-breakpoint-up(lg) {
|
|
36
|
-
.sidebar {
|
|
37
|
-
position: relative;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
.sidebar-wrapper:has([data-sidebar-dropdown-positioned] > .dropdown-menu.show) > .sidebar {
|
|
41
|
-
position: relative;
|
|
42
|
-
z-index: $zindex-dropdown;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
// The sticky inner surface is its own stacking context. Elevate that
|
|
46
|
-
// context while an identity menu is open so the rail's hover indicator
|
|
47
|
-
// remains behind the fixed dropdown instead of painting across it.
|
|
48
|
-
.sidebar-wrapper:has([data-sidebar-dropdown-positioned] > .dropdown-menu.show) > .sidebar > .sidebar-inner {
|
|
49
|
-
z-index: $zindex-dropdown;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
.sidebar-wrapper[data-sidebar-state="collapsed"] .sidebar[data-collapsible="icon"] {
|
|
53
|
-
flex-basis: var(--moo-sidebar-width-icon);
|
|
54
|
-
width: var(--moo-sidebar-width-icon);
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
.sidebar-wrapper[data-sidebar-state="collapsed"] .sidebar-inset {
|
|
58
|
-
position: relative;
|
|
59
|
-
z-index: 0;
|
|
60
|
-
}
|
|
61
|
-
|
|
62
6
|
[data-sidebar-state="collapsed"] .sidebar[data-collapsible="icon"] .sidebar-group-label,
|
|
63
7
|
[data-sidebar-state="collapsed"] .sidebar[data-collapsible="icon"] .sidebar-menu-button__text,
|
|
64
8
|
[data-sidebar-state="collapsed"] .sidebar[data-collapsible="icon"] .sidebar-menu-button__subtitle,
|
|
@@ -74,14 +18,18 @@
|
|
|
74
18
|
}
|
|
75
19
|
|
|
76
20
|
[data-sidebar-state="collapsed"] .sidebar[data-collapsible="icon"]:has(.sidebar-menu-button[data-bs-toggle="dropdown"][aria-expanded="true"]) {
|
|
77
|
-
|
|
21
|
+
// Keep the collapsed identity trigger above sticky page chrome too. This
|
|
22
|
+
// selector is more specific than the app-root rule above and otherwise
|
|
23
|
+
// would lower the Sidebar back to the dropdown layer.
|
|
24
|
+
z-index: $zindex-fixed + 1;
|
|
78
25
|
}
|
|
79
26
|
|
|
80
27
|
.sidebar-menu-flyout {
|
|
81
28
|
position: fixed;
|
|
82
29
|
z-index: $zindex-dropdown;
|
|
83
30
|
inset-block-start: var(--moo-sidebar-flyout-block-start);
|
|
84
|
-
|
|
31
|
+
left: var(--moo-sidebar-flyout-left);
|
|
32
|
+
right: auto;
|
|
85
33
|
display: flex;
|
|
86
34
|
flex-direction: column;
|
|
87
35
|
min-width: $spacer * 10;
|
|
@@ -168,32 +116,3 @@
|
|
|
168
116
|
display: none;
|
|
169
117
|
}
|
|
170
118
|
}
|
|
171
|
-
|
|
172
|
-
@include media-breakpoint-down(lg) {
|
|
173
|
-
.sidebar-rail {
|
|
174
|
-
display: none;
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
// Contained demos stay inside their preview instead of becoming viewport drawers.
|
|
178
|
-
.sidebar-wrapper--contained {
|
|
179
|
-
flex-direction: column;
|
|
180
|
-
height: auto;
|
|
181
|
-
min-height: 34rem;
|
|
182
|
-
overflow: visible;
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
.sidebar-wrapper--contained .sidebar {
|
|
186
|
-
position: relative;
|
|
187
|
-
inset: auto;
|
|
188
|
-
flex: 0 0 auto;
|
|
189
|
-
width: 100%;
|
|
190
|
-
height: auto;
|
|
191
|
-
visibility: visible;
|
|
192
|
-
transform: none;
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
.sidebar-wrapper--contained .sidebar-inner {
|
|
196
|
-
height: auto;
|
|
197
|
-
max-height: none;
|
|
198
|
-
}
|
|
199
|
-
}
|