Repository: microsoft/terminal (language: cpp)

Issue:
[1.16] Default theme updates
From discussion with @cinnamon-msft et. al.

* [ ] Dark theme by default (was `system`)
* [ ] `tab.background: terminalBackground` 
* [ ] Change the tab row colors back to what they were pre-Controlsv2
  - This point I might be mis-remembering. I think the crux was "make the colors better", and we had freedom to do whatever we wanted. 
  - The current dark tabRow.background is basically the same as the default scheme BG, and maybe that's good, maybe that's bad. 
* [ ] `tabRow.unfocusedBackground` set to something sensible in the default themes as well.


_The 🌶️ rejected ides:_
* Default to mica on
  * Rejected cause this would disable vintage opacity
  * Rejected cause our Mica story isn't fleshed out yet
  * Rejected cause dark tabs in light OS theme with Mica titlebars has pretty low contrast overall.
* ... with a non-acrylic, transparent control, so that you'd get the mica termcontrol body.
  * see all the above.


Discussion / hints:

Fix patch:
diff --git a/src/cascadia/TerminalApp/App.xaml b/src/cascadia/TerminalApp/App.xaml
index 2f025073f97..01b315644cc 100644
--- a/src/cascadia/TerminalApp/App.xaml
+++ b/src/cascadia/TerminalApp/App.xaml
@@ -149,10 +149,13 @@
                                 See GH #12356 for more history on the subject.
                             -->
                             <SolidColorBrush x:Key="TabViewBackground"
-                                             Color="#0a0a0a" />
+                                             Color="#2e2e2e" />
 
                             <StaticResource x:Key="UnfocusedBorderBrush"
                                             ResourceKey="ApplicationPageBackgroundThemeBrush" />
+
+                            <SolidColorBrush x:Key="SettingsUiTabBrush"
+                                             Color="#0c0c0c" />
                         </ResourceDictionary>
 
                         <ResourceDictionary x:Key="Light">
@@ -162,10 +165,13 @@
                                 GH #12398 has more history on this value, as well as GH #12400
                             -->
                             <SolidColorBrush x:Key="TabViewBackground"
-                                             Color="#dadada" />
+                                             Color="#e8e8e8" />
 
                             <StaticResource x:Key="UnfocusedBorderBrush"
                                             ResourceKey="ApplicationPageBackgroundThemeBrush" />
+
+                            <SolidColorBrush x:Key="SettingsUiTabBrush"
+                                             Color="#ffffff" />
                         </ResourceDictionary>
 
                         <ResourceDictionary x:Key="HighContrast">
@@ -183,6 +189,9 @@
                             -->
                             <StaticResource x:Key="TabViewBackground"
                                             ResourceKey="SystemColorButtonFaceColorBrush" />
+
+                            <StaticResource x:Key="SettingsUiTabBrush"
+                                            ResourceKey="SystemControlBackgroundBaseLowBrush" />
                         </ResourceDictionary>
 
                     </ResourceDictionary.ThemeDictionaries>
diff --git a/src/cascadia/TerminalApp/SettingsTab.cpp b/src/cascadia/TerminalApp/SettingsTab.cpp
index 54e48ed1e0d..6ac9bb5e79e 100644
--- a/src/cascadia/TerminalApp/SettingsTab.cpp
+++ b/src/cascadia/TerminalApp/SettingsTab.cpp
@@ -23,9 +23,11 @@ namespace winrt
 
 namespace winrt::TerminalApp::implementation
 {
-    SettingsTab::SettingsTab(MainPage settingsUI)
+    SettingsTab::SettingsTab(MainPage settingsUI,
+                             winrt::Windows::UI::Xaml::ElementTheme requestedTheme)
     {
         Content(settingsUI);
+        _requestedTheme = requestedTheme;
 
         _MakeTabViewItem();
         _CreateContextMenu();
@@ -36,6 +38,10 @@ namespace winrt::TerminalApp::implementation
     {
         auto settingsUI{ Content().as<MainPage>() };
         settingsUI.UpdateSettings(settings);
+
+        // Stash away the current requested theme of the app. We'll need that in
+        // _BackgroundBrush() to do a theme-aware resource lookup
+        _requestedTheme = settings.GlobalSettings().CurrentTheme().RequestedTheme();
     }
 
     // Method Description:
@@ -105,4 +111,16 @@ namespace winrt::TerminalApp::implementation
             TabViewItem().IconSource(IconPathConverter::IconSourceMUX(glyph));
         }
     }
+
+    winrt::Windows::UI::Xaml::Media::Brush SettingsTab::_BackgroundBrush()
+    {
+        // Look up the color we should use for the settings tab item from our
+        // resources. This should only be used for when "terminalBackground" is
+        // requested.
+        static const auto key = winrt::box_value(L"SettingsUiTabBrush");
+        // You can't just do a Application::Current().Resources().TryLookup
+        // lookup, cause the app theme never changes! Do the hacky version
+        // instead.
+        return ThemeLookup(Application::Current().Resources(), _requestedTheme, key).try_as<winrt::Windows::UI::Xaml::Media::Brush>();
+    }
 }
diff --git a/src/cascadia/TerminalApp/SettingsTab.h b/src/cascadia/TerminalApp/SettingsTab.h
index 803ed8cb03e..e9cfc9c8376 100644
--- a/src/cascadia/TerminalApp/SettingsTab.h
+++ b/src/cascadia/TerminalApp/SettingsTab.h
@@ -24,7 +24,8 @@ namespace winrt::TerminalApp::implementation
     struct SettingsTab : SettingsTabT<SettingsTab, TabBase>
     {
     public:
-        SettingsTab(winrt::Microsoft::Terminal::Settings::Editor::MainPage settingsUI);
+        SettingsTab(winrt::Microsoft::Terminal::Settings::Editor::MainPage settingsUI,
+                    winrt::Windows::UI::Xaml::ElementTheme requestedTheme);
 
         void UpdateSettings(Microsoft::Terminal::Settings::Model::CascadiaSettings settings);
         void Focus(winrt::Windows::UI::Xaml::FocusState focusState) override;
@@ -32,7 +33,11 @@ namespace winrt::TerminalApp::implementation
         std::vector<Microsoft::Terminal::Settings::Model::ActionAndArgs> BuildStartupActions() const override;
 
     private:
+        winrt::Windows::UI::Xaml::ElementTheme _requestedTheme;
+
         void _MakeTabViewItem() override;
         winrt::fire_and_forget _CreateIcon();
+
+        virtual winrt::Windows::UI::Xaml::Media::Brush _BackgroundBrush() override;
     };
 }
diff --git a/src/cascadia/TerminalApp/TabBase.cpp b/src/cascadia/TerminalApp/TabBase.cpp
index 167b5008066..dda15f10bc8 100644
--- a/src/cascadia/TerminalApp/TabBase.cpp
+++ b/src/cascadia/TerminalApp/TabBase.cpp
@@ -5,6 +5,8 @@
 #include <LibraryResources.h>
 #include "TabBase.h"
 #include "TabBase.g.cpp"
+#include "Utils.h"
+#include "ColorHelper.h"
 
 using namespace winrt;
 using namespace winrt::Windows::UI::Xaml;
