@wpmoo/ui 1.0.0-rc.9 → 1.1.0-dev.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (96) hide show
  1. package/ASSET_LICENSE.md +1 -1
  2. package/LICENSE +28 -0
  3. package/README.md +38 -3
  4. package/certification.json +3 -1
  5. package/dist/assets/css/moo-ui.css +436 -25
  6. package/dist/assets/css/moo-ui.min.css +10 -31
  7. package/dist/assets/css/moo.css +428 -24
  8. package/dist/assets/css/moo.min.css +10 -32
  9. package/dist/js/chart.js +23 -21
  10. package/dist/js/chart.min.js +5 -5
  11. package/dist/js/combobox.js +18 -65
  12. package/dist/js/context-menu.js +16 -68
  13. package/dist/js/datatable.js +38 -21
  14. package/dist/js/datepicker.js +5 -4
  15. package/dist/js/datepicker.min.js +4 -3
  16. package/dist/js/moo-ui.js +350 -15402
  17. package/dist/js/moo-ui.min.js +3 -23
  18. package/dist/js/sheet.js +46 -0
  19. package/dist/js/sidebar.js +3 -2
  20. package/dist/js/slider.js +12 -47
  21. package/dist/js/state.js +15 -41
  22. package/dist/js/table-of-contents.js +195 -0
  23. package/dist/release-manifest.json +4 -4
  24. package/package.json +5 -1
  25. package/scss/_components.scss +3 -2
  26. package/scss/_config.scss +15 -21
  27. package/scss/_settings.scss +0 -3
  28. package/scss/components/_accordion.scss +1 -12
  29. package/scss/components/_alert.scss +1 -12
  30. package/scss/components/_avatar.scss +1 -16
  31. package/scss/components/_badge.scss +1 -0
  32. package/scss/components/_breadcrumb.scss +1 -12
  33. package/scss/components/_button.scss +1 -29
  34. package/scss/components/_button_group.scss +1 -25
  35. package/scss/components/_card.scss +1 -29
  36. package/scss/components/_checkbox.scss +1 -38
  37. package/scss/components/_close_button.scss +1 -9
  38. package/scss/components/_collapsible.scss +1 -0
  39. package/scss/components/_combobox.scss +1 -6
  40. package/scss/components/_context-menu.scss +1 -11
  41. package/scss/components/_datatable.scss +46 -85
  42. package/scss/components/_datepicker.scss +1 -0
  43. package/scss/components/_dialog.scss +1 -14
  44. package/scss/components/_dropdown.scss +5 -51
  45. package/scss/components/_field.scss +1 -40
  46. package/scss/components/_input.scss +1 -5
  47. package/scss/components/_input_group.scss +1 -51
  48. package/scss/components/_kbd.scss +1 -0
  49. package/scss/components/_menubar.scss +1 -0
  50. package/scss/components/_navigation.scss +76 -6
  51. package/scss/components/_pagination.scss +1 -13
  52. package/scss/components/_popover.scss +1 -22
  53. package/scss/components/_progress.scss +1 -3
  54. package/scss/components/_radio_group.scss +105 -6
  55. package/scss/components/_search_trigger.scss +1 -2
  56. package/scss/components/_select.scss +1 -0
  57. package/scss/components/_separator.scss +1 -6
  58. package/scss/components/_sheet.scss +1 -17
  59. package/scss/components/_sidebar.scss +1 -2
  60. package/scss/components/_skeleton.scss +1 -8
  61. package/scss/components/_slider.scss +1 -2
  62. package/scss/components/_spinner.scss +1 -7
  63. package/scss/components/_switch.scss +1 -4
  64. package/scss/components/_table.scss +1 -2
  65. package/scss/components/_tabs.scss +1 -24
  66. package/scss/components/_textarea.scss +1 -0
  67. package/scss/components/_toast.scss +1 -4
  68. package/scss/components/_toc.scss +48 -0
  69. package/scss/components/_toggle_group.scss +1 -3
  70. package/scss/components/_tooltip.scss +1 -20
  71. package/scss/components/sidebar/_base.scss +0 -7
  72. package/scss/components/sidebar/_collapsed.scss +0 -6
  73. package/scss/components/sidebar/_identity.scss +0 -1
  74. package/scss/components/sidebar/_menus.scss +0 -17
  75. package/scss/foundations/_backdrop.scss +0 -26
  76. package/scss/foundations/_focus.scss +0 -56
  77. package/scss/foundations/_scope.scss +15 -0
  78. package/scss/layouts/_app.scss +0 -6
  79. package/scss/layouts/_page_grid.scss +0 -3
  80. package/scss/layouts/app/sidebar/_base.scss +0 -37
  81. package/scss/layouts/app/sidebar/_default.scss +0 -6
  82. package/scss/layouts/app/sidebar/_floating.scss +0 -11
  83. package/scss/layouts/app/sidebar/_inset.scss +0 -2
  84. package/scss/mixins/_banner.scss +9 -3
  85. package/scss/moo-core.scss +3 -6
  86. package/scss/moo-ui.scss +9 -5
  87. package/scss/settings/_bootstrap_overrides.scss +0 -8
  88. package/scss/settings/_component_variables.scss +4 -266
  89. package/scss/settings/_forms.scss +0 -37
  90. package/scss/settings/_palette.scss +0 -42
  91. package/scss/themes/_root.scss +3 -0
  92. package/scss/themes/_standalone.scss +0 -2
  93. package/scss/themes/_theme.scss +3 -2
  94. package/scss/utilities/_background_color.scss +18 -0
  95. package/scss/utilities/_border.scss +4 -0
  96. package/scss/utilities/_scroll_smooth.scss +9 -0
