Repository: microsoft/terminal (language: cpp)

Issue:
Bring back the old titlebar color
<!-- 
🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨

I ACKNOWLEDGE THE FOLLOWING BEFORE PROCEEDING:
1. If I delete this entire template and go my own path, the core team may close my issue without further explanation or engagement.
2. If I list multiple bugs/concerns in this one issue, the core team may close my issue without further explanation or engagement.
3. If I write an issue that has many duplicates, the core team may close my issue without further explanation or engagement (and without necessarily spending time to find the exact duplicate ID number).
4. If I leave the title incomplete when filing the issue, the core team may close my issue without further explanation or engagement.
5. If I file something completely blank in the body, the core team may close my issue without further explanation or engagement.

All good? Then proceed!
-->

# Description of the new feature/enhancement
The black titlebar looks incredibly bad. I literally can't believe you did this, maybe it's a bug. It is catastrophically bad without focus. I can't watch it. So much so that I'm willing to reinstall the terminal to the previous release every time after an automatic update.
<!-- 
A clear and concise description of what the problem is that the new feature would solve.
Describe why and how a user would use this new functionality (if applicable).
-->

# Proposed technical implementation details (optional)
![image](https://user-images.githubusercontent.com/35394377/164107883-7f9264f6-4a92-48ec-9a5e-1ca759e8b14a.png)
Please friends
<!-- 
A clear and concise description of what you want to happen.
-->



Discussion / hints:

Fix patch:
diff --git a/.github/actions/spelling/allow/apis.txt b/.github/actions/spelling/allow/apis.txt
index d44c2ddf837..0b8c0d91a9f 100644
--- a/.github/actions/spelling/allow/apis.txt
+++ b/.github/actions/spelling/allow/apis.txt
@@ -30,6 +30,8 @@ DERR
 dlldata
 DONTADDTORECENT
 DWORDLONG
+DWMSBT
+DWMWA
 endfor
 enumset
 environstrings
@@ -95,6 +97,7 @@ lround
 Lsa
 lsass
 LSHIFT
+MAINWINDOW
 memchr
 memicmp
 MENUCOMMAND
@@ -176,6 +179,8 @@ Stubless
 Subheader
 Subpage
 syscall
+SYSTEMBACKDROP
+TABROW
 TASKBARCREATED
 TBPF
 THEMECHANGED
diff --git a/src/cascadia/LocalTests_SettingsModel/SettingsModel.LocalTests.vcxproj b/src/cascadia/LocalTests_SettingsModel/SettingsModel.LocalTests.vcxproj
index 47214270725..76e0f07e293 100644
--- a/src/cascadia/LocalTests_SettingsModel/SettingsModel.LocalTests.vcxproj
+++ b/src/cascadia/LocalTests_SettingsModel/SettingsModel.LocalTests.vcxproj
@@ -41,6 +41,7 @@
     <ClCompile Include="DeserializationTests.cpp" />
     <ClCompile Include="SerializationTests.cpp" />
     <ClCompile Include="TerminalSettingsTests.cpp" />
+    <ClCompile Include="ThemeTests.cpp" />
     <ClCompile Include="pch.cpp">
       <PrecompiledHeader>Create</PrecompiledHeader>
     </ClCompile>
diff --git a/src/cascadia/LocalTests_SettingsModel/ThemeTests.cpp b/src/cascadia/LocalTests_SettingsModel/ThemeTests.cpp
new file mode 100644
index 00000000000..25e115a0cd7
--- /dev/null
+++ b/src/cascadia/LocalTests_SettingsModel/ThemeTests.cpp
@@ -0,0 +1,276 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT license.
+
+#include "pch.h"
+
+#include "../TerminalSettingsModel/Theme.h"
+#include "../TerminalSettingsModel/CascadiaSettings.h"
+#include "../types/inc/colorTable.hpp"
+#include "JsonTestClass.h"
+
+#include <defaults.h>
+
+using namespace Microsoft::Console;
+using namespace winrt::Microsoft::Terminal;
+using namespace winrt::Microsoft::Terminal::Settings::Model::implementation;
+using namespace WEX::Logging;
+using namespace WEX::TestExecution;
+using namespace WEX::Common;
+
+namespace SettingsModelLocalTests
+{
+    // TODO:microsoft/terminal#3838:
+    // Unfortunately, these tests _WILL NOT_ work in our CI. We're waiting for
+    // an updated TAEF that will let us install framework packages when the test
+    // package is deployed. Until then, these tests won't deploy in CI.
+
+    class ThemeTests : public JsonTestClass
+    {
+        // Use a custom AppxManifest to ensure that we can activate winrt types
+        // from our test. This property will tell taef to manually use this as
+        // the AppxManifest for this test class.
+        // This does not yet work for anything XAML-y. See TabTests.cpp for more
+        // details on that.
+        BEGIN_TEST_CLASS(ThemeTests)
+            TEST_CLASS_PROPERTY(L"RunAs", L"UAP")
+            TEST_CLASS_PROPERTY(L"UAP:AppXManifest", L"TestHostAppXManifest.xml")
+        END_TEST_CLASS()
+
+        TEST_METHOD(ParseSimpleTheme);
+        TEST_METHOD(ParseEmptyTheme);
+        TEST_METHOD(ParseNoWindowTheme);
+        TEST_METHOD(ParseNullWindowTheme);
+        TEST_METHOD(ParseThemeWithNullThemeColor);
+        TEST_METHOD(InvalidCurrentTheme);
+
+        static Core::Color rgb(uint8_t r, uint8_t g, uint8_t b) noexcept
+        {
+            return Core::Color{ r, g, b, 255 };
+        }
+    };
+
+    void ThemeTests::ParseSimpleTheme()
+    {
+        static constexpr std::string_view orangeTheme{ R"({
+            "name": "orange",
+            "tabRow":
+            {
+                "background": "#FFFF8800",
+                "unfocusedBackground": "#FF884400"
+            },
+            "window":
+            {
+                "applicationTheme": "light",
+                "useMica": true
+            }
+        })" };
+
+        const auto schemeObject = VerifyParseSucceeded(orangeTheme);
+        auto theme = Theme::FromJson(schemeObject);
+        VERIFY_ARE_EQUAL(L"orange", theme->Name());
+
+        VERIFY_IS_NOT_NULL(theme->TabRow());
+        VERIFY_IS_NOT_NULL(theme->TabRow().Background());
+        VERIFY_ARE_EQUAL(Settings::Model::ThemeColorType::Color, theme->TabRow().Background().ColorType());
+        VERIFY_ARE_EQUAL(rgb(0xff, 0x88, 0x00), theme->TabRow().Background().Color());
+
+        VERIFY_IS_NOT_NULL(theme->Window());
+        VERIFY_ARE_EQUAL(winrt::Windows::UI::Xaml::ElementTheme::Light, theme->Window().RequestedTheme());
+        VERIFY_ARE_EQUAL(true, theme->Window().UseMica());
+    }
+
+    void ThemeTests::ParseEmptyTheme()
+    {
+        Log::Comment(L"This theme doesn't have any elements defined.");
+        static constexpr std::string_view emptyTheme{ R"({
+            "name": "empty"
+        })" };
+
+        const auto schemeObject = VerifyParseSucceeded(emptyTheme);
+        auto theme = Theme::FromJson(schemeObject);
+        VERIFY_ARE_EQUAL(L"empty", theme->Name());
+        VERIFY_IS_NULL(theme->TabRow());
+        VERIFY_IS_NULL(theme->Window());
+        VERIFY_ARE_EQUAL(winrt::Windows::UI::Xaml::ElementTheme::Default, theme->RequestedTheme());
+    }
+
+    void ThemeTests::ParseNoWindowTheme()
+    {
+        Log::Comment(L"This theme doesn't have a window defined.");
+        static constexpr std::string_view emptyTheme{ R"({
+            "name": "noWindow",
+            "tabRow":
+            {
+                "background": "#FF112233",
+                "unfocusedBackground": "#FF884400"
+            },
+        })" };
+
+        const auto schemeObject = VerifyParseSucceeded(emptyTheme);
+        auto theme = Theme::FromJson(schemeObject);
+        VERIFY_ARE_EQUAL(L"noWindow", theme->Name());
+
+        VERIFY_IS_NOT_NULL(theme->TabRow());
+        VERIFY_IS_NOT_NULL(theme->TabRow().Background());
+        VERIFY_ARE_EQUAL(Settings::Model::ThemeColorType::Color, theme->TabRow().Background().ColorType());
+        VERIFY_ARE_EQUAL(rgb(0x11, 0x22, 0x33), theme->TabRow().Background().Color());
+
+        VERIFY_IS_NULL(theme->Window());
+        VERIFY_ARE_EQUAL(winrt::Windows::UI::Xaml::ElementTheme::Default, theme->RequestedTheme());
+    }
+
+    void ThemeTests::ParseNullWindowTheme()
+    {
+        Log::Comment(L"This theme doesn't have a window defined.");
+        static constexpr std::string_view emptyTheme{ R"({
+            "name": "nullWindow",
+            "tabRow":
+            {
+                "background": "#FF112233",
+                "unfocusedBackground": "#FF884400"
+            },
+            "window": null
+        })" };
+
+        const auto schemeObject = VerifyParseSucceeded(emptyTheme);
+        auto theme = Theme::FromJson(schemeObject);
+        VERIFY_ARE_EQUAL(L"nullWindow", theme->Name());
+
+        VERIFY_IS_NOT_NULL(theme->TabRow());
+        VERIFY_IS_NOT_NULL(theme->TabRow().Background());
+        VERIFY_ARE_EQUAL(Settings::Model::ThemeColorType::Color, theme->TabRow().Background().ColorType());
+        VERIFY_ARE_EQUAL(rgb(0x11, 0x22, 0x33), theme->TabRow().Background().Color());
+
+        VERIFY_IS_NULL(theme->Window());
+        VERIFY_ARE_EQUAL(winrt::Windows::UI::Xaml::ElementTheme::Default, theme->RequestedTheme());
+    }
+
+    void ThemeTests::ParseThemeWithNullThemeColor()
+    {
+        Log::Comment(L"These themes are all missing a tabRow background. Make sure we don't somehow default-construct one for them");
+
+        static constexpr std::string_view settingsString{ R"json({
+            "themes": [
+                {
+                    "name": "backgroundEmpty",
+                    "tabRow":
+                    {
+                    },
+                    "window":
+                    {
+                        "applicationTheme": "light",
+                        "useMica": true
+                    }
+                },
+                {
+                    "name": "backgroundNull",
+                    "tabRow":
+                    {
+                        "background": null
+                    },
+                    "window":
+                    {
+                        "applicationTheme": "light",
+                        "useMica": true
+                    }
+                },
+                {
+                    "name": "backgroundOmittedEntirely",
+                    "window":
+                    {
+                        "applicationTheme": "light",
+                        "useMica": true
+                    }
+                }
+            ]
+        })json" };
+
+        try
+        {
+            const auto settings{ winrt::make_self<CascadiaSettings>(settingsString, DefaultJson) };
+
+            const auto& themes{ settings->GlobalSettings().Themes() };
+            {
+                const auto& backgroundEmpty{ themes.Lookup(L"backgroundEmpty") };
+                VERIFY_ARE_EQUAL(L"backgroundEmpty", backgroundEmpty.Name());
+                VERIFY_IS_NOT_NULL(backgroundEmpty.TabRow());
+                VERIFY_IS_NULL(backgroundEmpty.TabRow().Background());
+            }
+            {
+                const auto& backgroundNull{ themes.Lookup(L"backgroundNull") };
+                VERIFY_ARE_EQUAL(L"backgroundNull", backgroundNull.Name());
+                VERIFY_IS_NOT_NULL(backgroundNull.TabRow());
+                VERIFY_IS_NULL(backgroundNull.TabRow().Background());
+            }
+            {
+                const auto& backgroundOmittedEntirely{ themes.Lookup(L"backgroundOmittedEntirely") };
+                VERIFY_ARE_EQUAL(L"backgroundOmittedEntirely", backgroundOmittedEntirely.Name());
+                VERIFY_IS_NULL(backgroundOmittedEntirely.TabRow());
+            }
+        }
+        catch (const SettingsException& ex)
+        {
+            auto loadError = ex.Error();
+            loadError;
+            throw ex;
+        }
+        catch (const SettingsTypedDeserializationException& e)
+        {
+            auto deserializationErrorMessage = til::u8u16(e.what());
+            Log::Comment(NoThrowString().Format(deserializationErrorMessage.c_str()));
+            throw e;
+        }
+    }
+
+    void ThemeTests::InvalidCurrentTheme()
+    {
+        Log::Comment(L"Make sure specifying an invalid theme falls back to a sensible default.");
+
+        static constexpr std::string_view settingsString{ R"json({
+            "theme": "foo",
+            "themes": [
+                {
+                    "name": "bar",
+                    "tabRow": {},
+                    "window":
+                    {
+                        "applicationTheme": "light",
+                        "useMica": true
+                    }
+                }
+            ]
+        })json" };
+
+        try
+        {
+            const auto settings{ winrt::make_self<CascadiaSettings>(settingsString, DefaultJson) };
+
+            VERIFY_ARE_EQUAL(1u, settings->Warnings().Size());
+            VERIFY_ARE_EQUAL(Settings::Model::SettingsLoadWarnings::UnknownTheme, settings->Warnings().GetAt(0));
+
+            const auto& themes{ settings->GlobalSettings().Themes() };
+            {
+                const auto& bar{ themes.Lookup(L"bar") };
+                VERIFY_ARE_EQUAL(L"bar", bar.Name());
+                VERIFY_IS_NOT_NULL(bar.TabRow());
+                VERIFY_IS_NULL(bar.TabRow().Background());
+            }
+
+            const auto currentTheme{ settings->GlobalSettings().CurrentTheme() };
+            VERIFY_IS_NOT_NULL(currentTheme);
+            VERIFY_ARE_EQUAL(L"system", currentTheme.Name());
+        }
+        catch (const SettingsException& ex)
+        {
+            auto loadError = ex.Error();
+            loadError;
+            throw ex;
+        }
+        catch (const SettingsTypedDeserializationException& e)
+        {
+            auto deserializationErrorMessage = til::u8u16(e.what());
+            Log::Comment(NoThrowString().Format(deserializationErrorMessage.c_str()));
+            throw e;
+        }
+    }
+}
diff --git a/src/cascadia/TerminalApp/AppLogic.cpp b/src/cascadia/TerminalApp/AppLogic.cpp
index 8f58309ff53..4a05ef2f21d 100644
--- a/src/cascadia/TerminalApp/AppLogic.cpp
+++ b/src/cascadia/TerminalApp/AppLogic.cpp
@@ -51,6 +51,7 @@ static const std::array settingsLoadWarningsLabels {
     USES_RESOURCE(L"InvalidSplitSize"),
     USES_RESOURCE(L"FailedToParseStartupActions"),
     USES_RESOURCE(L"FailedToParseSubCommands"),
+    USES_RESOURCE(L"UnknownTheme"),
 };
 static const std::array settingsLoadErrorsLabels {
     USES_RESOURCE(L"NoProfilesText"),
@@ -367,11 +368,12 @@ namespace winrt::TerminalApp::implementation
         // details here, but it does have the desired effect.
         // It's not enough to set the theme on the dialog alone.
         auto themingLambda{ [this](const Windows::Foundation::IInspectable& sender, const RoutedEventArgs&) {
-            auto theme{ _settings.GlobalSettings().Theme() };
+            auto theme{ _settings.GlobalSettings().CurrentTheme() };
+            auto requestedTheme{ theme.RequestedTheme() };
             auto element{ sender.try_as<winrt::Windows::UI::Xaml::FrameworkElement>() };
             while (element)
             {
-                element.RequestedTheme(theme);
+                element.RequestedTheme(requestedTheme);
                 element = element.Parent().try_as<winrt::Windows::UI::Xaml::FrameworkElement>();
             }
         } };
@@ -737,13 +739,7 @@ namespace winrt::TerminalApp::implementation
 
     winrt::Windows::UI::Xaml::ElementTheme AppLogic::GetRequestedTheme()
     {
-        if (!_loadedInitialSettings)
-        {
-            // Load settings if we haven't already
-            LoadSettings();
-        }
-
-        return _settings.GlobalSettings().Theme();
+        return Theme().RequestedTheme();
     }
 
     bool AppLogic::GetShowTabsInTitlebar()
@@ -964,7 +960,7 @@ namespace winrt::TerminalApp::implementation
 
     void AppLogic::_RefreshThemeRoutine()
     {
-        _ApplyTheme(_settings.GlobalSettings().Theme());
+        _ApplyTheme(GetRequestedTheme());
     }
 
     // Function Description:
@@ -1219,6 +1215,19 @@ namespace winrt::TerminalApp::implementation
         return {};
     }
 
