diff --git a/osu.Game/Graphics/Containers/SectionsContainer.cs b/osu.Game/Graphics/Containers/SectionsContainer.cs
index 8ab146efe7..76492cab55 100644
--- a/osu.Game/Graphics/Containers/SectionsContainer.cs
+++ b/osu.Game/Graphics/Containers/SectionsContainer.cs
@@ -22,7 +22,8 @@ namespace osu.Game.Graphics.Containers
         where T : Drawable
     {
         public Bindable<T> SelectedSection { get; } = new Bindable<T>();
-        private Drawable lastClickedSection;
+
+        private T lastClickedSection;
 
         public Drawable ExpandableHeader
         {
@@ -144,10 +145,25 @@ namespace osu.Game.Graphics.Containers
             footerHeight = null;
         }
 
-        public void ScrollTo(Drawable section)
+        public void ScrollTo(Drawable target)
         {
-            lastClickedSection = section;
-            scrollContainer.ScrollTo(scrollContainer.GetChildPosInContent(section) - scrollContainer.DisplayableContent * scroll_y_centre - (FixedHeader?.BoundingBox.Height ?? 0));
+            lastKnownScroll = null;
+
+            float fixedHeaderSize = FixedHeader?.BoundingBox.Height ?? 0;
+
+            // implementation similar to ScrollIntoView but a bit more nuanced.
+            float top = scrollContainer.GetChildPosInContent(target);
+
+            float bottomScrollExtent = scrollContainer.ScrollableExtent - fixedHeaderSize;
+            float scrollTarget = top - fixedHeaderSize - scrollContainer.DisplayableContent * scroll_y_centre;
+
+            if (scrollTarget > bottomScrollExtent)
+                scrollContainer.ScrollToEnd();
+            else
+                scrollContainer.ScrollTo(scrollTarget);
+
+            if (target is T section)
+                lastClickedSection = section;
         }
 
         public void ScrollToTop() => scrollContainer.ScrollTo(0);
@@ -170,13 +186,22 @@ namespace osu.Game.Graphics.Containers
 
             if (source == InvalidationSource.Child && (invalidation & Invalidation.DrawSize) != 0)
             {
-                lastKnownScroll = null;
+                InvalidateScrollPosition();
                 result = true;
             }
 
             return result;
         }
 
+        protected void InvalidateScrollPosition()
+        {
+            Schedule(() =>
+            {
+                lastKnownScroll = null;
+                lastClickedSection = null;
+            });
+        }
+
         protected override void UpdateAfterChildren()
         {
             base.UpdateAfterChildren();
@@ -224,15 +249,19 @@ namespace osu.Game.Graphics.Containers
 
                 float scrollCentre = fixedHeaderSize + scrollContainer.DisplayableContent * scroll_y_centre + selectionLenienceAboveSection;
 
-                if (Precision.AlmostBigger(0, scrollContainer.Current))
-                    SelectedSection.Value = lastClickedSection as T ?? Children.FirstOrDefault();
+                var presentChildren = Children.Where(c => c.IsPresent);
+
+                if (lastClickedSection != null)
+                    SelectedSection.Value = lastClickedSection;
+                else if (Precision.AlmostBigger(0, scrollContainer.Current))
+                    SelectedSection.Value = presentChildren.FirstOrDefault();
                 else if (Precision.AlmostBigger(scrollContainer.Current, scrollContainer.ScrollableExtent))
-                    SelectedSection.Value = lastClickedSection as T ?? Children.LastOrDefault();
+                    SelectedSection.Value = presentChildren.LastOrDefault();
                 else
                 {
-                    SelectedSection.Value = Children
+                    SelectedSection.Value = presentChildren
                                             .TakeWhile(section => scrollContainer.GetChildPosInContent(section) - currentScroll - scrollCentre <= 0)
-                                            .LastOrDefault() ?? Children.FirstOrDefault();
+                                            .LastOrDefault() ?? presentChildren.FirstOrDefault();
                 }
             }
         }