@@ -1,4 +1,4 @@
1
- // Bootstrap components
1
+ /* Bootstrap Components */
2
2
  @import "bootstrap/scss/tables";
3
3
  @import "bootstrap/scss/forms/labels";
4
4
  @import "bootstrap/scss/forms/form-text";
@@ -25,7 +25,7 @@
25
25
  @import "bootstrap/scss/offcanvas";
26
26
  @import "bootstrap/scss/helpers/color-bg";
27
27
 
28
- // Moo components
28
+ /* Moo Components */
29
29
  @import "components/kbd";
30
30
  @import "components/input";
31
31
  @import "components/textarea";
@@ -42,6 +42,7 @@
42
42
  @import "components/menubar";
43
43
  @import "components/avatar";
44
44
  @import "components/navigation";
45
+ @import "components/toc";
45
46
  @import "components/skeleton";
46
47
  @import "components/sidebar";
47
48
  @import "components/alert";
package/scss/_config.scss CHANGED
@@ -1,24 +1,18 @@
1
- // Moo UI public Sass config
2
- //
3
- // Import this before Bootstrap variables so overrides take precedence.
4
- //
5
- // Only the variables below are public. Derived $moo-* tokens and component
6
- // knobs are internal and may change without notice.
7
1
 
8
- $primary: #171717 !default; // Brand color
9
- $secondary: #f4f4f5 !default; // Secondary surface color
10
- $success: #047857 !default; // Success color
11
- $info: #0369a1 !default; // Informational color
12
- $warning: #b45309 !default; // Warning color
13
- $danger: #e7000b !default; // Destructive color
14
- $light: #fafafa !default; // Light surface color
15
- $dark: #18181b !default; // Dark surface color
2
+ $primary: #171717 !default;
3
+ $secondary: #f4f4f5 !default;
4
+ $success: #047857 !default;
5
+ $info: #0369a1 !default;
6
+ $warning: #b45309 !default;
7
+ $danger: #e7000b !default;
8
+ $light: #fafafa !default;
9
+ $dark: #18181b !default;
16
10
 