+    winrt::Windows::UI::Xaml::Media::Brush AppLogic::TitlebarBrush()
+    {
+        if (_root)
+        {
+            return _root->TitlebarBrush();
+        }
+        return { nullptr };
+    }
+    void AppLogic::WindowActivated(const bool activated)
+    {
+        _root->WindowActivated(activated);
+    }
+
     bool AppLogic::HasCommandlineArguments() const noexcept
     {
         return _hasCommandLineArguments;
@@ -1645,4 +1654,15 @@ namespace winrt::TerminalApp::implementation
     {
         return _settings.GlobalSettings().ShowTitleInTitlebar();
     }
+
+    Microsoft::Terminal::Settings::Model::Theme AppLogic::Theme()
+    {
+        if (!_loadedInitialSettings)
+        {
+            // Load settings if we haven't already
+            LoadSettings();
+        }
+        return _settings.GlobalSettings().CurrentTheme();
+    }
+
 }
diff --git a/src/cascadia/TerminalApp/AppLogic.h b/src/cascadia/TerminalApp/AppLogic.h
index 63d1c4e784d..2ba2deaeffb 100644
--- a/src/cascadia/TerminalApp/AppLogic.h
+++ b/src/cascadia/TerminalApp/AppLogic.h
@@ -117,6 +117,8 @@ namespace winrt::TerminalApp::implementation
         void WindowVisibilityChanged(const bool showOrHide);
 
         winrt::TerminalApp::TaskbarState TaskbarState();
+        winrt::Windows::UI::Xaml::Media::Brush TitlebarBrush();
+        void WindowActivated(const bool activated);
 
         bool GetMinimizeToNotificationArea();
         bool GetAlwaysShowNotificationIcon();
@@ -127,7 +129,13 @@ namespace winrt::TerminalApp::implementation
 
         Windows::Foundation::Collections::IMapView<Microsoft::Terminal::Control::KeyChord, Microsoft::Terminal::Settings::Model::Command> GlobalHotkeys();
 
+        Microsoft::Terminal::Settings::Model::Theme Theme();
+
         // -------------------------------- WinRT Events ---------------------------------
+        // PropertyChanged is surprisingly not a typed event, so we'll define that one manually.
+        winrt::event_token PropertyChanged(Windows::UI::Xaml::Data::PropertyChangedEventHandler const& handler) { return _root->PropertyChanged(handler); }
+        void PropertyChanged(winrt::event_token const& token) { _root->PropertyChanged(token); }
+
         TYPED_EVENT(RequestedThemeChanged, winrt::Windows::Foundation::IInspectable, winrt::Windows::UI::Xaml::ElementTheme);
         TYPED_EVENT(SettingsChanged, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable);
         TYPED_EVENT(SystemMenuChangeRequested, winrt::Windows::Foundation::IInspectable, winrt::TerminalApp::SystemMenuChangeArgs);
diff --git a/src/cascadia/TerminalApp/AppLogic.idl b/src/cascadia/TerminalApp/AppLogic.idl
index a5c0eb05a7b..42096bd08d1 100644
--- a/src/cascadia/TerminalApp/AppLogic.idl
+++ b/src/cascadia/TerminalApp/AppLogic.idl
@@ -35,7 +35,7 @@ namespace TerminalApp
 
     // See IDialogPresenter and TerminalPage's DialogPresenter for more
     // information.
-    [default_interface] runtimeclass AppLogic : IDirectKeyListener, IDialogPresenter
+    [default_interface] runtimeclass AppLogic : IDirectKeyListener, IDialogPresenter, Windows.UI.Xaml.Data.INotifyPropertyChanged
     {
         AppLogic();
 
@@ -94,6 +94,8 @@ namespace TerminalApp
         void WindowVisibilityChanged(Boolean showOrHide);
 
         TaskbarState TaskbarState{ get; };
+        Windows.UI.Xaml.Media.Brush TitlebarBrush { get; };
+        void WindowActivated(Boolean activated);
 
         Boolean ShouldUsePersistedLayout();
         Boolean ShouldImmediatelyHandoffToElevated();
@@ -105,6 +107,8 @@ namespace TerminalApp
         Boolean GetAlwaysShowNotificationIcon();
         Boolean GetShowTitleInTitlebar();
 
+        Microsoft.Terminal.Settings.Model.Theme Theme { get; };
+
         FindTargetWindowResult FindTargetWindow(String[] args);
 
         Windows.Foundation.Collections.IMapView<Microsoft.Terminal.Control.KeyChord, Microsoft.Terminal.Settings.Model.Command> GlobalHotkeys();
diff --git a/src/cascadia/TerminalApp/HighlightedText.h b/src/cascadia/TerminalApp/HighlightedText.h
index e71c7ef7523..c9c29e7cbb8 100644
--- a/src/cascadia/TerminalApp/HighlightedText.h
+++ b/src/cascadia/TerminalApp/HighlightedText.h
@@ -3,8 +3,6 @@
 
 #pragma once
 
-#include "winrt/Microsoft.UI.Xaml.Controls.h"
-
 #include "HighlightedTextSegment.g.h"
 #include "HighlightedText.g.h"
 
diff --git a/src/cascadia/TerminalApp/Resources/en-US/Resources.resw b/src/cascadia/TerminalApp/Resources/en-US/Resources.resw
index a0a653927e3..29e70316f88 100644
--- a/src/cascadia/TerminalApp/Resources/en-US/Resources.resw
+++ b/src/cascadia/TerminalApp/Resources/en-US/Resources.resw
@@ -242,6 +242,10 @@
     <value>&#x2022; Found a keybinding that was missing a required parameter value. This keybinding will be ignored.</value>
     <comment>{Locked="&#x2022;"} This glyph is a bullet, used in a bulleted list.</comment>
   </data>
+  <data name="UnknownTheme" xml:space="preserve">
+    <value>&#x2022; The specified "theme" was not found in the list of themes. Temporarily falling back to the default value.</value>
+    <comment>{Locked="&#x2022;"} This glyph is a bullet, used in a bulleted list.</comment>
+  </data>
   <data name="LegacyGlobalsProperty" xml:space="preserve">
     <value>The "globals" property is deprecated - your settings might need updating. </value>
     <comment>{Locked="\"globals\""} </comment>
diff --git a/src/cascadia/TerminalApp/TabManagement.cpp b/src/cascadia/TerminalApp/TabManagement.cpp
index 47cfa70d7b2..d88b7a7b4fe 100644
--- a/src/cascadia/TerminalApp/TabManagement.cpp
+++ b/src/cascadia/TerminalApp/TabManagement.cpp
@@ -154,6 +154,8 @@ namespace winrt::TerminalApp::implementation
                 // Possibly update the icon of the tab.
                 page->_UpdateTabIcon(*tab);
 
+                page->_updateTabRowColors();
+
                 // Update the taskbar progress as well. We'll raise our own
                 // SetTaskbarProgress event here, to get tell the hosting
                 // application to re-query this value from us.
@@ -925,6 +927,8 @@ namespace winrt::TerminalApp::implementation
                 _TitleChangedHandlers(*this, tab.Title());
             }
 
+            _updateTabRowColors();
+
             auto tab_impl = _GetTerminalTabImpl(tab);
             if (tab_impl)
             {
diff --git a/src/cascadia/TerminalApp/TerminalPage.cpp b/src/cascadia/TerminalApp/TerminalPage.cpp
index 197cbce47c6..a975a7f6149 100644
--- a/src/cascadia/TerminalApp/TerminalPage.cpp
+++ b/src/cascadia/TerminalApp/TerminalPage.cpp
@@ -183,43 +183,6 @@ namespace winrt::TerminalApp::implementation
 
         const auto isElevated = IsElevated();
 
-        if (_settings.GlobalSettings().UseAcrylicInTabRow())
-        {
-            const auto res = Application::Current().Resources();
-            const auto lightKey = winrt::box_value(L"Light");
-            const auto darkKey = winrt::box_value(L"Dark");
-            const auto tabViewBackgroundKey = winrt::box_value(L"TabViewBackground");
-
-            for (const auto& dictionary : res.MergedDictionaries())
-            {
-                // Don't change MUX resources
-                if (dictionary.Source())
-                {
-                    continue;
-                }
-
-                for (const auto& kvPair : dictionary.ThemeDictionaries())
-                {
-                    const auto themeDictionary = kvPair.Value().as<winrt::Windows::UI::Xaml::ResourceDictionary>();
-
-                    if (themeDictionary.HasKey(tabViewBackgroundKey))
-                    {
-                        const auto backgroundSolidBrush = themeDictionary.Lookup(tabViewBackgroundKey).as<Media::SolidColorBrush>();
-
-                        const til::color backgroundColor = backgroundSolidBrush.Color();
-
-                        const auto acrylicBrush = Media::AcrylicBrush();
-                        acrylicBrush.BackgroundSource(Media::AcrylicBackgroundSource::HostBackdrop);
-                        acrylicBrush.FallbackColor(backgroundColor);
-                        acrylicBrush.TintColor(backgroundColor);
-                        acrylicBrush.TintOpacity(0.5);
-
-                        themeDictionary.Insert(tabViewBackgroundKey, acrylicBrush);
-                    }
-                }
-            }
-        }
-
         _tabRow.PointerMoved({ get_weak(), &TerminalPage::_RestorePointerCursorHandler });
         _tabView.CanReorderTabs(!isElevated);
         _tabView.CanDragTabs(!isElevated);
@@ -260,6 +223,7 @@ namespace winrt::TerminalApp::implementation
             transparent.Color(Windows::UI::Colors::Transparent());
             _tabRow.Background(transparent);
         }
+        _updateTabRowColors();
 
         // Hookup our event handlers to the ShortcutActionDispatch
         _RegisterActionCallbacks();
@@ -1471,6 +1435,16 @@ namespace winrt::TerminalApp::implementation
 
         term.ConnectionStateChanged({ get_weak(), &TerminalPage::_ConnectionStateChangedHandler });
 
