diff --git a/core/includes/common.inc b/core/includes/common.inc
index 08ce2ab..f05211c 100644
--- a/core/includes/common.inc
+++ b/core/includes/common.inc
@@ -2088,6 +2088,85 @@ function _format_date_callback(array $matches = NULL, $new_langcode = NULL) {
 }
 
 /**
+ * Parses a color string.
+ *
+ * @param $hex
+ *   A hexadecimal color string to parse. Can be prefixed with '#' and have
+ *   either one or two digits per component.
+ * @param $alpha
+ *   (Optional) Whether or not to allow an alpha transparency component.
+ *   Defaults to FALSE.
+ *
+ * @return
+ *   An array with the keys 'red', 'green', 'blue' and 'alpha' with the
+ *   corresponding integers as values. FALSE if the input is invalid.
+ *
+ * @see drupal_rgba_to_hex()
+ */
+function drupal_hex_to_rgba($hex, $alpha = FALSE) {
+  // Ignore '#' prefixes.
+  $hex = ltrim($hex, '#');
+
+  // Convert shorhands like '#abc' to '#aabbcc'.
+  if (strlen($hex) <= 4) {
+    $hex = preg_replace('|([0-9a-z])|i', '\1\1', $hex);
+  }
+
+  // Parse out the components.
+  if (!preg_match('/^([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})([0-7][0-9a-f])?$/i', $hex, $rgba)) {
+    return FALSE;
+  }
+
+  // The input is invalid, if alpha is not allowed but given.
+  if (!$alpha && isset($rgba[4])) {
+    return FALSE;
+  }
+
+  // Return the result array.
+  return array(
+    'red' => hexdec($rgba[1]),
+    'green' => hexdec($rgba[2]),
+    'blue' => hexdec($rgba[3]),
+    'alpha' => isset($rgba[4]) ? hexdec($rgba[4]) : 0,
+  );
+}
+
+/**
+ * Converts a color array to a color string.
+ *
+ * @param $rgba
+ *   An array with integer values for the keys 'red', 'green', 'blue' and
+ *   'alpha'. Components that are not given default to 0.
+ *
+ * @return
+ *   A lowercase color string like '#00ff00'.
+ *
+ * @see drupal_hex_to_rgba()
+ */
+function drupal_rgba_to_hex($rgba) {
+  if (!is_array($rgba)) {
+    return '#000000';
+  }
+
+  $rgba += array(
+    'red' => 0,
+    'green' => 0,
+    'blue' => 0,
+    'alpha' => 0,
+  );
+
+  $result = '#';
+
+  foreach (array('red', 'green', 'blue', 'alpha') as $component) {
+    if ($component != 'alpha' || $rgba[$component]) {
+      $result .= str_pad(dechex($rgba[$component]), 2, '0', STR_PAD_LEFT);
+    }
+  }
+
+  return $result;
+}
+
+/**
  * @} End of "defgroup format".
  */
 