@@ -252,4 +254,291 @@ namespace winrt::TerminalApp::implementation
         });
     }
 
+    std::optional<winrt::Windows::UI::Color> TabBase::GetTabColor()
+    {
+        return std::nullopt;
+    }
+
+    void TabBase::ThemeColor(const winrt::Microsoft::Terminal::Settings::Model::ThemeColor& focused,
+                             const winrt::Microsoft::Terminal::Settings::Model::ThemeColor& unfocused,
+                             const til::color& tabRowColor)
+    {
+        _themeColor = focused;
+        _unfocusedThemeColor = unfocused;
+        _tabRowColor = tabRowColor;
+        _RecalculateAndApplyTabColor();
+    }
+
+    // Method Description:
+    // - This function dispatches a function to the UI thread to recalculate
+    //   what this tab's current background color should be. If a color is set,
+    //   it will apply the given color to the tab's background. Otherwise, it
+    //   will clear the tab's background color.
+    // Arguments:
+    // - <none>
+    // Return Value:
+    // - <none>
+    void TabBase::_RecalculateAndApplyTabColor()
+    {
+        auto weakThis{ get_weak() };
+
+        TabViewItem().Dispatcher().RunAsync(CoreDispatcherPriority::Normal, [weakThis]() {
+            auto ptrTab = weakThis.get();
+            if (!ptrTab)
+            {
+                return;
+            }
+
+            auto tab{ ptrTab };
+
+            // GetTabColor will return the color set by the color picker, or the
+            // color specified in the profile. If neither of those were set,
+            // then look to _themeColor to see if there's a value there.
+            // Otherwise, clear our color, falling back to the TabView defaults.
+            const auto currentColor = tab->GetTabColor();
+            if (currentColor.has_value())
+            {
+                tab->_ApplyTabColorOnUIThread(currentColor.value());
+            }
+            else if (tab->_themeColor != nullptr)
+            {
+                // Safely get the active control's brush.
+                const Media::Brush terminalBrush{ tab->_BackgroundBrush() };
+
+                if (const auto themeBrush{ tab->_themeColor.Evaluate(Application::Current().Resources(), terminalBrush, false) })
+                {
+                    // ThemeColor.Evaluate will get us a Brush (because the
+                    // TermControl could have an acrylic BG, for example). Take
+                    // that brush, and get the color out of it. We don't really
+                    // want to have the tab items themselves be acrylic.
+                    tab->_ApplyTabColorOnUIThread(til::color{ ThemeColor::ColorFromBrush(themeBrush) });
+                }
+                else
+                {
+                    tab->_ClearTabBackgroundColor();
+                }
+            }
+            else
+            {
+                tab->_ClearTabBackgroundColor();
+            }
+        });
+    }
+
+    // Method Description:
+    // - Applies the given color to the background of this tab's TabViewItem.
+    // - Sets the tab foreground color depending on the luminance of
+    // the background color
+    // - This method should only be called on the UI thread.
+    // Arguments:
+    // - color: the color the user picked for their tab
+    // Return Value:
+    // - <none>
+    void TabBase::_ApplyTabColorOnUIThread(const winrt::Windows::UI::Color& color)
+    {
+        Media::SolidColorBrush selectedTabBrush{};
+        Media::SolidColorBrush deselectedTabBrush{};
+        Media::SolidColorBrush fontBrush{};
+        Media::SolidColorBrush deselectedFontBrush{};
+        Media::SolidColorBrush secondaryFontBrush{};
+        Media::SolidColorBrush hoverTabBrush{};
+        Media::SolidColorBrush subtleFillColorSecondaryBrush;
+        Media::SolidColorBrush subtleFillColorTertiaryBrush;
+        // calculate the luminance of the current color and select a font
+        // color based on that
+        // see https://www.w3.org/TR/WCAG20/#relativeluminancedef
+        if (TerminalApp::ColorHelper::IsBrightColor(color))
+        {
+            fontBrush.Color(winrt::Windows::UI::Colors::Black());
+            auto secondaryFontColor = winrt::Windows::UI::Colors::Black();
+            // For alpha value see: https://github.com/microsoft/microsoft-ui-xaml/blob/7a33ad772d77d908aa6b316ec24e6d2eb3ebf571/dev/CommonStyles/Common_themeresources_any.xaml#L269
+            secondaryFontColor.A = 0x9E;
+            secondaryFontBrush.Color(secondaryFontColor);
+            auto subtleFillColorSecondary = winrt::Windows::UI::Colors::Black();
+            subtleFillColorSecondary.A = 0x09;
+            subtleFillColorSecondaryBrush.Color(subtleFillColorSecondary);
+            auto subtleFillColorTertiary = winrt::Windows::UI::Colors::Black();
+            subtleFillColorTertiary.A = 0x06;
+            subtleFillColorTertiaryBrush.Color(subtleFillColorTertiary);
+        }
+        else
+        {
+            fontBrush.Color(winrt::Windows::UI::Colors::White());
+            auto secondaryFontColor = winrt::Windows::UI::Colors::White();
+            // For alpha value see: https://github.com/microsoft/microsoft-ui-xaml/blob/7a33ad772d77d908aa6b316ec24e6d2eb3ebf571/dev/CommonStyles/Common_themeresources_any.xaml#L14
+            secondaryFontColor.A = 0xC5;
+            secondaryFontBrush.Color(secondaryFontColor);
+            auto subtleFillColorSecondary = winrt::Windows::UI::Colors::White();
+            subtleFillColorSecondary.A = 0x0F;
+            subtleFillColorSecondaryBrush.Color(subtleFillColorSecondary);
+            auto subtleFillColorTertiary = winrt::Windows::UI::Colors::White();
+            subtleFillColorTertiary.A = 0x0A;
+            subtleFillColorTertiaryBrush.Color(subtleFillColorTertiary);
+        }
+
+        selectedTabBrush.Color(color);
+
+        // Start with the current tab color, set to Opacity=.3
+        til::color deselectedTabColor{ color };
+        deselectedTabColor = deselectedTabColor.with_alpha(77); // 255 * .3 = 77
+
+        // If we DON'T have a color set from the color picker, or the profile's
+        // tabColor, but we do have a unfocused color in the theme, use the
+        // unfocused theme color here instead.
+        if (!GetTabColor().has_value() &&
+            _unfocusedThemeColor != nullptr)
+        {
+            // Safely get the active control's brush.
+            const Media::Brush terminalBrush{ _BackgroundBrush() };
+
+            // Get the color of the brush.
+            if (const auto themeBrush{ _unfocusedThemeColor.Evaluate(Application::Current().Resources(), terminalBrush, false) })
+            {
+                // We did figure out the brush. Get the color out of it. If it
+                // was "accent" or "terminalBackground", then we're gonna set
+                // the alpha to .3 manually here.
+                // (ThemeColor::UnfocusedTabOpacity will do this for us). If the
+                // user sets both unfocused and focused tab.background to
+                // terminalBackground, this will allow for some differentiation
+                // (and is generally just sensible).
+                deselectedTabColor = til::color{ ThemeColor::ColorFromBrush(themeBrush) }.with_alpha(_unfocusedThemeColor.UnfocusedTabOpacity());
+            }
+        }
+
+        // currently if a tab has a custom color, a deselected state is
+        // signified by using the same color with a bit of transparency
+        deselectedTabBrush.Color(deselectedTabColor.with_alpha(255));
+        deselectedTabBrush.Opacity(deselectedTabColor.a / 255.f);
+
+        hoverTabBrush.Color(color);
+        hoverTabBrush.Opacity(0.6);
+
+        // Account for the color of the tab row when setting the color of text
+        // on inactive tabs. Consider:
+        // * black active tabs
+        // * on a white tab row
+        // * with a transparent inactive tab color
+        //
+        // We don't want that to result in white text on a white tab row for
+        // inactive tabs.
+        const auto deselectedActualColor = deselectedTabColor.layer_over(_tabRowColor);
+        if (TerminalApp::ColorHelper::IsBrightColor(deselectedActualColor))
+        {
+            deselectedFontBrush.Color(winrt::Windows::UI::Colors::Black());
+        }
+        else
+        {
+            deselectedFontBrush.Color(winrt::Windows::UI::Colors::White());
+        }
+
+        // Prior to MUX 2.7, we set TabViewItemHeaderBackground, but now we can
+        // use TabViewItem().Background() for that. HOWEVER,
+        // TabViewItem().Background() only sets the color of the tab background
+        // when the TabViewItem is unselected. So we still need to set the other
+        // properties ourselves.
+        //
+        // In GH#11294 we thought we'd still need to set
+        // TabViewItemHeaderBackground manually, but GH#11382 discovered that
+        // Background() was actually okay after all.
+        TabViewItem().Background(deselectedTabBrush);
+        TabViewItem().Resources().Insert(winrt::box_value(L"TabViewItemHeaderBackgroundSelected"), selectedTabBrush);
+        TabViewItem().Resources().Insert(winrt::box_value(L"TabViewItemHeaderBackgroundPointerOver"), hoverTabBrush);
+        TabViewItem().Resources().Insert(winrt::box_value(L"TabViewItemHeaderBackgroundPressed"), selectedTabBrush);
+
+        // Similarly, TabViewItem().Foreground()  sets the color for the text
+        // when the TabViewItem isn't selected, but not when it is hovered,
+        // pressed, dragged, or selected, so we'll need to just set them all
+        // anyways.
+        TabViewItem().Foreground(deselectedFontBrush);
+        TabViewItem().Resources().Insert(winrt::box_value(L"TabViewItemHeaderForeground"), deselectedFontBrush);
+        TabViewItem().Resources().Insert(winrt::box_value(L"TabViewItemHeaderForegroundSelected"), fontBrush);
+        TabViewItem().Resources().Insert(winrt::box_value(L"TabViewItemHeaderForegroundPointerOver"), fontBrush);
+        TabViewItem().Resources().Insert(winrt::box_value(L"TabViewItemHeaderForegroundPressed"), fontBrush);
+
+        TabViewItem().Resources().Insert(winrt::box_value(L"TabViewItemHeaderCloseButtonForeground"), deselectedFontBrush);
+        TabViewItem().Resources().Insert(winrt::box_value(L"TabViewItemHeaderCloseButtonForegroundPressed"), secondaryFontBrush);
+        TabViewItem().Resources().Insert(winrt::box_value(L"TabViewItemHeaderCloseButtonForegroundPointerOver"), fontBrush);
+        TabViewItem().Resources().Insert(winrt::box_value(L"TabViewItemHeaderPressedCloseButtonForeground"), fontBrush);
+        TabViewItem().Resources().Insert(winrt::box_value(L"TabViewItemHeaderPointerOverCloseButtonForeground"), fontBrush);
+        TabViewItem().Resources().Insert(winrt::box_value(L"TabViewItemHeaderSelectedCloseButtonForeground"), fontBrush);
+        TabViewItem().Resources().Insert(winrt::box_value(L"TabViewItemHeaderCloseButtonBackgroundPressed"), subtleFillColorTertiaryBrush);
+        TabViewItem().Resources().Insert(winrt::box_value(L"TabViewItemHeaderCloseButtonBackgroundPointerOver"), subtleFillColorSecondaryBrush);
+
+        TabViewItem().Resources().Insert(winrt::box_value(L"TabViewButtonForegroundActiveTab"), fontBrush);
+        TabViewItem().Resources().Insert(winrt::box_value(L"TabViewButtonForegroundPressed"), fontBrush);
+        TabViewItem().Resources().Insert(winrt::box_value(L"TabViewButtonForegroundPointerOver"), fontBrush);
+
+        _RefreshVisualState();
+    }
+
+    // Method Description:
+    // - Clear out any color we've set for the TabViewItem.
+    // - This method should only be called on the UI thread.
+    // Arguments:
+    // - <none>
+    // Return Value:
+    // - <none>
+    void TabBase::_ClearTabBackgroundColor()
+    {
+        static const winrt::hstring keys[] = {
+            L"TabViewItemHeaderBackground",
+            L"TabViewItemHeaderBackgroundSelected",
+            L"TabViewItemHeaderBackgroundPointerOver",
+            L"TabViewItemHeaderBackgroundPressed",
+            L"TabViewItemHeaderForeground",
+            L"TabViewItemHeaderForegroundSelected",
+            L"TabViewItemHeaderForegroundPointerOver",
+            L"TabViewItemHeaderForegroundPressed",
+            L"TabViewItemHeaderCloseButtonForeground",
+            L"TabViewItemHeaderCloseButtonForegroundPressed",
+            L"TabViewItemHeaderCloseButtonForegroundPointerOver",
+            L"TabViewItemHeaderPressedCloseButtonForeground",
+            L"TabViewItemHeaderPointerOverCloseButtonForeground",
+            L"TabViewItemHeaderSelectedCloseButtonForeground",
+            L"TabViewItemHeaderCloseButtonBackgroundPressed",
+            L"TabViewItemHeaderCloseButtonBackgroundPointerOver",
+            L"TabViewButtonForegroundActiveTab",
+            L"TabViewButtonForegroundPressed",
+            L"TabViewButtonForegroundPointerOver"
+        };
+
+        // simply clear any of the colors in the tab's dict
+        for (const auto& keyString : keys)
+        {
+            auto key = winrt::box_value(keyString);
+            if (TabViewItem().Resources().HasKey(key))
+            {
+                TabViewItem().Resources().Remove(key);
+            }
+        }
+
+        // GH#11382 DON'T set the background to null. If you do that, then the
+        // tab won't be hit testable at all. Transparent, however, is a totally
+        // valid hit test target. That makes sense.
+        TabViewItem().Background(WUX::Media::SolidColorBrush{ Windows::UI::Colors::Transparent() });
+
+        _RefreshVisualState();
+    }
+
+    // Method Description:
+    // Toggles the visual state of the tab view item,
+    // so that changes to the tab color are reflected immediately
+    // Arguments:
+    // - <none>
+    // Return Value:
+    // - <none>
+    void TabBase::_RefreshVisualState()
+    {
+        if (TabViewItem().IsSelected())
+        {
+            VisualStateManager::GoToState(TabViewItem(), L"Normal", true);
+            VisualStateManager::GoToState(TabViewItem(), L"Selected", true);
+        }
+        else
+        {
+            VisualStateManager::GoToState(TabViewItem(), L"Selected", true);
+            VisualStateManager::GoToState(TabViewItem(), L"Normal", true);
+        }
+    }
+
 }