+        term.PropertyChanged([weakThis = get_weak()](auto& /*sender*/, auto& e) {
+            if (auto page{ weakThis.get() })
+            {
+                if (e.PropertyName() == L"BackgroundBrush")
+                {
+                    page->_updateTabRowColors();
+                }
+            }
+        });
+
         term.ShowWindowChanged({ get_weak(), &TerminalPage::_ShowWindowChangedHandler });
     }
 
@@ -1510,37 +1484,9 @@ namespace winrt::TerminalApp::implementation
             }
         });
 
-        // react on color changed events
-        hostingTab.ColorSelected([weakTab, weakThis](auto&& color) {
-            auto page{ weakThis.get() };
-            auto tab{ weakTab.get() };
-
-            if (page && tab && (tab->FocusState() != FocusState::Unfocused))
-            {
-                page->_SetNonClientAreaColors(color);
-            }
-        });
-
-        hostingTab.ColorCleared([weakTab, weakThis]() {
-            auto page{ weakThis.get() };
-            auto tab{ weakTab.get() };
-
-            if (page && tab && (tab->FocusState() != FocusState::Unfocused))
-            {
-                page->_ClearNonClientAreaColors();
-            }
-        });
-
         // Add an event handler for when the terminal or tab wants to set a
         // progress indicator on the taskbar
         hostingTab.TaskbarProgressChanged({ get_weak(), &TerminalPage::_SetTaskbarProgressHandler });
-
-        // TODO GH#3327: Once we support colorizing the NewTab button based on
-        // the color of the tab, we'll want to make sure to call
-        // _ClearNewTabButtonColor here, to reset it to the default (for the
-        // newly created tab).
-        // remove any colors left by other colored tabs
-        // _ClearNewTabButtonColor();
     }
 
     // Method Description:
@@ -2750,6 +2696,13 @@ namespace winrt::TerminalApp::implementation
         WUX::Media::Animation::Timeline::AllowDependentAnimations(!_settings.GlobalSettings().DisableAnimations());
 
         _tabRow.ShowElevationShield(IsElevated() && _settings.GlobalSettings().ShowAdminShield());
+
+        Media::SolidColorBrush transparent{ Windows::UI::Colors::Transparent() };
+        _tabView.Background(transparent);
+
+        ////////////////////////////////////////////////////////////////////////
+        // Begin Theme handling
+        _updateTabRowColors();
     }
 
     // This is a helper to aid in sorting commands by their `Name`s, alphabetically.
@@ -3134,32 +3087,6 @@ namespace winrt::TerminalApp::implementation
         _newTabButton.Foreground(foregroundBrush);
     }
 
-    // Method Description:
-    // - Sets the tab split button color when a new tab color is selected
-    // - This method could also set the color of the title bar and tab row
-    // in the future
-    // Arguments:
-    // - selectedTabColor: The color of the newly selected tab
-    // Return Value:
-    // - <none>
-    void TerminalPage::_SetNonClientAreaColors(const Windows::UI::Color& /*selectedTabColor*/)
-    {
-        // TODO GH#3327: Look at what to do with the NC area when we have XAML theming
-    }
-
-    // Method Description:
-    // - Clears the tab split button color when the tab's color is cleared
-    // - This method could also clear the color of the title bar and tab row
-    // in the future
-    // Arguments:
-    // - <none>
-    // Return Value:
-    // - <none>
-    void TerminalPage::_ClearNonClientAreaColors()
-    {
-        // TODO GH#3327: Look at what to do with the NC area when we have XAML theming
-    }
-
     // Function Description:
     // - This is a helper method to get the commandline out of a
     //   ExecuteCommandline action, break it into subcommands, and attempt to
@@ -3569,10 +3496,11 @@ namespace winrt::TerminalApp::implementation
     // - <none>
     void TerminalPage::_UpdateTeachingTipTheme(winrt::Windows::UI::Xaml::FrameworkElement element)
     {
-        auto theme{ _settings.GlobalSettings().Theme() };
+        auto theme{ _settings.GlobalSettings().CurrentTheme() };
+        auto requestedTheme{ theme.RequestedTheme() };
         while (element)
         {
-            element.RequestedTheme(theme);
+            element.RequestedTheme(requestedTheme);
             element = element.Parent().try_as<winrt::Windows::UI::Xaml::FrameworkElement>();
         }
     }
@@ -4076,4 +4004,132 @@ namespace winrt::TerminalApp::implementation
         applicationState.DismissedMessages(std::move(messages));
     }
 
+    void TerminalPage::_updateTabRowColors()
+    {
+        if (_settings == nullptr)
+        {
+            return;
+        }
+
+        const auto theme = _settings.GlobalSettings().CurrentTheme();
+        const auto requestedTheme{ theme.RequestedTheme() };
+
+        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>();
+
+        til::color bgColor = backgroundSolidBrush.Color();
+
+        if (_settings.GlobalSettings().UseAcrylicInTabRow())
+        {
+            const til::color backgroundColor = backgroundSolidBrush.Color();
+            const auto acrylicBrush = Media::AcrylicBrush();
+            acrylicBrush.BackgroundSource(Media::AcrylicBackgroundSource::HostBackdrop);
+            acrylicBrush.FallbackColor(bgColor);
+            acrylicBrush.TintColor(bgColor);
+            acrylicBrush.TintOpacity(0.5);
+
+            TitlebarBrush(acrylicBrush);
+        }
+        else if (theme.TabRow() && theme.TabRow().Background())
+        {
+            const auto tabRowBg = theme.TabRow().Background();
+            const auto terminalBrush = [this]() -> Media::Brush {
+                if (const auto& control{ _GetActiveControl() })
+                {
+                    return control.BackgroundBrush();
+                }
+                else if (auto settingsTab = _GetFocusedTab().try_as<TerminalApp::SettingsTab>())
+                {
+                    return settingsTab.Content().try_as<Settings::Editor::MainPage>().BackgroundBrush();
+                }
+                return nullptr;
+            }();
+
+            const auto themeBrush{ tabRowBg.Evaluate(res, terminalBrush, true) };
+            bgColor = ThemeColor::ColorFromBrush(themeBrush);
+            TitlebarBrush(themeBrush);
+        }
+        else
+        {
+            // Nothing was set in the theme - fall back to our original `TabViewBackground` color.
+            TitlebarBrush(backgroundSolidBrush);
+        }
+
+        if (!_settings.GlobalSettings().ShowTabsInTitlebar())
+        {
+            _tabRow.Background(TitlebarBrush());
+        }
+
+        // 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.
+        _SetNewTabButtonColor(bgColor, bgColor);
+    }
+
+    void TerminalPage::WindowActivated(const bool activated)
+    {
+        // Stash if we're activated. Use that when we reload
+        // the settings, change active panes, etc.
+        _activated = activated;
+        _updateTabRowColors();
+    }
 }
diff --git a/src/cascadia/TerminalApp/TerminalPage.h b/src/cascadia/TerminalApp/TerminalPage.h
index 310910f5228..f4e8091dd93 100644
--- a/src/cascadia/TerminalApp/TerminalPage.h
+++ b/src/cascadia/TerminalApp/TerminalPage.h
@@ -134,6 +134,7 @@ namespace winrt::TerminalApp::implementation
         bool IsElevated() const noexcept;
 
         void OpenSettingsUI();
+        void WindowActivated(const bool activated);
 
         WINRT_CALLBACK(PropertyChanged, Windows::UI::Xaml::Data::PropertyChangedEventHandler);
 
@@ -157,6 +158,8 @@ namespace winrt::TerminalApp::implementation
         TYPED_EVENT(QuitRequested, IInspectable, IInspectable);
         TYPED_EVENT(ShowWindowChanged, IInspectable, winrt::Microsoft::Terminal::Control::ShowWindowArgs)
 
+        WINRT_OBSERVABLE_PROPERTY(winrt::Windows::UI::Xaml::Media::Brush, TitlebarBrush, _PropertyChangedHandlers, nullptr);
+
     private:
         friend struct TerminalPageT<TerminalPage>; // for Xaml to bind events
         std::optional<HWND> _hostingHwnd;
@@ -196,6 +199,8 @@ namespace winrt::TerminalApp::implementation
         std::optional<int> _rearrangeFrom{};
         std::optional<int> _rearrangeTo{};
         bool _removing{ false };
+
+        bool _activated{ false };
         bool _visible{ true };
 
         std::vector<std::vector<Microsoft::Terminal::Settings::Model::ActionAndArgs>> _previouslyClosedPanesAndTabs{};
@@ -383,8 +388,6 @@ namespace winrt::TerminalApp::implementation
 
         void _RefreshUIForSettingsReload();
 
-        void _SetNonClientAreaColors(const Windows::UI::Color& selectedTabColor);
-        void _ClearNonClientAreaColors();
         void _SetNewTabButtonColor(const Windows::UI::Color& color, const Windows::UI::Color& accentColor);
         void _ClearNewTabButtonColor();
 
@@ -443,6 +446,8 @@ namespace winrt::TerminalApp::implementation
         static bool _IsMessageDismissed(const winrt::Microsoft::Terminal::Settings::Model::InfoBarMessage& message);
         static void _DismissMessage(const winrt::Microsoft::Terminal::Settings::Model::InfoBarMessage& message);
 
+        void _updateTabRowColors();
+
         winrt::fire_and_forget _ShowWindowChangedHandler(const IInspectable sender, const winrt::Microsoft::Terminal::Control::ShowWindowArgs args);
 
 #pragma region ActionHandlers
diff --git a/src/cascadia/TerminalApp/TerminalPage.idl b/src/cascadia/TerminalApp/TerminalPage.idl
index 90d317c1d98..89ea3ec3277 100644
--- a/src/cascadia/TerminalApp/TerminalPage.idl
+++ b/src/cascadia/TerminalApp/TerminalPage.idl
@@ -45,6 +45,9 @@ namespace TerminalApp
 
         TaskbarState TaskbarState{ get; };
 
+        Windows.UI.Xaml.Media.Brush TitlebarBrush { get; };
+        void WindowActivated(Boolean activated);
+
         event Windows.Foundation.TypedEventHandler<Object, String> TitleChanged;
         event Windows.Foundation.TypedEventHandler<Object, LastTabClosedEventArgs> LastTabClosed;
         event Windows.Foundation.TypedEventHandler<Object, Windows.UI.Xaml.UIElement> SetTitleBarContent;
diff --git a/src/cascadia/TerminalApp/TitlebarControl.xaml b/src/cascadia/TerminalApp/TitlebarControl.xaml
index fde78d3a81a..e5eda4f5041 100644
--- a/src/cascadia/TerminalApp/TitlebarControl.xaml
+++ b/src/cascadia/TerminalApp/TitlebarControl.xaml
@@ -13,7 +13,6 @@
       VerticalAlignment="Top"
       d:DesignHeight="36"
       d:DesignWidth="400"
-      Background="{ThemeResource TabViewBackground}"
       SizeChanged="Root_SizeChanged"
       mc:Ignorable="d">
 
diff --git a/src/cascadia/TerminalControl/TermControl.cpp b/src/cascadia/TerminalControl/TermControl.cpp
index 7a1b57df3a6..3122093d19c 100644
--- a/src/cascadia/TerminalControl/TermControl.cpp
+++ b/src/cascadia/TerminalControl/TermControl.cpp
@@ -568,6 +568,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation
 
             RootGrid().Background(solidColor);
         }
+
+        BackgroundBrush(RootGrid().Background());
     }
 
     // Method Description:
@@ -613,6 +615,20 @@ namespace winrt::Microsoft::Terminal::Control::implementation
         {
             solidColor.Color(bg);
         }
+
+        BackgroundBrush(RootGrid().Background());
+
+        // Don't use the normal BackgroundBrush() Observable Property setter
+        // here. (e.g. `BackgroundBrush()`). The one from the macro will
+        // automatically ignore changes where the value doesn't _actually_
+        // change. In our case, most of the time when changing the colors of the
+        // background, the _Brush_ itself doesn't change, we simply change the
+        // Color() of the brush. This results in the event not getting bubbled
+        // up.
+        //
+        // Firing it manually makes sure it does.
+        _BackgroundBrush = RootGrid().Background();
+        _PropertyChangedHandlers(*this, Windows::UI::Xaml::Data::PropertyChangedEventArgs{ L"BackgroundBrush" });
     }
 
     // Method Description:
diff --git a/src/cascadia/TerminalControl/TermControl.h b/src/cascadia/TerminalControl/TermControl.h
index e67f7c773ce..655fce03fdb 100644
--- a/src/cascadia/TerminalControl/TermControl.h
+++ b/src/cascadia/TerminalControl/TermControl.h
@@ -130,6 +130,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation
 
         void AdjustOpacity(const double opacity, const bool relative);
 
+        WINRT_CALLBACK(PropertyChanged, Windows::UI::Xaml::Data::PropertyChangedEventHandler);
+
         // -------------------------------- WinRT Events ---------------------------------
         // clang-format off
         WINRT_CALLBACK(FontSizeChanged, Control::FontSizeChangedEventArgs);
@@ -153,6 +155,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation
         TYPED_EVENT(WarningBell,               IInspectable, IInspectable);
         // clang-format on
 
+        WINRT_OBSERVABLE_PROPERTY(winrt::Windows::UI::Xaml::Media::Brush, BackgroundBrush, _PropertyChangedHandlers, nullptr);
+
     private:
         friend struct TermControlT<TermControl>; // friend our parent so it can bind private event handlers
 
