diff --git a/core/config/schema/core.data_types.schema.yml b/core/config/schema/core.data_types.schema.yml
index ee8c1471a684f945acb162ba8b92e18b5ceb2ccd..fc534dff9b58540746ecfe8b8bb146f95da8016d 100644
--- a/core/config/schema/core.data_types.schema.yml
+++ b/core/config/schema/core.data_types.schema.yml
@@ -101,12 +101,69 @@ action_configuration_default:
   sequence:
     - type: string
 
-theme_settings_default:
+theme_settings:
   type: mapping
   mapping:
-    shortcut_module_link:
-      type: boolean
-      label: 'Shortcut module link'
+    favicon:
+      type: mapping
+      label: 'Shortcut icon settings'
+      mapping:
+        mimetype:
+          type: string
+          label: 'MIME type'
+        path:
+          type: string
+          label: 'Path'
+        url:
+          type: string
+          label: 'URL'
+        use_default:
+          type: boolean
+          label: 'Use the default shortcut icon supplied by the theme'
+    features:
+      type: mapping
+      label: 'Shortcut icon settings'
+      mapping:
+        comment_user_picture:
+          type: boolean
+          label: 'User pictures in comments'
+        comment_user_verification:
+          type: boolean
+          label: 'User verification status in comments'
+        favicon:
+          type: boolean
+          label: 'Shortcut icon'
+        logo:
+          type: boolean
+          label: 'Logo'
+        name:
+          type: boolean
+          label: 'Site name'
+        node_user_picture:
+          type: boolean
+          label: 'User pictures in posts'
+        main_menu:
+          type: boolean
+          label: 'Main menu'
+        secondary_menu:
+          type: boolean
+          label: 'Secondary menu'
+        slogan:
+          type: boolean
+          label: 'Site slogan'
+    logo:
+      type: mapping
+      label: 'Shortcut icon settings'
+      mapping:
+        path:
+          type: string
+          label: 'Logo path'
+        url:
+          type: uri
+          label: 'URL'
+        use_default:
+          type: boolean
+          label: 'Use default'
 
 theme_breakpoints_default:
   type: sequence
diff --git a/core/includes/theme.inc b/core/includes/theme.inc
index dec14f48ab1d9c2c2120e8945a2fb65155fdad63..52ce3160165d2ada7e50c3f88030862f40f141df 100644
--- a/core/includes/theme.inc
+++ b/core/includes/theme.inc
@@ -12,6 +12,7 @@
 use Drupal\Component\Utility\UrlHelper;
 use Drupal\Component\Utility\Xss;
 use Drupal\Core\Config\Config;
+use Drupal\Core\Config\StorageException;
 use Drupal\Core\Language\Language;
 use Drupal\Core\Extension\Extension;
 use Drupal\Core\Extension\ExtensionNameLengthException;