diff --git a/src/cascadia/TerminalApp/TabBase.h b/src/cascadia/TerminalApp/TabBase.h
index a04873f8905..fb32fe0e377 100644
--- a/src/cascadia/TerminalApp/TabBase.h
+++ b/src/cascadia/TerminalApp/TabBase.h
@@ -25,6 +25,11 @@ namespace winrt::TerminalApp::implementation
         void SetActionMap(const Microsoft::Terminal::Settings::Model::IActionMapView& actionMap);
         virtual std::vector<Microsoft::Terminal::Settings::Model::ActionAndArgs> BuildStartupActions() const = 0;
 
+        virtual std::optional<winrt::Windows::UI::Color> GetTabColor();
+        void ThemeColor(const winrt::Microsoft::Terminal::Settings::Model::ThemeColor& focused,
+                        const winrt::Microsoft::Terminal::Settings::Model::ThemeColor& unfocused,
+                        const til::color& tabRowColor);
+
         WINRT_CALLBACK(RequestFocusActiveControl, winrt::delegate<void()>);
 
         WINRT_CALLBACK(Closed, winrt::Windows::Foundation::EventHandler<winrt::Windows::Foundation::IInspectable>);
@@ -51,6 +56,10 @@ namespace winrt::TerminalApp::implementation
         Microsoft::Terminal::Settings::Model::IActionMapView _actionMap{ nullptr };
         winrt::hstring _keyChord{};
 
