Repository: bootstrap-vue-next/bootstrap-vue-next
Pull request #2671: fix(scss) moved all scss styles out of components
Author: login: unstoppablecarl
Created: 2025-04-27T17:23:36Z | Merged: 2025-05-22T13:42:28Z

# Describe the PR

Moved all styles from components into separate scss files. The following components were modified:

BAlert
BDropdown
BModal
BOffcanvas
BPopover
BToast
BToastOrchestrator

I did my best to follow the existing style and file structure. Please advise if there are any mistakes.

## PR checklist

**What kind of change does this PR introduce?** (check at least one)

- [x] Bugfix :bug: - `fix(...)`
- [ ] Feature - `feat(...)`
- [ ] ARIA accessibility - `fix(...)`
- [ ] Documentation update - `docs(...)`
- [ ] Other (please describe)

**The PR fulfills these requirements:**

- [x] Pull request title and all commits follow the [**Conventional Commits**](https://www.conventionalcommits.org/) convention or has an [**override**](https://github.com/googleapis/release-please#how-can-i-fix-release-notes) in this pull request body **This is very important, as the `CHANGELOG` is generated from these messages, and determines the next version type. Pull requests that do not follow conventional commits or do not have an override will be denied**

I am not sure if this would be considered a breaking change for the scoped styles. 


<!-- This is an auto-generated comment: release notes by coderabbit.ai -->
## Summary by CodeRabbit

- **Style**
  - Centralized and updated styles for alerts, popovers, and toasts to improve consistency and maintainability.
  - Introduced a utility class to disable transitions when needed.
  - Enhanced visual alignment and background synchronization for popover arrows and toast progress bars.
  - Improved toast and alert close button styling for a more polished appearance.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->


Diff:
diff --git a/packages/bootstrap-vue-next/src/components/BAlert/BAlert.vue b/packages/bootstrap-vue-next/src/components/BAlert/BAlert.vue
index 93482132f..b922bc06e 100644
--- a/packages/bootstrap-vue-next/src/components/BAlert/BAlert.vue
+++ b/packages/bootstrap-vue-next/src/components/BAlert/BAlert.vue
@@ -278,14 +278,3 @@ defineExpose({
   stop,
 })
 </script>
-
-<style lang="scss" scoped>
-.alert :deep(.progress .progress-bar) {
-  --bs-progress-bar-transition: none;
-}
-
-.btn-close-custom {
-  margin-bottom: auto;
-  position: relative;
-}
-</style>
diff --git a/packages/bootstrap-vue-next/src/components/BAlert/_alert.scss b/packages/bootstrap-vue-next/src/components/BAlert/_alert.scss
new file mode 100644
index 000000000..e7a6d975b
--- /dev/null
+++ b/packages/bootstrap-vue-next/src/components/BAlert/_alert.scss
@@ -0,0 +1,8 @@
+.alert .progress .progress-bar {
+  --bs-progress-bar-transition: none;
+}
+
+.alert .btn-close-custom {
+  margin-bottom: auto;
+  position: relative;
+}
diff --git a/packages/bootstrap-vue-next/src/components/BOffcanvas/BOffcanvas.vue b/packages/bootstrap-vue-next/src/components/BOffcanvas/BOffcanvas.vue
index a39be3cda..7309daac1 100644
--- a/packages/bootstrap-vue-next/src/components/BOffcanvas/BOffcanvas.vue
+++ b/packages/bootstrap-vue-next/src/components/BOffcanvas/BOffcanvas.vue
@@ -313,9 +313,3 @@ defineExpose({
   isOpenByBreakpoint,
 })
 </script>
-
-<style lang="scss" scoped>
-.no-transition {
-  transition: none !important;
-}
-</style>
diff --git a/packages/bootstrap-vue-next/src/components/BPopover/BPopover.vue b/packages/bootstrap-vue-next/src/components/BPopover/BPopover.vue
index 0776eb193..ae3452acf 100644
--- a/packages/bootstrap-vue-next/src/components/BPopover/BPopover.vue
+++ b/packages/bootstrap-vue-next/src/components/BPopover/BPopover.vue
@@ -488,10 +488,3 @@ onMounted(() => {
 
 onBeforeUnmount(unbind)
 </script>
-<style lang="scss" scoped>
-.bs-popover-auto[data-popper-placement^='bottom']
-  .popover-arrow:has(+ div > .popover-header)::after,
-.bs-popover-bottom .popover-arrow:has(+ div > .popover-header)::after {
-  --bs-popover-bg: var(--bs-popover-header-bg);
-}
-</style>
diff --git a/packages/bootstrap-vue-next/src/components/BPopover/_popover.scss b/packages/bootstrap-vue-next/src/components/BPopover/_popover.scss
new file mode 100644
index 000000000..3ab787865
--- /dev/null
+++ b/packages/bootstrap-vue-next/src/components/BPopover/_popover.scss
@@ -0,0 +1,5 @@
+.bs-popover-auto[data-popper-placement^="bottom"]
+  .popover-arrow:has(+ div > .popover-header)::after,
+.bs-popover-bottom .popover-arrow:has(+ div > .popover-header)::after {
+  --bs-popover-bg: var(--bs-popover-header-bg);
+}
diff --git a/packages/bootstrap-vue-next/src/components/BToast/BToast.vue b/packages/bootstrap-vue-next/src/components/BToast/BToast.vue
index eedf4d91a..7feb752ff 100644
--- a/packages/bootstrap-vue-next/src/components/BToast/BToast.vue
+++ b/packages/bootstrap-vue-next/src/components/BToast/BToast.vue
@@ -282,18 +282,3 @@ defineExpose({
   stop,
 })
 </script>
-
-<style lang="scss" scoped>
-.toast :deep(.progress .progress-bar) {
-  --bs-progress-bar-transition: none;
-}
-.toast:not(.show) {
-  opacity: unset;
-}
-.toast.fade:not(.show) {
-  opacity: 0;
-}
-.btn-close-custom {
-  margin: var(--bs-toast-padding-x) var(--bs-toast-padding-x) auto;
-}
-</style>
diff --git a/packages/bootstrap-vue-next/src/components/BToast/BToastOrchestrator.vue b/packages/bootstrap-vue-next/src/components/BToast/BToastOrchestrator.vue
index 10bc75f86..84b8b2950 100644
--- a/packages/bootstrap-vue-next/src/components/BToast/BToastOrchestrator.vue
+++ b/packages/bootstrap-vue-next/src/components/BToast/BToastOrchestrator.vue
@@ -104,18 +104,3 @@ defineExpose({
   ...tools,
 })
 </script>
-
-<style lang="scss">
-.b-list-move,
-.b-list-enter-active,
-.b-list-leave-active {
-  transition: all 0.5s cubic-bezier(0.55, 0, 0.1, 1);
-}
-.b-list-enter-from,
-.b-list-leave-to {
-  opacity: 0; // TODO this should be the responsibility of the child
-}
-.b-list-leave-active {
-  position: fixed;
-}
-</style>
diff --git a/packages/bootstrap-vue-next/src/components/BToast/_toast.scss b/packages/bootstrap-vue-next/src/components/BToast/_toast.scss
new file mode 100644
index 000000000..4d0e0be01
--- /dev/null
+++ b/packages/bootstrap-vue-next/src/components/BToast/_toast.scss
@@ -0,0 +1,26 @@
+.toast .progress .progress-bar {
+  --bs-progress-bar-transition: none;
+}
+.toast:not(.show) {
+  opacity: unset;
+}
+.toast.fade:not(.show) {
+  opacity: 0;
+}
+
+.toast .btn-close-custom {
+  margin: var(--bs-toast-padding-x) var(--bs-toast-padding-x) auto;
+}
+
+.b-list-move,
+.b-list-enter-active,
+.b-list-leave-active {
+  transition: all 0.5s cubic-bezier(0.55, 0, 0.1, 1);
+}
+.b-list-enter-from,
+.b-list-leave-to {
+  opacity: 0; // TODO this should be the responsibility of the child
+}
+.b-list-leave-active {
+  position: fixed;
+}
diff --git a/packages/bootstrap-vue-next/src/styles/styles.scss b/packages/bootstrap-vue-next/src/styles/styles.scss
index 705f8b421..abd5aea52 100644
--- a/packages/bootstrap-vue-next/src/styles/styles.scss
+++ b/packages/bootstrap-vue-next/src/styles/styles.scss
@@ -10,6 +10,9 @@
 @import "../components/BFormInput/form-input";
 @import "../components/BFormFile/form-file";
 @import "../components/BFormSpinbutton/spinbutton";
+@import "../components/BAlert/alert";
+@import "../components/BPopover/popover";
+@import "../components/BToast/toast";
 
 .container,
 .container-fluid {
@@ -61,3 +64,7 @@
 .fade-leave-to {
   opacity: 0;
 }
+
+.no-transition {
+  transition: none !important;
+}


Reviews:
user: login: coderabbitai[bot]
body: **Actionable comments posted: 2**

<details>
<summary>🧹 Nitpick comments (4)</summary><blockquote>

<details>
<summary>packages/bootstrap-vue-next/src/components/BOffcanvas/_offcanvas.scss (1)</summary><blockquote>

`1-3`: **Avoid polluting the global namespace with `.no-transition`.**  
The class name `.no-transition` is very generic and may unintentionally affect other components or utilities. Consider scoping it under the offcanvas context or renaming it to a BOffcanvas-specific selector, for example:  
```scss
.offcanvas.no-transition { ... }
```  
or  
```scss
.b-offcanvas-no-transition { ... }
```

</blockquote></details>
<details>
<summary>packages/bootstrap-vue-next/src/components/BToast/_toast.scss (2)</summary><blockquote>

`12-18`: **Remove or clarify trial-and-error comments.**  
The multiline comment describing unknown animation behaviors adds noise. If you need to revisit or refine this logic, replace it with a concise TODO or remove it before merging.

---

`19-30`: **Scope list animation styles to avoid global side effects.**  
The `.b-list-*` classes are applied globally and could interfere with other transition groups across the app. Consider namespacing them under a toast-specific wrapper (e.g. `.b-toast-list-enter-active`), or nest them inside a `.toast` or `.b-toast-orchestrator` container.

</blockquote></details>
<details>
<summary>packages/bootstrap-vue-next/src/components/BAlert/_alert.scss (1)</summary><blockquote>

`1-7`: **Scope `.btn-close-custom` to the `.alert` component to prevent global style leakage.**  
By defining these styles globally, any element with `.btn-close-custom` will receive alert-specific positioning and margin, which may lead to unintended side effects. Consider nesting under the alert selector:

```diff
--- a/packages/bootstrap-vue-next/src/components/BAlert/_alert.scss
+++ b/packages/bootstrap-vue-next/src/components/BAlert/_alert.scss
@@ -1,7 +1,11 @@
-.btn-close-custom {
+.alert {
+  .btn-close-custom {
   position: absolute;
   top: 0;
   right: 0;
   z-index: 2;
-  margin: var(--bs-alert-padding-y) var(--bs-alert-padding-x);
-}
+   margin: var(--bs-alert-padding-y) var(--bs-alert-padding-x);
+  }
+}
```

</blockquote></details>

</blockquote></details>

<details>
<summary>📜 Review details</summary>

**Configuration used: CodeRabbit UI**
**Review profile: CHILL**
**Plan: Pro**

<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between 716054cfb1f6c064bc1df108d1e51bb7393b6429 and ee6c586c343509185106d26a6162dbacfb05a438.

</details>

<details>
<summary>📒 Files selected for processing (14)</summary>

* `packages/bootstrap-vue-next/src/components/BAlert/BAlert.vue` (0 hunks)
* `packages/bootstrap-vue-next/src/components/BAlert/_alert.scss` (1 hunks)
* `packages/bootstrap-vue-next/src/components/BDropdown/BDropdown.vue` (0 hunks)
* `packages/bootstrap-vue-next/src/components/BDropdown/_dropdown.scss` (1 hunks)
* `packages/bootstrap-vue-next/src/components/BModal/BModal.vue` (0 hunks)
* `packages/bootstrap-vue-next/src/components/BModal/_modal.scss` (1 hunks)
* `packages/bootstrap-vue-next/src/components/BOffcanvas/BOffcanvas.vue` (0 hunks)
* `packages/bootstrap-vue-next/src/components/BOffcanvas/_offcanvas.scss` (1 hunks)
* `packages/bootstrap-vue-next/src/components/BPopover/BPopover.vue` (0 hunks)
* `packages/bootstrap-vue-next/src/components/BPopover/_popover.scss` (1 hunks)
* `packages/bootstrap-vue-next/src/components/BToast/BToast.vue` (0 hunks)
* `packages/bootstrap-vue-next/src/components/BToast/BToastOrchestrator.vue` (0 hunks)
* `packages/bootstrap-vue-next/src/components/BToast/_toast.scss` (1 hunks)
* `packages/bootstrap-vue-next/src/styles/styles.scss` (1 hunks)

</details>

<details>
<summary>💤 Files with no reviewable changes (7)</summary>

* packages/bootstrap-vue-next/src/components/BDropdown/BDropdown.vue
* packages/bootstrap-vue-next/src/components/BModal/BModal.vue
* packages/bootstrap-vue-next/src/components/BToast/BToast.vue
* packages/bootstrap-vue-next/src/components/BAlert/BAlert.vue
* packages/bootstrap-vue-next/src/components/BOffcanvas/BOffcanvas.vue
* packages/bootstrap-vue-next/src/components/BToast/BToastOrchestrator.vue
* packages/bootstrap-vue-next/src/components/BPopover/BPopover.vue

</details>

<details>
<summary>🔇 Additional comments (4)</summary><blockquote>

<details>
<summary>packages/bootstrap-vue-next/src/components/BDropdown/_dropdown.scss (1)</summary>

`1-5`: **Looks good.**  
The nested SCSS correctly centralizes the `.dropdown-menu.fade { display: block; }` rule without any obvious side effects.

</details>
<details>
<summary>packages/bootstrap-vue-next/src/styles/styles.scss (1)</summary>

`13-18`: **Component style imports look correct.**  
The new imports for BAlert, BDropdown, BModal, BOffcanvas, BPopover, and BToast integrate all relocated SCSS partials into the global stylesheet in a consistent order.

</details>
<details>
<summary>packages/bootstrap-vue-next/src/components/BModal/_modal.scss (1)</summary>

`5-7`: **Verify z-index against the backdrop and other modals.**  
You’ve set `.modal-dialog { z-index: 1051; }`, but Bootstrap’s default backdrop is `1050` and the modal container is typically `1055`. Please confirm this ordering achieves the intended stacking without visual glitches.

</details>
<details>
<summary>packages/bootstrap-vue-next/src/components/BPopover/_popover.scss (1)</summary>

`1-5`: <details>
<summary>❌ Incorrect review comment</summary>

**Fix the broken selector by adding the missing descendant combinator.**  
The selector on lines 1–2 is split without a space, causing it to compile into `.bs-popover-auto[data-popper-placement^='bottom'].popover-arrow` and not match the intended element. Update to:

```diff
--- a/packages/bootstrap-vue-next/src/components/BPopover/_popover.scss
+++ b/packages/bootstrap-vue-next/src/components/BPopover/_popover.scss
@@ -1,5 +1,5 @@
-.bs-popover-auto[data-popper-placement^='bottom']
-.popover-arrow:has(+ div > .popover-header)::after,
+.bs-popover-auto[data-popper-placement^='bottom'] .popover-arrow:has(+ div > .popover-header)::after,
 .bs-popover-bottom .popover-arrow:has(+ div > .popover-header)::after {
   --bs-popover-bg: var(--bs-popover-header-bg);
 }
```

Also verify that the `:has()` pseudo-class is supported in your target browsers, or provide a fallback for environments where it isn’t available.

---

🏁 Script executed:

```shell
#!/bin/bash
set -e

# Locate the SCSS file
file=$(fd --hidden --exact-depth 6 '_popover.scss' | head -n1)
echo "File path: $file"
echo "----- First 5 lines of the file -----"
sed -n '1,5p' "$file"
```

Length of output: 690

---



</details>

**Descendant combinator is already implied**  
SCSS treats newlines as whitespace in selectors, so

    .bs-popover-auto[data-popper-placement^='bottom']
    .popover-arrow:has(+ div > .popover-header)::after { … }

already compiles to

    .bs-popover-auto[data-popper-placement^='bottom'] .popover-arrow:has(+ div > .popover-header)::after

No changes required to the selector.

**Verify `:has()` support**  
Ensure your target browsers support the CSS `:has()` pseudo-class or provide a fallback for environments where it isn’t available. 

> Likely an incorrect or invalid review comment.

</details>

</blockquote></details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-27T17:26:01Z
user: login: unstoppablecarl
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-27T17:30:10Z
user: login: coderabbitai[bot]
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-27T17:30:43Z
user: login: unstoppablecarl
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-27T17:31:09Z
user: login: coderabbitai[bot]
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-27T17:32:04Z
user: login: xvaara
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-28T19:59:49Z
user: login: coderabbitai[bot]
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-28T19:59:54Z
user: login: xvaara
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-28T20:03:46Z
user: login: unstoppablecarl
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-28T20:04:36Z
user: login: xvaara
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-28T20:05:50Z
user: login: coderabbitai[bot]
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-28T20:05:55Z
user: login: xvaara
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-28T20:08:46Z
user: login: unstoppablecarl
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-28T20:12:23Z
user: login: unstoppablecarl
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-28T20:13:38Z
user: login: unstoppablecarl
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-28T20:14:25Z
user: login: xvaara
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-28T20:39:40Z
user: login: xvaara
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-28T23:24:55Z
user: login: xvaara
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-28T23:25:08Z
user: login: xvaara
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-28T23:26:04Z
user: login: xvaara
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-28T23:29:14Z
user: login: unstoppablecarl
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-28T23:37:32Z
user: login: coderabbitai[bot]
body: **Actionable comments posted: 30**

<details>
<summary>🔭 Outside diff range comments (6)</summary><blockquote>

<details>
<summary>packages/bootstrap-vue-next/src/components/BDropdown/BDropdown.vue (1)</summary><blockquote>

`187-220`: _🛠️ Refactor suggestion_

**`autoUpdate` cleanup can leak when component is unmounted**  

`cleanup()` runs only when `hideFn` executes, but nothing guarantees the dropdown is hidden before the component is destroyed. If the component is removed from the DOM while still open, the animation frame listener keeps running.

```diff
+import {onBeforeUnmount} from 'vue'
 ...
   hideFn: () => {
     if (cleanup) {
       cleanup()
       cleanup = undefined
     }
   },
 })
+
+onBeforeUnmount(() => {
+  if (cleanup) cleanup()
+})
```

</blockquote></details>
<details>
<summary>packages/bootstrap-vue-next/src/directives/toggle.spec.ts (1)</summary><blockquote>

`57-63`: _⚠️ Potential issue_

**Prop updates should be awaited for deterministic tests**  

`wrapper.setProps()` returns a promise that resolves on the next tick.  
Without `await`, assertions execute before the DOM updates, creating timing-dependent flakiness.

```diff
-    await wrapper.setProps({target: ['test1', 'test2']})
+    await wrapper.setProps({target: ['test1', 'test2']})
+    await flushPromises()
```

Apply the same pattern to the update on line 76.

</blockquote></details>
<details>
<summary>packages/bootstrap-vue-next/src/components/BToast/BToastOrchestrator.vue (1)</summary><blockquote>

`4-6`: _⚠️ Potential issue_

**Invalid `:key` attribute – missing expression**

`<div>` currently renders with `:key` but no value, which Vue treats as `undefined` and will throw a template compilation error.

```diff
-        :key
+        :key="key"
```

</blockquote></details>
<details>
<summary>packages/bootstrap-vue-next/src/components/BTooltip/BTooltip.vue (1)</summary><blockquote>

`84-87`: _⚠️ Potential issue_

**`computedProps` order allows user-supplied `noninteractive` to overwrite the calculated default**

The spread order means:

```ts
{ noninteractive: computed, ...rest }
```

is overridden if `rest` already contains `noninteractive`, negating the fallback you just computed.

Flip the order so your override wins only when the prop was **not** provided:

```diff
-return {noninteractive: noninteractive !== undefined ? noninteractive : !interactive, ...rest}
+return {...rest, noninteractive: noninteractive !== undefined ? noninteractive : !interactive}
```

</blockquote></details>
<details>
<summary>apps/docs/src/docs/composables/useToastController.md (1)</summary><blockquote>

`34-40`: _🛠️ Refactor suggestion_

**API reference still names the old `show` method**

The heading and paragraph describe “showing a toast through the **show** method”, yet the new API exposes `create`.  This inconsistency can confuse readers.

Suggested fix:

```diff
-## Showing a Toast
-
-Showing a toast is done through the show method
+## Creating a Toast
+
+Create a toast by calling the `create` method
```
…and replace further mentions of “show method” with “create method”.

</blockquote></details>
<details>
<summary>packages/bootstrap-vue-next/src/components/BTabs/BTabs.vue (1)</summary><blockquote>

`239-249`: _⚠️ Potential issue_

**`handleClick` double-invokes `onClick()`**

`onClick()` is executed once for the `typeof` check and again to obtain the returned function, causing side-effects to run twice.

A safer pattern:

```diff
-  if (
-    index >= 0 &&
-    !tabs.value[index].disabled &&
-    tabs.value[index]?.onClick &&
-    typeof tabs.value[index].onClick === 'function' &&
-    typeof tabs.value[index].onClick() === 'function'
-  ) {
-    tabs.value[index].onClick?.()(event)
-  }
+  const handler = tabs.value[index]?.onClick
+  if (index >= 0 && !tabs.value[index].disabled && typeof handler === 'function') {
+    const maybeNested = handler(event) // support direct signature
+    if (typeof maybeNested === 'function') {
+      maybeNested(event)               // support curried signature
+    }
+  }
```

• Avoids double execution  
• Supports both `onClick(event)` and `onClick()(event)` APIs

</blockquote></details>

</blockquote></details>

<details>
<summary>🧹 Nitpick comments (71)</summary><blockquote>

<details>
<summary>packages/nuxt/CHANGELOG.md (1)</summary><blockquote>

`2-4`: **Consistent markdown spacing**  
There are multiple instances of consecutive blank lines between headings and content (e.g. lines 2–4, 10–12, 21–23, 26–28) which violate MD012. Please reduce to a single blank line to improve readability and satisfy markdownlint.  



Also applies to: 10-12, 21-23, 26-28

</blockquote></details>
<details>
<summary>apps/docs/src/docs/components/form-tags.md (1)</summary><blockquote>

`217-225`: **Extracted and simplified script block — promising refactor**

- The inline script logic has been correctly replaced with a clean import of `data`, centralizing the component reference data.  
- Please verify that the import path and filename casing/extension match the actual file (`apps/docs/src/docs/data/components/formTags.data.ts` or `.js`) so that `<ComponentReference :data="data" />` continues to work as expected.  


I also recommend simplifying this block further by leveraging `<script setup lang="ts">` (as used elsewhere in our docs), which removes the need for the `export default` boilerplate:

```diff
-<script lang="ts">
-import {data} from '../../data/components/formTags.data'
-
-export default {
-  setup() {
-    return {data}
-  }
-}
-</script>
+<script setup lang="ts">
+import {data} from '../../data/components/formTags.data'
+</script>
```

</blockquote></details>
<details>
<summary>apps/docs/src/docs/components/progress.md (3)</summary><blockquote>

`5-6`: **Use component name in description**  
To maintain consistency and clarity, consider referring explicitly to the `<BProgress>` component rather than “our custom progress component.” For example:  
“Use the `<BProgress>` component to display simple or complex progress bars, supporting stacked bars, animated backgrounds, and text labels.”

---

`28-28`: **Refine custom label description**  
The sentence can be shortened for clarity. For example:  
```diff
- Need more control over the label? Provide your own label by using the default slot within a `BProgressBar` sub-component, or by using the `label` property on `BProgressBar`:
+ Need more control over the label? Use the default slot or the `label` prop on `BProgressBar` to provide your own:
```

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 LanguageTool</summary>

[misspelling] ~28-~28: This word is normally spelled as one.
Context: ...he default slot within a `BProgressBar` sub-component, or by using the `label` property on `B...

(EN_COMPOUNDS_SUB_COMPONENT)

</details>

</details>

---

`40-44`: **Optional: Clarify height section**  
To maintain consistency with other sections, specify `<BProgress>` and tighten the wording:  
```diff
- ## Height
- The height of the progress bar can be controlled with the height prop. The height value should be a standard CSS dimension (px, rem, em, etc.). The default height is 1rem.
+ ## Height
+ Use the `height` prop on `<BProgress>` to set its height (any valid CSS unit: `px`, `rem`, `em`). Default is `1rem`.
```

</blockquote></details>
<details>
<summary>apps/playground/src/components/Comps/TCarousel.vue (1)</summary><blockquote>

`86-107`: **Reactive state management for dynamic carousel.**

The implementation uses a timeout to update the images array after 1 second, demonstrating how the carousel can handle dynamically changing data. This is a good example of Vue's reactivity system.


However, consider adding a comment explaining the purpose of the timeout - it appears to be for demonstration purposes but this isn't explicitly stated.

```diff
 setTimeout(() => {
   images.value = [images.value[0], ...newValue]
-}, 1000)
+}, 1000) // Demonstrate dynamic slide addition after initial render
```

</blockquote></details>
<details>
<summary>packages/bootstrap-vue-next/src/composables/useActivatedFocusTrap.ts (1)</summary><blockquote>

`60-66`: **Remove unnecessary async keyword.**

The function is marked as `async` but doesn't contain any `await` statements after the changes. Consider removing the `async` keyword for clarity and slight performance improvement.


```diff
-  watch(resolvedIsActive, async (newValue) => {
+  watch(resolvedIsActive, (newValue) => {
    if (newValue && resolvedNoTrap.value === false) {
      trap.activate()
    } else {
      trap.deactivate()
    }
  })
```

</blockquote></details>
<details>
<summary>packages/bootstrap-vue-next/src/directives/BTooltip/index.ts (1)</summary><blockquote>

`39-40`: **Consider replacing delete with undefined assignment.**

Using the `delete` operator can impact performance. Consider using an undefined assignment instead.

```diff
-    delete binding.oldValue
+    binding.oldValue = undefined
```

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 Biome (1.9.4)</summary>

[error] 40-40: Avoid the delete operator which can impact performance.

Unsafe fix: Use an undefined assignment instead.


(lint/performance/noDelete)

</details>

</details>

</blockquote></details>
<details>
<summary>apps/docs/src/docs/components/tooltip.md (2)</summary><blockquote>

`37-38`: **Replace “focus is in it” → “focus is on it” for natural wording**

Minor grammar polish improves readability.

```diff
- ... when the mouse is over it or focus is in it, allowing users ...
+ ... when the mouse is over it or focus is on it, allowing users ...
```

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 LanguageTool</summary>

[uncategorized] ~38-~38: The preposition “on” seems more likely in this position than the preposition “in”.
Context: ...n when the mouse is over it or focus is in it, allowing users to interact with its...

(AI_EN_LECTOR_REPLACEMENT_PREPOSITION_IN_ON)

</details>

</details>

---

`77-83`: **Remove unused import `onMounted` to avoid build-time warnings**

`onMounted` is imported but never referenced in the script setup block.

```diff
-import {ref, onMounted} from 'vue'
+import {ref} from 'vue'
```

</blockquote></details>
<details>
<summary>packages/bootstrap-vue-next/src/components/BCollapse/BCollapse.vue (2)</summary><blockquote>

`55-58`: **Casting `emit` to generic `EmitFn` loses type-safety**

`emit as EmitFn` circumvents the strongly-typed `BCollapseEmits`.  
Prefer passing the specific `emit` directly to `useShowHide` (and generically typing that composable) to preserve compile-time checks.

---

`145-152`: **No need for double-wrapping computed generic**

`computed<ShowHideSlotsData>(...)` is redundant once the callback already returns a structurally compatible object; letting TS infer the type avoids mismatches (especially after adding `active`).

```diff
-const sharedSlots = computed<ShowHideSlotsData>(() => ({
+const sharedSlots = computed(() => ({
```

</blockquote></details>
<details>
<summary>apps/docs/src/data/components/toast.data.ts (2)</summary><blockquote>

`27-46`: **Align `closeVariant` type with colour enums**

Other variant-style props use the `ColorVariant | null` alias.  
Recommend the same here for consistency and autocomplete support.

```diff
-          closeVariant: {
-            type: 'string | null',
+          closeVariant: {
+            type: 'ColorVariant | null',
```

---

`85-96`: **Update `progress` docs now that `noProgress` exists**

`progressProps` description still claims *“No progress will be shown if left undefined”* but `noProgress` is the canonical flag now.  This can confuse users.

Action: rewrite one of the descriptions so the two props are not contradictory.

</blockquote></details>
<details>
<summary>packages/bootstrap-vue-next/src/components/BNavbar/BNavbarToggle.vue (2)</summary><blockquote>

`48-51`: **Simplify reactive unwrap logic & avoid nested `toValue`**

`toValue(toValue(showHideData.values.value.get(id))?.value)` double-unwraps and is hard to read.

```diff
-    return toValue(toValue(showHideData.values.value.get(props.target))?.value) || false
+    const entry = toValue(showHideData.values.value.get(props.target))
+    return entry ? toValue(entry.value) : false
```

Similarly adjust the array branch for clarity.

---

`55-59`: **Guard against undefined targets while toggling**

`showHideData.values.value.get(target)` may return `undefined`; calling `.toggle()` will then throw at runtime. The optional-chaining helps, but silently fails. Consider logging or throwing to aid debugging.

</blockquote></details>
<details>
<summary>packages/bootstrap-vue-next/src/composables/useToastController/index.ts (1)</summary><blockquote>

`28-31`: **Simplified return statement**

The return statement spreads the injected plugin object into a new object. This is a clean approach but could be simplified further.


```diff
-  return {
-    ...toastContollerPlugin,
-  }
+  return toastContollerPlugin
```

</blockquote></details>
<details>
<summary>packages/bootstrap-vue-next/src/composables/usePopoverController/index.ts (1)</summary><blockquote>

`24-27`: **Simplified return statement**

The return statement spreads the injected plugin object into a new object. This pattern is consistent with useToastController but could be simplified.


```diff
-  return {
-    ...popoverControllerPlugin,
-  }
+  return popoverControllerPlugin
```

</blockquote></details>
<details>
<summary>packages/bootstrap-vue-next/src/components/BDropdown/BDropdown.vue (2)</summary><blockquote>

`230-244`: **Duplicate ‘Escape’ handlers – consolidate to avoid drift**  

Two nearly identical blocks listen for `Escape`. Extract to a small util to prevent behavioural divergence the next time one is updated.

```diff
-onKeyStroke(
-  'Escape',
-  () => {
-    hide()
-    getElement(referenceElement.value)?.focus()
-  },
-  {target: referenceElement}
-)
-...
-onKeyStroke(
-  'Escape',
-  () => {
-    hide()
-    getElement(referenceElement.value)?.focus()
-  },
-  {target: floatingElement}
-)
+const escHandler = () => {
+  hide()
+  getElement(referenceElement.value)?.focus()
+}
+onKeyStroke('Escape', escHandler, {target: referenceElement})
+onKeyStroke('Escape', escHandler, {target: floatingElement})
```

---

`246-272`: **`setInterval` polling inside keyboard navigation is fragile**  

Polling every 16 ms until `isVisible` flips can create runaway timers in edge-cases. Vue’s `watch` or a single `nextTick()` after `show()` can achieve the same without a busy loop. Consider:

```ts
if (!showRef.value) {
  await show()
  await nextTick()
  return keynav(e, v)
}
```

</blockquote></details>
<details>
<summary>packages/bootstrap-vue-next/src/directives/toggle.spec.ts (1)</summary><blockquote>

`71-75`: **Class/ARIA assertions depend on asynchronous collapse animation**  

After the first click you wait `asyncTimeout(50)` before asserting.  
If the environment is slower (CI), this can still race. Prefer waiting for the `spyX` mocks or the button’s `aria-expanded` mutation via `await wrapper.vm.$nextTick()` rather than a magic number.

</blockquote></details>
<details>
<summary>apps/docs/src/docs/migration-guide.md (2)</summary><blockquote>

`603-604`: **Add missing determiner for clarity**

>`content` prop has been renamed to `body` …

Insert “the” before `content` for grammatical correctness:

```diff
-`content` prop has been renamed to `body`
+The `content` prop has been renamed to `body`
```

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 LanguageTool</summary>

[uncategorized] ~603-~603: You might be missing the article “the” here.
Context: ...recation of the `html` prop.  `content` prop has been renamed to `body` for consiste...

(AI_EN_LECTOR_MISSING_DETERMINER_THE)

</details>

</details>

---

`739-740`: Same grammatical issue here—prepend “The”.

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 LanguageTool</summary>

[uncategorized] ~739-~739: You might be missing the article “the” here.
Context: ...recation of the `html` prop.  `content` prop has been renamed to `body` for consiste...

(AI_EN_LECTOR_MISSING_DETERMINER_THE)

</details>

</details>

</blockquote></details>
<details>
<summary>packages/bootstrap-vue-next/src/plugins/showHide/index.ts (1)</summary><blockquote>

`40-41`: **Expose a readonly view of the registry**

Providing the raw `values` ref allows any consumer to mutate the map bypassing `register/unregister`.  
Wrap with `readonly()` before injection:

```diff
-    app.provide(globalShowHideStorageInjectionKey, {register, values})
+    app.provide(globalShowHideStorageInjectionKey, {register, values: readonly(values)})
```

</blockquote></details>
<details>
<summary>packages/bootstrap-vue-next/src/utils/floatingUi.ts (1)</summary><blockquote>

`66-70`: **Deprecation warning should be dev-only**

`console.warn` in production bloats logs.  Gate it behind an environment check, e.g.:

```ts
if (process.env.NODE_ENV !== 'production') {
  console.warn('v-b-popover/v-b-tooltip: `content` is deprecated, use `body` instead')
}
```

</blockquote></details>
<details>
<summary>packages/bootstrap-vue-next/src/components/BToast/BToastOrchestrator.vue (2)</summary><blockquote>

`13-21`: **Heavy `filter` call inside template**

`filter` executes on every render; with a large toast list this can become a hotspot.  
Consider a computed getter that groups toasts by position once, then iterate over that computed.

---

`30-57`: **Event handler grows large—move to named method**

Inline arrow functions with multi-step logic hinder readability and make stack-traces noisy. Extract them to `setup()` locals:

```ts
function onHide(e, val, promise, options) { … }
function onHidden(e, val, promise, options) { … }
```

</blockquote></details>
<details>
<summary>apps/docs/src/components/PageContentsItem.vue (3)</summary><blockquote>

`2-3`: **Keep prop access consistent to avoid cognitive overhead**

Line 2 uses `item` whereas line 3 uses `props.item`.  
Both resolve to the same value in the template, but mixing the two styles hurts readability and can confuse future contributors (they may wonder whether the two identifiers differ).  
Pick one convention—`item` is shorter—and apply it everywhere in this component.

```diff
-  <BNavItem :href="buildLink(item)" link-class="otp-link" :active="item.id === activeId">
-    <template #default>{{ props.item.text }}</template>
+  <BNavItem :href="buildLink(item)" link-class="otp-link" :active="item.id === activeId">
+    <template #default>{{ item.text }}</template>
```

---

`8-9`: **Avoid non-null assertions on untrusted data**

`child.id!` will crash at runtime if a child accidentally lacks an `id`.  
Either add a run-time guard or enforce non-optional `id` in the `ContentsItem` type so the compiler guarantees safety.

```diff
-  :key="child.id!"
+  :key="child.id"
```

If the type must remain optional, insert a fallback:

```vue
:key="child.id ?? child.text"
```

---

`28-33`: **`childrenVisible` recurses on every render – memoise or cache if performance degrades**

For large documents `childrenVisible` walks the full subtree on each re-render.  
If you notice sluggish nav updates, consider caching the result per `activeId`.  
For now it’s fine, just something to keep in mind.

</blockquote></details>
<details>
<summary>packages/bootstrap-vue-next/src/components/BTooltip/BTooltip.vue (1)</summary><blockquote>

`20-22`: **Minor: simplify slot forwarding**

`v-if="slots.default"` is unnecessary—Vue omits empty slots automatically.  
Removing the check trims template size:

```diff
-<template v-if="slots.default" #default="scope">
-  <slot v-bind="scope" />
-</template>
+<template #default="scope"><slot v-bind="scope" /></template>
```

</blockquote></details>
<details>
<summary>packages/bootstrap-vue-next/src/components/BModal/BModalOrchestrator.vue (1)</summary><blockquote>

`5-8`: **Shadowing `_component` identifier hampers readability**

You destructure `{component: _component}` and immediately reference `_component` in the same tag.  
Because `_component` is reused for *both* the destructured value **and** the ref-forward target, a reader must trace scope manually.

Consider renaming the destructured variable:

```diff
-:is="_component ?? BModal"
-v-for="[key, {component: _component, ...}] in tools.modals?.value"
+:is="modalComp ?? BModal"
+v-for="[key, {component: modalComp, ...}] in tools.modals?.value"
```

</blockquote></details>
<details>
<summary>apps/docs/src/docs/composables/useToggle.md (3)</summary><blockquote>

`5-7`: **Grammar & clarity – refine introductory sentence**

Small wording tweaks will improve readability:

```diff
-You can use `useToggle` to get the closest toggleable component in **child component** and show,hide or toggle it. It can also be supplied a target id to show, hide or toggle a specific component
+`useToggle` lets a **child component** obtain its nearest toggle-able ancestor and show, hide, or toggle it.  
+Alternatively, supply a target **ID** to control a specific component.
```

Key points  
• insert missing article (“a child component”)  
• add spaces after commas  
• use “ID” (uppercase) for consistency  
• split the long sentence for easier scanning

---

`34-36`: **Minor grammar & terminology fixes**

```diff
-You can also provide an id to get particular component and show/hide it. Currently, we do not support using CSS selector to
-find modal since the component in lazy mode may not render at page initial.
+You can instead pass an **ID** to target a particular component and show or hide it.  
+Using a CSS selector is not yet supported because lazily–rendered components might not be present at page initialization.
```

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 LanguageTool</summary>

[uncategorized] ~35-~35: Use a comma before ‘and’ if it connects two independent clauses (unless they are closely connected and short).
Context: ...l. If the modal component does not exist and you attempt to call any of the exposed ...

(COMMA_COMPOUND_SENTENCE)

</details>

</details>

---

`69-70`: **Spelling – “whether”**

```diff
-control wether to resolve the promise
+control whether to resolve the promise
```

</blockquote></details>
<details>
<summary>apps/docs/src/docs/composables/usePopoverController.md (2)</summary><blockquote>

`25-28`: **Verb agreement – “access” → “accesses”**

```diff
-The only props it access are `teleportDisabled` and `teleportTo` to modify the location that it is placed
+The only props it **accesses** are `teleportDisabled` and `teleportTo`, which modify where it is placed
```

---

`58-62`: **Plural agreement – “methods accept”**

```diff
-The methods accepts reactive properties using `MaybeRef`,
+The methods **accept** reactive properties via `MaybeRef`,
```

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 LanguageTool</summary>

[grammar] ~60-~60: You should probably use “accept”.
Context: ...in `popover` and `tooltip`  The methods accepts reactive properties using `MaybeRef`, a...

(AGREEMENT_SENT_START)

</details>

</details>

</blockquote></details>
<details>
<summary>apps/docs/src/docs/composables/useToastController.md (4)</summary><blockquote>

`55-62`: **Outdated API name & minor grammar**

```diff
-The `show` method returns a `promise` that is resolved then the toast closes. You can give toast a unique id. Since `Toasts` are fluid and can move around a lot, returning the index at a given point in time is not ideal for as its position may be changed in the array. So, for use with the `remove` method, you need to give a unique identifier
+The `create` method returns a **Promise** that resolves when the toast closes.  
+Because toasts are fluid and can reorder themselves, an index is unreliable; assign a unique **id** if you plan to call `remove`.
```
Also change “effects” → “affects” a few lines below.

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 LanguageTool</summary>

[style] ~55-~55: This phrase is redundant. Consider writing “point” or “time”.
Context: ...d a lot, returning the index at a given point in time is not ideal for as its position may be...

(MOMENT_IN_TIME)

---

[uncategorized] ~61-~61: Did you mean “affects”?
Context: ...cludes `position`. The `position` value effects its position, its type is [Container Po...

(EFFECTS_TO_AFFECTS)

</details>

</details>

---

`106-107`: **Typo – “reactvity”**

```diff
-For reactvity, you must use a getter function.
+For reactivity, you must use a getter function.
```

---

`152-158`: **Style – avoid weak intensifier**

```diff
-Hiding a `Toast` programmatically is very simple.
+Hiding a `Toast` programmatically is straightforward.
```

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 LanguageTool</summary>

[style] ~152-~152: As an alternative to the over-used intensifier ‘very’, consider replacing this phrase.
Context: ...t  Hiding a `Toast` programmatically is very simple. `create` return an object that has fun...

(EN_WEAK_ADJECTIVE)

</details>

</details>

---

`303-321`: **Markdown-lint violations (blank lines & trailing spaces)**

There are multiple consecutive blank lines and trailing spaces (MD012, MD009).  
Run `markdownlint --fix` or trim the excess blank lines/trailing whitespace.

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 markdownlint-cli2 (0.17.2)</summary>

303-303: Multiple consecutive blank lines
Expected: 1; Actual: 2

(MD012, no-multiple-blanks)

---

313-313: Trailing spaces
Expected: 0 or 2; Actual: 1

(MD009, no-trailing-spaces)

---

314-314: Trailing spaces
Expected: 0 or 2; Actual: 1

(MD009, no-trailing-spaces)

---

316-316: Trailing spaces
Expected: 0 or 2; Actual: 1

(MD009, no-trailing-spaces)

</details>

</details>

</blockquote></details>
<details>
<summary>packages/bootstrap-vue-next/src/components/BTabs/BTabs.vue (1)</summary><blockquote>

`142-178`: **Computed fallback logic – ensure `BTab` identity check is robust**

`tab.type === BTab` will fail if consumers import `BTab` via a different ES module instance (e.g., bundler optimisation) or use `resolveComponent('BTab')` in templates.

Consider a more tolerant check:

```diff
-    (tab) => tab.type === BTab
+    (tab) =>
+      tab.type === BTab ||
+      (typeof tab.type === 'object' && 'name' in tab.type && tab.type.name === 'BTab')
```

This avoids silently skipping valid `<BTab>` nodes.

</blockquote></details>
<details>
<summary>packages/bootstrap-vue-next/src/directives/BToggle/index.ts (2)</summary><blockquote>

`64-82`: **Asynchronous `forEach` hides errors and can cause race conditions**  
`Array.prototype.forEach` does not await the passed async callback.
Errors are swallowed and registration order is non-deterministic.  Use a
`for..of` loop (or `Promise.all`) instead:

```diff
-  targets.forEach(async (targetId) => {
+  for (const targetId of targets) {
+    /* eslint-disable no-await-in-loop */
     let count = 0
     ...
     toValue(showHide).registerTrigger('click', el)
-      break
-    }
-  })
+      break
+    }
+  }
```

This guarantees sequential attempts and surfacing of any thrown errors.

---

`84-85`: **Set an initial `aria-expanded` value for better accessibility**  
`aria-controls` is written, but `aria-expanded` is never initialised.  
Screen-readers expect it to be `false` (collapsed) until the first
user-initiated toggle.  Consider:

```ts
el.setAttribute('aria-controls', targets.join(' '))
el.setAttribute('aria-expanded', 'false')
```

(The value can then be kept in sync inside the `showHide` instance.)

</blockquote></details>
<details>
<summary>packages/bootstrap-vue-next/CHANGELOG.md (1)</summary><blockquote>

`1638-1639`: **Consider adding CSS stylesheet import information**

Since this PR is focused on moving SCSS out of components, it might be helpful to add a note in a future changelog about how users should ensure proper stylesheet imports, especially if they're using custom build processes that might not automatically include the separated SCSS files.

</blockquote></details>
<details>
<summary>packages/bootstrap-vue-next/src/components/BModal/BModal.vue (2)</summary><blockquote>

`26-26`: **Consider merging inline `display:block` into the reactive `:style` binding**

`style="display: block"` is now hard-coded while `:style="computedZIndex"` supplies a dynamic object.  
Although Vue merges the two, keeping all presentation concerns in the reactive object avoids accidental style overrides (e.g. another computed style later setting `display:none`) and makes unit testing easier.

```diff
-        :style="computedZIndex"
-        style="display: block"
+        :style="[{display:'block'}, computedZIndex]"
```

[nitpick]

---

`79-92`: **Pass-through classes are fine, but add them to the prop JSDoc**

`cancelClass`/`okClass` are forwarded correctly, yet their intent is not obvious to consumers scanning only TS types or SFC props.  
Adding a short description to the `ComponentProps.ts` definitions (or local JSDoc) will prevent confusion with the already–existing `cancelVariant` / `okVariant`.

</blockquote></details>
<details>
<summary>packages/bootstrap-vue-next/src/components/BToast/BToast.vue (2)</summary><blockquote>

`14-15`: **Same `display:block` duplication as in `BModal`**

For consistency (and to avoid style collisions) consider moving the static declaration into the reactive `:style` array:

```diff
-      style="display: block"
+      :style="[{display:'block'}]"
```

---

`53-66`: **Guard against accidental navigation when `computedLink` is a router-link**

`@click="computedLink ? hide() : () => {}"` will still call `hide()` before the router navigation occurs.  
If the toast is navigational feedback you might want to wait for the navigation promise to resolve and then hide:

```diff
-            @click="computedLink ? hide() : () => {}"
+            @click="computedLink ? ($event) => { $event.preventDefault(); hide(); } : () => {}"
```

Optional, but prevents flicker when the toast instantly disappears on the same click that triggers a route change.

</blockquote></details>
<details>
<summary>packages/bootstrap-vue-next/src/plugins/toastController/index.ts (2)</summary><blockquote>

`170-174`: **Defaulting `modelValue` to `5000` conflates “visible” with “auto-hide in N ms”.**  

When no `modelValue` is provided you coerce it to `5000`, implicitly enabling auto-hide.  
This surprises callers who simply expect “`undefined` → closed”.  A clearer pattern is:

* default to `false` (closed)  
* if a *number* is explicitly supplied, treat it as the timeout

```diff
- v.modelValue = v.modelValue ?? 5000
+ if (v.modelValue === undefined) {
+   v.modelValue = false
+ } else if (v.modelValue === true) {
+   // Back-compat: convert boolean true to default timeout
+   v.modelValue = 5000
+ }
```

This avoids unexpected behaviour changes for existing code that relied on the previous boolean semantics.

---

`214-223`: **Deprecation warning every call – consider `once` throttling**  

`show()` logs a console warning every time it is invoked.  In loops or reactive updates this can spam the console.  Wrap the warning in a `once` guard to keep the message helpful without becoming noise.

</blockquote></details>
<details>
<summary>apps/docs/src/data/components/alert.data.ts (3)</summary><blockquote>

`63-66`: **Copy-paste description references the *footer* instead of the *header***  

`headerTag`’s description says:  
“Specify the HTML tag to render instead of the default tag for the **footer**”.

It should reference the *header* element to avoid confusion.

---

`145-146`: **`close-countdown` event description looks copy-pasted and is misleading**  

Current: “Content to place in the alert”.  
Expected: something like “Emitted periodically while the countdown is running”.

Updating the description improves API clarity for consumers.

---

`159-161`: **Argument name mirrors the event name instead of the actual prop**  

For the `update:model-value` event, the argument should be `modelValue`, not `update:model-value`.

```diff
- arg: 'update:model-value',
+ arg: 'modelValue',
```

This keeps the docs consistent with emitted payloads.

</blockquote></details>
<details>
<summary>packages/bootstrap-vue-next/src/types/ComponentProps.ts (2)</summary><blockquote>

`785-808`: **Duplicate `variant` prop via multiple inheritance may confuse consumers**  

`BAlertProps` extends both `ColorExtendables` **and** `Omit<BLinkProps, 'routerTag'>`.  
Each base interface already defines a `variant?: ColorVariant | null`.

While TypeScript happily merges the duplicates, IDE tooltips and generated docs often show *both* entries, leading to ambiguity (“which one should I set?”).

If the `BLink`-derived variant is not actually used by `BAlert`, consider projecting it out:

```ts
extends ColorExtendables,
  Omit<BLinkProps, 'routerTag' | 'variant'>,
  ShowHideProps
```

---

`1240-1253`: **New `noProgress` flag for `BToast` needs defensive type-level coupling**  

`noProgress` disables the progress bar, but callers can still pass `progressProps`, which will be ignored.  
To prevent “impossible” combinations you can model the two states with a discriminated union:

```ts
export type BToastProps =
  | (CommonToastProps & { noProgress?: false; progressProps?: ProgressProps })
  | (CommonToastProps & { noProgress: true; progressProps?: never });
```

This gives compile-time feedback instead of silent runtime precedence.

</blockquote></details>
<details>
<summary>packages/bootstrap-vue-next/src/composables/useModalController/index.ts (2)</summary><blockquote>

`38-44`: **Redundant `id` check can be removed for clarity**

Inside the `hide` helper we already know `id === undefined` because we are in the first branch, so the second condition is redundant and slightly obscures intention.

```diff
-  if (id === undefined) {
-    if (id === undefined && lastStack?.value) {
-      lastStack?.value.exposed?.hide(trigger)
-    }
-  } else {
+  if (id === undefined) {
+    lastStack?.value?.exposed?.hide(trigger)
+  } else {
```

---

`103-111`: **Tail-recursion risks & internal API reliance**

`findBModal` walks parents until it meets `component.parent.type.__name ===
'BModal'`.

1.  Deep component trees could theoretically blow the stack (edge-case
    but worth noting).
2.  Using `__name` relies on a Vue private field that is mangled in some
    bundlers/minifiers.  Relying on the component’s `name` option or a
    custom symbol would be safer.

</blockquote></details>
<details>
<summary>apps/docs/.vitepress/theme/Layout.vue (1)</summary><blockquote>

`337-340`: **Console noise in production**

The warning here will ship to production bundles.  Consider gating it
behind `import.meta.env.DEV` or removing it entirely.

</blockquote></details>
<details>
<summary>apps/docs/src/docs/composables/useModalController.md (2)</summary><blockquote>

`150-156`: **Minor grammar & clarity improvements**

```diff
-The `create` method will return a promise that resolves after the modal has been hidden to a `BvTriggableEvent` object.
-using the option 'resolveOnHide' the second argument of the `create` method will resolve the promise at time of hiding the modal, rather than waiting for the modal to be fully hidden.
+The `create` method returns a promise that resolves to a `BvTriggerableEvent` object once the modal is fully hidden.  
+If you pass the `{ resolveOnHide: true }` option as the second argument, the promise resolves **at the time the modal starts hiding**, instead of waiting for the hidden transition to finish.
```

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 LanguageTool</summary>

[misspelling] ~151-~151: Did you mean “at a time”, “at the time”, or “at times”?
Context: ...create` method will resolve the promise at time of hiding the modal, rather than waitin...

(AT_TIME2)

</details>

</details>

---

`190-192`: **Fix typographical error**

```diff
-Modal is destoyed when the current scope is exited. Or you can destroy it manually by calling the `destroy` method.
+The modal is destroyed when the current scope is exited, or you can destroy it manually by calling the `destroy` method.
```

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 LanguageTool</summary>

[uncategorized] ~190-~190: Did you mean: “By default,”?
Context: ...ces associated with it.  ### Lifecycle  By default the modal is destroyed once it's closed...

(BY_DEFAULT_COMMA)

</details>

</details>

</blockquote></details>
<details>
<summary>packages/bootstrap-vue-next/src/composables/useShowHide.ts (3)</summary><blockquote>

`246-255`: **Type-safety: resolver cast hides errors**

`_Resolve` is cast to `(...args)=>void` to satisfy TypeScript, silently bypassing type checks.  
Prefer narrowing the union instead of overriding the checker:

```diff
-      _Promise = new Promise<string>((resolve) => {
-        ;(_Resolve as (value: string | PromiseLike<string>) => void) = resolve
-      })
+      _Promise = new Promise<string>((resolve) => {
+        _Resolve = resolve as (value: boolean | string) => void
+      })
```

(or define a dedicated `_RejectString` resolver).

Accidental casts tend to re-appear during refactors and defeat the benefit of strict typing.

---

`340-358`: **Trigger registry can accumulate duplicates**

`registerTrigger` pushes blindly into the `triggerRegistry` array.  
If the same element is registered twice (e.g. component re-renders before unloading) two listeners will be attached, leading to duplicated `toggle` execution.

Consider guarding with a `Set` *or* deduplicating:

```diff
const key = `${trigger}-${el as unknown as number}`
if (triggerRegistry.some(t => t.trigger === trigger && t.el === el)) return
```

or migrate to `Map<Element, Set<string>>` for O(1) checks and simpler cleanup.

---

`370-379`: **Inefficient DOM writes in reactive loop**

Every change to `modelValue` iterates through **all** registered triggers and performs three DOM mutations (`setAttribute` + 2×`classList.toggle`).  
For many triggers this becomes noticeable.

Possible micro-optimisation:
* Skip the loop when the visible state did not change.
* Batch DOM writes via `requestAnimationFrame`.

Not critical but worth considering.

</blockquote></details>
<details>
<summary>apps/docs/src/data/components/table.data.ts (2)</summary><blockquote>

`918-931`: **Spelling / wording – “sticky colum”**

`description` contains a typo (“sticky colum”). It should read “sticky column”.

```diff
-                'If this will be a sticky colum. Must be set on all cells in this column. table must be in sticky-header or responsive mode to work',
+                'If this will be a sticky column. Must be set on all cells in this column. The table must be in sticky-header or responsive mode to work',
```

---

`858-867`: **Duplicate colspan/rowspan descriptions**

The same description blocks for `BTd` and `BTh` are copied verbatim.  
Consider extracting a small helper (e.g. `buildSpanProps()`) to avoid drift when wording changes.

</blockquote></details>
<details>
<summary>packages/bootstrap-vue-next/src/components/BAlert/BAlert.vue (1)</summary><blockquote>

`195-196`: **Left-over TODO**

`// TODO solid is never used` still exists and indicates unfinished cleanup.  
Either:

1. Remove the unused prop, or  
2. Implement the `solid` styling toggle.

Unresolved TODOs quickly become stale.

</blockquote></details>
<details>
<summary>packages/bootstrap-vue-next/src/utils/keys.ts (3)</summary><blockquote>

`172-176`: **Return a typed disposable from `register`**

The current return value only exposes `unregister`, yet most callers will also need a reference to the final `RegisterShowHideMapValue`—for example, to await `toggle()` or inspect `value`.

Consider:

```diff
-register: (input: RegisterShowHideFnInput) => {
-  unregister: () => void
-}
+register: (input: RegisterShowHideFnInput) => {
+  unregister: () => void
+  entry: RegisterShowHideMapValue
+}
```

This small addition removes the need for consumers to re-derive the entry from the internal `values` map and keeps encapsulation intact.

---

`222-235`: **Keep internal flags truly internal**

`_isAppend` and `_isOrchestratorInstalled` leak implementation details through the public Toast plugin interface.  If end-users should not rely on them, mark them as `readonly` and prefix with `__internal` or move them behind a non-exported symbol.  Otherwise, they become part of the public contract and will be hard to remove later.

---

`239-254`: **Deprecated API surface can be slimmed further**

The Modal plugin still exposes three separate deprecated helpers (`show`, `confirm`, `show`-boolean) alongside the new `create`.  To reduce maintenance cost:

1. Rename them to `deprecatedShow/Confirm` to make usage grep-friendly.
2. Add explicit JSDoc `@deprecated` tags so IDEs surface warnings.
3. Plan a follow-up PR to remove them after one minor version.

</blockquote></details>
<details>
<summary>packages/bootstrap-vue-next/src/types/ComponentOrchestratorTypes.ts (1)</summary><blockquote>

`13-22`: **DRY up duplicated “promise-like controller” interfaces**

`PromiseWithModalInternal`, `PromiseWithToastInternal`, and `PromiseWithPopoverInternal` repeat eight identical fields with only two type-variant ones (`ref`, parameter of `set`).  A generic base interface slashes ~60 lines and prevents future divergence:

```ts
interface PromiseWithController<R, P> extends AsyncDisposable {
  id: ControllerKey
  ref: ComponentPublicInstance<R> | null
  show: () => P
  hide: (trigger?: string) => P
  toggle: () => P
  set: (val: Partial<unknown>) => P
  get: () => unknown | undefined
  destroy: () => void
}
```

Then:

```ts
type PromiseWithModalInternal = PromiseWithController<typeof BModal, PromiseWithModal>
```

This also makes it trivial to extend to future orchestrators without copy-pasting.

</blockquote></details>

</blockquote></details>

<details>
<summary>📜 Review details</summary>

**Configuration used: CodeRabbit UI**
**Review profile: CHILL**
**Plan: Pro**


<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between c45b4116348d31b1a5ab0e227c3d78ad03a2a50c and 0ec06659ec308daf649dbf7f7f62efe580474b7f.

</details>

<details>
<summary>⛔ Files ignored due to path filters (1)</summary>

* `pnpm-lock.yaml` is excluded by `!**/pnpm-lock.yaml`

</details>

<details>
<summary>📒 Files selected for processing (105)</summary>

* `.github/workflows/release-main.yaml` (1 hunks)
* `.release-please-manifest.json` (1 hunks)
* `.vscode/launch.json` (1 hunks)
* `apps/docs/.vitepress/theme/Layout.vue` (6 hunks)
* `apps/docs/package.json` (1 hunks)
* `apps/docs/src/components/ComponentReference.vue` (1 hunks)
* `apps/docs/src/components/PageContents.vue` (1 hunks)
* `apps/docs/src/components/PageContentsItem.vue` (1 hunks)
* `apps/docs/src/components/TableOfContentsNav.vue` (2 hunks)
* `apps/docs/src/data/components/alert.data.ts` (2 hunks)
* `apps/docs/src/data/components/modal.data.ts` (2 hunks)
* `apps/docs/src/data/components/popover.data.ts` (2 hunks)
* `apps/docs/src/data/components/progress.data.ts` (3 hunks)
* `apps/docs/src/data/components/table.data.ts` (15 hunks)
* `apps/docs/src/data/components/toast.data.ts` (3 hunks)
* `apps/docs/src/docs/components.md` (1 hunks)
* `apps/docs/src/docs/components/demo/ProgressAnimated.vue` (1 hunks)
* `apps/docs/src/docs/components/demo/ProgressBackgrounds.vue` (1 hunks)
* `apps/docs/src/docs/components/demo/ProgressBasic.vue` (1 hunks)
* `apps/docs/src/docs/components/demo/ProgressCustomLabels.vue` (1 hunks)
* `apps/docs/src/docs/components/demo/ProgressHeight.vue` (1 hunks)
* `apps/docs/src/docs/components/demo/ProgressLabels.vue` (1 hunks)
* `apps/docs/src/docs/components/demo/ProgressMultipleBars.vue` (1 hunks)
* `apps/docs/src/docs/components/demo/ProgressStriped.vue` (1 hunks)
* `apps/docs/src/docs/components/demo/ProgressWidth.vue` (1 hunks)
* `apps/docs/src/docs/components/form-tags.md` (1 hunks)
* `apps/docs/src/docs/components/link.md` (1 hunks)
* `apps/docs/src/docs/components/progress.md` (1 hunks)
* `apps/docs/src/docs/components/spinner.md` (0 hunks)
* `apps/docs/src/docs/components/tooltip.md` (1 hunks)
* `apps/docs/src/docs/composables.md` (1 hunks)
* `apps/docs/src/docs/composables/useModalController.md` (8 hunks)
* `apps/docs/src/docs/composables/usePopoverController.md` (1 hunks)
* `apps/docs/src/docs/composables/useToastController.md` (8 hunks)
* `apps/docs/src/docs/composables/useToggle.md` (1 hunks)
* `apps/docs/src/docs/migration-guide.md` (3 hunks)
* `apps/docs/src/types/index.ts` (2 hunks)
* `apps/playground/package.json` (1 hunks)
* `apps/playground/src/components/Comps/TCarousel.vue` (1 hunks)
* `apps/playground/src/components/Comps/TModal.vue` (3 hunks)
* `apps/playground/src/components/Comps/TToast.vue` (2 hunks)
* `package.json` (1 hunks)
* `packages/bootstrap-vue-next/CHANGELOG.md` (1 hunks)
* `packages/bootstrap-vue-next/package.json` (3 hunks)
* `packages/bootstrap-vue-next/src/components/BAccordion/BAccordionItem.vue` (2 hunks)
* `packages/bootstrap-vue-next/src/components/BAlert/BAlert.vue` (1 hunks)
* `packages/bootstrap-vue-next/src/components/BAlert/_alert.scss` (1 hunks)
* `packages/bootstrap-vue-next/src/components/BAlert/alert.spec.ts` (16 hunks)
* `packages/bootstrap-vue-next/src/components/BBreadcrumb/breadcrumb.spec.ts` (9 hunks)
* `packages/bootstrap-vue-next/src/components/BCarousel/BCarousel.vue` (1 hunks)
* `packages/bootstrap-vue-next/src/components/BCollapse/BCollapse.vue` (3 hunks)
* `packages/bootstrap-vue-next/src/components/BDropdown/BDropdown.vue` (12 hunks)
* `packages/bootstrap-vue-next/src/components/BModal/BModal.vue` (11 hunks)
* `packages/bootstrap-vue-next/src/components/BModal/BModalOrchestrator.vue` (3 hunks)
* `packages/bootstrap-vue-next/src/components/BModal/modal.spec.ts` (34 hunks)
* `packages/bootstrap-vue-next/src/components/BNav/BNavItemDropdown.vue` (2 hunks)
* `packages/bootstrap-vue-next/src/components/BNavbar/BNavbarToggle.vue` (2 hunks)
* `packages/bootstrap-vue-next/src/components/BOffcanvas/BOffcanvas.vue` (4 hunks)
* `packages/bootstrap-vue-next/src/components/BPagination/BPagination.vue` (1 hunks)
* `packages/bootstrap-vue-next/src/components/BPagination/pagination.spec.ts` (2 hunks)
* `packages/bootstrap-vue-next/src/components/BPopover/BPopover.vue` (12 hunks)
* `packages/bootstrap-vue-next/src/components/BPopover/BPopoverOrchestrator.vue` (1 hunks)
* `packages/bootstrap-vue-next/src/components/BPopover/popover.spec.ts` (1 hunks)
* `packages/bootstrap-vue-next/src/components/BTable/BTable.vue` (1 hunks)
* `packages/bootstrap-vue-next/src/components/BTable/BTableSimple.vue` (1 hunks)
* `packages/bootstrap-vue-next/src/components/BTable/_table.scss` (1 hunks)
* `packages/bootstrap-vue-next/src/components/BTabs/BTab.vue` (1 hunks)
* `packages/bootstrap-vue-next/src/components/BTabs/BTabs.vue` (7 hunks)
* `packages/bootstrap-vue-next/src/components/BToast/BToast.vue` (6 hunks)
* `packages/bootstrap-vue-next/src/components/BToast/BToastOrchestrator.vue` (3 hunks)
* `packages/bootstrap-vue-next/src/components/BToast/_toast.scss` (1 hunks)
* `packages/bootstrap-vue-next/src/components/BToast/toast-orchestrator.spec.ts` (1 hunks)
* `packages/bootstrap-vue-next/src/components/BToast/toast.spec.ts` (1 hunks)
* `packages/bootstrap-vue-next/src/components/BTooltip/BTooltip.vue` (3 hunks)
* `packages/bootstrap-vue-next/src/composables/index.ts` (1 hunks)
* `packages/bootstrap-vue-next/src/composables/useActivatedFocusTrap.ts` (1 hunks)
* `packages/bootstrap-vue-next/src/composables/useFormSelect.ts` (1 hunks)
* `packages/bootstrap-vue-next/src/composables/useModal/index.ts` (1 hunks)
* `packages/bootstrap-vue-next/src/composables/useModalController/index.ts` (1 hunks)
* `packages/bootstrap-vue-next/src/composables/usePopoverController/index.ts` (1 hunks)
* `packages/bootstrap-vue-next/src/composables/useShowHide.ts` (11 hunks)
* `packages/bootstrap-vue-next/src/composables/useToastController/index.ts` (1 hunks)
* `packages/bootstrap-vue-next/src/composables/useToggle/index.ts` (1 hunks)
* `packages/bootstrap-vue-next/src/directives/BPopover/index.ts` (2 hunks)
* `packages/bootstrap-vue-next/src/directives/BToggle/index.ts` (2 hunks)
* `packages/bootstrap-vue-next/src/directives/BTooltip/index.ts` (2 hunks)
* `packages/bootstrap-vue-next/src/directives/toggle.spec.ts` (3 hunks)
* `packages/bootstrap-vue-next/src/plugins/modalController/index.ts` (1 hunks)
* `packages/bootstrap-vue-next/src/plugins/popoverController/index.ts` (1 hunks)
* `packages/bootstrap-vue-next/src/plugins/showHide/index.ts` (1 hunks)
* `packages/bootstrap-vue-next/src/plugins/toastController/index.ts` (2 hunks)
* `packages/bootstrap-vue-next/src/types/BootstrapVueOptions.ts` (1 hunks)
* `packages/bootstrap-vue-next/src/types/ComponentEmits.ts` (1 hunks)
* `packages/bootstrap-vue-next/src/types/ComponentOrchestratorTypes.ts` (1 hunks)
* `packages/bootstrap-vue-next/src/types/ComponentProps.ts` (8 hunks)
* `packages/bootstrap-vue-next/src/types/ComponentSlots.ts` (1 hunks)
* `packages/bootstrap-vue-next/src/types/Tab.ts` (1 hunks)
* `packages/bootstrap-vue-next/src/types/index.ts` (1 hunks)
* `packages/bootstrap-vue-next/src/utils/classes.ts` (2 hunks)
* `packages/bootstrap-vue-next/src/utils/flattenFragments.ts` (1 hunks)
* `packages/bootstrap-vue-next/src/utils/floatingUi.ts` (2 hunks)
* `packages/bootstrap-vue-next/src/utils/keys.ts` (4 hunks)
* `packages/bootstrap-vue-next/tsconfig.vitest.json` (1 hunks)
* `packages/nuxt/CHANGELOG.md` (1 hunks)
* `packages/nuxt/package.json` (2 hunks)

</details>

<details>
<summary>💤 Files with no reviewable changes (1)</summary>

* apps/docs/src/docs/components/spinner.md

</details>

<details>
<summary>✅ Files skipped from review due to trivial changes (32)</summary>

* packages/bootstrap-vue-next/src/composables/index.ts
* package.json
* .release-please-manifest.json
* .vscode/launch.json
* apps/docs/src/docs/components/link.md
* apps/docs/src/docs/components/demo/ProgressBasic.vue
* apps/docs/src/docs/components/demo/ProgressCustomLabels.vue
* packages/bootstrap-vue-next/src/components/BTable/BTableSimple.vue
* packages/bootstrap-vue-next/src/components/BTable/BTable.vue
* apps/docs/package.json
* packages/bootstrap-vue-next/src/components/BNav/BNavItemDropdown.vue
* apps/docs/src/docs/components/demo/ProgressLabels.vue
* apps/docs/src/docs/composables.md
* packages/bootstrap-vue-next/src/components/BTable/_table.scss
* apps/docs/src/docs/components/demo/ProgressStriped.vue
* apps/docs/src/components/TableOfContentsNav.vue
* apps/docs/src/docs/components/demo/ProgressBackgrounds.vue
* packages/bootstrap-vue-next/src/types/BootstrapVueOptions.ts
* packages/bootstrap-vue-next/src/utils/classes.ts
* apps/docs/src/docs/components/demo/ProgressHeight.vue
* apps/playground/package.json
* apps/docs/src/docs/components/demo/ProgressAnimated.vue
* apps/docs/src/docs/components/demo/ProgressWidth.vue
* apps/docs/src/docs/components/demo/ProgressMultipleBars.vue
* packages/bootstrap-vue-next/src/utils/flattenFragments.ts
* packages/bootstrap-vue-next/src/types/index.ts
* apps/docs/src/data/components/progress.data.ts
* apps/docs/src/types/index.ts
* packages/bootstrap-vue-next/src/components/BModal/modal.spec.ts
* packages/bootstrap-vue-next/package.json
* packages/bootstrap-vue-next/src/types/ComponentEmits.ts
* packages/bootstrap-vue-next/src/types/ComponentSlots.ts

</details>

<details>
<summary>🚧 Files skipped from review as they are similar to previous changes (2)</summary>

* packages/bootstrap-vue-next/src/components/BToast/_toast.scss
* packages/bootstrap-vue-next/src/components/BOffcanvas/BOffcanvas.vue

</details>

<details>
<summary>🧰 Additional context used</summary>

<details>
<summary>🧠 Learnings (4)</summary>

<details>
<summary>packages/bootstrap-vue-next/src/components/BPopover/BPopoverOrchestrator.vue (1)</summary>

```
Learnt from: xvaara
PR: bootstrap-vue-next/bootstrap-vue-next#2425
File: packages/bootstrap-vue-next/src/plugins/popoverController/index.ts:138-140
Timestamp: 2025-04-28T22:48:46.738Z
Learning: In bootstrap-vue-next, `PopoverOrchestratorParam` includes an `id` field of type `ControllerKey` through inheritance from base types. This field is declared in the `BvControllerOptions` interface and propagated through the type hierarchy.
```

</details>
<details>
<summary>packages/bootstrap-vue-next/src/plugins/popoverController/index.ts (1)</summary>

```
Learnt from: xvaara
PR: bootstrap-vue-next/bootstrap-vue-next#2425
File: packages/bootstrap-vue-next/src/plugins/popoverController/index.ts:138-140
Timestamp: 2025-04-28T22:48:46.738Z
Learning: In bootstrap-vue-next, `PopoverOrchestratorParam` includes an `id` field of type `ControllerKey` through inheritance from base types. This field is declared in the `BvControllerOptions` interface and propagated through the type hierarchy.
```

</details>
<details>
<summary>packages/bootstrap-vue-next/src/types/ComponentOrchestratorTypes.ts (1)</summary>

```
Learnt from: xvaara
PR: bootstrap-vue-next/bootstrap-vue-next#2425
File: packages/bootstrap-vue-next/src/plugins/popoverController/index.ts:138-140
Timestamp: 2025-04-28T22:48:46.738Z
Learning: In bootstrap-vue-next, `PopoverOrchestratorParam` includes an `id` field of type `ControllerKey` through inheritance from base types. This field is declared in the `BvControllerOptions` interface and propagated through the type hierarchy.
```

</details>
<details>
<summary>packages/bootstrap-vue-next/src/utils/keys.ts (1)</summary>

```
Learnt from: xvaara
PR: bootstrap-vue-next/bootstrap-vue-next#2425
File: packages/bootstrap-vue-next/src/plugins/popoverController/index.ts:138-140
Timestamp: 2025-04-28T22:48:46.738Z
Learning: In bootstrap-vue-next, `PopoverOrchestratorParam` includes an `id` field of type `ControllerKey` through inheritance from base types. This field is declared in the `BvControllerOptions` interface and propagated through the type hierarchy.
```

</details>

</details><details>
<summary>🧬 Code Graph Analysis (9)</summary>

<details>
<summary>packages/bootstrap-vue-next/src/composables/useToastController/index.ts (2)</summary><blockquote>

<details>
<summary>packages/bootstrap-vue-next/src/types/ComponentOrchestratorTypes.ts (1)</summary>

* `PromiseWithToast` (36-36)

</details>
<details>
<summary>packages/bootstrap-vue-next/src/utils/keys.ts (1)</summary>

* `toastPluginKey` (222-235)

</details>

</blockquote></details>
<details>
<summary>packages/bootstrap-vue-next/src/types/Tab.ts (1)</summary><blockquote>

<details>
<summary>packages/bootstrap-vue-next/src/types/AnyValuedAttributes.ts (1)</summary>

* `ClassValue` (2-2)

</details>

</blockquote></details>
<details>
<summary>packages/bootstrap-vue-next/src/directives/BTooltip/index.ts (1)</summary><blockquote>

<details>
<summary>packages/bootstrap-vue-next/src/utils/floatingUi.ts (3)</summary>

* `bind` (107-118)
* `resolveDirectiveProps` (77-100)
* `unbind` (120-127)

</details>

</blockquote></details>
<details>
<summary>packages/bootstrap-vue-next/src/composables/usePopoverController/index.ts (3)</summary><blockquote>

<details>
<summary>packages/bootstrap-vue-next/src/composables/index.ts (1)</summary>

* `usePopoverController` (8-8)

</details>
<details>
<summary>packages/bootstrap-vue-next/src/types/ComponentOrchestratorTypes.ts (1)</summary>

* `PromiseWithPopover` (47-49)

</details>
<details>
<summary>packages/bootstrap-vue-next/src/utils/keys.ts (1)</summary>

* `popoverPluginKey` (256-261)

</details>

</blockquote></details>
<details>
<summary>packages/bootstrap-vue-next/src/types/ComponentProps.ts (4)</summary><blockquote>

<details>
<summary>packages/bootstrap-vue-next/src/types/CommonTypes.ts (1)</summary>

* `TeleporterProps` (5-8)

</details>
<details>
<summary>packages/bootstrap-vue-next/src/types/index.ts (2)</summary>

* `ColorExtendables` (34-34)
* `ButtonVariant` (33-33)

</details>
<details>
<summary>packages/bootstrap-vue-next/src/types/ColorTypes.ts (2)</summary>

* `ColorExtendables` (48-52)
* `ButtonVariant` (30-30)

</details>
<details>
<summary>packages/bootstrap-vue-next/src/types/AnyValuedAttributes.ts (1)</summary>

* `ClassValue` (2-2)

</details>

</blockquote></details>
<details>
<summary>packages/bootstrap-vue-next/src/composables/useToggle/index.ts (2)</summary><blockquote>

<details>
<summary>packages/bootstrap-vue-next/src/composables/index.ts (1)</summary>

* `useToggle` (7-7)

</details>
<details>
<summary>packages/bootstrap-vue-next/src/utils/keys.ts (1)</summary>

* `globalShowHideStorageInjectionKey` (177-178)

</details>

</blockquote></details>
<details>
<summary>packages/bootstrap-vue-next/src/composables/useShowHide.ts (3)</summary><blockquote>

<details>
<summary>packages/bootstrap-vue-next/src/composables/useModal/index.ts (2)</summary>

* `hide` (53-55)
* `show` (50-52)

</details>
<details>
<summary>packages/bootstrap-vue-next/src/utils/keys.ts (1)</summary>

* `globalShowHideStorageInjectionKey` (177-178)

</details>
<details>
<summary>packages/bootstrap-vue-next/src/utils/dom.ts (1)</summary>

* `isVisible` (32-47)

</details>

</blockquote></details>
<details>
<summary>packages/bootstrap-vue-next/src/types/ComponentOrchestratorTypes.ts (6)</summary><blockquote>

<details>
<summary>packages/bootstrap-vue-next/src/utils/classes.ts (1)</summary>

* `BvTriggerableEvent` (68-90)

</details>
<details>
<summary>packages/bootstrap-vue-next/src/types/ComponentProps.ts (4)</summary>

* `BToastProps` (1234-1259)
* `BTooltipProps` (1298-1300)
* `BPopoverProps` (1261-1296)
* `BModalProps` (1308-1371)

</details>
<details>
<summary>packages/bootstrap-vue-next/src/types/index.ts (5)</summary>

* `ContainerPosition` (10-10)
* `ToastOrchestratorCreateParam` (67-67)
* `TooltipOrchestratorCreateParam` (66-66)
* `PopoverOrchestratorCreateParam` (68-68)
* `ModalOrchestratorCreateParam` (69-69)

</details>
<details>
<summary>packages/bootstrap-vue-next/src/types/Alignment.ts (1)</summary>

* `ContainerPosition` (19-19)

</details>
<details>
<summary>packages/bootstrap-vue-next/src/types/ComponentEmits.ts (3)</summary>

* `BToastEmits` (41-44)
* `BPopoverEmits` (34-39)
* `BModalEmits` (21-25)

</details>
<details>
<summary>packages/bootstrap-vue-next/src/types/ComponentSlots.ts (3)</summary>

* `BToastSlots` (63-67)
* `BPopoverSlots` (57-61)
* `BModalSlots` (33-42)

</details>

</blockquote></details>
<details>
<summary>packages/bootstrap-vue-next/src/utils/keys.ts (2)</summary><blockquote>

<details>
<summary>packages/bootstrap-vue-next/src/types/index.ts (4)</summary>

* `ToastOrchestratorCreateParam` (67-67)
* `ModalOrchestratorCreateParam` (69-69)
* `PopoverOrchestratorCreateParam` (68-68)
* `TooltipOrchestratorCreateParam` (66-66)

</details>
<details>
<summary>packages/bootstrap-vue-next/src/types/ComponentOrchestratorTypes.ts (13)</summary>

* `ToastOrchestratorCreateParam` (144-144)
* `ToastOrchestratorCreateOptions` (255-258)
* `PromiseWithToast` (36-36)
* `ControllerKey` (12-12)
* `ModalOrchestratorMapValue` (211-225)
* `ModalOrchestratorCreateParam` (245-245)
* `ModalOrchestratorCreateOptions` (247-254)
* `PromiseWithModal` (23-25)
* `PromiseWithModalBoolean` (31-34)
* `PopoverOrchestratorMapValue` (178-192)
* `PopoverOrchestratorCreateParam` (209-209)
* `PromiseWithPopover` (47-49)
* `TooltipOrchestratorCreateParam` (176-176)

</details>

</blockquote></details>

</details><details>
<summary>🪛 LanguageTool</summary>

<details>
<summary>apps/docs/src/docs/components/progress.md</summary>

[misspelling] ~18-~18: This word is normally spelled as one.
Context: ...e prop on the individual `BProgressBar` sub-components (see the [Multiple Bars](#multiple-bars...

(EN_COMPOUNDS_SUB_COMPONENTS)

---

[misspelling] ~28-~28: This word is normally spelled as one.
Context: ...he default slot within a `BProgressBar` sub-component, or by using the `label` property on `B...

(EN_COMPOUNDS_SUB_COMPONENT)

---

[misspelling] ~70-~70: This word is normally spelled as one.
Context: ...e bars  Include multiple `BProgressBar` sub-components in a `BProgress` component to build a h...

(EN_COMPOUNDS_SUB_COMPONENTS)

</details>
<details>
<summary>apps/docs/src/docs/composables/usePopoverController.md</summary>

[grammar] ~60-~60: You should probably use “accept”.
Context: ...in `popover` and `tooltip`  The methods accepts reactive properties using `MaybeRef`, a...

(AGREEMENT_SENT_START)

</details>
<details>
<summary>apps/docs/src/docs/composables/useToastController.md</summary>

[style] ~55-~55: This phrase is redundant. Consider writing “point” or “time”.
Context: ...d a lot, returning the index at a given point in time is not ideal for as its position may be...

(MOMENT_IN_TIME)

---

[uncategorized] ~61-~61: Did you mean “affects”?
Context: ...cludes `position`. The `position` value effects its position, its type is [Container Po...

(EFFECTS_TO_AFFECTS)

---

[style] ~152-~152: As an alternative to the over-used intensifier ‘very’, consider replacing this phrase.
Context: ...t  Hiding a `Toast` programmatically is very simple. `create` return an object that has fun...

(EN_WEAK_ADJECTIVE)

</details>
<details>
<summary>apps/docs/src/docs/migration-guide.md</summary>

[uncategorized] ~603-~603: You might be missing the article “the” here.
Context: ...recation of the `html` prop.  `content` prop has been renamed to `body` for consiste...

(AI_EN_LECTOR_MISSING_DETERMINER_THE)

---

[uncategorized] ~739-~739: You might be missing the article “the” here.
Context: ...recation of the `html` prop.  `content` prop has been renamed to `body` for consiste...

(AI_EN_LECTOR_MISSING_DETERMINER_THE)

</details>
<details>
<summary>apps/docs/src/docs/composables/useModalController.md</summary>

[misspelling] ~151-~151: Did you mean “at a time”, “at the time”, or “at times”?
Context: ...create` method will resolve the promise at time of hiding the modal, rather than waitin...

(AT_TIME2)

---

[style] ~183-~183: Three successive sentences begin with the same word. Consider rewording the sentence or use a thesaurus to find a synonym.
Context: ...s function sets the props of the modal. This is useful for updating the modal after ...

(ENGLISH_WORD_REPEAT_BEGINNING_RULE)

---

[style] ~187-~187: Three successive sentences begin with the same word. Consider rewording the sentence or use a thesaurus to find a synonym.
Context: ...d.  - `destroy: () => Promise<void>`    This function destroys the modal and cleans ...

(ENGLISH_WORD_REPEAT_BEGINNING_RULE)

---

[uncategorized] ~190-~190: Did you mean: “By default,”?
Context: ...ces associated with it.  ### Lifecycle  By default the modal is destroyed once it's closed...

(BY_DEFAULT_COMMA)

</details>
<details>
<summary>apps/docs/src/docs/components/tooltip.md</summary>

[uncategorized] ~38-~38: The preposition “on” seems more likely in this position than the preposition “in”.
Context: ...n when the mouse is over it or focus is in it, allowing users to interact with its...

(AI_EN_LECTOR_REPLACEMENT_PREPOSITION_IN_ON)

</details>
<details>
<summary>apps/docs/src/docs/composables/useToggle.md</summary>

[uncategorized] ~35-~35: Use a comma before ‘and’ if it connects two independent clauses (unless they are closely connected and short).
Context: ...l. If the modal component does not exist and you attempt to call any of the exposed ...

(COMMA_COMPOUND_SENTENCE)

</details>

</details>
<details>
<summary>🪛 Biome (1.9.4)</summary>

<details>
<summary>packages/bootstrap-vue-next/src/directives/BTooltip/index.ts</summary>

[error] 40-40: Avoid the delete operator which can impact performance.

Unsafe fix: Use an undefined assignment instead.


(lint/performance/noDelete)

</details>

</details>
<details>
<summary>🪛 markdownlint-cli2 (0.17.2)</summary>

<details>
<summary>apps/docs/src/docs/composables/useToastController.md</summary>

303-303: Multiple consecutive blank lines
Expected: 1; Actual: 2

(MD012, no-multiple-blanks)

---

313-313: Trailing spaces
Expected: 0 or 2; Actual: 1

(MD009, no-trailing-spaces)

---

314-314: Trailing spaces
Expected: 0 or 2; Actual: 1

(MD009, no-trailing-spaces)

---

316-316: Trailing spaces
Expected: 0 or 2; Actual: 1

(MD009, no-trailing-spaces)

</details>
<details>
<summary>packages/bootstrap-vue-next/CHANGELOG.md</summary>

5-5: Multiple consecutive blank lines
Expected: 1; Actual: 2

(MD012, no-multiple-blanks)

---

12-12: Multiple consecutive blank lines
Expected: 1; Actual: 2

(MD012, no-multiple-blanks)

---

19-19: Multiple consecutive blank lines
Expected: 1; Actual: 2

(MD012, no-multiple-blanks)

---

20-20: Multiple headings with the same content
null

(MD024, no-duplicate-heading)

---

27-27: Multiple consecutive blank lines
Expected: 1; Actual: 2

(MD012, no-multiple-blanks)

---

40-40: Multiple headings with the same content
null

(MD024, no-duplicate-heading)

---

59-59: Multiple consecutive blank lines
Expected: 1; Actual: 2

(MD012, no-multiple-blanks)

---

60-60: Multiple headings with the same content
null

(MD024, no-duplicate-heading)

</details>
<details>
<summary>packages/nuxt/CHANGELOG.md</summary>

5-5: Multiple consecutive blank lines
Expected: 1; Actual: 2

(MD012, no-multiple-blanks)

---

10-10: Multiple consecutive blank lines
Expected: 1; Actual: 2

(MD012, no-multiple-blanks)

---

21-21: Multiple consecutive blank lines
Expected: 1; Actual: 2

(MD012, no-multiple-blanks)

---

22-22: Multiple headings with the same content
null

(MD024, no-duplicate-heading)

---

26-26: Multiple consecutive blank lines
Expected: 1; Actual: 2

(MD012, no-multiple-blanks)

---

27-27: Multiple headings with the same content
null

(MD024, no-duplicate-heading)

---

37-37: Multiple consecutive blank lines
Expected: 1; Actual: 2

(MD012, no-multiple-blanks)

---

38-38: Multiple headings with the same content
null

(MD024, no-duplicate-heading)

---

42-42: Multiple consecutive blank lines
Expected: 1; Actual: 2

(MD012, no-multiple-blanks)

---

43-43: Multiple headings with the same content
null

(MD024, no-duplicate-heading)

---

53-53: Multiple consecutive blank lines
Expected: 1; Actual: 2

(MD012, no-multiple-blanks)

---

54-54: Multiple headings with the same content
null

(MD024, no-duplicate-heading)

---

58-58: Multiple consecutive blank lines
Expected: 1; Actual: 2

(MD012, no-multiple-blanks)

---

59-59: Multiple headings with the same content
null

(MD024, no-duplicate-heading)

</details>

</details>

</details>

<details>
<summary>🔇 Additional comments (88)</summary><blockquote>

<details>
<summary>packages/nuxt/package.json (5)</summary>

`4-4`: **Version bump to 0.29.3**  
The package version has been updated from 0.28.6 to 0.29.3 to align with the latest `bootstrap-vue-next` release. This change is consistent with the changelog and dependency updates.

---

`10-12`: **Switching to ESM-only module format**  
The `"type": "module"` field along with updating `"main"` to `module.mjs` removes CommonJS support. This is a breaking change for CJS consumers—please document it as such in `CHANGELOG.md`.

---

`14-16`: **Conditional exports for types and entrypoint**  
You’ve moved the `types` entry under `"exports"` and pointed it to `types.d.mts`. Ensure that TypeScript and bundlers correctly resolve type definitions via this conditional export; otherwise, you may need to reintroduce a top-level `"types"` field.

---

`37-38`: **Dependency bump: @nuxt/kit**  
The dependency on `@nuxt/kit` has been updated to `^3.17.1`, aligning with the Nuxt 3.17 ecosystem. This matches the monorepo’s version synchronization.

---

`40-56`: **DevDependencies version updates**  
Multiple devDependencies (e.g., `@nuxt/devtools`, `@nuxt/module-builder`, `@nuxt/schema`, ESLint, Nuxt, TypeScript, etc.) have been bumped to their latest compatible versions. These updates look correct and maintain alignment with the Nuxt 3.17 tooling.

</details>
<details>
<summary>apps/docs/src/docs/components/progress.md (6)</summary>

`65-66`: **Check info block syntax**  
Verify that `::: info NOTE` is supported by the documentation renderer. Some setups expect simply `::: info` or a custom title syntax.

---

`30-30`: <details>
<summary>✅ Verification successful</summary>

**Verify demo import path**  
Confirm that `ProgressCustomLabels.vue` is present in the demo directory and that `<<< DEMO ./demo/ProgressCustomLabels.vue` is correct.

---

🏁 Script executed:

```shell
#!/bin/bash
fd ProgressCustomLabels.vue apps/docs/src/docs/components/demo
```

Length of output: 124

---



</details>

**Verified demo import path**  
The file `ProgressCustomLabels.vue` exists at `apps/docs/src/docs/components/demo/ProgressCustomLabels.vue`, so the `<<< DEMO ./demo/ProgressCustomLabels.vue` reference in `apps/docs/src/docs/components/progress.md` is correct.

---

`24-24`: <details>
<summary>✅ Verification successful</summary>

**Verify demo import path**  
Ensure that `ProgressLabels.vue` exists and that the demo reference `<<< DEMO ./demo/ProgressLabels.vue` resolves correctly.

---

🏁 Script executed:

```shell
#!/bin/bash
fd ProgressLabels.vue apps/docs/src/docs/components/demo
```

Length of output: 112

---



</details>

**Demo import path verified**  
Confirmed `apps/docs/src/docs/components/demo/ProgressLabels.vue` exists and the reference `<<< DEMO ./demo/ProgressLabels.vue` in `apps/docs/src/docs/components/progress.md` resolves correctly.

---

`12-12`: <details>
<summary>✅ Verification successful</summary>

**Verify demo import path**  
Ensure that `ProgressBasic.vue` exists at `apps/docs/src/docs/components/demo/ProgressBasic.vue` and that the relative path `./demo/ProgressBasic.vue` is correct.

---

🏁 Script executed:

```shell
#!/bin/bash
# Check that the basic demo file exists
fd ProgressBasic.vue apps/docs/src/docs/components/demo
```

Length of output: 110

---



</details>

**Demo import path verified**  
Confirmed that `apps/docs/src/docs/components/demo/ProgressBasic.vue` exists and the relative path `./demo/ProgressBasic.vue` in `progress.md` is correct. No changes needed.

---

`58-63`: <details>
<summary>❌ Incorrect review comment</summary>

**Verify animated demo & note consistency**  
1. Confirm `ProgressAnimated.vue` exists at the demo directory.  
2. The heading “Animated Background” could be “Animated background” (lowercase “b”) to match “Striped Background.”

---

🏁 Script executed:

```shell
#!/bin/bash
fd ProgressAnimated.vue apps/docs/src/docs/components/demo
```

Length of output: 116

---

🏁 Script executed:

```shell
#!/bin/bash
grep -n "Striped Background" apps/docs/src/docs/components/progress.md
```

Length of output: 97

---



</details>

**No action needed: demo exists and headings are consistent**  
- The file `apps/docs/src/docs/components/demo/ProgressAnimated.vue` is present.  
- Both “## Striped Background” (line 52) and “## Animated Background” (lines 58–63) use Title Case for “Background,” so no change is required.  



> Likely an incorrect or invalid review comment.

---

`52-57`: <details>
<summary>✅ Verification successful</summary>

**Verify striped demo reference**  
Ensure the striped demo file is available at `apps/docs/src/docs/components/demo/ProgressStriped.vue` and that the callout accurately reflects the `striped` prop behavior.

---

🏁 Script executed:

```shell
#!/bin/bash
fd ProgressStriped.vue apps/docs/src/docs/components/demo
```

Length of output: 114

---

🏁 Script executed:

```shell
#!/bin/bash
# Display the contents of the striped demo to verify the `striped` prop usage
sed -n '1,200p' apps/docs/src/docs/components/demo/ProgressStriped.vue
```

Length of output: 453

---



</details>

**Striped demo reference verified**  
The demo file exists at `apps/docs/src/docs/components/demo/ProgressStriped.vue` and clearly showcases the `striped` prop behavior as described in the documentation.

</details>
<details>
<summary>.github/workflows/release-main.yaml (1)</summary>

`23-23`: <details>
<summary>✅ Verification successful</summary>

**Update to Release Please Action source path.**

The GitHub Action source has been changed from `google-github-actions/release-please-action@v4` to `googleapis/release-please-action@v4`. This reflects an organization change for the Release Please action.

---

🏁 Script executed:

```shell
#!/bin/bash
# Check if this is the current canonical source for release-please-action
curl -s https://api.github.com/repos/googleapis/release-please-action | jq '.full_name, .description, .archived'
curl -s https://api.github.com/repos/google-github-actions/release-please-action | jq '.full_name, .description, .archived'
```

Length of output: 420

---



</details>

**Confirm Release Please Action source update**  
Verified that the Release Please Action has moved from the archived `google-github-actions/release-please-action` to the active `googleapis/release-please-action`. The change in `.github/workflows/release-main.yaml` (line 23) is correct—no further updates required.

- File: `.github/workflows/release-main.yaml`  
  • Line 23:  
    ```diff
    - uses: google-github-actions/release-please-action@v4
    + uses: googleapis/release-please-action@v4
    ```

</details>
<details>
<summary>apps/playground/src/components/Comps/TCarousel.vue (1)</summary>

`64-78`: **New dynamic carousel implementation looks good.**

This new carousel implementation demonstrates reactive rendering by dynamically generating carousel slides from an array of images.

</details>
<details>
<summary>packages/bootstrap-vue-next/tsconfig.vitest.json (1)</summary>

`3-3`: **Include plugins directory in Vitest configuration.**

Including `"src/plugins/**.ts"` in the Vitest configuration ensures that the plugin files will be included in the test environment. This is a good practice for maintaining test coverage.

</details>
<details>
<summary>apps/docs/src/components/ComponentReference.vue (1)</summary>

`13-15`: **Improved document structure with semantic headings.**

Wrapping the component name in an `<h3>` tag with a kebab-case ID improves document structure and enables direct linking to specific components, enhancing navigation and accessibility.

</details>
<details>
<summary>packages/bootstrap-vue-next/src/composables/useActivatedFocusTrap.ts (1)</summary>

`1-1`: **Improved import organization.**

The import statement has been consolidated, which is a good practice for code organization and readability.

</details>
<details>
<summary>packages/bootstrap-vue-next/src/components/BToast/toast-orchestrator.spec.ts (2)</summary>

`4-4`: **LGTM! Added Bootstrap plugin import.**

Adding the import for `createBootstrap` is necessary for the enhanced test setup.

---

`11-14`: **Improved test environment setup.**

Adding the Bootstrap plugin to the global mounting options ensures the component is tested in a context that more closely resembles the actual application environment, which is a good practice for integration tests.

</details>
<details>
<summary>packages/bootstrap-vue-next/src/components/BCarousel/BCarousel.vue (1)</summary>

`24-24`: **Improved null safety with optional chaining.**

Adding optional chaining operators (`?.`) to access the `_id` property prevents potential runtime errors when a slide at index `i` is undefined or null, enhancing the component's robustness.

</details>
<details>
<summary>packages/bootstrap-vue-next/src/components/BPopover/popover.spec.ts (2)</summary>

`93-93`: **Updated test to use the new prop name.**

Test has been correctly updated to use the new `body` prop instead of the deprecated `content` prop, ensuring consistency with the component changes.

---

`101-101`: **Updated test to use the new prop name.**

Test has been correctly updated to use the new `body` prop instead of the deprecated `content` prop, ensuring consistency with the component changes.

</details>
<details>
<summary>packages/bootstrap-vue-next/src/components/BToast/toast.spec.ts (1)</summary>

`45-45`: **Test updated to match new toast header structure.**

The test now checks for the `ms-auto` class on the button element instead of checking the `<strong>` element for `me-auto`. This change aligns with the refactoring of moving styles out of components and applying utility classes directly to elements.

</details>
<details>
<summary>packages/bootstrap-vue-next/src/components/BAlert/_alert.scss (2)</summary>

`1-3`: **Progress bar transition disabled for alerts.**

This style rule disables the transition effect for progress bars inside alerts by setting `--bs-progress-bar-transition: none`. This ensures the progress bar inside alerts doesn't have animation effects, which is appropriate for alert contexts.

---

`5-8`: **Custom close button styling for alerts.**

This rule adds positioning for the custom close button inside alerts. The styles ensure proper vertical alignment of the close button by setting `margin-bottom: auto` and `position: relative`.

These styles were likely moved from a scoped style block in the BAlert component, which aligns with the PR objective of relocating all SCSS styles from within components to separate SCSS files.

</details>
<details>
<summary>apps/docs/src/docs/components.md (1)</summary>

`197-200`: **Added Tooltip component to documentation.**

The Tooltip component has been added to the component list with a description. This ensures users can discover and navigate to the Tooltip component documentation.

While this change appears unrelated to the main PR objective of moving SCSS styles out of components, it enhances the documentation coverage.

Is this addition of the Tooltip component intended to be part of this PR? The PR description focuses on moving SCSS styles out of components, but this change adds a new component entry to the documentation.

</details>
<details>
<summary>packages/bootstrap-vue-next/src/composables/useFormSelect.ts (1)</summary>

`10-10`: **Modified complex option detection logic.**

The `isComplex` type guard now checks for the presence of the `'options'` property instead of the `'label'` property to identify complex select options.

This change appears to be a functional modification rather than a style-related change as described in the PR objectives. It alters how complex options are detected in the form select component.

This change modifies functional behavior rather than moving SCSS styles. Is this intended to be part of this PR? The change seems reasonable as the presence of 'options' is a better indicator of a complex option than 'label', but it doesn't align with the stated PR purpose of moving styles out of components.

</details>
<details>
<summary>packages/bootstrap-vue-next/src/components/BPagination/BPagination.vue (1)</summary>

`362-362`: **Fixed button filtering logic by removing redundant condition.**

The `true ||` expression was making this condition always return true regardless of element dimensions. Removing it ensures buttons are correctly filtered based on their visibility.

</details>
<details>
<summary>packages/bootstrap-vue-next/src/components/BPagination/pagination.spec.ts (2)</summary>

`470-482`: **Added getBoundingClientRect mock for reliable testing.**

This mock ensures the keyboard navigation test has consistent behavior by guaranteeing elements have non-zero dimensions, which is essential for the button filtering logic to work correctly.

---

`500-500`: **Properly restoring the original getBoundingClientRect method.**

Good practice to restore the original method after the test to avoid side effects in subsequent tests.

</details>
<details>
<summary>packages/bootstrap-vue-next/src/directives/BPopover/index.ts (2)</summary>

`21-21`: **Updated property name from content to body.**

This change aligns with the renaming pattern across components for consistency, changing from `text.content` to `text.body`.

---

`37-37`: **Updated property name from content to body.**

Consistent with the earlier change, ensuring the same property name is used in both mounted and updated lifecycle hooks.

</details>
<details>
<summary>packages/bootstrap-vue-next/src/types/Tab.ts (1)</summary>

`14-15`: <details>
<summary>✅ Verification successful</summary>

**Update to TabType interface with modified onClick type and new navItemClasses property**

The `onClick` property has been changed from a direct event handler to a higher-order function that returns an event handler, and a new `navItemClasses` property has been added to enhance styling flexibility.

This change aligns with the updates in BTab.vue where the onClick handler is now wrapped in a function. Make sure all components consuming this interface have been updated accordingly.


---

🏁 Script executed:

```shell
#!/bin/bash
# Find all files that reference the TabType interface and its onClick property
rg "TabType.*onClick" --type=ts --type=vue
```

Length of output: 72

---

🏁 Script executed:

```shell
#!/bin/bash
set -e

echo "Locating BTab.vue..."
fd -e vue BTab.vue

echo "Searching for TabType references..."
rg "TabType" -n .

echo "Searching for onClick occurrences..."
rg "onClick" -n .

echo "Searching for navItemClasses occurrences..."
rg "navItemClasses" -n .
```

Length of output: 5107

---



</details>

**Update TabType interface: modified onClick signature & added navItemClasses**

The onClick property is now a higher-order function `(): (event: Event) => void`, and a new `navItemClasses?: ClassValue` prop has been introduced for styling flexibility. I’ve verified all consumers of `TabType`—including `BTabs.vue` and `BTab.vue`—now correctly wrap and invoke `onClick()` and apply `navItemClasses`. No further changes are needed.

</details>
<details>
<summary>packages/bootstrap-vue-next/src/components/BTabs/BTab.vue (3)</summary>

`79-80`: **Updated onClick handler to align with TabType interface changes**

The `onClick` handler is now properly implemented as a higher-order function, returning the event handler from attrs. This correctly matches the updated TabType interface.

---

`84-89`: **Improved tab registration logic**

Moving the registration and activation logic outside of the `onMounted` hook and guarding it with an `if (parentData)` check is a good improvement. This ensures immediate execution when `parentData` exists rather than waiting for the component to be mounted.

---

`91-94`: <details>
<summary>✅ Verification successful</summary>

**Added tab sorting on mount**

The new `onMounted` hook calls `parentData.sortTabs()` which should help maintain the correct tab order. Make sure the `sortTabs` method is properly implemented in the parent component.


---

🏁 Script executed:

```shell
#!/bin/bash
# Check for the implementation of sortTabs method in related files
rg "sortTabs" --type=ts --type=vue -A 5 -B 5
```

Length of output: 72

---

🏁 Script executed:

```shell
#!/bin/bash
# Search for sortTabs definition or usage across all files
rg "sortTabs" -A5 -B5
```

Length of output: 3242

---



</details>

**Tab sorting on mount is properly implemented**

The `sortTabs` method is defined in `BTabs.vue` and provided via the injection key declared in `utils/keys.ts`. Calling `parentData.sortTabs()` in `BTab.vue` on mount will correctly order tabs by their DOM position. No further changes needed.

</details>
<details>
<summary>packages/bootstrap-vue-next/src/composables/useModal/index.ts (1)</summary>

`53-55`: <details>
<summary>❓ Verification inconclusive</summary>

**Updated hide method signature and implementation**

The `hide` method now accepts an optional `trigger` parameter and passes an additional `true` parameter to the modal's hide method. This change might affect the behavior of modals when they're hidden.

Make sure the receiving end (the modal component) correctly handles this second parameter.

---

🏁 Script executed:

```shell
#!/bin/bash
# Check the implementation of the hide method in the BModal component
rg "hide\s*\(\s*trigger.*\)" --type=vue -A 10 packages/bootstrap-vue-next/src/components/BModal/
```

Length of output: 126

---

I wasn’t able to locate the `hide` implementation in the BModal component—let’s search the actual `.vue` file directly.

---

🏁 Script executed:

```shell
#!/bin/bash
# Find the BModal component file(s)
fd -e vue BModal packages/bootstrap-vue-next/src/components

# For each found file, list lines where `hide(` is defined or referenced
for file in $(fd -e vue BModal packages/bootstrap-vue-next/src/components); do
  echo "=== $file ==="
  rg "hide\s*\(" -n "$file" || echo "  (no matches)"
done
```

Length of output: 1306

---

Let’s inspect the `hide` method definition in `BModal.vue` to confirm its parameters:

---

🏁 Script executed:

```shell
#!/bin/bash
# Extract the <script> section from BModal.vue and search for the hide method signature
awk '/<script/,/<\/script>/' packages/bootstrap-vue-next/src/components/BModal/BModal.vue | rg "hide\s*\("
```

Length of output: 187

---

Let’s locate the actual `hide` method definition and the `expose` call in BModal.vue:

---

🏁 Script executed:

```shell
#!/bin/bash
file="packages/bootstrap-vue-next/src/components/BModal/BModal.vue"

echo "→ Searching for a hide definition (function or const):"
rg "function hide" -n "$file" || echo "  (no `function hide` found)"
rg "const hide" -n "$file" || echo "  (no `const hide` found)"

echo -e "\n→ Showing context around any hide declaration:"
rg "hide\s*\([^)]+\)\s*{" -n "$file" -C 3 || echo "  (no method signature match)"

echo -e "\n→ Inspecting expose() to see what is exposed:"
rg "expose\(" -n "$file" -A 6
```

Length of output: 1238

---

Let’s directly grep for the `hide` declaration and the `expose` call in BModal.vue:

---

🏁 Script executed:

```shell
#!/bin/bash
file="packages/bootstrap-vue-next/src/components/BModal/BModal.vue"

echo "→ Searching for arrow‐function or function declarations of hide:"
rg -n 'const hide' -A5 -B5 "$file" || echo "  (no const hide found)"
rg -n 'function hide' -A5 -B5 "$file" || echo "  (no function hide found)"

echo -e "\n→ Inspecting exposed methods via expose/defineExpose:"
rg -n 'expose\(' -A5 -B5 "$file" || rg -n 'defineExpose' -A5 -B5 "$file" || echo "  (no expose/defineExpose found)"
```

Length of output: 994

---



</details>

**Verify BModal.hide signature**

I wasn’t able to locate the actual definition of the exposed `hide` method in `BModal.vue`. Please confirm that it’s declared to accept both the optional `trigger?: string` argument and the internal boolean flag (passed as `true` by the composable), and that the implementation behaves correctly when that second parameter is provided.

• File to check:  
  - packages/bootstrap-vue-next/src/components/BModal/BModal.vue  
• Verify the signature and implementation of the `hide` method exposed via `defineExpose`

</details>
<details>
<summary>packages/bootstrap-vue-next/src/components/BBreadcrumb/breadcrumb.spec.ts (2)</summary>

`6-6`: **Added import for createBootstrap plugin**

The import for the `createBootstrap` plugin is correctly added, which is necessary for the test environment updates.

---

`12-16`: **Added Bootstrap plugin to all test mount options**

Adding the Bootstrap plugin to all test mount configurations ensures that the tests run in an environment that more closely matches the actual application. This is a good practice for integration testing.

This change is consistent with the PR objective of moving SCSS styles out of components, as the Bootstrap plugin likely includes global style registration.



Also applies to: 21-25, 30-34, 42-46, 56-58, 66-68, 76-78, 86-88, 96-98, 108-110, 123-125, 139-141, 154-156, 165-167, 176-178, 187-189

</details>
<details>
<summary>packages/bootstrap-vue-next/src/directives/BTooltip/index.ts (3)</summary>

`21-21`: **Updated property reference from content to body.**

The conditional check has been updated to check for `text.body` instead of `text.content`, aligning with the broader refactoring effort to standardize content property naming across components.

---

`27-27`: **Updated property reference from content to body.**

The fallback logic has been updated to use `text.body` instead of `text.content`, maintaining consistency with the property renaming.

---

`47-47`: **Updated property reference from content to body.**

The fallback logic has been updated to use `text.body` instead of `text.content`, maintaining consistency with the property renaming.

</details>
<details>
<summary>apps/docs/src/data/components/popover.data.ts (3)</summary>

`25-28`: **Renamed prop from 'content' to 'body' for consistency.**

The property has been renamed from `content` to `body` to align with the broader refactoring effort across components.

---

`29-32`: **Renamed 'customClass' to 'bodyClass' with updated default.**

The property has been renamed from `customClass` to `bodyClass` for better semantic clarity, and its type is now explicitly `ClassValue` with a default of `undefined`.

---

`113-116`: **Added new 'titleClass' prop for title styling.**

A new `titleClass` prop has been added to allow custom styling of the popover title, enhancing styling flexibility.

</details>
<details>
<summary>apps/docs/src/data/components/modal.data.ts (2)</summary>

`101-105`: **Added new 'cancelClass' prop for cancel button styling.**

A new `cancelClass` prop has been added to allow custom styling of the modal cancel button, enhancing styling flexibility.

---

`242-246`: **Added new 'okClass' prop for OK button styling.**

A new `okClass` prop has been added to allow custom styling of the modal OK button, enhancing styling flexibility.

</details>
<details>
<summary>apps/playground/src/components/Comps/TModal.vue (6)</summary>

`66-66`: **Added 'toValue' import for handling reactive values.**

Added `toValue` from Vue to safely unwrap potential refs when needed.

---

`83-83`: **Added default title to modal reference object.**

Added a `title` property with value 'foobar' to the `firstRef` reactive object, providing a default title for modals using this reference.

---

`96-99`: **Simplified modal show API call.**

The modal `show` function call has been simplified by removing the wrapping `props` object, making the API more intuitive.

---

`102-106`: **Updated to use slots API instead of component property.**

The modal configuration now uses the `slots` object with a default slot rendering a div, replacing the previous `component` property approach.

---

`109-109`: **Simplified ref props passing.**

The `simpleRefProps` function has been simplified to pass the `firstRef` directly to the `show` function, streamlining the API usage.

---

`112-119`: **Updated dynamic props handling with computed and toValue.**

The `dynamicRefProps` function now uses a computed property directly without the `props` wrapper and uses `toValue` to safely unwrap the reactive `firstRef.value.body` before parsing, improving reliability.

</details>
<details>
<summary>packages/bootstrap-vue-next/src/components/BCollapse/BCollapse.vue (1)</summary>

`32-34`: **Verify that `ShowHideSlotsData` now includes `active`**

`sharedSlots` adds an `active` key (l.151-152) yet the computed return type is still `ShowHideSlotsData`.  
If the type definition was not extended, TS will error or the cast will silently widen the type.

Action: confirm `active` is declared in `ShowHideSlotsData` (or switch to an augmented interface).

</details>
<details>
<summary>apps/docs/src/data/components/toast.data.ts (1)</summary>

`122-123`: **Confirm `variant` doesn’t shadow existing colour props**

Adding `variant` alongside `bgVariant` and `textVariant` may create ambiguity in usage & docs.  
Double-check implementation of `BToast` to ensure the three props coexist coherently.

</details>
<details>
<summary>apps/playground/src/components/Comps/TToast.vue (2)</summary>

`22-22`: **Confirm removal of commented-out imports is intentional**

The BToast and OrchestratedToast type imports are commented out instead of being completely removed. This suggests they might be temporarily disabled for testing. If they're no longer needed, consider removing them entirely.



Also applies to: 24-24

---

`43-47`: **Updated toast API usage**

The API usage has been simplified by flattening the parameter structure and using slots object instead of component property. This aligns with the updated toast controller API.



Also applies to: 50-55, 58-59, 61-68

</details>
<details>
<summary>apps/docs/src/components/PageContents.vue (2)</summary>

`3-8`: **Component refactored to use externally provided data**

The PageContents component now receives navigation data through props instead of computing it internally. This follows good component design principles by making it more reusable and focused.

---

`13-15`: **Props refactored for external data dependency**

The component now properly defines typed props for the contents tree and active ID. This improves type safety and makes the component's dependencies explicit.

</details>
<details>
<summary>packages/bootstrap-vue-next/src/composables/useToastController/index.ts (4)</summary>

`1-4`: **Improved imports for enhanced functionality**

The imports have been updated to include Vue's ref and the PromiseWithToast type, which are necessary for the enhanced implementation with better error handling.

---

`5-12`: **Added fallback promise and warning message**

A noopPromise function has been added to provide a fallback when the controller is used outside of a proper context. This improves error handling and developer experience.

---

`13-22`: **Improved default fallback object**

The inject call now provides a comprehensive default fallback object with reactive properties. This ensures the component doesn't fail silently when used incorrectly.

---

`23-27`: **Added explicit error handling**

The composable now throws a clear error when used outside a proper setup context or without the plugin. This helps developers quickly identify misuse.

</details>
<details>
<summary>packages/bootstrap-vue-next/src/composables/usePopoverController/index.ts (4)</summary>

`1-4`: **Updated imports for enhanced functionality**

The imports have been updated to include Vue's ref and the PromiseWithPopover type from the types directory, which support the enhanced implementation.

---

`5-12`: **Added fallback promise with warning**

A noopPromise function has been implemented to provide a fallback with appropriate warning when the controller is used incorrectly. This is consistent with the approach used in useToastController.

---

`13-18`: **Improved default fallback object**

The inject call now provides a comprehensive default fallback object with reactive properties, ensuring graceful degradation when the plugin is missing.

---

`19-23`: **Added explicit error handling**

The composable now throws a clear error when used outside a proper setup context or without the plugin, which helps developers identify misuse quickly.

</details>
<details>
<summary>packages/bootstrap-vue-next/src/components/BPopover/BPopoverOrchestrator.vue (1)</summary>

`4-11`: **Iterator key stability**

Using `Map.prototype.entries()` yields stable keys, but if the underlying Map is recreated the identity of `key` values changes and Vue will destroy/recreate components.  
Ensure the controller mutates the existing Map instead of replacing it, or derive a stable id (e.g., toast id) for `:key`.

</details>
<details>
<summary>packages/bootstrap-vue-next/src/components/BTooltip/BTooltip.vue (1)</summary>

`40-48`: **Default prop values diverged from BPopover – confirm this is intentional**

`delay` now defaults to `0` and `hideMargin` to `0`.  
Previous behaviour (if any) might have been `undefined`, inheriting Bootstrap’s default of **50 ms**.  
This is technically a breaking UX change: tooltips will now appear immediately.

If that is intentional, please add a release-note entry; otherwise restore the old defaults.

</details>
<details>
<summary>packages/bootstrap-vue-next/src/components/BModal/BModalOrchestrator.vue (2)</summary>

`42-44`: **Dynamic slot rendering: ensure `comp` is defined**

If a consumer passes `{slots: {footer: undefined}}`, `component :is="comp"` triggers a runtime warning.  
Guard or filter falsy entries:

```vue
<template v-for="(comp, slot) in slots" v-if="comp" #[slot]="scope">
  <component :is="comp" v-bind="scope" />
</template>
```

---

`27-38`: _⚠️ Potential issue_

**Same ref-vs-value issue in `@hidden` handler**

`promise.resolve` and `promise.value.destroy` should access `.value` first:

```diff
- promise.resolve(options.returnBoolean ? e.ok : e)
+ promise.value.resolve(options.returnBoolean ? e.ok : e)

- promise.value.destroy?.()
+ promise.value.destroy?.()
```

Without this fix, hidden modals will never resolve/destroy, leaking memory.




> Likely an incorrect or invalid review comment.

</details>
<details>
<summary>packages/bootstrap-vue-next/src/components/BTabs/BTabs.vue (1)</summary>

`317-320`: **`sortTabs` is exposed but not documented**

`sortTabs` is now provided via `tabsInjectionKey`; unless this method is consumed internally, document its purpose or mark it private to prevent accidental reliance by users.

</details>
<details>
<summary>packages/bootstrap-vue-next/CHANGELOG.md (4)</summary>

`3-8`: **Great documentation of new release!**

Good job adding detailed information about the fixes in the latest version 0.29.3. Having explicit issue reference numbers is very helpful for tracking the history of changes.

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 markdownlint-cli2 (0.17.2)</summary>

5-5: Multiple consecutive blank lines
Expected: 1; Actual: 2

(MD012, no-multiple-blanks)

</details>

</details>

---

`10-15`: **Proper feature documentation**

Well-structured feature entry for the BTable implementation of 'fixed' and 'noBorderCollapse' props. Including the issue number provides valuable context for the change.

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 markdownlint-cli2 (0.17.2)</summary>

12-12: Multiple consecutive blank lines
Expected: 1; Actual: 2

(MD012, no-multiple-blanks)

</details>

</details>

---

`17-55`: **Comprehensive changelog for major release**

Excellent documentation of breaking changes with clear explanations of what was modified. This is particularly important for the significant API changes in controllers and component behavior modifications. The detailed listing of each change helps users understand what they need to update in their code.

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 markdownlint-cli2 (0.17.2)</summary>

19-19: Multiple consecutive blank lines
Expected: 1; Actual: 2

(MD012, no-multiple-blanks)

---

20-20: Multiple headings with the same content
null

(MD024, no-duplicate-heading)

---

27-27: Multiple consecutive blank lines
Expected: 1; Actual: 2

(MD012, no-multiple-blanks)

---

40-40: Multiple headings with the same content
null

(MD024, no-duplicate-heading)

</details>

</details>

---

`57-79`: **Well-documented bug fixes**

The bug fixes section provides good detail on what was fixed, making it easy for users to determine if an issue they experienced has been addressed. Including specific component references and issue numbers is valuable.

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 markdownlint-cli2 (0.17.2)</summary>

59-59: Multiple consecutive blank lines
Expected: 1; Actual: 2

(MD012, no-multiple-blanks)

---

60-60: Multiple headings with the same content
null

(MD024, no-duplicate-heading)

</details>

</details>

</details>
<details>
<summary>packages/bootstrap-vue-next/src/components/BModal/BModal.vue (1)</summary>

`398-409`: **Nice improvement – slot helpers are now feature-par with other components**

Exposing `show` / `toggle` / `id` through `sharedSlots` unifies the API with `BToast`/`BPopover`.  This will make authored slot templates significantly cleaner.

</details>
<details>
<summary>packages/bootstrap-vue-next/src/components/BToast/BToast.vue (2)</summary>

`25-42`: **Close-button slot logic looks good**

The conditional rendering neatly mirrors the pattern introduced in alerts and modals.  No issues detected.

---

`266-273`: **Good addition – shared slot props bring parity with other components**

Nice to see `active`, `visible`, `toggle`, etc., exposed here as well.

</details>
<details>
<summary>packages/bootstrap-vue-next/src/components/BPopover/BPopover.vue (3)</summary>

`102-108`: **Helpful deprecation warning**

Logging once on component creation keeps the console readable.  Good practice.

---

`347-378`: **Event-listener management is clean and thorough**

Adding / removing listeners based on reactive props (`click`, `manual`, etc.) is handled well; thanks for the defensive `unbind` call.

---

`473-480`: **Great – unified slot API**

Consistent `sharedSlots` across components simplifies external usage.

</details>
<details>
<summary>packages/bootstrap-vue-next/src/types/ComponentProps.ts (1)</summary>

`1266-1294`: **Breaking rename from `content` → `body` in `BPopoverProps`**  

The prop rename is sensible for consistency, but it *is* a breaking change.  
Please ensure:

1.   Migration notes / changelog entry are added.  
2.   `vBPopover` directive & internal code are updated everywhere (looks done, but double-check).  
3.   A deprecation alias (`content` → `body`) is exported for one major version to ease adoption.

</details>
<details>
<summary>packages/bootstrap-vue-next/src/composables/useModalController/index.ts (1)</summary>

`70-82`: **Potential undefined access on `modal?.promise.value`**

`modal?.promise.value.show()` / `.hide()` assume that `promise.value`
exists.  If the consumer accidentally mutates `modal.promise` (or the
plugin returns a controller without it) this will throw.

Consider defensive guarding or surfacing an explicit error:

```diff
-        show() {
-          modal?.promise.value.show()
-        },
+        show() {
+          if (modal?.promise?.value?.show) {
+            modal.promise.value.show()
+          } else {
+            // eslint-disable-next-line no-console
+            console.warn('Modal found, but no promise API exposed')
+          }
+        },
```

</details>
<details>
<summary>packages/bootstrap-vue-next/src/components/BAlert/alert.spec.ts (1)</summary>

`79-85`: **Assertion duplicates previous test logic**

Lines 61-68 already cover the `aria-live` attribute when `isStatus` is
*false*.  
This test repeats the same steps for the `isStatus` = `true` branch but
could be merged with the test below (role =`status`) to avoid additional
mount cost.

[ suggest_optional_refactor ]

</details>
<details>
<summary>packages/bootstrap-vue-next/src/plugins/modalController/index.ts (1)</summary>

`193-198`: _⚠️ Potential issue_

**`onScopeDispose` is called with an extra argument – this won’t compile**

`onScopeDispose` accepts only a single callback parameter.  
Passing the second boolean literal `true` produces the TS error *“Expected 1 arguments, but got 2.”* and the callback will never be registered at runtime.

Apply the following fix (same pattern is repeated elsewhere in the codebase).

```diff
-      onScopeDispose(() => {
-        const modal = modals.value.get(_self)
-        if (modal) {
-          modal.promise.value.destroy?.()
-        }
-      }, true)
+      onScopeDispose(() => {
+        const modal = modals.value.get(_self)
+        if (modal) {
+          modal.promise.value.destroy?.()
+        }
+      })
```



<details>
<summary>⛔ Skipped due to learnings</summary>

```
Learnt from: xvaara
PR: bootstrap-vue-next/bootstrap-vue-next#2425
File: packages/bootstrap-vue-next/src/plugins/toastController/index.ts:203-208
Timestamp: 2025-04-22T20:13:41.342Z
Learning: In Vue 3.5+, the `onScopeDispose` function accepts two parameters: `function onScopeDispose(fn: () => void, failSilently?: boolean): void`. The second parameter `failSilently` controls whether to throw an error or silently ignore if the function is called outside of a component setup context.
```

```
Learnt from: xvaara
PR: bootstrap-vue-next/bootstrap-vue-next#2425
File: packages/bootstrap-vue-next/src/plugins/toastController/index.ts:203-208
Timestamp: 2025-04-22T20:13:41.342Z
Learning: In Vue 3.5+, the `onScopeDispose` function accepts two parameters: `function onScopeDispose(fn: () => void, failSilently?: boolean): void`. The second parameter `failSilently` controls whether to throw an error or silently ignore if the function is called outside of a component setup context.
```

```
Learnt from: xvaara
PR: bootstrap-vue-next/bootstrap-vue-next#2425
File: packages/bootstrap-vue-next/src/plugins/toastController/index.ts:203-208
Timestamp: 2025-04-22T20:13:41.342Z
Learning: In Vue 3.5+, the `onScopeDispose` function accepts two parameters: `function onScopeDispose(fn: () => void, failSilently?: boolean): void`. The second parameter `failSilently` controls whether to throw an error or silently ignore if the function is called outside of a component setup context.
```

</details>

</details>
<details>
<summary>packages/bootstrap-vue-next/src/components/BAlert/BAlert.vue (1)</summary>

`52-58`: _⚠️ Potential issue_

**Unconditional `hide()` call in click handler**

`@click="computedLink && props.dismissible ? hide('body') : () => {}"`  
When the expression short-circuits, Vue still calls the *result* of the ternary.  
If `computedLink && props.dismissible` is `false` you return a **function**, but if it’s `true` you execute `hide()` immediately and return its resolved value (a promise), *not* a handler.

Prefer passing a function:

```diff
-@click="computedLink && props.dismissible ? hide('body') : () => {}"
+@click="() => (computedLink && props.dismissible ? hide('body') : undefined)"
```
This guarantees the logic is evaluated on *click*, not during render.  




> Likely an incorrect or invalid review comment.

</details>
<details>
<summary>packages/bootstrap-vue-next/src/types/ComponentOrchestratorTypes.ts (1)</summary>

`102-112`: **Mandatory `_self` key forces boilerplate on callers**

`ToastOrchestratorArrayValue` requires an `_self: ControllerKey` whereas `ToastOrchestratorParam` makes it optional.  The inconsistency means the *create* API can be called without it, but reading from the orchestrator array later *must* set it.

If the library internally auto-generates a key, mark `_self` as optional here too to avoid consumer confusion:

```diff
-  '_self': ControllerKey
+  '_self'?: ControllerKey
```

</details>
<details>
<summary>packages/bootstrap-vue-next/src/components/BAccordion/BAccordionItem.vue (1)</summary>

`61-61`: **Good improvement to event typing consistency.**

The switch from component-specific event emit typings to the centralized `BCollapseEmits` type enhances type safety and maintainability. This change aligns with the broader refactoring effort to standardize event typing across components.



Also applies to: 94-94

</details>

</blockquote></details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-22T12:57:46Z

Review comments:
diff_hunk: @@ -0,0 +1,30 @@
+.toast :deep(.progress .progress-bar) {
+  --bs-progress-bar-transition: none;
+}
path: packages/bootstrap-vue-next/src/components/BToast/_toast.scss
user: login: coderabbitai[bot]
body: _⚠️ Potential issue_

**Incorrect use of `:deep` in a global SCSS file.**  
The `:deep(...)` combinator is only meaningful in Vue’s scoped `<style>` blocks. In this global stylesheet it will be emitted literally and won’t match anything. Replace it with a normal descendant selector:  
```diff
-.toast :deep(.progress .progress-bar) {
-  --bs-progress-bar-transition: none;
-}
+.toast .progress .progress-bar {
+  --bs-progress-bar-transition: none;
+}
```  



<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

`````suggestion
.toast .progress .progress-bar {
  --bs-progress-bar-transition: none;
}
`````

</details>

<!-- suggestion_end -->

<!-- This is an auto-generated reply by CodeRabbit -->
created_at: 2025-04-27T17:25:59Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,7 @@
+.modal {
+  display: block;
+}
path: packages/bootstrap-vue-next/src/components/BModal/_modal.scss
user: login: coderabbitai[bot]
body: _⚠️ Potential issue_

**Global override of `.modal { display: block; }` is too broad.**  
Bootstrap’s default modals are hidden via `display: none` until `.show` is applied. This global rule will force all `.modal` elements to render, potentially breaking other modals or third-party code. Scope this rule to your component’s root class (e.g. `.b-modal .modal { display: block; }`).

<!-- This is an auto-generated reply by CodeRabbit -->

✅ Addressed in commits 815fdf1 to 0ec0665

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-04-27T17:26:00Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,30 @@
+.toast :deep(.progress .progress-bar) {
+  --bs-progress-bar-transition: none;
+}
path: packages/bootstrap-vue-next/src/components/BToast/_toast.scss
user: login: unstoppablecarl
body: I knew this was an issue but wanted to see if anyone had suggestions other than just removing `:deep()`
created_at: 2025-04-27T17:30:10Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,30 @@
+.toast :deep(.progress .progress-bar) {
+  --bs-progress-bar-transition: none;
+}
path: packages/bootstrap-vue-next/src/components/BToast/_toast.scss
user: login: coderabbitai[bot]
body: @unstoppablecarl Thanks for the feedback. You're right, there are a few alternatives to simply removing `:deep()`:

1. **Standard CSS nesting** - The simplest solution:
   ```scss
   .toast .progress .progress-bar {
     --bs-progress-bar-transition: none;
   }
   ```

2. **Using SCSS @at-root** - Helps maintain organization while breaking out of nesting:
   ```scss
   .toast {
     @at-root #{&} .progress .progress-bar {
       --bs-progress-bar-transition: none;
     }
   }
   ```

3. **CSS custom properties approach** - Leverages cascade for deeper styling:
   ```scss
   .toast {
     --progress-bar-transition: none;
   }
   .progress .progress-bar {
     transition: var(--progress-bar-transition, inherit);
   }
   ```

4. **Utility classes + composition** - Add classes to the elements in your template instead

For this specific Bootstrap Vue implementation, option #1 is probably the most maintainable, straightforward, and aligned with Bootstrap's approach. The other options add complexity that might not be necessary in this case.

---

<details>
<summary>✏️ Learnings added</summary>

```
Learnt from: unstoppablecarl
PR: bootstrap-vue-next/bootstrap-vue-next#2671
File: packages/bootstrap-vue-next/src/components/BToast/_toast.scss:1-3
Timestamp: 2025-04-27T17:30:42.023Z
Learning: In global SCSS files for bootstrap-vue-next, `:deep()` combinator should not be used as it only works in Vue's scoped style blocks. Standard CSS/SCSS selectors should be used instead.
```

> Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure [review instructions](https://docs.coderabbit.ai/guides/review-instructions). You can manage existing learnings in the [app](https://app.coderabbit.ai/learnings).

</details>




<!-- <agentComponentType>chat-answer</agentComponentType> -->

<!-- <rawResChunk><answer>@unstoppablecarl Thanks for the feedback. You're right, there are a few alternatives to simply removing `:deep()`:

1. **Standard CSS nesting** - The simplest solution:
   ```scss
   .toast .progress .progress-bar {
     --bs-progress-bar-transition: none;
   }
   ```

2. **Using SCSS @at-root** - Helps maintain organization while breaking out of nesting:
   ```scss
   .toast {
     @at-root #{&} .progress .progress-bar {
       --bs-progress-bar-transition: none;
     }
   }
   ```

3. **CSS custom properties approach** - Leverages cascade for deeper styling:
   ```scss
   .toast {
     --progress-bar-transition: none;
   }
   .progress .progress-bar {
     transition: var(--progress-bar-transition, inherit);
   }
   ```

4. **Utility classes + composition** - Add classes to the elements in your template instead

For this specific Bootstrap Vue implementation, option #1 is probably the most maintainable, straightforward, and aligned with Bootstrap's approach. The other options add complexity that might not be necessary in this case.

---

<details>
<summary>✏️ Learnings added</summary>

```
Learnt from: unstoppablecarl
PR: bootstrap-vue-next/bootstrap-vue-next#2671
File: packages/bootstrap-vue-next/src/components/BToast/_toast.scss:1-3
Timestamp: 2025-04-27T17:30:42.023Z
Learning: In global SCSS files for bootstrap-vue-next, `:deep()` combinator should not be used as it only works in Vue's scoped style blocks. Standard CSS/SCSS selectors should be used instead.
```

> Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure [review instructions](https://docs.coderabbit.ai/guides/review-instructions). You can manage existing learnings in the [app](https://app.coderabbit.ai/learnings).

</details>

</answer></rawResChunk> -->

<!-- This is an auto-generated reply by CodeRabbit -->
created_at: 2025-04-27T17:30:43Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,7 @@
+.modal {
+  display: block;
+}
path: packages/bootstrap-vue-next/src/components/BModal/_modal.scss
user: login: unstoppablecarl
body: I knew this one would be an issue too. I assumed there was some kind of convention for this like `BV-modal` or something but couldn't find anything.
created_at: 2025-04-27T17:31:09Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,7 @@
+.modal {
+  display: block;
+}
path: packages/bootstrap-vue-next/src/components/BModal/_modal.scss
user: login: coderabbitai[bot]
body: <details>
<summary>🧩 Analysis chain</summary>

🏁 Script executed:
```
#!/bin/bash
# Look at the BModal component implementation to find the root class structure
cat packages/bootstrap-vue-next/src/components/BModal/BModal.vue
```

Length of output: 14235

---

🏁 Script executed:
```
#!/bin/bash
# Check if there's a pattern for component class naming in bootstrap-vue-next
# Let's look at how other components scope their styles in SCSS files

# First, let's check the style.scss to see all imports
cat packages/bootstrap-vue-next/src/styles/styles.scss

# Look at other existing component scss files for patterns
echo "=== Looking at other component SCSS files for patterns ==="
for file in $(find packages/bootstrap-vue-next/src/components -name "_*.scss" | grep -v BModal); do
  echo "=== $file ==="
  cat "$file"
  echo ""
done

# Check if there are any class naming conventions for components
echo "=== Checking for class naming conventions ==="
grep -r "class=\"b-" packages/bootstrap-vue-next/src/components --include="*.vue" | head -n 10
```

Length of output: 22784

---



</details>

@unstoppablecarl I checked how other Bootstrap Vue Next components scope their styles, and I see many use a `b-` prefix convention for component classes (like `b-avatar`, `b-form-tags`, etc.).

The BModal component template doesn't currently apply a `b-modal` class to its root, so you have two good options:

1. Add a `b-modal` class to the component's root div and scope the style:
```scss
.b-modal .modal {
  display: block;
}
```

2. Or use the existing parent-child relationship in the template to scope it:
```scss
.modal-dialog {
  z-index: 1051;
  
  & + .modal {
    display: block;
  }
}
```

Either approach prevents the global override while maintaining the desired functionality. The first option follows the library's class naming pattern more closely.


<!-- <agentComponentType>chat-answer</agentComponentType> -->

<!-- <rawResChunk><answer><details>
<summary>🧩 Analysis chain</summary>

🏁 Script executed:
```
#!/bin/bash
# Look at the BModal component implementation to find the root class structure
cat packages/bootstrap-vue-next/src/components/BModal/BModal.vue
```

Length of output: 14235

---

🏁 Script executed:
```
#!/bin/bash
# Check if there's a pattern for component class naming in bootstrap-vue-next
# Let's look at how other components scope their styles in SCSS files

# First, let's check the style.scss to see all imports
cat packages/bootstrap-vue-next/src/styles/styles.scss

# Look at other existing component scss files for patterns
echo "=== Looking at other component SCSS files for patterns ==="
for file in $(find packages/bootstrap-vue-next/src/components -name "_*.scss" | grep -v BModal); do
  echo "=== $file ==="
  cat "$file"
  echo ""
done

# Check if there are any class naming conventions for components
echo "=== Checking for class naming conventions ==="
grep -r "class=\"b-" packages/bootstrap-vue-next/src/components --include="*.vue" | head -n 10
```

Length of output: 22784

---



</details>

@unstoppablecarl I checked how other Bootstrap Vue Next components scope their styles, and I see many use a `b-` prefix convention for component classes (like `b-avatar`, `b-form-tags`, etc.).

The BModal component template doesn't currently apply a `b-modal` class to its root, so you have two good options:

1. Add a `b-modal` class to the component's root div and scope the style:
```scss
.b-modal .modal {
  display: block;
}
```

2. Or use the existing parent-child relationship in the template to scope it:
```scss
.modal-dialog {
  z-index: 1051;
  
  & + .modal {
    display: block;
  }
}
```

Either approach prevents the global override while maintaining the desired functionality. The first option follows the library's class naming pattern more closely.</answer></rawResChunk> -->

<!-- This is an auto-generated reply by CodeRabbit -->
created_at: 2025-04-27T17:32:04Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,7 @@
+.modal {
+  display: block;
+}
path: packages/bootstrap-vue-next/src/components/BModal/_modal.scss
user: login: xvaara
body: We want to override the global, since we provide our own functionality to hide the modal. Make sure this is the one style used. Do we need to add !important to it?
created_at: 2025-04-28T19:59:49Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,7 @@
+.modal {
+  display: block;
+}
path: packages/bootstrap-vue-next/src/components/BModal/_modal.scss
user: login: coderabbitai[bot]
body: > Seems like the humans are having a chat. I'll hop back into my burrow for now. If you need me again, just tag @coderabbitai in a new comment, and I'll come hopping out!

<!-- This is an auto-generated mute comment by CodeRabbit -->
created_at: 2025-04-28T19:59:54Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,7 @@
+.modal {
+  display: block;
+}
+
+.modal-dialog {
+  z-index: 1051;
path: packages/bootstrap-vue-next/src/components/BModal/_modal.scss
user: login: xvaara
body: I'm not sure, but I think this isn't needed, since the z-index is set in the parent div... so remove it and test?
created_at: 2025-04-28T20:03:46Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,7 @@
+.modal {
+  display: block;
+}
path: packages/bootstrap-vue-next/src/components/BModal/_modal.scss
user: login: unstoppablecarl
body: I think `!important` should be used as last resort. The least intrusive thing I can think of is `class="modal BV-modal"` and `.modal + .BV-modal {.. }` . 


created_at: 2025-04-28T20:04:36Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,30 @@
+.toast :deep(.progress .progress-bar) {
+  --bs-progress-bar-transition: none;
+}
path: packages/bootstrap-vue-next/src/components/BToast/_toast.scss
user: login: xvaara
body: I think option 1 should work.
created_at: 2025-04-28T20:05:50Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,30 @@
+.toast :deep(.progress .progress-bar) {
+  --bs-progress-bar-transition: none;
+}
path: packages/bootstrap-vue-next/src/components/BToast/_toast.scss
user: login: coderabbitai[bot]
body: > Seems like the humans are having a chat. I'll hop back into my burrow for now. If you need me again, just tag @coderabbitai in a new comment, and I'll come hopping out!

<!-- This is an auto-generated mute comment by CodeRabbit -->
created_at: 2025-04-28T20:05:55Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,7 @@
+.modal {
+  display: block;
+}
path: packages/bootstrap-vue-next/src/components/BModal/_modal.scss
user: login: xvaara
body: Actually.... We should just add style="display: block" to the div, so it works even without our css.
created_at: 2025-04-28T20:08:46Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,7 @@
+.modal {
+  display: block;
+}
+
+.modal-dialog {
+  z-index: 1051;
path: packages/bootstrap-vue-next/src/components/BModal/_modal.scss
user: login: unstoppablecarl
body: Tested and Agreed
created_at: 2025-04-28T20:12:23Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,7 @@
+.btn-close-custom {
path: packages/bootstrap-vue-next/src/components/BAlert/_alert.scss
user: login: unstoppablecarl
body: seems like this could be named better? I don't know if we want to make a possibly breaking change though?
created_at: 2025-04-28T20:13:38Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,5 @@
+.dropdown-menu {
path: packages/bootstrap-vue-next/src/components/BDropdown/_dropdown.scss
user: login: unstoppablecarl
body: This might be overly broad too? maybe it should be `.BV-dropdown-menu + .dropdown-menu` like the `BModal` case?
created_at: 2025-04-28T20:14:25Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,7 @@
+.btn-close-custom {
path: packages/bootstrap-vue-next/src/components/BAlert/_alert.scss
user: login: xvaara
body: these css are going to change soon. base this pr on #2425 instead of main, or wait for it to merged.
created_at: 2025-04-28T20:39:40Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,5 @@
+.dropdown-menu {
path: packages/bootstrap-vue-next/src/components/BDropdown/_dropdown.scss
user: login: xvaara
body: I inlined this in the other pr.
created_at: 2025-04-28T23:24:55Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,3 @@
+.modal {
+  display: block;
path: packages/bootstrap-vue-next/src/components/BModal/_modal.scss
user: login: xvaara
body: this is also inlined
created_at: 2025-04-28T23:25:08Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,3 @@
+.no-transition {
+  transition: none !important;
+}
path: packages/bootstrap-vue-next/src/components/BOffcanvas/_offcanvas.scss
user: login: xvaara
body: this could be in the styles.scss directly
created_at: 2025-04-28T23:26:04Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,30 @@
+.toast .progress .progress-bar {
+  --bs-progress-bar-transition: none;
+}
+.toast:not(.show) {
+  display: block;
+  opacity: unset;
+}
+.toast.fade:not(.show) {
+  opacity: 0;
+}
+
+/*
+If you remove the last element in the list, the animation goes farther to the right then normal.
+I don't know why
+I kind of like it though, and even if I didn't, I don't know how to get rid of it.
+Getting the transitions to work here was basically all trial and error.
path: packages/bootstrap-vue-next/src/components/BToast/_toast.scss
user: login: xvaara
body: This bug is fixed in the pr, and the css is a little different.
created_at: 2025-04-28T23:29:13Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,7 @@
+.modal {
+  display: block;
+}
path: packages/bootstrap-vue-next/src/components/BModal/_modal.scss
user: login: unstoppablecarl
body: This seems like something less flexible than what I proposed. I don't know a lot about this component so maybe it isn't an issue
created_at: 2025-04-28T23:37:32Z
author_association: CONTRIBUTOR
diff_hunk: @@ -2,244 +2,81 @@
 
 <PageHeader>
 
-Documentation and examples for using Bootstrap custom progress bars featuring support for stacked bars, animated backgrounds, and text labels.
+Use our custom progress component for displaying simple or complex progress bars, featuring
+support for horizontally stacked bars, animated backgrounds, and text labels.
 
 </PageHeader>
 
 ## Basic Usage
 
-<HighlightCard>
-  <BCard>
-    <BProgress :value="0" />
-    <BProgress class="mt-3" :value="25" />
-    <BProgress class="mt-3" :value="50" />
-    <BProgress class="mt-3" :value="75" />
-    <BProgress class="mt-3" :value="100" />
-  </BCard>
-  <template #html>
-
-```vue-html
-<BProgress :value="0" />
-<BProgress :value="25" />
-<BProgress :value="50" />
-<BProgress :value="75" />
-<BProgress :value="100" />
-```
-
-  </template>
-</HighlightCard>
+<<< DEMO ./demo/ProgressBasic.vue
 
 ## Value
 
 Set the maximum value with the `max` prop (default is `100`), and the current value via the `value` prop (default `0`).
 
-When creating multiple bars in a single process, place the value prop on the individual `BProgressBar` sub-components (see the Multiple Bars section below for more details)
+When creating multiple bars in a single process, place the value prop on the individual `BProgressBar` sub-components (see the [Multiple Bars](#multiple-bars) section below for more details)
 
 ## Labels
 
 Add labels to your progress bars by either enabling `show-progress` (percentage of max) or `show-value` for the current absolute value. You may also set the precision (number of digits after the decimal) via the `precision` prop (default is `0` digits after the decimal).
 
-<HighlightCard>
-  <BCard>
-    <h5>No label</h5>
-    <BProgress :value="33.3333" :max="50" class="mb-3" />
-    <h5>Value label</h5>
-    <BProgress :value="33.3333" :max="50" show-value class="mb-3" />
-    <h5>Progress label</h5>
-    <BProgress :value="33.3333" :max="50" show-progress class="mb-3" />
-    <h5>Value label with precision</h5>
-    <BProgress :value="33.3333" :max="50" :precision="2" show-value class="mb-3" />
-    <h5>Progress label with precision</h5>
-    <BProgress :value="33.3333" :max="50" :precision="2" show-progress class="mb-3" />
-  </BCard>
-  <template #html>
-
-```vue-html
-<h5>No label</h5>
-<BProgress :value="33.3333" :max="50" />
-<h5>Value label</h5>
-<BProgress :value="33.3333" :max="50" show-value />
-<h5>Progress label</h5>
-<BProgress :value="33.3333" :max="50" show-progress />
-<h5>Value label with precision</h5>
-<BProgress :value="33.3333" :max="50" :precision="2" show-value />
-<h5>Progress label with precision</h5>
-<BProgress :value="33.3333" :max="50" :precision="2" show-progress />
-```
-
-  </template>
-</HighlightCard>
+<<< DEMO ./demo/ProgressLabels.vue
 
 ### Custom progress label
 
-Need more control over the label? Provide your own label by using the default slot within a `BProgressBar` sub-component, or by using the `label` or `label-html` property on `BProgressBar`:
-
-<HighlightCard>
-  <BCard>
-    <h5>Custom label via default slot</h5>
-    <BProgress :max="50" height="2rem">
-      <BProgressBar :value="33.333333">
-        <span>Progress: <strong>{{ (33.333333).toFixed(2) }} / {{ 50 }}</strong></span>
-      </BProgressBar>
-    </BProgress>
-    <h5 class="mt-3">Custom label via property</h5>
-    <BProgress :max="50">
-      <BProgressBar :value="33.333333" :label="`${((33.333333 / 50) * 100).toFixed(2)}%`" />
-    </BProgress>
-    <h5 class="mt-3">Custom label via property (HTML support)</h5>
-    <BProgress :max="50">
-      <BProgressBar :value="33.333333">
-        <del>{{33.333333}}</del>
-      </BProgressBar>
-    </BProgress>
-  </BCard>
-  <template #html>
-
-```vue-html
-<h5>Custom label via default slot</h5>
-<BProgress :max="50" height="2rem">
-  <BProgressBar :value="33.333333">
-    <span>Progress: <strong>{{ (33.333333).toFixed(2) }} / {{ 50 }}</strong></span>
-  </BProgressBar>
-</BProgress>
-
-<h5 class="mt-3">Custom label via property</h5>
-<BProgress :max="50">
-  <BProgressBar
-    :value="33.333333"
-    :label="`${((33.333333 / 50) * 100).toFixed(2)}%`"
-  />
-</BProgress>
-
-<h5 class="mt-3">Custom label via property (HTML support)</h5>
-<BProgress :max="50">
-  <BProgressBar :value="33.333333">
-    <del>{{33.333333}}</del>
-  </BProgressBar>
-</BProgress>
-```
-
-  </template>
-</HighlightCard>
+Need more control over the label? Provide your own label by using the default slot within a `BProgressBar` sub-component, or by using the `label` property on `BProgressBar`:
 
-## Height
+<<< DEMO ./demo/ProgressCustomLabels.vue
 
-The height of the progress bar can be controlled with the height prop. The height value should be a standard CSS dimension (px, rem, em, etc.). The default height is 1rem.
+## Width
 
-<HighlightCard>
-  <BCard>
-    <BProgress :value="25" height="1px" />
-    <BProgress class="mt-3" :value="25" height="20px" />
-  </BCard>
-  <template #html>
+`<BProgress>` will always expand to the maximum with of its parent container. To change the width,
+place `<BProgress>` in a standard Bootstrap column or apply one of the standard Bootstrap width
+classes.
 
-```vue-html
-<BProgress :value="25" height="1px" />
-<BProgress :value="25" height="20px" />
-```
+<<< DEMO ./demo/ProgressWidth.vue
 
-  </template>
-</HighlightCard>
+## Height
 
-## Backgrounds
+The height of the progress bar can be controlled with the height prop. The height value should be a standard CSS dimension (px, rem, em, etc.). The default height is 1rem.
 
-Use background variants to change the appearance of individual progress bars. The default variant is `primary`.
+<<< DEMO ./demo/ProgressHeight.vue
 
-<HighlightCard>
-  <BCard>
-    <BProgress variant="success" :value="25" />
-    <BProgress class="mt-3" variant="info" :value="50" />
-    <BProgress class="mt-3" variant="warning" :value="75" />
-    <BProgress class="mt-3" variant="danger" :value="100" />
-  </BCard>
-  <template #html>
-
-```vue-html
-<BProgress variant="success" :value="25" />
-<BProgress variant="info" :value="50" />
-<BProgress variant="warning" :value="75" />
-<BProgress variant="danger" :value="100" />
-```
-
-  </template>
-</HighlightCard>
+## Backgrounds
 
-## Multiple bars
+Use background variants to change the appearance of individual progress bars. The default variant is `primary`.
 
-Include multiple `BProgressBar` sub-components in a `BProgress` component to build a horizontally stacked set of progress bars.
+<<< DEMO ./demo/ProgressBackgrounds.vue
path: apps/docs/src/docs/components/progress.md
user: login: coderabbitai[bot]
body: _💡 Verification agent_

<details>
<summary>🧩 Analysis chain</summary>

**Heading & demo import: verify and consider renaming**  
1. Confirm `ProgressBackgrounds.vue` exists and that `<<< DEMO ./demo/ProgressBackgrounds.vue` resolves.  
2. Consider renaming `## Backgrounds` to `## Background variants` for consistency with Bootstrap terminology.

---

🏁 Script executed:

```shell
#!/bin/bash
fd ProgressBackgrounds.vue apps/docs/src/docs/components/demo
```

Length of output: 122

---



</details>

**Demo import path confirmed**  
- The file `apps/docs/src/docs/components/demo/ProgressBackgrounds.vue` exists, and `<<< DEMO ./demo/ProgressBackgrounds.vue` correctly resolves.  
- Please rename the heading in `apps/docs/src/docs/components/progress.md` (around lines 46–47) from:  
  ```md
  ## Backgrounds
  ```  
  to:  
  ```md
  ## Background variants
  ```  
  to align with Bootstrap terminology.

<details>
<summary>🤖 Prompt for AI Agents</summary>

```
In apps/docs/src/docs/components/progress.md around lines 46 to 50, rename the
heading from "## Backgrounds" to "## Background variants" to align with
Bootstrap terminology. The demo import path is correct and does not need
changes.
```

</details>

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-05-22T12:57:41Z
author_association: CONTRIBUTOR
diff_hunk: @@ -2,244 +2,81 @@
 
 <PageHeader>
 
-Documentation and examples for using Bootstrap custom progress bars featuring support for stacked bars, animated backgrounds, and text labels.
+Use our custom progress component for displaying simple or complex progress bars, featuring
+support for horizontally stacked bars, animated backgrounds, and text labels.
 
 </PageHeader>
 
 ## Basic Usage
 
-<HighlightCard>
-  <BCard>
-    <BProgress :value="0" />
-    <BProgress class="mt-3" :value="25" />
-    <BProgress class="mt-3" :value="50" />
-    <BProgress class="mt-3" :value="75" />
-    <BProgress class="mt-3" :value="100" />
-  </BCard>
-  <template #html>
-
-```vue-html
-<BProgress :value="0" />
-<BProgress :value="25" />
-<BProgress :value="50" />
-<BProgress :value="75" />
-<BProgress :value="100" />
-```
-
-  </template>
-</HighlightCard>
+<<< DEMO ./demo/ProgressBasic.vue
 
 ## Value
 
 Set the maximum value with the `max` prop (default is `100`), and the current value via the `value` prop (default `0`).
 
-When creating multiple bars in a single process, place the value prop on the individual `BProgressBar` sub-components (see the Multiple Bars section below for more details)
+When creating multiple bars in a single process, place the value prop on the individual `BProgressBar` sub-components (see the [Multiple Bars](#multiple-bars) section below for more details)
path: apps/docs/src/docs/components/progress.md
user: login: coderabbitai[bot]
body: _⚠️ Potential issue_

**Typo: “process” → “progress component”**  
The word “process” is incorrect in this context. It should reference the `<BProgress>` component. I suggest:  
```diff
- When creating multiple bars in a single process, place the value prop on the individual `BProgressBar` sub-components...
+ When creating multiple bars in a single `<BProgress>` component, place the `value` prop on each `BProgressBar` sub-component...
```  



<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

```suggestion
When creating multiple bars in a single `<BProgress>` component, place the `value` prop on each `BProgressBar` sub-component (see the [Multiple Bars](#multiple-bars) section below for more details)
```

</details>

<!-- suggestion_end -->

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 LanguageTool</summary>

[misspelling] ~18-~18: This word is normally spelled as one.
Context: ...e prop on the individual `BProgressBar` sub-components (see the [Multiple Bars](#multiple-bars...

(EN_COMPOUNDS_SUB_COMPONENTS)

</details>

</details>

<details>
<summary>🤖 Prompt for AI Agents</summary>

```
In apps/docs/src/docs/components/progress.md at line 18, replace the incorrect
word "process" with "progress component" to correctly refer to the <BProgress>
component in the sentence about creating multiple bars. This fixes the typo and
clarifies the documentation.
```

</details>

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-05-22T12:57:41Z
author_association: CONTRIBUTOR
diff_hunk: @@ -2,244 +2,81 @@
 
 <PageHeader>
 
-Documentation and examples for using Bootstrap custom progress bars featuring support for stacked bars, animated backgrounds, and text labels.
+Use our custom progress component for displaying simple or complex progress bars, featuring
+support for horizontally stacked bars, animated backgrounds, and text labels.
 
 </PageHeader>
 
 ## Basic Usage
 
-<HighlightCard>
-  <BCard>
-    <BProgress :value="0" />
-    <BProgress class="mt-3" :value="25" />
-    <BProgress class="mt-3" :value="50" />
-    <BProgress class="mt-3" :value="75" />
-    <BProgress class="mt-3" :value="100" />
-  </BCard>
-  <template #html>
-
-```vue-html
-<BProgress :value="0" />
-<BProgress :value="25" />
-<BProgress :value="50" />
-<BProgress :value="75" />
-<BProgress :value="100" />
-```
-
-  </template>
-</HighlightCard>
+<<< DEMO ./demo/ProgressBasic.vue
 
 ## Value
 
 Set the maximum value with the `max` prop (default is `100`), and the current value via the `value` prop (default `0`).
 
-When creating multiple bars in a single process, place the value prop on the individual `BProgressBar` sub-components (see the Multiple Bars section below for more details)
+When creating multiple bars in a single process, place the value prop on the individual `BProgressBar` sub-components (see the [Multiple Bars](#multiple-bars) section below for more details)
 
 ## Labels
 
 Add labels to your progress bars by either enabling `show-progress` (percentage of max) or `show-value` for the current absolute value. You may also set the precision (number of digits after the decimal) via the `precision` prop (default is `0` digits after the decimal).
 
-<HighlightCard>
-  <BCard>
-    <h5>No label</h5>
-    <BProgress :value="33.3333" :max="50" class="mb-3" />
-    <h5>Value label</h5>
-    <BProgress :value="33.3333" :max="50" show-value class="mb-3" />
-    <h5>Progress label</h5>
-    <BProgress :value="33.3333" :max="50" show-progress class="mb-3" />
-    <h5>Value label with precision</h5>
-    <BProgress :value="33.3333" :max="50" :precision="2" show-value class="mb-3" />
-    <h5>Progress label with precision</h5>
-    <BProgress :value="33.3333" :max="50" :precision="2" show-progress class="mb-3" />
-  </BCard>
-  <template #html>
-
-```vue-html
-<h5>No label</h5>
-<BProgress :value="33.3333" :max="50" />
-<h5>Value label</h5>
-<BProgress :value="33.3333" :max="50" show-value />
-<h5>Progress label</h5>
-<BProgress :value="33.3333" :max="50" show-progress />
-<h5>Value label with precision</h5>
-<BProgress :value="33.3333" :max="50" :precision="2" show-value />
-<h5>Progress label with precision</h5>
-<BProgress :value="33.3333" :max="50" :precision="2" show-progress />
-```
-
-  </template>
-</HighlightCard>
+<<< DEMO ./demo/ProgressLabels.vue
 
 ### Custom progress label
 
-Need more control over the label? Provide your own label by using the default slot within a `BProgressBar` sub-component, or by using the `label` or `label-html` property on `BProgressBar`:
-
-<HighlightCard>
-  <BCard>
-    <h5>Custom label via default slot</h5>
-    <BProgress :max="50" height="2rem">
-      <BProgressBar :value="33.333333">
-        <span>Progress: <strong>{{ (33.333333).toFixed(2) }} / {{ 50 }}</strong></span>
-      </BProgressBar>
-    </BProgress>
-    <h5 class="mt-3">Custom label via property</h5>
-    <BProgress :max="50">
-      <BProgressBar :value="33.333333" :label="`${((33.333333 / 50) * 100).toFixed(2)}%`" />
-    </BProgress>
-    <h5 class="mt-3">Custom label via property (HTML support)</h5>
-    <BProgress :max="50">
-      <BProgressBar :value="33.333333">
-        <del>{{33.333333}}</del>
-      </BProgressBar>
-    </BProgress>
-  </BCard>
-  <template #html>
-
-```vue-html
-<h5>Custom label via default slot</h5>
-<BProgress :max="50" height="2rem">
-  <BProgressBar :value="33.333333">
-    <span>Progress: <strong>{{ (33.333333).toFixed(2) }} / {{ 50 }}</strong></span>
-  </BProgressBar>
-</BProgress>
-
-<h5 class="mt-3">Custom label via property</h5>
-<BProgress :max="50">
-  <BProgressBar
-    :value="33.333333"
-    :label="`${((33.333333 / 50) * 100).toFixed(2)}%`"
-  />
-</BProgress>
-
-<h5 class="mt-3">Custom label via property (HTML support)</h5>
-<BProgress :max="50">
-  <BProgressBar :value="33.333333">
-    <del>{{33.333333}}</del>
-  </BProgressBar>
-</BProgress>
-```
-
-  </template>
-</HighlightCard>
+Need more control over the label? Provide your own label by using the default slot within a `BProgressBar` sub-component, or by using the `label` property on `BProgressBar`:
 
-## Height
+<<< DEMO ./demo/ProgressCustomLabels.vue
 
-The height of the progress bar can be controlled with the height prop. The height value should be a standard CSS dimension (px, rem, em, etc.). The default height is 1rem.
+## Width
 
-<HighlightCard>
-  <BCard>
-    <BProgress :value="25" height="1px" />
-    <BProgress class="mt-3" :value="25" height="20px" />
-  </BCard>
-  <template #html>
+`<BProgress>` will always expand to the maximum with of its parent container. To change the width,
+place `<BProgress>` in a standard Bootstrap column or apply one of the standard Bootstrap width
+classes.
 
-```vue-html
-<BProgress :value="25" height="1px" />
-<BProgress :value="25" height="20px" />
-```
+<<< DEMO ./demo/ProgressWidth.vue
path: apps/docs/src/docs/components/progress.md
user: login: coderabbitai[bot]
body: _⚠️ Potential issue_

**Fix typo and improve width section clarity**  
There’s a typo (“with” → “width”) and the phrasing can be more concise. Proposed diff:  
```diff
- ## Width
- `<BProgress>` will always expand to the maximum with of its parent container. To change the width,
- place `<BProgress>` in a standard Bootstrap column or apply one of the standard Bootstrap width
- classes.
+ ## Width
+ The `<BProgress>` component always expands to the full width of its parent container. To adjust its width,
+ wrap it in a Bootstrap column or apply a width utility class (e.g., `w-50`).
```  



<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

```suggestion
## Width
The `<BProgress>` component always expands to the full width of its parent container. To adjust its width,
wrap it in a Bootstrap column or apply a width utility class (e.g., `w-50`).

<<< DEMO ./demo/ProgressWidth.vue
```

</details>

<!-- suggestion_end -->

<details>
<summary>🤖 Prompt for AI Agents</summary>

```
In apps/docs/src/docs/components/progress.md around lines 32 to 38, fix the typo
by changing "with" to "width" in the description of the <BProgress> component.
Also, rephrase the sentences to be more concise and clear, ensuring the
explanation about how <BProgress> expands and how to control its width using
Bootstrap columns or width classes is straightforward and easy to understand.
```

</details>

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-05-22T12:57:42Z
author_association: CONTRIBUTOR
diff_hunk: @@ -2,244 +2,81 @@
 
 <PageHeader>
 
-Documentation and examples for using Bootstrap custom progress bars featuring support for stacked bars, animated backgrounds, and text labels.
+Use our custom progress component for displaying simple or complex progress bars, featuring
+support for horizontally stacked bars, animated backgrounds, and text labels.
 
 </PageHeader>
 
 ## Basic Usage
 
-<HighlightCard>
-  <BCard>
-    <BProgress :value="0" />
-    <BProgress class="mt-3" :value="25" />
-    <BProgress class="mt-3" :value="50" />
-    <BProgress class="mt-3" :value="75" />
-    <BProgress class="mt-3" :value="100" />
-  </BCard>
-  <template #html>
-
-```vue-html
-<BProgress :value="0" />
-<BProgress :value="25" />
-<BProgress :value="50" />
-<BProgress :value="75" />
-<BProgress :value="100" />
-```
-
-  </template>
-</HighlightCard>
+<<< DEMO ./demo/ProgressBasic.vue
 
 ## Value
 
 Set the maximum value with the `max` prop (default is `100`), and the current value via the `value` prop (default `0`).
 
-When creating multiple bars in a single process, place the value prop on the individual `BProgressBar` sub-components (see the Multiple Bars section below for more details)
+When creating multiple bars in a single process, place the value prop on the individual `BProgressBar` sub-components (see the [Multiple Bars](#multiple-bars) section below for more details)
 
 ## Labels
 
 Add labels to your progress bars by either enabling `show-progress` (percentage of max) or `show-value` for the current absolute value. You may also set the precision (number of digits after the decimal) via the `precision` prop (default is `0` digits after the decimal).
 
-<HighlightCard>
-  <BCard>
-    <h5>No label</h5>
-    <BProgress :value="33.3333" :max="50" class="mb-3" />
-    <h5>Value label</h5>
-    <BProgress :value="33.3333" :max="50" show-value class="mb-3" />
-    <h5>Progress label</h5>
-    <BProgress :value="33.3333" :max="50" show-progress class="mb-3" />
-    <h5>Value label with precision</h5>
-    <BProgress :value="33.3333" :max="50" :precision="2" show-value class="mb-3" />
-    <h5>Progress label with precision</h5>
-    <BProgress :value="33.3333" :max="50" :precision="2" show-progress class="mb-3" />
-  </BCard>
-  <template #html>
-
-```vue-html
-<h5>No label</h5>
-<BProgress :value="33.3333" :max="50" />
-<h5>Value label</h5>
-<BProgress :value="33.3333" :max="50" show-value />
-<h5>Progress label</h5>
-<BProgress :value="33.3333" :max="50" show-progress />
-<h5>Value label with precision</h5>
-<BProgress :value="33.3333" :max="50" :precision="2" show-value />
-<h5>Progress label with precision</h5>
-<BProgress :value="33.3333" :max="50" :precision="2" show-progress />
-```
-
-  </template>
-</HighlightCard>
+<<< DEMO ./demo/ProgressLabels.vue
 
 ### Custom progress label
 
-Need more control over the label? Provide your own label by using the default slot within a `BProgressBar` sub-component, or by using the `label` or `label-html` property on `BProgressBar`:
-
-<HighlightCard>
-  <BCard>
-    <h5>Custom label via default slot</h5>
-    <BProgress :max="50" height="2rem">
-      <BProgressBar :value="33.333333">
-        <span>Progress: <strong>{{ (33.333333).toFixed(2) }} / {{ 50 }}</strong></span>
-      </BProgressBar>
-    </BProgress>
-    <h5 class="mt-3">Custom label via property</h5>
-    <BProgress :max="50">
-      <BProgressBar :value="33.333333" :label="`${((33.333333 / 50) * 100).toFixed(2)}%`" />
-    </BProgress>
-    <h5 class="mt-3">Custom label via property (HTML support)</h5>
-    <BProgress :max="50">
-      <BProgressBar :value="33.333333">
-        <del>{{33.333333}}</del>
-      </BProgressBar>
-    </BProgress>
-  </BCard>
-  <template #html>
-
-```vue-html
-<h5>Custom label via default slot</h5>
-<BProgress :max="50" height="2rem">
-  <BProgressBar :value="33.333333">
-    <span>Progress: <strong>{{ (33.333333).toFixed(2) }} / {{ 50 }}</strong></span>
-  </BProgressBar>
-</BProgress>
-
-<h5 class="mt-3">Custom label via property</h5>
-<BProgress :max="50">
-  <BProgressBar
-    :value="33.333333"
-    :label="`${((33.333333 / 50) * 100).toFixed(2)}%`"
-  />
-</BProgress>
-
-<h5 class="mt-3">Custom label via property (HTML support)</h5>
-<BProgress :max="50">
-  <BProgressBar :value="33.333333">
-    <del>{{33.333333}}</del>
-  </BProgressBar>
-</BProgress>
-```
-
-  </template>
-</HighlightCard>
+Need more control over the label? Provide your own label by using the default slot within a `BProgressBar` sub-component, or by using the `label` property on `BProgressBar`:
 
-## Height
+<<< DEMO ./demo/ProgressCustomLabels.vue
 
-The height of the progress bar can be controlled with the height prop. The height value should be a standard CSS dimension (px, rem, em, etc.). The default height is 1rem.
+## Width
 
-<HighlightCard>
-  <BCard>
-    <BProgress :value="25" height="1px" />
-    <BProgress class="mt-3" :value="25" height="20px" />
-  </BCard>
-  <template #html>
+`<BProgress>` will always expand to the maximum with of its parent container. To change the width,
+place `<BProgress>` in a standard Bootstrap column or apply one of the standard Bootstrap width
+classes.
 
-```vue-html
-<BProgress :value="25" height="1px" />
-<BProgress :value="25" height="20px" />
-```
+<<< DEMO ./demo/ProgressWidth.vue
 
-  </template>
-</HighlightCard>
+## Height
 
-## Backgrounds
+The height of the progress bar can be controlled with the height prop. The height value should be a standard CSS dimension (px, rem, em, etc.). The default height is 1rem.
 
-Use background variants to change the appearance of individual progress bars. The default variant is `primary`.
+<<< DEMO ./demo/ProgressHeight.vue
 
-<HighlightCard>
-  <BCard>
-    <BProgress variant="success" :value="25" />
-    <BProgress class="mt-3" variant="info" :value="50" />
-    <BProgress class="mt-3" variant="warning" :value="75" />
-    <BProgress class="mt-3" variant="danger" :value="100" />
-  </BCard>
-  <template #html>
-
-```vue-html
-<BProgress variant="success" :value="25" />
-<BProgress variant="info" :value="50" />
-<BProgress variant="warning" :value="75" />
-<BProgress variant="danger" :value="100" />
-```
-
-  </template>
-</HighlightCard>
+## Backgrounds
 
-## Multiple bars
+Use background variants to change the appearance of individual progress bars. The default variant is `primary`.
 
-Include multiple `BProgressBar` sub-components in a `BProgress` component to build a horizontally stacked set of progress bars.
+<<< DEMO ./demo/ProgressBackgrounds.vue
 
-<HighlightCard>
-  <BCard>
-    <BProgress>
-      <BProgressBar :value="15" />
-      <BProgressBar :value="30" variant="success" />
-      <BProgressBar :value="20" variant="info" />
-    </BProgress>
-  </BCard>
-  <template #html>
-
-```vue-html
-<BProgress>
-  <BProgressBar :value="15" />
-  <BProgressBar :value="30" variant="success" />
-  <BProgressBar :value="20" variant="info" />
-</BProgress>
-```
-
-  </template>
-</HighlightCard>
-
-## Striped
+## Striped Background
 
 Set `striped` to apply a stripe via CSS gradient over the progress bar's background variant.
 
-<HighlightCard>
-  <BCard>
-    <BProgress striped :value="10" />
-    <BProgress striped class="mt-3" variant="success" :value="25" />
-    <BProgress striped class="mt-3" variant="info" :value="50" />
-    <BProgress striped class="mt-3" variant="warning" :value="75" />
-    <BProgress striped class="mt-3" variant="danger" :value="100" />
-  </BCard>
-  <template #html>
-
-```vue-html
-<BProgress striped :value="10" />
-<BProgress striped :value="25" variant="success" />
-<BProgress striped :value="50" variant="info" />
-<BProgress striped :value="75" variant="warning" />
-<BProgress striped :value="100" variant="danger" />
-```
-
-  </template>
-</HighlightCard>
-
-## Animated stripes
+<<< DEMO ./demo/ProgressStriped.vue
+
+## Animated Background
 
 The striped gradient can also be animated by setting the `animated` prop.
 
-<HighlightCard>
-  <BCard>
-    <BProgress :value="75" striped :animated="animate" />
-    <BButton class="mt-3" @click="animate = !animate">Toggle animation</BButton>
-  </BCard>
-  <template #html>
+<<< DEMO ./demo/ProgressAnimated.vue
 
-```vue
-<template>
-  <BProgress :value="75" striped :animated="animate" />
-  <BButton class="mt-3" @click="animate = !animate">Toggle animation</BButton>
-</template>
+::: info NOTE
+If `animated` is true, `striped` will automatically be enabled.
+:::
 
-<script setup lang="ts">
-import {ref} from 'vue'
+## Multiple bars
 
-const animate = ref(false)
-</script>
-```
+Include multiple `BProgressBar` sub-components in a `BProgress` component to build a horizontally stacked set of progress bars.
+
+<<< DEMO ./demo/ProgressMultipleBars.vue
+
+::: info NOTE
+
path: apps/docs/src/docs/components/progress.md
user: login: coderabbitai[bot]
body: _💡 Verification agent_

<details>
<summary>🧩 Analysis chain</summary>

**Refine multiple bars section & verify demo**  
1. The description can be tightened:  
```diff
- Include multiple `BProgressBar` sub-components in a `BProgress` component to build a horizontally stacked set of progress bars.
+ Include multiple `<BProgressBar>` components within `<BProgress>` to create a stacked progress bar.
```  
2. Ensure `ProgressMultipleBars.vue` demo exists.

---

🏁 Script executed:

```shell
#!/bin/bash
fd ProgressMultipleBars.vue apps/docs/src/docs/components/demo
```

Length of output: 124

---



</details>

**Update “Multiple bars” section description**  
The demo file `ProgressMultipleBars.vue` has been verified in `apps/docs/src/docs/components/demo/ProgressMultipleBars.vue`. Please tighten the description in `apps/docs/src/docs/components/progress.md` as follows:

File: apps/docs/src/docs/components/progress.md  
Lines: 68-75  
```diff
- Include multiple `BProgressBar` sub-components in a `BProgress` component to build a horizontally stacked set of progress bars.
+ Include multiple `<BProgressBar>` components within `<BProgress>` to create a stacked progress bar.
```  


<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

```suggestion
## Multiple bars

Include multiple `<BProgressBar>` components within `<BProgress>` to create a stacked progress bar.

<<< DEMO ./demo/ProgressMultipleBars.vue

::: info NOTE
```

</details>

<!-- suggestion_end -->

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 LanguageTool</summary>

[misspelling] ~70-~70: This word is normally spelled as one.
Context: ...e bars  Include multiple `BProgressBar` sub-components in a `BProgress` component to build a h...

(EN_COMPOUNDS_SUB_COMPONENTS)

</details>

</details>

<details>
<summary>🤖 Prompt for AI Agents</summary>

```
In apps/docs/src/docs/components/progress.md around lines 68 to 75, tighten the
description of the "Multiple bars" section to be more concise and clear. Update
the text to briefly explain that multiple BProgressBar components can be
included within a BProgress component to create a horizontally stacked progress
bar set. Also confirm the demo file ProgressMultipleBars.vue exists in
apps/docs/src/docs/components/demo and reference it appropriately.
```

</details>

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-05-22T12:57:42Z
author_association: CONTRIBUTOR
diff_hunk: @@ -19,15 +19,16 @@
 // Please do not commit this file
 import {computed, h, onMounted, ref} from 'vue'
 import {
-  BToast,
+  // BToast,
   type ColorVariant,
-  type OrchestratedToast,
+  // type OrchestratedToast,
   useToastController,
 } from 'bootstrap-vue-next'
 
 const {show, toasts} = useToastController()
 
-const firstRef = ref<OrchestratedToast>({
+// const firstRef = ref<OrchestratedToast>({
+const firstRef = ref({
path: apps/playground/src/components/Comps/TToast.vue
user: login: coderabbitai[bot]
body: _🛠️ Refactor suggestion_

**Type information removed from firstRef**

The type annotation for `firstRef` has been removed, changing from a strongly typed `OrchestratedToast` reference to an untyped object. This could lead to type safety issues.


Consider adding appropriate type information:

```diff
-const firstRef = ref({
+const firstRef = ref<{body: string}>({
  body: `${Math.random()}`,
})
```


<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

```suggestion
const firstRef = ref<{body: string}>({
  body: `${Math.random()}`,
})
```

</details>

<!-- suggestion_end -->

<details>
<summary>🤖 Prompt for AI Agents</summary>

```
In apps/playground/src/components/Comps/TToast.vue around lines 30 to 31, the
type annotation for the ref variable firstRef was removed, causing loss of type
safety. Restore the type annotation by explicitly typing firstRef as
ref<OrchestratedToast> to ensure proper type checking and maintain strong typing
in the code.
```

</details>

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-05-22T12:57:42Z
author_association: CONTRIBUTOR
diff_hunk: @@ -58,6 +58,7 @@
           :class="[props.menuClass, computedMenuClasses]"
           :aria-labelledby="computedId"
           :role="props.role"
+          style="display: block"
           @click="onClickInside"
path: packages/bootstrap-vue-next/src/components/BDropdown/BDropdown.vue
user: login: coderabbitai[bot]
body: _⚠️ Potential issue_

**Inline `display: block` overrides framework control**  

`v-show` manipulates the element’s `display` style at runtime.  
Hard-coding `style="display: block"` means the element will _always_ render even when `v-show="false"` (Vue will attempt to restore the cached “block” value). This breaks the intended hide/show logic and duplicates the style you already moved to the SCSS (`_dropdown.scss`).  

```diff
-          :style="[floatingStyles, sizeStyles]"
-          class="dropdown-menu overflow-auto"
-          :class="[props.menuClass, computedMenuClasses]"
-          :aria-labelledby="computedId"
-          :role="props.role"
-          style="display: block"
+          :style="[floatingStyles, sizeStyles]"
+          class="dropdown-menu overflow-auto"
+          :class="[props.menuClass, computedMenuClasses]"
+          :aria-labelledby="computedId"
+          :role="props.role"
```  

Rely solely on the class–based rule in the extracted SCSS.



<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

```suggestion
          :style="[floatingStyles, sizeStyles]"
          class="dropdown-menu overflow-auto"
          :class="[props.menuClass, computedMenuClasses]"
          :aria-labelledby="computedId"
          :role="props.role"
          @click="onClickInside"
```

</details>

<!-- suggestion_end -->

<details>
<summary>🤖 Prompt for AI Agents</summary>

```
In packages/bootstrap-vue-next/src/components/BDropdown/BDropdown.vue at lines
61-62, remove the inline style attribute `style="display: block"` because it
conflicts with Vue's `v-show` directive by forcing the element to always
display. Instead, rely on the CSS class rules defined in the `_dropdown.scss`
file to control the display property, ensuring the show/hide logic works
correctly.
```

</details>

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-05-22T12:57:42Z
author_association: CONTRIBUTOR
diff_hunk: @@ -1,32 +1,42 @@
-import {computed, type Plugin, reactive, readonly} from 'vue'
+import {type Plugin, readonly, ref, type Ref} from 'vue'
 import {
   globalShowHideStorageInjectionKey,
   type RegisterShowHideFnInput,
-  type RegisterShowHideValue,
+  type RegisterShowHideMapValue,
 } from '../../utils/keys'
 
 export const showHidePlugin: Plugin = {
   install(app) {
-    const values: Record<
-      string,
-      {value: boolean; toggle: () => void; show: () => void; hide: (trigger?: string) => void}
-    > = reactive({})
+    const values: Ref<Map<string, RegisterShowHideMapValue>> = ref(new Map())
 
-    const fun = (({id, value, toggle, show, hide}: RegisterShowHideFnInput) => {
-      values[id] = computed(() => ({value: value.value, toggle, show, hide})) as unknown as {
-        value: boolean
-        toggle: () => void
-        show: () => void
-        hide: (trigger?: string) => void
-      }
+    const register = ({
+      id,
+      component,
+      value,
+      toggle,
+      show,
+      hide,
+      registerTrigger,
+      unregisterTrigger,
+    }: RegisterShowHideFnInput) => {
+      values.value.set(id, {
+        id,
+        component,
+        value: readonly(value),
+        toggle,
+        show,
+        hide,
+        registerTrigger,
+        unregisterTrigger,
+      })
       return {
         unregister() {
-          delete values[id]
+          // delete values.value[id]
+          values.value.delete(id)
         },
       }
-    }) as unknown as RegisterShowHideValue
+    }
path: packages/bootstrap-vue-next/src/plugins/showHide/index.ts
user: login: coderabbitai[bot]
body: _⚠️ Potential issue_

**Guard against duplicate IDs to avoid silent override**

Registering two components with the same `id` will silently overwrite the first entry in the `Map`, breaking toggling for the original component.

```diff
-      values.value.set(id, {
+      if (values.value.has(id)) {
+        console.warn(`[showHide] duplicate id "${id}" – existing registration will be kept`)
+        return {unregister: () => { /* no-op */ }}
+      }
+      values.value.set(id, {
```



<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

```suggestion
    const values: Ref<Map<string, RegisterShowHideMapValue>> = ref(new Map())

    const register = ({
      id,
      component,
      value,
      toggle,
      show,
      hide,
      registerTrigger,
      unregisterTrigger,
    }: RegisterShowHideFnInput) => {
      if (values.value.has(id)) {
        console.warn(`[showHide] duplicate id "${id}" – existing registration will be kept`)
        return { unregister: () => { /* no-op */ } }
      }
      values.value.set(id, {
        id,
        component,
        value: readonly(value),
        toggle,
        show,
        hide,
        registerTrigger,
        unregisterTrigger,
      })
      return {
        unregister() {
          values.value.delete(id)
        },
      }
    }
```

</details>

<!-- suggestion_end -->

<details>
<summary>🤖 Prompt for AI Agents</summary>

```
In packages/bootstrap-vue-next/src/plugins/showHide/index.ts between lines 10
and 38, the register function allows duplicate IDs which silently overwrite
existing entries in the Map. To fix this, add a check before setting a new entry
in the Map to see if the ID already exists. If it does, throw an error or handle
the duplicate case explicitly to prevent overwriting and ensure each ID is
unique.
```

</details>

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-05-22T12:57:42Z
author_association: CONTRIBUTOR
diff_hunk: @@ -38,33 +38,39 @@ export const resolveActiveStatus = (values: DirectiveBinding['value']): boolean
 export const resolveContent = (
   values: DirectiveBinding['value'],
   el: HTMLElement
-): {title?: string; content?: string} => {
+): {title?: string; body?: string} => {
   const isActive = resolveActiveStatus(values)
   if (!isActive) return {}
 
   const missingBindingValue =
     typeof values === 'undefined' ||
-    (typeof values === 'object' && !values.title && !values.content)
+    (typeof values === 'object' && !values.title && !values.content && !values.body)
   const title = el.getAttribute('title') || el.getAttribute('data-original-title')
path: packages/bootstrap-vue-next/src/utils/floatingUi.ts
user: login: coderabbitai[bot]
body: _⚠️ Potential issue_

**Guard against `null` to avoid runtime crash**

`typeof values === 'object'` is also `true` for `null`, so `values.title` & friends will throw.  
Use a truthy check (or optional-chaining) before property access.

```diff
-  const missingBindingValue =
-    typeof values === 'undefined' ||
-    (typeof values === 'object' && !values.title && !values.content && !values.body)
+  const missingBindingValue =
+    values == null ||
+    (typeof values === 'object' && !values?.title && !values?.content && !values?.body)
```



<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

```suggestion
  const missingBindingValue =
    values == null ||
    (typeof values === 'object' && !values?.title && !values?.content && !values?.body)
  const title = el.getAttribute('title') || el.getAttribute('data-original-title')
```

</details>

<!-- suggestion_end -->

<details>
<summary>🤖 Prompt for AI Agents</summary>

```
In packages/bootstrap-vue-next/src/utils/floatingUi.ts around lines 45 to 48,
the code checks if values is an object but does not guard against null, which
causes runtime errors when accessing properties like values.title. Fix this by
adding a truthy check before accessing properties, for example using optional
chaining or explicitly checking values is not null before accessing its
properties.
```

</details>

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-05-22T12:57:42Z
author_association: CONTRIBUTOR
diff_hunk: @@ -87,11 +93,10 @@ export const resolveDirectiveProps = (
         : binding.modifiers.top
           ? 'top'
           : undefined,
-  html: true,
   ...(typeof binding.value === 'object' ? binding.value : undefined),
   ...(binding.modifiers.interactive ? {noninteractive: false} : undefined),
   title: null,
-  content: null,
+  body: null,
 })
path: packages/bootstrap-vue-next/src/utils/floatingUi.ts
user: login: coderabbitai[bot]
body: _⚠️ Potential issue_

**`title` / `body` are being overwritten with `null`**

You spread `binding.value` first and immediately override the two key props.  
If a user supplies `body`/`title` through the directive, the override wipes them out and nothing is rendered.

```diff
   ...(typeof binding.value === 'object' ? binding.value : undefined),
   ...(binding.modifiers.interactive ? {noninteractive: false} : undefined),
-  title: null,
-  body: null,
+  // keep undefined so user-provided values survive
+  ...(binding.value?.title ? {} : {title: undefined}),
+  ...(binding.value?.body ? {} : {body: undefined}),
```

Alternatively, just delete these two lines—they are redundant.



<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

```suggestion
  ...(typeof binding.value === 'object' ? binding.value : undefined),
  ...(binding.modifiers.interactive ? {noninteractive: false} : undefined),
  // keep undefined so user-provided values survive
  ...(binding.value?.title ? {} : {title: undefined}),
  ...(binding.value?.body ? {} : {body: undefined}),
})
```

</details>

<!-- suggestion_end -->

<details>
<summary>🤖 Prompt for AI Agents</summary>

```
In packages/bootstrap-vue-next/src/utils/floatingUi.ts around lines 96 to 100,
the properties title and body are being overwritten with null after spreading
binding.value, which causes any user-supplied title or body to be wiped out. To
fix this, remove the lines setting title: null and body: null so that
user-provided values are preserved and rendered correctly.
```

</details>

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-05-22T12:57:42Z
author_association: CONTRIBUTOR
diff_hunk: @@ -1,49 +1,51 @@
 <template>
-  <PopoverList />
-  <TooltipList />
+  <ConditionalTeleport :to="props.teleportTo" :disabled="props.teleportDisabled">
+    <div id="__BVID__popover-container" v-bind="$attrs">
+      <component
+        :is="_component ?? (val.tooltip ? BTooltip : BPopover)"
+        v-for="[
+          key,
+          {component: _component, promise, slots, ...val},
+        ] in tools.popovers?.value.entries() ?? []"
+        :key="key"
+        v-bind="val"
+        initial-animation
+        :teleport-disabled="true"
+        @hide="
+          (e: BvTriggerableEvent) => {
+            val.onHide?.(e)
+            if (e.defaultPrevented) {
+              return
+            }
+            promise.resolve(e)
+          }
+        "
+      >
path: packages/bootstrap-vue-next/src/components/BPopover/BPopoverOrchestrator.vue
user: login: coderabbitai[bot]
body: _🛠️ Refactor suggestion_

**`@hidden` event parity missing**

Unlike the toast orchestrator, the popover orchestrator only listens to `hide`.  
If the controller relies on the promise resolving at `hidden`, popovers will never resolve for callers expecting that behaviour.

Add a symmetrical `@hidden` handler or clarify in docs.

<details>
<summary>🤖 Prompt for AI Agents</summary>

```
In packages/bootstrap-vue-next/src/components/BPopover/BPopoverOrchestrator.vue
between lines 14 and 23, the component listens to the 'hide' event but lacks a
corresponding 'hidden' event handler, causing promise resolution issues for
callers expecting resolution on 'hidden'. Add a symmetrical '@hidden' event
handler similar to the '@hide' handler that calls the appropriate callback and
resolves the promise to ensure consistent behavior with the toast orchestrator.
```

</details>

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-05-22T12:57:42Z
author_association: CONTRIBUTOR
diff_hunk: @@ -2,33 +2,47 @@
   <ConditionalTeleport :to="props.teleportTo" :disabled="props.teleportDisabled">
     <div id="__BVID__modal-container" v-bind="$attrs">
       <component
-        :is="modal.component ?? BModal"
-        v-for="[self, modal] in tools.modals?.value"
-        :key="self"
-        v-bind="modal.props"
-        v-model="modal.props._modelValue"
+        :is="_component ?? BModal"
+        v-for="[key, {component: _component, promise, options, slots, ...val}] in tools.modals
+          ?.value"
+        :key="key"
+        :ref="(ref: ComponentPublicInstance) => (promise.value.ref = ref)"
+        v-bind="val"
         initial-animation
         :teleport-disabled="true"
-        @update:model-value="tools.leave?.(self)"
         @hide="
           (e: BvTriggerableEvent) => {
-            // These following are confirm rules, otherwise we always resolve true
-            if (modal.props._isConfirm === true) {
-              if (e.trigger === 'ok') {
-                modal.props._promise.resolve(true)
-                return
-              }
-              if (e.trigger === 'cancel') {
-                modal.props._promise.resolve(false)
-                return
-              }
-              modal.props._promise.resolve(null)
+            e.ok = e.trigger === 'ok' ? true : e.trigger === 'cancel' ? false : null
+            val.onHide?.(e)
+            if (e.defaultPrevented) {
+              return
+            }
+            promise.stop?.()
+            if (options?.resolveOnHide) {
+              promise.resolve(options.returnBoolean ? e.ok : e)
+            }
+          }
path: packages/bootstrap-vue-next/src/components/BModal/BModalOrchestrator.vue
user: login: coderabbitai[bot]
body: _⚠️ Potential issue_

**Dereference the promise ref before calling its helpers**

`promise` is a `ref`, yet `stop()` is invoked directly.  
At runtime this yields `undefined is not a function`.

```diff
- promise.stop?.()
+ promise.value.stop?.()
```

Apply the same adjustment for `resolve()` below.



<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

```suggestion
            e.ok = e.trigger === 'ok' ? true : e.trigger === 'cancel' ? false : null
            val.onHide?.(e)
            if (e.defaultPrevented) {
              return
            }
            promise.value.stop?.()
            if (options?.resolveOnHide) {
              promise.resolve(options.returnBoolean ? e.ok : e)
            }
```

</details>

<!-- suggestion_end -->

<details>
<summary>🤖 Prompt for AI Agents</summary>

```
In packages/bootstrap-vue-next/src/components/BModal/BModalOrchestrator.vue
between lines 15 and 24, the code calls stop() and resolve() directly on the
promise ref, which causes a runtime error because promise is a ref object. To
fix this, dereference the promise ref by calling stop() and resolve() on
promise.value instead of promise directly.
```

</details>

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-05-22T12:57:43Z
author_association: CONTRIBUTOR
diff_hunk: @@ -33,84 +33,86 @@ const getTargets = (
   return targets.filter((t, index, arr) => t && arr.indexOf(t) === index)
 }
 
-const toggle = (
-  targetIds: readonly string[],
-  el: Readonly<HTMLElement>,
-  binding: DirectiveBinding,
+const handleUpdate = (
+  el: Element,
+  binding: DirectiveBinding<string | readonly string[] | undefined>,
   vnode: VNode
 ) => {
+  // Determine targets
+  const targets = getTargets(binding, el)
+  if (targets.length === 0) return
+
   const provides = findProvides(binding, vnode)
-  const showHide = (provides as Record<symbol, RegisterShowHideValue>)[
+  const showHideMap = (provides as Record<symbol, RegisterShowHideValue>)[
     globalShowHideStorageInjectionKey
-  ]?.map
-
-  targetIds.forEach((targetId) => {
-    if (showHide?.[targetId]) {
-      showHide[targetId].toggle()
-      return
-    }
-    const target = document.getElementById(targetId)
-
-    if (target !== null) {
-      target.dispatchEvent(new Event('bv-toggle'))
+  ]?.values
+  if ((el as HTMLElement).dataset.bvtoggle) {
+    const oldTargets = ((el as HTMLElement).dataset.bvtoggle || '').split(' ')
+    if (oldTargets.length === 0) return
+    for (const targetId of oldTargets) {
+      const showHide = showHideMap?.value.get(targetId)
+      if (!showHide) {
+        continue
+      }
+      if (!targets.includes(targetId)) {
+        toValue(showHide).unregisterTrigger('click', el, false)
+      }
     }
-  })
-  setTimeout(() => checkVisibility(targetIds, el), 50)
-}
+  }
+  ;(el as HTMLElement).dataset.bvtoggle = targets.join(' ')
 
-const checkVisibility = (targetIds: readonly string[], el: Readonly<HTMLElement>) => {
-  let visible = false
-  targetIds.forEach((targetId) => {
-    const target = document.getElementById(targetId)
+  targets.forEach(async (targetId) => {
+    let count = 0
+    while (count < 5) {
+      const showHide = showHideMap?.value.get(targetId)
+      if (!showHide) {
+        count++
+        await new Promise((resolve) => setTimeout(resolve, 100))
+        if (count < 4) continue
+        // eslint-disable-next-line no-console
+        console.warn(`[v-b-toggle] Target with ID ${targetId} not found`)
+        break
+      }
+      // Register the trigger element
 
-    if (target?.classList.contains('show')) {
-      visible = true
-    }
-    if (target?.classList.contains('closing')) {
-      visible = false
+      toValue(showHide).unregisterTrigger('click', el, false)
+      toValue(showHide).registerTrigger('click', el)
+      break
     }
   })
-  el.setAttribute('aria-expanded', visible ? 'true' : 'false')
-  el.classList.remove(visible ? 'collapsed' : 'not-collapsed')
-  el.classList.add(visible ? 'not-collapsed' : 'collapsed')
-}
 
-const handleUpdate = (
-  el: WithToggle,
+  el.setAttribute('aria-controls', targets.join(' '))
+}
+const handleUnmount = (
+  el: Element,
   binding: DirectiveBinding<string | readonly string[] | undefined>,
   vnode: VNode
 ) => {
   // Determine targets
   const targets = getTargets(binding, el)
   if (targets.length === 0) return
+  const provides = findProvides(binding, vnode)
path: packages/bootstrap-vue-next/src/directives/BToggle/index.ts
user: login: coderabbitai[bot]
body: _⚠️ Potential issue_

**Same stale-state issue on `unmounted` when `getTargets` returns an empty list**  
If `v-b-toggle` received an empty binding but still has a non-empty
`data-bvtoggle`, the current guard (`if (targets.length === 0) return`)
skips all clean-up.  Consider falling back to the IDs stored in
`dataset.bvtoggle` when `getTargets` produces no results, or simply
dropping the guard and performing the clean-up unconditionally.

<details>
<summary>🤖 Prompt for AI Agents</summary>

```
In packages/bootstrap-vue-next/src/directives/BToggle/index.ts around lines 87
to 94, the current guard skips cleanup if getTargets returns an empty array,
which causes stale state issues on unmounted. To fix this, remove the guard that
returns early when targets is empty and instead fall back to using the IDs
stored in el.dataset.bvtoggle for cleanup, or perform cleanup unconditionally
regardless of targets length to ensure proper state reset.
```

</details>

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-05-22T12:57:43Z
author_association: CONTRIBUTOR
diff_hunk: @@ -33,84 +33,86 @@ const getTargets = (
   return targets.filter((t, index, arr) => t && arr.indexOf(t) === index)
 }
 
-const toggle = (
-  targetIds: readonly string[],
-  el: Readonly<HTMLElement>,
-  binding: DirectiveBinding,
+const handleUpdate = (
+  el: Element,
+  binding: DirectiveBinding<string | readonly string[] | undefined>,
   vnode: VNode
 ) => {
+  // Determine targets
+  const targets = getTargets(binding, el)
+  if (targets.length === 0) return
+
path: packages/bootstrap-vue-next/src/directives/BToggle/index.ts
user: login: coderabbitai[bot]
body: _⚠️ Potential issue_

**Early-return prevents deregistration & leaves stale state behind**  
If the set of targets becomes empty on a subsequent `updated` hook, the code returns immediately and never:

* removes the old `data-bvtoggle` attribute
* unregisters the element as a trigger on the previously‐registered targets
* clears ARIA attributes / CSS classes

This causes memory-leaks and incorrect behaviour (the element still toggles the *old* targets).

```diff
-  const targets = getTargets(binding, el)
-  if (targets.length === 0) return
+  const targets = getTargets(binding, el)
+  if (targets.length === 0) {
+    // Clean-up any previous registrations before exiting
+    handleUnmount(el, binding, vnode)
+    return
+  }
```

(You can move `handleUnmount` above `handleUpdate` or extract the shared
cleanup logic into a small helper.)  



<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

```suggestion
  // Determine targets
  const targets = getTargets(binding, el)
  if (targets.length === 0) {
    // Clean-up any previous registrations before exiting
    handleUnmount(el, binding, vnode)
    return
  }
```

</details>

<!-- suggestion_end -->

<details>
<summary>🤖 Prompt for AI Agents</summary>

```
In packages/bootstrap-vue-next/src/directives/BToggle/index.ts around lines 41
to 44, the early return when targets are empty prevents cleanup of old state,
causing memory leaks and incorrect toggling behavior. Refactor the updated hook
to always perform cleanup by removing the old data-bvtoggle attribute,
unregistering the element from previous targets, and clearing ARIA attributes
and CSS classes before returning. You can achieve this by moving the cleanup
logic into a shared helper function or by placing the handleUnmount logic above
handleUpdate to ensure proper deregistration even when targets are empty.
```

</details>

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-05-22T12:57:43Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,70 @@
+import {
+  type ComponentInternalInstance,
+  computed,
+  getCurrentInstance,
+  inject,
+  type MaybeRefOrGetter,
+  ref,
+  toValue,
+} from 'vue'
+
+import {globalShowHideStorageInjectionKey} from '../../utils/keys'
+
+export const useToggle = (id: MaybeRefOrGetter<string | undefined> = undefined) => {
+  const instance = getCurrentInstance()
+  const {values: registry} = inject(globalShowHideStorageInjectionKey, {
+    register: () => ({unregister: () => {}}),
+    values: ref(new Map()),
+  })
+
+  if (!registry) {
+    throw new Error(
+      'useToggle() was called outside of the setup() function or the plugin is not provided.'
+    )
+  }
path: packages/bootstrap-vue-next/src/composables/useToggle/index.ts
user: login: coderabbitai[bot]
body: _⚠️ Potential issue_

**`inject` fallback masks configuration errors**

Because you supply a default object to `inject`, `registry` is **always** defined.  
If the plugin is forgotten, `show()`/`hide()` silently reject with “not found”, making the root cause hard to trace.

```diff
-const {values: registry} = inject(globalShowHideStorageInjectionKey, {
+const injected = inject(globalShowHideStorageInjectionKey, undefined)
+if (!injected) {
+  throw new Error(
+    'useToggle() requires the BootstrapVueNext plugin providing global show/hide storage.'
+  )
+}
+const {values: registry} = injected
```

This surfaces mis-configuration immediately and avoids hidden runtime paths.




> Committable suggestion skipped: line range outside the PR's diff.

<details>
<summary>🤖 Prompt for AI Agents</summary>

```
In packages/bootstrap-vue-next/src/composables/useToggle/index.ts around lines
15 to 24, remove the default fallback object passed to the inject function so
that if the injection key is not provided, inject returns undefined. This change
will allow the existing check for !registry to correctly detect missing plugin
configuration and throw an error immediately, preventing silent failures in
show()/hide() calls.
```

</details>

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-05-22T12:57:43Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,70 @@
+import {
+  type ComponentInternalInstance,
+  computed,
+  getCurrentInstance,
+  inject,
+  type MaybeRefOrGetter,
+  ref,
+  toValue,
+} from 'vue'
+
+import {globalShowHideStorageInjectionKey} from '../../utils/keys'
+
+export const useToggle = (id: MaybeRefOrGetter<string | undefined> = undefined) => {
+  const instance = getCurrentInstance()
+  const {values: registry} = inject(globalShowHideStorageInjectionKey, {
+    register: () => ({unregister: () => {}}),
+    values: ref(new Map()),
+  })
+
+  if (!registry) {
+    throw new Error(
+      'useToggle() was called outside of the setup() function or the plugin is not provided.'
+    )
+  }
+  // const registry = toRef(() => appRegistry.value)
+  const findComponent = (
+    component: ComponentInternalInstance
+  ): ComponentInternalInstance | null => {
+    if (!component.parent) {
+      return null
+    }
+
+    if (
+      component.parent.exposed?.['id'] &&
+      registry.value.get(toValue(component.parent.exposed.id))
+    ) {
+      return component.parent
+    }
+
+    return findComponent(component.parent)
+  }
+
+  const myComponent = computed(() => {
+    const resolvedId = toValue(id)
+
+    if (!registry) return null
+    if (resolvedId) {
+      const value = registry.value.get(resolvedId)
+      return toValue(value) || null
+    }
+
+    if (!instance) {
+      return null
+    }
+
+    const component = findComponent(instance)
+    return toValue(registry.value.get(toValue(component?.exposed?.id))) || null
+  })
+
+  return {
+    value: computed(() => myComponent.value?.value),
+    show: (resolveOnHide?: boolean) =>
+      myComponent.value?.show(resolveOnHide) || Promise.reject('not found'),
+    hide: (trigger?: string) =>
+      myComponent.value?.hide(trigger, true) || Promise.reject('not found'),
+    toggle: (resolveOnHide?: boolean) =>
+      myComponent.value?.toggle(resolveOnHide) || Promise.reject('not found'),
+    component: computed(() => myComponent.value?.component),
+  }
path: packages/bootstrap-vue-next/src/composables/useToggle/index.ts
user: login: coderabbitai[bot]
body: _🛠️ Refactor suggestion_

**Return a proper `Error` instead of string rejection**

For consumers using `try/await`, rejecting with a primitive string complicates inspection and stack traces.

```diff
-      myComponent.value?.show(resolveOnHide) || Promise.reject('not found'),
+      myComponent.value?.show(resolveOnHide) ||
+      Promise.reject(new Error('useToggle: component not found')),
```

Same for `hide` and `toggle`.




<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

```suggestion
  return {
    value: computed(() => myComponent.value?.value),
    show: (resolveOnHide?: boolean) =>
      myComponent.value?.show(resolveOnHide) ||
      Promise.reject(new Error('useToggle: component not found')),
    hide: (trigger?: string) =>
      myComponent.value?.hide(trigger, true) ||
      Promise.reject(new Error('useToggle: component not found')),
    toggle: (resolveOnHide?: boolean) =>
      myComponent.value?.toggle(resolveOnHide) ||
      Promise.reject(new Error('useToggle: component not found')),
    component: computed(() => myComponent.value?.component),
  }
```

</details>

<!-- suggestion_end -->

<details>
<summary>🤖 Prompt for AI Agents</summary>

```
In packages/bootstrap-vue-next/src/composables/useToggle/index.ts around lines
60 to 69, the Promise rejections currently use primitive strings which hinder
error inspection and stack trace generation. Update the rejection values in the
show, hide, and toggle methods to reject with a new Error object instead of a
string. For example, replace Promise.reject('not found') with Promise.reject(new
Error('not found')) to provide proper error objects for better debugging.
```

</details>

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-05-22T12:57:43Z
author_association: CONTRIBUTOR
diff_hunk: @@ -13,7 +13,7 @@
       <div
         v-show="showRef && !hidden"
         :id="computedId"
-        v-bind="$attrs"
+        v-bind="attrs"
         ref="_element"
path: packages/bootstrap-vue-next/src/components/BPopover/BPopover.vue
user: login: coderabbitai[bot]
body: _⚠️ Potential issue_

**Potential duplicate `id` attribute when consumer passes one through `$attrs`**

`v-bind="attrs"` may contain an `id`, resulting in two `id` attributes (`computedId` + user-supplied) which is invalid HTML.

Consider filtering the id from `attrs` (and other structural attributes you already bind explicitly):

```ts
const attrs = useAttrs()
const filteredAttrs = computed(() => {
  const {id, ...rest} = attrs
  return rest
})
```
and then `v-bind="filteredAttrs"`.

<details>
<summary>🤖 Prompt for AI Agents</summary>

```
In packages/bootstrap-vue-next/src/components/BPopover/BPopover.vue around lines
16 to 17, the current code binds all attributes from `attrs` including a
potential user-supplied `id`, which causes duplicate `id` attributes when
combined with the component's own `computedId`. To fix this, create a computed
property that filters out the `id` from `attrs` and bind this filtered object
instead of the full `attrs`. This prevents duplicate `id` attributes and ensures
valid HTML.
```

</details>

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-05-22T12:57:43Z
author_association: CONTRIBUTOR
diff_hunk: @@ -15,85 +33,201 @@ export const toastPlugin: Plugin = {
 
     const _isAppend = ref(false)
 
-    const _setIsAppend = (value: boolean) => {
-      _isAppend.value = value
-    }
-
-    /**
-     * @returns {ControllerKey} If `id` is passed to props, it will use that id, otherwise, a symbol will be created that corresponds to its unique id.
-     * You can pass this id to the hide function to force a Toast to hide
-     */
-    const show = (obj: ToastOrchestratorShowParam = {}): ControllerKey => {
-      const resolvedProps = toRef(obj.props)
-
-      const _self = resolvedProps.value?.id || Symbol('Toast controller')
+    const buildPromise = (
+      _id: ControllerKey
+    ): {
+      value: PromiseWithToast
+      resolve: (value: BvTriggerableEvent) => void
+      stop?: WatchHandle
+    } => {
+      let resolveFunc: (value: BvTriggerableEvent) => void = () => {}
 
-      const toastToAdd: ToastOrchestratorArrayValue = {
-        component: !obj.component ? undefined : markRaw(obj.component),
-        props: {
-          ...resolvedProps.value,
-          pos: resolvedProps.value?.pos || posDefault,
-          _modelValue: resolvedProps.value?.value || 5000,
-          _self,
+      const promise = new Promise<BvTriggerableEvent>((resolve) => {
+        resolveFunc = resolve
+      }) as PromiseWithToast
+      Object.assign(promise, {
+        id: _id,
+        ref: null,
+        show() {
+          if (!this.ref) return this.set({modelValue: true})
+          this.ref.show()
+          return promise
         },
-      }
+        hide(trigger?: string) {
+          if (!this.ref) return this.set({modelValue: false})
+          this.ref.hide(trigger, true)
+          return promise
+        },
+        toggle() {
+          if (!this.ref) return this.set({modelValue: !this.get()?.modelValue})
+          this.ref.toggle()
+          return promise
+        },
+        get() {
+          return toasts.value.find((el) => el._self === _id)
+        },
+        set(val: Partial<ToastOrchestratorParam>) {
+          const toastIndex = toasts.value.findIndex((el) => el._self === _id)
+          const toast = toasts.value[toastIndex]
+          if (toast) {
+            const v = {...toValue(toast), ...toValue(val)}
+            if (toast.modelValue !== v.modelValue) {
+              toast['onUpdate:modelValue']?.(v.modelValue as boolean)
+            }
+            toasts.value.splice(toastIndex, 1, {
+              ...v,
+              title: toValue(v.title),
+              body: toValue(v.body),
+              modelValue: toValue(v.modelValue),
+            })
+          }
+          return promise
+        },
+        async destroy() {
+          const toast = toasts.value.find((el) => el._self === _id)
+          if (!toast) return
+          toast.promise.stop?.()
+          if (toast.modelValue) {
+            await new Promise((resolve) => {
+              toast.modelValue = false
+              const prev = toast['onHidden']
+              toast['onHidden'] = (e) => {
+                prev?.(e)
+                resolve(e)
+              }
+              nextTick(() => {
+                toast['onUpdate:modelValue']?.(false)
+              })
+            })
+          }
+          toasts.value.splice(
+            toasts.value.findIndex((el) => el._self === _id),
+            1
+          )
+        },
path: packages/bootstrap-vue-next/src/plugins/toastController/index.ts
user: login: coderabbitai[bot]
body: _🛠️ Refactor suggestion_

**`destroy()` can hang indefinitely if the `hidden` event is never emitted**  

`destroy()` awaits a promise that resolves only when the `onHidden` callback fires.  
If, for any reason, the toast is removed from the DOM before the transition completes, or the user overrides `onHidden` without calling the previous handler, `await` will never resolve, leaking the watcher and leaving the calling scope blocked.

Consider adding a defensive timeout (e.g. `setTimeout(resolve, X)`) or resolving immediately when the element is already gone to guarantee forward progress.

<details>
<summary>🤖 Prompt for AI Agents</summary>

```
In packages/bootstrap-vue-next/src/plugins/toastController/index.ts around lines
86 to 107, the destroy() method awaits a promise that only resolves when the
onHidden callback fires, which can cause it to hang indefinitely if the hidden
event never occurs. To fix this, add a defensive timeout that resolves the
promise after a reasonable delay, and also check if the toast element is already
removed before awaiting, resolving immediately in that case to ensure the method
always completes and does not block.
```

</details>

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-05-22T12:57:43Z
author_association: CONTRIBUTOR
diff_hunk: @@ -15,85 +33,201 @@ export const toastPlugin: Plugin = {
 
     const _isAppend = ref(false)
 
-    const _setIsAppend = (value: boolean) => {
-      _isAppend.value = value
-    }
-
-    /**
-     * @returns {ControllerKey} If `id` is passed to props, it will use that id, otherwise, a symbol will be created that corresponds to its unique id.
-     * You can pass this id to the hide function to force a Toast to hide
-     */
-    const show = (obj: ToastOrchestratorShowParam = {}): ControllerKey => {
-      const resolvedProps = toRef(obj.props)
-
-      const _self = resolvedProps.value?.id || Symbol('Toast controller')
+    const buildPromise = (
+      _id: ControllerKey
+    ): {
+      value: PromiseWithToast
+      resolve: (value: BvTriggerableEvent) => void
+      stop?: WatchHandle
+    } => {
+      let resolveFunc: (value: BvTriggerableEvent) => void = () => {}
 
path: packages/bootstrap-vue-next/src/plugins/toastController/index.ts
user: login: coderabbitai[bot]
body: _⚠️ Potential issue_

**`buildPromise()` returns a helper *wrapper*, but consumers never gain access to the `resolve` callback**  

`buildPromise()` puts the `resolve` function outside of the `PromiseWithToast` instance (`value`) and only exposes it on the wrapper it returns (`{value, resolve, stop}`).

Later (`create()`), you pass just `promise.value` back to the user (`return promise.value`).  
As a result:

1.  External code has no way to settle the promise (`await toast;` will never resolve).
2.  Internally, components such as the orchestrator must address the *wrapper* to resolve, which they do **via `toast.promise.resolve`** (see `destroy()`’s call path) – this breaks once the promise is extracted, producing an unresolved-promise leak.

A straightforward fix is to attach `resolve` directly onto the `PromiseWithToast` instance that is returned:

```diff
-Object.assign(promise, {
+Object.assign(promise, {
+  // expose resolve so orchestrators / external callers can settle the promise
+  resolve: (e: BvTriggerableEvent) => {
+    resolveFunc(e)
+    return promise
+  },
```

This keeps the public API self-contained while preserving existing internal uses (`toast.promise.resolve?()`).




> Committable suggestion skipped: line range outside the PR's diff.

<details>
<summary>🤖 Prompt for AI Agents</summary>

```
In packages/bootstrap-vue-next/src/plugins/toastController/index.ts around lines
36 to 44, the buildPromise function returns an object with the promise and a
separate resolve function, but only the promise is exposed to consumers,
preventing them from resolving it and causing unresolved promise leaks. To fix
this, attach the resolve function directly to the PromiseWithToast instance (the
promise object) so that both internal and external code can access and settle
the promise through the same object, maintaining the existing API and preventing
unresolved promises.
```

</details>

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-05-22T12:57:43Z
author_association: CONTRIBUTOR
diff_hunk: @@ -9,61 +10,116 @@ export default {
       sourcePath: '/BAlert/BAlert.vue',
       props: {
         '': {
-          closeVariant: {
-            type: 'ButtonVariant | null',
-            default: 'secondary',
-            description: 'Applies one of the Bootstrap button variants to the close button',
+          alertClass: {
+            type: 'ClassValue',
+            default: undefined,
+            description: 'CSS class (or classes) to add to the alert wrapper element',
+          },
+          bgVariant: {
+            type: 'ColorVariant | null',
+            default: null,
+          },
+          body: {
+            type: 'string',
+            default: undefined,
+            description: 'The text content of the body',
+          },
+          bodyClass: {
+            type: 'ClassValue',
+            default: undefined,
+            description: 'CSS class (or classes) to add to the alert body element',
           },
           closeClass: {
             type: 'ClassValue',
             default: undefined,
             description: 'Applies one or more custom classes to the close button',
           },
+          closeContent: {
+            type: 'string',
+            default: undefined,
+            description: 'Sets the text of the close button. The `close` slot takes precedence',
+          },
           closeLabel: {
             type: 'string',
             default: 'Close',
             description: 'Sets the aria-label attribute on the close button',
           },
-          closeContent: {
-            type: 'string',
-            default: undefined,
-            description: 'Sets the text of the close button. The `close` slot takes precedence',
+          closeVariant: {
+            type: 'string | null',
+            default: null,
+            description: 'Color variant for the close button',
           },
           dismissible: {
             type: 'boolean',
             default: false,
             description: 'When set, enables the close button',
           },
-          fade: {
+          headerClass: {
+            type: 'ClassValue',
+            default: undefined,
+            description: 'CSS class (or classes) to add to the alert header element',
+          },
+          headerTag: {
+            type: 'string',
+            default: 'div',
+            description: 'Specify the HTML tag to render instead of the default tag for the footer',
+          },
+          id: {
+            type: 'string',
+            default: undefined,
+            description:
+              'Used to set the `id` attribute on the rendered content, and used as the base to generate any additional element IDs as needed',
+          },
+          interval: {
+            type: 'number | requestAnimationFrame',
+            default: 'requestAnimationFrame',
+            description: 'The interval in milliseconds to update the countdown timer',
+          },
path: apps/docs/src/data/components/alert.data.ts
user: login: coderabbitai[bot]
body: _⚠️ Potential issue_

**`interval` literal type is mis-spelled / mis-quoted**  

You typed the union as `number | requestAnimationFrame`.  Because `requestAnimationFrame` is a **function**, TypeScript will treat this as `number | typeof global.requestAnimationFrame`, not the intended string literal `'requestAnimationFrame'`.

```diff
- type: 'number | requestAnimationFrame',
+ type: "number | 'requestAnimationFrame'",
```

Using the correct literal keeps the generated docs accurate and prevents type-mismatch warnings.




<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

```suggestion
          interval: {
-            type: 'number | requestAnimationFrame',
+            type: "number | 'requestAnimationFrame'",
            default: 'requestAnimationFrame',
            description: 'The interval in milliseconds to update the countdown timer',
          },
```

</details>

<!-- suggestion_end -->

<details>
<summary>🤖 Prompt for AI Agents</summary>

```
In apps/docs/src/data/components/alert.data.ts around lines 73 to 77, the type
for the interval property is incorrectly written as a union of number and the
function requestAnimationFrame instead of the string literal
'requestAnimationFrame'. To fix this, change the type annotation to use the
string literal 'requestAnimationFrame' in quotes, like 'number |
"requestAnimationFrame"', ensuring the union is between number and the string
literal, not the function itself.
```

</details>

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-05-22T12:57:43Z
author_association: CONTRIBUTOR
diff_hunk: @@ -1,28 +1,136 @@
-import {inject} from 'vue'
-import {useSharedModalStack} from '../useModalManager'
+import {
+  type ComponentInternalInstance,
+  computed,
+  getCurrentInstance,
+  inject,
+  ref,
+  toValue,
+} from 'vue'
 import {modalControllerPluginKey} from '../../utils/keys'
+import type {
+  ControllerKey,
+  PromiseWithModal,
+  PromiseWithModalBoolean,
+} from '../../types/ComponentOrchestratorTypes'
+import {useSharedModalStack} from '../../composables/useModalManager'
 
 export const useModalController = () => {
-  const {lastStack, stack} = useSharedModalStack()
-
-  const modalControllerPlugin = inject(modalControllerPluginKey)
+  const noopPromise = () => {
+    // eslint-disable-next-line no-console
+    console.warn(
+      'useModalController() was called outside of the setup() function! or the plugin is not provided.'
+    )
+    return Promise.resolve(null) as unknown as PromiseWithModal
+  }
+  const modalControllerPlugin = inject(modalControllerPluginKey, {
+    modals: ref(new Map()),
+    _isOrchestratorInstalled: ref(false),
+    create: noopPromise,
+    show: noopPromise as () => PromiseWithModalBoolean,
+    confirm: noopPromise as () => PromiseWithModalBoolean,
+  })
+  if (modalControllerPlugin.create === noopPromise) {
+    throw Error(
+      'useModalController() was called outside of the setup() function! or the plugin is not provided.'
+    )
+  }
 
-  const hide = (trigger = '') => {
-    if (lastStack?.value) {
-      lastStack?.value.exposed?.hide(trigger)
+  const {lastStack, stack, registry} = useSharedModalStack()
+  const hide = (trigger?: string, id?: ControllerKey) => {
+    if (id === undefined) {
+      if (id === undefined && lastStack?.value) {
+        lastStack?.value.exposed?.hide(trigger)
+      }
+    } else {
+      const stackModal = stack?.value.find((modal) => modal.exposed?.id === id)
+      if (stackModal) {
+        stackModal.exposed?.hide(trigger)
+        return
+      }
+      const modal = modalControllerPlugin.modals.value.get(id)
+      if (modal) {
+        modal.modelValue = false
+        modal['onUpdate:modelValue']?.(false)
+      } else {
+        stack?.value.forEach((modal) => {
+          if (modal.exposed?.id === id) {
path: packages/bootstrap-vue-next/src/composables/useModalController/index.ts
user: login: coderabbitai[bot]
body: _🛠️ Refactor suggestion_

**`onUpdate:modelValue` may be undefined or not a function**

`modal['onUpdate:modelValue']?.(false)` is invoked without a type-guard that ensures it is actually a function.  
If a consumer passes something unexpected (e.g. a boolean via v-model modifiers) `TypeError: … is not a function` will be thrown.

```diff
-        modal['onUpdate:modelValue']?.(false)
+        const update = modal['onUpdate:modelValue']
+        if (typeof update === 'function') {
+          update(false)
+        }
```



<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

```suggestion
      const modal = modalControllerPlugin.modals.value.get(id)
      if (modal) {
        modal.modelValue = false
        const update = modal['onUpdate:modelValue']
        if (typeof update === 'function') {
          update(false)
        }
      } else {
        stack?.value.forEach((modal) => {
          if (modal.exposed?.id === id) {
```

</details>

<!-- suggestion_end -->

<details>
<summary>🤖 Prompt for AI Agents</summary>

```
In packages/bootstrap-vue-next/src/composables/useModalController/index.ts
between lines 50 and 56, the code calls modal['onUpdate:modelValue'] without
verifying it is a function, which can cause a TypeError if it is undefined or
not a function. Add a type guard to check that modal['onUpdate:modelValue'] is a
function before invoking it with false to prevent runtime errors.
```

</details>

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-05-22T12:57:44Z
author_association: CONTRIBUTOR
diff_hunk: @@ -3,29 +3,33 @@ import {afterEach, describe, expect, it} from 'vitest'
 import BAlert from './BAlert.vue'
 import BCloseButton from '../BButton/BCloseButton.vue'
 import BButton from '../BButton/BButton.vue'
-import {Transition as BTransition} from 'vue'
+import {Transition} from 'vue'
 
 describe('alert', () => {
   enableAutoUnmount(afterEach)
 
-  it('has BTransition', () => {
+  it('has Transition', () => {
     const wrapper = mount(BAlert)
-    const $transition = wrapper.findComponent(BTransition)
+    const $transition = wrapper.findComponent(Transition)
     expect($transition.exists()).toBe(true)
   })
 
-  it('there is not a nested div by default', () => {
+  it('there is a hidden div by default', () => {
     const wrapper = mount(BAlert)
     const $div = wrapper.find('div')
-    expect($div.exists()).toBe(false)
+    expect($div.exists()).toBe(true)
+    expect($div.attributes().style).toContain('display: none;')
   })
 
-  it('there is a nested div when modelValue true', () => {
+  it('there is a div when modelValue true', async () => {
     const wrapper = mount(BAlert, {
       props: {modelValue: true},
     })
+    await wrapper.vm.$nextTick()
+    // await new Promise((resolve) => setTimeout(resolve, 3000))
     const $div = wrapper.find('div')
     expect($div.exists()).toBe(true)
+    expect($div.attributes().style).toBeUndefined()
   })
path: packages/bootstrap-vue-next/src/components/BAlert/alert.spec.ts
user: login: coderabbitai[bot]
body: _🛠️ Refactor suggestion_

**Stray commented code & hard-coded delay**

The commented three-second `setTimeout` and the current `await
new Promise(setTimeout…)` blocks introduce noise and may cause flakiness
in CI.

Prefer the utilities that the test runner already provides
(`nextTick`, `flushPromises`, `vi.useFakeTimers()` etc.).

```diff
-    await wrapper.vm.$nextTick()
-    // await new Promise((resolve) => setTimeout(resolve, 3000))
+    await wrapper.vm.$nextTick()
```

The same applies to all `await new Promise((resolve) => setTimeout(resolve, 30))`
chunks further down – replace them with `await nextTick()` or
`await flushPromises()`.




<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

```suggestion
    await wrapper.vm.$nextTick()
    const $div = wrapper.find('div')
    expect($div.exists()).toBe(true)
    expect($div.attributes().style).toBeUndefined()
  })
```

</details>

<!-- suggestion_end -->

<details>
<summary>🤖 Prompt for AI Agents</summary>

```
In packages/bootstrap-vue-next/src/components/BAlert/alert.spec.ts around lines
28 to 33, remove the commented-out three-second setTimeout and any hard-coded
delay using new Promise with setTimeout. Replace these with appropriate test
runner utilities like await wrapper.vm.$nextTick() or await flushPromises() to
avoid test flakiness and reduce noise. Apply this replacement consistently to
all similar delay patterns in the file.
```

</details>

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-05-22T12:57:44Z
author_association: CONTRIBUTOR
diff_hunk: @@ -199,9 +202,11 @@ const route = useRoute()
 const content = useTemplateRef<ComponentPublicInstance<HTMLElement>>('_content')
 const target = useTemplateRef<ComponentPublicInstance<HTMLElement>>('_target')
 
-useScrollspy(content, target, {
-  contentQuery: ':scope > div > [id], #component-reference',
+const {current: activeId, list: items} = useScrollspy(content, target, {
+  contentQuery: ':scope > div > [id], #component-reference, .component-reference h3',
   targetQuery: ':scope [href]',
+  rootMargin: '0px 0px -25%',
+  manual: true,
 })
path: apps/docs/.vitepress/theme/Layout.vue
user: login: coderabbitai[bot]
body: _⚠️ Potential issue_

**`manual: true` without calling `.observe()`**

`useScrollspy` returns controls (`observe`, `disconnect`, …) that must be
invoked when `manual: true` is passed.  
At the moment nothing starts observation, so `activeId` will stay
`undefined`.

Either remove the `manual` flag or add:

```ts
onMounted(() => items.observe())
onBeforeUnmount(() => items.disconnect())
```

<details>
<summary>🤖 Prompt for AI Agents</summary>

```
In apps/docs/.vitepress/theme/Layout.vue around lines 205 to 210, the
useScrollspy hook is called with manual: true but the observe method is never
invoked, so activeId remains undefined. To fix this, add lifecycle hooks to call
items.observe() onMounted and items.disconnect() onBeforeUnmount, or
alternatively remove the manual: true flag to let useScrollspy handle
observation automatically.
```

</details>

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-05-22T12:57:44Z
author_association: CONTRIBUTOR
diff_hunk: @@ -293,6 +298,49 @@ const set = (newValue: keyof typeof map) => {
   colorMode.value = newValue
 }
 
+const headers = computed(() =>
+  items.value.map((item) => {
+    const rawTag = item.el?.tagName?.toUpperCase() ?? ''
+    const isHeading = /^H[1-6]$/.test(rawTag)
+    const tag = isHeading ? rawTag : 'DIV'
+    const level = tag.startsWith('H') ? parseInt(tag.replace('H', '')) : 3
+    return {
+      ...item,
+      tag,
+      level,
+    } as HeaderItem
+  })
+)
+
+const contents = computed(() => {
+  const root: ContentsItem[] = []
+  const stack: ContentsItem[] = []
+
+  headers.value.forEach((header) => {
+    const item = {...header, children: [] as ContentsItem[]} as ContentsItem
+
+    while (stack.length && stack[stack.length - 1].level >= item.level) {
+      stack.pop()
+    }
+
+    if (stack.length === 0) {
+      root.push(item)
+    } else {
+      stack[stack.length - 1].children.push(item)
+    }
+
+    stack.push(item)
+  })
+
+  if (root.length !== 1) {
+    // Something isn't right if we have no root items or more than one root item
+    // eslint-disable-next-line no-console
+    console.warn('Unexpected header structure:', headers, 'Root items:', root)
+  }
+
+  return root.length > 0 ? root[0] : undefined
+})
path: apps/docs/.vitepress/theme/Layout.vue
user: login: coderabbitai[bot]
body: _🛠️ Refactor suggestion_

**`contents` builder drops additional root headings**

Only the first root‐level heading is returned; the rest are silently
discarded, which breaks pages that legitimately have several `<h2>` root
sections.

```diff
-  return root.length > 0 ? root[0] : undefined
+  return root
```

And update `PageContents` to accept an array.




<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

```suggestion
const contents = computed(() => {
  const root: ContentsItem[] = []
  const stack: ContentsItem[] = []

  headers.value.forEach((header) => {
    const item = { ...header, children: [] as ContentsItem[] } as ContentsItem

    while (stack.length && stack[stack.length - 1].level >= item.level) {
      stack.pop()
    }

    if (stack.length === 0) {
      root.push(item)
    } else {
      stack[stack.length - 1].children.push(item)
    }

    stack.push(item)
  })

  if (root.length !== 1) {
    // Something isn't right if we have no root items or more than one root item
    // eslint-disable-next-line no-console
    console.warn('Unexpected header structure:', headers, 'Root items:', root)
  }

-  return root.length > 0 ? root[0] : undefined
+  return root
})
```

</details>

<!-- suggestion_end -->

<details>
<summary>🤖 Prompt for AI Agents</summary>

```
In apps/docs/.vitepress/theme/Layout.vue between lines 301 and 342, the contents
computed property currently returns only the first root-level heading,
discarding any additional root headings. To fix this, modify the return
statement to return the entire root array instead of just root[0], so all
root-level headings are preserved. Additionally, update the PageContents
component to accept and handle an array of root headings rather than a single
item.
```

</details>

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-05-22T12:57:44Z
author_association: CONTRIBUTOR
diff_hunk: @@ -1,134 +1,212 @@
-import {isRef, onScopeDispose, type Plugin, ref, toRef, toValue, watch} from 'vue'
+import {
+  isReadonly,
+  isRef,
+  markRaw,
+  nextTick,
+  onScopeDispose,
+  type Plugin,
+  ref,
+  type Ref,
+  toRef,
+  toValue,
+  watch,
+  type WatchHandle,
+} from 'vue'
 import {popoverPluginKey} from '../../utils/keys'
 import type {
   ControllerKey,
+  PopoverOrchestratorCreateParam,
+  PopoverOrchestratorMapValue,
   PopoverOrchestratorParam,
-  PopoverOrchestratorShowParam,
+  PromiseWithPopover,
+  PromiseWithPopoverInternal,
+  TooltipOrchestratorCreateParam,
+  TooltipOrchestratorMapValue,
   TooltipOrchestratorParam,
-  TooltipOrchestratorShowParam,
 } from '../../types/ComponentOrchestratorTypes'
+import type {BvTriggerableEvent} from '../../utils'
 
 export const popoverPlugin: Plugin = {
   install(app) {
-    const popovers = ref(new Map<ControllerKey, PopoverOrchestratorParam>())
-    /**
-     * @returns {ControllerKey} If `id` is passed to props, it will use that id, otherwise,
-     * a symbol will be created that corresponds to its unique id.
-     */
-    const popover = (obj: PopoverOrchestratorShowParam): ControllerKey => {
-      const resolvedProps = toRef(obj)
-      const _self = resolvedProps.value?.id || Symbol('Popover controller')
+    const _isOrchestratorInstalled = ref(false)
+    const popovers = ref(
+      new Map<ControllerKey, PopoverOrchestratorMapValue | TooltipOrchestratorMapValue>()
+    )
 
-      watch(
-        resolvedProps,
-        (newValue) => {
-          // eslint-disable-next-line @typescript-eslint/ban-ts-comment
-          // @ts-ignore: How to add refs for title & content?
-          popovers.value.set(_self, {
-            ...newValue,
-            ...(typeof newValue['modelValue'] !== 'undefined' && isRef(obj)
-              ? {
-                  'onUpdate:modelValue': (val: boolean) => {
-                    // eslint-disable-next-line @typescript-eslint/ban-ts-comment
-                    // @ts-ignore: How to add emit types?
-                    newValue['onUpdate:modelValue']?.(val)
-                    obj.value.modelValue = val
-                  },
-                }
-              : {}),
-          })
+    const buildPromise = (
+      _id: ControllerKey,
+      store: Ref<Map<ControllerKey, PopoverOrchestratorMapValue | TooltipOrchestratorMapValue>>
+    ): {
+      value: PromiseWithPopover
+      resolve: (value: BvTriggerableEvent) => void
+      stop?: WatchHandle
+    } => {
+      let resolveFunc: (value: BvTriggerableEvent) => void = () => {}
+
+      const promise = new Promise<BvTriggerableEvent>((resolve) => {
+        resolveFunc = resolve
+      }) as PromiseWithPopover
+      Object.assign(promise, {
+        id: _id,
+        ref: null,
+        show() {
+          if (!this.ref) return this.set({modelValue: true})
+          this.ref.show()
+          return promise
+        },
+        hide(trigger?: string) {
+          if (!this.ref) return this.set({modelValue: false})
+          this.ref.hide(trigger, true)
+          return promise
+        },
+        toggle() {
+          if (!this.ref) return this.set({modelValue: !this.get()?.modelValue})
+          this.ref.toggle()
+          return promise
+        },
+        get() {
+          return store.value.get(_id)
+        },
+        set(val: Partial<PopoverOrchestratorParam | TooltipOrchestratorParam>) {
+          const item = store.value.get(_id)
+          if (item) {
+            const v = {...toValue(item), ...toValue(val)}
+            // add modal to v
+            if (item.modelValue !== v.modelValue) {
+              item['onUpdate:modelValue']?.(v.modelValue as boolean)
+            }
+            store.value.set(_id, {
+              ...v,
+              title: toValue(v.title),
+              body: toValue(v.body),
+              modelValue: toValue(v.modelValue),
+            })
+          }
+          return promise
+        },
+        async destroy() {
+          const item = store.value.get(_id)
+          if (!item) return
+          item.promise.stop?.()
+          if (item.modelValue) {
+            await new Promise((resolve) => {
+              item.modelValue = false
+              const prev = item['onHidden']
+              item['onHidden'] = (e) => {
+                prev?.(e)
+                resolve(e)
+              }
+              nextTick(() => {
+                item['onUpdate:modelValue']?.(false)
+              })
+            })
+          }
+          store.value.delete(_id)
         },
-        {
-          immediate: true,
-          deep: true,
-        }
-      )
-      onScopeDispose(() => popovers.value.delete(_self), true)
 
-      return _self
-    }
-    /**
-     * @param {ControllerKey} self You can get the symbol param from the return value from the show method, or use props.id
-     */
-    const setPopover = (self: ControllerKey, val: Partial<PopoverOrchestratorParam>) => {
-      const popover = popovers.value.get(self)
-      if (!popover) return
-      // eslint-disable-next-line @typescript-eslint/ban-ts-comment
-      // @ts-ignore: How to add refs for title & content?
-      popovers.value.set(self, {
-        ...popover,
-        ...toValue(val),
-      })
+        async [Symbol.asyncDispose]() {
+          await this.destroy()
+        },
+      } as PromiseWithPopoverInternal)
+
+      return {
+        value: promise,
+        resolve: resolveFunc,
+      }
     }
-    /**
-     * @param {ControllerKey} self You can get the symbol param from the return value from the show method, or use props.id
-     */
-    const removePopover = (self: ControllerKey) => popovers.value.delete(self)
 
-    const tooltips = ref(new Map<ControllerKey, TooltipOrchestratorParam>())
     /**
-     * @returns {ControllerKey} If `id` is passed to props, it will use that id, otherwise,
-     * a symbol will be created that corresponds to its unique id.
+     * Create a popover or tooltip
+     * @param obj The popover or tooltip props
+     * @param tooltip If true, create a tooltip, otherwise create a popover
+     * @returns {PromiseWithPopover} A promise object with methods to control the popover (show, hide, toggle, get, set, destroy)
      */
-    const tooltip = (obj: TooltipOrchestratorShowParam): ControllerKey => {
+    const create = (obj: PopoverOrchestratorCreateParam, tooltip?: boolean): PromiseWithPopover => {
+      if (!_isOrchestratorInstalled.value) {
+        throw new Error(
+          'The BPopoverOrchestrator component must be mounted to use the popover controller'
+        )
+      }
+      const {component, slots} = toValue(obj)
+      if (component) {
+        if (isRef(obj)) obj.value.component = markRaw(component)
+        else if (typeof obj === 'object') obj.component = markRaw(component)
+      }
+      if (slots) {
+        if (isRef(obj)) obj.value.slots = markRaw(slots)
+        else if (typeof obj === 'object') obj.slots = markRaw(slots)
+      }
       const resolvedProps = toRef(obj)
-      const _self = resolvedProps.value?.id || Symbol('Tooltip controller')
+      const _self = resolvedProps.value?.id || Symbol('Popover controller')
 
-      watch(
+      const promise = buildPromise(_self, popovers)
+
+      promise.stop = watch(
         resolvedProps,
-        (newValue) => {
-          // eslint-disable-next-line @typescript-eslint/ban-ts-comment
-          // @ts-ignore: How to add refs for title & content?
+        (_newValue) => {
+          const newValue = {...toValue(_newValue)}
+          const previous = popovers.value.get(_self)
+          // if (!previous) return
+          const v: Partial<PopoverOrchestratorMapValue> = {
+            ...(previous || {}),
+          }
+
+          for (const key in newValue) {
+            if (key.startsWith('on')) {
+              v[key as keyof PopoverOrchestratorCreateParam] =
+                newValue[key as keyof PopoverOrchestratorCreateParam]
+            } else {
+              v[key as keyof PopoverOrchestratorCreateParam] = toValue(
+                newValue[key as keyof PopoverOrchestratorCreateParam]
+              )
+            }
+          }
           popovers.value.set(_self, {
-            ...newValue,
-            ...(typeof newValue['modelValue'] !== 'undefined' && isRef(obj)
-              ? {
-                  'onUpdate:modelValue': (val: boolean) => {
-                    // eslint-disable-next-line @typescript-eslint/ban-ts-comment
-                    // @ts-ignore: How to add emit types?
-                    newValue['onUpdate:modelValue']?.(val)
-                    obj.value.modelValue = val
-                  },
+            ...v,
+            ...(v.modelValue === undefined && {modelValue: false}),
+            'onUpdate:modelValue': (val: boolean) => {
+              newValue['onUpdate:modelValue']?.(val)
+              const {modelValue} = toValue(obj)
+              if (isRef(obj) && !isRef(modelValue)) obj.value.modelValue = val
+              if (isRef(modelValue) && !isReadonly(modelValue)) {
+                ;(modelValue as Ref<PopoverOrchestratorParam['modelValue']>).value = val
+              }
+              if (v.modelValue !== val) {
+                const popover = popovers.value.get(_self)
+                if (popover) {
+                  popover.modelValue = val
                 }
-              : {}),
+              }
+            },
+            tooltip,
+            promise,
           })
         },
         {
           immediate: true,
           deep: true,
         }
       )
-      onScopeDispose(() => tooltips.value.delete(_self), true)
 
-      return _self
-    }
-    /**
-     * @param {ControllerKey} self You can get the symbol param from the return value from the show method, or use props.id
-     */
-    const setTooltip = (self: ControllerKey, val: Partial<TooltipOrchestratorParam>) => {
-      const tooltip = tooltips.value.get(self)
-      if (!tooltip) return
-      // eslint-disable-next-line @typescript-eslint/ban-ts-comment
-      // @ts-ignore: How to add refs for title & content?
-      tooltips.value.set(self, {
-        ...tooltip,
-        ...toValue(val),
-      })
+      onScopeDispose(() => {
+        const popover = popovers.value.get(_self)
+        if (popover) {
+          popover.promise.value.destroy?.()
+        }
+      }, true)
path: packages/bootstrap-vue-next/src/plugins/popoverController/index.ts
user: login: coderabbitai[bot]
body: _⚠️ Potential issue_

**Same compile-time issue: extra argument supplied to `onScopeDispose`**

Just like in the modal controller, `onScopeDispose` is invoked with two arguments, breaking type-checking and preventing disposal registration.

```diff
-      onScopeDispose(() => {
-        const popover = popovers.value.get(_self)
-        if (popover) {
-          popover.promise.value.destroy?.()
-        }
-      }, true)
+      onScopeDispose(() => {
+        const popover = popovers.value.get(_self)
+        if (popover) {
+          popover.promise.value.destroy?.()
+        }
+      })
```



<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

```suggestion
      onScopeDispose(() => {
        const popover = popovers.value.get(_self)
        if (popover) {
          popover.promise.value.destroy?.()
        }
      })
```

</details>

<!-- suggestion_end -->

<details>
<summary>🤖 Prompt for AI Agents</summary>

```
In packages/bootstrap-vue-next/src/plugins/popoverController/index.ts around
lines 191 to 196, the onScopeDispose function is called with two arguments, but
it only accepts one, causing a compile-time error. Remove the second argument
(true) from the onScopeDispose call to fix the type-checking issue and ensure
proper disposal registration.
```

</details>

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-05-22T12:57:44Z
author_association: CONTRIBUTOR
diff_hunk: @@ -184,9 +175,17 @@ export const useShowHide = (
 
   let showTimeout: ReturnType<typeof setTimeout> | undefined
   let hideTimeout: ReturnType<typeof setTimeout> | undefined
+  let _Resolve: ((value: boolean | string) => void) | undefined
+  let _Promise: Promise<boolean | string> | undefined
+  let _resolveOnHide: boolean | undefined
+  const show = (resolveOnHide: boolean = false): Promise<boolean | string> => {
+    if (showRef.value && !hideTimeout && !_Promise) return Promise.resolve(true)
+    _resolveOnHide = resolveOnHide
+    if (showRef.value && !hideTimeout && _Promise) return _Promise
 
-  const show = () => {
-    if (showRef.value && !hideTimeout) return
+    _Promise = new Promise<boolean | string>((resolve) => {
+      _Resolve = resolve
+    })
 
     const event = buildTriggerableEvent('show', {cancelable: true})
path: packages/bootstrap-vue-next/src/composables/useShowHide.ts
user: login: coderabbitai[bot]
body: _🛠️ Refactor suggestion_

_⚠️ Potential issue_

**Potential promise-leak & stale resolver**

`_Promise`/`_Resolve` are created on every `show()` invocation but are only nulled in `onAfterEnter` *if* `!_resolveOnHide`.  
When `resolveOnHide === true` ( i.e. `toggle(true)` ) the resolver is never reset, leaving the stale pair in memory until the next *successful* show, causing:

* memory retention of the previous resolver
* subsequent `show()` calls to return the old promise before a new cycle starts

```diff
-    if (!_resolveOnHide) {
-      _Resolve?.(true)
-      _Promise = undefined
-      _Resolve = undefined
-    }
+    const shouldResolve = !_resolveOnHide
+    _resolveOnHide = false          // reset for the next cycle
+    if (shouldResolve) {
+      _Resolve?.(true)
+      _Promise = undefined
+      _Resolve = undefined
+    }
```
Without resetting `_resolveOnHide` the branch above will never execute again after the first `toggle(true)` call.  
Please ensure the flag and the promise pair are always cleared.  
 


> Committable suggestion skipped: line range outside the PR's diff.

<details>
<summary>🤖 Prompt for AI Agents</summary>

```
In packages/bootstrap-vue-next/src/composables/useShowHide.ts around lines 176
to 190, the _Promise and _Resolve variables are created on every show() call but
only cleared conditionally, causing stale promises and memory leaks when
resolveOnHide is true. To fix this, ensure that both _Promise and _Resolve, as
well as the _resolveOnHide flag, are reset to undefined whenever the promise is
resolved or rejected, regardless of the resolveOnHide value, so no stale
references remain and subsequent show() calls create fresh promises.
```

</details>

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-05-22T12:57:44Z
author_association: CONTRIBUTOR
diff_hunk: @@ -1,149 +1,280 @@
 <template>
-  <Transition v-bind="fadeTransitions" enter-to-class="show">
+  <Transition
+    v-if="renderRef || contentShowing"
+    v-bind="transitionProps"
+    :appear="!!modelValue || props.visible"
+  >
     <div
-      v-if="isAlertVisible"
+      v-show="isAlertVisible"
+      :id="props.id"
       ref="_element"
       class="alert"
-      role="alert"
-      aria-live="polite"
-      aria-atomic="true"
-      :class="computedClasses"
+      :class="[props.alertClass, computedClasses]"
+      tabindex="0"
+      :role="!isAlertVisible ? undefined : props.isStatus ? 'status' : 'alert'"
+      :aria-live="!isAlertVisible ? undefined : props.isStatus ? 'polite' : 'assertive'"
+      :aria-atomic="!isAlertVisible ? undefined : true"
     >
-      <slot />
-      <template v-if="props.dismissible">
-        <BButton v-if="hasCloseSlot || props.closeContent" v-bind="closeAttrs" @click="hide">
-          <slot name="close">
-            {{ props.closeContent }}
-          </slot>
-        </BButton>
-        <BCloseButton v-else :aria-label="props.closeLabel" v-bind="closeAttrs" @click="hide" />
+      <component
+        :is="props.headerTag"
+        v-if="contentShowing && (slots.title || props.title)"
+        class="alert-heading d-flex gap-2"
+        :class="props.headerClass"
+      >
+        <slot name="title" v-bind="sharedSlots">
+          <span>
+            {{ props.title }}
+          </span>
+        </slot>
+        <template v-if="props.dismissible">
+          <BButton
+            v-if="slots.close || props.closeContent"
+            :class="[props.closeClass]"
+            class="ms-auto ps-1 btn-close-custom"
+            :variant="props.closeVariant"
+            @click.stop.prevent="hide('close')"
+          >
+            <slot name="close" v-bind="sharedSlots">
+              {{ props.closeContent }}
+            </slot>
+          </BButton>
+          <BCloseButton
+            v-else
+            :aria-label="props.closeLabel"
+            :class="[props.closeClass]"
+            @click.stop.prevent="hide('close')"
+          />
+        </template>
+      </component>
+      <template v-if="contentShowing && (slots.default || props.body)">
+        <div class="d-flex gap-2">
+          <component
+            :is="computedTag"
+            class="alert-body"
+            :class="props.bodyClass"
+            v-bind="computedLinkProps"
+            @click="computedLink && props.dismissible ? hide() : () => {}"
+          >
+            <slot v-bind="sharedSlots">
+              {{ props.body }}
+            </slot>
+          </component>
+
+          <template v-if="props.dismissible && !(slots.title || props.title)">
+            <BButton
+              v-if="slots.close || props.closeContent"
+              :class="[props.closeClass]"
+              class="ms-auto btn-close-custom"
+              :variant="props.closeVariant"
+              @click.stop.prevent="hide('close')"
+            >
+              <slot name="close" v-bind="sharedSlots">
+                {{ props.closeContent }}
+              </slot>
+            </BButton>
+            <BCloseButton
+              v-else
+              :aria-label="props.closeLabel"
+              :class="[props.closeClass]"
+              @click.stop.prevent="hide('close')"
+            />
+          </template>
+        </div>
       </template>
+      <BProgress
+        v-if="typeof modelValue === 'number' && props.progressProps !== undefined"
+        :animated="props.progressProps.animated"
+        :precision="props.progressProps.precision"
+        :show-progress="props.progressProps.showProgress"
+        :show-value="props.progressProps.showValue"
+        :striped="props.progressProps.striped"
+        :variant="props.progressProps.variant"
+        :max="modelValue"
+        :value="remainingMs"
+        height="4px"
+      />
     </div>
   </Transition>
 </template>
 
 <script setup lang="ts">
-import BCloseButton from '../BButton/BCloseButton.vue'
-import BButton from '../BButton/BButton.vue'
+import {computed, type EmitFn, useTemplateRef, watch, watchEffect} from 'vue'
+import {useBLinkHelper} from '../../composables/useBLinkHelper'
 import type {BAlertProps} from '../../types/ComponentProps'
-import {computed, useTemplateRef, watchEffect} from 'vue'
+import type {BToastEmits} from '../../types/ComponentEmits'
+import type {BToastSlots, ShowHideSlotsData} from '../../types/ComponentSlots'
+import BCloseButton from '../BButton/BCloseButton.vue'
+import BLink from '../BLink/BLink.vue'
+import BProgress from '../BProgress/BProgress.vue'
 import {useCountdown} from '../../composables/useCountdown'
 import {useDefaults} from '../../composables/useDefaults'
-import {isEmptySlot} from '../../utils/dom'
 import {useCountdownHover} from '../../composables/useCountdownHover'
-import {useFadeTransition} from '../../composables/useTransitions'
+import {useId} from '../../composables/useId'
+import {useShowHide} from '../../composables/useShowHide'
 
 const _props = withDefaults(defineProps<Omit<BAlertProps, 'modelValue'>>(), {
+  bgVariant: null,
+  body: undefined,
+  bodyClass: undefined,
   closeClass: undefined,
   closeContent: undefined,
   closeLabel: 'Close',
   closeVariant: 'secondary',
   dismissible: false,
-  fade: false,
-  immediate: true,
+  headerClass: undefined,
+  headerTag: 'div',
+  id: undefined,
+  initialAnimation: false,
   interval: 'requestAnimationFrame',
+  isStatus: false,
+  lazy: false,
+  noFade: false,
   noHoverPause: false,
   noResumeOnHoverLeave: false,
+  progressProps: undefined,
+  unmountLazy: false,
   showOnPause: true,
+  show: false,
+  solid: false,
+  textVariant: null,
+  title: undefined,
+  alertClass: undefined,
+  transProps: undefined,
+  visible: false,
+  // Link props
+  // All others use defaults
+  noRel: undefined,
+  active: undefined,
+  activeClass: undefined,
+  disabled: undefined,
+  exactActiveClass: undefined,
+  href: undefined,
+  icon: undefined,
+  opacity: undefined,
+  opacityHover: undefined,
+  stretched: false,
+  rel: undefined,
+  replace: undefined,
+  routerComponentName: undefined,
+  target: undefined,
+  to: undefined,
+  underlineOffset: undefined,
+  underlineOffsetHover: undefined,
+  underlineOpacity: undefined,
+  underlineOpacityHover: undefined,
+  underlineVariant: undefined,
+  // End link props
   variant: 'info',
 })
-
 const props = useDefaults(_props, 'BAlert')
 
-const emit = defineEmits<{
-  'close': []
-  'close-countdown': [value: number]
-  'closed': []
-}>()
+const emit = defineEmits<BToastEmits>()
 
-const slots = defineSlots<{
-  // eslint-disable-next-line @typescript-eslint/no-explicit-any
-  close?: (props: Record<string, never>) => any
-  // eslint-disable-next-line @typescript-eslint/no-explicit-any
-  default?: (props: Record<string, never>) => any
-}>()
-
-const fadeTransitions = useFadeTransition(() => props.fade)
+const slots = defineSlots<BToastSlots>()
 
 const element = useTemplateRef<HTMLElement>('_element')
 
 const modelValue = defineModel<Exclude<BAlertProps['modelValue'], undefined>>({default: false})
+const {computedLink, computedLinkProps} = useBLinkHelper(props)
+
+const computedId = useId(() => props.id, 'alert')
+
+const {
+  showRef,
+  renderRef,
+  hide,
+  toggle,
+  show,
+  buildTriggerableEvent,
+  computedNoAnimation,
+  isVisible,
+  transitionProps,
+  contentShowing,
+} = useShowHide(modelValue, props, emit as EmitFn, element, computedId)
 
-const hasCloseSlot = computed(() => !isEmptySlot(slots.close))
+// TODO solid is never used
 const countdownLength = computed(() =>
   typeof modelValue.value === 'boolean' ? 0 : modelValue.value
 )
 
-const computedClasses = computed(() => ({
-  [`alert-${props.variant}`]: props.variant !== null,
-  'alert-dismissible': props.dismissible,
-}))
-
-const closeClasses = computed(() => [props.closeClass, {'btn-close-custom': hasCloseSlot.value}])
-
 const {
   isActive,
   pause,
+  restart,
   resume,
   stop,
   isPaused,
-  restart,
   value: remainingMs,
 } = useCountdown(countdownLength, props.interval, {
-  immediate: typeof modelValue.value === 'number' && props.immediate,
+  immediate: typeof modelValue.value === 'number' && !!modelValue.value,
 })
 useCountdownHover(
   element,
   computed(() => ({
-    noHoverPause: props.noHoverPause,
-    noResumeOnHoverLeave: props.noResumeOnHoverLeave,
+    noHoverPause: props.noHoverPause || typeof modelValue.value !== 'number',
+    noResumeOnHoverLeave: props.noResumeOnHoverLeave || typeof modelValue.value !== 'number',
     modelValueIgnoresHover: typeof modelValue.value === 'boolean',
   })),
   {pause, resume}
 )
 
-const isAlertVisible = computed(() =>
-  typeof modelValue.value === 'boolean'
-    ? modelValue.value
-    : isActive.value || (props.showOnPause && isPaused.value)
-)
-
-const closeAttrs = computed(() => ({
-  variant: hasCloseSlot.value ? props.closeVariant : undefined,
-  class: closeClasses.value,
-}))
-
 watchEffect(() => {
   emit('close-countdown', remainingMs.value)
 })
 
-const hide = () => {
-  emit('close')
+const computedTag = computed(() => (computedLink.value ? BLink : 'div'))
+
+const isAlertVisible = computed(
+  () => showRef.value || isActive.value || (props.showOnPause && isPaused.value)
+)
+
+// const colorClasses = useColorVariantClasses(props)
+const computedClasses = computed(() => [
+  // colorClasses.value,
+  {
+    [`alert-${props.variant}`]: props.variant !== null,
+    'alert-dismissible': props.dismissible && !(slots.close || props.closeContent),
+    'show': isVisible.value,
+    'fade': !computedNoAnimation.value,
+  },
+])
+
+watch(modelValue, (newValue) => {
+  if (typeof newValue === 'number') {
+    const event = buildTriggerableEvent('show', {cancelable: true, trigger: 'model'})
+    emit('show', event)
+    if (event.defaultPrevented) {
+      emit('show-prevented', buildTriggerableEvent('show-prevented'))
+    } else {
+      restart()
+    }
+  }
+})
path: packages/bootstrap-vue-next/src/components/BAlert/BAlert.vue
user: login: coderabbitai[bot]
body: _⚠️ Potential issue_

**Duplicate “show” event emission**

The watcher on `modelValue` emits its own `show` / `show-prevented` events, *in addition* to the ones already dispatched by `useShowHide`.  
This results in consumers receiving the same semantic event twice.

Unless a strong reason exists, remove the extra emission or rename it to avoid confusion.

```diff
-watch(modelValue, (newValue) => {
-  ...
-  emit('show', event)
-  ...
-})
+// Duplicate with useShowHide – consider removing
```



> Committable suggestion skipped: line range outside the PR's diff.

<details>
<summary>🤖 Prompt for AI Agents</summary>

```
In packages/bootstrap-vue-next/src/components/BAlert/BAlert.vue around lines 242
to 252, the watcher on modelValue emits 'show' and 'show-prevented' events that
duplicate those already emitted by useShowHide, causing consumers to receive the
same events twice. To fix this, remove the redundant emit calls for 'show' and
'show-prevented' inside the watcher to avoid duplicate event emissions and
potential confusion.
```

</details>

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-05-22T12:57:44Z
author_association: CONTRIBUTOR
diff_hunk: @@ -1,149 +1,280 @@
 <template>
-  <Transition v-bind="fadeTransitions" enter-to-class="show">
+  <Transition
+    v-if="renderRef || contentShowing"
+    v-bind="transitionProps"
+    :appear="!!modelValue || props.visible"
+  >
     <div
-      v-if="isAlertVisible"
+      v-show="isAlertVisible"
+      :id="props.id"
       ref="_element"
       class="alert"
-      role="alert"
-      aria-live="polite"
-      aria-atomic="true"
-      :class="computedClasses"
+      :class="[props.alertClass, computedClasses]"
+      tabindex="0"
+      :role="!isAlertVisible ? undefined : props.isStatus ? 'status' : 'alert'"
+      :aria-live="!isAlertVisible ? undefined : props.isStatus ? 'polite' : 'assertive'"
+      :aria-atomic="!isAlertVisible ? undefined : true"
     >
-      <slot />
-      <template v-if="props.dismissible">
-        <BButton v-if="hasCloseSlot || props.closeContent" v-bind="closeAttrs" @click="hide">
-          <slot name="close">
-            {{ props.closeContent }}
-          </slot>
-        </BButton>
-        <BCloseButton v-else :aria-label="props.closeLabel" v-bind="closeAttrs" @click="hide" />
+      <component
+        :is="props.headerTag"
+        v-if="contentShowing && (slots.title || props.title)"
+        class="alert-heading d-flex gap-2"
+        :class="props.headerClass"
+      >
+        <slot name="title" v-bind="sharedSlots">
+          <span>
+            {{ props.title }}
+          </span>
+        </slot>
+        <template v-if="props.dismissible">
+          <BButton
+            v-if="slots.close || props.closeContent"
+            :class="[props.closeClass]"
+            class="ms-auto ps-1 btn-close-custom"
+            :variant="props.closeVariant"
+            @click.stop.prevent="hide('close')"
+          >
+            <slot name="close" v-bind="sharedSlots">
+              {{ props.closeContent }}
+            </slot>
+          </BButton>
+          <BCloseButton
+            v-else
+            :aria-label="props.closeLabel"
+            :class="[props.closeClass]"
+            @click.stop.prevent="hide('close')"
+          />
+        </template>
+      </component>
+      <template v-if="contentShowing && (slots.default || props.body)">
+        <div class="d-flex gap-2">
+          <component
+            :is="computedTag"
+            class="alert-body"
+            :class="props.bodyClass"
+            v-bind="computedLinkProps"
+            @click="computedLink && props.dismissible ? hide() : () => {}"
+          >
+            <slot v-bind="sharedSlots">
+              {{ props.body }}
+            </slot>
+          </component>
+
+          <template v-if="props.dismissible && !(slots.title || props.title)">
+            <BButton
+              v-if="slots.close || props.closeContent"
+              :class="[props.closeClass]"
+              class="ms-auto btn-close-custom"
+              :variant="props.closeVariant"
+              @click.stop.prevent="hide('close')"
+            >
+              <slot name="close" v-bind="sharedSlots">
+                {{ props.closeContent }}
+              </slot>
+            </BButton>
+            <BCloseButton
+              v-else
+              :aria-label="props.closeLabel"
+              :class="[props.closeClass]"
+              @click.stop.prevent="hide('close')"
+            />
+          </template>
+        </div>
       </template>
+      <BProgress
+        v-if="typeof modelValue === 'number' && props.progressProps !== undefined"
+        :animated="props.progressProps.animated"
+        :precision="props.progressProps.precision"
+        :show-progress="props.progressProps.showProgress"
+        :show-value="props.progressProps.showValue"
+        :striped="props.progressProps.striped"
+        :variant="props.progressProps.variant"
+        :max="modelValue"
+        :value="remainingMs"
+        height="4px"
+      />
     </div>
   </Transition>
 </template>
 
 <script setup lang="ts">
-import BCloseButton from '../BButton/BCloseButton.vue'
-import BButton from '../BButton/BButton.vue'
+import {computed, type EmitFn, useTemplateRef, watch, watchEffect} from 'vue'
+import {useBLinkHelper} from '../../composables/useBLinkHelper'
 import type {BAlertProps} from '../../types/ComponentProps'
-import {computed, useTemplateRef, watchEffect} from 'vue'
+import type {BToastEmits} from '../../types/ComponentEmits'
+import type {BToastSlots, ShowHideSlotsData} from '../../types/ComponentSlots'
+import BCloseButton from '../BButton/BCloseButton.vue'
+import BLink from '../BLink/BLink.vue'
+import BProgress from '../BProgress/BProgress.vue'
 import {useCountdown} from '../../composables/useCountdown'
 import {useDefaults} from '../../composables/useDefaults'
-import {isEmptySlot} from '../../utils/dom'
 import {useCountdownHover} from '../../composables/useCountdownHover'
-import {useFadeTransition} from '../../composables/useTransitions'
+import {useId} from '../../composables/useId'
+import {useShowHide} from '../../composables/useShowHide'
 
 const _props = withDefaults(defineProps<Omit<BAlertProps, 'modelValue'>>(), {
+  bgVariant: null,
+  body: undefined,
+  bodyClass: undefined,
   closeClass: undefined,
   closeContent: undefined,
   closeLabel: 'Close',
   closeVariant: 'secondary',
   dismissible: false,
-  fade: false,
-  immediate: true,
+  headerClass: undefined,
+  headerTag: 'div',
+  id: undefined,
+  initialAnimation: false,
   interval: 'requestAnimationFrame',
+  isStatus: false,
+  lazy: false,
+  noFade: false,
   noHoverPause: false,
   noResumeOnHoverLeave: false,
+  progressProps: undefined,
+  unmountLazy: false,
   showOnPause: true,
+  show: false,
+  solid: false,
+  textVariant: null,
+  title: undefined,
+  alertClass: undefined,
+  transProps: undefined,
+  visible: false,
+  // Link props
+  // All others use defaults
+  noRel: undefined,
+  active: undefined,
+  activeClass: undefined,
+  disabled: undefined,
+  exactActiveClass: undefined,
+  href: undefined,
+  icon: undefined,
+  opacity: undefined,
+  opacityHover: undefined,
+  stretched: false,
+  rel: undefined,
+  replace: undefined,
+  routerComponentName: undefined,
+  target: undefined,
+  to: undefined,
+  underlineOffset: undefined,
+  underlineOffsetHover: undefined,
+  underlineOpacity: undefined,
+  underlineOpacityHover: undefined,
+  underlineVariant: undefined,
+  // End link props
   variant: 'info',
 })
-
 const props = useDefaults(_props, 'BAlert')
 
-const emit = defineEmits<{
-  'close': []
-  'close-countdown': [value: number]
-  'closed': []
-}>()
+const emit = defineEmits<BToastEmits>()
 
-const slots = defineSlots<{
-  // eslint-disable-next-line @typescript-eslint/no-explicit-any
-  close?: (props: Record<string, never>) => any
-  // eslint-disable-next-line @typescript-eslint/no-explicit-any
-  default?: (props: Record<string, never>) => any
-}>()
-
-const fadeTransitions = useFadeTransition(() => props.fade)
+const slots = defineSlots<BToastSlots>()
 
 const element = useTemplateRef<HTMLElement>('_element')
 
 const modelValue = defineModel<Exclude<BAlertProps['modelValue'], undefined>>({default: false})
+const {computedLink, computedLinkProps} = useBLinkHelper(props)
+
+const computedId = useId(() => props.id, 'alert')
+
+const {
+  showRef,
+  renderRef,
+  hide,
+  toggle,
+  show,
+  buildTriggerableEvent,
+  computedNoAnimation,
+  isVisible,
+  transitionProps,
+  contentShowing,
+} = useShowHide(modelValue, props, emit as EmitFn, element, computedId)
 
-const hasCloseSlot = computed(() => !isEmptySlot(slots.close))
+// TODO solid is never used
 const countdownLength = computed(() =>
   typeof modelValue.value === 'boolean' ? 0 : modelValue.value
 )
 
-const computedClasses = computed(() => ({
-  [`alert-${props.variant}`]: props.variant !== null,
-  'alert-dismissible': props.dismissible,
-}))
-
-const closeClasses = computed(() => [props.closeClass, {'btn-close-custom': hasCloseSlot.value}])
-
 const {
   isActive,
   pause,
+  restart,
   resume,
   stop,
   isPaused,
-  restart,
   value: remainingMs,
 } = useCountdown(countdownLength, props.interval, {
-  immediate: typeof modelValue.value === 'number' && props.immediate,
+  immediate: typeof modelValue.value === 'number' && !!modelValue.value,
 })
 useCountdownHover(
   element,
   computed(() => ({
-    noHoverPause: props.noHoverPause,
-    noResumeOnHoverLeave: props.noResumeOnHoverLeave,
+    noHoverPause: props.noHoverPause || typeof modelValue.value !== 'number',
+    noResumeOnHoverLeave: props.noResumeOnHoverLeave || typeof modelValue.value !== 'number',
     modelValueIgnoresHover: typeof modelValue.value === 'boolean',
   })),
   {pause, resume}
 )
 
-const isAlertVisible = computed(() =>
-  typeof modelValue.value === 'boolean'
-    ? modelValue.value
-    : isActive.value || (props.showOnPause && isPaused.value)
-)
-
-const closeAttrs = computed(() => ({
-  variant: hasCloseSlot.value ? props.closeVariant : undefined,
-  class: closeClasses.value,
-}))
-
 watchEffect(() => {
   emit('close-countdown', remainingMs.value)
 })
 
-const hide = () => {
-  emit('close')
+const computedTag = computed(() => (computedLink.value ? BLink : 'div'))
+
+const isAlertVisible = computed(
+  () => showRef.value || isActive.value || (props.showOnPause && isPaused.value)
+)
+
+// const colorClasses = useColorVariantClasses(props)
+const computedClasses = computed(() => [
+  // colorClasses.value,
+  {
+    [`alert-${props.variant}`]: props.variant !== null,
+    'alert-dismissible': props.dismissible && !(slots.close || props.closeContent),
+    'show': isVisible.value,
+    'fade': !computedNoAnimation.value,
+  },
+])
+
+watch(modelValue, (newValue) => {
+  if (typeof newValue === 'number') {
+    const event = buildTriggerableEvent('show', {cancelable: true, trigger: 'model'})
+    emit('show', event)
+    if (event.defaultPrevented) {
+      emit('show-prevented', buildTriggerableEvent('show-prevented'))
+    } else {
+      restart()
+    }
+  }
+})
 
-  if (typeof modelValue.value === 'boolean') {
-    modelValue.value = false
-  } else {
+// isActive in the composable will cause the alert to hide when the countdown is done
+watch(isActive, (newValue) => {
+  if (newValue === false && isPaused.value === false) {
+    hide()
     modelValue.value = 0
     stop()
   }
-
-  emit('closed')
-}
+})
path: packages/bootstrap-vue-next/src/components/BAlert/BAlert.vue
user: login: coderabbitai[bot]
body: _🛠️ Refactor suggestion_

_⚠️ Potential issue_

**Race condition when programmatically hiding**

Inside the `isActive` watcher you:

1. `hide()` — starts the leave transition (async)
2. `modelValue.value = 0` — immediately resets the v-model

This triggers the `useShowHide` watcher → `show()`/`hide()` logic again, potentially interrupting the ongoing transition.  
Move the model update into the promise chain returned by `hide()` or await the transition.

```diff
if (newValue === false && isPaused.value === false) {
-  hide()
-  modelValue.value = 0
+  hide().then(() => {
+    modelValue.value = 0
+  })
   stop()
}
```
 


<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

```suggestion
// isActive in the composable will cause the alert to hide when the countdown is done
watch(isActive, (newValue) => {
  if (newValue === false && isPaused.value === false) {
    hide().then(() => {
      modelValue.value = 0
    })
    stop()
  }
})
```

</details>

<!-- suggestion_end -->

<details>
<summary>🤖 Prompt for AI Agents</summary>

```
In packages/bootstrap-vue-next/src/components/BAlert/BAlert.vue around lines 254
to 261, the watcher on isActive calls hide() which starts an asynchronous leave
transition, then immediately sets modelValue.value to 0, causing a race
condition by retriggering show/hide logic and interrupting the transition. To
fix this, update modelValue.value only after the hide() transition completes by
awaiting the promise returned by hide() or chaining the model update in the
hide() promise resolution.
```

</details>

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-05-22T12:57:44Z
author_association: CONTRIBUTOR
diff_hunk: @@ -143,21 +149,30 @@ export const collapseInjectionKey: InjectionKey<{
 // Show/Hide components
 export type RegisterShowHideFnInput = {
   id: string
+  component: ComponentInternalInstance
   value: Ref<boolean>
-  toggle: () => void
-  show: () => void
-  hide: (trigger?: string) => void
+  toggle: (resolveOnHide?: boolean) => Promise<boolean | string>
+  show: (resolveOnHide?: boolean) => Promise<boolean | string>
+  hide: (trigger?: string) => Promise<boolean | string>
+  registerTrigger: (trigger: string, el: Element) => void
+  unregisterTrigger: (trigger: string, el: Element, clean: boolean) => void
+}
+
+export interface RegisterShowHideMapValue {
+  id: string
+  component: ComponentInternalInstance
+  value: Readonly<Ref<boolean>>
+  toggle: (resolveOnHide?: boolean) => Promise<boolean | string | null>
+  show: (resolveOnHide?: boolean) => Promise<boolean | string | null>
+  hide: (trigger?: string, noTraverse?: boolean) => Promise<boolean | string | null>
+  registerTrigger: (trigger: string, el: Element) => void
+  unregisterTrigger: (trigger: string, el: Element, clean: boolean) => void
 }
path: packages/bootstrap-vue-next/src/utils/keys.ts
user: login: coderabbitai[bot]
body: _🛠️ Refactor suggestion_

**Align `RegisterShowHideFnInput` ↔ `RegisterShowHideMapValue` method signatures**

`RegisterShowHideFnInput.hide` accepts only one optional argument, whereas the corresponding `hide` inside `RegisterShowHideMapValue` additionally supports `noTraverse?: boolean` and the return types differ (`Promise<boolean | string>` vs `Promise<boolean | string | null>`).  
When the map is populated with the raw `input` object, this structural mismatch is still *technically* accepted (bivariant parameter types), but you lose strict type-safety and open the door to unhandled `null` values.

```diff
-  hide: (trigger?: string) => Promise<boolean | string>
+  hide: (trigger?: string, noTraverse?: boolean) => Promise<boolean | string | null>
```

Mirroring the map signature here keeps the two types in lock-step and prevents silent runtime surprises.  



<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

```suggestion
export type RegisterShowHideFnInput = {
  id: string
  component: ComponentInternalInstance
  value: Ref<boolean>
  toggle: (resolveOnHide?: boolean) => Promise<boolean | string>
  show: (resolveOnHide?: boolean) => Promise<boolean | string>
-  hide: (trigger?: string) => Promise<boolean | string>
+  hide: (trigger?: string, noTraverse?: boolean) => Promise<boolean | string | null>
  registerTrigger: (trigger: string, el: Element) => void
  unregisterTrigger: (trigger: string, el: Element, clean: boolean) => void
}
```

</details>

<!-- suggestion_end -->

<details>
<summary>🤖 Prompt for AI Agents</summary>

```
In packages/bootstrap-vue-next/src/utils/keys.ts between lines 150 and 170, the
method signatures of RegisterShowHideFnInput and RegisterShowHideMapValue are
inconsistent, specifically the hide method's parameters and return types. To fix
this, update RegisterShowHideFnInput.hide to accept the same optional parameters
as RegisterShowHideMapValue.hide, including noTraverse?: boolean, and adjust its
return type to Promise<boolean | string | null> to match exactly. This alignment
ensures strict type safety and prevents runtime issues from unhandled null
values.
```

</details>

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-05-22T12:57:45Z
author_association: CONTRIBUTOR
diff_hunk: @@ -1,76 +1,258 @@
-import type {Component, MaybeRefOrGetter, RendererElement, RendererNode, VNode} from 'vue'
+import type {Component, ComponentPublicInstance, MaybeRef, MaybeRefOrGetter, WatchHandle} from 'vue'
 import type {BModalProps, BPopoverProps, BToastProps, BTooltipProps} from './ComponentProps'
 import type {ContainerPosition} from './Alignment'
+import type {BModalSlots, BPopoverSlots, BToastSlots} from './ComponentSlots'
+import type {BModalEmits, BPopoverEmits, BToastEmits} from './ComponentEmits'
+import type {BvTriggerableEvent} from '../utils'
+import type BModal from '../components/BModal/BModal.vue'
+import type BToast from '../components/BToast/BToast.vue'
+import type BPopover from '../components/BPopover/BPopover.vue'
+import type BTooltip from '../components/BTooltip/BTooltip.vue'
 
 export type ControllerKey = symbol | string
+export interface PromiseWithModalInternal extends AsyncDisposable {
+  id: ControllerKey
+  ref: ComponentPublicInstance<typeof BModal> | null
+  show: () => PromiseWithModal
+  hide: (trigger?: string) => PromiseWithModal
+  toggle: () => PromiseWithModal
+  set: (val: Partial<ModalOrchestratorParam>) => PromiseWithModal
+  get: () => ModalOrchestratorParam | undefined
+  destroy: () => void
+}
+export interface PromiseWithModal
+  extends Promise<BvTriggerableEvent | boolean | null>,
+    PromiseWithModalInternal {}
+
+/**
+ * Promise that resolves to a boolean or null
+ * @deprecated
+ */
+export interface PromiseWithModalBoolean
+  extends Promise<boolean | null>,
+    AsyncDisposable,
+    PromiseWithModalInternal {}
+
+export interface PromiseWithToast extends Promise<BvTriggerableEvent>, PromiseWithToastInternal {}
+export interface PromiseWithToastInternal extends AsyncDisposable {
+  id: ControllerKey
+  ref: ComponentPublicInstance<typeof BToast> | null
+  show: () => PromiseWithToast
+  hide: (trigger?: string) => void
+  toggle: () => void
+  set: (val: Partial<ToastOrchestratorParam>) => void
+  get: () => ToastOrchestratorParam | undefined
+  destroy: () => void
+}
+export interface PromiseWithPopover
+  extends Promise<BvTriggerableEvent>,
+    PromiseWithPopoverInternal {}
+export interface PromiseWithPopoverInternal extends AsyncDisposable {
+  id: ControllerKey
+  ref: ComponentPublicInstance<typeof BPopover | typeof BTooltip> | null
+  show: () => PromiseWithPopover
+  hide: (trigger?: string) => void
+  toggle: () => void
+  set: (val: Partial<PopoverOrchestratorParam | TooltipOrchestratorParam>) => void
+  get: () => PopoverOrchestratorParam | undefined
+  destroy: () => void
+}
+
+type Prefix<P extends string, S extends string> = `${P}${S}`
 
-export type OrchestratedToastModelValue = boolean | number
-export type PrivateOrchestratedToast = Omit<BToastProps, 'modelValue'> & {
+type CamelCase<S extends string> = S extends `${infer P1}-${infer P2}${infer P3}`
+  ? `${Lowercase<P1>}${Uppercase<P2>}${CamelCase<P3>}`
+  : Lowercase<S>
+
+export type ToastOrchestratorArrayValue = Omit<BToastProps, 'modelValue'> & {
+  /**
+   * Position
+   * @default 'top-end'
+   */
+  'position'?: ContainerPosition
+  /**
+   * Sets whether or not the toast should be appended to the container
+   * @default undefined Implicitly defaults to the BToastOrchestrator's appendToast value
+   */
+  'appendToast'?: boolean
+  /**
+   * A proxy for the modelValue, essentially, you set this once, and it mimics the modelValue.
+   *
+   * Falsy values default to 5000
+   *
+   * @default 5000
+   */
+  'modelValue'?: BToastProps['modelValue'] // Convert it to be the same name as useModalController.
+  '_self': ControllerKey
+  'onUpdate:modelValue'?: (val: boolean) => void
+  'component'?: Readonly<Component>
+  'options': ToastOrchestratorCreateOptions
+  'promise': {
+    value: PromiseWithToast
+    resolve: (value: BvTriggerableEvent) => void
+    stop?: WatchHandle
+  }
+} & {
+  [K in keyof BToastEmits as CamelCase<Prefix<'on-', K>>]?: (e: BToastEmits[K][0]) => void
+} & {
+  slots?: {
+    [K in keyof BToastSlots]?: BToastSlots[K] | Readonly<Component>
+  }
+}
+export type ToastOrchestratorParam = Omit<BToastProps, 'modelValue'> & {
+  'title'?: MaybeRefOrGetter<BToastProps['title']>
+  'body'?: MaybeRefOrGetter<BToastProps['body']>
   /**
    * Position
    * @default 'top-end'
+   * @deprecated
    */
-  pos?: ContainerPosition
+  'pos'?: ContainerPosition
+  /**
+   * Position
+   * @default 'top-end'
+   */
+  'position'?: ContainerPosition
   /**
    * Sets whether or not the toast should be appended to the container
-   * @default undefined Implicitly defualts to the BToastOrchestrator's appendToast value
+   * @default undefined Implicitly defaults to the BToastOrchestrator's appendToast value
    */
-  appendToast?: boolean
+  'appendToast'?: boolean
   /**
    * A proxy for the modelValue, essentially, you set this once, and it mimics the modelValue.
    *
    * Falsy values default to 5000
    *
    * @default 5000
    */
-  value?: OrchestratedToastModelValue // show or hide
-  _self: ControllerKey
-  _modelValue: OrchestratedToastModelValue // Convert it to be the same name as useModalController.
-  // The difference between the two is that unlike that one, this value can be defined (there's cannot be).
+  'modelValue'?: MaybeRefOrGetter<BToastProps['modelValue']>
+  '_self'?: ControllerKey
+  'onUpdate:modelValue'?: (val: boolean) => void
+  'component'?: Readonly<Component>
+  /**
+   * @deprecated
+   */
+  'props'?: Record<string, unknown>
+} & {
+  [K in keyof BToastEmits as CamelCase<Prefix<'on-', K>>]?: (e: BToastEmits[K][0]) => void
+} & {
+  slots?: {
+    [K in keyof BToastSlots]?: MaybeRefOrGetter<BToastSlots[K] | Readonly<Component>>
+  }
 }
-export type PublicOrchestratedToast = Omit<PrivateOrchestratedToast, '_modelValue' | '_self'>
-export type ToastOrchestratorArrayValue = {
-  component: unknown
-  props: PrivateOrchestratedToast
+
+export type ToastOrchestratorCreateParam = MaybeRef<ToastOrchestratorParam>
+
+export type TooltipOrchestratorMapValue = BTooltipProps & {
+  'onUpdate:modelValue'?: (val: boolean) => void
+  'component'?: Readonly<Component>
+  'promise': {
+    value: PromiseWithPopover
+    resolve: (value: BvTriggerableEvent) => void
+    stop?: WatchHandle
+  }
+  'slots'?: {
+    [K in keyof Omit<BPopoverSlots, 'target'>]?: BPopoverSlots[K] | Readonly<Component>
+  }
+} & {
+  [K in keyof BPopoverEmits as CamelCase<Prefix<'on-', K>>]?: (e: BPopoverEmits[K][0]) => void
 }
-export type ToastOrchestratorShowParam = {
-  component?: Readonly<Component>
-  props?: MaybeRefOrGetter<PublicOrchestratedToast>
+
+export type TooltipOrchestratorParam = Omit<BTooltipProps, 'body' | 'title' | 'modelValue'> & {
+  'onUpdate:modelValue'?: (val: boolean) => void
+  'title'?: MaybeRefOrGetter<BTooltipProps['title']>
+  'body'?: MaybeRefOrGetter<BTooltipProps['body']>
+  'modelValue'?: MaybeRefOrGetter<BTooltipProps['modelValue']>
+  'component'?: Readonly<Component>
+  'slots'?: {
+    [K in keyof Omit<BPopoverSlots, 'target'>]?: MaybeRefOrGetter<
+      BPopoverSlots[K] | Readonly<Component>
+    >
+  }
+} & {
+  [K in keyof BPopoverEmits as CamelCase<Prefix<'on-', K>>]?: (e: BPopoverEmits[K][0]) => void
 }
 
-export type SlotFunction = () => VNode<RendererNode, RendererElement, {[key: string]: unknown}>
+export type TooltipOrchestratorCreateParam = MaybeRef<TooltipOrchestratorParam>
 
-export type TooltipOrchestratorParam = {
-  title?: MaybeRefOrGetter<BTooltipProps['title'] | SlotFunction>
-  content?: MaybeRefOrGetter<BTooltipProps['content'] | SlotFunction>
-} & Omit<BTooltipProps, 'content' | 'title'>
+export type PopoverOrchestratorMapValue = BPopoverProps &
+  BTooltipProps & {
+    'onUpdate:modelValue'?: (val: boolean) => void
+    'component'?: Readonly<Component>
+    'promise': {
+      value: PromiseWithPopover
+      resolve: (value: BvTriggerableEvent) => void
+      stop?: WatchHandle
+    }
+    'slots'?: {
+      [K in keyof Omit<BPopoverSlots, 'target'>]?: BPopoverSlots[K] | Readonly<Component>
+    }
+  } & {
+    [K in keyof BPopoverEmits as CamelCase<Prefix<'on-', K>>]?: (e: BPopoverEmits[K][0]) => void
+  }
 
-export type TooltipOrchestratorShowParam = MaybeRefOrGetter<TooltipOrchestratorParam>
+export type PopoverOrchestratorParam = Omit<BPopoverProps, 'body' | 'title' | 'modelValue'> & {
+  'onUpdate:modelValue'?: (val: boolean) => void
+  'title'?: MaybeRefOrGetter<BPopoverProps['title']>
+  'body'?: MaybeRefOrGetter<BPopoverProps['body']>
+  'modelValue'?: MaybeRefOrGetter<BPopoverProps['modelValue']>
+  'component'?: Readonly<Component>
+  'slots'?: {
+    [K in keyof Omit<BPopoverSlots, 'target'>]?: MaybeRefOrGetter<
+      BPopoverSlots[K] | Readonly<Component>
+    >
+  }
+} & {
+  [K in keyof BPopoverEmits as CamelCase<Prefix<'on-', K>>]?: (e: BPopoverEmits[K][0]) => void
+}
 
-export type PopoverOrchestratorParam = {
-  title?: MaybeRefOrGetter<BPopoverProps['title'] | SlotFunction>
-  content?: MaybeRefOrGetter<BPopoverProps['content'] | SlotFunction>
-} & Omit<BPopoverProps, 'content' | 'title'>
+export type PopoverOrchestratorCreateParam = MaybeRef<PopoverOrchestratorParam>
 
-export type PopoverOrchestratorShowParam = MaybeRefOrGetter<PopoverOrchestratorParam>
+export type ModalOrchestratorMapValue = BModalProps & {
+  'onUpdate:modelValue'?: (val: boolean) => void
+  'options': ModalOrchestratorCreateOptions
+  'promise': {
+    value: PromiseWithModal | PromiseWithModalBoolean
+    resolve: (value: BvTriggerableEvent | boolean | null) => void
+    stop?: WatchHandle
+  }
+  'component'?: Readonly<Component>
+  'slots'?: {
+    [K in keyof BModalSlots]?: BModalSlots[K] | Readonly<Component>
+  }
+} & {
+  [K in keyof BModalEmits as CamelCase<Prefix<'on-', K>>]?: (e: BModalEmits[K][0]) => void
+}
 
-export type PrivateOrchestratedModal = Omit<BModalProps, 'modelValue'> & {
-  _modelValue: BModalProps['modelValue']
-  _promise: {
-    value: Promise<boolean | null>
-    resolve: (value: boolean | null) => void
+export type ModalOrchestratorParam = Omit<BModalProps, 'body' | 'title' | 'modelValue'> & {
+  'onUpdate:modelValue'?: (val: boolean) => void
+  'title'?: MaybeRefOrGetter<BModalProps['title']>
+  'body'?: MaybeRefOrGetter<BModalProps['body']>
+  'modelValue'?: MaybeRefOrGetter<BModalProps['modelValue']>
+  'options'?: ModalOrchestratorCreateOptions
+  'component'?: Readonly<Component>
+  /**
+   * @deprecated
+   */
+  'props'?: Record<string, unknown>
+  'slots'?: {
+    [K in keyof BModalSlots]?: MaybeRefOrGetter<BModalSlots[K] | Readonly<Component>>
   }
path: packages/bootstrap-vue-next/src/types/ComponentOrchestratorTypes.ts
user: login: coderabbitai[bot]
body: _🛠️ Refactor suggestion_

**`ModalOrchestratorParam.options` is shadow-copying the function argument**

`ModalOrchestratorCreateParam` already allows callers to provide `{ options }`.  Allowing the same field inside the param object duplicates configuration paths and complicates validation.

Recommend removing `options` from `ModalOrchestratorParam` and keeping it exclusively in the `create(..., options)` call to honour the single-source-of-truth principle.

<details>
<summary>🤖 Prompt for AI Agents</summary>

```
In packages/bootstrap-vue-next/src/types/ComponentOrchestratorTypes.ts between
lines 227 and 240, the ModalOrchestratorParam type includes an 'options' field
that duplicates configuration already provided by ModalOrchestratorCreateParam's
options argument. To fix this, remove the 'options' property from
ModalOrchestratorParam entirely, ensuring that configuration is only passed
through the create function's options parameter to maintain a single source of
truth and simplify validation.
```

</details>

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-05-22T12:57:45Z
author_association: CONTRIBUTOR
diff_hunk: @@ -1,76 +1,258 @@
-import type {Component, MaybeRefOrGetter, RendererElement, RendererNode, VNode} from 'vue'
+import type {Component, ComponentPublicInstance, MaybeRef, MaybeRefOrGetter, WatchHandle} from 'vue'
 import type {BModalProps, BPopoverProps, BToastProps, BTooltipProps} from './ComponentProps'
 import type {ContainerPosition} from './Alignment'
+import type {BModalSlots, BPopoverSlots, BToastSlots} from './ComponentSlots'
+import type {BModalEmits, BPopoverEmits, BToastEmits} from './ComponentEmits'
+import type {BvTriggerableEvent} from '../utils'
+import type BModal from '../components/BModal/BModal.vue'
+import type BToast from '../components/BToast/BToast.vue'
+import type BPopover from '../components/BPopover/BPopover.vue'
+import type BTooltip from '../components/BTooltip/BTooltip.vue'
 
 export type ControllerKey = symbol | string
+export interface PromiseWithModalInternal extends AsyncDisposable {
+  id: ControllerKey
+  ref: ComponentPublicInstance<typeof BModal> | null
+  show: () => PromiseWithModal
+  hide: (trigger?: string) => PromiseWithModal
+  toggle: () => PromiseWithModal
+  set: (val: Partial<ModalOrchestratorParam>) => PromiseWithModal
+  get: () => ModalOrchestratorParam | undefined
+  destroy: () => void
+}
+export interface PromiseWithModal
+  extends Promise<BvTriggerableEvent | boolean | null>,
+    PromiseWithModalInternal {}
+
+/**
+ * Promise that resolves to a boolean or null
+ * @deprecated
+ */
+export interface PromiseWithModalBoolean
+  extends Promise<boolean | null>,
+    AsyncDisposable,
+    PromiseWithModalInternal {}
+
+export interface PromiseWithToast extends Promise<BvTriggerableEvent>, PromiseWithToastInternal {}
+export interface PromiseWithToastInternal extends AsyncDisposable {
+  id: ControllerKey
+  ref: ComponentPublicInstance<typeof BToast> | null
+  show: () => PromiseWithToast
+  hide: (trigger?: string) => void
+  toggle: () => void
+  set: (val: Partial<ToastOrchestratorParam>) => void
+  get: () => ToastOrchestratorParam | undefined
+  destroy: () => void
+}
+export interface PromiseWithPopover
+  extends Promise<BvTriggerableEvent>,
+    PromiseWithPopoverInternal {}
+export interface PromiseWithPopoverInternal extends AsyncDisposable {
+  id: ControllerKey
+  ref: ComponentPublicInstance<typeof BPopover | typeof BTooltip> | null
+  show: () => PromiseWithPopover
+  hide: (trigger?: string) => void
+  toggle: () => void
+  set: (val: Partial<PopoverOrchestratorParam | TooltipOrchestratorParam>) => void
+  get: () => PopoverOrchestratorParam | undefined
+  destroy: () => void
+}
path: packages/bootstrap-vue-next/src/types/ComponentOrchestratorTypes.ts
user: login: coderabbitai[bot]
body: _⚠️ Potential issue_

**Inconsistent async return contracts across orchestrator helpers**

`PromiseWithModalInternal.hide/toggle` return `PromiseWithModal`, while the Toast & Popover counterparts return `void`.  
From an API-consumer standpoint the ability to **await** the hide/toggle lifecycle is invaluable (think animations, cleanup, focus-restore).

```diff
 interface PromiseWithToastInternal extends AsyncDisposable {
   ...
-  hide: (trigger?: string) => void
-  toggle: () => void
+  hide: (trigger?: string) => PromiseWithToast
+  toggle: () => PromiseWithToast
 }
 ...
 interface PromiseWithPopoverInternal extends AsyncDisposable {
   ...
-  hide: (trigger?: string) => void
-  toggle: () => void
+  hide: (trigger?: string) => PromiseWithPopover
+  toggle: () => PromiseWithPopover
 }
```

Unifying these signatures also enables generic utility code that operates on *any* `PromiseWithX`.  



<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

```suggestion
export interface PromiseWithToast extends Promise<BvTriggerableEvent>, PromiseWithToastInternal {}
export interface PromiseWithToastInternal extends AsyncDisposable {
  id: ControllerKey
  ref: ComponentPublicInstance<typeof BToast> | null
  show: () => PromiseWithToast
  hide: (trigger?: string) => PromiseWithToast
  toggle: () => PromiseWithToast
  set: (val: Partial<ToastOrchestratorParam>) => void
  get: () => ToastOrchestratorParam | undefined
  destroy: () => void
}

export interface PromiseWithPopover
  extends Promise<BvTriggerableEvent>,
    PromiseWithPopoverInternal {}
export interface PromiseWithPopoverInternal extends AsyncDisposable {
  id: ControllerKey
  ref: ComponentPublicInstance<typeof BPopover | typeof BTooltip> | null
  show: () => PromiseWithPopover
  hide: (trigger?: string) => PromiseWithPopover
  toggle: () => PromiseWithPopover
  set: (val: Partial<PopoverOrchestratorParam | TooltipOrchestratorParam>) => void
  get: () => PopoverOrchestratorParam | undefined
  destroy: () => void
}
```

</details>

<!-- suggestion_end -->

<details>
<summary>🤖 Prompt for AI Agents</summary>

```
In packages/bootstrap-vue-next/src/types/ComponentOrchestratorTypes.ts between
lines 36 and 59, the hide and toggle methods in PromiseWithToastInternal and
PromiseWithPopoverInternal currently return void, unlike
PromiseWithModalInternal where they return PromiseWithModal. To unify the API
and allow awaiting these lifecycle methods, update the hide and toggle method
signatures in PromiseWithToastInternal and PromiseWithPopoverInternal to return
PromiseWithToast and PromiseWithPopover respectively, matching the modal
interface.
```

</details>

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-05-22T12:57:45Z
author_association: CONTRIBUTOR

PR comments:
user: login: bolt-new-by-stackblitz[bot]
created_at: 2025-04-27T17:23:38Z
body: <a href="https://stackblitz.com/~/github.com/bootstrap-vue-next/bootstrap-vue-next/pull/2671"><img src="https://developer.stackblitz.com/img/review_pr_small.svg" alt="Review PR in StackBlitz Codeflow" align="left" width="103" height="20"></a> _Run & review this pull request in [StackBlitz Codeflow](https://stackblitz.com/~/github.com/bootstrap-vue-next/bootstrap-vue-next/pull/2671)._

author_association: NONE
user: login: coderabbitai[bot]
created_at: 2025-04-27T17:23:45Z
body: <!-- This is an auto-generated comment: summarize by coderabbit.ai -->
<!-- walkthrough_start -->

## Walkthrough

Component-scoped style blocks were removed from several Vue component files and their corresponding styles were migrated into dedicated SCSS partials for `BAlert`, `BPopover`, and `BToast`. These SCSS files were then imported into the main styles entry point. Additionally, a `.no-transition` utility class was globally introduced.

## Changes

| File(s)                                                                                               | Change Summary                                                                                                                                                                                                                                                                                                                                                 |
|-------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `BAlert.vue`, `BPopover.vue`, `BToast.vue`, `BToastOrchestrator.vue`, `BOffcanvas.vue`                | Removed scoped style blocks from each Vue component, moving all relevant CSS to SCSS partials or global styles. No changes to component logic or exports.                                                                                                                                                                                                      |
| `_alert.scss`, `_popover.scss`, `_toast.scss`                                                         | Added new SCSS partials for each component, containing the migrated styles for progress bar transitions, custom close buttons, popover arrow backgrounds, toast opacity, and list animations.                                                                                                                           |
| `styles.scss`                                                                                         | Imported the new SCSS partials for `BAlert`, `BPopover`, and `BToast`. Introduced a global `.no-transition` utility class to disable transitions.                                                                                                                                                                       |

## Sequence Diagram(s)

```mermaid
sequenceDiagram
    participant App
    participant StylesEntry as styles.scss
    participant AlertSCSS as _alert.scss
    participant PopoverSCSS as _popover.scss
    participant ToastSCSS as _toast.scss

    App->>StylesEntry: Import global styles
    StylesEntry->>AlertSCSS: Import BAlert styles
    StylesEntry->>PopoverSCSS: Import BPopover styles
    StylesEntry->>ToastSCSS: Import BToast styles
    StylesEntry->>App: Styles applied globally
```

## Poem

> In the garden of styles, a bunny hops by,  
> Gathering CSS from components, oh my!  
> Into the SCSS burrow, each rule finds a home,  
> Now global and tidy, wherever you roam.  
> With transitions tamed and arrows in sync,  
> The code is much cleaner—what do you think?  
> 🐇✨

<!-- walkthrough_end -->

<!-- announcements_start -->

> [!NOTE]
> <details>
> <summary>⚡️ AI Code Reviews for VS Code, Cursor, Windsurf</summary>
> 
> CodeRabbit now has a plugin for VS Code, Cursor and Windsurf. This brings AI code reviews directly in the code editor. Each commit is reviewed immediately, finding bugs before the PR is raised. Seamless context handoff to your AI code agent ensures that you can easily incorporate review feedback.
> Learn more [here](http://coderabbit.ai/ide).
> 
> </details>

---

> [!NOTE]
> <details>
> <summary>⚡️ Faster reviews with caching</summary>
> 
> CodeRabbit now supports caching for code and dependencies, helping speed up reviews. This means quicker feedback, reduced wait times, and a smoother review experience overall. Cached data is encrypted and stored securely. This feature will be automatically enabled for all accounts on May 30th. To opt out, configure `Review - Disable Cache` at either the organization or repository level. If you prefer to disable all data retention across your organization, simply turn off the `Data Retention` setting under your Organization Settings.
> Enjoy the performance boost—your workflow just got faster.
> 
> </details>

<!-- announcements_end -->

---

<details>
<summary>📜 Recent review details</summary>

**Configuration used: CodeRabbit UI**
**Review profile: CHILL**
**Plan: Pro**


<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between 8e929bc8bfa48867257437cc17ca125ba1309643 and 86426bb7f54e85cd42ff4997f3ec19713ece52a0.

</details>

<details>
<summary>📒 Files selected for processing (3)</summary>

* `packages/bootstrap-vue-next/src/components/BToast/BToast.vue` (0 hunks)
* `packages/bootstrap-vue-next/src/components/BToast/_toast.scss` (1 hunks)
* `packages/bootstrap-vue-next/src/styles/styles.scss` (2 hunks)

</details>

<details>
<summary>💤 Files with no reviewable changes (1)</summary>

* packages/bootstrap-vue-next/src/components/BToast/BToast.vue

</details>

<details>
<summary>🚧 Files skipped from review as they are similar to previous changes (2)</summary>

* packages/bootstrap-vue-next/src/components/BToast/_toast.scss
* packages/bootstrap-vue-next/src/styles/styles.scss

</details>

</details>
<!-- internal state start -->


<!-- DwQgtGAEAqAWCWBnSTIEMB26CuAXA9mAOYCmGJATmriQCaQDG+Ats2bgFyQAOFk+AIwBWJBrngA3EsgEBPRvlqU0AgfFwA6NPEgQAfACgjoCEYDEZyAAUASpETZWaCrKNwSPbABsvkCiQBHbGlcSHFcLzpIACIAM3gADwAKRAZERABKSGZ8KXo0H3s05ERcWUjkfDx+WIVmbnxyDFxEaMgAdzQS7AFmdRp6OTDYD2xESkhsDFL8bm4VSIZnLw0YEeztLAZYTFIUZopFbAYo+CxcBGRbFGR/L3wl8QwidEKAZQBhN7f7MorIWKHZgddQIc7rJj1RrsZAXEjMcZeKTIM4EewkeZUGiQT7fAHwCqrdx1BpNFroWKxUQDSBDC6oba7DxnBhebBKSAAIQAgpEKLgADRcgAih24tHw7QwQs5AFlFAUZQB5SlLDASLoyqyzXKUGXQfBdQXoDD0TkGo1KijbEJY/AUDRudZoPCwe3oeBseho5ibXCbBTTJA0DAMeTtUHDDwkBLBs4vUrlDyYejxSK/CjHXDYfwdBDp30Aa3jUfG2UU8Hij3gjUQRPW1wZ1BIRHt8AAXlEuuhadgiPEEvXk673QVEPhIBh8DSpid+X75CoqqF2iM4Xx6chGc8PH0iLBQgIPEwg0p/Plaf40MXnowdjvILRgmEJ3CirMoon/keSzk8vxqnwWo30hUkYUdAwADkJzQWhaHUGsMAKAESGoHNpCFNAGBOdJ4DULx1HkT1eF1NhmkQIV3QlBhHHYahEMmcVm2QdpKGZUM2SUegzmGVBbCHHALndfwghCKIqToAQsMLfgsEweQGhDcRkL6Upr2kfYo3QOZDiw2AwnUjABOuG0GELAjSn2eDHg0i5qC0k8pGaRDkMhPpySIbB4CUAjyBJAjMBOG5JlDSh/TOTDTS0l0hL4eD6CnUIT3iChgVgkZ/FCl8UE0cxLA+FgyPJBwnBcJ0PG4bxfBE4JLP8E5JCiNyYQBIEwnaCcT1wCg8Lwe06zWYdYqFKYZjmBZRGWIV/CpTKTgvO5m249JnyopAaNwxoUCUZz4i7ZAzAARgADmOgBWIVfJvF5N2sOw0V4Gs+A6igZPdLqY1ygxuUSjLA263qCAoIUEg1Zw0CFEiJG8rs4IQxpkIk2gpLMvwSC8Jbsrfe9aF8l4gMgL4fg1HqJpRcFj0aGgElCAnbu3Ugh3kIrEK7Hh/GhqorjsJIzAAJjOs6ADYslXNiezYIT6E6WExO9Cd/ABkgpEJvESfgMnWpYLScloVyWDA5pVk5Looi2zcQbBqh7D7UhSiiOyV0jECc0y0I0B0w1thqctodvL8NOXH3QOhcihXUDoqi8QYPCPcgqw13w0Vg711iJyB1c1gADCABEQMBdYKMB2zOJQEizk16BzsA87AAgjQLtAEjACNaAuCvAW1t8s8zgRCVSdIO4JDw0W7jhDmnCvEWpe161QDrvHoXhpEcBZ5DIBxczhMte/+ZwPCIe4pJ8RdsOkRA8PTFMNlRAMTwv+3ml+cp4wggAxHN1xyfxLbQcHIFEqUVmqcPDkBwhfMoPsHBzHtE8F4G0CDAk5PgacpQqDcHZiQAcGcCi1UgEkHa059qQCzmYAA3gAEmXgOAAvlnLIPF6ZU0+uHeoXhZAlkdjbb20CGj8myPAfcoREq0g8DVeA55IBEEahTRgrt2B3iZHWIw0F+DrjqEVCiaNoYkHaFo90SMUYyVYrmKGMNuKyLQBqAkE1HzUDQEOXMJjQEThjNwAiDAI6APELWIUJ45zTEonwGqIRkCxHeveO2ojZCNBATbIgdsfHTGyOwYBAl4KpDGBfLaj1fQ9XYQCB4YwzZYEWjSJADgNLX0Uqk5CxFDhSE0dldOu9dyYDQKQIqhSfCSk4esAOkBPpUDEIhCC3I4ZJIKOwzCAIqraPgLojo3YAYJLYoMeQb5il8CYGeFQahwrh1NPAGy6yTSCRYJjZqzQhQCGqFOWZ/I/ocwWe0R8JBwpeEqHwQx0kOjixZJxOgEF9DGHAFAMg9ACYxUIKQcgWImqFXYFwXg/BhDUkajIeQOzlCqHUFoHQIKTBQDgKgVAmBznEDIMoGkVzOB+DQK8kqeT5BDGxVQXFmhtC6DAIYUFpgDDzDMh06QAB6AQKCWjdQ9mACQwQwDkBpiKxA1oRUhzJIgEVPI+S4E1byMKGhZUkA4AYaIpqDAWEgNyAAkpSuFmMmXOHkATBm0hyrvm4J+P4sd7io3sm+CFPtu5av1YaiuaqFEy38u89G8h/B/iBWsUlHF2RRHTpmf4GSFglhIkQfw6RaTODCFQIMST8HV1rjmvN+cpIUDrsWiBiEuBTnIPQkE9IsBZy0NqyAHAlAYiSBoSt59ICDsOLm8+NdnAZArtfBB2svwlnCXwTtAhcAYDAKyfA4wN1jEQRXLh4xQhZzydI9d4rcCIK4NCmdUUs4NAbY0LgZTGqdsgKo6cf1Yizkmb4d0qQercCSpEjSziNhKAgnlS1XgaBYkQrCV86wlCsnBkkyotQ3GwLNnwSqfdjmDOcuIV1UFoSOlNdEIwEAwBGEFYWYVGrxWoKldwGVcqFU6uVQwVVBtQ4tF1dqkVAB9Ao+qB6IGNWR81lhrW2upZ+RwzLg7AcQE6BkwH9jdSODhdqE5yCvNxD8AZ6aQOgkYc6bt4an4BwGsSFKdVvAj2cKQcknah3pEnRQCu6N4QtXIPbCxF8OQubHVWiuZwAseE7cJ/kWchSZtw7edOUqS1wdklpVzMhC1DEPXAkhud87pfc3WzAD6MD7onFnZtJBX3EnGCeegRntLuIWfBwZkQmkRguA5DG+aV1ro3fcbdc7mChdPBFrtYUYvogvX0kgEjaTTkQRsCgp7srQt9OIBglceBbvhhgGbwJn1SHYasd96iBkkQ9fyZr/A+BoKzDmVySn0C5lRIcJ880IIWt5DB+itYsaIdEBjWDf2CYYf5FhzwuHNupMI8p4j5BSNmoMJR6j0k6NiolWg6Vhr5WfSVSqizvHOQqliGqDUGrieqkwOTg1wRxNmq+za2Fsn6AOpcIppRbqXW3HhLqLSqQPz0H08/dMfcHgySUPEPbt5u4aCnEV5LjQw3dZYpGLOSWStNuhJAEAxFYGYFwAAbg7m1INJOyddFp1VkkPGTsTg/RMbFN3ID3GkZt3WlZjm/eSfvMDJAIOSagz91D/2PBIaB97tDgyEh8JpO6HDHj8PhGayo6EJqkco4FWju2GPGPoJYyQXHiqONcahOqzV2oGhSAoBXnU1erf0/I4zmT8LWfycdRznccPiTwUpGjeNyA0DuqiMLgZYvUad2BEGyvuoHShpt2SOeJQvUGWW9GquGgK118oGAaFABtPW/owANDmDv9xWFvPNAAHoAF4ADk57EF34ALojpP7P3fFBDjtA4DsRASQABqR8SQSAPQN/bfWtEYWCSgDIDgDgNAWIGDG9DfLfKvHfR/bWQdCAz/b/X/LoQA4AiQUA8AtAyA1CM8WA+AxAygKeLzMQfqIUbLGbVWYmZwDWPuCLPLY/bAgQIgMrEhEmJILg9/avMAKAs8GuIgadO3NRP6J3d0V3PDbnZ7NpcDIwSDb7alFLUeAHZDYHZJUHGPTDSFbDHoRPGHFPeHf3dPcjZHflGjdHBjSVfPHHNjfHTjQnCnGfavQTEQygDQUTRvQPaTZnVvG2UqJ1WobnIwbkScRZUfPbU/FcbsFOB2BDCLATPwh0UTIedMUzCLJwrHZjVwz6CuBwxmRNEoRIj1UIf0NfckLI1XTrNbeiTbc/E4LpP1dYDAtKKKHo7gkrKIRo4YeyHYFWIfLIyAcQiYT0L0DWGgApcJHpCMGXdYSY5wb/VYK1UIQ9WENOPEIbdmD8fkeQctfLHgvg0PHBNkDwAmM47g0gsQ8g9Ay4pdaKL/SUO/ZALOOAhApAngcYdkQgLzIqJfRRB8LBKkEZI7eQRAWQUMWAQ4PbTsPY2OaSXNKoKKJge4PgOmNYiA57b/NtfSN8SY6YvgIxDEqYaWEYWRSY1AdorsWo7ohbFgAPL7aDLQv7HQsPQHFDFLQw2PCHBPPDCwojaCBHGwijew7PUVQopjAvIvdjAnbjcvc0Q0UoTVC0UoBvKU5vUI+1dvdnZ1JTN1VJXMAXD1IXdOMfH1GSLo6MKKPEiLdUo0K3MNVUiNbsONXUWgMEgFFNVnL1PY+yOLSIJeYLYdGtItYrXbfYcLEhDQeuUoSbWCIQXdM2QVQiQpZdJMjUzgRKAdRAN0doadLbTtZMzQWIaAjgQsgIks6dSKC8E9HiJg28N43rddTdQbXdFgV9U7T9b9FyX9W7bqe7KgXwZQ0DX0DkHkhfdgL4sIeEc/bEP9BgADL6DQzk/QlrN8cPfkkHdDIw8HEwyHcwgjSwiU6wiTOwqjLPIVHPeUlw1jPHEvTwrU/Mj8y0a0EYLHIGXUiTfUqlMItnSI8Eu2N1S0kfG0lfcfCXLBM4ThetOMzAT0SPLbWYLCbM5Qt4t8Ssl3YMMtTfMACyXAAuXUSbFdEi4MMAdgHfLCcQKQSi4i0iki1CKQXfaEqrIUKi1iui2tSfZigQai0oNiqxQvAgVM29FimiyIcSzixi63DrfSO9HbJJLgAcOgVtSNUCSIBY2NXnf8SfLSLOV00oK0G0P82eefQnGQh3b5IchGEcjMccx7JRP5XMGc6w9Qjk4PbQ9It5PQ9CwU4w53EU6HC88UzqJ7ac6Aq4/cncn2MHOPUwqHJPBCDSUzVAXvWIRHWwzPcouUzHBUko4vFUsvGEL8zUgTSsgI4oIIoCu1GkUCzvCC76OIvTGCpMYskgd5HMl8I0ec8iLbUik0NCkPJNDTd7BNbYuRGYPoFEtLSM/NaMsLGGAayyRKT3asP7IYMMksdQWEZCpJIkfM1rS/ckLhX3ERYsyULAMYkeEYObTCzxSBUad5IUVcYeDa0IEElqZSgEOK25YRacewEs+68SyATsQ4fgLMsoASNMZMT5CcSXM4DSRLY6lLN40a+NToqKfikVOS8YvbdbFLMYEsIfGiNQBgGuEgEuCYcQPodsxyrAWfSAAABg0DOnRDq2s3WBeuzNB2aEoApqiiJpLFGvUHhBRBKD6rRGhonEQuUhPmdylymVkE+sjCH0Skeq6NQAFsgS6kOF8BhnVtEXuGeF3IhHzHoD+vIlWHGXgh/WmS0iJsLwYsal+GbB9klpoARGCnvV23EkSATWJHwu7NjjwAIFKVEFm2RDmsWxbKwAXWeE+yk23PQrnISuCqPKFNPPCvSth1TwR0qPAtuIwAKRTkHw6pxC6oqBGD6uUqDg91iA4VvHkmjzzrCrMKUMUEdOT2kDyulLvMKvo2KufMLzcJLysyVWDLqvSAaqkyZ2AsNIiNaqI2JGwCYmxCruGH8FAXiPTj134TUj9pajnKsyT3ZwaFREYI9U8SrDNuPpy0vtwohE9OGuDX5C1AgKbK5G1M0EtQmWHJdu1sWXTjwAJGwpV0TPlw1121C3JimuODoE+oQG9jDKqUKHgZDyGA9ncTbpeHV0xsfUnG111yhHnGaH7Pt3UWULRBjDjH9mDOd0UM219y8vZPTr8u5ICuzpDxCpPO7rSrFLhyvKHrsN5SJXw0hVqGhRb0uURWaCfQZXCIU1ZT7vZX2XxW5UMGkbcnUAE28kQAE2eV0ToAEzUn4RBQMGkd6qFgYDOmOgcYAGYAAWFxs6dmgAThOjOkOnZqFloD5iFjQCFkOiFj5mRiwliAEHZrOjQA8eOl0dsbBUgD5h8Zce8fZpIGOjcYcfZr5gYAEDOm8YYBCaSeyb5iPAYDce8YAHYpIBAGmAnDoUn9G3GzoBA3HDoIn3HjpaAXHDoBBDo0AEm4mSA+Y+Z6mGAXHaB6njpYJ2aXG0A+Yxn2bNtCU0mcmGBAmhZSnRAXH2aBmEChY6nkZYh6nLnYhImsESAnH2a3H6m3GBArn2m0mzp6nDoGAhZ6naBCmom+ZKR6mimvGxnVmhZvHaBjpYgfGlhjp6mhZjpDozpcn3mIBIBjoSBvG+ZvGBAGBjoBBqy3HTpfmBZnmXGZmGBDoZm0BDoBYpJDojnvGzmXH0WoBnG3GQnVArmzo3HcmzoGBaAuXKQ6mGnYgXHRBDoWnJWTgzo1n2aUnUmMWDHcAjHaATGzHWJaABMA19AgA -->

<!-- internal state end -->
<!-- tips_start -->

---

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

<details>
<summary>❤️ Share</summary>

- [X](https://twitter.com/intent/tweet?text=I%20just%20used%20%40coderabbitai%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20the%20proprietary%20code.%20Check%20it%20out%3A&url=https%3A//coderabbit.ai)
- [Mastodon](https://mastodon.social/share?text=I%20just%20used%20%40coderabbitai%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20the%20proprietary%20code.%20Check%20it%20out%3A%20https%3A%2F%2Fcoderabbit.ai)
- [Reddit](https://www.reddit.com/submit?title=Great%20tool%20for%20code%20review%20-%20CodeRabbit&text=I%20just%20used%20CodeRabbit%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20proprietary%20code.%20Check%20it%20out%3A%20https%3A//coderabbit.ai)
- [LinkedIn](https://www.linkedin.com/sharing/share-offsite/?url=https%3A%2F%2Fcoderabbit.ai&mini=true&title=Great%20tool%20for%20code%20review%20-%20CodeRabbit&summary=I%20just%20used%20CodeRabbit%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20proprietary%20code)

</details>

<details>
<summary>🪧 Tips</summary>

### Chat

There are 3 ways to chat with [CodeRabbit](https://coderabbit.ai?utm_source=oss&utm_medium=github&utm_campaign=bootstrap-vue-next/bootstrap-vue-next&utm_content=2671):

- Review comments: Directly reply to a review comment made by CodeRabbit. Example:
  - `I pushed a fix in commit <commit_id>, please review it.`
  - `Explain this complex logic.`
  - `Open a follow-up GitHub issue for this discussion.`
- Files and specific lines of code (under the "Files changed" tab): Tag `@coderabbitai` in a new review comment at the desired location with your query. Examples:
  - `@coderabbitai explain this code block.`
  -	`@coderabbitai modularize this function.`
- PR comments: Tag `@coderabbitai` in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
  - `@coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.`
  - `@coderabbitai read src/utils.ts and explain its main purpose.`
  - `@coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.`
  - `@coderabbitai help me debug CodeRabbit configuration file.`

### Support

Need help? Create a ticket on our [support page](https://www.coderabbit.ai/contact-us/support) for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

### CodeRabbit Commands (Invoked using PR comments)

- `@coderabbitai pause` to pause the reviews on a PR.
- `@coderabbitai resume` to resume the paused reviews.
- `@coderabbitai review` to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
- `@coderabbitai full review` to do a full review from scratch and review all the files again.
- `@coderabbitai summary` to regenerate the summary of the PR.
- `@coderabbitai generate docstrings` to [generate docstrings](https://docs.coderabbit.ai/finishing-touches/docstrings) for this PR.
- `@coderabbitai generate sequence diagram` to generate a sequence diagram of the changes in this PR.
- `@coderabbitai resolve` resolve all the CodeRabbit review comments.
- `@coderabbitai configuration` to show the current CodeRabbit configuration for the repository.
- `@coderabbitai help` to get help.

### Other keywords and placeholders

- Add `@coderabbitai ignore` anywhere in the PR description to prevent this PR from being reviewed.
- Add `@coderabbitai summary` to generate the high-level summary at a specific location in the PR description.
- Add `@coderabbitai` anywhere in the PR title to generate the title automatically.

### CodeRabbit Configuration File (`.coderabbit.yaml`)

- You can programmatically configure CodeRabbit by adding a `.coderabbit.yaml` file to the root of your repository.
- Please see the [configuration documentation](https://docs.coderabbit.ai/guides/configure-coderabbit) for more information.
- If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: `# yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json`

### Documentation and Community

- Visit our [Documentation](https://docs.coderabbit.ai) for detailed information on how to use CodeRabbit.
- Join our [Discord Community](http://discord.gg/coderabbit) to get help, request features, and share feedback.
- Follow us on [X/Twitter](https://twitter.com/coderabbitai) for updates and announcements.

</details>

<!-- tips_end -->
author_association: CONTRIBUTOR
user: login: unstoppablecarl
created_at: 2025-04-27T17:25:49Z
body: related to #1885 


author_association: CONTRIBUTOR
user: login: pkg-pr-new[bot]
created_at: 2025-04-28T15:58:37Z
body: 
[bsvn-vite-ts](https://pkg.pr.new/template/60e83bd3-053e-4652-bd92-bd4755800903)


```
npm i https://pkg.pr.new/bootstrap-vue-next/bootstrap-vue-next@2671
```


```
npm i https://pkg.pr.new/bootstrap-vue-next/bootstrap-vue-next/@bootstrap-vue-next/nuxt@2671
```


_commit: <a href="https://github.com/bootstrap-vue-next/bootstrap-vue-next/runs/42713433558"><code>86426bb</code></a>_

author_association: NONE
user: login: xvaara
created_at: 2025-04-28T20:58:06Z
body: also in #2556 I tested a way to get the css var from modal. Now that we are doing this we should add the --bs-modal-zindex and --bs-toast-max-width var from variables.scss to the :root 

I don't think we need to support custom bootstrap #{$prefix} values? 


author_association: CONTRIBUTOR
user: login: xvaara
created_at: 2025-05-22T13:17:42Z
body: @coderabbitai full review
author_association: CONTRIBUTOR
user: login: coderabbitai[bot]
created_at: 2025-05-22T13:17:47Z
body: <!-- This is an auto-generated reply by CodeRabbit -->
<details>
<summary>✅ Actions performed</summary>

Full review triggered.

</details>
author_association: CONTRIBUTOR