@@ -862,6 +863,7 @@ function theme_get_setting($setting_name, $theme = NULL) {
       else {
         $theme_keys = array($theme);
       }
+      // Read hard-coded default settings from the theme info files.
       foreach ($theme_keys as $theme_key) {
         if (!empty($themes[$theme_key]->info['settings'])) {
           $cache[$theme]->merge($themes[$theme_key]->info['settings']);
@@ -873,12 +875,17 @@ function theme_get_setting($setting_name, $theme = NULL) {
     $cache[$theme]->merge(\Drupal::config('system.theme.global')->get());
 
     if ($theme) {
-      // Get the saved theme-specific settings from the configuration system.
-      $cache[$theme]->merge(\Drupal::config($theme . '.settings')->get());
+      // Retrieve configured theme-specific settings, if any.
+      try {
+        if ($theme_settings = \Drupal::config($theme . '.settings')->get()) {
+          $cache[$theme]->merge($theme_settings);
+        }
+      }
+      catch (StorageException $e) {
+      }
 
       // If the theme does not support a particular feature, override the global
       // setting and set the value to NULL.
-      //$supports = $cache[$theme]->get('supports');
       if (!empty($theme_object->info['features'])) {
         foreach (_system_default_theme_features() as $feature) {
           if (!in_array($feature, $theme_object->info['features'])) {
diff --git a/core/modules/system/config/schema/system.schema.yml b/core/modules/system/config/schema/system.schema.yml
index 5f8b4d47bb598ab26f00deb5b4d3df24098def43..91f06055590dd07f066868fb19f2975bd480a0ff 100644
--- a/core/modules/system/config/schema/system.schema.yml
+++ b/core/modules/system/config/schema/system.schema.yml
@@ -385,66 +385,5 @@ system.mail:
           label: 'Default'
 
 system.theme.global:
-  type: mapping
+  type: theme_settings
   label: 'Theme global settings'
-  mapping:
-    favicon:
-      type: mapping
-      label: 'Shortcut icon settings'
-      mapping:
-        mimetype:
-          type: string
-          label: 'MIME type'
-        path:
-          type: string
-          label: 'Path'
-        url:
-          type: string
-          label: 'URL'
-        use_default:
-          type: boolean
-          label: 'Use the default shortcut icon supplied by the theme'
-    features:
-      type: mapping
-      label: 'Shortcut icon settings'
-      mapping:
-        comment_user_picture:
-          type: boolean
-          label: 'User pictures in comments'
-        comment_user_verification:
-          type: boolean
-          label: 'User verification status in comments'
-        favicon:
-          type: boolean
-          label: 'Shortcut icon'
-        logo:
-          type: boolean
-          label: 'Logo'
-        name:
-          type: boolean
-          label: 'Site name'
-        node_user_picture:
-          type: boolean
-          label: 'User pictures in posts'
-        main_menu:
-          type: boolean
-          label: 'Main menu'
-        secondary_menu:
-          type: boolean
-          label: 'Secondary menu'
-        slogan:
-          type: boolean
-          label: 'Site slogan'
-    logo:
-      type: mapping
-      label: 'Shortcut icon settings'
-      mapping:
-        path:
-          type: string
-          label: 'Logo path'
-        url:
-          type: uri
-          label: 'URL'
-        use_default:
-          type: boolean
-          label: 'Use default'
diff --git a/core/modules/system/lib/Drupal/system/Tests/Theme/ThemeSettingsTest.php b/core/modules/system/lib/Drupal/system/Tests/Theme/ThemeSettingsTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..8593bae000ae5e8c5ebe783af52dc685380249fb
--- /dev/null
+++ b/core/modules/system/lib/Drupal/system/Tests/Theme/ThemeSettingsTest.php
@@ -0,0 +1,73 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\system\Tests\Theme\ThemeSettingsTest.
+ */
+
+namespace Drupal\system\Tests\Theme;
+
+use Drupal\Core\Extension\ExtensionDiscovery;
+use Drupal\simpletest\DrupalUnitTestBase;
+
+/**
+ * Tests theme settings functionality.
+ */
+class ThemeSettingsTest extends DrupalUnitTestBase {
+
+  /**
+   * Modules to enable.
+   *
+   * @var array
+   */
+  public static $modules = array('system');
+
+  /**
+   * List of discovered themes.
+   *
+   * @var array
+   */
+  protected $availableThemes;
+
+  public static function getInfo() {
+    return array(
+      'name' => 'Theme settings',
+      'description' => 'Tests theme settings functionality.',
+      'group' => 'Theme',
+    );
+  }
+
+  function setUp() {
+    parent::setUp();
+    // Theme settings rely on System module's system.theme.global configuration.
+    $this->installConfig(array('system'));
+
+    if (!isset($this->availableThemes)) {
+      $discovery = new ExtensionDiscovery();
+      $this->availableThemes = $discovery->scan('theme');
+    }
+  }
+
+  /**
+   * Tests that $theme.settings are imported and used as default theme settings.
+   */
+  function testDefaultConfig() {
+    $name = 'test_basetheme';
+    $path = $this->availableThemes[$name]->getPath();
+    $this->assertTrue(file_exists("$path/config/$name.settings.yml"));
+    $this->container->get('theme_handler')->enable(array($name));
+    $this->assertIdentical(theme_get_setting('base', $name), 'only');
+  }
+
+  /**
+   * Tests that the $theme.settings default config file is optional.
+   */
+  function testNoDefaultConfig() {
+    $name = 'stark';
+    $path = $this->availableThemes[$name]->getPath();
+    $this->assertFalse(file_exists("$path/config/$name.settings.yml"));
+    $this->container->get('theme_handler')->enable(array($name));
+    $this->assertNotNull(theme_get_setting('features.favicon', $name));
+  }
+
+}
diff --git a/core/modules/system/tests/themes/test_basetheme/config/schema/test_basetheme.schema.yml b/core/modules/system/tests/themes/test_basetheme/config/schema/test_basetheme.schema.yml
new file mode 100644
index 0000000000000000000000000000000000000000..8a4bd452a974b005432d475f3dffd902711ab6bc
--- /dev/null
+++ b/core/modules/system/tests/themes/test_basetheme/config/schema/test_basetheme.schema.yml
@@ -0,0 +1,7 @@
+test_basetheme.settings:
+  type: theme_settings
+  label: 'Test base theme settings'
+  mapping:
+    base:
+      type: string
+      label: 'Base theme setting'
diff --git a/core/modules/system/tests/themes/test_basetheme/config/test_basetheme.settings.yml b/core/modules/system/tests/themes/test_basetheme/config/test_basetheme.settings.yml
new file mode 100644
index 0000000000000000000000000000000000000000..2d5de349cedae1a27e23c0dba539e6f70294fb92
--- /dev/null
+++ b/core/modules/system/tests/themes/test_basetheme/config/test_basetheme.settings.yml
@@ -0,0 +1,3 @@
+features:
+  favicon: false
+base: only
diff --git a/core/themes/bartik/bartik.info.yml b/core/themes/bartik/bartik.info.yml
index 3e49ffdaec749c14af8bdefeba8e753411a8fc86..bd0bf898e61eb6835730e0546385a3ecdfcecabc 100644
--- a/core/themes/bartik/bartik.info.yml
+++ b/core/themes/bartik/bartik.info.yml
@@ -35,6 +35,6 @@ regions:
   footer_thirdcolumn: 'Footer third column'
   footer_fourthcolumn: 'Footer fourth column'
   footer: Footer
-# @todo D8: Remove once themes have to be installed.
+
 settings:
-  shortcut_module_link: '0'
+  shortcut_module_link: false
diff --git a/core/themes/bartik/config/bartik.settings.yml b/core/themes/bartik/config/bartik.settings.yml
index 3bb124eb8571fa0948aaf45600b782ae308e11f8..48877a3811a584cdf7d773767e4957bba07252e1 100644
--- a/core/themes/bartik/config/bartik.settings.yml
+++ b/core/themes/bartik/config/bartik.settings.yml
@@ -1 +1,4 @@
+# @todo There is no UI yet for configuring this, but the setting is included
+#   here, because ConfigImportUITest requires a non-empty bartik.settings.yml
+#   file: https://drupal.org/node/2235901.
 shortcut_module_link: false
diff --git a/core/themes/bartik/config/schema/bartik.schema.yml b/core/themes/bartik/config/schema/bartik.schema.yml
index 7788d4f1537e1e4ba3ae34392b9127b2f28e9073..bebcfc17a9ba30cefa234c04bdeea64a83bb22c5 100644
--- a/core/themes/bartik/config/schema/bartik.schema.yml
+++ b/core/themes/bartik/config/schema/bartik.schema.yml
@@ -1,5 +1,11 @@
 # Schema for the configuration files of the Bartik theme.
 
 bartik.settings:
-  type: theme_settings_default
+  type: theme_settings
   label: 'Bartik settings'
+  mapping:
+    # @todo Module-specific settings should be defined by the module:
+    #   https://drupal.org/node/2235901.
+    shortcut_module_link:
+      type: boolean
+      label: 'Shortcut module link'
diff --git a/core/themes/seven/config/schema/seven.schema.yml b/core/themes/seven/config/schema/seven.schema.yml
index 13f1384063e7623317cdd8cef3d498fb45990b94..6d4c86e241630e5bfe78c7926a9bae839bae9c2b 100644
--- a/core/themes/seven/config/schema/seven.schema.yml
+++ b/core/themes/seven/config/schema/seven.schema.yml
@@ -1,7 +1,7 @@
 # Schema for the configuration files of the Seven theme.
 
 seven.settings:
-  type: theme_settings_default
+  type: theme_settings
   label: 'Seven settings'
 
 seven.breakpoints:
diff --git a/core/themes/seven/config/seven.settings.yml b/core/themes/seven/config/seven.settings.yml
deleted file mode 100644
index f84652cc2e5ed9e5480071212459fbd32c808a4b..0000000000000000000000000000000000000000
--- a/core/themes/seven/config/seven.settings.yml
+++ /dev/null
@@ -1 +0,0 @@
-shortcut_module_link: true
diff --git a/core/themes/seven/seven.info.yml b/core/themes/seven/seven.info.yml
index 582b498d1905a19105d631c517b00313af3431bf..2140bd9637ddb6ab1e1a66cb660b42e54d2807f0 100644
--- a/core/themes/seven/seven.info.yml
+++ b/core/themes/seven/seven.info.yml
@@ -15,9 +15,6 @@ stylesheets-override:
   - jquery.ui.theme.css
 edit_stylesheets:
   - edit.css
-settings:
-  # @todo D8: Remove once themes have to be installed.
-  shortcut_module_link: '1'
 regions:
   content: Content
   help: Help
@@ -26,3 +23,6 @@ regions:
   sidebar_first: 'First sidebar'
 regions_hidden:
   - sidebar_first
+
+settings:
+  shortcut_module_link: true
diff --git a/core/themes/stark/config/schema/stark.schema.yml b/core/themes/stark/config/schema/stark.schema.yml
index 6e0293eacfcffa94f47cbf481bf392c33c220251..03311706e194db7bf9599fa05ee3575e20eb0288 100644
--- a/core/themes/stark/config/schema/stark.schema.yml
+++ b/core/themes/stark/config/schema/stark.schema.yml
@@ -1,7 +1,7 @@
 # Schema for the configuration files of the Stark theme.
 
 stark.settings:
-  type: theme_settings_default
+  type: theme_settings
   label: 'Stark settings'
 
 stark.breakpoints:
diff --git a/core/themes/stark/config/stark.settings.yml b/core/themes/stark/config/stark.settings.yml
deleted file mode 100644
index 3bb124eb8571fa0948aaf45600b782ae308e11f8..0000000000000000000000000000000000000000
--- a/core/themes/stark/config/stark.settings.yml
+++ /dev/null
@@ -1 +0,0 @@
-shortcut_module_link: false