+        winrt::Microsoft::Terminal::Settings::Model::ThemeColor _themeColor{ nullptr };
+        winrt::Microsoft::Terminal::Settings::Model::ThemeColor _unfocusedThemeColor{ nullptr };
+        til::color _tabRowColor;
+
         virtual void _CreateContextMenu();
         virtual winrt::hstring _CreateToolTipTitle();
 
@@ -63,6 +72,12 @@ namespace winrt::TerminalApp::implementation
         winrt::fire_and_forget _UpdateSwitchToTabKeyChord();
         void _UpdateToolTip();
 
+        void _RecalculateAndApplyTabColor();
+        void _ApplyTabColorOnUIThread(const winrt::Windows::UI::Color& color);
+        void _ClearTabBackgroundColor();
+        void _RefreshVisualState();
+        virtual winrt::Windows::UI::Xaml::Media::Brush _BackgroundBrush() = 0;
+
         friend class ::TerminalAppLocalTests::TabTests;
     };
 }
diff --git a/src/cascadia/TerminalApp/TerminalPage.cpp b/src/cascadia/TerminalApp/TerminalPage.cpp
index 36937f4481d..37436c48cc3 100644
--- a/src/cascadia/TerminalApp/TerminalPage.cpp
+++ b/src/cascadia/TerminalApp/TerminalPage.cpp
@@ -19,6 +19,7 @@
 #include "DebugTapConnection.h"
 #include "SettingsTab.h"
 #include "TabRowControl.h"
+#include "Utils.h"
 
 using namespace winrt;
 using namespace winrt::Microsoft::Terminal::Control;
@@ -3321,7 +3322,7 @@ namespace winrt::TerminalApp::implementation
                 }
             });
 
-            auto newTabImpl = winrt::make_self<SettingsTab>(sui);
+            auto newTabImpl = winrt::make_self<SettingsTab>(sui, _settings.GlobalSettings().CurrentTheme().RequestedTheme());
 
             // Add the new tab to the list of our tabs.
             _tabs.Append(*newTabImpl);
@@ -4101,79 +4102,11 @@ namespace winrt::TerminalApp::implementation
         const auto theme = _settings.GlobalSettings().CurrentTheme();
         auto requestedTheme{ theme.RequestedTheme() };
 
-        // First: Update the colors of our individual TabViewItems. This applies tab.background to the tabs via TerminalTab::ThemeColor
-        {
-            auto tabBackground = theme.Tab() ? theme.Tab().Background() : nullptr;
-            for (const auto& tab : _tabs)
-            {
-                if (const auto& terminalTabImpl{ _GetTerminalTabImpl(tab) })
-                {
-                    terminalTabImpl->ThemeColor(tabBackground);
-                }
-            }
-        }
-
         const auto res = Application::Current().Resources();
 
-        // XAML Hacks:
-        //
-        // the App is always in the OS theme, so the
-        // App::Current().Resources() lookup will always get the value for the
-        // OS theme, not the requested theme.
-        //
-        // This helper allows us to instead lookup the value of a resource
-        // specified by `key` for the given `requestedTheme`, from the
-        // dictionaries in App.xaml. Make sure the value is actually there!
-        // Otherwise this'll throw like any other Lookup for a resource that
-        // isn't there.
-        static const auto lookup = [](auto& res, auto& requestedTheme, auto& key) {
-            // You want the Default version of the resource? Great, the App is
-            // always in the OS theme. Just look it up and be done.
-            if (requestedTheme == ElementTheme::Default)
-            {
-                return res.Lookup(key);
-            }
-            static const auto lightKey = winrt::box_value(L"Light");
-            static const auto darkKey = winrt::box_value(L"Dark");
-            // There isn't an ElementTheme::HighContrast.
-
-            auto requestedThemeKey = requestedTheme == ElementTheme::Dark ? darkKey : lightKey;
-            for (const auto& dictionary : res.MergedDictionaries())
-            {
-                // Don't look in the MUX resources. They come first. A person
-                // with more patience than me may find a way to look through our
-                // dictionaries first, then the MUX ones, but that's not needed
-                // currently
-                if (dictionary.Source())
-                {
-                    continue;
-                }
-                // Look through the theme dictionaries we defined:
-                for (const auto& [dictionaryKey, dict] : dictionary.ThemeDictionaries())
-                {
-                    // Does the key for this dict match the theme we're looking for?
-                    if (winrt::unbox_value<winrt::hstring>(dictionaryKey) !=
-                        winrt::unbox_value<winrt::hstring>(requestedThemeKey))
-                    {
-                        // No? skip it.
-                        continue;
-                    }
-                    // Look for the requested resource in this dict.
-                    const auto themeDictionary = dict.as<winrt::Windows::UI::Xaml::ResourceDictionary>();
-                    if (themeDictionary.HasKey(key))
-                    {
-                        return themeDictionary.Lookup(key);
-                    }
-                }
-            }
-
-            // We didn't find it in the requested dict, fall back to the default dictionary.
-            return res.Lookup(key);
-        };
-
         // Use our helper to lookup the theme-aware version of the resource.
         const auto tabViewBackgroundKey = winrt::box_value(L"TabViewBackground");
-        const auto backgroundSolidBrush = lookup(res, requestedTheme, tabViewBackgroundKey).as<Media::SolidColorBrush>();
+        const auto backgroundSolidBrush = ThemeLookup(res, requestedTheme, tabViewBackgroundKey).as<Media::SolidColorBrush>();
 
         til::color bgColor = backgroundSolidBrush.Color();
 
@@ -4218,6 +4151,21 @@ namespace winrt::TerminalApp::implementation
             _tabRow.Background(TitlebarBrush());
         }
 