diff --git a/src/cascadia/TerminalControl/TermControl.idl b/src/cascadia/TerminalControl/TermControl.idl
index 3d8cde2714e..101359b30e9 100644
--- a/src/cascadia/TerminalControl/TermControl.idl
+++ b/src/cascadia/TerminalControl/TermControl.idl
@@ -14,7 +14,8 @@ namespace Microsoft.Terminal.Control
     [default_interface] runtimeclass TermControl : Windows.UI.Xaml.Controls.UserControl,
                                                    IDirectKeyListener,
                                                    IMouseWheelListener,
-                                                   ICoreState
+                                                   ICoreState,
+                                                   Windows.UI.Xaml.Data.INotifyPropertyChanged
     {
         TermControl(IControlSettings settings,
                     IControlAppearance unfocusedAppearance,
@@ -90,5 +91,6 @@ namespace Microsoft.Terminal.Control
         // opacity set by the settings should call this instead.
         Double BackgroundOpacity { get; };
 
+        Windows.UI.Xaml.Media.Brush BackgroundBrush { get; };
     }
 }
diff --git a/src/cascadia/TerminalSettingsEditor/GlobalAppearance.cpp b/src/cascadia/TerminalSettingsEditor/GlobalAppearance.cpp
index b34bf0a1e95..1c6032b8329 100644
--- a/src/cascadia/TerminalSettingsEditor/GlobalAppearance.cpp
+++ b/src/cascadia/TerminalSettingsEditor/GlobalAppearance.cpp
@@ -41,17 +41,22 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
         L"zh-Hant",
     };
 
-    GlobalAppearance::GlobalAppearance()
+    constexpr std::wstring_view systemThemeName{ L"system" };
+    constexpr std::wstring_view darkThemeName{ L"dark" };
+    constexpr std::wstring_view lightThemeName{ L"light" };
+
+    GlobalAppearance::GlobalAppearance() :
+        _ThemeList{ single_threaded_observable_vector<Model::Theme>() }
     {
         InitializeComponent();
 
-        INITIALIZE_BINDABLE_ENUM_SETTING(Theme, ElementTheme, winrt::Windows::UI::Xaml::ElementTheme, L"Globals_Theme", L"Content");
         INITIALIZE_BINDABLE_ENUM_SETTING(TabWidthMode, TabViewWidthMode, winrt::Microsoft::UI::Xaml::Controls::TabViewWidthMode, L"Globals_TabWidthMode", L"Content");
     }
 
     void GlobalAppearance::OnNavigatedTo(const NavigationEventArgs& e)
     {
         _State = e.Parameter().as<Editor::GlobalAppearancePageNavigationState>();
+        _UpdateThemeList();
     }
 
     winrt::hstring GlobalAppearance::LanguageDisplayConverter(const winrt::hstring& tag)
@@ -195,4 +200,61 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
         }
     }
 
+    // Function Description:
+    // - Updates the list of all themes available to choose from.
+    void GlobalAppearance::_UpdateThemeList()
+    {
+        // Surprisingly, though this is called every time we navigate to the page,
+        // the list does not keep growing on each navigation.
+        const auto& ThemeMap{ _State.Globals().Themes() };
+        for (const auto& pair : ThemeMap)
+        {
+            _ThemeList.Append(pair.Value());
+        }
+    }
+
+    winrt::Windows::Foundation::IInspectable GlobalAppearance::CurrentTheme()
+    {
+        return _State.Globals().CurrentTheme();
+    }
+
+    // Get the name out of the newly selected item, stash that as the Theme name
+    // set for the globals. That controls which theme is actually the current
+    // theme.
+    void GlobalAppearance::CurrentTheme(const winrt::Windows::Foundation::IInspectable& tag)
+    {
+        if (const auto& theme{ tag.try_as<Model::Theme>() })
+        {
+            _State.Globals().Theme(theme.Name());
+        }
+    }
+
+    // Method Description:
+    // - Convert the names of the inbox themes to some more descriptive,
+    //   well-known values. If the passed in theme isn't an inbox one, then just
+    //   return its set Name.
+    //    - "light" becomes "Light"
+    //    - "dark" becomes "Dark"
+    //    - "system" becomes "Use Windows theme"
+    // - These values are all localized based on the app language.
+    // Arguments:
+    // - theme: the theme to get the display name for.
+    // Return Value:
+    // - the potentially localized name to use for this Theme.
+    winrt::hstring GlobalAppearance::ThemeNameConverter(const Model::Theme& theme)
+    {
+        if (theme.Name() == darkThemeName)
+        {
+            return RS_(L"Globals_ThemeDark/Content");
+        }
+        else if (theme.Name() == lightThemeName)
+        {
+            return RS_(L"Globals_ThemeLight/Content");
+        }
+        else if (theme.Name() == systemThemeName)
+        {
+            return RS_(L"Globals_ThemeSystem/Content");
+        }
+        return theme.Name();
+    }
 }
diff --git a/src/cascadia/TerminalSettingsEditor/GlobalAppearance.h b/src/cascadia/TerminalSettingsEditor/GlobalAppearance.h
index 7e48178baf4..3da730eb6c6 100644
--- a/src/cascadia/TerminalSettingsEditor/GlobalAppearance.h
+++ b/src/cascadia/TerminalSettingsEditor/GlobalAppearance.h
@@ -26,9 +26,10 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
         void OnNavigatedTo(const winrt::Windows::UI::Xaml::Navigation::NavigationEventArgs& e);
 
         WINRT_PROPERTY(Editor::GlobalAppearancePageNavigationState, State, nullptr);
-        GETSET_BINDABLE_ENUM_SETTING(Theme, winrt::Windows::UI::Xaml::ElementTheme, State().Globals().Theme);
         GETSET_BINDABLE_ENUM_SETTING(TabWidthMode, winrt::Microsoft::UI::Xaml::Controls::TabViewWidthMode, State().Globals().TabWidthMode);
 
+        WINRT_PROPERTY(Windows::Foundation::Collections::IObservableVector<Model::Theme>, ThemeList, nullptr);
+
     public:
         // LanguageDisplayConverter maps the given BCP 47 tag to a localized string.
         // For instance "en-US" produces "English (United States)", while "de-DE" produces
@@ -40,9 +41,16 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
         winrt::Windows::Foundation::IInspectable CurrentLanguage();
         void CurrentLanguage(const winrt::Windows::Foundation::IInspectable& tag);
 
+        winrt::Windows::Foundation::IInspectable CurrentTheme();
+        void CurrentTheme(const winrt::Windows::Foundation::IInspectable& tag);
+        static winrt::hstring ThemeNameConverter(const Model::Theme& theme);
+
     private:
         winrt::Windows::Foundation::Collections::IObservableVector<winrt::hstring> _languageList;
         winrt::Windows::Foundation::IInspectable _currentLanguage;
+        winrt::Windows::Foundation::IInspectable _currentTheme;
+
+        void _UpdateThemeList();
     };
 }
 
diff --git a/src/cascadia/TerminalSettingsEditor/GlobalAppearance.idl b/src/cascadia/TerminalSettingsEditor/GlobalAppearance.idl
index 7e3483140ab..a45ba503fc6 100644
--- a/src/cascadia/TerminalSettingsEditor/GlobalAppearance.idl
+++ b/src/cascadia/TerminalSettingsEditor/GlobalAppearance.idl
@@ -21,7 +21,8 @@ namespace Microsoft.Terminal.Settings.Editor
         IInspectable CurrentLanguage;
 
         IInspectable CurrentTheme;
-        Windows.Foundation.Collections.IObservableVector<Microsoft.Terminal.Settings.Editor.EnumEntry> ThemeList { get; };
+        static String ThemeNameConverter(Microsoft.Terminal.Settings.Model.Theme theme);
+        Windows.Foundation.Collections.IObservableVector<Microsoft.Terminal.Settings.Model.Theme> ThemeList { get; };
 
         IInspectable CurrentTabWidthMode;
         Windows.Foundation.Collections.IObservableVector<Microsoft.Terminal.Settings.Editor.EnumEntry> TabWidthModeList { get; };
diff --git a/src/cascadia/TerminalSettingsEditor/GlobalAppearance.xaml b/src/cascadia/TerminalSettingsEditor/GlobalAppearance.xaml
index 2d15f1c2103..980b5323acb 100644
--- a/src/cascadia/TerminalSettingsEditor/GlobalAppearance.xaml
+++ b/src/cascadia/TerminalSettingsEditor/GlobalAppearance.xaml
@@ -8,6 +8,7 @@
       xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
       xmlns:local="using:Microsoft.Terminal.Settings.Editor"
       xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
+      xmlns:model="using:Microsoft.Terminal.Settings.Model"
       xmlns:muxc="using:Microsoft.UI.Xaml.Controls"
       mc:Ignorable="d">
 
@@ -42,10 +43,15 @@
             <!--  Theme  -->
             <local:SettingContainer x:Uid="Globals_Theme">
                 <ComboBox AutomationProperties.AccessibilityView="Content"
-                          ItemTemplate="{StaticResource EnumComboBoxTemplate}"
                           ItemsSource="{x:Bind ThemeList, Mode=OneWay}"
                           SelectedItem="{x:Bind CurrentTheme, Mode=TwoWay}"
-                          Style="{StaticResource ComboBoxSettingStyle}" />
+                          Style="{StaticResource ComboBoxSettingStyle}">
+                    <ComboBox.ItemTemplate>
+                        <DataTemplate x:DataType="model:Theme">
+                            <TextBlock Text="{x:Bind local:GlobalAppearance.ThemeNameConverter((model:Theme)), Mode=OneWay}" />
+                        </DataTemplate>
+                    </ComboBox.ItemTemplate>
+                </ComboBox>
             </local:SettingContainer>
 
             <!--  Always show tabs  -->
diff --git a/src/cascadia/TerminalSettingsEditor/MainPage.cpp b/src/cascadia/TerminalSettingsEditor/MainPage.cpp
index 2009502c569..45ac57129ef 100644
--- a/src/cascadia/TerminalSettingsEditor/MainPage.cpp
+++ b/src/cascadia/TerminalSettingsEditor/MainPage.cpp
@@ -609,4 +609,10 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
     {
         return _breadcrumbs;
     }
+
+    winrt::Windows::UI::Xaml::Media::Brush MainPage::BackgroundBrush()
+    {
+        return SettingsNav().Background();
+    }
+
 }
diff --git a/src/cascadia/TerminalSettingsEditor/MainPage.h b/src/cascadia/TerminalSettingsEditor/MainPage.h
index 2f7d5388cdb..ac257da0beb 100644
--- a/src/cascadia/TerminalSettingsEditor/MainPage.h
+++ b/src/cascadia/TerminalSettingsEditor/MainPage.h
@@ -40,6 +40,8 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
         bool TryPropagateHostingWindow(IInspectable object) noexcept;
         uint64_t GetHostingWindow() const noexcept;
 
+        winrt::Windows::UI::Xaml::Media::Brush BackgroundBrush();
+
         Windows::Foundation::Collections::IObservableVector<IInspectable> Breadcrumbs() noexcept;
 
         TYPED_EVENT(OpenJson, Windows::Foundation::IInspectable, Model::SettingsTarget);
diff --git a/src/cascadia/TerminalSettingsEditor/MainPage.idl b/src/cascadia/TerminalSettingsEditor/MainPage.idl
index 4c771419753..2b724ab67b4 100644
--- a/src/cascadia/TerminalSettingsEditor/MainPage.idl
+++ b/src/cascadia/TerminalSettingsEditor/MainPage.idl
@@ -39,5 +39,7 @@ namespace Microsoft.Terminal.Settings.Editor
         void SetHostingWindow(UInt64 window);
 
         Windows.Foundation.Collections.IObservableVector<IInspectable> Breadcrumbs { get; };
+
+        Windows.UI.Xaml.Media.Brush BackgroundBrush { get; };
     }
 }
diff --git a/src/cascadia/TerminalSettingsModel/CascadiaSettings.cpp b/src/cascadia/TerminalSettingsModel/CascadiaSettings.cpp
index 8a5b98ea5c9..4154786c303 100644
--- a/src/cascadia/TerminalSettingsModel/CascadiaSettings.cpp
+++ b/src/cascadia/TerminalSettingsModel/CascadiaSettings.cpp
@@ -409,6 +409,7 @@ void CascadiaSettings::_validateSettings()
     _validateMediaResources();
     _validateKeybindings();
     _validateColorSchemesInCommands();
+    _validateThemeExists();
 }
 
 // Method Description:
@@ -1152,3 +1153,14 @@ void CascadiaSettings::ExportFile(winrt::hstring path, winrt::hstring content)
     }
     CATCH_LOG();
 }
+
+void CascadiaSettings::_validateThemeExists()
+{
+    if (!_globals->Themes().HasKey(_globals->Theme()))
+    {
+        _warnings.Append(SettingsLoadWarnings::UnknownTheme);
+
+        // safely fall back to system as the theme.
+        _globals->Theme(L"system");
+    }
+}
diff --git a/src/cascadia/TerminalSettingsModel/CascadiaSettings.h b/src/cascadia/TerminalSettingsModel/CascadiaSettings.h
index 737fa8075a8..c6141df700c 100644
--- a/src/cascadia/TerminalSettingsModel/CascadiaSettings.h
+++ b/src/cascadia/TerminalSettingsModel/CascadiaSettings.h
@@ -73,6 +73,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
             const Json::Value& colorSchemes;
             const Json::Value& profileDefaults;
             const Json::Value& profilesList;
+            const Json::Value& themes;
         };
 
         static std::pair<size_t, size_t> _lineAndColumnFromPosition(const std::string_view& string, const size_t position);
