diff --git a/src/components.d.ts b/src/components.d.ts
index bea93282..ef219e38 100644
--- a/src/components.d.ts
+++ b/src/components.d.ts
@@ -14,7 +14,7 @@ export namespace Components {
         /**
           * Hides the alert
          */
-        "hide": () => Promise<boolean>;
+        "hide": () => Promise<void>;
         /**
           * Indicates whether or not the alert is open. You can use this in lieu of the show/hide methods.
          */
@@ -22,7 +22,7 @@ export namespace Components {
         /**
           * Shows the alert.
          */
-        "show": () => Promise<boolean>;
+        "show": () => Promise<void>;
         /**
           * The type of alert.
          */
@@ -276,7 +276,7 @@ export namespace Components {
         /**
           * Hides the alert
          */
-        "hide": () => Promise<boolean>;
+        "hide": () => Promise<void>;
         /**
           * Indicates whether or not the details is open. You can use this in lieu of the show/hide methods.
          */
@@ -284,7 +284,7 @@ export namespace Components {
         /**
           * Shows the alert.
          */
-        "show": () => Promise<boolean>;
+        "show": () => Promise<void>;
         /**
           * The summary to show in the details header. If you need to display HTML, use the `summary` slot instead.
          */
@@ -294,7 +294,7 @@ export namespace Components {
         /**
           * Hides the dialog
          */
-        "hide": () => Promise<boolean>;
+        "hide": () => Promise<void>;
         /**
           * The dialog's label as displayed in the header. You should always include a relevant label even when using `no-header`, as it is required for proper accessibility.
          */
@@ -310,7 +310,7 @@ export namespace Components {
         /**
           * Shows the dialog
          */
-        "show": () => Promise<boolean>;
+        "show": () => Promise<void>;
     }
     interface SlDrawer {
         /**
@@ -320,7 +320,7 @@ export namespace Components {
         /**
           * Hides the drawer
          */
-        "hide": () => Promise<boolean>;
+        "hide": () => Promise<void>;
         /**
           * The drawer's label as displayed in the header. You should always include a relevant label even when using `no-header`, as it is required for proper accessibility.
          */
@@ -340,7 +340,7 @@ export namespace Components {
         /**
           * Shows the drawer
          */
-        "show": () => Promise<boolean>;
+        "show": () => Promise<void>;
     }
     interface SlDropdown {
         /**
@@ -358,7 +358,7 @@ export namespace Components {
         /**
           * Hides the dropdown panel
          */
-        "hide": () => Promise<boolean>;
+        "hide": () => Promise<void>;
         /**
           * Indicates whether or not the dropdown is open. You can use this in lieu of the show/hide methods.
          */
@@ -381,7 +381,7 @@ export namespace Components {
         /**
           * Shows the dropdown panel
          */
-        "show": () => Promise<boolean>;
+        "show": () => Promise<void>;
         /**
           * The distance in pixels from which to offset the panel along its trigger.
          */
@@ -963,7 +963,7 @@ export namespace Components {
         /**
           * Shows the tooltip.
          */
-        "hide": () => Promise<boolean>;
+        "hide": () => Promise<void>;
         /**
           * Indicates whether or not the tooltip is open. You can use this in lieu of the show/hide methods.
          */
@@ -986,7 +986,7 @@ export namespace Components {
         /**
           * Shows the tooltip.
          */
-        "show": () => Promise<boolean>;
+        "show": () => Promise<void>;
         /**
           * The distance in pixels from which to offset the tooltip along its target.
          */
diff --git a/src/components/alert/alert.tsx b/src/components/alert/alert.tsx
index 78fa7c46..4880c1e4 100644
--- a/src/components/alert/alert.tsx
+++ b/src/components/alert/alert.tsx
@@ -20,6 +20,7 @@ import { Component, Element, Event, EventEmitter, Host, Method, Prop, Watch, h }
 })
 export class Tab {
   alert: HTMLElement;
+  isShowing = false;
 
   @Element() host: HTMLSlAlertElement;
 
@@ -64,28 +65,38 @@ export class Tab {
   /** Shows the alert. */
   @Method()
   async show() {
-    if (this.open) return;
+    // Prevent subsequent calls to the method, whether manually or triggered by the `open` watcher
+    if (this.isShowing) {
+      return;
+    }
 
     const slShow = this.slShow.emit();
     if (slShow.defaultPrevented) {
-      return false;
+      this.open = false;
+      return;
     }
 
     this.host.hidden = false;
     this.host.clientWidth; // force a reflow
+    this.isShowing = true;
     this.open = true;
   }
 
   /** Hides the alert */
   @Method()
   async hide() {
-    if (!this.open) return;
+    // Prevent subsequent calls to the method, whether manually or triggered by the `open` watcher
+    if (!this.isShowing) {
+      return;
+    }
 
     const slHide = this.slHide.emit();
     if (slHide.defaultPrevented) {
-      return false;
+      this.open = true;
+      return;
     }
 
+    this.isShowing = false;
     this.open = false;
   }
 
diff --git a/src/components/details/details.tsx b/src/components/details/details.tsx
index 204a0d81..ce6070a4 100644
--- a/src/components/details/details.tsx
+++ b/src/components/details/details.tsx
@@ -23,10 +23,11 @@ let id = 0;
   shadow: true
 })
 export class Details {
+  body: HTMLElement;
+  componentId = `details-${++id}`;
   details: HTMLElement;
   header: HTMLElement;
-  componentId = `details-${++id}`;
-  body: HTMLElement;
+  isShowing = false;
 
   /** Indicates whether or not the details is open. You can use this in lieu of the show/hide methods. */
   @Prop({ mutable: true, reflect: true }) open = false;
@@ -76,11 +77,15 @@ export class Details {
   /** Shows the alert. */
   @Method()
   async show() {
-    if (this.open) return;
+    // Prevent subsequent calls to the method, whether manually or triggered by the `open` watcher
+    if (this.isShowing) {
+      return;
+    }
 
     const slShow = this.slShow.emit();
     if (slShow.defaultPrevented) {
-      return false;
+      this.open = false;
+      return;
     }
 
     if (this.body.scrollHeight === 0) {
@@ -93,17 +98,22 @@ export class Details {
       this.body.style.overflow = 'hidden';
     }
 
+    this.isShowing = true;
     this.open = true;
   }
 
   /** Hides the alert */
   @Method()
   async hide() {
-    if (!this.open) return;
+    // Prevent subsequent calls to the method, whether manually or triggered by the `open` watcher
+    if (!this.isShowing) {
+      return;
+    }
 
     const slHide = this.slHide.emit();
     if (slHide.defaultPrevented) {
-      return false;
+      this.open = true;
+      return;
     }
 
     // We can't transition out of `height: auto`, so let's set it to the current height first
@@ -115,6 +125,7 @@ export class Details {
       this.body.style.height = '0';
     });
 
+    this.isShowing = false;
     this.open = false;
   }
 
diff --git a/src/components/dialog/dialog.tsx b/src/components/dialog/dialog.tsx
index 007a9b41..5b67e5a8 100644
--- a/src/components/dialog/dialog.tsx
+++ b/src/components/dialog/dialog.tsx
@@ -27,9 +27,10 @@ let id = 0;
   shadow: true
 })
 export class Dialog {
-  panel: HTMLElement;
-  dialog: HTMLElement;
   componentId = `dialog-${++id}`;
+  dialog: HTMLElement;
+  isShowing = false;
+  panel: HTMLElement;
 
   @Element() host: HTMLSlDialogElement;
 
@@ -99,16 +100,21 @@ export class Dialog {
   /** Shows the dialog */
   @Method()
   async show() {
-    if (this.open) return;
+    // Prevent subsequent calls to the method, whether manually or triggered by the `open` watcher
+    if (this.isShowing) {
+      return;
+    }
 
     const slShow = this.slShow.emit();
     if (slShow.defaultPrevented) {
-      return false;
+      this.open = false;
+      return;
     }
 
     this.dialog.hidden = false;
     this.host.clientWidth; // force a reflow
-    requestAnimationFrame(() => (this.open = true));
+    this.isShowing = true;
+    this.open = true;
 
     lockBodyScrolling(this.host);
     document.addEventListener('focusin', this.handleDocumentFocusIn);
@@ -117,13 +123,18 @@ export class Dialog {
   /** Hides the dialog */
   @Method()
   async hide() {
-    if (!this.open) return;
+    // Prevent subsequent calls to the method, whether manually or triggered by the `open` watcher
+    if (!this.isShowing) {
+      return;
+    }
 
     const slHide = this.slHide.emit();
     if (slHide.defaultPrevented) {
-      return false;
+      this.open = true;
+      return;
     }
 
+    this.isShowing = false;
     this.open = false;
 
     unlockBodyScrolling(this.host);
diff --git a/src/components/drawer/drawer.tsx b/src/components/drawer/drawer.tsx
index 7066956a..6f83d645 100644
--- a/src/components/drawer/drawer.tsx
+++ b/src/components/drawer/drawer.tsx
@@ -26,9 +26,10 @@ let id = 0;
   shadow: true
 })
 export class Drawer {
-  panel: HTMLElement;
-  drawer: HTMLElement;
   componentId = `drawer-${++id}`;
+  drawer: HTMLElement;
+  isShowing = false;
+  panel: HTMLElement;
 
   @Element() host: HTMLSlDrawerElement;
 
@@ -107,16 +108,21 @@ export class Drawer {
   /** Shows the drawer */
   @Method()
   async show() {
-    if (this.open) return;
+    // Prevent subsequent calls to the method, whether manually or triggered by the `open` watcher
+    if (this.isShowing) {
+      return;
+    }
 
     const slShow = this.slShow.emit();
     if (slShow.defaultPrevented) {
-      return false;
+      this.open = false;
+      return;
     }
 
     this.drawer.hidden = false;
     this.host.clientWidth; // force a reflow
-    requestAnimationFrame(() => (this.open = true));
+    this.isShowing = true;
+    this.open = true;
 
     // Lock body scrolling only if the drawer isn't contained
     if (!this.contained) {
@@ -129,17 +135,21 @@ export class Drawer {
   /** Hides the drawer */
   @Method()
   async hide() {
-    if (!this.open) return;
+    // Prevent subsequent calls to the method, whether manually or triggered by the `open` watcher
+    if (!this.isShowing) {
+      return;
+    }
 
     const slHide = this.slHide.emit();
     if (slHide.defaultPrevented) {
-      return false;
+      this.open = true;
+      return;
     }
 
+    this.isShowing = false;
     this.open = false;
 
     unlockBodyScrolling(this.host);
-
     document.removeEventListener('focusin', this.handleDocumentFocusIn);
   }
 
diff --git a/src/components/dropdown/dropdown.tsx b/src/components/dropdown/dropdown.tsx
index 9a6d0cb3..c0097574 100644
--- a/src/components/dropdown/dropdown.tsx
+++ b/src/components/dropdown/dropdown.tsx
@@ -23,6 +23,7 @@ let id = 0;
 })
 export class Dropdown {
   componentId = `dropdown-${++id}`;
+  isShowing = false;
   panel: HTMLElement;
   popover: Popover;
   trigger: HTMLElement;
@@ -123,11 +124,15 @@ export class Dropdown {
   /** Shows the dropdown panel */
   @Method()
   async show() {
-    if (this.open) return;
+    // Prevent subsequent calls to the method, whether manually or triggered by the `open` watcher
+    if (this.isShowing) {
+      return;
+    }
 
     const slShow = this.slShow.emit();
     if (slShow.defaultPrevented) {
-      return false;
+      this.open = false;
+      return;
     }
 
     this.panel.addEventListener('slActivate', this.handleMenuItemActivate);
@@ -135,18 +140,23 @@ export class Dropdown {
     document.addEventListener('mousedown', this.handleDocumentMouseDown);
     document.addEventListener('keydown', this.handleDocumentKeyDown);
 
-    this.popover.show();
+    this.isShowing = true;
     this.open = true;
+    this.popover.show();
   }
 
   /** Hides the dropdown panel */
   @Method()
   async hide() {
-    if (!this.open) return;
+    // Prevent subsequent calls to the method, whether manually or triggered by the `open` watcher
+    if (!this.isShowing) {
+      return;
+    }
 
     const slHide = this.slHide.emit();
     if (slHide.defaultPrevented) {
-      return false;
+      this.open = true;
+      return;
     }
 
     this.panel.removeEventListener('slActivate', this.handleMenuItemActivate);
@@ -154,8 +164,9 @@ export class Dropdown {
     document.removeEventListener('mousedown', this.handleDocumentMouseDown);
     document.removeEventListener('keydown', this.handleDocumentKeyDown);
 
-    this.popover.hide();
+    this.isShowing = false;
     this.open = false;
+    this.popover.hide();
   }
 
   focusOnTrigger() {
diff --git a/src/components/tooltip/tooltip.tsx b/src/components/tooltip/tooltip.tsx
index 7fb11247..1056b092 100644
--- a/src/components/tooltip/tooltip.tsx
+++ b/src/components/tooltip/tooltip.tsx
@@ -19,6 +19,7 @@ let id = 0;
 })
 export class Tooltip {
   componentId = `tooltip-${++id}`;
+  isShowing = false;
   popover: Popover;
   target: HTMLElement;
   tooltip: any;
@@ -126,29 +127,39 @@ export class Tooltip {
   /** Shows the tooltip. */
   @Method()
   async show() {
-    if (this.open) return;
+    // Prevent subsequent calls to the method, whether manually or triggered by the `open` watcher
+    if (this.isShowing) {
+      return;
+    }
 
     const slShow = this.slShow.emit();
     if (slShow.defaultPrevented) {
-      return false;
+      this.open = false;
+      return;
     }
 
-    this.popover.show();
+    this.isShowing = true;
     this.open = true;
+    this.popover.show();
   }
 
   /** Shows the tooltip. */
   @Method()
   async hide() {
-    if (!this.open) return;
+    // Prevent subsequent calls to the method, whether manually or triggered by the `open` watcher
+    if (!this.isShowing) {
+      return;
+    }
 
     const slHide = this.slHide.emit();
     if (slHide.defaultPrevented) {
-      return false;
+      this.open = true;
+      return;
     }
 
-    this.popover.hide();
+    this.isShowing = false;
     this.open = false;
+    this.popover.hide();
   }
 
   getTarget() {