+        // Second: Update the colors of our individual TabViewItems. This
+        // applies tab.background to the tabs via TerminalTab::ThemeColor.
+        //
+        // Do this second, so that we already know the bgColor of the titlebar.
+        {
+            const auto tabBackground = theme.Tab() ? theme.Tab().Background() : nullptr;
+            const auto tabUnfocusedBackground = theme.Tab() ? theme.Tab().UnfocusedBackground() : nullptr;
+            for (const auto& tab : _tabs)
+            {
+                winrt::com_ptr<TabBase> tabImpl;
+                tabImpl.copy_from(winrt::get_self<TabBase>(tab));
+                tabImpl->ThemeColor(tabBackground, tabUnfocusedBackground, bgColor);
+            }
+        }
+
         // Update the new tab button to have better contrast with the new color.
         // In theory, it would be convenient to also change these for the
         // inactive tabs as well, but we're leaving that as a follow up.
diff --git a/src/cascadia/TerminalApp/TerminalTab.cpp b/src/cascadia/TerminalApp/TerminalTab.cpp
index 6ef8c735853..4bac51928bf 100644
--- a/src/cascadia/TerminalApp/TerminalTab.cpp
+++ b/src/cascadia/TerminalApp/TerminalTab.cpp
@@ -1394,169 +1394,6 @@ namespace winrt::TerminalApp::implementation
         _RecalculateAndApplyTabColor();
     }
 
-    void TerminalTab::ThemeColor(const winrt::Microsoft::Terminal::Settings::Model::ThemeColor& color)
-    {
-        _themeColor = color;
-        _RecalculateAndApplyTabColor();
-    }
-
-    // Method Description:
-    // - This function dispatches a function to the UI thread to recalculate
-    //   what this tab's current background color should be. If a color is set,
-    //   it will apply the given color to the tab's background. Otherwise, it
-    //   will clear the tab's background color.
-    // Arguments:
-    // - <none>
-    // Return Value:
-    // - <none>
-    void TerminalTab::_RecalculateAndApplyTabColor()
-    {
-        auto weakThis{ get_weak() };
-
-        TabViewItem().Dispatcher().RunAsync(CoreDispatcherPriority::Normal, [weakThis]() {
-            auto ptrTab = weakThis.get();
-            if (!ptrTab)
-                return;
-
-            auto tab{ ptrTab };
-
-            // GetTabColor will return the color set by the color picker, or the
-            // color specified in the profile. If neither of those were set,
-            // then look to _themeColor to see if there's a value there.
-            // Otherwise, clear our color, falling back to the TabView defaults.
-            auto currentColor = tab->GetTabColor();
-            if (currentColor.has_value())
-            {
-                tab->_ApplyTabColor(currentColor.value());
-            }
-            else if (tab->_themeColor != nullptr)
-            {
-                // One-liner to safely get the active control's brush.
-                Media::Brush terminalBrush{ nullptr };
-                if (const auto& c{ tab->GetActiveTerminalControl() })
-                {
-                    terminalBrush = c.BackgroundBrush();
-                }
-
-                if (const auto themeBrush{ tab->_themeColor.Evaluate(Application::Current().Resources(), terminalBrush, false) })
-                {
-                    // ThemeColor.Evaluate will get us a Brush (because the
-                    // TermControl could have an acrylic BG, for example). Take
-                    // that brush, and get the color out of it. We don't really
-                    // want to have the tab items themselves be acrylic.
-                    tab->_ApplyTabColor(til::color{ ThemeColor::ColorFromBrush(themeBrush) });
-                }
-                else
-                {
-                    tab->_ClearTabBackgroundColor();
-                }
-            }
-            else
-            {
-                tab->_ClearTabBackgroundColor();
-            }
-        });
-    }
-
-    // Method Description:
-    // - Applies the given color to the background of this tab's TabViewItem.
-    // - Sets the tab foreground color depending on the luminance of
-    // the background color
-    // - This method should only be called on the UI thread.
-    // Arguments:
-    // - color: the color the user picked for their tab
-    // Return Value:
-    // - <none>
-    void TerminalTab::_ApplyTabColor(const winrt::Windows::UI::Color& color)
-    {
-        Media::SolidColorBrush selectedTabBrush{};
-        Media::SolidColorBrush deselectedTabBrush{};
-        Media::SolidColorBrush fontBrush{};
-        Media::SolidColorBrush secondaryFontBrush{};
-        Media::SolidColorBrush hoverTabBrush{};
-        Media::SolidColorBrush subtleFillColorSecondaryBrush;
-        Media::SolidColorBrush subtleFillColorTertiaryBrush;
-        // calculate the luminance of the current color and select a font
-        // color based on that
-        // see https://www.w3.org/TR/WCAG20/#relativeluminancedef
-        if (TerminalApp::ColorHelper::IsBrightColor(color))
-        {
-            fontBrush.Color(winrt::Windows::UI::Colors::Black());
-            auto secondaryFontColor = winrt::Windows::UI::Colors::Black();
-            // For alpha value see: https://github.com/microsoft/microsoft-ui-xaml/blob/7a33ad772d77d908aa6b316ec24e6d2eb3ebf571/dev/CommonStyles/Common_themeresources_any.xaml#L269
-            secondaryFontColor.A = 0x9E;
-            secondaryFontBrush.Color(secondaryFontColor);
-            auto subtleFillColorSecondary = winrt::Windows::UI::Colors::Black();
-            subtleFillColorSecondary.A = 0x09;
-            subtleFillColorSecondaryBrush.Color(subtleFillColorSecondary);
-            auto subtleFillColorTertiary = winrt::Windows::UI::Colors::Black();
-            subtleFillColorTertiary.A = 0x06;
-            subtleFillColorTertiaryBrush.Color(subtleFillColorTertiary);
-        }
-        else
-        {
-            fontBrush.Color(winrt::Windows::UI::Colors::White());
-            auto secondaryFontColor = winrt::Windows::UI::Colors::White();
-            // For alpha value see: https://github.com/microsoft/microsoft-ui-xaml/blob/7a33ad772d77d908aa6b316ec24e6d2eb3ebf571/dev/CommonStyles/Common_themeresources_any.xaml#L14
-            secondaryFontColor.A = 0xC5;
-            secondaryFontBrush.Color(secondaryFontColor);
-            auto subtleFillColorSecondary = winrt::Windows::UI::Colors::White();
-            subtleFillColorSecondary.A = 0x0F;
-            subtleFillColorSecondaryBrush.Color(subtleFillColorSecondary);
-            auto subtleFillColorTertiary = winrt::Windows::UI::Colors::White();
-            subtleFillColorTertiary.A = 0x0A;
-            subtleFillColorTertiaryBrush.Color(subtleFillColorTertiary);
-        }
-
-        selectedTabBrush.Color(color);
-
-        // currently if a tab has a custom color, a deselected state is
-        // signified by using the same color with a bit of transparency
-        deselectedTabBrush.Color(color);
-        deselectedTabBrush.Opacity(0.3);
-
-        hoverTabBrush.Color(color);
-        hoverTabBrush.Opacity(0.6);
-
-        // Prior to MUX 2.7, we set TabViewItemHeaderBackground, but now we can
-        // use TabViewItem().Background() for that. HOWEVER,
-        // TabViewItem().Background() only sets the color of the tab background
-        // when the TabViewItem is unselected. So we still need to set the other
-        // properties ourselves.
-        //
-        // In GH#11294 we thought we'd still need to set
-        // TabViewItemHeaderBackground manually, but GH#11382 discovered that
-        // Background() was actually okay after all.
-        TabViewItem().Background(deselectedTabBrush);
-        TabViewItem().Resources().Insert(winrt::box_value(L"TabViewItemHeaderBackgroundSelected"), selectedTabBrush);
-        TabViewItem().Resources().Insert(winrt::box_value(L"TabViewItemHeaderBackgroundPointerOver"), hoverTabBrush);
-        TabViewItem().Resources().Insert(winrt::box_value(L"TabViewItemHeaderBackgroundPressed"), selectedTabBrush);
-
-        // TabViewItem().Foreground() unfortunately does not work for us. It
-        // sets the color for the text when the TabViewItem isn't selected, but
-        // not when it is hovered, pressed, dragged, or selected, so we'll need
-        // to just set them all anyways.
-        TabViewItem().Resources().Insert(winrt::box_value(L"TabViewItemHeaderForeground"), fontBrush);
-        TabViewItem().Resources().Insert(winrt::box_value(L"TabViewItemHeaderForegroundSelected"), fontBrush);
-        TabViewItem().Resources().Insert(winrt::box_value(L"TabViewItemHeaderForegroundPointerOver"), fontBrush);
-        TabViewItem().Resources().Insert(winrt::box_value(L"TabViewItemHeaderForegroundPressed"), fontBrush);
-        TabViewItem().Resources().Insert(winrt::box_value(L"TabViewItemHeaderCloseButtonForeground"), fontBrush);
-        TabViewItem().Resources().Insert(winrt::box_value(L"TabViewItemHeaderCloseButtonForegroundPressed"), secondaryFontBrush);
-        TabViewItem().Resources().Insert(winrt::box_value(L"TabViewItemHeaderCloseButtonForegroundPointerOver"), fontBrush);
-        TabViewItem().Resources().Insert(winrt::box_value(L"TabViewItemHeaderPressedCloseButtonForeground"), fontBrush);
-        TabViewItem().Resources().Insert(winrt::box_value(L"TabViewItemHeaderPointerOverCloseButtonForeground"), fontBrush);
-        TabViewItem().Resources().Insert(winrt::box_value(L"TabViewItemHeaderSelectedCloseButtonForeground"), fontBrush);
-        TabViewItem().Resources().Insert(winrt::box_value(L"TabViewItemHeaderCloseButtonBackgroundPressed"), subtleFillColorTertiaryBrush);
-        TabViewItem().Resources().Insert(winrt::box_value(L"TabViewItemHeaderCloseButtonBackgroundPointerOver"), subtleFillColorSecondaryBrush);
-        TabViewItem().Resources().Insert(winrt::box_value(L"TabViewButtonForegroundActiveTab"), fontBrush);
-        TabViewItem().Resources().Insert(winrt::box_value(L"TabViewButtonForegroundPressed"), fontBrush);
-        TabViewItem().Resources().Insert(winrt::box_value(L"TabViewButtonForegroundPointerOver"), fontBrush);
-
-        _RefreshVisualState();
-
-        _ColorSelectedHandlers(color);
-    }
-
     // Method Description:
     // - Clear the custom runtime color of the tab, if any color is set. This
     //   will re-apply whatever the tab's base color should be (either the color