@@ -7028,6 +7107,9 @@ function drupal_common_theme() {
     'range' => array(
       'render element' => 'element',
     ),
+    'color' => array(
+      'render element' => 'element',
+    ),
     'form' => array(
       'render element' => 'element',
     ),
diff --git a/core/includes/form.inc b/core/includes/form.inc
index 038cfca..a345d7a 100644
--- a/core/includes/form.inc
+++ b/core/includes/form.inc
@@ -4086,6 +4086,45 @@ function form_validate_url(&$element, &$form_state) {
 }
 
 /**
+ * Form element validation handler for #type 'color'.
+ */
+function form_validate_color($element, &$form_state) {
+  // Empty means black.
+  $value = trim($element['#value']);
+  if ($value === '') {
+    $value = '#000000';
+  }
+
+  // Try to parse the value.
+  if ($parsed = drupal_hex_to_rgba($value)) {
+    // Set a normalized value.
+    form_set_value($element, drupal_rgba_to_hex($parsed), $form_state);
+  }
+  else {
+    form_error($element, t('%name must be a valid color.', array('%name' => empty($element['#title']) ? $element['#parents'][0] : $element['#title'])));
+  }
+}
+
+/**
+ * Returns HTML for a color form element.
+ *
+ * @param $variables
+ *   An associative array containing:
+ *   - element: An associative array containing the properties of the element.
+ *     Properties used: #title, #value, #description, #attributes.
+ *
+ * @ingroup themeable
+ */
+function theme_color($variables) {
+  $element = $variables['element'];
+  $element['#attributes']['type'] = 'color';
+  element_set_attributes($element, array('id', 'name', 'value'));
+  _form_set_class($element, array('form-color'));
+
+  return '<input' . drupal_attributes($element['#attributes']) . ' />' . drupal_render_children($element);
+}
+
+/**
  * Returns HTML for a form.
  *
  * @param $variables
diff --git a/core/modules/simpletest/drupal_web_test_case.php b/core/modules/simpletest/drupal_web_test_case.php
index 5d868a2..574ea9d 100644
--- a/core/modules/simpletest/drupal_web_test_case.php
+++ b/core/modules/simpletest/drupal_web_test_case.php
@@ -2268,6 +2268,7 @@ class DrupalWebTestCase extends DrupalTestCase {
           case 'url':
           case 'number':
           case 'range':
+          case 'color':
           case 'hidden':
           case 'password':
           case 'email':
diff --git a/core/modules/system/system.module b/core/modules/system/system.module
index 200a335..0a47407 100644
--- a/core/modules/system/system.module
+++ b/core/modules/system/system.module
@@ -425,6 +425,14 @@ function system_element_info() {
     '#theme' => 'range',
     '#theme_wrappers' => array('form_element'),
   );
+  $types['color'] = array(
+    '#input' => TRUE,
+    '#default_value' => '#000000',
+    '#process' => array('ajax_process_form'),
+    '#element_validate' => array('form_validate_color'),
+    '#theme' => 'color',
+    '#theme_wrappers' => array('form_element'),
+  );
   $types['machine_name'] = array(
     '#input' => TRUE,
     '#default_value' => NULL,
diff --git a/core/modules/system/tests/common.test b/core/modules/system/tests/common.test
index cc51a01..0a12240 100644
--- a/core/modules/system/tests/common.test
+++ b/core/modules/system/tests/common.test
@@ -2073,6 +2073,67 @@ class CommonValidNumberStepUnitTestCase extends DrupalUnitTestCase {
 }
 
 /**
+ * Tests color conversion functions.
+ */
+class CommonColorConversionTestCase extends DrupalUnitTestCase {
+  public static function getInfo() {
+    return array(
+      'name' => 'Color conversion',
+      'description' => 'Tests color conversion by drupal_hex_to_rgba() and drupal_rgba_to_hex()',
+      'group' => 'Common',
+    );
+  }
+
+  /**
+   * Tests drupal_hex_to_rgba().
+   */
+  function testDrupalHexToRGBA() {
+    // Test shorthand conversion without alpha.
+    $this->assertEqual(drupal_hex_to_rgba('#000'), array('red' => 0, 'green' => 0, 'blue' => 0, 'alpha' => 0));
+    $this->assertEqual(drupal_hex_to_rgba('#fff'), array('red' => 255, 'green' => 255, 'blue' => 255, 'alpha' => 0));
+    $this->assertEqual(drupal_hex_to_rgba('#abc'), array('red' => 170, 'green' => 187, 'blue' => 204, 'alpha' => 0));
+    $this->assertEqual(drupal_hex_to_rgba('cba'), array('red' => 204, 'green' => 187, 'blue' => 170, 'alpha' => 0));
+
+    // Test shorthand conversion with alpha.
+    $this->assertEqual(drupal_hex_to_rgba('#0007', TRUE), array('red' => 0, 'green' => 0, 'blue' => 0, 'alpha' => 119));
+    $this->assertEqual(drupal_hex_to_rgba('#7001', TRUE), array('red' => 119, 'green' => 0, 'blue' => 0, 'alpha' => 17));
+    $this->assertEqual(drupal_hex_to_rgba('2000', TRUE), array('red' => 34, 'green' => 0, 'blue' => 0, 'alpha' => 0));
+
+    // Test conversion without alpha.
+    $this->assertEqual(drupal_hex_to_rgba('#000000'), array('red' => 0, 'green' => 0, 'blue' => 0, 'alpha' => 0));
+    $this->assertEqual(drupal_hex_to_rgba('#010203'), array('red' => 1, 'green' => 2, 'blue' => 3, 'alpha' => 0));
+
+    // Test too high alpha values.
+    $this->assertIdentical(drupal_hex_to_rgba('#0008', TRUE), FALSE);
+    $this->assertIdentical(drupal_hex_to_rgba('#a00f', TRUE), FALSE);
+    $this->assertIdentical(drupal_hex_to_rgba('#aa00aaf1', TRUE), FALSE);
+
+    // Test alpha values are invalid if they are not allowed.
+    $this->assertIdentical(drupal_hex_to_rgba('#1111'), FALSE);
+    $this->assertIdentical(drupal_hex_to_rgba('#22334455'), FALSE);
+    $this->assertIdentical(drupal_hex_to_rgba('#0000'), FALSE);
+
+    // Test bogus input.
+    $this->assertIdentical(drupal_hex_to_rgba('#foo'), FALSE);
+    $this->assertIdentical(drupal_hex_to_rgba('123456789'), FALSE);
+  }
+
+  /**
+   * Tests drupal_rgba_to_hex().
+   */
+  function testDrupalRGBAToHex() {
+    // Test conversion without alpha.
+    $this->assertEqual(drupal_rgba_to_hex(array('red' => 7)), '#070000');
+    $this->assertEqual(drupal_rgba_to_hex(array('green' => 255)), '#00ff00');
+    $this->assertEqual(drupal_rgba_to_hex(array('red' => 1, 'green' => 2, 'blue' => 3)), '#010203');
+
+    // Test conversion with alpha.
+    $this->assertEqual(drupal_rgba_to_hex(array('alpha' => 10)), '#0000000a');
+    $this->assertEqual(drupal_rgba_to_hex(array('green' => 15, 'alpha' => 3)), '#000f0003');
+  }
+}
+
+/**
  * Tests writing of data records with drupal_write_record().
  */
 class CommonDrupalWriteRecordTestCase extends DrupalWebTestCase {
diff --git a/core/modules/system/tests/form.test b/core/modules/system/tests/form.test
index 8bcce76..27c38bc 100644
--- a/core/modules/system/tests/form.test
+++ b/core/modules/system/tests/form.test
@@ -366,6 +366,41 @@ class FormsTestCase extends DrupalWebTestCase {
   }
 
   /**
+   * Tests validation of #type 'color' elements.
+   */
+  function testColorValidation() {
+    // Keys are inputs, values are expected results.
+    $values = array(
+      '' => '#000000',
+      '#000' => '#000000',
+      'AAA' => '#aaaaaa',
+      '#af0DEE' => '#af0dee',
+      '#99ccBc' => '#99ccbc',
+      '#aabbcc' => '#aabbcc',
+      '123456' => '#123456',
+    );
+
+    // Tests that valid values are properly normalized.
+    foreach ($values as $input => $expected) {
+      $edit = array(
+        'color' => $input,
+      );
+      $result = json_decode($this->drupalPost('form-test/color', $edit, 'Submit'));
+      $this->assertEqual($result->color, $expected);
+    }
+
+    // Tests invalid values are rejected.
+    $values = array('#0008', '#1234', '#fffffg', '#abcdef22', '17', '#uaa');
+    foreach ($values as $input) {
+      $edit = array(
+        'color' => $input,
+      );
+      $this->drupalPost('form-test/color', $edit, 'Submit');
+      $this->assertRaw(t('%name must be a valid color.', array('%name' => 'Color')));
+    }
+  }
+
+  /**
    * Test handling of disabled elements.
    *
    * @see _form_test_disabled_elements()
@@ -408,7 +443,7 @@ class FormsTestCase extends DrupalWebTestCase {
 
     // All the elements should be marked as disabled, including the ones below
     // the disabled container.
-    $this->assertEqual(count($disabled_elements), 39, 'The correct elements have the disabled property in the HTML code.');
+    $this->assertEqual(count($disabled_elements), 40, 'The correct elements have the disabled property in the HTML code.');
 
     $this->drupalPost(NULL, $edit, t('Submit'));
     $returned_values['hijacked'] = drupal_json_decode($this->content);
diff --git a/core/modules/system/tests/modules/form_test/form_test.module b/core/modules/system/tests/modules/form_test/form_test.module
index c727c51..d37182c 100644
--- a/core/modules/system/tests/modules/form_test/form_test.module
+++ b/core/modules/system/tests/modules/form_test/form_test.module
@@ -145,6 +145,12 @@ function form_test_menu() {
     'page arguments' => array('form_test_number', 'range'),
     'access callback' => TRUE,
   );
+  $items['form-test/color'] = array(
+    'title' => 'Color',
+    'page callback' => 'drupal_get_form',
+    'page arguments' => array('form_test_color'),
+    'access callback' => TRUE,
+  );
   $items['form-test/checkboxes-radios'] = array(
     'title' => t('Checkboxes, Radios'),
     'page callback' => 'drupal_get_form',
@@ -1277,6 +1283,32 @@ function form_test_number($form, &$form_state, $element = 'number') {
 }
 
 /**
+ * Form constructor for testing #type 'color' elements.
+ *
+ * @see form_test_color_submit()
+ * @ingroup forms
+ */
+function form_test_color($form, &$form_state) {
+  $form['color'] = array(
+    '#type' => 'color',
+    '#title' => 'Color',
+  );
+  $form['submit'] = array(
+    '#type' => 'submit',
+    '#value' => 'Submit',
+  );
+  return $form;
+}
+
+/**
+ * Form submission handler for form_test_color().
+ */
+function form_test_color_submit($form, &$form_state) {
+  drupal_json_output($form_state['values']);
+  exit;
+}
+
+/**
  * Builds a form to test the placeholder attribute.
  */
 function form_test_placeholder_test($form, &$form_state) {
@@ -1496,6 +1528,16 @@ function _form_test_disabled_elements($form, &$form_state) {
     );
   }
 
+  // Color.
+  $form['color'] = array(
+    '#type' => 'color',
+    '#title' => 'color',
+    '#disabled' => TRUE,
+    '#default_value' => '#0000ff',
+    '#test_hijack_value' => '#ff0000',
+    '#disabled' => TRUE,
+  );
+
   // Date.
   $form['date'] = array(
     '#type' => 'date',
diff --git a/core/themes/bartik/css/style.css b/core/themes/bartik/css/style.css
index 7ab3d4e..3563b44 100644
--- a/core/themes/bartik/css/style.css
+++ b/core/themes/bartik/css/style.css
@@ -1194,6 +1194,7 @@ input.form-email,
 input.form-url,
 input.form-search,
 input.form-number,
+input.form-color,
 textarea.form-textarea,
 select.form-select {
   border: 1px solid #ccc;
diff --git a/core/themes/seven/style.css b/core/themes/seven/style.css
index a3d6773..9e807d5 100644
--- a/core/themes/seven/style.css
+++ b/core/themes/seven/style.css
@@ -612,6 +612,7 @@ div.teaser-checkbox .form-item,
 .form-disabled input.form-url,
 .form-disabled input.form-search,
 .form-disabled input.form-number,
+.form-disabled input.form-color,
 .form-disabled input.form-file,
 .form-disabled textarea.form-textarea,
 .form-disabled select.form-select {
@@ -703,6 +704,7 @@ input.form-email,
 input.form-url,
 input.form-search,
 input.form-number,
+input.form-color,
 input.form-file,
 textarea.form-textarea,
 select.form-select {
@@ -721,6 +723,7 @@ input.form-email:focus,
 input.form-url:focus,
 input.form-search:focus,
 input.form-number:focus,
+input.form-color:focus,
 input.form-file:focus,
 textarea.form-textarea:focus,
 select.form-select:focus {