diff --git a/osu.Game/Graphics/Containers/UserTrackingScrollContainer.cs b/osu.Game/Graphics/Containers/UserTrackingScrollContainer.cs
index 17506ce0f5..0561051e35 100644
--- a/osu.Game/Graphics/Containers/UserTrackingScrollContainer.cs
+++ b/osu.Game/Graphics/Containers/UserTrackingScrollContainer.cs
@@ -42,6 +42,12 @@ namespace osu.Game.Graphics.Containers
             base.OnUserScroll(value, animated, distanceDecay);
         }
 
+        public new void ScrollIntoView(Drawable target, bool animated = true)
+        {
+            UserScrolling = false;
+            base.ScrollIntoView(target, animated);
+        }
+
         public new void ScrollTo(float value, bool animated = true, double? distanceDecay = null)
         {
             UserScrolling = false;
diff --git a/osu.Game/Overlays/Settings/SettingsSection.cs b/osu.Game/Overlays/Settings/SettingsSection.cs
index f993a46dc6..6f167bf059 100644
--- a/osu.Game/Overlays/Settings/SettingsSection.cs
+++ b/osu.Game/Overlays/Settings/SettingsSection.cs
@@ -4,9 +4,11 @@
 using System.Collections.Generic;
 using System.Linq;
 using osu.Framework.Allocation;
+using osu.Framework.Bindables;
 using osu.Framework.Graphics;
 using osu.Framework.Graphics.Containers;
 using osu.Framework.Graphics.Shapes;
+using osu.Framework.Input.Events;
 using osu.Framework.Localisation;
 using osu.Game.Graphics;
 using osu.Game.Graphics.Sprites;
@@ -19,6 +21,10 @@ namespace osu.Game.Overlays.Settings
         protected FillFlowContainer FlowContent;
         protected override Container<Drawable> Content => FlowContent;
 
+        private IBindable<SettingsSection> selectedSection;
+
+        private OsuSpriteText header;
+
         public abstract Drawable CreateIcon();
         public abstract LocalisableString Header { get; }
 
@@ -36,6 +42,9 @@ namespace osu.Game.Overlays.Settings
 
         public bool FilteringActive { get; set; }
 
+        [Resolved]
+        private SettingsPanel settingsPanel { get; set; }
+
         protected SettingsSection()
         {
             Margin = new MarginPadding { Top = margin };
@@ -61,6 +70,7 @@ namespace osu.Game.Overlays.Settings
             {
                 new Box
                 {
+                    Name = "separator",
                     Colour = new Color4(0, 0, 0, 255),
                     RelativeSizeAxes = Axes.X,
                     Height = border_size,
@@ -76,7 +86,7 @@ namespace osu.Game.Overlays.Settings
                     AutoSizeAxes = Axes.Y,
                     Children = new Drawable[]
                     {
-                        new OsuSpriteText
+                        header = new OsuSpriteText
                         {
                             Font = OsuFont.GetFont(size: header_size),
                             Text = Header,
@@ -91,6 +101,51 @@ namespace osu.Game.Overlays.Settings
                     }
                 },
             });
+
+            selectedSection = settingsPanel.CurrentSection.GetBoundCopy();
+            selectedSection.BindValueChanged(_ => updateContentFade(), true);
+        }
+
+        private bool isCurrentSection => selectedSection.Value == this;
+
+        protected override bool OnHover(HoverEvent e)
+        {
+            updateContentFade();
+            return base.OnHover(e);
+        }
+
+        protected override void OnHoverLost(HoverLostEvent e)
+        {
+            updateContentFade();
+            base.OnHoverLost(e);
+        }
+
+        protected override bool OnClick(ClickEvent e)
+        {
+            if (!isCurrentSection)
+                settingsPanel.SectionsContainer.ScrollTo(this);
+
+            return base.OnClick(e);
+        }
+
+        protected override bool ShouldBeConsideredForInput(Drawable child) =>
+            // only the current section should accept input.
+            // this provides the behaviour of the first click scrolling the target section to the centre of the screen.
+            isCurrentSection;
+
+        private void updateContentFade()
+        {
+            float contentFade = 1;
+            float headerFade = 1;
+
+            if (!isCurrentSection)
+            {
+                contentFade = 0.25f;
+                headerFade = IsHovered ? 0.5f : 0.25f;
+            }
+
+            header.FadeTo(headerFade, 500, Easing.OutQuint);
+            FlowContent.FadeTo(contentFade, 500, Easing.OutQuint);
         }
     }
 }