@@ -1571,54 +1408,14 @@ namespace winrt::TerminalApp::implementation
         _RecalculateAndApplyTabColor();
     }
 
-    // Method Description:
-    // - Clear out any color we've set for the TabViewItem.
-    // - This method should only be called on the UI thread.
-    // Arguments:
-    // - <none>
-    // Return Value:
-    // - <none>
-    void TerminalTab::_ClearTabBackgroundColor()
+    winrt::Windows::UI::Xaml::Media::Brush TerminalTab::_BackgroundBrush()
     {
-        winrt::hstring keys[] = {
-            L"TabViewItemHeaderBackground",
-            L"TabViewItemHeaderBackgroundSelected",
-            L"TabViewItemHeaderBackgroundPointerOver",
-            L"TabViewItemHeaderBackgroundPressed",
-            L"TabViewItemHeaderForeground",
-            L"TabViewItemHeaderForegroundSelected",
-            L"TabViewItemHeaderForegroundPointerOver",
-            L"TabViewItemHeaderForegroundPressed",
-            L"TabViewItemHeaderCloseButtonForeground",
-            L"TabViewItemHeaderCloseButtonForegroundPressed",
-            L"TabViewItemHeaderCloseButtonForegroundPointerOver",
-            L"TabViewItemHeaderPressedCloseButtonForeground",
-            L"TabViewItemHeaderPointerOverCloseButtonForeground",
-            L"TabViewItemHeaderSelectedCloseButtonForeground",
-            L"TabViewItemHeaderCloseButtonBackgroundPressed",
-            L"TabViewItemHeaderCloseButtonBackgroundPointerOver",
-            L"TabViewButtonForegroundActiveTab",
-            L"TabViewButtonForegroundPressed",
-            L"TabViewButtonForegroundPointerOver"
-        };
-
-        // simply clear any of the colors in the tab's dict
-        for (auto keyString : keys)
+        Media::Brush terminalBrush{ nullptr };
+        if (const auto& c{ GetActiveTerminalControl() })
         {
-            auto key = winrt::box_value(keyString);
-            if (TabViewItem().Resources().HasKey(key))
-            {
-                TabViewItem().Resources().Remove(key);
-            }
+            terminalBrush = c.BackgroundBrush();
         }
-
-        // GH#11382 DON'T set the background to null. If you do that, then the
-        // tab won't be hit testable at all. Transparent, however, is a totally
-        // valid hit test target. That makes sense.
-        TabViewItem().Background(WUX::Media::SolidColorBrush{ Windows::UI::Colors::Transparent() });
-
-        _RefreshVisualState();
-        _ColorClearedHandlers();
+        return terminalBrush;
     }
 
     // Method Description:
@@ -1633,27 +1430,6 @@ namespace winrt::TerminalApp::implementation
         _ColorPickerRequestedHandlers();
     }
 
-    // Method Description:
-    // Toggles the visual state of the tab view item,
-    // so that changes to the tab color are reflected immediately
-    // Arguments:
-    // - <none>
-    // Return Value:
-    // - <none>
-    void TerminalTab::_RefreshVisualState()
-    {
-        if (TabViewItem().IsSelected())
-        {
-            VisualStateManager::GoToState(TabViewItem(), L"Normal", true);
-            VisualStateManager::GoToState(TabViewItem(), L"Selected", true);
-        }
-        else
-        {
-            VisualStateManager::GoToState(TabViewItem(), L"Selected", true);
-            VisualStateManager::GoToState(TabViewItem(), L"Normal", true);
-        }
-    }
-
     // - Get the total number of leaf panes in this tab. This will be the number
     //   of actual controls hosted by this tab.
     // Arguments:
