diff --git a/core/config/schema/core.data_types.schema.yml b/core/config/schema/core.data_types.schema.yml
index e392b6a8e67aa986486051ba5edcb95090382e75..8e14b369f25d1843774e154e3b473b216422779e 100644
--- a/core/config/schema/core.data_types.schema.yml
+++ b/core/config/schema/core.data_types.schema.yml
@@ -194,6 +194,11 @@ route:
         - type: string
           label: 'Param'
 
+# HTML color value.
+color_hex:
+  type: string
+  label: 'Color'
+
 # Config dependencies.
 config_dependencies:
   type: mapping
diff --git a/core/modules/color/color.module b/core/modules/color/color.module
index 7809f5f69b8aa2afaceb659fc5a3fc59c81a4c41..02cabf39f602a4bd25eb67777c07d93c02724b8a 100644
--- a/core/modules/color/color.module
+++ b/core/modules/color/color.module
@@ -66,7 +66,7 @@ function color_css_alter(&$css) {
   $themes = list_themes();
 
   // Override stylesheets.
-  $color_paths = \Drupal::config('color.' . $theme_key)->get('stylesheets');
+  $color_paths = \Drupal::config('color.theme.' . $theme_key)->get('stylesheets');
 
   if (!empty($color_paths) && !empty($themes[$theme_key]->stylesheets['all'])) {
 
@@ -95,7 +95,7 @@ function color_preprocess_page(&$variables) {
   global $theme_key;
 
   // Override logo.
-  $logo = \Drupal::config('color.' . $theme_key)->get('logo');
+  $logo = \Drupal::config('color.theme.' . $theme_key)->get('logo');
   if ($logo && $variables['logo'] && preg_match('!' . $theme_key . '/logo.png$!', $variables['logo'])) {
     $variables['logo'] = file_create_url($logo);
   }
@@ -150,7 +150,7 @@ function color_get_palette($theme, $default = FALSE) {
 
   // Load variable.
   // @todo Default color config should be moved to yaml in the theme.
-  return \Drupal::config('color.' . $theme)->get('palette') ?: $palette;
+  return \Drupal::config('color.theme.' . $theme)->get('palette') ?: $palette;
 }
 
 /**
@@ -178,7 +178,7 @@ function color_scheme_form($complete_form, &$form_state, $theme) {
 
   // See if we're using a predefined scheme.
   // Note: we use the original theme when the default scheme is chosen.
-  $current_scheme = \Drupal::config('color.' . $theme)->get('palette');
+  $current_scheme = \Drupal::config('color.theme.' . $theme)->get('palette');
   foreach ($schemes as $key => $scheme) {
     if ($current_scheme == $scheme) {
       $scheme_name = $key;
@@ -349,7 +349,7 @@ function color_scheme_form_submit($form, &$form_state) {
   $theme = $form_state['values']['theme'];
   $info = $form_state['values']['info'];
 
-  $config = \Drupal::config('color.' . $theme);
+  $config = \Drupal::config('color.theme.' . $theme);
 
   // Resolve palette.
   $palette = $form_state['values']['palette'];
@@ -606,7 +606,7 @@ function _color_render_images($theme, &$info, &$paths, $palette) {
     if ($file == 'screenshot.png') {
       $slice = imagecreatetruecolor(150, 90);
       imagecopyresampled($slice, $target, 0, 0, $x, $y, 150, 90, $width, $height);
-      \Drupal::config('color.' . $theme)
+      \Drupal::config('color.theme.' . $theme)
         ->set('screenshot', $image)
         ->save();
     }
diff --git a/core/modules/color/config/schema/color.schema.yml b/core/modules/color/config/schema/color.schema.yml
new file mode 100644
index 0000000000000000000000000000000000000000..4b81f0c197e652b424a586388c4b6867202b7f73
--- /dev/null
+++ b/core/modules/color/config/schema/color.schema.yml
@@ -0,0 +1,20 @@
+color.theme.*:
+  type: mapping
+  label: 'Color theme settings'
+  mapping:
+    palette:
+      type: sequence
+      label: 'Palette settings'
+      sequence:
+        - type: color_hex
+    logo:
+      type: path
+      label: 'Logo path'
+    stylesheets:
+      type: sequence
+      sequence:
+        - type: path
+    files:
+      type: sequence
+      sequence:
+        - type: path
diff --git a/core/modules/color/lib/Drupal/color/Tests/ColorConfigSchemaTest.php b/core/modules/color/lib/Drupal/color/Tests/ColorConfigSchemaTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..ebaa303ffc4d69a2584af8e901c2a216e994b319
--- /dev/null
+++ b/core/modules/color/lib/Drupal/color/Tests/ColorConfigSchemaTest.php
@@ -0,0 +1,62 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\color\Tests\ColorConfigSchemaTest.
+ */
+
+namespace Drupal\color\Tests;
+
+use Drupal\config\Tests\ConfigSchemaTestBase;
+
+/**
+ * Tests the Color config schema.
+ */
+class ColorConfigSchemaTest extends ConfigSchemaTestBase {
+
+  /**
+   * Modules to enable.
+   *
+   * @var array
+   */
+  public static $modules = array('color');
+
+  /**
+   * A user with administrative permissions.
+   *
+   * @var \Drupal\user\UserInterface
+   */
+  protected $adminUser;
+
+  public static function getInfo() {
+    return array(
+      'name' => 'Color config schema',
+      'description' => 'Ensures the color config schema is correct.',
+      'group' => 'Color',
+    );
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  function setUp() {
+    parent::setUp();
+    \Drupal::service('theme_handler')->enable(array('bartik'));
+
+    // Create user.
+    $this->adminUser = $this->drupalCreateUser(array('administer themes'));
+    $this->drupalLogin($this->adminUser);
+  }
+
+  /**
+   * Tests whether the color config schema is valid.
+   */
+  function testValidColorConfigSchema() {
+    $settings_path = 'admin/appearance/settings/bartik';
+    $edit['scheme'] = '';
+    $edit['palette[bg]'] = '#123456';
+    $this->drupalPostForm($settings_path, $edit, t('Save configuration'));
+    $this->assertConfigSchema(\Drupal::service('config.typed'), 'color.theme.bartik', \Drupal::config('color.theme.bartik')->get());
+  }
+
+}
diff --git a/core/modules/color/lib/Drupal/color/Tests/ColorTest.php b/core/modules/color/lib/Drupal/color/Tests/ColorTest.php
index 79462bee904ff11a5b27c492962e5fe21b1d14f6..32bdd1a4591d0e59f8c7d2e0ca0abe113ac872b4 100644
--- a/core/modules/color/lib/Drupal/color/Tests/ColorTest.php
+++ b/core/modules/color/lib/Drupal/color/Tests/ColorTest.php
@@ -124,7 +124,7 @@ function _testColor($theme, $test_values) {
     $this->drupalPostForm($settings_path, $edit, t('Save configuration'));
 
     $this->drupalGet('<front>');
-    $stylesheets = \Drupal::config('color.' . $theme)->get('stylesheets');
+    $stylesheets = \Drupal::config('color.theme.' . $theme)->get('stylesheets');
     foreach ($stylesheets as $stylesheet) {
       $this->assertPattern('|' . file_create_url($stylesheet) . '|', 'Make sure the color stylesheet is included in the content. (' . $theme . ')');
       $stylesheet_content = join("\n", file($stylesheet));
@@ -137,7 +137,7 @@ function _testColor($theme, $test_values) {
     $this->drupalPostForm($settings_path, $edit, t('Save configuration'));
 
     $this->drupalGet('<front>');
-    $stylesheets = \Drupal::config('color.' . $theme)->get('stylesheets');
+    $stylesheets = \Drupal::config('color.theme.' . $theme)->get('stylesheets');
     foreach ($stylesheets as $stylesheet) {
       $stylesheet_content = join("\n", file($stylesheet));
       $this->assertTrue(strpos($stylesheet_content, 'color: ' . $test_values['scheme_color']) !== FALSE, 'Make sure the color we changed is in the color stylesheet. (' . $theme . ')');
diff --git a/core/modules/config/lib/Drupal/config/Tests/ConfigSchemaTestBase.php b/core/modules/config/lib/Drupal/config/Tests/ConfigSchemaTestBase.php
new file mode 100644
index 0000000000000000000000000000000000000000..61eee7e9a3e68bea6f14e685efea82ce37aef0dd
--- /dev/null
+++ b/core/modules/config/lib/Drupal/config/Tests/ConfigSchemaTestBase.php
@@ -0,0 +1,152 @@
+<?php
+
+/**
+ * @file
+ * Contains Drupal\config\Tests\DefaultConfigTest.
+ */
+
+namespace Drupal\config\Tests;
+
+use Drupal\Core\Config\Schema\Property;
+use Drupal\Core\Config\TypedConfigManagerInterface;
+use Drupal\Core\TypedData\Type\BooleanInterface;
+use Drupal\Core\TypedData\Type\StringInterface;
+use Drupal\Component\Utility\String;
+use Drupal\Core\Config\Schema\SchemaIncompleteException;
+use Drupal\Core\TypedData\PrimitiveInterface;
+use Drupal\Core\TypedData\Type\FloatInterface;
+use Drupal\Core\TypedData\Type\IntegerInterface;
+use Drupal\simpletest\WebTestBase;
+
+/**
+ * Provides a base class to help test configuration schema.
+ */
+abstract class ConfigSchemaTestBase extends WebTestBase {
+
+  /**
+   * The config schema wrapper object for the configuration object under test.
+   *
+   * @var \Drupal\Core\Config\Schema\Element
+   */
+  protected $schema;
+
+  /**
+   * The configuration object name under test.
+   *
+   * @var string
+   */
+  protected $configName;
+
+  /**
+   * @var boolean
+   */
+  protected $configPass;
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function getInfo() {
+    return array(
+      'name' => 'Default configuration',
+      'description' => 'Tests that default configuration provided by all modules matches schema.',
+      'group' => 'Configuration',
+    );
+  }
+
+  /**
+   * Asserts the TypedConfigManager has a valid schema for the configuration.
+   *
+   * @param \Drupal\Core\Config\TypedConfigManagerInterface $typed_config
+   *   The TypedConfigManager.
+   * @param string $config_name
+   *   The configuration name.
+   * @param array $config_data
+   *   The configuration data.
+   */
+  public function assertConfigSchema(TypedConfigManagerInterface $typed_config, $config_name, $config_data) {
+    $this->configName = $config_name;
+    if (!$typed_config->hasConfigSchema($config_name)) {
+      $this->fail(String::format('No schema for !config_name', array('!config_name' => $config_name)));
+      return;
+    }
+    $definition = $typed_config->getDefinition($config_name);
+    $this->schema = $typed_config->create($definition, $config_data);
+    $this->configPass = TRUE;
+    foreach ($config_data as $key => $value) {
+      $this->checkValue($key, $value);
+    }
+    if ($this->configPass) {
+      $this->pass(String::format('Schema found for !config_name and values comply with schema.', array('!config_name' => $config_name)));
+    }
+  }
+
+  /**
+   * Helper method to check data type.
+   *
+   * @param string $key
+   *   A string of configuration key.
+   * @param mixed $value
+   *   Value of given key.
+   *
+   * @return mixed
+   *   Returns mixed value.
+   */
+  protected function checkValue($key, $value) {
+    if (is_scalar($value) || $value === NULL) {
+      try {
+        $success = FALSE;
+        $type = gettype($value);
+        $element = $this->schema->get($key);
+        if ($element instanceof PrimitiveInterface) {
+          if ($type == 'integer' && $element instanceof IntegerInterface) {
+            $success = TRUE;
+          }
+          if ($type == 'double' && $element instanceof FloatInterface) {
+            $success = TRUE;
+          }
+          if ($type == 'boolean' && $element instanceof BooleanInterface) {
+            $success = TRUE;
+          }
+          if ($type == 'string' && ($element instanceof StringInterface || $element instanceof Property)) {
+            $success = TRUE;
+          }
+          // Null values are allowed for all types.
+          if ($value === NULL) {
+            $success = TRUE;
+          }
+        }
+        else {
+          // @todo throw an exception due to an incomplete schema. Only possible
+          //   once https://drupal.org/node/1910624 is complete.
+        }
+        $class = get_class($element);
+        if (!$success) {
+          $this->fail("{$this->configName}:$key has the wrong schema. Variable type is $type and schema class is $class.");
+        }
+      }
+      catch (SchemaIncompleteException $e) {
+        $this->fail("{$this->configName}:$key has no schema.");
+      }
+    }
+    else {
+      // Any non-scalar value must be an array.
+      if (!is_array($value)) {
+        $value = (array) $value;
+      }
+      // Recurse into any nested keys.
+      foreach ($value as $nested_value_key => $nested_value) {
+        $value[$nested_value_key] = $this->checkValue($key . '.' . $nested_value_key, $nested_value);
+      }
+    }
+    return $value;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function fail($message = NULL, $group = 'Other') {
+    $this->configPass = FALSE;
+    return parent::fail($message, $group);
+  }
+
+}
diff --git a/core/modules/config/lib/Drupal/config/Tests/DefaultConfigTest.php b/core/modules/config/lib/Drupal/config/Tests/DefaultConfigTest.php
index 1ef380085bfcc7779d8cd90d24798f690346d3c3..8a5d02253ddd98f5fffabdd5f0b41df030204e76 100644
--- a/core/modules/config/lib/Drupal/config/Tests/DefaultConfigTest.php
+++ b/core/modules/config/lib/Drupal/config/Tests/DefaultConfigTest.php
@@ -9,21 +9,12 @@
 
 use Drupal\config_test\TestInstallStorage;
 use Drupal\Core\Config\InstallStorage;
-use Drupal\Core\Config\Schema\Property;
 use Drupal\Core\Config\TypedConfigManager;
-use Drupal\Core\TypedData\Type\BooleanInterface;
-use Drupal\Core\TypedData\Type\StringInterface;
-use Drupal\simpletest\DrupalUnitTestBase;
-use Drupal\Component\Utility\String;
-use Drupal\Core\Config\Schema\SchemaIncompleteException;
-use Drupal\Core\TypedData\PrimitiveInterface;
-use Drupal\Core\TypedData\Type\FloatInterface;
-use Drupal\Core\TypedData\Type\IntegerInterface;
 
 /**
  * Tests default configuration availability and type with configuration schema.
  */
-class DefaultConfigTest extends DrupalUnitTestBase {
+class DefaultConfigTest extends ConfigSchemaTestBase {
 
   /**
    * Modules to enable.
@@ -32,25 +23,6 @@ class DefaultConfigTest extends DrupalUnitTestBase {
    */
   public static $modules = array('config_test');
 
-  /**
-   * The config schema wrapper object for the configuration object under test.
-   *
-   * @var \Drupal\Core\Config\Schema\Element
-   */
-  protected $schema;
-
-  /**
-   * The configuration object name under test.
-   *
-   * @var string
-   */
-  protected $configName;
-
-  /**
-   * @var boolean
-   */
-  protected $configPass;
-
   /**
    * {@inheritdoc}
    */
@@ -95,91 +67,9 @@ public function testDefaultConfig() {
         continue;
       }
 
-      $this->configName = $config_name;
       $data = $default_config_storage->read($config_name);
-      if (!$typed_config->hasConfigSchema($config_name)) {
-        $this->fail(String::format('No schema for !config_name', array('!config_name' => $config_name)));
-        continue;
-      }
-      $definition = $typed_config->getDefinition($config_name);
-      $this->schema = $typed_config->create($definition, $data);
-      $this->configPass = TRUE;
-      foreach ($data as $key => $value) {
-        $this->checkValue($key, $value);
-      }
-      if ($this->configPass) {
-        $this->pass(String::format('Schema found for !config_name and values comply with schema.', array('!config_name' => $config_name)));
-      }
-    }
-  }
-
-  /**
-   * Helper method to check data type.
-   *
-   * @param string $key
-   *   A string of configuration key.
-   * @param mixed $value
-   *   Value of given key.
-   *
-   * @return mixed
-   *   Returns mixed value.
-   */
-  protected function checkValue($key, $value) {
-    if (is_scalar($value) || $value === NULL) {
-      try {
-        $success = FALSE;
-        $type = gettype($value);
-        $element = $this->schema->get($key);
-        if ($element instanceof PrimitiveInterface) {
-          if ($type == 'integer' && $element instanceof IntegerInterface) {
-            $success = TRUE;
-          }
-          if ($type == 'double' && $element instanceof FloatInterface) {
-            $success = TRUE;
-          }
-          if ($type == 'boolean' && $element instanceof BooleanInterface) {
-            $success = TRUE;
-          }
-          if ($type == 'string' && ($element instanceof StringInterface || $element instanceof Property)) {
-            $success = TRUE;
-          }
-          // Null values are allowed for all types.
-          if ($value === NULL) {
-            $success = TRUE;
-          }
-        }
-        else {
-          // @todo throw an exception due to an incomplete schema. Only possible
-          //   once https://drupal.org/node/1910624 is complete.
-        }
-        $class = get_class($element);
-        if (!$success) {
-          $this->fail("{$this->configName}:$key has the wrong schema. Variable type is $type and schema class is $class.");
-        }
-      }
-      catch (SchemaIncompleteException $e) {
-        $this->fail("{$this->configName}:$key has no schema.");
-      }
+      $this->assertConfigSchema($typed_config, $config_name, $data);
     }
-    else {
-      // Any non-scalar value must be an array.
-      if (!is_array($value)) {
-        $value = (array) $value;
-      }
-      // Recurse into any nested keys.
-      foreach ($value as $nested_value_key => $nested_value) {
-        $value[$nested_value_key] = $this->checkValue($key . '.' . $nested_value_key, $nested_value);
-      }
-    }
-    return $value;
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  protected function fail($message = NULL, $group = 'Other') {
-    $this->configPass = FALSE;
-    return parent::fail($message, $group);
   }
 
 }