17
- $body-color: #18181b !default; // Document text color
18
- $body-bg: #fff !default; // Document background color
19
- $font-size-base: 0.875rem !default; // Bootstrap-compatible base font size
11
+ $body-color: #18181b !default;
12
+ $body-bg: #fff !default;
13
+ $font-size-base: 0.875rem !default;
20
14
 
21
- $border-color: #e4e4e7 !default; // Default border color
22
- $border-radius: 0.5rem !default; // Default radius
23
- $border-radius-sm: 0.375rem !default; // Small radius
24
- $border-radius-lg: 0.625rem !default; // Large radius
15
+ $border-color: #e4e4e7 !default;
16
+ $border-radius: 0.5rem !default;
17
+ $border-radius-sm: 0.375rem !default;
18
+ $border-radius-lg: 0.625rem !default;
@@ -1,6 +1,3 @@
1
- // Ordered Sass settings aggregate. These partials emit variables only; preserve
2
- // this sequence because later component and catalog knobs depend on earlier
3
- // palette and form values before Bootstrap's own defaults are loaded.
4
1
  @import "config";
5
2
  @import "settings/palette";
6
3
  @import "settings/forms";
@@ -1,7 +1,4 @@
1
- // The reference trigger uses a real Lucide chevron-down icon that rotates
2
- // on expand, not Bootstrap's baked-in ::after background-image glyph.
3
- // Render the same icon our other components use and drive the rotation
4
- // off Bootstrap's own :not(.collapsed) state hook instead.
1
+ /* Accordion */
5
2
  .accordion-button::after {
6
3
  content: none;
7
4
  }
@@ -27,8 +24,6 @@
27
24
  text-align: start;
28
25
  }
29
26
 
30
- // The reference trigger has no active/pressed background — only a hover
31
- // underline signals interactivity, matching a plain text-link affordance.
32
27
  .accordion-button:hover {
33
28
  text-decoration: underline;
34
29
  }
@@ -43,10 +38,6 @@
43
38
  text-decoration: none;
44
39
  }
45
40
 
46
- // The reference's flush trigger and body sit edge-to-edge with no
47
- // horizontal padding, and the trigger reads as medium weight, not
48
- // Bootstrap's own regular default -- both retune existing Bootstrap
49
- // accordion tokens/properties, not new selectors.
50
41
  .accordion-flush {
51
42
  --bs-accordion-btn-padding-x: 0;
52
43
  --bs-accordion-btn-padding-y: #{$spacer * 0.625};
@@ -58,8 +49,6 @@
58
49
  font-weight: $font-weight-medium;
59
50
  }
60
51
 
61
- // A card owns its own surface, so a nested accordion must let that surface
62
- // show through instead of painting Bootstrap's body background on top of it.
63
52
  .card .accordion {
64
53
  --bs-accordion-bg: #{$moo-accordion-contained-bg};
65
54
  }
@@ -1,6 +1,4 @@
1
- // Bootstrap's base .alert has a transparent bg/border until a contextual
2
- // .alert-{state} modifier sets them; the design reference's quiet style
3
- // keeps a visible neutral border even on the unmodified/default alert.
1
+ /* Alert */
4
2
  $moo-alert-dismissible-close-space: $btn-close-width + ($btn-close-padding-x * 2) + 0.5rem !default;
5
3
 