@@ -158,6 +159,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
         void _validateKeybindings() const;
         void _validateColorSchemesInCommands() const;
         bool _hasInvalidColorScheme(const Model::Command& command) const;
+        void _validateThemeExists();
 
         // user settings
         winrt::com_ptr<implementation::GlobalAppSettings> _globals = winrt::make_self<implementation::GlobalAppSettings>();
diff --git a/src/cascadia/TerminalSettingsModel/CascadiaSettingsSerialization.cpp b/src/cascadia/TerminalSettingsModel/CascadiaSettingsSerialization.cpp
index c8baba43e8c..f8e4dc4f80f 100644
--- a/src/cascadia/TerminalSettingsModel/CascadiaSettingsSerialization.cpp
+++ b/src/cascadia/TerminalSettingsModel/CascadiaSettingsSerialization.cpp
@@ -39,6 +39,7 @@ static constexpr std::string_view ProfilesKey{ "profiles" };
 static constexpr std::string_view DefaultSettingsKey{ "defaults" };
 static constexpr std::string_view ProfilesListKey{ "list" };
 static constexpr std::string_view SchemesKey{ "schemes" };
+static constexpr std::string_view ThemesKey{ "themes" };
 
 static constexpr std::wstring_view jsonExtension{ L".json" };
 static constexpr std::wstring_view FragmentsSubDirectory{ L"\\Fragments" };
@@ -531,6 +532,25 @@ void SettingsLoader::_parse(const OriginTag origin, const winrt::hstring& source
         }
     }
 
+    {
+        for (const auto& themeJson : json.themes)
+        {
+            if (const auto theme = Theme::FromJson(themeJson))
+            {
+                if (origin != OriginTag::InBox &&
+                    (theme->Name() == L"system" || theme->Name() == L"light" || theme->Name() == L"dark"))
+                {
+                    // If the theme didn't come from the in-box themes, and its
+                    // name was one of the reserved names, then just ignore it.
+                    // Themes don't support layering - we don't want the user
+                    // versions of these themes overriding the built-in ones.
+                    continue;
+                }
+                settings.globals->AddTheme(*theme);
+            }
+        }
+    }
+
     {
         settings.baseLayerProfile = Profile::FromJson(json.profileDefaults);
         // Remove the `guid` member from the default settings.
@@ -629,10 +649,11 @@ SettingsLoader::JsonSettings SettingsLoader::_parseJson(const std::string_view&
 {
     auto root = content.empty() ? Json::Value{ Json::ValueType::objectValue } : _parseJSON(content);
     const auto& colorSchemes = _getJSONValue(root, SchemesKey);
+    const auto& themes = _getJSONValue(root, ThemesKey);
     const auto& profilesObject = _getJSONValue(root, ProfilesKey);
     const auto& profileDefaults = _getJSONValue(profilesObject, DefaultSettingsKey);
     const auto& profilesList = profilesObject.isArray() ? profilesObject : _getJSONValue(profilesObject, ProfilesListKey);
-    return JsonSettings{ std::move(root), colorSchemes, profileDefaults, profilesList };
+    return JsonSettings{ std::move(root), colorSchemes, profileDefaults, profilesList, themes };
 }
 
 // Just a common helper function between _parse and _parseFragment.
@@ -1092,6 +1113,20 @@ Json::Value CascadiaSettings::ToJson() const
     }
     json[JsonKey(SchemesKey)] = schemes;
 
+    Json::Value themes{ Json::ValueType::arrayValue };
+    for (const auto& entry : _globals->Themes())
+    {
+        // Ignore the built in themes, when serializing the themes back out. We
+        // don't want to re-include them in the user settings file.
+        const auto theme{ winrt::get_self<Theme>(entry.Value()) };
+        if (theme->Name() == L"system" || theme->Name() == L"light" || theme->Name() == L"dark")
+        {
+            continue;
+        }
+        themes.append(theme->ToJson());
+    }
+    json[JsonKey(ThemesKey)] = themes;
+
     return json;
 }
 
diff --git a/src/cascadia/TerminalSettingsModel/GlobalAppSettings.cpp b/src/cascadia/TerminalSettingsModel/GlobalAppSettings.cpp
index e417042e44e..64ddea29f06 100644
--- a/src/cascadia/TerminalSettingsModel/GlobalAppSettings.cpp
+++ b/src/cascadia/TerminalSettingsModel/GlobalAppSettings.cpp
@@ -17,6 +17,7 @@ using namespace winrt::Microsoft::UI::Xaml::Controls;
 
 static constexpr std::string_view LegacyKeybindingsKey{ "keybindings" };
 static constexpr std::string_view ActionsKey{ "actions" };
+static constexpr std::string_view ThemeKey{ "theme" };
 static constexpr std::string_view DefaultProfileKey{ "defaultProfile" };
 static constexpr std::string_view LegacyUseTabSwitcherModeKey{ "useTabSwitcher" };
 
@@ -39,6 +40,14 @@ void GlobalAppSettings::_FinalizeInheritance()
                 _colorSchemes.Insert(k, v);
             }
         }
+
+        for (const auto& [k, v] : parent->_themes)
+        {
+            if (!_themes.HasKey(k))
+            {
+                _themes.Insert(k, v);
+            }
+        }
     }
 }
 
@@ -65,6 +74,14 @@ winrt::com_ptr<GlobalAppSettings> GlobalAppSettings::Copy() const
             globals->_colorSchemes.Insert(kv.Key(), *schemeImpl->Copy());
         }
     }
+    if (_themes)
+    {
+        for (auto kv : _themes)
+        {
+            const auto themeImpl{ winrt::get_self<implementation::Theme>(kv.Value()) };
+            globals->_themes.Insert(kv.Key(), *themeImpl->Copy());
+        }
+    }
 
     for (const auto& parent : _parents)
     {
@@ -192,3 +209,18 @@ Json::Value GlobalAppSettings::ToJson() const
     json[JsonKey(ActionsKey)] = _actionMap->ToJson();
     return json;
 }
+
+winrt::Microsoft::Terminal::Settings::Model::Theme GlobalAppSettings::CurrentTheme() noexcept
+{
+    return _themes.TryLookup(Theme());
+}
+
+void GlobalAppSettings::AddTheme(const Model::Theme& theme)
+{
+    _themes.Insert(theme.Name(), theme);
+}
+
+winrt::Windows::Foundation::Collections::IMapView<winrt::hstring, winrt::Microsoft::Terminal::Settings::Model::Theme> GlobalAppSettings::Themes() noexcept
+{
+    return _themes.GetView();
+}
diff --git a/src/cascadia/TerminalSettingsModel/GlobalAppSettings.h b/src/cascadia/TerminalSettingsModel/GlobalAppSettings.h
index 14584b26a72..3c876c75d3f 100644
--- a/src/cascadia/TerminalSettingsModel/GlobalAppSettings.h
+++ b/src/cascadia/TerminalSettingsModel/GlobalAppSettings.h
@@ -22,6 +22,7 @@ Author(s):
 #include "ActionMap.h"
 #include "Command.h"
 #include "ColorScheme.h"
+#include "Theme.h"
 
 // fwdecl unittest classes
 namespace SettingsModelLocalTests
@@ -62,6 +63,10 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
             DisableAnimations(!invertedDisableAnimationsValue);
         }
 
+        Windows::Foundation::Collections::IMapView<hstring, Model::Theme> Themes() noexcept;
+        void AddTheme(const Model::Theme& theme);
+        Model::Theme CurrentTheme() noexcept;
+
         INHERITABLE_SETTING(Model::GlobalAppSettings, hstring, UnparsedDefaultProfile, L"");
 
 #define GLOBAL_SETTINGS_INITIALIZE(type, name, jsonKey, ...) \
@@ -78,7 +83,9 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
 
         winrt::guid _defaultProfile;
         winrt::com_ptr<implementation::ActionMap> _actionMap{ winrt::make_self<implementation::ActionMap>() };
+
         std::vector<SettingsLoadWarnings> _keybindingsWarnings;
         Windows::Foundation::Collections::IMap<winrt::hstring, Model::ColorScheme> _colorSchemes{ winrt::single_threaded_map<winrt::hstring, Model::ColorScheme>() };
+        Windows::Foundation::Collections::IMap<winrt::hstring, Model::Theme> _themes{ winrt::single_threaded_map<winrt::hstring, Model::Theme>() };
     };
 }
diff --git a/src/cascadia/TerminalSettingsModel/GlobalAppSettings.idl b/src/cascadia/TerminalSettingsModel/GlobalAppSettings.idl
index 51386e6be34..c0226eaab6b 100644
--- a/src/cascadia/TerminalSettingsModel/GlobalAppSettings.idl
+++ b/src/cascadia/TerminalSettingsModel/GlobalAppSettings.idl
@@ -3,6 +3,7 @@
 
 #include "IInheritable.idl.h"
 
+import "Theme.idl";
 import "ColorScheme.idl";
 import "ActionMap.idl";
 
@@ -54,7 +55,6 @@ namespace Microsoft.Terminal.Settings.Model
         INHERITABLE_SETTING(Boolean, ShowTitleInTitlebar);
         INHERITABLE_SETTING(Boolean, ConfirmCloseAllTabs);
         INHERITABLE_SETTING(String, Language);
-        INHERITABLE_SETTING(Windows.UI.Xaml.ElementTheme, Theme);
         INHERITABLE_SETTING(Microsoft.UI.Xaml.Controls.TabViewWidthMode, TabWidthMode);
         INHERITABLE_SETTING(Boolean, UseAcrylicInTabRow);
         INHERITABLE_SETTING(Boolean, ShowTabsInTitlebar);
@@ -94,5 +94,10 @@ namespace Microsoft.Terminal.Settings.Model
         void RemoveColorScheme(String schemeName);
 
         ActionMap ActionMap { get; };
+
+        Windows.Foundation.Collections.IMapView<String, Theme> Themes();
+        void AddTheme(Theme theme);
+        INHERITABLE_SETTING(String, Theme);
+        Theme CurrentTheme { get; };
     }
 }
diff --git a/src/cascadia/TerminalSettingsModel/MTSMSettings.h b/src/cascadia/TerminalSettingsModel/MTSMSettings.h
index e425bdfd877..259c22951b4 100644
--- a/src/cascadia/TerminalSettingsModel/MTSMSettings.h
+++ b/src/cascadia/TerminalSettingsModel/MTSMSettings.h
@@ -33,8 +33,8 @@ Author(s):
     X(bool, AlwaysShowTabs, "alwaysShowTabs", true)                                                                                                        \
     X(bool, ShowTitleInTitlebar, "showTerminalTitleInTitlebar", true)                                                                                      \
     X(bool, ConfirmCloseAllTabs, "confirmCloseAllTabs", true)                                                                                              \
+    X(hstring, Theme, "theme")                                                                                                                             \
     X(hstring, Language, "language")                                                                                                                       \
-    X(winrt::Windows::UI::Xaml::ElementTheme, Theme, "theme", winrt::Windows::UI::Xaml::ElementTheme::Default)                                             \
     X(winrt::Microsoft::UI::Xaml::Controls::TabViewWidthMode, TabWidthMode, "tabWidthMode", winrt::Microsoft::UI::Xaml::Controls::TabViewWidthMode::Equal) \
     X(bool, UseAcrylicInTabRow, "useAcrylicInTabRow", false)                                                                                               \
     X(bool, ShowTabsInTitlebar, "showTabsInTitlebar", true)                                                                                                \
@@ -114,3 +114,14 @@ Author(s):
 // Intentionally omitted Appearance settings:
 // * ForegroundKey, BackgroundKey, SelectionBackgroundKey, CursorColorKey: all optional colors
 // * Opacity: needs special parsing
+
+#define MTSM_THEME_SETTINGS(X)                                                             \
+    X(winrt::Microsoft::Terminal::Settings::Model::WindowTheme, Window, "window", nullptr) \
+    X(winrt::Microsoft::Terminal::Settings::Model::TabRowTheme, TabRow, "tabRow", nullptr)
+
+#define MTSM_THEME_WINDOW_SETTINGS(X)                                                                                              \
+    X(winrt::Windows::UI::Xaml::ElementTheme, RequestedTheme, "applicationTheme", winrt::Windows::UI::Xaml::ElementTheme::Default) \
+    X(bool, UseMica, "useMica", false)
+
+#define MTSM_THEME_TABROW_SETTINGS(X) \
+    X(winrt::Microsoft::Terminal::Settings::Model::ThemeColor, Background, "background", nullptr)
diff --git a/src/cascadia/TerminalSettingsModel/Microsoft.Terminal.Settings.ModelLib.vcxproj b/src/cascadia/TerminalSettingsModel/Microsoft.Terminal.Settings.ModelLib.vcxproj
index 21184811d31..678446dff77 100644
--- a/src/cascadia/TerminalSettingsModel/Microsoft.Terminal.Settings.ModelLib.vcxproj
+++ b/src/cascadia/TerminalSettingsModel/Microsoft.Terminal.Settings.ModelLib.vcxproj
@@ -48,6 +48,9 @@
     <ClInclude Include="ColorScheme.h">
       <DependentUpon>ColorScheme.idl</DependentUpon>
     </ClInclude>
+    <ClInclude Include="Theme.h">
+      <DependentUpon>Theme.idl</DependentUpon>
+    </ClInclude>
     <ClInclude Include="Command.h">
       <DependentUpon>Command.idl</DependentUpon>
     </ClInclude>