diff --git a/osu.Game/Overlays/SettingsPanel.cs b/osu.Game/Overlays/SettingsPanel.cs
index 376e36ea4e..e4e76592d8 100644
--- a/osu.Game/Overlays/SettingsPanel.cs
+++ b/osu.Game/Overlays/SettingsPanel.cs
@@ -8,6 +8,7 @@ using System.Threading.Tasks;
 using osuTK;
 using osuTK.Graphics;
 using osu.Framework.Allocation;
+using osu.Framework.Bindables;
 using osu.Framework.Extensions.IEnumerableExtensions;
 using osu.Framework.Graphics;
 using osu.Framework.Graphics.Containers;
@@ -21,6 +22,7 @@ using osu.Game.Overlays.Settings;
 
 namespace osu.Game.Overlays
 {
+    [Cached]
     public abstract class SettingsPanel : OsuFocusedOverlayContainer
     {
         public const float CONTENT_MARGINS = 15;
@@ -46,7 +48,7 @@ namespace osu.Game.Overlays
         protected Sidebar Sidebar;
         private SidebarButton selectedSidebarButton;
 
-        protected SettingsSectionsContainer SectionsContainer;
+        public SettingsSectionsContainer SectionsContainer { get; private set; }
 
         private SeekLimitedSearchTextBox searchTextBox;
 
@@ -65,6 +67,8 @@ namespace osu.Game.Overlays
 
         private Task sectionsLoadingTask;
 
+        public IBindable<SettingsSection> CurrentSection = new Bindable<SettingsSection>();
+
         protected SettingsPanel(bool showSidebar)
         {
             this.showSidebar = showSidebar;
@@ -105,6 +109,7 @@ namespace osu.Game.Overlays
                 Masking = true,
                 RelativeSizeAxes = Axes.Both,
                 ExpandableHeader = CreateHeader(),
+                SelectedSection = { BindTarget = CurrentSection },
                 FixedHeader = searchTextBox = new SeekLimitedSearchTextBox
                 {
                     RelativeSizeAxes = Axes.X,
@@ -238,8 +243,10 @@ namespace osu.Game.Overlays
                     if (selectedSidebarButton != null)
                         selectedSidebarButton.Selected = false;
 
-                    selectedSidebarButton = Sidebar.Children.Single(b => b.Section == section.NewValue);
-                    selectedSidebarButton.Selected = true;
+                    selectedSidebarButton = Sidebar.Children.FirstOrDefault(b => b.Section == section.NewValue);
+
+                    if (selectedSidebarButton != null)
+                        selectedSidebarButton.Selected = true;
                 }, true);
             });
         }
@@ -273,6 +280,16 @@ namespace osu.Game.Overlays
         {
             public SearchContainer<SettingsSection> SearchContainer;
 
+            public string SearchTerm
+            {
+                get => SearchContainer.SearchTerm;
+                set
+                {
+                    SearchContainer.SearchTerm = value;
+                    InvalidateScrollPosition();
+                }
+            }
+
             protected override FlowContainer<SettingsSection> CreateScrollContentContainer()
                 => SearchContainer = new SearchContainer<SettingsSection>
                 {