diff --git a/cms/envs/common.py b/cms/envs/common.py
index 2d8c46cc960be115feae4cc90c7a6da6dd5dfc4b..59d6c9328dee35773b5f8b758d541c3ad24ddef9 100644
--- a/cms/envs/common.py
+++ b/cms/envs/common.py
@@ -312,8 +312,11 @@ TEMPLATES = [
         # Options specific to this backend.
         'OPTIONS': {
             'loaders': (
-                'django.template.loaders.filesystem.Loader',
-                'django.template.loaders.app_directories.Loader',
+                # We have to use mako-aware template loaders to be able to include
+                # mako templates inside django templates (such as main_django.html).
+                'openedx.core.djangoapps.theming.template_loaders.ThemeTemplateLoader',
+                'edxmako.makoloader.MakoFilesystemLoader',
+                'edxmako.makoloader.MakoAppDirectoriesLoader',
             ),
             'context_processors': (
                 'django.template.context_processors.request',
diff --git a/cms/static/sass/bootstrap/studio-main.scss b/cms/static/sass/bootstrap/studio-main.scss
index de32b0257af309b114a5df3454494d3eccdbc09e..f664fceb394345efbb8db7a14cb106bee82e51e1 100644
--- a/cms/static/sass/bootstrap/studio-main.scss
+++ b/cms/static/sass/bootstrap/studio-main.scss
@@ -3,7 +3,7 @@
 // -----------------------------
 
 // Bootstrap theme
-@import 'bootstrap/theme';
+@import 'cms/bootstrap/theme';
 @import 'bootstrap/scss/bootstrap';
 
 // Variables
diff --git a/cms/static/sass/elements/_system-feedback.scss b/cms/static/sass/elements/_system-feedback.scss
index 2bdcc879c0ef57610849070f6605c3c79b750ff3..239ddb83050925952e9b91ae27dd42674c99fee8 100644
--- a/cms/static/sass/elements/_system-feedback.scss
+++ b/cms/static/sass/elements/_system-feedback.scss
@@ -72,6 +72,60 @@
   }
 }
 
+.page-banner {
+    max-width: $fg-max-width;
+    margin: 0 auto;
+
+    .user-messages {
+        padding-top: $baseline;
+
+        // Hack: force override the global important rule
+        // that courseware links don't have an underline.
+        a:hover {
+            color: $link-color;
+            text-decoration: underline !important;
+        }
+    }
+
+    .alert {
+        margin-bottom: $baseline !important;
+        padding: $baseline;
+        border: 1px solid;
+
+        .icon-alert {
+            margin-right: $baseline / 4;
+        }
+
+        &.alert-info {
+            color: $state-info-text;
+            background-color: $state-info-bg;
+            border-color: $state-info-border;
+            box-shadow: none;
+        }
+
+        &.alert-success {
+            color: $state-success-text;
+            background-color: $state-success-bg;
+            border-color: $state-success-border;
+            box-shadow: none;
+        }
+
+        &.alert-warning {
+            color: $state-warning-text;
+            background-color: $state-warning-bg;
+            border-color: $state-warning-border;
+            box-shadow: none;
+        }
+
+        &.alert-danger {
+            color: $state-danger-text;
+            background-color: $state-danger-bg;
+            border-color: $state-danger-border;
+            box-shadow: none;
+        }
+    }
+}
+
 .alert, .notification, .prompt {
 
   // types - confirm
diff --git a/cms/static/sass/partials/cms/base/_variables.scss b/cms/static/sass/partials/cms/base/_variables.scss
index 92aa6404295b5e61e488650f1a6ddd557d170ec4..87b48c01e3b992085f2ff8b3b6e01d5d1a5a1e70 100644
--- a/cms/static/sass/partials/cms/base/_variables.scss
+++ b/cms/static/sass/partials/cms/base/_variables.scss
@@ -241,6 +241,7 @@ $ui-action-primary-color-focus: $blue-s1 !default;
 
 $ui-link-color: $blue-u2 !default;
 $ui-link-color-focus: $blue-s1 !default;
+$link-color: $ui-link-color;
 
 // +Specific UI
 // ====================
@@ -281,3 +282,23 @@ $action-primary-active-bg: #1AA1DE !default; // $m-blue
 $very-light-text: $white !default;
 
 $color-background-alternate: rgb(242, 248, 251) !default;
+
+// ----------------------------
+// #COLORS- Bootstrap-style
+// ----------------------------
+
+$state-success-text: $black !default;
+$state-success-bg: #dff0d8 !default;
+$state-success-border: darken($state-success-bg, 5%) !default;
+
+$state-info-text: $black !default;
+$state-info-bg: #d9edf7 !default;
+$state-info-border: darken($state-info-bg, 7%) !default;
+
+$state-warning-text: $black !default;
+$state-warning-bg: #fcf8e3 !default;
+$state-warning-border: darken($state-warning-bg, 5%) !default;
+
+$state-danger-text: $black !default;
+$state-danger-bg: #f2dede !default;
+$state-danger-border: darken($state-danger-bg, 5%) !default;
diff --git a/cms/static/sass/partials/bootstrap/_theme.scss b/cms/static/sass/partials/cms/bootstrap/_theme.scss
similarity index 100%
rename from cms/static/sass/partials/bootstrap/_theme.scss
rename to cms/static/sass/partials/cms/bootstrap/_theme.scss
diff --git a/cms/templates/base.html b/cms/templates/base.html
index 9cc015c745fee33737a6e63330e04ab587950c22..5cba0a40514fec5b7c67a64ff21d906f508b7c70 100644
--- a/cms/templates/base.html
+++ b/cms/templates/base.html
@@ -9,9 +9,11 @@
 <%!
 from django.utils.translation import ugettext as _
 
+from openedx.core.djangoapps.util.user_messages import PageLevelMessages
 from openedx.core.djangolib.js_utils import (
     dump_js_escaped_json, js_escaped_string
 )
+from openedx.core.djangolib.markup import HTML
 %>
 
 <%page expression_filter="h"/>
@@ -75,17 +77,34 @@ from openedx.core.djangolib.js_utils import (
 
     <!-- view -->
     <div class="wrapper wrapper-view" dir="${static.dir_rtl()}">
-        <% online_help_token = self.online_help_token() if hasattr(self, 'online_help_token') else None %>
-        <%include file="widgets/header.html" args="online_help_token=online_help_token" />
+      <% online_help_token = self.online_help_token() if hasattr(self, 'online_help_token') else None %>
+      <%include file="widgets/header.html" args="online_help_token=online_help_token" />
+
+      <%
+      banner_messages = list(PageLevelMessages.user_messages(request))
+      %>
+
+      % if banner_messages:
+        <div class="page-banner">
+          <div class="user-messages">
+            % for message in banner_messages:
+              <div class="alert ${message.css_class}" role="alert">
+                <span class="icon icon-alert fa ${message.icon_class}" aria-hidden="true"></span>
+                ${HTML(message.message_html)}
+              </div>
+            % endfor
+          </div>
+        </div>
+      % endif
 
       <div id="page-alert">
       <%block name="page_alert"></%block>
       </div>
 
       <main id="main" aria-label="Content" tabindex="-1">
-          <div id="content">
-          <%block name="content"></%block>
-          </div>
+        <div id="content" class="content">
+        <%block name="content"></%block>
+        </div>
       </main>
 
       % if user.is_authenticated():
diff --git a/cms/templates/fragments/standalone-page-bootstrap.html b/cms/templates/fragments/standalone-page-bootstrap.html
new file mode 100644
index 0000000000000000000000000000000000000000..b9c4f8c03e2e9e7698d9910c3d203b320285629b
--- /dev/null
+++ b/cms/templates/fragments/standalone-page-bootstrap.html
@@ -0,0 +1,13 @@
+## mako
+
+<%page expression_filter="h"/>
+
+## Override the default styles_version to use Bootstrap
+<%! main_css = "css/bootstrap/studio-main.css" %>
+
+<%inherit file="../base.html" />
+<%block name="title">${page_title if page_title else ''}</%block>
+
+<%block name="content">
+  <%include file="/fragments/standalone-page-fragment.html" args="fragment=fragment"/>
+</%block>
diff --git a/cms/templates/fragments/standalone-page-fragment.html b/cms/templates/fragments/standalone-page-fragment.html
new file mode 100644
index 0000000000000000000000000000000000000000..492f049cd6ca9d1a89874980baa8edd8fcead3c3
--- /dev/null
+++ b/cms/templates/fragments/standalone-page-fragment.html
@@ -0,0 +1,15 @@
+<%! from openedx.core.djangolib.markup import HTML %>
+
+<%block name="head_extra">
+    ${HTML(fragment.head_html())}
+</%block>
+
+<%block name="footer_extra">
+    ${HTML(fragment.foot_html())}
+</%block>
+
+<div class="wrapper-content wrapper">
+    <section class="content">
+        ${HTML(fragment.body_html())}
+    </section>
+</div>
diff --git a/cms/templates/fragments/standalone-page-v1.html b/cms/templates/fragments/standalone-page-v1.html
new file mode 100644
index 0000000000000000000000000000000000000000..32cd02641088c7f97428a3116d8281cb175bd0b5
--- /dev/null
+++ b/cms/templates/fragments/standalone-page-v1.html
@@ -0,0 +1,10 @@
+## mako
+
+<%page expression_filter="h"/>
+
+<%inherit file="../base.html" />
+<%block name="title">${page_title if page_title else ''}</%block>
+
+<%block name="content">
+  <%include file="/fragments/standalone-page-fragment.html" args="fragment=fragment"/>
+</%block>
diff --git a/cms/templates/fragments/standalone-page-v2.html b/cms/templates/fragments/standalone-page-v2.html
new file mode 100644
index 0000000000000000000000000000000000000000..3a78db48ebe171459e9c32f49f6409c9ab4f7531
--- /dev/null
+++ b/cms/templates/fragments/standalone-page-v2.html
@@ -0,0 +1,13 @@
+## mako
+
+<%page expression_filter="h"/>
+
+## Override the default styles_version to the Pattern Library version (version 2)
+<%! main_css = "style-main-v2" %>
+
+<%inherit file="../base.html" />
+<%block name="title">${page_title if page_title else ''}</%block>
+
+<%block name="content">
+  <%include file="/fragments/standalone-page-fragment.html" args="fragment=fragment"/>
+</%block>
diff --git a/cms/templates/ux/reference/bootstrap/test.html b/cms/templates/ux/reference/bootstrap/test.html
index 311a85a13230e6aa53c1857257c3065267a1aae3..bf68ae86a4f1e38d4b46ece697a84c99275eef53 100644
--- a/cms/templates/ux/reference/bootstrap/test.html
+++ b/cms/templates/ux/reference/bootstrap/test.html
@@ -1,20 +1,9 @@
-## Override the default styles_version to use Bootstrap
-<%! main_css = "css/bootstrap/studio-main.css" %>
+## mako
 
 <%page expression_filter="h"/>
 
-<%!
-from openedx.core.djangoapps.util.user_messages import (
-    register_error_message,
-    register_info_message,
-    register_success_message,
-    register_warning_message,
-)
-%>
-
-<%
-register_info_message(request, _('This is a test message'))
-%>
+## Override the default styles_version to use Bootstrap
+<%! main_css = "css/bootstrap/studio-main.css" %>
 
 <%inherit file="/base.html" />
 <%block name="title">Bootstrap Test Page</%block>
diff --git a/cms/urls.py b/cms/urls.py
index 36324081ea729f04c6e751d41d006e6b144e45b7..03184d8f21fd429ba0c17f61e53ce45a4c5713bd 100644
--- a/cms/urls.py
+++ b/cms/urls.py
@@ -64,6 +64,9 @@ urlpatterns = patterns(
     # Darklang View to change the preview language (or dark language)
     url(r'^update_lang/', include('openedx.core.djangoapps.dark_lang.urls', namespace='dark_lang')),
 
+    # URLs for managing theming
+    url(r'^theming/', include('openedx.core.djangoapps.theming.urls', namespace='theming')),
+
     # For redirecting to help pages.
     url(r'^help_token/', include('help_tokens.urls')),
 )
diff --git a/lms/djangoapps/courseware/tests/test_course_info.py b/lms/djangoapps/courseware/tests/test_course_info.py
index 31f74d61d96e79f64147cf2a5393c20d90c52600..00b48818080db917041e416b79453ccdbaaf28a8 100644
--- a/lms/djangoapps/courseware/tests/test_course_info.py
+++ b/lms/djangoapps/courseware/tests/test_course_info.py
@@ -388,7 +388,7 @@ class SelfPacedCourseInfoTestCase(LoginEnrollmentTestCase, SharedModuleStoreTest
         self.assertEqual(resp.status_code, 200)
 
     def test_num_queries_instructor_paced(self):
-        self.fetch_course_info_with_queries(self.instructor_paced_course, 24, 3)
+        self.fetch_course_info_with_queries(self.instructor_paced_course, 25, 3)
 
     def test_num_queries_self_paced(self):
-        self.fetch_course_info_with_queries(self.self_paced_course, 24, 3)
+        self.fetch_course_info_with_queries(self.self_paced_course, 25, 3)
diff --git a/lms/djangoapps/courseware/tests/test_views.py b/lms/djangoapps/courseware/tests/test_views.py
index 418a1548868bc2c627ec6b49dbf5f5d91e025dab..45087978070b7b3ed0907f9bd8e842a64d5c620d 100644
--- a/lms/djangoapps/courseware/tests/test_views.py
+++ b/lms/djangoapps/courseware/tests/test_views.py
@@ -211,8 +211,8 @@ class IndexQueryTestCase(ModuleStoreTestCase):
     NUM_PROBLEMS = 20
 
     @ddt.data(
-        (ModuleStoreEnum.Type.mongo, 10, 142),
-        (ModuleStoreEnum.Type.split, 4, 142),
+        (ModuleStoreEnum.Type.mongo, 10, 143),
+        (ModuleStoreEnum.Type.split, 4, 143),
     )
     @ddt.unpack
     def test_index_query_counts(self, store_type, expected_mongo_query_count, expected_mysql_query_count):
@@ -1464,12 +1464,12 @@ class ProgressPageTests(ProgressPageBaseTests):
         """Test that query counts remain the same for self-paced and instructor-paced courses."""
         SelfPacedConfiguration(enabled=self_paced_enabled).save()
         self.setup_course(self_paced=self_paced)
-        with self.assertNumQueries(39, table_blacklist=QUERY_COUNT_TABLE_BLACKLIST), check_mongo_calls(1):
+        with self.assertNumQueries(40, table_blacklist=QUERY_COUNT_TABLE_BLACKLIST), check_mongo_calls(1):
             self._get_progress_page()
 
     @ddt.data(
-        (False, 39, 25),
-        (True, 32, 21)
+        (False, 40, 26),
+        (True, 33, 22)
     )
     @ddt.unpack
     def test_progress_queries(self, enable_waffle, initial, subsequent):
diff --git a/lms/djangoapps/django_comment_client/base/tests.py b/lms/djangoapps/django_comment_client/base/tests.py
index 23ccb0911384e14c45b26537bfec8a060ddde37c..49e709833c3ead4ca2a4f6aa459b488a309604be 100644
--- a/lms/djangoapps/django_comment_client/base/tests.py
+++ b/lms/djangoapps/django_comment_client/base/tests.py
@@ -404,8 +404,8 @@ class ViewsQueryCountTestCase(
         return inner
 
     @ddt.data(
-        (ModuleStoreEnum.Type.mongo, 3, 4, 31),
-        (ModuleStoreEnum.Type.split, 3, 13, 31),
+        (ModuleStoreEnum.Type.mongo, 3, 4, 32),
+        (ModuleStoreEnum.Type.split, 3, 13, 32),
     )
     @ddt.unpack
     @count_queries
@@ -413,8 +413,8 @@ class ViewsQueryCountTestCase(
         self.create_thread_helper(mock_request)
 
     @ddt.data(
-        (ModuleStoreEnum.Type.mongo, 3, 3, 27),
-        (ModuleStoreEnum.Type.split, 3, 10, 27),
+        (ModuleStoreEnum.Type.mongo, 3, 3, 28),
+        (ModuleStoreEnum.Type.split, 3, 10, 28),
     )
     @ddt.unpack
     @count_queries
diff --git a/lms/envs/common.py b/lms/envs/common.py
index 5d5ef06219957a33449e6d64621fb117811dc4d4..d87c336fc1fd14b4dd5aee19c5906d6d2927a788 100644
--- a/lms/envs/common.py
+++ b/lms/envs/common.py
@@ -582,9 +582,6 @@ TEMPLATES = [
 ]
 DEFAULT_TEMPLATE_ENGINE = TEMPLATES[0]
 
-# The template used to render a web fragment as a standalone page
-STANDALONE_FRAGMENT_VIEW_TEMPLATE = 'fragment-view-chromeless.html'
-
 ###############################################################################################
 
 # use the ratelimit backend to prevent brute force attacks
diff --git a/lms/static/sass/bootstrap/_layouts.scss b/lms/static/sass/bootstrap/_layouts.scss
index 05ded2191abd910d78b2c22383d22b784ae80305..660140e439fdbc544a52b96ae5d5dd2c4622bebe 100644
--- a/lms/static/sass/bootstrap/_layouts.scss
+++ b/lms/static/sass/bootstrap/_layouts.scss
@@ -1,8 +1,10 @@
 // LMS layouts
 
 .content-wrapper {
+    margin-top: $baseline;
+
     .course-tabs {
-        padding-bottom: none;
+        padding-bottom: 0;
 
         .nav-item {
             &.active, &:hover{
diff --git a/lms/templates/fragments/standalone-page-bootstrap.html b/lms/templates/fragments/standalone-page-bootstrap.html
new file mode 100644
index 0000000000000000000000000000000000000000..eee76b7ac7607feb8fb03221db50339e2fbc858e
--- /dev/null
+++ b/lms/templates/fragments/standalone-page-bootstrap.html
@@ -0,0 +1,11 @@
+## mako
+
+<%page expression_filter="h"/>
+
+## Override the default styles_version to use Bootstrap
+<%! main_css = "css/bootstrap/lms-main.css" %>
+
+<%inherit file="/main.html" />
+<%block name="pagetitle">${page_title if page_title else ''}</%block>
+
+<%include file="/fragments/standalone-page-fragment.html" args="fragment=fragment"/>
diff --git a/lms/templates/fragment-view-chromeless.html b/lms/templates/fragments/standalone-page-fragment.html
similarity index 68%
rename from lms/templates/fragment-view-chromeless.html
rename to lms/templates/fragments/standalone-page-fragment.html
index 68a13c355df5beb719baa7c2b121d4d9807f2403..b89f69e2f744c559c0170be9d9a27986e70c96af 100644
--- a/lms/templates/fragment-view-chromeless.html
+++ b/lms/templates/fragments/standalone-page-fragment.html
@@ -1,16 +1,9 @@
 ## mako
 
-<%! main_css = "style-main-v2" %>
-
 <%page expression_filter="h"/>
-<%inherit file="/main.html" />
-
-<%namespace name='static' file='static_content.html'/>
 
 <%! from openedx.core.djangolib.markup import HTML %>
 
-<% header_file = None %>
-
 <%block name="head_extra">
     ${HTML(fragment.head_html())}
 </%block>
diff --git a/lms/templates/fragments/standalone-page-v1.html b/lms/templates/fragments/standalone-page-v1.html
new file mode 100644
index 0000000000000000000000000000000000000000..d9f97304afd876f44bec805f65a4975d758a5957
--- /dev/null
+++ b/lms/templates/fragments/standalone-page-v1.html
@@ -0,0 +1,8 @@
+## mako
+
+<%page expression_filter="h"/>
+
+<%inherit file="/main.html" />
+<%block name="pagetitle">${page_title if page_title else ''}</%block>
+
+<%include file="/fragments/standalone-page-fragment.html" args="fragment=fragment"/>
diff --git a/lms/templates/fragments/standalone-page-v2.html b/lms/templates/fragments/standalone-page-v2.html
new file mode 100644
index 0000000000000000000000000000000000000000..83f533387ba5e3a722a34dcb6d3216b4a6c52c6d
--- /dev/null
+++ b/lms/templates/fragments/standalone-page-v2.html
@@ -0,0 +1,11 @@
+## mako
+
+<%page expression_filter="h"/>
+
+## Override the default styles_version to the Pattern Library version (version 2)
+<%! main_css = "style-main-v2" %>
+
+<%inherit file="/main.html" />
+<%block name="pagetitle">${page_title if page_title else ''}</%block>
+
+<%include file="/fragments/standalone-page-fragment.html" args="fragment=fragment"/>
diff --git a/lms/urls.py b/lms/urls.py
index 9733bf921c9167125de48cf5aae76ecb4158bb57..86c68ef411c91a2438167d7c12b85675c714857c 100644
--- a/lms/urls.py
+++ b/lms/urls.py
@@ -104,6 +104,9 @@ urlpatterns = (
     # URLs for managing dark launches of languages
     url(r'^update_lang/', include('openedx.core.djangoapps.dark_lang.urls', namespace='dark_lang')),
 
+    # URLs for managing theming
+    url(r'^theming/', include('openedx.core.djangoapps.theming.urls', namespace='theming')),
+
     # For redirecting to help pages.
     url(r'^help_token/', include('help_tokens.urls')),
 
diff --git a/openedx/core/djangoapps/bookmarks/tests/test_views.py b/openedx/core/djangoapps/bookmarks/tests/test_views.py
index 30ce17cc2d0eaeb69dd22b814e0e5e663c679989..e8344f70ec6bb5b577c69a5e123ed8646d344bd9 100644
--- a/openedx/core/djangoapps/bookmarks/tests/test_views.py
+++ b/openedx/core/djangoapps/bookmarks/tests/test_views.py
@@ -268,7 +268,7 @@ class BookmarksListViewTests(BookmarksViewsTestsBase):
         self.assertEqual(response.data['developer_message'], u'Parameter usage_id not provided.')
 
         # Send empty data dictionary.
-        with self.assertNumQueries(7):  # No queries for bookmark table.
+        with self.assertNumQueries(8):  # No queries for bookmark table.
             response = self.send_post(
                 client=self.client,
                 url=reverse('bookmarks'),
diff --git a/openedx/core/djangoapps/debug/views.py b/openedx/core/djangoapps/debug/views.py
index ba2d200a2a5e3b17140e2175afd5b0a9068febe2..b0ac1f8acbdf94abcb2cab60c108512381467e3a 100644
--- a/openedx/core/djangoapps/debug/views.py
+++ b/openedx/core/djangoapps/debug/views.py
@@ -36,13 +36,13 @@ def show_reference_template(request, template):
 
         # Support dynamic rendering of messages
         if request.GET.get('alert'):
-            register_info_message(request, request.GET.get('alert'))
+            PageLevelMessages.register_info_message(request, request.GET.get('alert'))
         if request.GET.get('success'):
-            register_success_message(request, request.GET.get('success'))
+            PageLevelMessages.register_success_message(request, request.GET.get('success'))
         if request.GET.get('warning'):
-            register_warning_message(request, request.GET.get('warning'))
+            PageLevelMessages.register_warning_message(request, request.GET.get('warning'))
         if request.GET.get('error'):
-            register_error_message(request, request.GET.get('error'))
+            PageLevelMessages.register_error_message(request, request.GET.get('error'))
 
         # Add some messages to the course skeleton pages
         if u'course-skeleton.html' in request.path:
diff --git a/openedx/core/djangoapps/plugin_api/views.py b/openedx/core/djangoapps/plugin_api/views.py
index 4b461bb3c49ffefdb378c6baf23348897dad8daa..f45260ed138b27b33c472539513c2861d3b9e029 100644
--- a/openedx/core/djangoapps/plugin_api/views.py
+++ b/openedx/core/djangoapps/plugin_api/views.py
@@ -8,6 +8,7 @@ from django.conf import settings
 from django.contrib.staticfiles.storage import staticfiles_storage
 from django.http import HttpResponse
 from django.shortcuts import render_to_response
+from edxmako.shortcuts import is_any_marketing_link_set, is_marketing_link_set, marketing_link
 from web_fragments.views import FragmentView
 
 log = logging.getLogger('plugin_api')
@@ -17,8 +18,6 @@ class EdxFragmentView(FragmentView):
     """
     The base class of all Open edX fragment views.
     """
-    USES_PATTERN_LIBRARY = True
-
     page_title = None
 
     @staticmethod
@@ -78,6 +77,44 @@ class EdxFragmentView(FragmentView):
         for js_file in self.js_dependencies():
             fragment.add_javascript_url(staticfiles_storage.url(js_file))
 
+    def create_base_standalone_context(self, request, fragment, **kwargs):
+        """
+        Creates the base context for rendering a fragment as a standalone page.
+        """
+        return {
+            'uses_pattern_library': True,
+            'disable_accordion': True,
+            'allow_iframing': True,
+            'disable_header': True,
+            'disable_footer': True,
+            'disable_window_wrap': True,
+        }
+
+    def _add_studio_standalone_context_variables(self, request, context):
+        """
+        Adds Studio-specific context variables for fragment standalone pages.
+
+        Note: this is meant to be a temporary hack to ensure that Studio
+        receives the context variables that are expected by some of its
+        shared templates. Ideally these templates shouldn't depend upon
+        this data being provided but should instead import the functionality
+        it needs.
+        """
+        context.update({
+            'request': request,
+            'settings': settings,
+            'EDX_ROOT_URL': settings.EDX_ROOT_URL,
+            'marketing_link': marketing_link,
+            'is_any_marketing_link_set': is_any_marketing_link_set,
+            'is_marketing_link_set': is_marketing_link_set,
+        })
+
+    def standalone_page_title(self, request, fragment, **kwargs):
+        """
+        Returns the page title for the standalone page, or None if there is no title.
+        """
+        return None
+
     def render_standalone_response(self, request, fragment, **kwargs):
         """
         Renders a standalone page for the specified fragment.
@@ -86,14 +123,18 @@ class EdxFragmentView(FragmentView):
         """
         if fragment is None:
             return HttpResponse(status=204)
-        context = {
-            'uses-pattern-library': self.USES_PATTERN_LIBRARY,
+        context = self.create_base_standalone_context(request, fragment, **kwargs)
+        self._add_studio_standalone_context_variables(request, context)
+        context.update({
             'settings': settings,
             'fragment': fragment,
-            'disable_accordion': True,
-            'allow_iframing': True,
-            'disable_header': True,
-            'disable_footer': True,
-            'disable_window_wrap': True,
-        }
-        return render_to_response(settings.STANDALONE_FRAGMENT_VIEW_TEMPLATE, context)
+            'page_title': self.standalone_page_title(request, fragment, **kwargs),
+        })
+        if context.get('uses_pattern_library', False):
+            template = 'fragments/standalone-page-v2.html'
+        elif context.get('uses_bootstrap', False):
+            template = 'fragments/standalone-page-bootstrap.html'
+        else:
+            template = 'fragments/standalone-page-v1.html'
+
+        return render_to_response(template, context)
diff --git a/openedx/core/djangoapps/theming/helpers.py b/openedx/core/djangoapps/theming/helpers.py
index 16b93c02be470e2c12f28eb61494ec3eb353c669..d178c811116c34f7ba14bbbce406be9ac3d0169c 100644
--- a/openedx/core/djangoapps/theming/helpers.py
+++ b/openedx/core/djangoapps/theming/helpers.py
@@ -366,6 +366,16 @@ def get_themes(themes_dir=None):
     return themes
 
 
+def theme_exists(theme_name, themes_dir=None):
+    """
+    Returns True if a theme exists with the specified name.
+    """
+    for theme in get_themes(themes_dir=themes_dir):
+        if theme.theme_dir_name == theme_name:
+            return True
+    return False
+
+
 def get_theme_dirs(themes_dir=None):
     """
     Returns theme dirs in given dirs
diff --git a/openedx/core/djangoapps/theming/middleware.py b/openedx/core/djangoapps/theming/middleware.py
index bb4b0970ef97b0c23109373a858831aeb28db632..d2f25321e526e8be2d3e83bc67ddd18c45c7a528 100644
--- a/openedx/core/djangoapps/theming/middleware.py
+++ b/openedx/core/djangoapps/theming/middleware.py
@@ -7,19 +7,25 @@ Note:
 """
 from django.conf import settings
 
-from openedx.core.djangoapps.theming.models import SiteTheme
+from .models import SiteTheme
+from .views import get_user_preview_site_theme
 
 
 class CurrentSiteThemeMiddleware(object):
     """
     Middleware that sets `site_theme` attribute to request object.
     """
-
     def process_request(self, request):
         """
-        fetch Site Theme for the current site and add it to the request object.
+        Set the request's 'site_theme' attribute based upon the current user.
         """
-        default_theme = None
-        if settings.DEFAULT_SITE_THEME:
-            default_theme = SiteTheme(site=request.site, theme_dir_name=settings.DEFAULT_SITE_THEME)
-        request.site_theme = SiteTheme.get_theme(request.site, default=default_theme)
+        # Determine if the user has specified a preview site
+        preview_site_theme = get_user_preview_site_theme(request)
+        if preview_site_theme:
+            site_theme = preview_site_theme
+        else:
+            default_theme = None
+            if settings.DEFAULT_SITE_THEME:
+                default_theme = SiteTheme(site=request.site, theme_dir_name=settings.DEFAULT_SITE_THEME)
+            site_theme = SiteTheme.get_theme(request.site, default=default_theme)
+        request.site_theme = site_theme
diff --git a/openedx/core/djangoapps/theming/models.py b/openedx/core/djangoapps/theming/models.py
index 9efc21a3c022c14c4ea5adf4257e0b073881f631..da771dcbcddeccce588f28f0b313af75fb042833 100644
--- a/openedx/core/djangoapps/theming/models.py
+++ b/openedx/core/djangoapps/theming/models.py
@@ -1,7 +1,6 @@
 """
 Django models supporting the Comprehensive Theming subsystem
 """
-from django.conf import settings
 from django.contrib.sites.models import Site
 from django.db import models
 
diff --git a/openedx/core/djangoapps/theming/templates/theming/theming-admin-fragment.html b/openedx/core/djangoapps/theming/templates/theming/theming-admin-fragment.html
new file mode 100644
index 0000000000000000000000000000000000000000..81c22b711582070e84833df32024bb34d980956d
--- /dev/null
+++ b/openedx/core/djangoapps/theming/templates/theming/theming-admin-fragment.html
@@ -0,0 +1,41 @@
+## mako
+
+<%page expression_filter="h"/>
+
+<%namespace name='static' file='../static_content.html'/>
+
+<%!
+from django.utils.translation import ugettext as _
+from openedx.core.djangoapps.theming.helpers import get_themes
+%>
+
+<h3>
+    ${_("Theming Administration")}
+</h3>
+<div>
+    <form class="form" action="${request.path}" method="post">
+        <div class="form-group">
+            <label>${_("Preview Theme")}
+                <select class="form-control" name="preview_theme">
+                    <%
+                    all_themes = list(get_themes())
+                    all_themes.sort(key=lambda x: x.theme_dir_name)
+                    current_theme_name = request.site_theme.theme_dir_name if request.site_theme else None
+                    %>
+                    % for theme in all_themes:
+                        <% theme_name = theme.theme_dir_name %>
+                        <option value="${theme_name}"${' selected=selected' if theme_name == current_theme_name else ''}>${theme_name}</option>
+                    % endfor
+                </select>
+            </label>
+        </div>
+        <div class="form-actions">
+            <button class="btn btn-primary" type="submit" name="action" value="set_preview_theme">${_("Submit")}</button>
+            <button class="btn btn-secondary" type="submit" name="action" value="reset_preview_theme">${_("Reset")}</button>
+        </div>
+
+        <input type="hidden" name="csrfmiddlewaretoken" value="${ csrf_token }"/>
+    </form>
+
+    <p>See also <a href="/admin">Django admin</a> for more theming settings.</p>
+</div>
diff --git a/openedx/core/djangoapps/theming/tests/test_middleware.py b/openedx/core/djangoapps/theming/tests/test_middleware.py
index 5f217d5b02decae3e41a8db2f2a84a62f7fde870..a151e0210666625b54019618d1f02f72dd2aed61 100644
--- a/openedx/core/djangoapps/theming/tests/test_middleware.py
+++ b/openedx/core/djangoapps/theming/tests/test_middleware.py
@@ -1,14 +1,20 @@
 """
     Tests for middleware for comprehensive themes.
 """
-from mock import Mock
-from django.test import TestCase, override_settings
-from django.contrib.sites.models import Site
 
+from django.contrib.messages.middleware import MessageMiddleware
+from django.test import RequestFactory, TestCase, override_settings
+from django.contrib.sites.models import Site
 from openedx.core.djangoapps.theming.middleware import CurrentSiteThemeMiddleware
+from student.tests.factories import UserFactory
+
+from ..views import set_user_preview_site_theme
+
+TEST_URL = '/test'
+TEST_THEME_NAME = 'test-theme'
 
 
-class TestCurrentSiteThemeMiddlewareLMS(TestCase):
+class TestCurrentSiteThemeMiddleware(TestCase):
     """
     Test theming middleware.
     """
@@ -16,22 +22,38 @@ class TestCurrentSiteThemeMiddlewareLMS(TestCase):
         """
         Initialize middleware and related objects
         """
-        super(TestCurrentSiteThemeMiddlewareLMS, self).setUp()
+        super(TestCurrentSiteThemeMiddleware, self).setUp()
 
         self.site_theme_middleware = CurrentSiteThemeMiddleware()
-        self.request = Mock()
-        self.request.site, __ = Site.objects.get_or_create(domain="test", name="test")
-        self.request.session = {}
+        self.user = UserFactory.create()
+
+    def create_mock_get_request(self):
+        """
+        Returns a mock GET request.
+        """
+        request = RequestFactory().get(TEST_URL)
+        self.initialize_mock_request(request)
+        return request
+
+    def initialize_mock_request(self, request):
+        """
+        Initialize a test request.
+        """
+        request.user = self.user
+        request.site, __ = Site.objects.get_or_create(domain='test', name='test')
+        request.session = {}
+        MessageMiddleware().process_request(request)
 
-    @override_settings(DEFAULT_SITE_THEME="test-theme")
+    @override_settings(DEFAULT_SITE_THEME=TEST_THEME_NAME)
     def test_default_site_theme(self):
         """
         Test that request.site_theme returns theme defined by DEFAULT_SITE_THEME setting
         when there is no theme associated with the current site.
         """
-        self.assertEqual(self.site_theme_middleware.process_request(self.request), None)
-        self.assertIsNotNone(self.request.site_theme)
-        self.assertEqual(self.request.site_theme.theme_dir_name, "test-theme")
+        request = self.create_mock_get_request()
+        self.assertEqual(self.site_theme_middleware.process_request(request), None)
+        self.assertIsNotNone(request.site_theme)
+        self.assertEqual(request.site_theme.theme_dir_name, TEST_THEME_NAME)
 
     @override_settings(DEFAULT_SITE_THEME=None)
     def test_default_site_theme_2(self):
@@ -39,5 +61,30 @@ class TestCurrentSiteThemeMiddlewareLMS(TestCase):
         Test that request.site_theme returns None when there is no theme associated with
         the current site and DEFAULT_SITE_THEME is also None.
         """
-        self.assertEqual(self.site_theme_middleware.process_request(self.request), None)
-        self.assertIsNone(self.request.site_theme)
+        request = self.create_mock_get_request()
+        self.assertEqual(self.site_theme_middleware.process_request(request), None)
+        self.assertIsNone(request.site_theme)
+
+    def test_preview_theme(self):
+        """
+        Verify that preview themes behaves correctly.
+        """
+        # First request a preview theme
+        post_request = RequestFactory().post('/test')
+        self.initialize_mock_request(post_request)
+        set_user_preview_site_theme(post_request, TEST_THEME_NAME)
+
+        # Next request a page and verify that the theme is returned
+        get_request = self.create_mock_get_request()
+        self.assertEqual(self.site_theme_middleware.process_request(get_request), None)
+        self.assertEqual(get_request.site_theme.theme_dir_name, TEST_THEME_NAME)
+
+        # Request to reset the theme
+        post_request = RequestFactory().post('/test')
+        self.initialize_mock_request(post_request)
+        set_user_preview_site_theme(post_request, None)
+
+        # Finally verify that no theme is returned
+        get_request = self.create_mock_get_request()
+        self.assertEqual(self.site_theme_middleware.process_request(get_request), None)
+        self.assertIsNone(get_request.site_theme)
diff --git a/openedx/core/djangoapps/theming/tests/test_views.py b/openedx/core/djangoapps/theming/tests/test_views.py
new file mode 100644
index 0000000000000000000000000000000000000000..e6654402d87c78e293cdc716278e9f2ea352639a
--- /dev/null
+++ b/openedx/core/djangoapps/theming/tests/test_views.py
@@ -0,0 +1,105 @@
+"""
+    Tests for comprehensive them
+"""
+
+from courseware.tests.factories import GlobalStaffFactory
+from django.conf import settings
+from django.contrib.messages.middleware import MessageMiddleware
+from django.test import TestCase, override_settings
+from django.contrib.sites.models import Site
+from openedx.core.djangoapps.theming.middleware import CurrentSiteThemeMiddleware
+from student.tests.factories import UserFactory
+
+THEMING_ADMIN_URL = '/theming/admin'
+TEST_THEME_NAME = 'test-theme'
+TEST_PASSWORD = 'test'
+
+
+class TestThemingViews(TestCase):
+    """
+    Test theming views.
+    """
+    def setUp(self):
+        """
+        Initialize middleware and related objects
+        """
+        super(TestThemingViews, self).setUp()
+
+        self.site_theme_middleware = CurrentSiteThemeMiddleware()
+        self.user = UserFactory.create()
+
+    def initialize_mock_request(self, request):
+        """
+        Initialize a test request.
+        """
+        request.user = self.user
+        request.site, __ = Site.objects.get_or_create(domain='test', name='test')
+        request.session = {}
+        MessageMiddleware().process_request(request)
+
+    def test_preview_theme_access(self):
+        """
+        Verify that users have the correct access to preview themes.
+        """
+        # Anonymous users get redirected to the login page
+        response = self.client.get(THEMING_ADMIN_URL)
+        self.assertRedirects(
+            response,
+            '{login_url}?next={url}'.format(
+                login_url=settings.LOGIN_REDIRECT_URL,
+                url=THEMING_ADMIN_URL,
+            )
+        )
+
+        # Logged in non-global staff get a 404
+        self.client.login(username=self.user.username, password=TEST_PASSWORD)
+        response = self.client.get(THEMING_ADMIN_URL)
+        self.assertEqual(response.status_code, 404)
+
+        # Global staff can access the page
+        global_staff = GlobalStaffFactory()
+        self.client.login(username=global_staff.username, password=TEST_PASSWORD)
+        response = self.client.get(THEMING_ADMIN_URL)
+        self.assertEqual(response.status_code, 200)
+
+    def test_preview_theme(self):
+        """
+        Verify that preview themes behaves correctly.
+        """
+        global_staff = GlobalStaffFactory()
+        self.client.login(username=global_staff.username, password=TEST_PASSWORD)
+
+        # First request a preview theme
+        post_response = self.client.post(
+            THEMING_ADMIN_URL,
+            {
+                'action': 'set_preview_theme',
+                'preview_theme': TEST_THEME_NAME,
+            }
+        )
+        self.assertRedirects(post_response, THEMING_ADMIN_URL)
+
+        # Next request a page and verify that the correct theme has been chosen
+        response = self.client.get(THEMING_ADMIN_URL)
+        self.assertEquals(response.status_code, 200)
+        self.assertContains(
+            response,
+            '<option value="{theme_name}" selected=selected>'.format(theme_name=TEST_THEME_NAME)
+        )
+
+        # Request to reset the theme
+        post_response = self.client.post(
+            THEMING_ADMIN_URL,
+            {
+                'action': 'reset_preview_theme'
+            }
+        )
+        self.assertRedirects(post_response, THEMING_ADMIN_URL)
+
+        # Finally verify that the test theme is no longer selected
+        response = self.client.get(THEMING_ADMIN_URL)
+        self.assertEquals(response.status_code, 200)
+        self.assertContains(
+            response,
+            '<option value="{theme_name}">'.format(theme_name=TEST_THEME_NAME)
+        )
diff --git a/openedx/core/djangoapps/theming/urls.py b/openedx/core/djangoapps/theming/urls.py
new file mode 100644
index 0000000000000000000000000000000000000000..79d25a297bdbfb7bbef873985df03c4a9e8f8e95
--- /dev/null
+++ b/openedx/core/djangoapps/theming/urls.py
@@ -0,0 +1,18 @@
+"""
+Defines URLs for theming views.
+"""
+
+from django.conf.urls import url
+
+from .helpers import is_comprehensive_theming_enabled
+from .views import ThemingAdministrationFragmentView
+
+
+if is_comprehensive_theming_enabled():
+    urlpatterns = [
+        url(
+            r'^admin',
+            ThemingAdministrationFragmentView.as_view(),
+            name='openedx.theming.update_theme_fragment_view',
+        ),
+    ]
diff --git a/openedx/core/djangoapps/theming/views.py b/openedx/core/djangoapps/theming/views.py
new file mode 100644
index 0000000000000000000000000000000000000000..52502784659e797d44d504d2e09cafe8fac2ef35
--- /dev/null
+++ b/openedx/core/djangoapps/theming/views.py
@@ -0,0 +1,135 @@
+"""
+Views file for theming administration.
+"""
+
+from django.conf import settings
+from django.contrib.auth.decorators import login_required
+from django.http import Http404
+from django.shortcuts import redirect
+from django.template.loader import render_to_string
+from django.utils.decorators import method_decorator
+from django.utils.translation import ugettext as _
+from openedx.core.djangoapps.plugin_api.views import EdxFragmentView
+from openedx.core.djangoapps.user_api.preferences.api import (
+    delete_user_preference,
+    get_user_preference,
+    set_user_preference,
+)
+from openedx.core.djangoapps.util.user_messages import PageLevelMessages
+from student.roles import GlobalStaff
+from web_fragments.fragment import Fragment
+
+from .helpers import theme_exists
+from .models import SiteTheme
+
+PREVIEW_SITE_THEME_PREFERENCE_KEY = 'preview-site-theme'
+PREVIEW_THEME_FIELD = 'preview_theme'
+
+
+def user_can_preview_themes(user):
+    """
+    Returns true if the specified user is allowed to preview themes.
+    """
+    if not user or user.is_anonymous():
+        return False
+
+    # In development mode, all users can preview themes
+    if settings.DEBUG:
+        return True
+
+    # Otherwise, only global staff can preview themes
+    return GlobalStaff().has_user(user)
+
+
+def get_user_preview_site_theme(request):
+    """
+    Returns the preview site for the current user, or None if not set.
+    """
+    user = request.user
+    if not user or user.is_anonymous():
+        return None
+    preview_site_name = get_user_preference(user, PREVIEW_SITE_THEME_PREFERENCE_KEY)
+    if not preview_site_name:
+        return None
+    return SiteTheme(site=request.site, theme_dir_name=preview_site_name)
+
+
+def set_user_preview_site_theme(request, preview_site_theme):
+    """
+    Sets the current user's preferred preview site theme.
+
+    Args:
+        request: the current request
+        preview_site_theme (str or SiteTheme): the preview site theme or theme name.
+          None can be specified to remove the preview site theme.
+    """
+    if preview_site_theme:
+        if isinstance(preview_site_theme, SiteTheme):
+            preview_site_theme_name = preview_site_theme.theme_dir_name
+        else:
+            preview_site_theme_name = preview_site_theme
+        if theme_exists(preview_site_theme_name):
+            set_user_preference(request.user, PREVIEW_SITE_THEME_PREFERENCE_KEY, preview_site_theme_name)
+            PageLevelMessages.register_success_message(
+                request,
+                _('Site theme changed to {site_theme}'.format(site_theme=preview_site_theme_name))
+            )
+        else:
+            PageLevelMessages.register_error_message(
+                request,
+                _('Theme {site_theme} does not exist'.format(site_theme=preview_site_theme_name))
+            )
+    else:
+        delete_user_preference(request.user, PREVIEW_SITE_THEME_PREFERENCE_KEY)
+        PageLevelMessages.register_success_message(request, _('Site theme reverted to the default'))
+
+
+class ThemingAdministrationFragmentView(EdxFragmentView):
+    """
+    Fragment view to allow a user to administer theming.
+    """
+
+    def render_to_fragment(self, request, course_id=None, **kwargs):
+        """
+        Renders the theming administration view as a fragment.
+        """
+        html = render_to_string('theming/theming-admin-fragment.html', {})
+        return Fragment(html)
+
+    @method_decorator(login_required)
+    def get(self, request, *args, **kwargs):
+        """
+        Renders the theming admin fragment to authorized users.
+        """
+        if not user_can_preview_themes(request.user):
+            raise Http404
+        return super(ThemingAdministrationFragmentView, self).get(request, *args, **kwargs)
+
+    @method_decorator(login_required)
+    def post(self, request, **kwargs):
+        """
+        Accept requests to update the theme preview.
+        """
+        if not user_can_preview_themes(request.user):
+            raise Http404
+        action = request.POST.get('action', None)
+        if action == 'set_preview_theme':
+            preview_theme_name = request.POST.get(PREVIEW_THEME_FIELD, '')
+            set_user_preview_site_theme(request, preview_theme_name)
+        elif action == 'reset_preview_theme':
+            set_user_preview_site_theme(request, None)
+        return redirect(request.path)
+
+    def create_base_standalone_context(self, request, fragment, **kwargs):
+        """
+        Creates the context to use when rendering a standalone page.
+        """
+        return {
+            'uses_bootstrap': True,
+        }
+
+    def standalone_page_title(self, request, fragment, **kwargs):
+        """
+        Returns the page title for the standalone update page.
+        """
+        return _('Theming Administration')
diff --git a/openedx/core/djangoapps/user_api/accounts/tests/test_views.py b/openedx/core/djangoapps/user_api/accounts/tests/test_views.py
index dd6f2dd0173be00cfa5c2e37e4d19d9de0825bad..12d911b30624205bdfed06b796b93c35886490ca 100644
--- a/openedx/core/djangoapps/user_api/accounts/tests/test_views.py
+++ b/openedx/core/djangoapps/user_api/accounts/tests/test_views.py
@@ -174,7 +174,7 @@ class TestOwnUsernameAPI(CacheIsolationTestCase, UserAPITestCase):
         Test that a client (logged in) can get her own username.
         """
         self.client.login(username=self.user.username, password=TEST_PASSWORD)
-        self._verify_get_own_username(14)
+        self._verify_get_own_username(15)
 
     def test_get_username_inactive(self):
         """
@@ -184,7 +184,7 @@ class TestOwnUsernameAPI(CacheIsolationTestCase, UserAPITestCase):
         self.client.login(username=self.user.username, password=TEST_PASSWORD)
         self.user.is_active = False
         self.user.save()
-        self._verify_get_own_username(14)
+        self._verify_get_own_username(15)
 
     def test_get_username_not_logged_in(self):
         """
@@ -305,7 +305,7 @@ class TestAccountsAPI(CacheIsolationTestCase, UserAPITestCase):
         """
         self.different_client.login(username=self.different_user.username, password=TEST_PASSWORD)
         self.create_mock_profile(self.user)
-        with self.assertNumQueries(18):
+        with self.assertNumQueries(19):
             response = self.send_get(self.different_client)
         self._verify_full_shareable_account_response(response, account_privacy=ALL_USERS_VISIBILITY)
 
@@ -320,7 +320,7 @@ class TestAccountsAPI(CacheIsolationTestCase, UserAPITestCase):
         """
         self.different_client.login(username=self.different_user.username, password=TEST_PASSWORD)
         self.create_mock_profile(self.user)
-        with self.assertNumQueries(18):
+        with self.assertNumQueries(19):
             response = self.send_get(self.different_client)
         self._verify_private_account_response(response, account_privacy=PRIVATE_VISIBILITY)
 
@@ -395,12 +395,12 @@ class TestAccountsAPI(CacheIsolationTestCase, UserAPITestCase):
             self.assertEqual(False, data["accomplishments_shared"])
 
         self.client.login(username=self.user.username, password=TEST_PASSWORD)
-        verify_get_own_information(16)
+        verify_get_own_information(17)
 
         # Now make sure that the user can get the same information, even if not active
         self.user.is_active = False
         self.user.save()
-        verify_get_own_information(10)
+        verify_get_own_information(11)
 
     def test_get_account_empty_string(self):
         """
@@ -414,7 +414,7 @@ class TestAccountsAPI(CacheIsolationTestCase, UserAPITestCase):
         legacy_profile.save()
 
         self.client.login(username=self.user.username, password=TEST_PASSWORD)
-        with self.assertNumQueries(16):
+        with self.assertNumQueries(17):
             response = self.send_get(self.client)
         for empty_field in ("level_of_education", "gender", "country", "bio"):
             self.assertIsNone(response.data[empty_field])
diff --git a/openedx/features/course_bookmarks/views/course_bookmarks.py b/openedx/features/course_bookmarks/views/course_bookmarks.py
index 95e753525cc5c25e1029767d69de0c34d1f0b95a..207db69965222cd7f29eaee0891e9418d7c0258e 100644
--- a/openedx/features/course_bookmarks/views/course_bookmarks.py
+++ b/openedx/features/course_bookmarks/views/course_bookmarks.py
@@ -8,6 +8,7 @@ from django.core.urlresolvers import reverse
 from django.shortcuts import render_to_response
 from django.template.loader import render_to_string
 from django.utils.decorators import method_decorator
+from django.utils.translation import ugettext as _
 from django.views.decorators.cache import cache_control
 from django.views.decorators.csrf import ensure_csrf_cookie
 from django.views.generic import View
@@ -80,3 +81,9 @@ class CourseBookmarksFragmentView(EdxFragmentView):
         self.add_fragment_resource_urls(fragment)
         fragment.add_javascript(inline_js)
         return fragment
+
+    def standalone_page_title(self, request, fragment, **kwargs):
+        """
+        Returns the standalone page title.
+        """
+        return _('Bookmarks')
diff --git a/openedx/features/course_experience/tests/views/test_course_home.py b/openedx/features/course_experience/tests/views/test_course_home.py
index a3a08bd9cb4df450c0fe77417cb55ddc20647ba1..39478c162eacd15627109bc07e22cff6afdcf3b1 100644
--- a/openedx/features/course_experience/tests/views/test_course_home.py
+++ b/openedx/features/course_experience/tests/views/test_course_home.py
@@ -160,7 +160,7 @@ class TestCourseHomePage(CourseHomePageTestCase):
         course_home_url(self.course)
 
         # Fetch the view and verify the query counts
-        with self.assertNumQueries(37, table_blacklist=QUERY_COUNT_TABLE_BLACKLIST):
+        with self.assertNumQueries(38, table_blacklist=QUERY_COUNT_TABLE_BLACKLIST):
             with check_mongo_calls(4):
                 url = course_home_url(self.course)
                 self.client.get(url)
diff --git a/openedx/features/course_experience/tests/views/test_course_updates.py b/openedx/features/course_experience/tests/views/test_course_updates.py
index 49c979632a1c5a41d148d48ed45903c6288bbc4f..ad2d481381005232f325c74d263b0178266712f1 100644
--- a/openedx/features/course_experience/tests/views/test_course_updates.py
+++ b/openedx/features/course_experience/tests/views/test_course_updates.py
@@ -127,7 +127,7 @@ class TestCourseUpdatesPage(SharedModuleStoreTestCase):
         course_updates_url(self.course)
 
         # Fetch the view and verify that the query counts haven't changed
-        with self.assertNumQueries(30, table_blacklist=QUERY_COUNT_TABLE_BLACKLIST):
+        with self.assertNumQueries(31, table_blacklist=QUERY_COUNT_TABLE_BLACKLIST):
             with check_mongo_calls(4):
                 url = course_updates_url(self.course)
                 self.client.get(url)
diff --git a/themes/edge.edx.org/cms/static/sass/partials/bootstrap/_theme.scss b/themes/edge.edx.org/cms/static/sass/partials/cms/bootstrap/_theme.scss
similarity index 100%
rename from themes/edge.edx.org/cms/static/sass/partials/bootstrap/_theme.scss
rename to themes/edge.edx.org/cms/static/sass/partials/cms/bootstrap/_theme.scss
diff --git a/themes/edx.org/cms/static/sass/partials/bootstrap/_theme.scss b/themes/edx.org/cms/static/sass/partials/cms/bootstrap/_theme.scss
similarity index 100%
rename from themes/edx.org/cms/static/sass/partials/bootstrap/_theme.scss
rename to themes/edx.org/cms/static/sass/partials/cms/bootstrap/_theme.scss
diff --git a/themes/red-theme/cms/static/sass/partials/base/_variables.scss b/themes/red-theme/cms/static/sass/partials/base/_variables.scss
deleted file mode 100755
index c32bb59471481c953277b5706539c83fca830dcd..0000000000000000000000000000000000000000
--- a/themes/red-theme/cms/static/sass/partials/base/_variables.scss
+++ /dev/null
@@ -1,14 +0,0 @@
-// Color overrides
-$white: rgb(255,255,255);
-$red: #d9534f !default;
-
-$footer-bg: $white;
-$header-bg: $white;
-$header-border-color: $red;
-
-$base-font-color: $red;
-$link-color: $red;
-$lms-active-color: $red;
-$lms-label-color: $red;
-
-@import 'lms/static/sass/partials/base/variables';
diff --git a/themes/red-theme/cms/static/sass/partials/bootstrap/_theme.scss b/themes/red-theme/cms/static/sass/partials/cms/bootstrap/_theme.scss
similarity index 92%
rename from themes/red-theme/cms/static/sass/partials/bootstrap/_theme.scss
rename to themes/red-theme/cms/static/sass/partials/cms/bootstrap/_theme.scss
index 7fb5406954665f86fd6412f4db943f82f3e7b537..30d2c1a98f7d9b5feb2aab82b1bf92bd2038b79c 100644
--- a/themes/red-theme/cms/static/sass/partials/bootstrap/_theme.scss
+++ b/themes/red-theme/cms/static/sass/partials/cms/bootstrap/_theme.scss
@@ -3,7 +3,7 @@
 // Theme colors
 //
 // Note: define colors needed by your theme first
-$red: #d9534f !default;
+$red: #d9534f;
 $brand-primary: $red;
 
 // Theme fonts