6
4
  .alert {
@@ -29,21 +27,12 @@ $moo-alert-dismissible-close-space: $btn-close-width + ($btn-close-padding-x * 2
29
27
  padding: $btn-close-padding-y $btn-close-padding-x;
30
28
  }
31
29
 
32
- // The reference's destructive alert keeps the same neutral surface and
33
- // border as the default alert and tints only the text/icon color —
34
- // verified against the live reference page's Destructive example (bg-card,
35
- // text-destructive). Bootstrap's own alert-danger class instead fills a
36
- // saturated subtle-danger background, so re-anchor its consumption
37
- // variables back to the neutral ones and layer only the color shift on top.
38
30
  .alert-danger {
39
31
  --bs-alert-bg: transparent;
40
32
  --bs-alert-border-color: var(--moo-border);
41
33
  --bs-alert-color: var(--moo-destructive);
42
34
  }
43
35
 
44
- // Warning and success alerts follow the same quiet pattern as destructive:
45
- // neutral border, tinted alpha surface, hue-colored text. Matches the
46
- // reference's alert variants (amber for warning, green for success).
47
36
  .alert-warning {
48
37
  --bs-alert-bg: var(--moo-warning-surface);
49
38
  --bs-alert-border-color: var(--moo-border);
@@ -1,5 +1,4 @@
1
- // Bootstrap has no native avatar component. This public `.avatar` namespace is
2
- // intentionally small and consumes the shared Bootstrap radius/border primitives.
1
+ /* Avatar */
3
2
  .avatar {
4
3
  position: relative;
5
4
  display: inline-flex;
@@ -8,9 +7,6 @@
8
7
  width: $avatar-size;
9
8
  height: $avatar-size;
10
9
  flex: 0 0 auto;
11
- // A badge (such as a presence dot) sits at the avatar's corner and must
12
- // stay visible outside the circle; the image/fallback clip to the circle
13
- // on their own, so the root itself does not need to clip its children.
14
10
  overflow: visible;
15
11
  border: 0;
16
12
  border-radius: var(--bs-border-radius-pill);
@@ -66,10 +62,6 @@
66
62
  padding-inline: 0.2rem;
67
63
  border: 0;
68
64
  border-radius: var(--bs-border-radius-pill);
69
- // Mirrors the reference's ring-2 treatment: the badge keeps its own
70
- // 10px/12px/15px box while the page-background ring paints outside it.
71
- // box-shadow (not border) keeps the ring out of the layout box, matching
72
- // the .avatar-group ring below and Tailwind's own ring-* technique.
73
65
  box-shadow: $avatar-badge-ring-shadow;
74
66
  background-color: var(--moo-muted-surface);
75
67
  color: var(--moo-foreground);
@@ -83,9 +75,6 @@
83
75
  height: 0.625rem;
84
76
  }
85
77
 
86
- // A plain presence/status dot (no text or icon content) -- the reference's
87
- // own AvatarBadge with no children, recolored per use via badge_class
88
- // (such as a Bootstrap bg-success utility) rather than a hardcoded color here.
89
78
  .avatar-badge--dot {
90
79
  height: $avatar-badge-dot-size;
91
80
  min-width: $avatar-badge-dot-size;
@@ -121,10 +110,6 @@
121
110
  align-items: center;
122
111
  }
123
112
 
124
- // Matches the reference's ring-2 ring-background: each avatar in a group
125
- // carries a page-background ring (light = white, dark = black) so stacked
126
- // avatars stay visibly separated. --bs-body-bg is theme-aware in both the
127
- // standalone and scoped builds, mirroring the badge ring treatment.
128
113
  .avatar-group > .avatar {
129
114
  box-shadow: $avatar-group-ring-shadow;
130
115
  z-index: 0;
@@ -1,3 +1,4 @@
1
+ /* Badge */
1
2
  .badge {
2
3
  display: inline-flex;
3
4
  height: $moo-chip-height;
@@ -1,7 +1,4 @@
1
- // Bootstrap mutes the active/current item and leaves prior items on default
2
- // link styling (colored, underlined); the design reference inverts this —
3
- // prior items are muted, the active item reads at full foreground
4
- // strength — verified against the live reference page.
1
+ /* Breadcrumb */
5
2
  .breadcrumb {
6
3
  --bs-breadcrumb-item-active-color: var(--moo-foreground);
7
4
  --bs-breadcrumb-item-padding-x: #{$spacer * 0.375};
@@ -46,8 +43,6 @@
46
43
  line-height: 1;
47
44
  }
48
45
 
49
- // Bootstrap emits these RTL changes through RTLCSS. The catalog serves one
50
- // runtime CSS file, so scoped RTL demos bridge that generated output.
51
46
  :dir(rtl) .breadcrumb {
52
47
  --bs-breadcrumb-divider: "#{$breadcrumb-divider-flipped}";
53
48
  }
@@ -78,8 +73,6 @@
78
73
  text-decoration: none;
79
74
  }
80
75
 
81
- // A static collapsed-segment marker (no interactive behavior of its own,
82
- // matching the reference's ellipsis placeholder).
83
76
  .breadcrumb-ellipsis {
84
77
  display: inline-flex;
85
78
  align-items: center;
@@ -91,10 +84,6 @@
91
84
  height: 1rem;
92
85
  }
93
86
 
94
- // Bootstrap's dropdown wrapper is a plain block box; inside a breadcrumb
95
- // item that sits beside a floated divider, a block box wraps its content
96
- // onto its own line instead of flowing beside the float. Making this
97
- // wrapper an atomic inline-flex box keeps it on the breadcrumb's line.
98
87
  .breadcrumb-dropdown-item,
99
88
  .breadcrumb-dropdown-item .dropdown {
100
89
  display: inline-flex;
@@ -1,5 +1,4 @@
1
- // Bootstrap owns button geometry through its --bs-btn-* consumption values.
2
- // Moo adds only icon composition and the missing xs/icon-only variants.
1
+ /* Buttons */
3
2
  .btn {
4
3
  display: inline-flex;
5
4
  align-items: center;
@@ -23,8 +22,6 @@ label.btn {
23
22
  cursor: default;
24
23
  }
25
24
 
26
- // Bootstrap provides sm/lg; xs and icon-only variants reuse the same
27
- // consumption properties instead of introducing a parallel size system.
28
25
  .btn-xs,
29
26
  .btn-icon-xs {
30
27
  --bs-btn-padding-y: #{$spacer * 0.1875};
@@ -66,18 +63,7 @@ label.btn {
66
63
  padding-inline-end: $icon-link-gap;
67
64
  }
68
65
 
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
66
  .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
67
  --bs-btn-line-height: #{$btn-line-height};
82
68
  --bs-btn-padding-y: #{$btn-padding-y};
83
69
  }
@@ -86,8 +72,6 @@ label.btn {
86
72
  .btn-icon-xs,
87
73
  .btn-icon-sm,
88
74
  .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
75
  --moo-btn-icon-size: #{$moo-btn-icon-size};
92
76
  padding-inline: 0;
93
77
  inline-size: var(--moo-btn-icon-size);
@@ -115,11 +99,6 @@ label.btn {
115
99
  height: $font-size-base;
116
100
  }
117
101
 
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
102
  .btn[aria-busy="true"] > [data-icon="inline-start"] {
124
103
  animation: $moo-spinner-animation-name $spinner-animation-speed linear infinite;
125
104
  }
@@ -136,14 +115,10 @@ label.btn {
136
115
  opacity: var(--moo-disabled-control-opacity);
137
116
  }
138
117
 
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
118
  .btn:not(:disabled):not(.disabled):not([aria-disabled="true"]):active {
143
119
  transform: translateY($moo-control-active-translate-y);
144
120
  }
145
121
 
146
- // Bootstrap consumes these properties; owned variants supply only exceptions.
147
122
  :is(.btn-primary, .btn-secondary, .btn-outline-secondary, .btn-outline-primary, .btn-ghost, .btn-danger) {
148
123
  --bs-btn-border-color: var(--bs-btn-bg);
149
124
  --bs-btn-hover-color: var(--bs-btn-color);
@@ -172,9 +147,6 @@ label.btn {
172
147
 
173
148
  .btn-secondary {
174
149
  --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
150
  --bs-btn-bg: var(--moo-surface);
179
151
  --bs-btn-border-color: var(--moo-border);
180
152
  --bs-btn-hover-bg: color-mix(in srgb, var(--moo-muted-surface) 95%, var(--moo-foreground));
@@ -1,7 +1,4 @@
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.
1
+ /* Button Group */
5
2
  [dir="rtl"] .btn-group,
6
3
  [dir="rtl"] .btn-toolbar {
7
4
  direction: ltr;
@@ -12,12 +9,6 @@
12
9
  unicode-bidi: plaintext;
13
10
  }
14
11
 
15
- // Bootstrap raises hovered, focused, and active group buttons above siblings
16
- // so the current border wins the shared seam. In a mixed-emphasis group (for
17
- // example primary + outline) pointer interaction paints one pixel into the
18
- // neighbor and reads like a width jump. Keep keyboard focus, active classes,
19
- // and checked inputs stacked, but let plain pointer hover/click use DOM order
20
- // so the joined edge stays visually stable.
21
12
  .btn-group > .btn:hover:not(:focus-visible):not(.active),
22
13
  .btn-group > .btn:focus:not(:focus-visible):not(.active),
23
14
  .btn-group > .btn:active:not(:focus-visible):not(.active),
@@ -34,16 +25,11 @@
34
25
  z-index: 1;
35
26
  }
36
27
 
37
- // A button group is one compound control surface; pressing one segment should
38
- // not translate that segment away from its joined border while siblings stay
39
- // still. The active color change remains as the press affordance.
40
28
  .btn-group > .btn:not(:disabled):not(.disabled):not([aria-disabled="true"]):active,
41
29
  .btn-group-vertical > .btn:not(:disabled):not(.disabled):not([aria-disabled="true"]):active {
42
30
  transform: none;
43
31
  }
44
32
 
45
- // Icon buttons in button groups should stay square, not stretch to fill
46
- // the group width (Bootstrap's flex: 1 1 auto makes them rectangular).
47
33
  .btn-group > .btn-icon,
48
34
  .btn-group > .btn-icon-xs,
49
35
  .btn-group > .btn-icon-sm,
@@ -55,8 +41,6 @@
55
41
  flex: 0 0 auto;
56
42
  }
57
43
 
58
- // Bootstrap's .btn-group-vertical > .btn { width: 100% } would stretch icon
59
- // buttons; re-assert the token-derived square so vertical rails stay square.
60
44
  .btn-group-vertical > .btn-icon,
61
45
  .btn-group-vertical > .btn-icon-xs,
62
46
  .btn-group-vertical > .btn-icon-sm,
@@ -64,14 +48,6 @@
64
48
  width: var(--moo-btn-icon-size);
65
49
  }
66
50
 
67
- // Select inside a button group: compact and content-sized, matching the
68
- // compact currency selector pattern. Bootstrap's flex defaults would shrink
69
- // it below its longest option; flex: 0 0 auto keeps the intrinsic (content)
70
- // width. The min-width floor derives from Bootstrap's form-select caret-space
71
- // token, not magic numbers. Focus keeps Bootstrap's default visible treatment.
72
- //
73
- // The input-group override matches Bootstrap's own `.input-group > .form-select`
74
- // specificity (0,2,0) which would otherwise stretch the select with flex: 1 1 auto.
75
51
  .btn-group-select,
76
52
  .input-group > .btn-group-select {
77
53
  flex: 0 0 auto;
@@ -1,15 +1,8 @@
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).
1
+ /* Card */
7
2
  .card {
8
3
  --bs-card-bg: color-mix(in srgb, var(--bs-secondary-bg) #{$moo-card-bg-mix}, var(--bs-body-bg));
9
4
  --bs-card-color: var(--moo-foreground);
10
5
  --bs-card-border-color: var(--moo-border);
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
6
  --bs-card-border-radius: var(--bs-border-radius-xl);
14
7
  --bs-card-inner-border-radius: calc(var(--bs-card-border-radius) - var(--bs-card-border-width));
15
8
  --bs-card-box-shadow: var(--bs-box-shadow-sm);
@@ -18,32 +11,19 @@
18
11
  --bs-card-title-color: var(--moo-foreground);
19
12
  --bs-card-subtitle-color: var(--moo-muted-foreground);
20
13
  --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
14
  --moo-card-spacing: #{$moo-card-spacing};
26
15
 
27
- // One vertical rhythm owned by the card; sections add only inline padding.
28
16
  gap: var(--moo-card-spacing);
29
17
  padding-block: var(--moo-card-spacing);
30
18
 
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
19
  &:has(.card-footer) {
35
20
  padding-block-end: 0;
36
21
  }
37
22
 
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
23
  &:has(> .card-image) {
42
24
  padding-block-start: 0;
43
25
  }
44
26
 
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
27
  border: var(--bs-card-border-width) solid var(--bs-card-border-color);
48
28
  box-shadow: var(--bs-card-box-shadow);
49
29
 
@@ -58,8 +38,6 @@
58
38
  --moo-card-spacing: #{$moo-card-spacing-sm};
59
39
  }
60
40
 
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
41
  .card-header {
64
42
  display: grid;
65
43
  grid-template-columns: 1fr auto;
@@ -92,8 +70,6 @@
92
70
  padding-inline: var(--moo-card-spacing);
93
71
  }
94
72
 
95
- // Use a separately mixed footer surface so tuning the main card fill does not
96
- // collapse the footer contrast.
97
73
  .card-footer {
98
74
  display: flex;
99
75
  align-items: center;
@@ -104,14 +80,10 @@
104
80
  }
105
81
 
106
82
  .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
83
  border-top-left-radius: var(--bs-card-border-radius);
110
84
  border-top-right-radius: var(--bs-card-border-radius);
111
85
  }
112
86
 
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
87
  .card-image {
116
88
  aspect-ratio: 16 / 9;
117
89
  overflow: hidden;
@@ -1,15 +1,8 @@
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.
1
+ /* Checkbox */
6
2
  .form-check-input {
7
3
  margin-top: 0.125rem;
8
4
  }
9
5
 
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
6
  .form-check-input[type="checkbox"]:checked:not([role="switch"]) {
14
7
  --#{$prefix}form-check-bg-image: none;
15
8
 
@@ -42,11 +35,6 @@
42
35
  --#{$prefix}form-check-bg-image: radial-gradient(circle at center, var(--moo-primary-foreground) 0 34%, transparent 36%);
43
36
  }
44
37
 
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
38
  .form-check-input:is([type="checkbox"], [type="radio"]):not(.is-invalid):focus:active,
51
39
  .form-check-input:is([type="checkbox"], [type="radio"]):not(.is-invalid):focus:not(:focus-visible) {
52
40
  border-color: var(--bs-border-color);
@@ -63,24 +51,10 @@
63
51
  border-color: $form-check-input-indeterminate-border-color;
64
52
  }
65
53
 
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
54
  .form-check-input.is-invalid {
77
55
  box-shadow: 0 0 0 #{$moo-form-focus-ring-width} var(--moo-form-invalid-ring-color);
78
56
  }
79
57
 
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
58
  .form-check-input.is-invalid:focus:active,
85
59
  .form-check-input.is-invalid:focus:not(:focus-visible) {
86
60
  box-shadow: 0 0 0 #{$moo-form-focus-ring-width} var(--moo-form-invalid-ring-color);
@@ -96,17 +70,6 @@
96
70
  box-shadow: 0 0 0 #{$moo-form-focus-ring-width} var(--moo-form-invalid-ring-color);
97
71
  }
98
72
 
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
73
  .form-check-input:is([type="checkbox"], [type="radio"]):not([role="switch"]).is-invalid:checked,
111
74
  .form-check-input:is([type="checkbox"], [type="radio"]):not([role="switch"]).is-invalid:checked:focus:active,
112
75
  .form-check-input:is([type="checkbox"], [type="radio"]):not([role="switch"]).is-invalid:checked:focus:not(:focus-visible) {
@@ -1,10 +1,9 @@
1
+ /* Close Button */
1
2
  .btn-close {
2
3
  --bs-btn-close-color: var(--moo-foreground);
3
4
  --bs-btn-hover-color: var(--bs-btn-close-color);
4
5
  --bs-btn-hover-bg: var(--moo-muted-surface);
5
6
  --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
7
  --bs-btn-close-bg: #{escape-svg($moo-close-button-bg-image)};
9
8
 
10
9
  display: inline-flex;
@@ -14,8 +13,6 @@
14
13
  border-width: var(--bs-border-width);
15
14
  border-style: var(--bs-border-style);
16
15
  border-color: $moo-close-button-border-color;
17
- // Keep the square hit area visually circular in every context, including
18
- // modal and offcanvas headers.
19
16
  border-radius: var(--bs-border-radius-pill);
20
17
  color: var(--bs-btn-close-color);
21
18
  background-image: none;
@@ -31,11 +28,6 @@
31
28
  mask: var(--bs-btn-close-bg) center / contain no-repeat;
32
29
  }
33
30
 
34
- // Bootstrap's dark rule filters the whole element to recolor its background
35
- // image. The glyph above uses currentColor through a mask, so filtering would
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
31
  :scope[data-bs-theme="light"] .btn-close,
40
32
  :scope[data-bs-theme="dark"] .btn-close {
41
33
  filter: none;
@@ -1,3 +1,4 @@
1
+ /* Collapsible */
1
2
  .collapsible {
2
3
  display: flex;
3
4
  flex-direction: column;
@@ -1,6 +1,4 @@
1
- // Bootstrap has no native Combobox component. The Basic pass composes a
2
- // form-control with a dropdown-menu and keeps only the missing positioning
3
- // and inline affordance in this component namespace.
1
+ /* Combobox */
4
2
  .combobox {
5
3
  --moo-combobox-width: #{$moo-combobox-width};
6
4
  --moo-combobox-multiple-width: #{$moo-combobox-multiple-width};
@@ -93,9 +91,6 @@
93
91
  cursor: text;
94
92
  }
95
93
 
96
- // .combobox-chips's focus-visible ring lives in foundations/_focus.scss,
97
- // next to the other compound-control rings, instead of here -- see that
98
- // file's comment for why.
99
94
 
100
95
  .combobox-value {
101
96
  display: flex;
@@ -1,8 +1,4 @@
1
- // Bootstrap has no native context-menu or "explicit fallback trigger"
2
- // concept, so there is no Bootstrap token to consume here: the fallback
3
- // button is a Moo-owned corner affordance layered over arbitrary surface
4
- // content, verified in the browser to otherwise fall into normal block flow
5
- // below the surface instead of reading as a menu trigger for it.
1
+ /* Context Menu */
6
2
  .context-menu {
7
3
  position: relative;
8
4
  }
@@ -17,12 +13,6 @@
17
13
  inset-inline-end: 0.5rem;
18
14
  }
19
15
 
20
- // A pointer-driven open focuses the menu container itself, not an item, so
21
- // :hover carries the highlight instead of a focus ring competing with it
22
- // (verified in the browser: focusing the first item unconditionally left a
23
- // stray ring on it while a different item was hovered). The shared
24
- // [tabindex]:focus-visible ring in foundations/_focus.scss otherwise applies
25
- // here too, since a tabindex="-1" container still matches that selector.
26
16
  ul.context-menu-menu:focus-visible {
27
17
  outline: 0;
28
18
  }