diff --git a/src/cascadia/TerminalApp/TerminalTab.h b/src/cascadia/TerminalApp/TerminalTab.h
index 5f7a4c8ed82..53492095db1 100644
--- a/src/cascadia/TerminalApp/TerminalTab.h
+++ b/src/cascadia/TerminalApp/TerminalTab.h
@@ -71,9 +71,7 @@ namespace winrt::TerminalApp::implementation
         void ResetTabText();
         void ActivateTabRenamer();
 
-        std::optional<winrt::Windows::UI::Color> GetTabColor();
-
-        void ThemeColor(const winrt::Microsoft::Terminal::Settings::Model::ThemeColor& color);
+        virtual std::optional<winrt::Windows::UI::Color> GetTabColor() override;
         void SetRuntimeTabColor(const winrt::Windows::UI::Color& color);
         void ResetRuntimeTabColor();
         void RequestColorPicker();
@@ -100,8 +98,6 @@ namespace winrt::TerminalApp::implementation
         }
 
         WINRT_CALLBACK(ActivePaneChanged, winrt::delegate<>);
-        WINRT_CALLBACK(ColorSelected, winrt::delegate<winrt::Windows::UI::Color>);
-        WINRT_CALLBACK(ColorCleared, winrt::delegate<>);
         WINRT_CALLBACK(TabRaiseVisualBell, winrt::delegate<>);
         WINRT_CALLBACK(DuplicateRequested, winrt::delegate<>);
         WINRT_CALLBACK(SplitTabRequested, winrt::delegate<>);
@@ -119,7 +115,6 @@ namespace winrt::TerminalApp::implementation
         std::optional<winrt::Windows::UI::Color> _runtimeTabColor{};
         winrt::TerminalApp::TabHeaderControl _headerControl{};
         winrt::TerminalApp::TerminalTabStatus _tabStatus{};
-        winrt::Microsoft::Terminal::Settings::Model::ThemeColor _themeColor{ nullptr };
 
         winrt::TerminalApp::ColorPickupFlyout _tabColorPickup{ nullptr };
         winrt::event_token _colorSelectedToken;
@@ -163,8 +158,6 @@ namespace winrt::TerminalApp::implementation
         void _CreateContextMenu() override;
         virtual winrt::hstring _CreateToolTipTitle() override;
 
-        void _RefreshVisualState();
-
         void _DetachEventHandlersFromControl(const uint32_t paneId, const winrt::Microsoft::Terminal::Control::TermControl& control);
         void _AttachEventHandlersToControl(const uint32_t paneId, const winrt::Microsoft::Terminal::Control::TermControl& control);
         void _AttachEventHandlersToPane(std::shared_ptr<Pane> pane);
@@ -173,16 +166,14 @@ namespace winrt::TerminalApp::implementation
 
         winrt::hstring _GetActiveTitle() const;
 
-        void _RecalculateAndApplyTabColor();
-        void _ApplyTabColor(const winrt::Windows::UI::Color& color);
-        void _ClearTabBackgroundColor();
-
         void _RecalculateAndApplyReadOnly();
 
         void _UpdateProgressState();
 
         void _DuplicateTab();
 
+        virtual winrt::Windows::UI::Xaml::Media::Brush _BackgroundBrush() override;
+
         friend class ::TerminalAppLocalTests::TabTests;
     };
 }
diff --git a/src/cascadia/TerminalSettingsModel/MTSMSettings.h b/src/cascadia/TerminalSettingsModel/MTSMSettings.h
index 4ebc362ddd6..502b4adb404 100644
--- a/src/cascadia/TerminalSettingsModel/MTSMSettings.h
+++ b/src/cascadia/TerminalSettingsModel/MTSMSettings.h
@@ -129,6 +129,7 @@ Author(s):
     X(winrt::Microsoft::Terminal::Settings::Model::ThemeColor, Background, "background", nullptr) \
     X(winrt::Microsoft::Terminal::Settings::Model::ThemeColor, UnfocusedBackground, "unfocusedBackground", nullptr)
 
-#define MTSM_THEME_TAB_SETTINGS(X)                                                                \
-    X(winrt::Microsoft::Terminal::Settings::Model::ThemeColor, Background, "background", nullptr) \
+#define MTSM_THEME_TAB_SETTINGS(X)                                                                                  \
+    X(winrt::Microsoft::Terminal::Settings::Model::ThemeColor, Background, "background", nullptr)                   \
+    X(winrt::Microsoft::Terminal::Settings::Model::ThemeColor, UnfocusedBackground, "unfocusedBackground", nullptr) \
     X(winrt::Microsoft::Terminal::Settings::Model::TabCloseButtonVisibility, ShowCloseButton, "showCloseButton", winrt::Microsoft::Terminal::Settings::Model::TabCloseButtonVisibility::Always)
diff --git a/src/cascadia/TerminalSettingsModel/Theme.cpp b/src/cascadia/TerminalSettingsModel/Theme.cpp
index dfb108634ac..c7236a8463f 100644
--- a/src/cascadia/TerminalSettingsModel/Theme.cpp
+++ b/src/cascadia/TerminalSettingsModel/Theme.cpp
@@ -199,6 +199,30 @@ winrt::WUX::Media::Brush ThemeColor::Evaluate(const winrt::WUX::ResourceDictiona
     return nullptr;
 }
 