@@ -128,6 +131,9 @@
     <ClCompile Include="ColorScheme.cpp">
       <DependentUpon>ColorScheme.idl</DependentUpon>
     </ClCompile>
+    <ClCompile Include="Theme.cpp">
+      <DependentUpon>Theme.idl</DependentUpon>
+    </ClCompile>
     <ClCompile Include="Command.cpp">
       <DependentUpon>Command.idl</DependentUpon>
     </ClCompile>
@@ -173,6 +179,7 @@
     <Midl Include="ApplicationState.idl" />
     <Midl Include="CascadiaSettings.idl" />
     <Midl Include="ColorScheme.idl" />
+    <Midl Include="Theme.idl" />
     <Midl Include="Command.idl" />
     <Midl Include="DefaultTerminal.idl" />
     <Midl Include="GlobalAppSettings.idl" />
diff --git a/src/cascadia/TerminalSettingsModel/TerminalSettingsSerializationHelpers.h b/src/cascadia/TerminalSettingsModel/TerminalSettingsSerializationHelpers.h
index 9dd273ee3c9..75e3fb63c1a 100644
--- a/src/cascadia/TerminalSettingsModel/TerminalSettingsSerializationHelpers.h
+++ b/src/cascadia/TerminalSettingsModel/TerminalSettingsSerializationHelpers.h
@@ -547,6 +547,79 @@ JSON_ENUM_MAPPER(::winrt::Microsoft::Terminal::Settings::Model::InfoBarMessage)
     };
 };
 
+template<>
+struct ::Microsoft::Terminal::Settings::Model::JsonUtils::ConversionTrait<winrt::Microsoft::Terminal::Settings::Model::ThemeColor>
+{
+    winrt::Microsoft::Terminal::Settings::Model::ThemeColor FromJson(const Json::Value& json)
+    {
+        if (json == Json::Value::null)
+        {
+            return nullptr;
+        }
+        const auto string{ Detail::GetStringView(json) };
+        if (string == "accent")
+        {
+            return winrt::Microsoft::Terminal::Settings::Model::ThemeColor::FromAccent();
+        }
+        else if (string == "terminalBackground")
+        {
+            return winrt::Microsoft::Terminal::Settings::Model::ThemeColor::FromTerminalBackground();
+        }
+        else
+        {
+            return winrt::Microsoft::Terminal::Settings::Model::ThemeColor::FromColor(::Microsoft::Console::Utils::ColorFromHexString(string));
+        }
+    }
+
+    bool CanConvert(const Json::Value& json)
+    {
+        if (json == Json::Value::null)
+        {
+            return true;
+        }
+        if (!json.isString())
+        {
+            return false;
+        }
+
+        const auto string{ Detail::GetStringView(json) };
+        const auto isColorSpec = (string.length() == 9 || string.length() == 7 || string.length() == 4) && string.front() == '#';
+        const auto isAccent = string == "accent";
+        const auto isTerminalBackground = string == "terminalBackground";
+        return isColorSpec || isAccent || isTerminalBackground;
+    }
+
+    Json::Value ToJson(const winrt::Microsoft::Terminal::Settings::Model::ThemeColor& val)
+    {
+        if (val == nullptr)
+        {
+            return Json::Value::null;
+        }
+
+        switch (val.ColorType())
+        {
+        case winrt::Microsoft::Terminal::Settings::Model::ThemeColorType::Accent:
+        {
+            return "accent";
+        }
+        case winrt::Microsoft::Terminal::Settings::Model::ThemeColorType::Color:
+        {
+            return til::u16u8(til::color{ val.Color() }.ToHexString(false));
+        }
+        case winrt::Microsoft::Terminal::Settings::Model::ThemeColorType::TerminalBackground:
+        {
+            return "terminalBackground";
+        }
+        }
+        return til::u16u8(til::color{ val.Color() }.ToHexString(false));
+    }
+
+    std::string TypeDescription() const
+    {
+        return "ThemeColor (#rrggbb, #rgb, #aarrggbb, accent, terminalBackground)";
+    }
+};
+
 // Possible ScrollToMarkDirection values
 JSON_ENUM_MAPPER(::winrt::Microsoft::Terminal::Control::ScrollToMarkDirection)
 {
diff --git a/src/cascadia/TerminalSettingsModel/TerminalWarnings.idl b/src/cascadia/TerminalSettingsModel/TerminalWarnings.idl
index c8aea6b95ab..43dee4e5a61 100644
--- a/src/cascadia/TerminalSettingsModel/TerminalWarnings.idl
+++ b/src/cascadia/TerminalSettingsModel/TerminalWarnings.idl
@@ -21,6 +21,7 @@ namespace Microsoft.Terminal.Settings.Model
         InvalidSplitSize,
         FailedToParseStartupActions,
         FailedToParseSubCommands,
+        UnknownTheme,
         WARNINGS_SIZE // IMPORTANT: This MUST be the last value in this enum. It's an unused placeholder.
     };
 
