diff --git a/osu.Game/Graphics/UserInterface/OsuContextMenu.cs b/osu.Game/Graphics/UserInterface/OsuContextMenu.cs
index 433d37834f8c..72ffde357493 100644
--- a/osu.Game/Graphics/UserInterface/OsuContextMenu.cs
+++ b/osu.Game/Graphics/UserInterface/OsuContextMenu.cs
@@ -12,17 +12,11 @@ namespace osu.Game.Graphics.UserInterface
 {
     public partial class OsuContextMenu : OsuMenu
     {
-        private const int fade_duration = 250;
-
         [Resolved]
         private OsuMenuSamples menuSamples { get; set; } = null!;
 
-        // todo: this shouldn't be required after https://github.com/ppy/osu-framework/issues/4519 is fixed.
-        private bool wasOpened;
-        private readonly bool playClickSample;
-
-        public OsuContextMenu(bool playClickSample = false)
-            : base(Direction.Vertical)
+        public OsuContextMenu(bool playSamples)
+            : base(Direction.Vertical, topLevelMenu: false, playSamples)
         {
             MaskingContainer.CornerRadius = 5;
             MaskingContainer.EdgeEffect = new EdgeEffectParameters
@@ -35,8 +29,6 @@ public OsuContextMenu(bool playClickSample = false)
             ItemsContainer.Padding = new MarginPadding { Vertical = DrawableOsuMenuItem.MARGIN_VERTICAL };
 
             MaxHeight = 250;
-
-            this.playClickSample = playClickSample;
         }
 
         [BackgroundDependencyLoader]
@@ -47,26 +39,12 @@ private void load(OsuColour colours)
 
         protected override void AnimateOpen()
         {
-            wasOpened = true;
-            this.FadeIn(fade_duration, Easing.OutQuint);
-
-            if (!playClickSample)
-                return;
-
-            menuSamples.PlayClickSample();
-            menuSamples.PlayOpenSample();
-        }
-
-        protected override void AnimateClose()
-        {
-            this.FadeOut(fade_duration, Easing.OutQuint);
-
-            if (wasOpened)
-                menuSamples.PlayCloseSample();
+            if (PlaySamples && !WasOpened)
+                menuSamples.PlayClickSample();
 
-            wasOpened = false;
+            base.AnimateOpen();
         }
 
-        protected override Menu CreateSubMenu() => new OsuContextMenu();
+        protected override Menu CreateSubMenu() => new OsuContextMenu(false); // sub menu samples are handled by OsuMenu.OnSubmenuOpen.
     }
 }
diff --git a/osu.Game/Graphics/UserInterface/OsuMenu.cs b/osu.Game/Graphics/UserInterface/OsuMenu.cs
index 7cc1bab25f6d..11d9000dfa06 100644
--- a/osu.Game/Graphics/UserInterface/OsuMenu.cs
+++ b/osu.Game/Graphics/UserInterface/OsuMenu.cs
@@ -18,21 +18,32 @@ namespace osu.Game.Graphics.UserInterface
 {
     public partial class OsuMenu : Menu
     {
+        protected const double DELAY_BEFORE_FADE_OUT = 50;
+        protected const double FADE_DURATION = 280;
+
         // todo: this shouldn't be required after https://github.com/ppy/osu-framework/issues/4519 is fixed.
-        private bool wasOpened;
+        protected bool WasOpened { get; private set; }
+
+        public bool PlaySamples { get; }
 
         [Resolved]
         private OsuMenuSamples menuSamples { get; set; } = null!;
 
         public OsuMenu(Direction direction, bool topLevelMenu = false)
+            : this(direction, topLevelMenu, playSamples: !topLevelMenu)
+        {
+        }
+
+        protected OsuMenu(Direction direction, bool topLevelMenu, bool playSamples)
             : base(direction, topLevelMenu)
         {
+            PlaySamples = playSamples;
             BackgroundColour = Color4.Black.Opacity(0.5f);
 
             MaskingContainer.CornerRadius = 4;
             ItemsContainer.Padding = new MarginPadding(5);
 
-            OnSubmenuOpen += _ => { menuSamples?.PlaySubOpenSample(); };
+            OnSubmenuOpen += _ => menuSamples?.PlaySubOpenSample();
         }
 
         protected override void Update()
@@ -56,20 +67,22 @@ protected override void Update()
 
         protected override void AnimateOpen()
         {
-            if (!TopLevelMenu && !wasOpened)
+            if (PlaySamples && !WasOpened)
                 menuSamples?.PlayOpenSample();
 
-            this.FadeIn(300, Easing.OutQuint);
-            wasOpened = true;
+            WasOpened = true;
+            this.FadeIn(FADE_DURATION, Easing.OutQuint);
         }
 
         protected override void AnimateClose()
         {
-            if (!TopLevelMenu && wasOpened)
+            if (PlaySamples && WasOpened)
                 menuSamples?.PlayCloseSample();
 
-            this.FadeOut(300, Easing.OutQuint);
-            wasOpened = false;
+            this.Delay(DELAY_BEFORE_FADE_OUT)
+                .FadeOut(FADE_DURATION, Easing.OutQuint);
+
+            WasOpened = false;
         }
 
         protected override void UpdateSize(Vector2 newSize)
@@ -77,12 +90,21 @@ protected override void UpdateSize(Vector2 newSize)
             if (Direction == Direction.Vertical)
             {
                 Width = newSize.X;
-                this.ResizeHeightTo(newSize.Y, 300, Easing.OutQuint);
+
+                if (newSize.Y > 0)
+                    this.ResizeHeightTo(newSize.Y, 300, Easing.OutQuint);
+                else
+                    // Delay until the fade out finishes from AnimateClose.
+                    this.Delay(DELAY_BEFORE_FADE_OUT + FADE_DURATION).ResizeHeightTo(0);
             }
             else
             {
                 Height = newSize.Y;
-                this.ResizeWidthTo(newSize.X, 300, Easing.OutQuint);
+                if (newSize.X > 0)
+                    this.ResizeWidthTo(newSize.X, 300, Easing.OutQuint);
+                else
+                    // Delay until the fade out finishes from AnimateClose.
+                    this.Delay(DELAY_BEFORE_FADE_OUT + FADE_DURATION).ResizeWidthTo(0);
             }
         }