+// Method Description:
+// - This is not an actual property on a theme color setting, but rather
+//   something derived from the value itself. This is "the opacity we should use
+//   for this ThemeColor should it be used as a unfocusedTab color". Basically,
+//   terminalBackground and accent use 30% opacity when set, to match the how
+//   inactive tabs were colored before themes existed.
+// Arguments:
+// - <none>
+// Return Value:
+// - the opacity that should be used if this color is being applied to a
+//   tab.unfocusedBackground property.
+uint8_t ThemeColor::UnfocusedTabOpacity() const noexcept
+{
+    switch (ColorType())
+    {
+    case ThemeColorType::Accent:
+    case ThemeColorType::TerminalBackground:
+        return 77; // 77 = .3 * 256
+    case ThemeColorType::Color:
+        return _Color.a;
+    }
+    return 0;
+}
+
 #define THEME_SETTINGS_FROM_JSON(type, name, jsonKey, ...)                    \
     {                                                                         \
         std::optional<type> _val;                                             \
diff --git a/src/cascadia/TerminalSettingsModel/Theme.h b/src/cascadia/TerminalSettingsModel/Theme.h
index 96a496b908c..e39d42b8f20 100644
--- a/src/cascadia/TerminalSettingsModel/Theme.h
+++ b/src/cascadia/TerminalSettingsModel/Theme.h
@@ -39,6 +39,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
         winrt::Windows::UI::Xaml::Media::Brush Evaluate(const winrt::Windows::UI::Xaml::ResourceDictionary& res,
                                                         const winrt::Windows::UI::Xaml::Media::Brush& terminalBackground,
                                                         const bool forTitlebar);
+        uint8_t UnfocusedTabOpacity() const noexcept;
 
         WINRT_PROPERTY(til::color, Color);
         WINRT_PROPERTY(winrt::Microsoft::Terminal::Settings::Model::ThemeColorType, ColorType);
diff --git a/src/cascadia/TerminalSettingsModel/Theme.idl b/src/cascadia/TerminalSettingsModel/Theme.idl
index 32127abf2ce..39260bb0cf4 100644
--- a/src/cascadia/TerminalSettingsModel/Theme.idl
+++ b/src/cascadia/TerminalSettingsModel/Theme.idl
@@ -32,6 +32,7 @@ namespace Microsoft.Terminal.Settings.Model
         Windows.UI.Xaml.Media.Brush Evaluate(Windows.UI.Xaml.ResourceDictionary res,
                                              Windows.UI.Xaml.Media.Brush terminalBackground,
                                              Boolean forTitlebar);
+        UInt8 UnfocusedTabOpacity { get; };
     }
 
     runtimeclass WindowTheme {
@@ -45,6 +46,7 @@ namespace Microsoft.Terminal.Settings.Model
 
     runtimeclass TabTheme {
         ThemeColor Background { get; };
+        ThemeColor UnfocusedBackground { get; };
         TabCloseButtonVisibility ShowCloseButton { get; };
     }
 
diff --git a/src/cascadia/TerminalSettingsModel/defaults.json b/src/cascadia/TerminalSettingsModel/defaults.json
index 02e07a223c3..117248e4d7c 100644
--- a/src/cascadia/TerminalSettingsModel/defaults.json
+++ b/src/cascadia/TerminalSettingsModel/defaults.json
@@ -26,7 +26,7 @@
     // Miscellaneous
     "confirmCloseAllTabs": true,
     "startOnUserLogin": false,
-    "theme": "system",
+    "theme": "dark",
     "snapToGridOnResize": true,
     "disableAnimations": false,
     "startupActions": "",
@@ -283,20 +283,38 @@
     "themes": [
         {
             "name": "light",
-            "window":{
+            "window": {
                 "applicationTheme": "light"
+            },
+            "tab": {
+                "background": "terminalBackground",
+                "unfocusedBackground": "#00000000"
+            },
+            "tabRow": {
+                "unfocusedBackground": "#FFFFFFFF"
             }
         },
         {
             "name": "dark",
-            "window":{
+            "window": {
                 "applicationTheme": "dark"
+            },
+            "tab": {
+                "background": "terminalBackground",
+                "unfocusedBackground": "#00000000"
+            },
+            "tabRow": {
+                "unfocusedBackground": "#333333FF"
             }
         },
         {
             "name": "system",
-            "window":{
+            "window": {
                 "applicationTheme": "system"
+            },
+            "tab": {
+                "background": "terminalBackground",
+                "unfocusedBackground": "#00000000"
             }
         }
     ],
diff --git a/src/cascadia/WinRTUtils/inc/Utils.h b/src/cascadia/WinRTUtils/inc/Utils.h
index 82824c5bfaa..ec8233fcb3c 100644
--- a/src/cascadia/WinRTUtils/inc/Utils.h
+++ b/src/cascadia/WinRTUtils/inc/Utils.h
@@ -53,3 +53,66 @@ winrt::Windows::Foundation::IAsyncOperation<winrt::hstring> SaveFilePicker(HWND
 }
 
 winrt::Windows::Foundation::IAsyncOperation<winrt::hstring> OpenImagePicker(HWND parentHwnd);
+
+#ifdef WINRT_Windows_UI_Xaml_H
+// Only compile me if Windows.UI.Xaml is already included.
+//
+// XAML Hacks:
+//
+// the App is always in the OS theme, so the
+// App::Current().Resources() lookup will always get the value for the
+// OS theme, not the requested theme.
+//
+// This helper allows us to instead lookup the value of a resource
+// specified by `key` for the given `requestedTheme`, from the
+// dictionaries in App.xaml. Make sure the value is actually there!
+// Otherwise this'll throw like any other Lookup for a resource that
+// isn't there.
+winrt::Windows::Foundation::IInspectable ThemeLookup(const auto& res,
+                                                     const winrt::Windows::UI::Xaml::ElementTheme& requestedTheme,
+                                                     const winrt::Windows::Foundation::IInspectable& key)
+{
+    // You want the Default version of the resource? Great, the App is
+    // always in the OS theme. Just look it up and be done.
+    if (requestedTheme == winrt::Windows::UI::Xaml::ElementTheme::Default)
+    {
+        return res.Lookup(key);
+    }
+    static const auto lightKey = winrt::box_value(L"Light");
+    static const auto darkKey = winrt::box_value(L"Dark");
+    // There isn't an ElementTheme::HighContrast.
+
+    const auto requestedThemeKey = requestedTheme == winrt::Windows::UI::Xaml::ElementTheme::Dark ? darkKey : lightKey;
+    for (const auto& dictionary : res.MergedDictionaries())
+    {
+        // Don't look in the MUX resources. They come first. A person
+        // with more patience than me may find a way to look through our
+        // dictionaries first, then the MUX ones, but that's not needed
+        // currently
+        if (dictionary.Source())
+        {
+            continue;
+        }
+        // Look through the theme dictionaries we defined:
+        for (const auto& [dictionaryKey, dict] : dictionary.ThemeDictionaries())
+        {
+            // Does the key for this dict match the theme we're looking for?
+            if (winrt::unbox_value<winrt::hstring>(dictionaryKey) !=
+                winrt::unbox_value<winrt::hstring>(requestedThemeKey))
+            {
+                // No? skip it.
+                continue;
+            }
+            // Look for the requested resource in this dict.
+            const auto themeDictionary = dict.as<winrt::Windows::UI::Xaml::ResourceDictionary>();
+            if (themeDictionary.HasKey(key))
+            {
+                return themeDictionary.Lookup(key);
+            }
+        }
+    }
+
+    // We didn't find it in the requested dict, fall back to the default dictionary.
+    return res.Lookup(key);
+};
+#endif
diff --git a/src/inc/til/color.h b/src/inc/til/color.h
index 5054be53318..ca07522b35b 100644
--- a/src/inc/til/color.h
+++ b/src/inc/til/color.h
@@ -134,6 +134,29 @@ namespace til // Terminal Implementation Library. Also: "Today I Learned"
             };
         }
 
+        // source-over alpha blending/composition.
+        // `this` (source/top) will be blended "over" `destination` (bottom).
+        // `this` and `destination` are expected to be in straight alpha.
+        // See https://en.wikipedia.org/wiki/Alpha_compositing#Description
+        constexpr color layer_over(const color& destination) const
+        {
+            const auto sourceAlpha = a / 255.0f;
+            const auto destinationAlpha = destination.a / 255.0f;
+            const auto aInverse = 1.0f - sourceAlpha;
+
+            const auto resultA = a + destination.a * aInverse;
+            const auto resultR = (r * sourceAlpha + destination.r * destinationAlpha * aInverse) / resultA;
+            const auto resultG = (g * sourceAlpha + destination.g * destinationAlpha * aInverse) / resultA;
+            const auto resultB = (b * sourceAlpha + destination.b * destinationAlpha * aInverse) / resultA;
+
+            return {
+                static_cast<uint8_t>(resultR + 0.5f),
+                static_cast<uint8_t>(resultG + 0.5f),
+                static_cast<uint8_t>(resultB + 0.5f),
+                static_cast<uint8_t>(resultA + 0.5f),
+            };
+        }
+
 #ifdef D3DCOLORVALUE_DEFINED
         constexpr operator D3DCOLORVALUE() const
         {


Test patch: