diff --git a/core/includes/common.inc b/core/includes/common.inc
index 0b95dcd..2df044b 100644
--- a/core/includes/common.inc
+++ b/core/includes/common.inc
@@ -3,6 +3,7 @@
 use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
 use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
 use Drupal\Core\Database\Database;
+use Drupal\Core\Template\Attribute;
 
 /**
  * @file
@@ -2270,11 +2271,7 @@ function drupal_http_header_attributes(array $attributes = array()) {
  * @ingroup sanitization
  */
 function drupal_attributes(array $attributes = array()) {
-  foreach ($attributes as $attribute => &$data) {
-    $data = implode(' ', (array) $data);
-    $data = $attribute . '="' . check_plain($data) . '"';
-  }
-  return $attributes ? ' ' . implode(' ', $attributes) : '';
+  return new Attribute($attributes);
 }
 
 /**
diff --git a/core/includes/theme.inc b/core/includes/theme.inc
index 84e2ec7..2249cc9 100644
--- a/core/includes/theme.inc
+++ b/core/includes/theme.inc
@@ -2312,7 +2312,7 @@ function _theme_table_cell($cell, $header = FALSE) {
  */
 function template_preprocess(&$variables, $hook) {
   global $user;
-  static $count = array();
+  static $count = array(), $default_attributes;
 
   // Track run count for each hook to provide zebra striping.
   // See "template_preprocess_block()" which provides the same feature specific to blocks.
@@ -2323,9 +2323,6 @@ function template_preprocess(&$variables, $hook) {
   // Tell all templates where they are located.
   $variables['directory'] = path_to_theme();
 
-  // Initialize html class attribute for the current hook.
-  $variables['classes_array'] = array(drupal_html_class($hook));
-
   // Merge in variables that don't depend on hook and don't change during a
   // single page request.
   // Use the advanced drupal_static() pattern, since this is called very often.
@@ -2340,7 +2337,17 @@ function template_preprocess(&$variables, $hook) {
   if (!isset($default_variables) || ($user !== $default_variables['user'])) {
     $default_variables = _template_preprocess_default_variables();
   }
-  $variables += $default_variables;
+  if (!isset($default_attributes)) {
+    $default_attributes = drupal_attributes(array('class' => array()));
+  }
+  $variables += $default_variables + array(
+    'attributes' => clone $default_attributes,
+    'title_attributes' => clone $default_attributes,
+    'content_attributes' => clone $default_attributes,
+  );
+
+  // Initialize html class attribute for the current hook.
+  $variables['attributes']['class'][] = drupal_html_class($hook);
 }
 
 /**
@@ -2351,9 +2358,6 @@ function _template_preprocess_default_variables() {
 
   // Variables that don't depend on a database connection.
   $variables = array(
-    'attributes_array' => array(),
-    'title_attributes_array' => array(),
-    'content_attributes_array' => array(),
     'title_prefix' => array(),
     'title_suffix' => array(),
     'user' => $user,
@@ -2385,34 +2389,6 @@ function _template_preprocess_default_variables() {
 }
 
 /**
- * Adds helper variables derived from variables defined during preprocessing.
- *
- * When preparing variables for a theme hook implementation, all 'preprocess'
- * functions run first, then all 'process' functions (see theme() for details
- * about the full sequence).
- *
- * This function serializes array variables manipulated during the preprocessing
- * phase into strings for convenient use by templates. As with
- * template_preprocess(), this function does not get called for theme hooks
- * implemented as functions.
- *
- * @see theme()
- * @see template_preprocess()
- */
-function template_process(&$variables, $hook) {
-  // Flatten out classes.
-  $variables['classes'] = implode(' ', $variables['classes_array']);
-
-  // Flatten out attributes, title_attributes, and content_attributes.
-  // Because this function can be called very often, and often with empty
-  // attributes, optimize performance by only calling drupal_attributes() if
-  // necessary.
-  $variables['attributes'] = $variables['attributes_array'] ? drupal_attributes($variables['attributes_array']) : '';
-  $variables['title_attributes'] = $variables['title_attributes_array'] ? drupal_attributes($variables['title_attributes_array']) : '';
-  $variables['content_attributes'] = $variables['content_attributes_array'] ? drupal_attributes($variables['content_attributes_array']) : '';
-}
-
-/**
  * Preprocess variables for html.tpl.php
  *
  * @see system_elements()
@@ -2424,22 +2400,24 @@ function template_preprocess_html(&$variables) {
   // Compile a list of classes that are going to be applied to the body element.
   // This allows advanced theming based on context (home page, node of certain type, etc.).
   // Add a class that tells us whether we're on the front page or not.
-  $variables['classes_array'][] = $variables['is_front'] ? 'front' : 'not-front';
+  $variables['attributes']['class'][] = $variables['is_front'] ? 'front' : 'not-front';
   // Add a class that tells us whether the page is viewed by an authenticated user or not.
-  $variables['classes_array'][] = $variables['logged_in'] ? 'logged-in' : 'not-logged-in';
+  $variables['attributes']['class'][] = $variables['logged_in'] ? 'logged-in' : 'not-logged-in';
 
   // Add information about the number of sidebars.
   if (!empty($variables['page']['sidebar_first']) && !empty($variables['page']['sidebar_second'])) {
-    $variables['classes_array'][] = 'two-sidebars';
+    $variables['attributes']['class'][] = 'two-sidebars';
   }
   elseif (!empty($variables['page']['sidebar_first'])) {
-    $variables['classes_array'][] = 'one-sidebar sidebar-first';
+    $variables['attributes']['class'][] = 'one-sidebar';
+    $variables['attributes']['class'][] = 'sidebar-first';
   }
   elseif (!empty($variables['page']['sidebar_second'])) {
-    $variables['classes_array'][] = 'one-sidebar sidebar-second';
+    $variables['attributes']['class'][] = 'one-sidebar';
+    $variables['attributes']['class'][] = 'sidebar-second';
   }
   else {
-    $variables['classes_array'][] = 'no-sidebars';
+    $variables['attributes']['class'][] = 'no-sidebars';
   }
 
   // Populate the body classes.
@@ -2449,23 +2427,22 @@ function template_preprocess_html(&$variables) {
         // Add current suggestion to page classes to make it possible to theme
         // the page depending on the current page type (e.g. node, admin, user,
         // etc.) as well as more specific data like node-12 or node-edit.
-        $variables['classes_array'][] = drupal_html_class($suggestion);
+        $variables['attributes']['class'][] = drupal_html_class($suggestion);
       }
     }
   }
 
   // If on an individual node page, add the node type to body classes.
   if ($node = menu_get_object()) {
-    $variables['classes_array'][] = drupal_html_class('node-type-' . $node->type);
+    $variables['attributes']['class'][] = drupal_html_class('node-type-' . $node->type);
   }
 
   // Initializes attributes which are specific to the html and body elements.
-  $variables['html_attributes_array'] = array();
-  $variables['body_attributes_array'] = array();
+  $variables['html_attributes'] = drupal_attributes();
 
   // HTML element attributes.
-  $variables['html_attributes_array']['lang'] = $language_interface->langcode;
-  $variables['html_attributes_array']['dir'] = $language_interface->direction ? 'rtl' : 'ltr';
+  $variables['html_attributes']['lang'] = $language_interface->langcode;
+  $variables['html_attributes']['dir'] = $language_interface->direction ? 'rtl' : 'ltr';
 
   // Add favicon.
   if (theme_get_setting('toggle_favicon')) {
@@ -2633,10 +2610,6 @@ function template_process_page(&$variables) {
  * @see html.tpl.php
  */
 function template_process_html(&$variables) {
-  // Flatten out html_attributes and body_attributes.
-  $variables['html_attributes'] = drupal_attributes($variables['html_attributes_array']);
-  $variables['body_attributes'] = drupal_attributes($variables['body_attributes_array']);
-
   // Render page_top and page_bottom into top level variables.
   $variables['page_top'] = drupal_render($variables['page']['page_top']);
   $variables['page_bottom'] = drupal_render($variables['page']['page_bottom']);
@@ -2654,8 +2627,8 @@ function template_process_html(&$variables) {
  * Generate an array of suggestions from path arguments.
  *
  * This is typically called for adding to the 'theme_hook_suggestions' or
- * 'classes_array' variables from within preprocess functions, when wanting to
- * base the additional suggestions on the path of the current page.
+ * 'attributes' class key variables from within preprocess functions, when
+ * wanting to base the additional suggestions on the path of the current page.
  *
  * @param $args
  *   An array of path arguments, such as from function arg().
@@ -2670,7 +2643,7 @@ function template_process_html(&$variables) {
  * @return
  *   An array of suggestions, suitable for adding to
  *   $variables['theme_hook_suggestions'] within a preprocess function or to
- *   $variables['classes_array'] if the suggestions represent extra CSS classes.
+ *   $variables['attributes']['class'] if the suggestions represent extra CSS classes.
  */
 function theme_get_suggestions($args, $base, $delimiter = '__') {
 
@@ -2800,18 +2773,19 @@ function template_preprocess_maintenance_page(&$variables) {
   $variables['title']             = drupal_get_title();
 
   // Compile a list of classes that are going to be applied to the body element.
-  $variables['classes_array'][] = 'in-maintenance';
+  $variables['attributes']['class'][] = 'in-maintenance';
   if (isset($variables['db_is_active']) && !$variables['db_is_active']) {
-    $variables['classes_array'][] = 'db-offline';
+    $variables['attributes']['class'][] = 'db-offline';
   }
   if ($variables['layout'] == 'both') {
-    $variables['classes_array'][] = 'two-sidebars';
+    $variables['attributes']['class'][] = 'two-sidebars';
   }
   elseif ($variables['layout'] == 'none') {
-    $variables['classes_array'][] = 'no-sidebars';
+    $variables['attributes']['class'][] = 'no-sidebars';
   }
   else {
-    $variables['classes_array'][] = 'one-sidebar sidebar-' . $variables['layout'];
+    $variables['attributes']['class'][] = 'one-sidebar';
+    $variables['attributes']['class'][] = 'sidebar-' . $variables['layout'];
   }
 
   // Dead databases will show error messages so supplying this template will
@@ -2849,6 +2823,6 @@ function template_preprocess_region(&$variables) {
   $variables['content'] = $variables['elements']['#children'];
   $variables['region'] = $variables['elements']['#region'];
 
-  $variables['classes_array'][] = drupal_region_class($variables['region']);
+  $variables['attributes']['class'][] = drupal_region_class($variables['region']);
   $variables['theme_hook_suggestions'][] = 'region__' . $variables['region'];
 }
diff --git a/core/lib/Drupal/Core/Template/Attribute.php b/core/lib/Drupal/Core/Template/Attribute.php
new file mode 100644
index 0000000..dd276e3
--- /dev/null
+++ b/core/lib/Drupal/Core/Template/Attribute.php
@@ -0,0 +1,100 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\Core\Template\Attribute.
+ */
+
+namespace Drupal\Core\Template;
+use ArrayAccess;
+use Twig_Markup;
+
+/**
+ * A class that can be used for collecting then rendering HTML attributtes.
+ *
+ * To use, one may both pass in an array of already defined attributes and
+ * add attributes to it like using array syntax.
+ * @code
+ *  $attributes = new Attribute(array('id' => 'socks'));
+ *  $attributes['class'] = array('black-cat', 'white-cat');
+ *  $attributes['class'][] = 'black-white-cat';
+ *  echo '<cat ' . $attributes . '>';
+ *  // Produces <cat id="socks" class="black-cat white-cat black-white-cat">
+ * @endcode
+ *
+ * individual parts of the attribute may be printed first.
+ * @code
+ *  $attributes = new Attribute(array('id' => 'socks'));
+ *  $attributes['class'] = array('black-cat', 'white-cat');
+ *  $attributes['class'][] = 'black-white-cat';
+ *  echo '<cat class="cat ' . $attributes['class'] . '" ' . $attributes . '>';
+ *  // Produces <cat class="cat black-cat white-cat black-white-cat" id="socks">
+ * @endcode
+ *
+ * Once it's turned into a string, it cannot be re-used.
+ *
+ * @see drupal_attributes()
+ */
+class Attribute implements ArrayAccess {
+  protected $storage = array();
+
+  function __construct($attributes = array()) {
+    foreach ($attributes as $name => $value) {
+      $this->offsetSet($name, $value);
+    }
+  }
+
+  function offsetGet($name) {
+    if (isset($this->storage[$name])) {
+      return $this->storage[$name];
+    }
+  }
+
+  function offsetSet($name, $value) {
+    if (is_array($value)) {
+      $value = new AttributeArray($name, $value);
+    }
+    elseif (is_bool($value)) {
+      $value = new AttributeBoolean($name, $value);
+    }
+    else {
+      $value = new AttributeString($name, $value);
+    }
+    // The $name could be NULL.
+    if (isset($name)) {
+      $this->storage[$name] = $value;
+    }
+    else {
+      $this->storage[] = $value;
+    }
+  }
+
+  function offsetUnset($name) {
+    unset($this->storage[$name]);
+  }
+
+  function offsetExists($name) {
+    return isset($this->storage[$name]);
+  }
+
+  function __toString() {
+    $return = '';
+    foreach ($this->storage as $name => $value) {
+      if (!$value->printed()) {
+        $rendered = is_object($value) ? $value->render() : (check_plain($name) . ' = "' . check_plain($value) . '"');
+        if ($rendered) {
+          $return .= " $rendered";
+        }
+      }
+    }
+    return $return;
+  }
+
+  function  __clone() {
+    foreach ($this->storage as $name => $value) {
+      if (is_object($value)) {
+        $this->storage[$name] = clone $value;
+      }
+    }
+  }
+}
diff --git a/core/lib/Drupal/Core/Template/AttributeArray.php b/core/lib/Drupal/Core/Template/AttributeArray.php
new file mode 100644
index 0000000..f796c2a
--- /dev/null
+++ b/core/lib/Drupal/Core/Template/AttributeArray.php
@@ -0,0 +1,58 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\Core\Template\AttributeArray.
+ */
+
+/**
+ * A class that defines a type of Attribute that can be added to as an array.
+ *
+ * To use with Attribute, the array must be specified.
+ * Correct:
+ * @code
+ *  $attributes = new Attribute(array());
+ *  $attributes['class'] = array();
+ *  $attributes['class'][] = 'cat';
+ * @endcode
+ * Incorrect:
+ * @code
+ *  $attributes = new Attribute(array());
+ *  $attributes['class'][] = 'cat';
+ * @endcode
+ *
+ * @see Drupal\Core\Template\Attribute
+ */
+namespace Drupal\Core\Template;
+use ArrayAccess;
+
+class AttributeArray extends AttributeValueBase implements ArrayAccess {
+  function offsetGet($offset) {
+    return $this->value[$offset];
+  }
+
+  function offsetSet($offset, $value) {
+    if (isset($offset)) {
+      $this->value[$offset] = $value;
+    }
+    else {
+      $this->value[] = $value;
+    }
+  }
+
+  function offsetUnset($offset) {
+    unset($this->value[$offset]);
+  }
+
+  function offsetExists($offset) {
+    return isset($this->value[$offset]);
+  }
+  function value() {
+    return $this->value;
+  }
+
+  function __toString() {
+    $this->printed = TRUE;
+    return implode(' ', array_map('check_plain', $this->value));
+  }
+}
diff --git a/core/lib/Drupal/Core/Template/AttributeBoolean.php b/core/lib/Drupal/Core/Template/AttributeBoolean.php
new file mode 100644
index 0000000..deda2f0
--- /dev/null
+++ b/core/lib/Drupal/Core/Template/AttributeBoolean.php
@@ -0,0 +1,38 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\Core\Template\AttributeBoolean.
+ */
+
+namespace Drupal\Core\Template;
+
+/**
+ * A class that defines a type of boolean HTML attribute.
+ *
+ * Boolean HTML attributes are not attributes with values of TRUE/FALSE.
+ * They are attributes that if they exist in the tag, they are TRUE.
+ * Examples include selected, disabled, checked, readonly.
+ *
+ * To set a boolean attribute on the Attribute class, set it to TRUE.
+ * @code
+ *  $attributes = new Attribute(array());
+ *  $attributes['disabled'] = TRUE;
+ *  echo '<select ' . $attributes . '/>';
+ *  // produces <select disabled>;
+ *  $attributes['disabled'] = FALSE;
+ *  echo '<select ' . $attributes . '/>';
+ *  // produces <select>;
+ * @endcode
+ *
+ * @see Drupal\Core\Template\Attribute
+ */
+class AttributeBoolean extends AttributeValueBase {
+  function render() {
+    return $this->__toString();
+  }
+  function __toString() {
+    $this->printed = TRUE;
+    return $this->value === FALSE ? '' : check_plain($this->name);
+  }
+}
diff --git a/core/lib/Drupal/Core/Template/AttributeString.php b/core/lib/Drupal/Core/Template/AttributeString.php
new file mode 100644
index 0000000..4f34145
--- /dev/null
+++ b/core/lib/Drupal/Core/Template/AttributeString.php
@@ -0,0 +1,30 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\Core\Template\AttributeString.
+ */
+
+namespace Drupal\Core\Template;
+
+/**
+ * A class that represents most standard HTML attributes.
+ *
+ * To use with the Attribute class, set the key to be the attribute name
+ * and the value the attribute value.
+ * @code
+ *  $attributes = new Attribute(array());
+ *  $attributes['id'] = 'socks';
+ *  $attributes['style'] = 'background-color:white';
+ *  echo '<cat ' . $attributes . '>';
+ *  // Produces: <cat id="socks" style="background-color:white">.
+ * @endcode
+ *
+ * @see Drupal\Core\Template\Attribute
+ */
+class AttributeString extends AttributeValueBase {
+  function __toString() {
+    $this->printed = TRUE;
+    return check_plain($this->value);
+  }
+}
diff --git a/core/lib/Drupal/Core/Template/AttributeValueBase.php b/core/lib/Drupal/Core/Template/AttributeValueBase.php
new file mode 100644
index 0000000..8d8c1a0
--- /dev/null
+++ b/core/lib/Drupal/Core/Template/AttributeValueBase.php
@@ -0,0 +1,31 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\Core\Template\AttributeValueBase.
+ */
+
+namespace Drupal\Core\Template;
+
+/**
+ * Defines the base class for an attribute type.
+ *
+ * @see Drupal\Core\Template\Attribute
+ */
+abstract class AttributeValueBase {
+  protected $printed = FALSE, $value, $name;
+
+  function __construct($name, $value) {
+    $this->name = $name;
+    $this->value = $value;
+  }
+
+  function render() {
+    return $this->name . '="' . $this . '"';
+  }
+
+  function printed() {
+    return $this->printed;
+  }
+  abstract function __toString();
+}
diff --git a/core/modules/aggregator/aggregator-wrapper.tpl.php b/core/modules/aggregator/aggregator-wrapper.tpl.php
index 7cac5bf..0e97ba4 100644
--- a/core/modules/aggregator/aggregator-wrapper.tpl.php
+++ b/core/modules/aggregator/aggregator-wrapper.tpl.php
@@ -14,7 +14,7 @@
  * @ingroup themeable
  */
 ?>
-<div class="<?php print $classes; ?>">
+<div class="<?php print $attributes['class']; ?>">
   <?php print $content; ?>
   <?php print $pager; ?>
 </div>
diff --git a/core/modules/aggregator/aggregator.module b/core/modules/aggregator/aggregator.module
index 2d37b73..37278c5 100644
--- a/core/modules/aggregator/aggregator.module
+++ b/core/modules/aggregator/aggregator.module
@@ -814,6 +814,6 @@ function aggregator_aggregator_fetch_info() {
  */
 function aggregator_preprocess_block(&$variables) {
   if ($variables['block']->module == 'aggregator') {
-    $variables['attributes_array']['role'] = 'complementary';
+    $variables['attributes']['role'] = 'complementary';
   }
 }
diff --git a/core/modules/block/block.module b/core/modules/block/block.module
index 455f98f..db80556 100644
--- a/core/modules/block/block.module
+++ b/core/modules/block/block.module
@@ -998,10 +998,10 @@ function template_preprocess_block(&$variables) {
   // Create the $content variable that templates expect.
   $variables['content'] = $variables['elements']['#children'];
 
-  $variables['classes_array'][] = drupal_html_class('block-' . $variables['block']->module);
+  $variables['attributes']['class'][] = drupal_html_class('block-' . $variables['block']->module);
 
   // Add default class for block content.
-  $variables['content_attributes_array']['class'][] = 'content';
+  $variables['content_attributes']['class'][] = 'content';
 
   $variables['theme_hook_suggestions'][] = 'block__' . $variables['block']->region;
   $variables['theme_hook_suggestions'][] = 'block__' . $variables['block']->module;
diff --git a/core/modules/block/block.tpl.php b/core/modules/block/block.tpl.php
index 78387a8..895b49f 100644
--- a/core/modules/block/block.tpl.php
+++ b/core/modules/block/block.tpl.php
@@ -10,10 +10,9 @@
  * - $block->module: Module that generated the block.
  * - $block->delta: An ID for the block, unique within each module.
  * - $block->region: The block region embedding the current block.
- * - $classes: String of classes that can be used to style contextually through
- *   CSS. It can be manipulated through the variable $classes_array from
- *   preprocess functions. The default values can be one or more of the
- *   following:
+ * - $attributes: An instance of Attributes class that can be manipulated as an
+ *    array and printed as a string.
+ *    It includes the 'class' information, which includes:
  *   - block: The current template type, i.e., "theming hook".
  *   - block-[module]: The module generating the block. For example, the user
  *     module is responsible for handling the default user navigation block. In
@@ -26,8 +25,6 @@
  *   the template.
  *
  * Helper variables:
- * - $classes_array: Array of html class attribute values. It is flattened
- *   into a string within the variable $classes.
  * - $block_zebra: Outputs 'odd' and 'even' dependent on each block region.
  * - $zebra: Same output as $block_zebra but independent of any block region.
  * - $block_id: Counter dependent on each block region.
@@ -44,7 +41,7 @@
  * @ingroup themeable
  */
 ?>
-<div id="<?php print $block_html_id; ?>" class="<?php print $classes; ?>"<?php print $attributes; ?>>
+<div id="<?php print $block_html_id; ?>" <?php print $attributes; ?>>
 
   <?php print render($title_prefix); ?>
 <?php if ($block->subject): ?>
diff --git a/core/modules/block/lib/Drupal/block/Tests/BlockTemplateSuggestionsUnitTest.php b/core/modules/block/lib/Drupal/block/Tests/BlockTemplateSuggestionsUnitTest.php
index 32b1e09..f30956b 100644
--- a/core/modules/block/lib/Drupal/block/Tests/BlockTemplateSuggestionsUnitTest.php
+++ b/core/modules/block/lib/Drupal/block/Tests/BlockTemplateSuggestionsUnitTest.php
@@ -47,10 +47,10 @@ class BlockTemplateSuggestionsUnitTest extends UnitTestBase {
     $variables2['elements']['#block'] = $block2;
     $variables2['elements']['#children'] = '';
     // Test adding a class to the block content.
-    $variables2['content_attributes_array']['class'][] = 'test-class';
+    $variables2['content_attributes']['class'][] = 'test-class';
     template_preprocess_block($variables2);
     $this->assertEqual($variables2['theme_hook_suggestions'], array('block__footer', 'block__block', 'block__block__hyphen_test'), t('Hyphens (-) in block delta were replaced by underscore (_)'));
     // Test that the default class and added class are available.
-    $this->assertEqual($variables2['content_attributes_array']['class'], array('test-class', 'content'), t('Default .content class added to block content_attributes_array'));
+    $this->assertEqual($variables2['content_attributes']['class'], array('test-class', 'content'), t('Default .content class added to block content_attributes_array'));
   }
 }
diff --git a/core/modules/book/book.module b/core/modules/book/book.module
index 987d281..a4a3b14 100644
--- a/core/modules/book/book.module
+++ b/core/modules/book/book.module
@@ -1068,7 +1068,7 @@ function _book_link_defaults($nid) {
  */
 function book_preprocess_block(&$variables) {
   if ($variables['block']-> module == 'book') {
-    $variables['attributes_array']['role'] = 'navigation';
+    $variables['attributes']['role'] = 'navigation';
   }
 }
 
@@ -1233,8 +1233,8 @@ function template_preprocess_book_export_html(&$variables) {
   $variables['head'] = drupal_get_html_head();
 
   // HTML element attributes.
-  $variables['html_attributes_array']['lang'] = $language_interface->langcode;
-  $variables['html_attributes_array']['dir'] = $language_interface->direction ? 'rtl' : 'ltr';
+  $variables['html_attributes']['lang'] = $language_interface->langcode;
+  $variables['html_attributes']['dir'] = $language_interface->direction ? 'rtl' : 'ltr';
 }
 
 /**
@@ -1244,7 +1244,7 @@ function template_preprocess_book_export_html(&$variables) {
  */
 function template_process_book_export_html(&$variables) {
   // Flatten out html_attributes
-  $variables['html_attributes'] = drupal_attributes($variables['html_attributes_array']);
+  $variables['html_attributes'] = drupal_attributes($variables['html_attributes']);
 }
 
 /**
diff --git a/core/modules/comment/comment-wrapper.tpl.php b/core/modules/comment/comment-wrapper.tpl.php
index 1f97851..a5567ba 100644
--- a/core/modules/comment/comment-wrapper.tpl.php
+++ b/core/modules/comment/comment-wrapper.tpl.php
@@ -8,9 +8,9 @@
  * - $content: The array of content-related elements for the node. Use
  *   render($content) to print them all, or
  *   print a subset such as render($content['comment_form']).
- * - $classes: String of classes that can be used to style contextually through
- *   CSS. It can be manipulated through the variable $classes_array from
- *   preprocess functions. The default value has the following:
+ * - $attributes: An instance of Attributes class that can be manipulated as an
+ *    array and printed as a string.
+ *    It includes the 'class' information, which includes:
  *   - comment-wrapper: The current template type, i.e., "theming hook".
  * - $title_prefix (array): An array containing additional output populated by
  *   modules, intended to be displayed in front of the main title tag that
@@ -27,15 +27,11 @@
  *   - COMMENT_MODE_FLAT
  *   - COMMENT_MODE_THREADED
  *
- * Other variables:
- * - $classes_array: Array of html class attribute values. It is flattened
- *   into a string within the variable $classes.
- *
  * @see template_preprocess_comment_wrapper()
  * @see theme_comment_wrapper()
  */
 ?>
-<section id="comments" class="<?php print $classes; ?>"<?php print $attributes; ?>>
+<section id="comments" <?php print $attributes; ?>>
   <?php if ($content['comments'] && $node->type != 'forum'): ?>
     <?php print render($title_prefix); ?>
     <h2 class="title"><?php print t('Comments'); ?></h2>
diff --git a/core/modules/comment/comment.module b/core/modules/comment/comment.module
index 9f47d37..17717a7 100644
--- a/core/modules/comment/comment.module
+++ b/core/modules/comment/comment.module
@@ -2108,7 +2108,7 @@ function comment_form_submit($form, &$form_state) {
  */
 function comment_preprocess_block(&$variables) {
   if ($variables['block']->module == 'comment') {
-    $variables['attributes_array']['role'] = 'navigation';
+    $variables['attributes']['role'] = 'navigation';
   }
 }
 
@@ -2156,20 +2156,20 @@ function template_preprocess_comment(&$variables) {
   // Gather comment classes.
   // 'published' class is not needed, it is either 'preview' or 'unpublished'.
   if ($variables['status'] != 'published') {
-    $variables['classes_array'][] = $variables['status'];
+    $variables['attributes']['class'][] = $variables['status'];
   }
   if ($variables['new']) {
-    $variables['classes_array'][] = 'new';
+    $variables['attributes']['class'][] = 'new';
   }
   if (!$comment->uid) {
-    $variables['classes_array'][] = 'by-anonymous';
+    $variables['attributes']['class'][] = 'by-anonymous';
   }
   else {
     if ($comment->uid == $variables['node']->uid) {
-      $variables['classes_array'][] = 'by-node-author';
+      $variables['attributes']['class'][] = 'by-node-author';
     }
     if ($comment->uid == $variables['user']->uid) {
-      $variables['classes_array'][] = 'by-viewer';
+      $variables['attributes']['class'][] = 'by-viewer';
     }
   }
 }
diff --git a/core/modules/comment/comment.tpl.php b/core/modules/comment/comment.tpl.php
index e94e6e4..72d7847 100644
--- a/core/modules/comment/comment.tpl.php
+++ b/core/modules/comment/comment.tpl.php
@@ -25,10 +25,9 @@
  * - $status: Comment status. Possible values are:
  *   unpublished, published, or preview.
  * - $title: Linked title.
- * - $classes: String of classes that can be used to style contextually through
- *   CSS. It can be manipulated through the variable $classes_array from
- *   preprocess functions. The default values can be one or more of the
- *   following:
+ * - $attributes: An instance of Attributes class that can be manipulated as an
+ *    array and printed as a string.
+ *    It includes the 'class' information, which includes:
  *   - comment: The current template type; e.g., 'theming hook'.
  *   - by-anonymous: Comment by an unregistered user.
  *   - by-node-author: Comment by the author of the parent node.
@@ -48,17 +47,13 @@
  * - $comment: Full comment object.
  * - $node: Node entity the comments are attached to.
  *
- * Other variables:
- * - $classes_array: Array of html class attribute values. It is flattened
- *   into a string within the variable $classes.
- *
  * @see template_preprocess()
  * @see template_preprocess_comment()
  * @see template_process()
  * @see theme_comment()
  */
 ?>
-<article class="<?php print $classes; ?> clearfix"<?php print $attributes; ?>>
+<article class="<?php print $attributes['class']; ?> clearfix"<?php print $attributes; ?>>
 
   <?php print render($title_prefix); ?>
   <?php if ($new): ?>
diff --git a/core/modules/contextual/contextual.module b/core/modules/contextual/contextual.module
index 844e9ee..a09d09f 100644
--- a/core/modules/contextual/contextual.module
+++ b/core/modules/contextual/contextual.module
@@ -108,7 +108,7 @@ function contextual_preprocess(&$variables, $hook) {
       '#element' => $element,
     );
     // Mark this element as potentially having contextual links attached to it.
-    $variables['classes_array'][] = 'contextual-region';
+    $variables['attributes']['class'][] = 'contextual-region';
   }
 }
 
diff --git a/core/modules/field/field.module b/core/modules/field/field.module
index ce2e214..952c1ab 100644
--- a/core/modules/field/field.module
+++ b/core/modules/field/field.module
@@ -1076,7 +1076,7 @@ function template_preprocess_field(&$variables, $hook) {
   // drupal_html_class().
   $variables['field_name_css'] = strtr($element['#field_name'], '_', '-');
   $variables['field_type_css'] = strtr($element['#field_type'], '_', '-');
-  $variables['classes_array'] = array(
+  $variables['attributes']['class'] = array(
     'field',
     'field-name-' . $variables['field_name_css'],
     'field-type-' . $variables['field_type_css'],
@@ -1085,7 +1085,7 @@ function template_preprocess_field(&$variables, $hook) {
   // Add a "clearfix" class to the wrapper since we float the label and the
   // field items in field.css if the label is inline.
   if ($element['#label_display'] == 'inline') {
-    $variables['classes_array'][] = 'clearfix';
+    $variables['attributes']['class'][] = 'clearfix';
   }
 
   // Add specific suggestions that can override the default implementation.
@@ -1104,19 +1104,23 @@ function template_preprocess_field(&$variables, $hook) {
  * @see field.tpl.php
  */
 function template_process_field(&$variables, $hook) {
+  static $default_attributes;
   // The default theme implementation is a function, so template_process() does
   // not automatically run, so we need to flatten the classes and attributes
   // here. For best performance, only call drupal_attributes() when needed, and
   // note that template_preprocess_field() does not initialize the
-  // *_attributes_array variables.
-  $variables['classes'] = implode(' ', $variables['classes_array']);
-  $variables['attributes'] = empty($variables['attributes_array']) ? '' : drupal_attributes($variables['attributes_array']);
-  $variables['title_attributes'] = empty($variables['title_attributes_array']) ? '' : drupal_attributes($variables['title_attributes_array']);
-  $variables['content_attributes'] = empty($variables['content_attributes_array']) ? '' : drupal_attributes($variables['content_attributes_array']);
+  // *_attributes variables.
+  if (!isset($default_attributes)) {
+    $default_attributes = drupal_attributes();
+  }
+  $variables['attributes'] = isset($variables['attributes']) ? drupal_attributes($variables['attributes']) : clone $default_attributes;
+  $variables['title_attributes'] = isset($variables['title_attributes']) ? drupal_attributes($variables['title_attributes']) : clone($default_attributes);
+  $variables['content_attributes'] = isset($variables['content_attributes']) ? drupal_attributes($variables['content_attributes']) : clone($default_attributes);
   foreach ($variables['items'] as $delta => $item) {
-    $variables['item_attributes'][$delta] = empty($variables['item_attributes_array'][$delta]) ? '' : drupal_attributes($variables['item_attributes_array'][$delta]);
+    $variables['item_attributes'][$delta] = isset($variables['item_attributes'][$delta]) ? drupal_attributes($variables['item_attributes'][$delta]) : clone($default_attributes);
   }
 }
+
 /**
  * @} End of "defgroup field".
  */
@@ -1198,7 +1202,7 @@ function theme_field($variables) {
   $output .= '</div>';
 
   // Render the top-level DIV.
-  $output = '<div class="' . $variables['classes'] . '"' . $variables['attributes'] . '>' . $output . '</div>';
+  $output = '<div ' . $variables['attributes'] . '>' . $output . '</div>';
 
   return $output;
 }
diff --git a/core/modules/field/theme/field.tpl.php b/core/modules/field/theme/field.tpl.php
index c496908..8febcf3 100644
--- a/core/modules/field/theme/field.tpl.php
+++ b/core/modules/field/theme/field.tpl.php
@@ -11,10 +11,9 @@
  * - $items: An array of field values. Use render() to output them.
  * - $label: The item label.
  * - $label_hidden: Whether the label display is set to 'hidden'.
- * - $classes: String of classes that can be used to style contextually through
- *   CSS. It can be manipulated through the variable $classes_array from
- *   preprocess functions. The default values can be one or more of the
- *   following:
+ * - $attributes: An instance of Attributes class that can be manipulated as an
+ *    array and printed as a string.
+ *    It includes the 'class' information, which includes:
  *   - field: The current template type, i.e., "theming hook".
  *   - field-name-[field_name]: The current field name. For example, if the
  *     field name is "field_description" it would result in
@@ -35,8 +34,6 @@
  *   hidden.
  * - $field_name_css: The css-compatible field name.
  * - $field_type_css: The css-compatible field type.
- * - $classes_array: Array of html class attribute values. It is flattened
- *   into a string within the variable $classes.
  *
  * @see template_preprocess_field()
  * @see theme_field()
@@ -48,7 +45,7 @@ See http://api.drupal.org/api/function/theme_field/8 for details.
 After copying this file to your theme's folder and customizing it, remove this
 HTML comment.
 -->
-<div class="<?php print $classes; ?>"<?php print $attributes; ?>>
+<div class="<?php print $attributes['class']; ?>"<?php print $attributes; ?>>
   <?php if (!$label_hidden): ?>
     <div class="field-label"<?php print $title_attributes; ?>><?php print $label ?>:&nbsp;</div>
   <?php endif; ?>
diff --git a/core/modules/forum/forum.module b/core/modules/forum/forum.module
index ca9bf7e..b19e797 100644
--- a/core/modules/forum/forum.module
+++ b/core/modules/forum/forum.module
@@ -1029,7 +1029,7 @@ function forum_get_topics($tid, $sortby, $forum_per_page) {
  */
 function forum_preprocess_block(&$variables) {
   if ($variables['block']->module == 'forum') {
-    $variables['attributes_array']['role'] = 'navigation';
+    $variables['attributes']['role'] = 'navigation';
   }
 }
 
diff --git a/core/modules/help/help.module b/core/modules/help/help.module
index 92575d6..a74bf52 100644
--- a/core/modules/help/help.module
+++ b/core/modules/help/help.module
@@ -72,6 +72,6 @@ function help_help($path, $arg) {
  */
 function help_preprocess_block(&$variables) {
   if ($variables['block']->module == 'system' && $variables['block']->delta == 'help') {
-    $variables['attributes_array']['role'] = 'complementary';
+    $variables['attributes']['role'] = 'complementary';
   }
 }
diff --git a/core/modules/language/language.module b/core/modules/language/language.module
index 46ba812..f884778 100644
--- a/core/modules/language/language.module
+++ b/core/modules/language/language.module
@@ -498,7 +498,7 @@ function language_block_view($type) {
  */
 function language_preprocess_block(&$variables) {
   if ($variables['block']->module == 'language') {
-    $variables['attributes_array']['role'] = 'navigation';
+    $variables['attributes']['role'] = 'navigation';
   }
 }
 
diff --git a/core/modules/locale/locale.module b/core/modules/locale/locale.module
index ce20b59..cef0b57 100644
--- a/core/modules/locale/locale.module
+++ b/core/modules/locale/locale.module
@@ -579,12 +579,12 @@ function locale_preprocess_node(&$variables) {
       // If the node language was different from the page language, we should
       // add markup to identify the language. Otherwise the page language is
       // inherited.
-      $variables['attributes_array']['lang'] = $variables['langcode'];
+      $variables['attributes']['lang'] = $variables['langcode'];
       if ($node_language->direction != $language_interface->direction) {
         // If text direction is different form the page's text direction, add
         // direction information as well.
         $dir = array('ltr', 'rtl');
-        $variables['attributes_array']['dir'] = $dir[$node_language->direction];
+        $variables['attributes']['dir'] = $dir[$node_language->direction];
       }
     }
   }
diff --git a/core/modules/menu/menu.module b/core/modules/menu/menu.module
index 9015f67..6dc64f9 100644
--- a/core/modules/menu/menu.module
+++ b/core/modules/menu/menu.module
@@ -801,6 +801,6 @@ function menu_get_menus($all = TRUE) {
  */
 function menu_preprocess_block(&$variables) {
   if ($variables['block']->module == 'menu') {
-    $variables['attributes_array']['role'] = 'navigation';
+    $variables['attributes']['role'] = 'navigation';
   }
 }
diff --git a/core/modules/node/node.module b/core/modules/node/node.module
index bb00780..0a164b4 100644
--- a/core/modules/node/node.module
+++ b/core/modules/node/node.module
@@ -1337,10 +1337,10 @@ function node_preprocess_block(&$variables) {
   if ($variables['block']->module == 'node') {
     switch ($variables['block']->delta) {
       case 'syndicate':
-        $variables['attributes_array']['role'] = 'complementary';
+        $variables['attributes']['role'] = 'complementary';
         break;
       case 'recent':
-        $variables['attributes_array']['role'] = 'navigation';
+        $variables['attributes']['role'] = 'navigation';
         break;
     }
   }
@@ -1404,24 +1404,24 @@ function template_preprocess_node(&$variables) {
   }
 
   // Add article ARIA role.
-  $variables['attributes_array']['role'] = 'article';
+  $variables['attributes']['role'] = 'article';
 
   // Gather node classes.
-  $variables['classes_array'][] = drupal_html_class('node-' . $node->type);
+  $variables['attributes']['class'][] = drupal_html_class('node-' . $node->type);
   if ($variables['promote']) {
-    $variables['classes_array'][] = 'promoted';
+    $variables['attributes']['class'][] = 'promoted';
   }
   if ($variables['sticky']) {
-    $variables['classes_array'][] = 'sticky';
+    $variables['attributes']['class'][] = 'sticky';
   }
   if (!$variables['status']) {
-    $variables['classes_array'][] = 'unpublished';
+    $variables['attributes']['class'][] = 'unpublished';
   }
   if ($variables['view_mode']) {
-    $variables['classes_array'][] = drupal_html_class('view-mode-' . $variables['view_mode']);
+    $variables['attributes']['class'][] = drupal_html_class('view-mode-' . $variables['view_mode']);
   }
   if (isset($variables['preview'])) {
-    $variables['classes_array'][] = 'preview';
+    $variables['attributes']['class'][] = 'preview';
   }
 
   // Clean up name so there are no underscores.
diff --git a/core/modules/node/node.tpl.php b/core/modules/node/node.tpl.php
index c3db5ae..1d90054 100644
--- a/core/modules/node/node.tpl.php
+++ b/core/modules/node/node.tpl.php
@@ -18,10 +18,9 @@
  * - $display_submitted: Whether submission information should be displayed.
  * - $submitted: Submission information created from $name and $date during
  *   template_preprocess_node().
- * - $classes: String of classes that can be used to style contextually through
- *   CSS. It can be manipulated through the variable $classes_array from
- *   preprocess functions. The default values can be one or more of the
- *   following:
+ * - $attributes: An instance of Attributes class that can be manipulated as an
+ *    array and printed as a string.
+ *    It includes the 'class' information, which includes:
  *   - node: The current template type, i.e., "theming hook".
  *   - node-[type]: The current node type. For example, if the node is a
  *     "Article" it would result in "node-article". Note that the machine
@@ -46,8 +45,6 @@
  * - $comment_count: Number of comments attached to the node.
  * - $uid: User ID of the node author.
  * - $created: Time the node was published formatted in Unix timestamp.
- * - $classes_array: Array of html class attribute values. It is flattened
- *   into a string within the variable $classes.
  * - $zebra: Outputs either "even" or "odd". Useful for zebra striping in
  *   teaser listings.
  * - $id: Position of the node. Increments each time it's output.
@@ -78,7 +75,7 @@
  * @see template_process()
  */
 ?>
-<article id="node-<?php print $node->nid; ?>" class="<?php print $classes; ?> clearfix"<?php print $attributes; ?>>
+<article id="node-<?php print $node->nid; ?>" class="<?php print $attributes['class']; ?> clearfix"<?php print $attributes; ?>>
 
   <?php print render($title_prefix); ?>
   <?php if (!$page): ?>
diff --git a/core/modules/overlay/overlay.module b/core/modules/overlay/overlay.module
index e57ff5f..3a205f3 100644
--- a/core/modules/overlay/overlay.module
+++ b/core/modules/overlay/overlay.module
@@ -481,7 +481,7 @@ function overlay_system_info_alter(&$info, $file, $type) {
 function overlay_preprocess_html(&$variables) {
   if (overlay_get_mode() == 'child') {
     // Add overlay class, so themes can react to being displayed in the overlay.
-    $variables['classes_array'][] = 'overlay';
+    $variables['attributes']['class'][] = 'overlay';
   }
 }
 
@@ -504,7 +504,7 @@ function template_preprocess_overlay(&$variables) {
   $variables['tabs'] = menu_primary_local_tasks();
   $variables['title'] = drupal_get_title();
   $variables['disable_overlay'] = overlay_disable_message();
-  $variables['content_attributes_array']['class'][] = 'clearfix';
+  $variables['content_attributes']['class'][] = 'clearfix';
 }
 
 /**
diff --git a/core/modules/overlay/overlay.tpl.php b/core/modules/overlay/overlay.tpl.php
index 54b10af..00fa36c 100644
--- a/core/modules/overlay/overlay.tpl.php
+++ b/core/modules/overlay/overlay.tpl.php
@@ -10,10 +10,6 @@
  * - $tabs (array): Tabs linking to any sub-pages beneath the current page
  *   (e.g., the view and edit tabs when displaying a node).
  *
- * Helper variables:
- * - $classes_array: Array of html class attribute values. It is flattened
- *   into a string within the variable $classes.
- *
  * @see template_preprocess()
  * @see template_preprocess_overlay()
  * @see template_process()
diff --git a/core/modules/poll/poll.module b/core/modules/poll/poll.module
index db43e19..259ff14 100644
--- a/core/modules/poll/poll.module
+++ b/core/modules/poll/poll.module
@@ -777,7 +777,7 @@ function poll_vote($form, &$form_state) {
  */
 function poll_preprocess_block(&$variables) {
   if ($variables['block']->module == 'poll') {
-    $variables['attributes_array']['role'] = 'complementary';
+    $variables['attributes']['role'] = 'complementary';
   }
 }
 
diff --git a/core/modules/rdf/rdf.module b/core/modules/rdf/rdf.module
index 43ee4ed..5a2ba4f 100644
--- a/core/modules/rdf/rdf.module
+++ b/core/modules/rdf/rdf.module
@@ -438,8 +438,8 @@ function rdf_process(&$variables, $hook) {
   // and field items. It does this by adjusting the variable sent to the
   // template so that the template doesn't have to worry about it. See
   // theme_rdf_template_variable_wrapper().
-  if (!empty($variables['rdf_template_variable_attributes_array'])) {
-    foreach ($variables['rdf_template_variable_attributes_array'] as $variable_name => $attributes) {
+  if (!empty($variables['rdf_template_variable_attributes'])) {
+    foreach ($variables['rdf_template_variable_attributes'] as $variable_name => $attributes) {
       $context = array(
         'hook' => $hook,
         'variable_name' => $variable_name,
@@ -452,11 +452,11 @@ function rdf_process(&$variables, $hook) {
   // reasons, can't be placed into that template's $attributes variable. This
   // is "meta" information that is related to particular content, so render it
   // close to that content.
-  if (!empty($variables['rdf_metadata_attributes_array'])) {
+  if (!empty($variables['rdf_metadata_attributes'])) {
     if (!isset($variables['content']['#prefix'])) {
       $variables['content']['#prefix'] = '';
     }
-    $variables['content']['#prefix'] = theme('rdf_metadata', array('metadata' => $variables['rdf_metadata_attributes_array'])) . $variables['content']['#prefix'];
+    $variables['content']['#prefix'] = theme('rdf_metadata', array('metadata' => $variables['rdf_metadata_attributes'])) . $variables['content']['#prefix'];
   }
 }
 
@@ -467,8 +467,11 @@ function rdf_preprocess_html(&$variables) {
   // Adds RDF namespace prefix bindings in the form of an RDFa 1.1 prefix
   // attribute inside the html element.
   $prefixes = array();
+  if (!isset($variables['html_attributes']['prefix'])) {
+    $variables['html_attributes']['prefix'] = array();
+  }
   foreach(rdf_get_namespaces() as $prefix => $uri) {
-    $variables['html_attributes_array']['prefix'][] = $prefix . ': ' . $uri . "\n";
+    $variables['html_attributes']['prefix'][] = $prefix . ': ' . $uri . "\n";
   }
 }
 
@@ -480,15 +483,15 @@ function rdf_preprocess_node(&$variables) {
   // URI of the resource described within the HTML element, while the @typeof
   // attribute indicates its RDF type (e.g., foaf:Document, sioc:Person, and so
   // on.)
-  $variables['attributes_array']['about'] = empty($variables['node_url']) ? NULL: $variables['node_url'];
-  $variables['attributes_array']['typeof'] = empty($variables['node']->rdf_mapping['rdftype']) ? NULL : $variables['node']->rdf_mapping['rdftype'];
+  $variables['attributes']['about'] = empty($variables['node_url']) ? NULL: $variables['node_url'];
+  $variables['attributes']['typeof'] = empty($variables['node']->rdf_mapping['rdftype']) ? NULL : $variables['node']->rdf_mapping['rdftype'];
 
   // Adds RDFa markup to the title of the node. Because the RDFa markup is
   // added to the <h2> tag which might contain HTML code, we specify an empty
   // datatype to ensure the value of the title read by the RDFa parsers is a
   // literal.
-  $variables['title_attributes_array']['property'] = empty($variables['node']->rdf_mapping['title']['predicates']) ? NULL : $variables['node']->rdf_mapping['title']['predicates'];
-  $variables['title_attributes_array']['datatype'] = '';
+  $variables['title_attributes']['property'] = empty($variables['node']->rdf_mapping['title']['predicates']) ? NULL : $variables['node']->rdf_mapping['title']['predicates'];
+  $variables['title_attributes']['datatype'] = '';
 
   // In full node mode, the title is not displayed by node.tpl.php so it is
   // added in the <head> tag of the HTML page.
@@ -508,17 +511,17 @@ function rdf_preprocess_node(&$variables) {
 
   // Adds RDFa markup for the date.
   if (!empty($variables['rdf_mapping']['created'])) {
-    $date_attributes_array = rdf_rdfa_attributes($variables['rdf_mapping']['created'], $variables['created']);
-    $variables['rdf_template_variable_attributes_array']['date'] = $date_attributes_array;
+    $date_attributes = rdf_rdfa_attributes($variables['rdf_mapping']['created'], $variables['created']);
+    $variables['rdf_template_variable_attributes']['date'] = $date_attributes;
     if ($variables['submitted']) {
-      $variables['rdf_template_variable_attributes_array']['submitted'] = $date_attributes_array;
+      $variables['rdf_template_variable_attributes']['submitted'] = $date_attributes;
     }
   }
   // Adds RDFa markup for the relation between the node and its author.
   if (!empty($variables['rdf_mapping']['uid'])) {
-    $variables['rdf_template_variable_attributes_array']['name']['rel'] = $variables['rdf_mapping']['uid']['predicates'];
+    $variables['rdf_template_variable_attributes']['name']['rel'] = $variables['rdf_mapping']['uid']['predicates'];
     if ($variables['submitted']) {
-      $variables['rdf_template_variable_attributes_array']['submitted']['rel'] = $variables['rdf_mapping']['uid']['predicates'];
+      $variables['rdf_template_variable_attributes']['submitted']['rel'] = $variables['rdf_mapping']['uid']['predicates'];
     }
   }
 
@@ -565,7 +568,7 @@ function rdf_preprocess_field(&$variables) {
 
   if (!empty($mapping) && !empty($mapping[$field_name])) {
     foreach ($element['#items'] as $delta => $item) {
-      $variables['item_attributes_array'][$delta] = rdf_rdfa_attributes($mapping[$field_name], $item);
+      $variables['item_attributes'][$delta] = rdf_rdfa_attributes($mapping[$field_name], $item);
       // If this field is an image, RDFa will not output correctly when the
       // image is in a containing <a> tag. If the field is a file, RDFa will
       // not output correctly if the filetype icon comes before the link to the
@@ -573,10 +576,10 @@ function rdf_preprocess_field(&$variables) {
       // this field has a URI.
       if (isset($item['uri'])) {
         if (!empty($element[$delta]['#image_style'])) {
-          $variables['item_attributes_array'][$delta]['resource'] = image_style_url($element[$delta]['#image_style'], $item['uri']);
+          $variables['item_attributes'][$delta]['resource'] = image_style_url($element[$delta]['#image_style'], $item['uri']);
         }
         else {
-          $variables['item_attributes_array'][$delta]['resource'] = file_create_url($item['uri']);
+          $variables['item_attributes'][$delta]['resource'] = file_create_url($item['uri']);
         }
       }
     }
@@ -593,8 +596,8 @@ function rdf_preprocess_user_profile(&$variables) {
   // Adds RDFa markup to the user profile page. Fields displayed in this page
   // will automatically describe the user.
   if (!empty($account->rdf_mapping['rdftype'])) {
-    $variables['attributes_array']['typeof'] = $account->rdf_mapping['rdftype'];
-    $variables['attributes_array']['about'] = url($uri['path'], $uri['options']);
+    $variables['attributes']['typeof'] = $account->rdf_mapping['rdftype'];
+    $variables['attributes']['about'] = url($uri['path'], $uri['options']);
   }
   // Adds the relationship between the sioc:UserAccount and the foaf:Person who
   // holds the account.
@@ -629,8 +632,8 @@ function rdf_preprocess_username(&$variables) {
   // might not be transliterated to the same language that the content is in,
   // we do not want it to inherit the language attribute, so we set the
   // attribute to an empty string.
-  if (empty($variables['attributes_array']['lang'])) {
-    $variables['attributes_array']['lang'] = '';
+  if (empty($variables['attributes']['lang'])) {
+    $variables['attributes']['lang'] = '';
   }
 
   // $variables['account'] is a pseudo account object, and as such, does not
@@ -649,7 +652,7 @@ function rdf_preprocess_username(&$variables) {
   // a user profile URI for it (only a homepage which cannot be used as user
   // profile in RDF.)
   if ($variables['uid'] > 0) {
-    $variables['attributes_array']['about'] = url('user/' . $variables['uid']);
+    $variables['attributes']['about'] = url('user/' . $variables['uid']);
   }
 
   $attributes = array();
@@ -672,7 +675,7 @@ function rdf_preprocess_username(&$variables) {
   // (see http://www.w3.org/TR/rdfa-syntax/#rdfa-attributes).
   // Therefore, merge rather than override so as not to clobber values set by
   // earlier preprocess functions.
-  $variables['attributes_array'] = array_merge_recursive($variables['attributes_array'], $attributes);
+  $variables['attributes'] = array_merge_recursive($variables['attributes'], $attributes);
 }
 
 /**
@@ -686,30 +689,30 @@ function rdf_preprocess_comment(&$variables) {
     // typeof attribute indicates its RDF type (e.g., sioc:Post, foaf:Document,
     // and so on.)
     $uri = entity_uri('comment', $comment);
-    $variables['attributes_array']['about'] = url($uri['path'], $uri['options']);
-    $variables['attributes_array']['typeof'] = $comment->rdf_mapping['rdftype'];
+    $variables['attributes']['about'] = url($uri['path'], $uri['options']);
+    $variables['attributes']['typeof'] = $comment->rdf_mapping['rdftype'];
   }
 
   // Adds RDFa markup for the date of the comment.
   if (!empty($comment->rdf_mapping['created'])) {
     // The comment date is precomputed as part of the rdf_data so that it can be
     // cached as part of the entity.
-    $date_attributes_array = $comment->rdf_data['date'];
-    $variables['rdf_template_variable_attributes_array']['created'] = $date_attributes_array;
-    $variables['rdf_template_variable_attributes_array']['submitted'] = $date_attributes_array;
+    $date_attributes = $comment->rdf_data['date'];
+    $variables['rdf_template_variable_attributes']['created'] = $date_attributes;
+    $variables['rdf_template_variable_attributes']['submitted'] = $date_attributes;
   }
   // Adds RDFa markup for the relation between the comment and its author.
   if (!empty($comment->rdf_mapping['uid'])) {
-    $variables['rdf_template_variable_attributes_array']['author']['rel'] = $comment->rdf_mapping['uid']['predicates'];
-    $variables['rdf_template_variable_attributes_array']['submitted']['rel'] = $comment->rdf_mapping['uid']['predicates'];
+    $variables['rdf_template_variable_attributes']['author']['rel'] = $comment->rdf_mapping['uid']['predicates'];
+    $variables['rdf_template_variable_attributes']['submitted']['rel'] = $comment->rdf_mapping['uid']['predicates'];
   }
   if (!empty($comment->rdf_mapping['title'])) {
     // Adds RDFa markup to the subject of the comment. Because the RDFa markup
     // is added to an <h3> tag which might contain HTML code, we specify an
     // empty datatype to ensure the value of the title read by the RDFa parsers
     // is a literal.
-    $variables['title_attributes_array']['property'] = $comment->rdf_mapping['title']['predicates'];
-    $variables['title_attributes_array']['datatype'] = '';
+    $variables['title_attributes']['property'] = $comment->rdf_mapping['title']['predicates'];
+    $variables['title_attributes']['datatype'] = '';
   }
 
   // Annotates the parent relationship between the current comment and the node
@@ -720,7 +723,7 @@ function rdf_preprocess_comment(&$variables) {
     // The parent node URI is precomputed as part of the rdf_data so that it can
     // be cached as part of the entity.
     $parent_node_attributes['resource'] = $comment->rdf_data['nid_uri'];
-    $variables['rdf_metadata_attributes_array'][] = $parent_node_attributes;
+    $variables['rdf_metadata_attributes'][] = $parent_node_attributes;
 
     // Adds the relation to parent comment, if it exists.
     if ($comment->pid != 0) {
@@ -728,7 +731,7 @@ function rdf_preprocess_comment(&$variables) {
       // The parent comment URI is precomputed as part of the rdf_data so that
       // it can be cached as part of the entity.
       $parent_comment_attributes['resource'] = $comment->rdf_data['pid_uri'];
-      $variables['rdf_metadata_attributes_array'][] = $parent_comment_attributes;
+      $variables['rdf_metadata_attributes'][] = $parent_comment_attributes;
     }
   }
 }
@@ -805,7 +808,7 @@ function rdf_preprocess_image(&$variables) {
  * an extra wrapper element, you can override this function to not wrap that
  * variable and instead print the following inside your template file:
  * @code
- *   drupal_attributes($rdf_template_variable_attributes_array[$variable_name])
+ *   drupal_attributes($rdf_template_variable_attributes[$variable_name])
  * @endcode
  *
  * @param $variables
@@ -828,7 +831,7 @@ function rdf_preprocess_image(&$variables) {
  *     therefore can be validly wrapped by a <span> tag. FALSE if the content
  *     might contain block level HTML elements and therefore cannot be validly
  *     wrapped by a <span> tag. Modules implementing preprocess functions that
- *     set 'rdf_template_variable_attributes_array' for a particular template
+ *     set 'rdf_template_variable_attributes' for a particular template
  *     variable that might contain block level HTML must also implement
  *     hook_preprocess_HOOK() for theme_rdf_template_variable_wrapper() and set
  *     'inline' to FALSE for that context. Themes that render normally inline
diff --git a/core/modules/search/search-result.tpl.php b/core/modules/search/search-result.tpl.php
index 949452a..cd44d1e 100644
--- a/core/modules/search/search-result.tpl.php
+++ b/core/modules/search/search-result.tpl.php
@@ -33,11 +33,9 @@
  *   being the count. Depends on comment.module.
  *
  * Other variables:
- * - $classes_array: Array of HTML class attribute values. It is flattened
- *   into a string within the variable $classes.
- * - $title_attributes_array: Array of HTML attributes for the title. It is
+ * - $title_attributes: Array of HTML attributes for the title. It is
  *   flattened into a string within the variable $title_attributes.
- * - $content_attributes_array: Array of HTML attributes for the content. It is
+ * - $content_attributes: Array of HTML attributes for the content. It is
  *   flattened into a string within the variable $content_attributes.
  *
  * Since $info_split is keyed, a direct print of the item is possible.
@@ -62,7 +60,7 @@
  * @see template_process()
  */
 ?>
-<li class="<?php print $classes; ?>"<?php print $attributes; ?>>
+<li <?php print $attributes; ?>>
   <?php print render($title_prefix); ?>
   <h3 class="title"<?php print $title_attributes; ?>>
     <a href="<?php print $url; ?>"><?php print $title; ?></a>
diff --git a/core/modules/search/search.module b/core/modules/search/search.module
index 06c4384..88eaf45 100644
--- a/core/modules/search/search.module
+++ b/core/modules/search/search.module
@@ -160,8 +160,8 @@ function search_block_view($delta = '') {
  */
 function search_preprocess_block(&$variables) {
   if ($variables['block']->module == 'search' && $variables['block']->delta == 'form') {
-    $variables['attributes_array']['role'] = 'search';
-    $variables['content_attributes_array']['class'][] = 'container-inline';
+    $variables['attributes']['role'] = 'search';
+    $variables['content_attributes']['class'][] = 'container-inline';
   }
 }
 
diff --git a/core/modules/search/search.pages.inc b/core/modules/search/search.pages.inc
index c940223..2c93b67 100644
--- a/core/modules/search/search.pages.inc
+++ b/core/modules/search/search.pages.inc
@@ -110,8 +110,8 @@ function template_preprocess_search_result(&$variables) {
   $variables['url'] = check_url($result['link']);
   $variables['title'] = check_plain($result['title']);
   if (isset($result['language']) && $result['language'] != $language_interface->langcode && $result['language'] != LANGUAGE_NOT_SPECIFIED) {
-    $variables['title_attributes_array']['lang'] = $result['language'];
-    $variables['content_attributes_array']['lang'] = $result['language'];
+    $variables['title_attributes']['lang'] = $result['language'];
+    $variables['content_attributes']['lang'] = $result['language'];
   }
 
   $info = array();
diff --git a/core/modules/shortcut/shortcut.module b/core/modules/shortcut/shortcut.module
index be29dce..27eb8c2 100644
--- a/core/modules/shortcut/shortcut.module
+++ b/core/modules/shortcut/shortcut.module
@@ -644,7 +644,7 @@ function shortcut_renderable_links($shortcut_set = NULL) {
  */
 function shortcut_preprocess_block(&$variables) {
   if ($variables['block']->module == 'shortcut') {
-    $variables['attributes_array']['role'] = 'navigation';
+    $variables['attributes']['role'] = 'navigation';
   }
 }
 
diff --git a/core/modules/statistics/statistics.module b/core/modules/statistics/statistics.module
index ef1bceb..c960a91 100644
--- a/core/modules/statistics/statistics.module
+++ b/core/modules/statistics/statistics.module
@@ -453,6 +453,6 @@ function statistics_update_index() {
  */
 function statistics_preprocess_block(&$variables) {
   if ($variables['block']->module == 'statistics') {
-    $variables['attributes_array']['role'] = 'navigation';
+    $variables['attributes']['role'] = 'navigation';
   }
 }
diff --git a/core/modules/system/html.tpl.php b/core/modules/system/html.tpl.php
index 960eefb..0b3bbad 100644
--- a/core/modules/system/html.tpl.php
+++ b/core/modules/system/html.tpl.php
@@ -47,7 +47,7 @@
     <?php print $styles; ?>
     <?php print $scripts; ?>
   </head>
-  <body class="<?php print $classes; ?>" <?php print $body_attributes;?>>
+  <body class="<?php print $attributes['class']; ?>" <?php print $attributes;?>>
     <div id="skip-link">
       <a href="#main-content" class="element-invisible element-focusable"><?php print t('Skip to main content'); ?></a>
     </div>
diff --git a/core/modules/system/lib/Drupal/system/Tests/Common/AttributesUnitTest.php b/core/modules/system/lib/Drupal/system/Tests/Common/AttributesUnitTest.php
index 2204d3a..3031625 100644
--- a/core/modules/system/lib/Drupal/system/Tests/Common/AttributesUnitTest.php
+++ b/core/modules/system/lib/Drupal/system/Tests/Common/AttributesUnitTest.php
@@ -26,15 +26,15 @@ class AttributesUnitTest extends UnitTestBase {
    */
   function testDrupalAttributes() {
     // Verify that special characters are HTML encoded.
-    $this->assertIdentical(drupal_attributes(array('title' => '&"\'<>')), ' title="&amp;&quot;&#039;&lt;&gt;"', t('HTML encode attribute values.'));
+    $this->assertIdentical((string) drupal_attributes(array('title' => '&"\'<>')), ' title="&amp;&quot;&#039;&lt;&gt;"', t('HTML encode attribute values.'));
 
     // Verify multi-value attributes are concatenated with spaces.
     $attributes = array('class' => array('first', 'last'));
-    $this->assertIdentical(drupal_attributes(array('class' => array('first', 'last'))), ' class="first last"', t('Concatenate multi-value attributes.'));
+    $this->assertIdentical((string) drupal_attributes(array('class' => array('first', 'last'))), ' class="first last"', t('Concatenate multi-value attributes.'));
 
     // Verify empty attribute values are rendered.
-    $this->assertIdentical(drupal_attributes(array('alt' => '')), ' alt=""', t('Empty attribute value #1.'));
-    $this->assertIdentical(drupal_attributes(array('alt' => NULL)), ' alt=""', t('Empty attribute value #2.'));
+    $this->assertIdentical((string) drupal_attributes(array('alt' => '')), ' alt=""', t('Empty attribute value #1.'));
+    $this->assertIdentical((string) drupal_attributes(array('alt' => NULL)), ' alt=""', t('Empty attribute value #2.'));
 
     // Verify multiple attributes are rendered.
     $attributes = array(
@@ -42,9 +42,9 @@ class AttributesUnitTest extends UnitTestBase {
       'class' => array('first', 'last'),
       'alt' => 'Alternate',
     );
-    $this->assertIdentical(drupal_attributes($attributes), ' id="id-test" class="first last" alt="Alternate"', t('Multiple attributes.'));
+    $this->assertIdentical((string) drupal_attributes($attributes), ' id="id-test" class="first last" alt="Alternate"', t('Multiple attributes.'));
 
     // Verify empty attributes array is rendered.
-    $this->assertIdentical(drupal_attributes(array()), '', t('Empty attributes array.'));
+    $this->assertIdentical((string) drupal_attributes(array()), '', t('Empty attributes array.'));
   }
 }
diff --git a/core/modules/system/maintenance-page.tpl.php b/core/modules/system/maintenance-page.tpl.php
index 5cde0ff..6730bb9 100644
--- a/core/modules/system/maintenance-page.tpl.php
+++ b/core/modules/system/maintenance-page.tpl.php
@@ -21,7 +21,7 @@
   <?php print $styles; ?>
   <?php print $scripts; ?>
 </head>
-<body class="<?php print $classes; ?>">
+<body class="<?php print $attributes['class']; ?>">
   <div id="page">
     <div id="header">
       <div id="logo-title">
diff --git a/core/modules/system/region.tpl.php b/core/modules/system/region.tpl.php
index b29e24f..4db7fe9 100644
--- a/core/modules/system/region.tpl.php
+++ b/core/modules/system/region.tpl.php
@@ -6,17 +6,15 @@
  *
  * Available variables:
  * - $content: The content for this region, typically blocks.
- * - $classes: String of classes that can be used to style contextually through
- *   CSS. It can be manipulated through the variable $classes_array from
- *   preprocess functions. The default values can be one or more of the following:
+ * - $attributes: An instance of Attributes class that can be manipulated as an
+ *    array and printed as a string.
+ *    It includes the 'class' information, which includes:
  *   - region: The current template type, i.e., "theming hook".
  *   - region-[name]: The name of the region with underscores replaced with
  *     dashes. For example, the page_top region would have a region-page-top class.
  * - $region: The name of the region variable as defined in the theme's .info file.
  *
  * Helper variables:
- * - $classes_array: Array of html class attribute values. It is flattened
- *   into a string within the variable $classes.
  * - $is_admin: Flags true when the current user is an administrator.
  * - $is_front: Flags true when presented in the front page.
  * - $logged_in: Flags true when the current user is a logged-in member.
@@ -27,7 +25,7 @@
  */
 ?>
 <?php if ($content): ?>
-  <div class="<?php print $classes; ?>">
+  <div <?php print $attributes; ?>>
     <?php print $content; ?>
   </div>
 <?php endif; ?>
diff --git a/core/modules/system/system.module b/core/modules/system/system.module
index 48a25ec..1b89c69 100644
--- a/core/modules/system/system.module
+++ b/core/modules/system/system.module
@@ -2352,17 +2352,17 @@ function system_preprocess_block(&$variables) {
 
     switch ($variables['block']->delta) {
       case 'powered-by':
-        $variables['attributes_array']['role'] = 'complementary';
+        $variables['attributes']['role'] = 'complementary';
         break;
       case 'help':
-        $variables['attributes_array']['role'] = 'complementary';
+        $variables['attributes']['role'] = 'complementary';
         break;
 
       // System menu blocks should get the same class as menu module blocks.
       default:
         if (in_array($variables['block']->delta, array_keys(menu_list_system_menus()))) {
-          $variables['attributes_array']['role'] = 'navigation';
-          $variables['classes_array'][] = 'block-menu';
+          $variables['attributes']['role'] = 'navigation';
+          $variables['attributes']['class'][] = 'block-menu';
         }
     }
   }
diff --git a/core/modules/system/tests/modules/theme_test/theme_test.module b/core/modules/system/tests/modules/theme_test/theme_test.module
index 4ed90d2..59ff629 100644
--- a/core/modules/system/tests/modules/theme_test/theme_test.module
+++ b/core/modules/system/tests/modules/theme_test/theme_test.module
@@ -142,8 +142,8 @@ function _theme_test_suggestion() {
  * Implements hook_preprocess_HOOK() for html.tpl.php.
  */
 function theme_test_preprocess_html(&$variables) {
-  $variables['html_attributes_array']['theme_test_html_attribute'] = 'theme test html attribute value';
-  $variables['body_attributes_array']['theme_test_body_attribute'] = 'theme test body attribute value';
+  $variables['html_attributes']['theme_test_html_attribute'] = 'theme test html attribute value';
+  $variables['attributes']['theme_test_body_attribute'] = 'theme test body attribute value';
 }
 
 /**
diff --git a/core/modules/system/theme.api.php b/core/modules/system/theme.api.php
index cd7ecfd..b98132e 100644
--- a/core/modules/system/theme.api.php
+++ b/core/modules/system/theme.api.php
@@ -137,7 +137,7 @@ function hook_preprocess(&$variables, $hook) {
   if (isset($element) && is_array($element) && !empty($element['#contextual_links'])) {
     $variables['title_suffix']['contextual_links'] = contextual_links_view($element);
     if (!empty($variables['title_suffix']['contextual_links'])) {
-      $variables['classes_array'][] = 'contextual-links-region';
+      $variables['attributes']['class'][] = 'contextual-links-region';
     }
   }
 }
@@ -178,8 +178,8 @@ function hook_preprocess_HOOK(&$variables) {
  */
 function hook_process(&$variables, $hook) {
   // Wraps variables in RDF wrappers.
-  if (!empty($variables['rdf_template_variable_attributes_array'])) {
-    foreach ($variables['rdf_template_variable_attributes_array'] as $variable_name => $attributes) {
+  if (!empty($variables['rdf_template_variable_attributes'])) {
+    foreach ($variables['rdf_template_variable_attributes'] as $variable_name => $attributes) {
       $context = array(
         'hook' => $hook,
         'variable_name' => $variable_name,
diff --git a/core/modules/taxonomy/taxonomy-term.tpl.php b/core/modules/taxonomy/taxonomy-term.tpl.php
index b1ff20e..8fdaa94 100644
--- a/core/modules/taxonomy/taxonomy-term.tpl.php
+++ b/core/modules/taxonomy/taxonomy-term.tpl.php
@@ -13,9 +13,9 @@
  *   given element.
  * - $term_url: Direct url of the current term.
  * - $term_name: Name of the current term.
- * - $classes: String of classes that can be used to style contextually through
- *   CSS. It can be manipulated through the variable $classes_array from
- *   preprocess functions. The default values can be one or more of the following:
+ * - $attributes: An instance of Attributes class that can be manipulated as an
+ *    array and printed as a string.
+ *    It includes the 'class' information, which includes:
  *   - taxonomy-term: The current template type, i.e., "theming hook".
  *   - vocabulary-[vocabulary-name]: The vocabulary to which the term belongs to.
  *     For example, if the term is a "Tag" it would result in "vocabulary-tag".
@@ -24,8 +24,6 @@
  * - $term: Full term object. Contains data that may not be safe.
  * - $view_mode: View mode, e.g. 'full', 'teaser'...
  * - $page: Flag for the full page state.
- * - $classes_array: Array of html class attribute values. It is flattened
- *   into a string within the variable $classes.
  * - $zebra: Outputs either "even" or "odd". Useful for zebra striping in
  *   teaser listings.
  * - $id: Position of the term. Increments each time it's output.
@@ -38,7 +36,7 @@
  * @see template_process()
  */
 ?>
-<div id="taxonomy-term-<?php print $term->tid; ?>" class="<?php print $classes; ?>">
+<div id="taxonomy-term-<?php print $term->tid; ?>"<?php print $attributes; ?>>
 
   <?php if (!$page): ?>
     <h2><a href="<?php print $term_url; ?>"><?php print $term_name; ?></a></h2>
diff --git a/core/modules/taxonomy/taxonomy.module b/core/modules/taxonomy/taxonomy.module
index f55ac1d..5ec9d3f 100644
--- a/core/modules/taxonomy/taxonomy.module
+++ b/core/modules/taxonomy/taxonomy.module
@@ -647,7 +647,7 @@ function template_preprocess_taxonomy_term(&$variables) {
 
   // Gather classes, and clean up name so there are no underscores.
   $vocabulary_name_css = str_replace('_', '-', $term->vocabulary_machine_name);
-  $variables['classes_array'][] = 'vocabulary-' . $vocabulary_name_css;
+  $variables['attributes']['class'][] = 'vocabulary-' . $vocabulary_name_css;
 
   $variables['theme_hook_suggestions'][] = 'taxonomy_term__' . $term->vocabulary_machine_name;
   $variables['theme_hook_suggestions'][] = 'taxonomy_term__' . $term->tid;
diff --git a/core/modules/toolbar/toolbar.module b/core/modules/toolbar/toolbar.module
index 904a2f8..70f12bf 100644
--- a/core/modules/toolbar/toolbar.module
+++ b/core/modules/toolbar/toolbar.module
@@ -146,9 +146,9 @@ function toolbar_pre_render($toolbar) {
  */
 function toolbar_preprocess_html(&$vars) {
   if (isset($vars['page']['page_top']['toolbar']) && user_access('access toolbar')) {
-    $vars['classes_array'][] = 'toolbar';
+    $vars['attributes']['class'][] = 'toolbar';
     if (!_toolbar_is_collapsed()) {
-      $vars['classes_array'][] = 'toolbar-drawer';
+      $vars['attributes']['class'][] = 'toolbar-drawer';
     }
   }
 }
@@ -160,7 +160,7 @@ function toolbar_preprocess_html(&$vars) {
  * down, so it appears below the toolbar.
  */
 function toolbar_preprocess_toolbar(&$variables) {
-  $variables['classes_array'][] = "overlay-displace-top";
+  $variables['attributes']['class'][] = "overlay-displace-top";
 }
 
 /**
diff --git a/core/modules/toolbar/toolbar.tpl.php b/core/modules/toolbar/toolbar.tpl.php
index 6f303b1..c085b40 100644
--- a/core/modules/toolbar/toolbar.tpl.php
+++ b/core/modules/toolbar/toolbar.tpl.php
@@ -5,23 +5,19 @@
  * Default template for admin toolbar.
  *
  * Available variables:
- * - $classes: String of classes that can be used to style contextually through
- *   CSS. It can be manipulated through the variable $classes_array from
- *   preprocess functions. The default value has the following:
+ * - $attributes: An instance of Attributes class that can be manipulated as an
+ *    array and printed as a string.
+ *    It includes the 'class' information, which includes:
  *   - toolbar: The current template type, i.e., "theming hook".
  * - $toolbar['toolbar_user']: User account / logout links.
  * - $toolbar['toolbar_menu']: Top level management menu links.
  * - $toolbar['toolbar_drawer']: A place for extended toolbar content.
  *
- * Other variables:
- * - $classes_array: Array of html class attribute values. It is flattened
- *   into a string within the variable $classes.
- *
  * @see template_preprocess()
  * @see template_preprocess_toolbar()
  */
 ?>
-<nav id="toolbar" role="navigation" class="<?php print $classes; ?> clearfix">
+<nav id="toolbar" role="navigation" class="<?php print $attributes['class']; ?> clearfix" <?php print $attributes; ?>>
   <div class="toolbar-menu clearfix">
     <?php print render($toolbar['toolbar_home']); ?>
     <?php print render($toolbar['toolbar_user']); ?>
diff --git a/core/modules/user/user.module b/core/modules/user/user.module
index 2250110..9c7fde4 100644
--- a/core/modules/user/user.module
+++ b/core/modules/user/user.module
@@ -1190,13 +1190,13 @@ function user_preprocess_block(&$variables) {
   if ($variables['block']->module == 'user') {
     switch ($variables['block']->delta) {
       case 'login':
-        $variables['attributes_array']['role'] = 'form';
+        $variables['attributes']['role'] = 'form';
         break;
       case 'new':
-        $variables['attributes_array']['role'] = 'complementary';
+        $variables['attributes']['role'] = 'complementary';
         break;
       case 'online':
-        $variables['attributes_array']['role'] = 'complementary';
+        $variables['attributes']['role'] = 'complementary';
         break;
     }
   }
@@ -1327,7 +1327,7 @@ function template_preprocess_username(&$variables) {
   // We do not want the l() function to check_plain() a second time.
   $variables['link_options']['html'] = TRUE;
   // Set a default class.
-  $variables['attributes_array'] = array('class' => array('username'));
+  $variables['attributes'] = array('class' => array('username'));
 }
 
 /**
@@ -1340,14 +1340,14 @@ function template_process_username(&$variables) {
   // This is done in the process phase so that attributes may be added by
   // modules or the theme during the preprocess phase.
   if (isset($variables['link_path'])) {
-    // $variables['attributes_array'] contains attributes that should be applied
+    // $variables['attributes'] contains attributes that should be applied
     // regardless of whether a link is being rendered or not.
     // $variables['link_attributes'] contains attributes that should only be
     // applied if a link is being rendered. Preprocess functions are encouraged
     // to use the former unless they want to add attributes on the link only.
     // If a link is being rendered, these need to be merged. Some attributes are
     // themselves arrays, so the merging needs to be recursive.
-    $variables['link_options']['attributes'] = array_merge_recursive($variables['link_attributes'], $variables['attributes_array']);
+    $variables['link_options']['attributes'] = array_merge_recursive($variables['link_attributes'], $variables['attributes']);
   }
 }
 
@@ -1363,7 +1363,7 @@ function template_process_username(&$variables) {
  *     other desired page to link to for more information about the user.
  *   - link_options: An array of options to pass to the l() function's $options
  *     parameter if linking the user's name to the user's page.
- *   - attributes_array: An array of attributes to pass to the
+ *   - attributes: An array of attributes to pass to the
  *     drupal_attributes() function if not linking to the user's page.
  *
  * @see template_preprocess_username()
@@ -1379,8 +1379,8 @@ function theme_username($variables) {
   else {
     // Modules may have added important attributes so they must be included
     // in the output. Additional classes may be added as array elements like
-    // $variables['attributes_array']['class'][] = 'myclass';
-    $output = '<span' . drupal_attributes($variables['attributes_array']) . '>' . $variables['name'] . $variables['extra'] . '</span>';
+    // $variables['attributes']['class'][] = 'myclass';
+    $output = '<span' . drupal_attributes($variables['attributes']) . '>' . $variables['name'] . $variables['extra'] . '</span>';
   }
   return $output;
 }
diff --git a/core/themes/bartik/template.php b/core/themes/bartik/template.php
index aefb861..7275dee 100644
--- a/core/themes/bartik/template.php
+++ b/core/themes/bartik/template.php
@@ -12,20 +12,20 @@
  */
 function bartik_preprocess_html(&$variables) {
   if (!empty($variables['page']['featured'])) {
-    $variables['classes_array'][] = 'featured';
+    $variables['attributes']['class'][] = 'featured';
   }
 
   if (!empty($variables['page']['triptych_first'])
     || !empty($variables['page']['triptych_middle'])
     || !empty($variables['page']['triptych_last'])) {
-    $variables['classes_array'][] = 'triptych';
+    $variables['attributes']['class'][] = 'triptych';
   }
 
   if (!empty($variables['page']['footer_firstcolumn'])
     || !empty($variables['page']['footer_secondcolumn'])
     || !empty($variables['page']['footer_thirdcolumn'])
     || !empty($variables['page']['footer_fourthcolumn'])) {
-    $variables['classes_array'][] = 'footer-columns';
+    $variables['attributes']['class'][] = 'footer-columns';
   }
 }
 
@@ -116,7 +116,7 @@ function bartik_process_maintenance_page(&$variables) {
 function bartik_preprocess_block(&$variables) {
   // In the header region visually hide block titles.
   if ($variables['block']->region == 'header') {
-    $variables['title_attributes_array']['class'][] = 'element-invisible';
+    $variables['title_attributes']['class'][] = 'element-invisible';
   }
 }
 
@@ -146,7 +146,8 @@ function bartik_field__taxonomy_term_reference($variables) {
   $output .= '</ul>';
 
   // Render the top-level DIV.
-  $output = '<div class="' . $variables['classes'] . (!in_array('clearfix', $variables['classes_array']) ? ' clearfix' : '') . '"' . $variables['attributes'] .'>' . $output . '</div>';
+  $variables['attributes']['class'][] = 'clearfix';
+  $output = '<div ' . $variables['attributes'] . '>' . $output . '</div>';
 
   return $output;
 }
diff --git a/core/themes/bartik/templates/comment-wrapper.tpl.php b/core/themes/bartik/templates/comment-wrapper.tpl.php
index dc06cf2..6c90f54 100644
--- a/core/themes/bartik/templates/comment-wrapper.tpl.php
+++ b/core/themes/bartik/templates/comment-wrapper.tpl.php
@@ -8,9 +8,9 @@
  * - $content: The array of content-related elements for the node. Use
  *   render($content) to print them all, or
  *   print a subset such as render($content['comment_form']).
- * - $classes: String of classes that can be used to style contextually through
- *   CSS. It can be manipulated through the variable $classes_array from
- *   preprocess functions. The default value has the following:
+ * - $attributes: An instance of Attributes class that can be manipulated as an
+ *    array and printed as a string.
+ *    It includes the 'class' information, which includes:
  *   - comment-wrapper: The current template type, i.e., "theming hook".
  * - $title_prefix (array): An array containing additional output populated by
  *   modules, intended to be displayed in front of the main title tag that
@@ -27,17 +27,13 @@
  *   - COMMENT_MODE_FLAT
  *   - COMMENT_MODE_THREADED
  *
- * Other variables:
- * - $classes_array: Array of html class attribute values. It is flattened
- *   into a string within the variable $classes.
- *
  * @see template_preprocess_comment_wrapper()
  * @see theme_comment_wrapper()
  *
  * @ingroup themeable
  */
 ?>
-<div id="comments" class="<?php print $classes; ?>"<?php print $attributes; ?>>
+<div id="comments" <?php print $attributes; ?>>
   <?php if ($content['comments'] && $node->type != 'forum'): ?>
     <?php print render($title_prefix); ?>
     <h2 class="title"><?php print t('Comments'); ?></h2>
diff --git a/core/themes/bartik/templates/comment.tpl.php b/core/themes/bartik/templates/comment.tpl.php
index 0b70594..04aa8e5 100644
--- a/core/themes/bartik/templates/comment.tpl.php
+++ b/core/themes/bartik/templates/comment.tpl.php
@@ -25,10 +25,9 @@
  * - $status: Comment status. Possible values are:
  *   unpublished, published, or preview.
  * - $title: Linked title.
- * - $classes: String of classes that can be used to style contextually through
- *   CSS. It can be manipulated through the variable $classes_array from
- *   preprocess functions. The default values can be one or more of the
- *   following:
+ * - $attributes: An instance of Attributes class that can be manipulated as an
+ *    array and printed as a string.
+ *    It includes the 'class' information, which includes:
  *   - comment: The current template type; e.g., 'theming hook'.
  *   - by-anonymous: Comment by an unregistered user.
  *   - by-node-author: Comment by the author of the parent node.
@@ -48,10 +47,6 @@
  * - $comment: Full comment object.
  * - $node: Node entity the comments are attached to.
  *
- * Other variables:
- * - $classes_array: Array of html class attribute values. It is flattened
- *   into a string within the variable $classes.
- *
  * @see template_preprocess()
  * @see template_preprocess_comment()
  * @see template_process()
@@ -60,7 +55,7 @@
  * @ingroup themeable
  */
 ?>
-<div class="<?php print $classes; ?> clearfix"<?php print $attributes; ?>>
+<div class="<?php print $attributes['class']; ?> clearfix"<?php print $attributes; ?>>
 
   <div class="attribution">
 
diff --git a/core/themes/bartik/templates/maintenance-page.tpl.php b/core/themes/bartik/templates/maintenance-page.tpl.php
index 9cdeff7..caf1611 100644
--- a/core/themes/bartik/templates/maintenance-page.tpl.php
+++ b/core/themes/bartik/templates/maintenance-page.tpl.php
@@ -22,7 +22,7 @@
   <?php print $styles; ?>
   <?php print $scripts; ?>
 </head>
-<body class="<?php print $classes; ?>" <?php print $attributes;?>>
+<body class="<?php print $attributes['class']; ?>" <?php print $attributes;?>>
 
   <div id="skip-link">
     <a href="#main-content" class="element-invisible element-focusable"><?php print t('Skip to main content'); ?></a>
diff --git a/core/themes/bartik/templates/node.tpl.php b/core/themes/bartik/templates/node.tpl.php
index 318197c..baf45c0 100644
--- a/core/themes/bartik/templates/node.tpl.php
+++ b/core/themes/bartik/templates/node.tpl.php
@@ -19,10 +19,9 @@
  * - $display_submitted: Whether submission information should be displayed.
  * - $submitted: Submission information created from $name and $date during
  *   template_preprocess_node().
- * - $classes: String of classes that can be used to style contextually through
- *   CSS. It can be manipulated through the variable $classes_array from
- *   preprocess functions. The default values can be one or more of the
- *   following:
+ * - $attributes: An instance of Attributes class that can be manipulated as an
+ *    array and printed as a string.
+ *    It includes the 'class' information, which includes:
  *   - node: The current template type, i.e., "theming hook".
  *   - node-[type]: The current node type. For example, if the node is a
  *     "Article" it would result in "node-article". Note that the machine
@@ -47,8 +46,6 @@
  * - $comment_count: Number of comments attached to the node.
  * - $uid: User ID of the node author.
  * - $created: Time the node was published formatted in Unix timestamp.
- * - $classes_array: Array of html class attribute values. It is flattened
- *   into a string within the variable $classes.
  * - $zebra: Outputs either "even" or "odd". Useful for zebra striping in
  *   teaser listings.
  * - $id: Position of the node. Increments each time it's output.
@@ -81,7 +78,7 @@
  * @ingroup themeable
  */
 ?>
-<div id="node-<?php print $node->nid; ?>" class="<?php print $classes; ?> clearfix"<?php print $attributes; ?>>
+<div id="node-<?php print $node->nid; ?>" class="<?php print $attributes['class']; ?> clearfix"<?php print $attributes; ?>>
 
   <?php print render($title_prefix); ?>
   <?php if (!$page): ?>
diff --git a/core/themes/seven/maintenance-page.tpl.php b/core/themes/seven/maintenance-page.tpl.php
index 12c0f9e..09d33a1 100644
--- a/core/themes/seven/maintenance-page.tpl.php
+++ b/core/themes/seven/maintenance-page.tpl.php
@@ -22,7 +22,7 @@
     <?php print $styles; ?>
     <?php print $scripts; ?>
   </head>
-  <body class="<?php print $classes; ?>">
+  <body class="<?php print $attributes['class']; ?>">
 
   <?php print $page_top; ?>
 