diff --git a/src/cascadia/TerminalSettingsModel/Theme.cpp b/src/cascadia/TerminalSettingsModel/Theme.cpp
new file mode 100644
index 00000000000..e4b770a6ced
--- /dev/null
+++ b/src/cascadia/TerminalSettingsModel/Theme.cpp
@@ -0,0 +1,331 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT license.
+
+#include "pch.h"
+#include "Theme.h"
+#include "../../types/inc/Utils.hpp"
+#include "../../types/inc/colorTable.hpp"
+#include "Utils.h"
+#include "JsonUtils.h"
+#include "TerminalSettingsSerializationHelpers.h"
+
+#include "ThemeColor.g.cpp"
+#include "WindowTheme.g.cpp"
+#include "TabRowTheme.g.cpp"
+#include "Theme.g.cpp"
+
+using namespace ::Microsoft::Console;
+using namespace Microsoft::Terminal::Settings::Model;
+using namespace winrt::Microsoft::Terminal::Settings::Model::implementation;
+using namespace winrt::Windows::UI;
+
+namespace winrt
+{
+    namespace MUX = Microsoft::UI::Xaml;
+    namespace WUX = Windows::UI::Xaml;
+}
+
+static constexpr std::string_view NameKey{ "name" };
+
+static constexpr wchar_t RegKeyDwm[] = L"Software\\Microsoft\\Windows\\DWM";
+static constexpr wchar_t RegKeyAccentColor[] = L"AccentColor";
+
+winrt::Microsoft::Terminal::Settings::Model::ThemeColor ThemeColor::FromColor(const winrt::Microsoft::Terminal::Core::Color& coreColor) noexcept
+{
+    auto result = winrt::make_self<implementation::ThemeColor>();
+    result->_Color = coreColor;
+    result->_ColorType = ThemeColorType::Color;
+    return *result;
+}
+
+winrt::Microsoft::Terminal::Settings::Model::ThemeColor ThemeColor::FromAccent() noexcept
+{
+    auto result = winrt::make_self<implementation::ThemeColor>();
+    result->_ColorType = ThemeColorType::Accent;
+    return *result;
+}
+
+winrt::Microsoft::Terminal::Settings::Model::ThemeColor ThemeColor::FromTerminalBackground() noexcept
+{
+    auto result = winrt::make_self<implementation::ThemeColor>();
+    result->_ColorType = ThemeColorType::TerminalBackground;
+    return *result;
+}
+
+static wil::unique_hkey openDwmRegKey()
+{
+    HKEY hKey{ nullptr };
+    if (RegOpenKeyEx(HKEY_CURRENT_USER, RegKeyDwm, 0, KEY_READ, &hKey) == ERROR_SUCCESS)
+    {
+        return wil::unique_hkey{ hKey };
+    }
+    return nullptr;
+}
+static DWORD readDwmSubValue(const wil::unique_hkey& dwmRootKey, const wchar_t* key)
+{
+    DWORD val{ 0 };
+    DWORD size{ sizeof(val) };
+    LOG_IF_FAILED(RegQueryValueExW(dwmRootKey.get(), key, nullptr, nullptr, reinterpret_cast<BYTE*>(&val), &size));
+    return val;
+}
+
+static til::color _getAccentColorForTitlebar()
+{
+    // The color used for the "Use Accent color in the title bar" in DWM is
+    // stored in HKCU\Software\Microsoft\Windows\DWM\AccentColor.
+    return til::color{ static_cast<COLORREF>(readDwmSubValue(openDwmRegKey(), RegKeyAccentColor)) }.with_alpha(255);
+}
+
+til::color ThemeColor::ColorFromBrush(const winrt::WUX::Media::Brush& brush)
+{
+    if (auto acrylic = brush.try_as<winrt::WUX::Media::AcrylicBrush>())
+    {
+        return acrylic.TintColor();
+    }
+    else if (auto solidColor = brush.try_as<winrt::WUX::Media::SolidColorBrush>())
+    {
+        return solidColor.Color();
+    }
+    return {};
+}
+
+winrt::WUX::Media::Brush ThemeColor::Evaluate(const winrt::WUX::ResourceDictionary& res,
+                                              const winrt::WUX::Media::Brush& terminalBackground,
+                                              const bool forTitlebar)
+{
+    static const auto accentColorKey{ winrt::box_value(L"SystemAccentColor") };
+
+    // NOTE: Currently, the DWM titlebar is always drawn, underneath our XAML
+    // content. If the opacity is <1.0, then you'll be able to see it, including
+    // the original caption buttons, which we don't want.
+
+    switch (ColorType())
+    {
+    case ThemeColorType::Accent:
+    {
+        til::color accentColor = forTitlebar ?
+                                     _getAccentColorForTitlebar() :
+                                     til::color{ winrt::unbox_value<winrt::Windows::UI::Color>(res.Lookup(accentColorKey)) };
+
+        const winrt::WUX::Media::SolidColorBrush accentBrush{ accentColor };
+        // _getAccentColorForTitlebar should have already filled the alpha
+        // channel in with 255
+        return accentBrush;
+    }
+    case ThemeColorType::Color:
+    {
+        return winrt::WUX::Media::SolidColorBrush{ forTitlebar ?
+                                                       Color().with_alpha(255) :
+                                                       Color() };
+    }
+    case ThemeColorType::TerminalBackground:
+    {
+        // If we're evaluating this color for the tab row, there are some rules
+        // we have to follow, unfortunately. We can't allow a transparent
+        // background, so we have to make sure to fill that in with Opacity(1.0)
+        // manually.
+        //
+        // So for that case, just make a new brush with the relevant properties
+        // set.
+        if (forTitlebar)
+        {
+            if (auto acrylic = terminalBackground.try_as<winrt::WUX::Media::AcrylicBrush>())
+            {
+                winrt::WUX::Media::AcrylicBrush newBrush{};
+                newBrush.TintColor(acrylic.TintColor());
+                newBrush.FallbackColor(acrylic.FallbackColor());
+                newBrush.TintLuminosityOpacity(acrylic.TintLuminosityOpacity());
+
+                // Allow acrylic opacity, but it's gotta be HostBackdrop acrylic.
+                //
+                // For now, just always use 50% opacity for this. If we do ever
+                // figure out how to get rid of our titlebar under the XAML tab
+                // row (GH#10509), we can always get rid of the HostBackdrop
+                // thing, and all this copying, and just return the
+                // terminalBackground brush directly.
+                //
+                // Because we're wholesale copying the brush, we won't be able
+                // to adjust it's opacity with the mouse wheel. This seems like
+                // an acceptable tradeoff for now.
+                newBrush.TintOpacity(.5);
+                newBrush.BackgroundSource(winrt::WUX::Media::AcrylicBackgroundSource::HostBackdrop);
+                return newBrush;
+            }
+            else if (auto solidColor = terminalBackground.try_as<winrt::WUX::Media::SolidColorBrush>())
+            {
+                winrt::WUX::Media::SolidColorBrush newBrush{};
+                newBrush.Color(til::color{ solidColor.Color() }.with_alpha(255));
+                return newBrush;
+            }
+        }
+
+        return terminalBackground;
+    }
+    }
+    return nullptr;
+}
+
+#define THEME_SETTINGS_FROM_JSON(type, name, jsonKey, ...)                    \
+    {                                                                         \
+        std::optional<type> _val;                                             \
+        _val = JsonUtils::GetValueForKey<std::optional<type>>(json, jsonKey); \
+        if (_val)                                                             \
+            result->name(*_val);                                              \
+    }
+
+#define THEME_SETTINGS_TO_JSON(type, name, jsonKey, ...) \
+    JsonUtils::SetValueForKey(json, jsonKey, val.name());
+
+#define THEME_OBJECT_CONVERTER(nameSpace, name, macro)                                         \
+    template<>                                                                                 \
+    struct ::Microsoft::Terminal::Settings::Model::JsonUtils::ConversionTrait<nameSpace::name> \
+    {                                                                                          \
+        nameSpace::name FromJson(const Json::Value& json)                                      \
+        {                                                                                      \
+            if (json == Json::Value::null)                                                     \
+                return nullptr;                                                                \
+            auto result = winrt::make_self<nameSpace::implementation::name>();                 \
+            macro(THEME_SETTINGS_FROM_JSON);                                                   \
+            return *result;                                                                    \
+        }                                                                                      \
+                                                                                               \
+        bool CanConvert(const Json::Value& json)                                               \
+        {                                                                                      \
+            return json.isObject();                                                            \
+        }                                                                                      \
+                                                                                               \
+        Json::Value ToJson(const nameSpace::name& val)                                         \
+        {                                                                                      \
+            if (val == nullptr)                                                                \
+                return Json::Value::null;                                                      \
+            Json::Value json{ Json::ValueType::objectValue };                                  \
+            macro(THEME_SETTINGS_TO_JSON);                                                     \
+            return json;                                                                       \
+        }                                                                                      \
+                                                                                               \
+        std::string TypeDescription() const                                                    \
+        {                                                                                      \
+            return "name (You should never see this)";                                         \
+        }                                                                                      \
+    };
+
+THEME_OBJECT_CONVERTER(winrt::Microsoft::Terminal::Settings::Model, WindowTheme, MTSM_THEME_WINDOW_SETTINGS);
+THEME_OBJECT_CONVERTER(winrt::Microsoft::Terminal::Settings::Model, TabRowTheme, MTSM_THEME_TABROW_SETTINGS);
+
+#undef THEME_SETTINGS_FROM_JSON
+#undef THEME_SETTINGS_TO_JSON
+#undef THEME_OBJECT_CONVERTER
+
+Theme::Theme() noexcept :
+    Theme{ winrt::WUX::ElementTheme::Default }
+{
+}
+
+Theme::Theme(const winrt::WUX::ElementTheme& requestedTheme) noexcept
+{
+    auto window{ winrt::make_self<implementation::WindowTheme>() };
+    window->RequestedTheme(requestedTheme);
+    _Window = *window;
+}
+
+winrt::com_ptr<Theme> Theme::Copy() const
+{
+    auto theme{ winrt::make_self<Theme>() };
+
+    theme->_Name = _Name;
+
+    if (_Window)
+    {
+        theme->_Window = *winrt::get_self<implementation::WindowTheme>(_Window)->Copy();
+    }
+    if (_TabRow)
+    {
+        theme->_TabRow = *winrt::get_self<implementation::TabRowTheme>(_TabRow)->Copy();
+    }
+
+    return theme;
+}
+
+// Method Description:
+// - Create a new instance of this class from a serialized JsonObject.
+// Arguments:
+// - json: an object which should be a serialization of a ColorScheme object.
+// Return Value:
+// - Returns nullptr for invalid JSON.
+winrt::com_ptr<Theme> Theme::FromJson(const Json::Value& json)
+{
+    auto result = winrt::make_self<Theme>();
+
+    if (json.isString())
+    {
+        // We found a string, not an object. Just secretly promote that string
+        // to a theme object with just the applicationTheme set from that value.
+        JsonUtils::GetValue(json, result->_Name);
+        winrt::WUX::ElementTheme requestedTheme{ winrt::WUX::ElementTheme::Default };
+        JsonUtils::GetValue(json, requestedTheme);
+
+        auto window{ winrt::make_self<implementation::WindowTheme>() };
+        window->RequestedTheme(requestedTheme);
+        result->_Window = *window;
+
+        return result;
+    }
+
+    JsonUtils::GetValueForKey(json, NameKey, result->_Name);
+
+    // This will use each of the ConversionTrait's from above to quickly parse the sub-objects
+
+#define THEME_SETTINGS_LAYER_JSON(type, name, jsonKey, ...)                   \
+    {                                                                         \
+        std::optional<type> _val;                                             \
+        _val = JsonUtils::GetValueForKey<std::optional<type>>(json, jsonKey); \
+        if (_val)                                                             \
+            result->_##name = *_val;                                          \
+        else                                                                  \
+            result->_##name = nullptr;                                        \
+    }
+
+    MTSM_THEME_SETTINGS(THEME_SETTINGS_LAYER_JSON)
+#undef THEME_SETTINGS_LAYER_JSON
+
+    return result;
+}
+
+// Method Description:
+// - Create a new serialized JsonObject from an instance of this class
+// Arguments:
+// - <none>
+// Return Value:
+// - the JsonObject representing this instance
+Json::Value Theme::ToJson() const
+{
+    Json::Value json{ Json::ValueType::objectValue };
+
+    JsonUtils::SetValueForKey(json, NameKey, _Name);
+
+    // Don't serialize anything if the object is null.
+#define THEME_SETTINGS_TO_JSON(type, name, jsonKey, ...) \
+    if (_##name)                                         \
+        JsonUtils::SetValueForKey(json, jsonKey, _##name);
+
+    MTSM_THEME_SETTINGS(THEME_SETTINGS_TO_JSON)
+#undef THEME_SETTINGS_TO_JSON
+
+    return json;
+}
+
+winrt::hstring Theme::ToString()
+{
+    return Name();
+}
+// Method Description:
+// - A helper for retrieving the RequestedTheme out of the window property.
+//   There's a bunch of places throughout the app that all ask for the
+//   RequestedTheme, this saves some hassle. If there wasn't a `window` defined
+//   for this theme, this'll quickly just return `system`, to use the OS theme.
+// Return Value:
+// - the set applicationTheme for this Theme, otherwise the system theme.
+winrt::WUX::ElementTheme Theme::RequestedTheme() const noexcept
+{
+    return _Window ? _Window.RequestedTheme() : winrt::WUX::ElementTheme::Default;
+}
diff --git a/src/cascadia/TerminalSettingsModel/Theme.h b/src/cascadia/TerminalSettingsModel/Theme.h
new file mode 100644
index 00000000000..4e4ba8e66be
--- /dev/null
+++ b/src/cascadia/TerminalSettingsModel/Theme.h
@@ -0,0 +1,107 @@
+/*++
+Copyright (c) Microsoft Corporation
+Licensed under the MIT license.
+
+Module Name:
+- Theme.hpp
+
+Abstract:
+- A Theme represents a collection of settings which control the appearance of
+  the Terminal window itself. Things like the color of the titlebar, the style
+  of the tabs.
+
+Author(s):
+- Mike Griese - March 2022
+
+--*/
+#pragma once
+
+#include "MTSMSettings.h"
+
+#include "ThemeColor.g.h"
+#include "WindowTheme.g.h"
+#include "TabRowTheme.g.h"
+#include "Theme.g.h"
+
+namespace winrt::Microsoft::Terminal::Settings::Model::implementation
+{
+    struct ThemeColor : ThemeColorT<ThemeColor>
+    {
+    public:
+        ThemeColor() noexcept = default;
+        static winrt::Microsoft::Terminal::Settings::Model::ThemeColor FromColor(const winrt::Microsoft::Terminal::Core::Color& coreColor) noexcept;
+        static winrt::Microsoft::Terminal::Settings::Model::ThemeColor FromAccent() noexcept;
+        static winrt::Microsoft::Terminal::Settings::Model::ThemeColor FromTerminalBackground() noexcept;
+
+        static til::color ColorFromBrush(const winrt::Windows::UI::Xaml::Media::Brush& brush);
+
+        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);
+
+        WINRT_PROPERTY(til::color, Color);
+        WINRT_PROPERTY(winrt::Microsoft::Terminal::Settings::Model::ThemeColorType, ColorType);
+    };
+
+#define THEME_SETTINGS_INITIALIZE(type, name, jsonKey, ...) \
+    WINRT_PROPERTY(type, name, ##__VA_ARGS__)
+
+#define THEME_SETTINGS_COPY(type, name, jsonKey, ...) \
+    result->_##name = _##name;
+
+#define COPY_THEME_OBJECT(T, macro)           \
+    winrt::com_ptr<T> Copy()                  \
+    {                                         \
+        auto result{ winrt::make_self<T>() }; \
+        macro(THEME_SETTINGS_COPY);           \
+        return result;                        \
+    }
+
+    struct WindowTheme : WindowThemeT<WindowTheme>
+    {
+        MTSM_THEME_WINDOW_SETTINGS(THEME_SETTINGS_INITIALIZE);
+
+    public:
+        COPY_THEME_OBJECT(WindowTheme, MTSM_THEME_WINDOW_SETTINGS);
+    };
+
+    struct TabRowTheme : TabRowThemeT<TabRowTheme>
+    {
+        MTSM_THEME_TABROW_SETTINGS(THEME_SETTINGS_INITIALIZE);
+
+    public:
+        COPY_THEME_OBJECT(TabRowTheme, MTSM_THEME_TABROW_SETTINGS);
+    };
+
+    struct Theme : ThemeT<Theme>
+    {
+    public:
+        Theme() noexcept;
+        Theme(const winrt::Windows::UI::Xaml::ElementTheme& requestedTheme) noexcept;
+
+        com_ptr<Theme> Copy() const;
+
+        hstring ToString();
+
+        static com_ptr<Theme> FromJson(const Json::Value& json);
+        void LayerJson(const Json::Value& json);
+        Json::Value ToJson() const;
+
+        winrt::Windows::UI::Xaml::ElementTheme RequestedTheme() const noexcept;
+
+        WINRT_PROPERTY(winrt::hstring, Name);
+
+        MTSM_THEME_SETTINGS(THEME_SETTINGS_INITIALIZE)
+
+    private:
+    };
+
+#undef THEME_SETTINGS_INITIALIZE
+#undef THEME_SETTINGS_COPY
+}
+
+namespace winrt::Microsoft::Terminal::Settings::Model::factory_implementation
+{
+    BASIC_FACTORY(ThemeColor);
+    BASIC_FACTORY(Theme);
+}
diff --git a/src/cascadia/TerminalSettingsModel/Theme.idl b/src/cascadia/TerminalSettingsModel/Theme.idl
new file mode 100644
index 00000000000..f2932a5e1fd
--- /dev/null
+++ b/src/cascadia/TerminalSettingsModel/Theme.idl
@@ -0,0 +1,55 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT license.
+
+namespace Microsoft.Terminal.Settings.Model
+{
+
+    enum ThemeColorType
+    {
+        Accent,
+        Color,
+        TerminalBackground
+    };
+
+    runtimeclass ThemeColor
+    {
+        ThemeColor();
+        static ThemeColor FromColor(Microsoft.Terminal.Core.Color color);
+        static ThemeColor FromAccent();
+        static ThemeColor FromTerminalBackground();
+
+        Microsoft.Terminal.Core.Color Color { get; };
+        ThemeColorType ColorType;
+
+        static Microsoft.Terminal.Core.Color ColorFromBrush(Windows.UI.Xaml.Media.Brush brush);
+        Windows.UI.Xaml.Media.Brush Evaluate(Windows.UI.Xaml.ResourceDictionary res,
+                                             Windows.UI.Xaml.Media.Brush terminalBackground,
+                                             Boolean forTitlebar);
+    }
+
+    runtimeclass WindowTheme {
+        Windows.UI.Xaml.ElementTheme RequestedTheme { get; };
+        Boolean UseMica { get; };
+    }
+
+    runtimeclass TabRowTheme {
+        ThemeColor Background { get; };
+    }
+
+    [default_interface] runtimeclass Theme : Windows.Foundation.IStringable {
+        Theme();
+        Theme(Windows.UI.Xaml.ElementTheme requestedTheme);
+
+        String Name;
+
+        // window.* Namespace
+        WindowTheme Window { get; };
+
+        // tabRow.* Namespace
+        TabRowTheme TabRow { get; };
+
+        // A helper for retrieving the RequestedTheme out of the window property
+        Windows.UI.Xaml.ElementTheme RequestedTheme { get; };
+
+    }
+}
diff --git a/src/cascadia/TerminalSettingsModel/defaults.json b/src/cascadia/TerminalSettingsModel/defaults.json
index 3527bf125a7..530a28b6ad3 100644
--- a/src/cascadia/TerminalSettingsModel/defaults.json
+++ b/src/cascadia/TerminalSettingsModel/defaults.json
@@ -280,6 +280,26 @@
             "brightWhite": "#EEEEEC"
         }
     ],
+    "themes": [
+        {
+            "name": "light",
+            "window":{
+                "applicationTheme": "light"
+            }
+        },
+        {
+            "name": "dark",
+            "window":{
+                "applicationTheme": "dark"
+            }
+        },
+        {
+            "name": "system",
+            "window":{
+                "applicationTheme": "system"
+            }
+        }
+    ],
     "actions":
     [
         // Application-level Keys
diff --git a/src/cascadia/WindowsTerminal/AppHost.cpp b/src/cascadia/WindowsTerminal/AppHost.cpp
index ab496e7be4a..1f929efda20 100644
--- a/src/cascadia/WindowsTerminal/AppHost.cpp
+++ b/src/cascadia/WindowsTerminal/AppHost.cpp
@@ -405,6 +405,11 @@ void AppHost::Initialize()
         }
     });
 
+    // Load bearing: make sure the PropertyChanged handler is added before we
+    // call Create, so that when the app sets up the titlebar brush, we're
+    // already prepared to listen for the change notification
+    _revokers.PropertyChanged = _logic.PropertyChanged(winrt::auto_revoke, { this, &AppHost::_PropertyChangedHandler });
+
     _logic.Create();
 
     _revokers.TitleChanged = _logic.TitleChanged(winrt::auto_revoke, { this, &AppHost::AppTitleChanged });
@@ -698,8 +703,12 @@ void AppHost::_UpdateTitleBarContent(const winrt::Windows::Foundation::IInspecta
 {
     if (_useNonClientArea)
     {
-        (static_cast<NonClientIslandWindow*>(_window.get()))->SetTitlebarContent(arg);
+        auto nonClientWindow{ static_cast<NonClientIslandWindow*>(_window.get()) };
+        nonClientWindow->SetTitlebarContent(arg);
+        nonClientWindow->SetTitlebarBackground(_logic.TitlebarBrush());
     }
+
+    _updateTheme();
 }
 
 // Method Description:
@@ -710,9 +719,9 @@ void AppHost::_UpdateTitleBarContent(const winrt::Windows::Foundation::IInspecta
 // - arg: the ElementTheme to use as the new theme for the UI
 // Return Value:
 // - <none>
-void AppHost::_UpdateTheme(const winrt::Windows::Foundation::IInspectable&, const winrt::Windows::UI::Xaml::ElementTheme& arg)
+void AppHost::_UpdateTheme(const winrt::Windows::Foundation::IInspectable&, const winrt::Windows::UI::Xaml::ElementTheme& /*arg*/)
 {
-    _window->OnApplicationThemeChanged(arg);
+    _updateTheme();
 }
 
 void AppHost::_FocusModeChanged(const winrt::Windows::Foundation::IInspectable&,
@@ -902,8 +911,15 @@ void AppHost::_FindTargetWindow(const winrt::Windows::Foundation::IInspectable&
     args.ResultTargetWindowName(targetWindow.WindowName());
 }
 
-winrt::fire_and_forget AppHost::_WindowActivated()
+winrt::fire_and_forget AppHost::_WindowActivated(bool activated)
 {
+    _logic.WindowActivated(activated);
+
+    if (!activated)
+    {
+        co_return;
+    }
+
     co_await winrt::resume_background();
 
     if (auto peasant{ _windowManager.CurrentWindow() })
@@ -1326,6 +1342,25 @@ winrt::fire_and_forget AppHost::_RenameWindowRequested(const winrt::Windows::Fou
     }
 }
 
+void AppHost::_updateTheme()
+{
+    auto theme = _logic.Theme();
+
+    _window->OnApplicationThemeChanged(theme.RequestedTheme());
+
+    // This block of code enables Mica for our window. By all accounts, this
+    // version of the code will only work on Windows 11, SV2. There's a slightly
+    // different API surface for enabling Mica on Windows 11 22000.0.
+    //
+    // This code is left here, commented out, for future enablement of Mica.
+    // We'll revisit this in GH#10509. Because we can't enable transparent
+    // titlebars for showing Mica currently, we're just gonna disable it
+    // entirely while we sort that out.
+    //
+    // const int attribute = theme.Window().UseMica() ? /*DWMSBT_MAINWINDOW*/ 2 : /*DWMSBT_NONE*/ 1;
+    // DwmSetWindowAttribute(_window->GetHandle(), /* DWMWA_SYSTEMBACKDROP_TYPE */ 38, &attribute, sizeof(attribute));
+}
+
 void AppHost::_HandleSettingsChanged(const winrt::Windows::Foundation::IInspectable& /*sender*/,
                                      const winrt::Windows::Foundation::IInspectable& /*args*/)
 {
@@ -1357,6 +1392,7 @@ void AppHost::_HandleSettingsChanged(const winrt::Windows::Foundation::IInspecta
     }
 
     _window->SetMinimizeToNotificationAreaBehavior(_logic.GetMinimizeToNotificationArea());
+    _updateTheme();
 }
 
 void AppHost::_IsQuakeWindowChanged(const winrt::Windows::Foundation::IInspectable&,
@@ -1578,3 +1614,13 @@ void AppHost::_CloseRequested(const winrt::Windows::Foundation::IInspectable& /*
     const auto pos = _GetWindowLaunchPosition();
     _logic.CloseWindow(pos);
 }
+
+void AppHost::_PropertyChangedHandler(const winrt::Windows::Foundation::IInspectable& /*sender*/,
+                                      const winrt::Windows::UI::Xaml::Data::PropertyChangedEventArgs& e)
+{
+    if (e.PropertyName() == L"TitlebarBrush")
+    {
+        auto nonClientWindow{ static_cast<NonClientIslandWindow*>(_window.get()) };
+        nonClientWindow->SetTitlebarBackground(_logic.TitlebarBrush());
+    }
+}
diff --git a/src/cascadia/WindowsTerminal/AppHost.h b/src/cascadia/WindowsTerminal/AppHost.h
index a3ae4edd2ba..5c97ed3be57 100644
--- a/src/cascadia/WindowsTerminal/AppHost.h
+++ b/src/cascadia/WindowsTerminal/AppHost.h
@@ -56,7 +56,7 @@ class AppHost
     void _RaiseVisualBell(const winrt::Windows::Foundation::IInspectable& sender,
                           const winrt::Windows::Foundation::IInspectable& arg);
     void _WindowMouseWheeled(const til::point coord, const int32_t delta);
-    winrt::fire_and_forget _WindowActivated();
+    winrt::fire_and_forget _WindowActivated(bool activated);
     void _WindowMoved();
 
     void _DispatchCommandline(winrt::Windows::Foundation::IInspectable sender,
@@ -122,6 +122,14 @@ class AppHost
                                         const winrt::Windows::Foundation::IInspectable& args);
     void _HideNotificationIconRequested(const winrt::Windows::Foundation::IInspectable& sender,
                                         const winrt::Windows::Foundation::IInspectable& args);
+
+    void _updateTheme();
+
+    void _PropertyChangedHandler(const winrt::Windows::Foundation::IInspectable& sender,
+                                 const winrt::Windows::UI::Xaml::Data::PropertyChangedEventArgs& args);
+
+    void _initialResizeAndRepositionWindow(const HWND hwnd, RECT proposedRect, winrt::Microsoft::Terminal::Settings::Model::LaunchMode& launchMode);
+
     std::unique_ptr<NotificationIcon> _notificationIcon;
     winrt::event_token _ReAddNotificationIconToken;
     winrt::event_token _NotificationIconPressedToken;
@@ -165,5 +173,6 @@ class AppHost
         winrt::Microsoft::Terminal::Remoting::WindowManager::ShowNotificationIconRequested_revoker ShowNotificationIconRequested;
         winrt::Microsoft::Terminal::Remoting::WindowManager::HideNotificationIconRequested_revoker HideNotificationIconRequested;
         winrt::Microsoft::Terminal::Remoting::WindowManager::QuitAllRequested_revoker QuitAllRequested;
+        winrt::TerminalApp::AppLogic::PropertyChanged_revoker PropertyChanged;
     } _revokers{};
 };
diff --git a/src/cascadia/WindowsTerminal/IslandWindow.cpp b/src/cascadia/WindowsTerminal/IslandWindow.cpp
index 3b54950ec00..6bf1bc52b8e 100644
--- a/src/cascadia/WindowsTerminal/IslandWindow.cpp
+++ b/src/cascadia/WindowsTerminal/IslandWindow.cpp
@@ -439,10 +439,8 @@ long IslandWindow::_calculateTotalSize(const bool isWidth, const long clientSize
     case WM_ACTIVATE:
     {
         // wparam = 0 indicates the window was deactivated
-        if (LOWORD(wparam) != 0)
-        {
-            _WindowActivatedHandlers();
-        }
+        const bool activated = LOWORD(wparam) != 0;
+        _WindowActivatedHandlers(activated);
 
         break;
     }
diff --git a/src/cascadia/WindowsTerminal/IslandWindow.h b/src/cascadia/WindowsTerminal/IslandWindow.h
index 64e16d9397c..89bfcbc71fd 100644
--- a/src/cascadia/WindowsTerminal/IslandWindow.h
+++ b/src/cascadia/WindowsTerminal/IslandWindow.h
@@ -67,7 +67,7 @@ class IslandWindow :
     WINRT_CALLBACK(DragRegionClicked, winrt::delegate<>);
     WINRT_CALLBACK(WindowCloseButtonClicked, winrt::delegate<>);
     WINRT_CALLBACK(MouseScrolled, winrt::delegate<void(til::point, int32_t)>);
-    WINRT_CALLBACK(WindowActivated, winrt::delegate<void()>);
+    WINRT_CALLBACK(WindowActivated, winrt::delegate<void(bool)>);
     WINRT_CALLBACK(HotkeyPressed, winrt::delegate<void(long)>);
     WINRT_CALLBACK(NotifyNotificationIconPressed, winrt::delegate<void()>);
     WINRT_CALLBACK(NotifyWindowHidden, winrt::delegate<void()>);
diff --git a/src/cascadia/WindowsTerminal/NonClientIslandWindow.cpp b/src/cascadia/WindowsTerminal/NonClientIslandWindow.cpp
index 01b0bd66346..8c8f285fe3e 100644
--- a/src/cascadia/WindowsTerminal/NonClientIslandWindow.cpp
+++ b/src/cascadia/WindowsTerminal/NonClientIslandWindow.cpp
@@ -1129,3 +1129,8 @@ bool NonClientIslandWindow::_IsTitlebarVisible() const
 {
     return !(_fullscreen || _borderless);
 }
+
+void NonClientIslandWindow::SetTitlebarBackground(winrt::Windows::UI::Xaml::Media::Brush brush)
+{
+    _titlebar.Background(brush);
+}
diff --git a/src/cascadia/WindowsTerminal/NonClientIslandWindow.h b/src/cascadia/WindowsTerminal/NonClientIslandWindow.h
index 0b54ec92db3..20b9bde43ef 100644
--- a/src/cascadia/WindowsTerminal/NonClientIslandWindow.h
+++ b/src/cascadia/WindowsTerminal/NonClientIslandWindow.h
@@ -47,6 +47,8 @@ class NonClientIslandWindow : public IslandWindow
     void SetTitlebarContent(winrt::Windows::UI::Xaml::UIElement content);
     void OnApplicationThemeChanged(const winrt::Windows::UI::Xaml::ElementTheme& requestedTheme) override;
 
+    void SetTitlebarBackground(winrt::Windows::UI::Xaml::Media::Brush brush);
+
 private:
     std::optional<til::point> _oldIslandPos;
 
diff --git a/src/cascadia/WindowsTerminal/pch.h b/src/cascadia/WindowsTerminal/pch.h
index 19bfcdb27ff..d5490a62039 100644
--- a/src/cascadia/WindowsTerminal/pch.h
+++ b/src/cascadia/WindowsTerminal/pch.h
@@ -64,6 +64,7 @@ Module Name:
 #include <winrt/Windows.UI.Core.h>
 #include <winrt/Windows.UI.Xaml.Controls.h>
 #include <winrt/Windows.UI.Xaml.Controls.Primitives.h>
+#include <winrt/Windows.UI.Xaml.Data.h>
 #include <winrt/Windows.ui.xaml.media.h>
 #include <winrt/Windows.ApplicationModel.h>
 #include <winrt/Windows.ApplicationModel.Resources.Core.h>
diff --git a/src/inc/til/color.h b/src/inc/til/color.h
index fa5a141c302..5054be53318 100644
--- a/src/inc/til/color.h
+++ b/src/inc/til/color.h
@@ -187,14 +187,13 @@ namespace til // Terminal Implementation Library. Also: "Today I Learned"
             wss << L"#" << std::uppercase << std::setfill(L'0') << std::hex;
             // Force the compiler to promote from byte to int. Without it, the
             // stringstream will try to write the components as chars
+            wss << std::setw(2) << static_cast<int>(r);
+            wss << std::setw(2) << static_cast<int>(g);
+            wss << std::setw(2) << static_cast<int>(b);
             if (!omitAlpha)
             {
                 wss << std::setw(2) << static_cast<int>(a);
             }
-            wss << std::setw(2) << static_cast<int>(r);
-            wss << std::setw(2) << static_cast<int>(g);
-            wss << std::setw(2) << static_cast<int>(b);
-
             return wss.str();
         }
     };
diff --git a/src/types/utils.cpp b/src/types/utils.cpp
index c3e432204d5..10e7661ce77 100644
--- a/src/types/utils.cpp
+++ b/src/types/utils.cpp
@@ -87,31 +87,43 @@ std::string Utils::ColorToHexString(const til::color color)
 //      the correct format, throws E_INVALIDARG
 til::color Utils::ColorFromHexString(const std::string_view str)
 {
-    THROW_HR_IF(E_INVALIDARG, str.size() != 7 && str.size() != 4);
+    THROW_HR_IF(E_INVALIDARG, str.size() != 9 && str.size() != 7 && str.size() != 4);
     THROW_HR_IF(E_INVALIDARG, str.at(0) != '#');
 
     std::string rStr;
     std::string gStr;
     std::string bStr;
+    std::string aStr;
 
     if (str.size() == 4)
     {
         rStr = std::string(2, str.at(1));
         gStr = std::string(2, str.at(2));
         bStr = std::string(2, str.at(3));
+        aStr = "ff";
     }
-    else
+    else if (str.size() == 7)
+    {
+        rStr = std::string(&str.at(1), 2);
+        gStr = std::string(&str.at(3), 2);
+        bStr = std::string(&str.at(5), 2);
+        aStr = "ff";
+    }
+    else if (str.size() == 9)
     {
+        // #rrggbbaa
         rStr = std::string(&str.at(1), 2);
         gStr = std::string(&str.at(3), 2);
         bStr = std::string(&str.at(5), 2);
+        aStr = std::string(&str.at(7), 2);
     }
 
     const auto r = gsl::narrow_cast<BYTE>(std::stoul(rStr, nullptr, 16));
     const auto g = gsl::narrow_cast<BYTE>(std::stoul(gStr, nullptr, 16));
     const auto b = gsl::narrow_cast<BYTE>(std::stoul(bStr, nullptr, 16));
+    const auto a = gsl::narrow_cast<BYTE>(std::stoul(aStr, nullptr, 16));
 
-    return til::color{ r, g, b };
+    return til::color{ r, g, b, a };
 }
 
 // Routine Description:


Test patch: