diff --git a/core/modules/breakpoint/breakpoint.info b/core/modules/breakpoint/breakpoint.info
new file mode 100644
index 0000000..7868658
--- /dev/null
+++ b/core/modules/breakpoint/breakpoint.info
@@ -0,0 +1,7 @@
+name = Breakpoint
+description = Manage breakpoints and breakpoint sets for responsive designs.
+package = Core
+version = VERSION
+core = 8.x
+
+dependencies[] = config
diff --git a/core/modules/breakpoint/breakpoint.module b/core/modules/breakpoint/breakpoint.module
new file mode 100644
index 0000000..22b8884
--- /dev/null
+++ b/core/modules/breakpoint/breakpoint.module
@@ -0,0 +1,263 @@
+<?php
+
+/**
+ * @file
+ * Manage breakpoints and breakpoint sets for responsive designs.
+ */
+
+use \Drupal\breakpoint\Breakpoint;
+use \Drupal\breakpoint\BreakpointGroup;
+
+/**
+ * Implements hook_enable().
+ *
+ * Import breakpoints from all enabled themes.
+ */
+function breakpoint_enable() {
+  config_install_default_config('module', 'breakpoint');
+  $themes = list_themes();
+  breakpoint_themes_enabled(array_keys($themes));
+}
+
+/**
+ * Implements hook_themes_enabled().
+ *
+ * Import breakpoints from all new enabled themes.
+ *
+ * @param array $theme_list
+ *   An array of theme names.
+ */
+function breakpoint_themes_enabled($theme_list) {
+  $themes = list_themes();
+  foreach ($theme_list as $theme_key) {
+    if ($themes[$theme_key]->status) {
+      $theme_breakpoints = breakpoint_get_theme_breakpoint_list($theme_key);
+      if (!empty($theme_breakpoints)) {
+        $weight = 0;
+        // Build a breakpoint set for each theme.
+        $breakpoint_group = new BreakpointGroup();
+        $breakpoint_group->id = $theme_key;
+        $breakpoint_group->label = $themes[$theme_key]->info['name'];
+        $breakpoint_group->sourceType = Breakpoint::SOURCE_TYPE_THEME;
+        foreach ($theme_breakpoints as $name => $mediaQuery) {
+          $breakpoint = new Breakpoint;
+          $breakpoint->name = $name;
+          $breakpoint->label = ucfirst($name);
+          $breakpoint->mediaQuery = $mediaQuery;
+          $breakpoint->source = $theme_key;
+          $breakpoint->sourceType = Breakpoint::SOURCE_TYPE_THEME;
+          $breakpoint->status = TRUE;
+          $breakpoint->weight = $weight++;
+          $breakpoint->save();
+          $breakpoint_group->breakpoints[$breakpoint->id()] = $breakpoint;
+        }
+        $breakpoint_group->save();
+
+        $uri = $breakpoint_group->uri();
+        if ($uri) {
+          $uri_options = $uri;
+          unset($uri_options['path']);
+          $uri = $uri['path'];
+        }
+        $message = t('The breakpoints from theme %theme are imported.', array(
+          '%theme' => check_plain($themes[$theme_key]->info['name']),
+        ));
+        if (module_exists('breakpoint_ui') && $uri) {
+          $message .= '<p>' . l(t('A new breakpoint set is created for theme %theme.', array(
+            '%theme' => check_plain($themes[$theme_key]->info['name']),
+          )), $uri, $uri_options);
+        }
+        drupal_set_message($message, 'status');
+      }
+    }
+  }
+}
+
+/**
+ * Implements hook_themes_disabled().
+ *
+ * Remove breakpoints from all disabled themes.
+ *
+ * @param array $theme_list
+ *   An array of theme names.
+ */
+function breakpoint_themes_disabled($theme_list) {
+  $breakpoint_groups = entity_load_multiple('breakpoint_group', $theme_list);
+  foreach ($breakpoint_groups as $breakpoint_group) {
+    $breakpoint_group->delete();
+    // delete all breakpoints defined by this theme.
+    $names = drupal_container()->get('config.storage')->listAll('breakpoints.breakpoint.' . Breakpoint::SOURCE_TYPE_THEME . '.' . $breakpoint_group->id() . '.');
+    $entity_info = entity_get_info('breakpoint');
+
+    foreach ($names as &$name) {
+      $name = substr($name, strlen($entity_info['config prefix']) + 1);
+    }
+    $breakpoints = entity_load_multiple('breakpoint', $names);
+
+    foreach ($breakpoints as $breakpoint) {
+      $breakpoint->delete();
+    }
+  }
+}
+
+/**
+ * Load general settings.
+ */
+function breakpoint_settings() {
+  $config = config('breakpoint');
+  if ($config->isNew()) {
+    return FALSE;
+  }
+  return (object)$config->get();
+}
+
+/**
+ * Save multipliers to settings.
+ *
+ * @param array $multipliers
+ *   array containing multipliers.
+ */
+function breakpoint_settings_save_multipliers($multipliers) {
+  $config = config('breakpoint');
+  $config->set('multipliers', $multipliers);
+  $config->save();
+}
+
+/**
+ * Reload breakpoint sets as they were defined in the theme.
+ *
+ * @param string $theme_key
+ *   The name of the theme.
+ *
+ * @return BreakpointGroup
+ *   Returns a BreakpointGroup containing the breakpoints defined by the theme.
+ */
+function breakpoint_group_reload_from_theme($theme_key) {
+  // Clear caches so theme.info is fresh.
+  system_rebuild_theme_data();
+  drupal_theme_rebuild();
+
+  $themes = list_themes();
+  if ($themes[$theme_key]->status) {
+    $theme_breakpoints = breakpoint_get_theme_breakpoint_list($theme_key);
+    if (!empty($theme_breakpoints)) {
+      $weight = 0;
+      // Build a set for each theme
+      $breakpoint_group = new BreakpointGroup();
+      $breakpoint_group->id = $theme_key;
+      $breakpoint_group->label = $themes[$theme_key]->info['name'];
+      $breakpoint_group->sourceType = Breakpoint::SOURCE_TYPE_THEME;
+      foreach ($theme_breakpoints as $name => $mediaQuery) {
+        $breakpoint = new Breakpoint;
+        $breakpoint->name = $name;
+        $breakpoint->label = ucfirst($name);
+        $breakpoint->mediaQuery = $mediaQuery;
+        $breakpoint->source = $theme_key;
+        $breakpoint->sourceType = Breakpoint::SOURCE_TYPE_THEME;
+        $breakpoint->status = TRUE;
+        $breakpoint->weight = $weight++;
+        $breakpoint->save();
+        $breakpoint_group->breakpoints[$breakpoint->getConfigName()] = $breakpoint;
+      }
+    }
+    return $breakpoint_group;
+  }
+  return FALSE;
+}
+
+/**
+ * Get a list of available breakpoints from a specified theme.
+ *
+ * @param $theme_key
+ *   The name of the theme.
+ *
+ * @return
+ *   An array of breakpoints in the form $breakpoint['name'] = 'media query'.
+ */
+function breakpoint_get_theme_breakpoint_list($theme_key) {
+  $themes = list_themes();
+  if (!isset($themes[$theme_key])) {
+    return array();
+  }
+
+  $config = config($theme_key . '.breakpoints');
+  if ($config) {
+    return $config->get();
+  }
+  return array();
+}
+
+/**
+ * Implements hook_entity_info().
+ */
+function breakpoint_entity_info() {
+  // Breakpoint
+  $types['breakpoint'] = array(
+    'label' => 'Breakpoint',
+    'entity class' => 'Drupal\breakpoint\Breakpoint',
+    'controller class' => 'Drupal\Core\Config\Entity\ConfigStorageController',
+    'config prefix' => 'breakpoint.breakpoint',
+    'entity keys' => array(
+      'id' => 'id',
+      'label' => 'label',
+      'uuid' => 'uuid',
+    ),
+  );
+
+  // Breakpointset
+  $types['breakpoint_group'] = array(
+    'label' => 'Breakpoint group',
+    'entity class' => 'Drupal\breakpoint\BreakpointGroup',
+    'controller class' => 'Drupal\breakpoint\BreakpointGroupController',
+    'config prefix' => 'breakpoint.breakpoint_group',
+    'entity keys' => array(
+      'id' => 'id',
+      'label' => 'label',
+      'uuid' => 'uuid',
+    ),
+  );
+
+  return $types;
+}
+
+/**
+ * Load one breakpoint set by its identifier.
+ */
+function breakpoint_group_load($id) {
+  return entity_load('breakpoint_group', $id);
+}
+
+/**
+ * Load all breakpoint sets.
+ */
+function breakpoint_group_load_all() {
+  $breakpoint_groups = entity_load_multiple('breakpoint_group');
+  return $breakpoint_groups;
+}
+
+/**
+ * Load one breakpoint by its identifier.
+ */
+function breakpoint_load($id) {
+  return entity_load('breakpoint', $id);
+}
+
+/**
+ * Load all breakpoints.
+ */
+function breakpoint_load_all() {
+  $breakpoints = entity_load_multiple('breakpoint');
+  return $breakpoints;
+}
+
+/**
+ * Load all breakpoint groups as select options.
+ */
+function breakpoint_group_select_options() {
+  $options = array();
+  $breakpoint_groups = breakpoint_group_load_all();
+  foreach ($breakpoint_groups as $breakpoint_group) {
+    $options[$breakpoint_group->id()] = $breakpoint_group->label();
+  }
+  return $options;
+}
diff --git a/core/modules/breakpoint/config/breakpoint.yml b/core/modules/breakpoint/config/breakpoint.yml
new file mode 100644
index 0000000..0ba703d
--- /dev/null
+++ b/core/modules/breakpoint/config/breakpoint.yml
@@ -0,0 +1,2 @@
+multipliers: [1x, 1.5x, 2x]
+
diff --git a/core/modules/breakpoint/lib/Drupal/breakpoint/Breakpoint.php b/core/modules/breakpoint/lib/Drupal/breakpoint/Breakpoint.php
new file mode 100644
index 0000000..671274c
--- /dev/null
+++ b/core/modules/breakpoint/lib/Drupal/breakpoint/Breakpoint.php
@@ -0,0 +1,294 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\breakpoint\Breakpoint.
+ */
+
+namespace Drupal\breakpoint;
+
+use Drupal\Core\Config\Entity\ConfigEntityBase;
+use Exception;
+
+/**
+ * Defines the Breakpoint entity.
+ */
+class Breakpoint extends ConfigEntityBase {
+
+  /**
+   * The possible values for sourceType.
+   */
+  const SOURCE_TYPE_THEME = 'theme';
+  const SOURCE_TYPE_MODULE = 'module';
+  const SOURCE_TYPE_CUSTOM = 'custom';
+
+  /**
+   * The breakpoint ID (config name).
+   *
+   * @var string
+   */
+  public $id;
+
+  /**
+   * The breakpoint UUID.
+   *
+   * @var string
+   */
+  public $uuid;
+
+  /**
+   * The breakpoint name (machine name).
+   *
+   * @var string
+   */
+  public $name;
+
+  /**
+   * The breakpoint label.
+   *
+   * @var string
+   */
+  public $label;
+
+  /**
+   * The breakpoint media query.
+   *
+   * @var string
+   */
+  public $mediaQuery = '';
+
+  /**
+   * The breakpoint source.
+   *
+   * @var string
+   */
+  public $source = 'user';
+
+  /**
+   * The breakpoint source type.
+   *
+   * @var string
+   *   Allowed values:
+   *     Breakpoint::SOURCE_TYPE_THEME
+   *     Breakpoint::SOURCE_TYPE_MODULE
+   *     Breakpoint::SOURCE_TYPE_CUSTOM
+   */
+  public $sourceType = Breakpoint::SOURCE_TYPE_CUSTOM;
+
+  /**
+   * The breakpoint status.
+   *
+   * @var string
+   */
+  public $status = TRUE;
+
+  /**
+   * The breakpoint weight.
+   *
+   * @var weight
+   */
+  public $weight = 0;
+
+  /**
+   * The breakpoint multipliers.
+   *
+   * @var multipliers
+   */
+  public $multipliers = array();
+
+  /**
+   * Overrides Drupal\config\ConfigEntityBase::__construct().
+   */
+  public function __construct(array $values = array(), $entity_type = 'breakpoint') {
+    parent::__construct($values, $entity_type);
+  }
+
+  /**
+   * Overrides Drupal\config\ConfigEntityBase::save().
+   */
+  public function save() {
+    if (empty($this->id)) {
+      $this->id = $this->buildConfigName();
+    }
+    if (empty($this->label)) {
+      $this->label = ucfirst($this->name);
+    }
+    if (!$this->isValid()) {
+      throw new Exception(t('Invalid media query detected.'));
+    }
+    return parent::save();
+  }
+
+  /**
+   * Get config name.
+   */
+  public function getConfigName() {
+    return $this->sourceType
+      . '.' . $this->source
+      . '.' . $this->name;
+  }
+
+  /**
+   * Build config name.
+   */
+  protected function buildConfigName() {
+    // Check for illegal values in breakpoint source type.
+    if (!in_array($this->sourceType, array(
+        Breakpoint::SOURCE_TYPE_CUSTOM,
+        Breakpoint::SOURCE_TYPE_MODULE,
+        Breakpoint::SOURCE_TYPE_THEME)
+      )) {
+      throw new Exception(
+          t(
+            "Expected one of '@custom', '@module' or '@theme' for breakpoint sourceType property but got '@sourcetype'.",
+            array(
+              '@custom' => Breakpoint::SOURCE_TYPE_CUSTOM,
+              '@module' => Breakpoint::SOURCE_TYPE_MODULE,
+              '@theme' => Breakpoint::SOURCE_TYPE_THEME,
+              '@sourcetype' => $this->sourceType,
+            )
+          )
+      );
+    }
+    // Check for illegal characters in breakpoint source.
+    if (preg_match('/[^a-z_]+/', $this->source)) {
+      throw new Exception(t("Invalid value '@source' for breakpoint source property. Breakpoint source property can only contain lowercase letters and underscores.", array('@source' => $this->source)));
+    }
+    // Check for illegal characters in breakpoint names.
+    if (preg_match('/[^0-9a-z_\-]/', $this->name)) {
+      throw new Exception(t("Invalid value '@name' for breakpoint name property. Breakpoint name property can only contain lowercase alphanumeric characters, underscores (_), and hyphens (-).", array('@name' => $this->name)));
+    }
+    return $this->sourceType
+      . '.' . $this->source
+      . '.' . $this->name;
+  }
+
+  /**
+   * Shortcut function to enable a breakpoint and save it.
+   *
+   * @see breakpoint_action_confirm_submit()
+   */
+  public function enable() {
+    if (!$this->status) {
+      $this->status = 1;
+      $this->save();
+    }
+  }
+
+  /**
+   * Shortcut function to disable a breakpoint and save it.
+   *
+   * @see breakpoint_action_confirm_submit()
+   */
+  public function disable() {
+    if ($this->status) {
+      $this->status = 0;
+      $this->save();
+    }
+  }
+
+  /**
+   * Check if the mediaQuery is valid.
+   *
+   * @see isValidMediaQuery()
+   */
+  public function isValid() {
+    return $this::isValidMediaQuery($this->mediaQuery);
+  }
+
+  /**
+   * Check if a mediaQuery is valid.
+   *
+   * @see http://www.w3.org/TR/css3-mediaqueries/
+   * @see http://www.w3.org/Style/CSS/Test/MediaQueries/20120229/reports/implement-report.html
+   */
+  public static function isValidMediaQuery($media_query) {
+    $media_features = array(
+      'width' => 'length', 'min-width' => 'length', 'max-width' => 'length',
+      'height' => 'length', 'min-height' => 'length', 'max-height' => 'length',
+      'device-width' => 'length', 'min-device-width' => 'length', 'max-device-width' => 'length',
+      'device-height' => 'length', 'min-device-height' => 'length', 'max-device-height' => 'length',
+      'orientation' => array('portrait', 'landscape'),
+      'aspect-ratio' => 'ratio', 'min-aspect-ratio' => 'ratio', 'max-aspect-ratio' => 'ratio',
+      'device-aspect-ratio' => 'ratio', 'min-device-aspect-ratio' => 'ratio', 'max-device-aspect-ratio' => 'ratio',
+      'color' => 'integer', 'min-color' => 'integer', 'max-color' => 'integer',
+      'color-index' => 'integer', 'min-color-index' => 'integer', 'max-color-index' => 'integer',
+      'monochrome' => 'integer', 'min-monochrome' => 'integer', 'max-monochrome' => 'integer',
+      'resolution' => 'resolution', 'min-resolution' => 'resolution', 'max-resolution' => 'resolution',
+      'scan' => array('progressive', 'interlace'),
+      'grid' => 'integer',
+    );
+    if ($media_query) {
+      // Strip new lines and trim.
+      $media_query = str_replace(array("\r", "\n"), ' ', trim($media_query));
+
+      // Remove comments /* ... */.
+      $media_query = preg_replace('/\/\*[\s\S]*?\*\//', '', $media_query);
+
+      // Check mediaQuery_list: S* [mediaQuery [ ',' S* mediaQuery ]* ]?
+      $parts = explode(',', $media_query);
+      foreach ($parts as $part) {
+        // Split on ' and '
+        $query_parts = explode(' and ', trim($part));
+        $media_type_found = FALSE;
+        foreach ($query_parts as $query_part) {
+          $matches = array();
+          // Check expression: '(' S* media_feature S* [ ':' S* expr ]? ')' S*
+          if (preg_match('/^\(([\w\-]+)(:\s?([\w\-]+))?\)/', trim($query_part), $matches)) {
+            // Single expression.
+            if (isset($matches[1]) && !isset($matches[2])) {
+              if (!array_key_exists($matches[1], $media_features)) {
+                return FALSE;
+              }
+            }
+            // Full expression.
+            elseif (isset($matches[3]) && !isset($matches[4])) {
+              $value = trim($matches[3]);
+              if (!array_key_exists($matches[1], $media_features)) {
+                return FALSE;
+              }
+              if (is_array($media_features[$matches[1]])) {
+                // Check if value is allowed.
+                if (!array_key_exists($value, $media_features[$matches[1]])) {
+                  return FALSE;
+                }
+              }
+              else {
+                switch ($media_features[$matches[1]]) {
+                  case 'length':
+                    $length_matches = array();
+                    if (preg_match('/^(\-)?(\d+)?((?:|em|ex|px|cm|mm|in|pt|pc|deg|rad|grad|ms|s|hz|khz|dpi|dpcm))$/i', trim($value), $length_matches)) {
+                      // Only -0 is allowed.
+                      if ($length_matches[1] === '-' && $length_matches[2] !== '0') {
+                        return FALSE;
+                      }
+                      // If there's a unit, a number is needed as well.
+                      if ($length_matches[2] === '' && $length_matches[3] !== '') {
+                        return FALSE;
+                      }
+                    }
+                    else {
+                      return FALSE;
+                    }
+                    break;
+                }
+              }
+            }
+          }
+          // Check [ONLY | NOT]? S* media_type
+          elseif (preg_match('/((?:only|not)?\s?)([\w\-]+)$/i', trim($query_part), $matches)) {
+            if ($media_type_found) {
+              throw new Exception(t('Only when media type allowed.'));
+            }
+            $media_type_found = TRUE;
+          }
+          else {
+            throw new Exception(t("Invalid value '@query_part' for breakpoint media query property.", array('@query_part' => $query_part)));
+          }
+        }
+      }
+      return TRUE;
+    }
+    return FALSE;
+  }
+}
diff --git a/core/modules/breakpoint/lib/Drupal/breakpoint/BreakpointGroup.php b/core/modules/breakpoint/lib/Drupal/breakpoint/BreakpointGroup.php
new file mode 100644
index 0000000..a2059ce
--- /dev/null
+++ b/core/modules/breakpoint/lib/Drupal/breakpoint/BreakpointGroup.php
@@ -0,0 +1,124 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\breakpoint\BreakpointGroup.
+ */
+
+namespace Drupal\breakpoint;
+
+use Drupal\Core\Config\Entity\ConfigEntityBase;
+
+/**
+ * Defines the BreakpointGroup entity.
+ */
+class BreakpointGroup extends ConfigEntityBase {
+
+  /**
+   * The BreakpointGroup ID (machine name).
+   *
+   * @var string
+   */
+  public $id;
+
+  /**
+   * The BreakpointGroup UUID.
+   *
+   * @var string
+   */
+  public $uuid;
+
+  /**
+   * The BreakpointGroup label.
+   *
+   * @var string
+   */
+  public $label;
+
+  /**
+   * The BreakpointGroup breakpoints.
+   *
+   * @var array
+   *   Array containing all breakpoints of this set.
+   *
+   * @see Drupal\breakpoints\Breakpoint
+   */
+  public $breakpoints = array();
+
+  /**
+   * The BreakpointGroup source type.
+   *
+   * @var string
+   *   Allowed values:
+   *     Breakpoint::SOURCE_TYPE_THEME
+   *     Breakpoint::SOURCE_TYPE_MODULE
+   *     Breakpoint::SOURCE_TYPE_CUSTOM
+   *
+   * @see Drupal\breakpoint\Breakpoint
+   */
+  public $sourceType = Breakpoint::SOURCE_TYPE_CUSTOM;
+
+  /**
+   * The BreakpointGroup overridden status.
+   *
+   * @var string
+   */
+  public $overridden = FALSE;
+
+  /**
+   * Overrides Drupal\config\ConfigEntityBase::__construct().
+   */
+  public function __construct(array $values = array(), $entity_type = 'breakpoint_group') {
+    parent::__construct($values, $entity_type);
+    $this->loadAllBreakpoints();
+  }
+
+  /**
+   * Overrides Drupal\Core\Entity::save().
+   */
+  public function save() {
+    // Only save the keys, but return the full objects.
+    $this->breakpoints = array_keys($this->breakpoints);
+    parent::save();
+    $this->loadAllBreakpoints();
+  }
+
+  /**
+   * Override and save a breakpoint set.
+   */
+  public function override() {
+    return entity_get_controller($this->entityType)->override($this);
+  }
+
+  /**
+   * Revert a breakpoint set after it has been overridden.
+   */
+  public function revert() {
+    return entity_get_controller($this->entityType)->revert($this);
+  }
+
+  /**
+   * Implements EntityInterface::createDuplicate().
+   */
+  public function createDuplicate() {
+    $duplicate = new BreakpointGroup();
+    $duplicate->id = '';
+    $duplicate->label = t('Clone of') . ' ' . $this->label();
+    $duplicate->breakpoints = $this->breakpoints;
+    return $duplicate;
+  }
+
+  /**
+   * Load all breakpoints, remove non-existing ones.
+   */
+  protected function loadAllBreakpoints() {
+    $breakpoints = $this->breakpoints;
+    $this->breakpoints = array();
+    foreach ($breakpoints as $breakpoint_id) {
+      $breakpoint = breakpoint_load($breakpoint_id);
+      if ($breakpoint) {
+        $this->breakpoints[$breakpoint_id] = $breakpoint;
+      }
+    }
+  }
+}
diff --git a/core/modules/breakpoint/lib/Drupal/breakpoint/BreakpointGroupController.php b/core/modules/breakpoint/lib/Drupal/breakpoint/BreakpointGroupController.php
new file mode 100644
index 0000000..77d7d91
--- /dev/null
+++ b/core/modules/breakpoint/lib/Drupal/breakpoint/BreakpointGroupController.php
@@ -0,0 +1,59 @@
+<?php
+/**
+ * @file
+ * Definition of Drupal\breakpoint\BreakpointGroupController.
+ */
+
+namespace Drupal\breakpoint;
+
+use Drupal\Core\Config\Entity\ConfigStorageController;
+use Drupal\breakpoint\BreakpointGroup;
+use Drupal\breakpoint\Breakpoint;
+
+/**
+ * Defines the BreakpointGroup entity's controller.
+ */
+class BreakpointGroupController extends ConfigStorageController {
+
+  /**
+   * Override and save a breakpoint set.
+   */
+  public function override(BreakpointGroup $breakpoint_group) {
+    if (!$breakpoint_group->sourceType == Breakpoint::SOURCE_TYPE_THEME) {
+      return FALSE;
+    }
+    foreach ($breakpoint_group->breakpoints as $key => $breakpoint) {
+      if ($breakpoint->sourceType == Breakpoint::SOURCE_TYPE_THEME && $breakpoint->source == $breakpoint_group->id()) {
+        $new_breakpoint = $breakpoint->createDuplicate();
+        $new_breakpoint->id = '';
+        $new_breakpoint->sourceType = Breakpoint::SOURCE_TYPE_CUSTOM;
+        $new_breakpoint->save();
+
+        // Remove old one, add new one.
+        unset($breakpoint_group->breakpoints[$key]);
+        $breakpoint_group->breakpoints[$new_breakpoint->id] = $new_breakpoint;
+      }
+    }
+    $breakpoint_group->overridden = TRUE;
+    $breakpoint_group->save();
+    return $breakpoint_group;
+  }
+
+  /**
+   * Revert a breakpoint set after it has been overridden.
+   */
+  public function revert(BreakpointGroup $breakpoint_group) {
+    if (!$breakpoint_group->overridden || !$breakpoint_group->sourceType == Breakpoint::SOURCE_TYPE_THEME) {
+      return FALSE;
+    }
+
+    // Reload all breakpoints from theme.
+    $reloaded_set = breakpoint_group_reload_from_theme($breakpoint_group->id());
+    if ($reloaded_set) {
+      $breakpoint_group->breakpoints = $reloaded_set->breakpoints;
+      $breakpoint_group->overridden = FALSE;
+      $breakpoint_group->save();
+    }
+    return $breakpoint_group;
+  }
+}
diff --git a/core/modules/breakpoint/lib/Drupal/breakpoint/Tests/BreakpointApiTest.php b/core/modules/breakpoint/lib/Drupal/breakpoint/Tests/BreakpointApiTest.php
new file mode 100644
index 0000000..41b7893
--- /dev/null
+++ b/core/modules/breakpoint/lib/Drupal/breakpoint/Tests/BreakpointApiTest.php
@@ -0,0 +1,90 @@
+<?php
+/**
+ * @file
+ * Definition of Drupal\breakpoint\Tests\BreakpointsApiTest.
+ */
+
+namespace Drupal\breakpoint\Tests;
+
+use Drupal\breakpoint\Tests\BreakpointsTestBase;
+use Drupal\breakpoint\Breakpoint;
+use Exception;
+
+/**
+ * Tests for general breakpoint api functions.
+ */
+class BreakpointApiTest extends BreakpointTestBase {
+
+  /**
+   * Drupal\simpletest\WebTestBase\getInfo().
+   */
+  public static function getInfo() {
+    return array(
+      'name' => 'Breakpoint general API functions',
+      'description' => 'Test general API functions of the breakpoint module.',
+      'group' => 'Breakpoint',
+    );
+  }
+
+  /**
+   * Test Breakpoint::buildConfigName().
+   */
+  public function testConfigName() {
+    $breakpoint = new Breakpoint(
+      array(
+        'label' => drupal_strtolower($this->randomName()),
+        'source' => 'custom_module',
+        // Try an invalid sourceType.
+        'sourceType' => 'oops',
+      )
+    );
+
+    try {
+      $breakpoint->save();
+    }
+    catch (Exception $e) {
+      $exception = TRUE;
+    }
+    $this->assertTrue($exception, t('breakpoint_config_name: An exception is thrown when an invalid sourceType is entered.'));
+    $this->assertEqual((string) $breakpoint->id(), '', t('breakpoint_config_name: No id is set when an invalid sourceType is entered.'));
+
+    // Try an invalid source.
+    $breakpoint->sourceType = Breakpoint::SOURCE_TYPE_CUSTOM;
+    $breakpoint->source = 'custom*_module source';
+    $exception = FALSE;
+    try {
+      $breakpoint->save();
+    }
+    catch (Exception $e) {
+      $exception = TRUE;
+    }
+    $this->assertTrue($exception, t('breakpoint_config_name: An exception is thrown when an invalid source is entered.'));
+    $this->assertEqual((string) $breakpoint->id(), '', t('breakpoint_config_name: No id is set when an invalid sourceType is entered.'));
+
+    // Try an invalid name (make sure there is at least once capital letter).
+    $breakpoint->source = 'custom_module';
+    $breakpoint->name = drupal_ucfirst($this->randomName());
+    $exception = FALSE;
+    try {
+      $breakpoint->save();
+    }
+    catch (Exception $e) {
+      $exception = TRUE;
+    }
+    $this->assertTrue($exception, t('breakpoint_config_name: An exception is thrown when an invalid name is entered.'));
+    $this->assertEqual((string) $breakpoint->id(), '', t('breakpoint_config_name: No id is set when an invalid sourceType is entered.'));
+
+    // Try a valid breakpoint.
+    $breakpoint->name = drupal_strtolower($this->randomName());
+    $breakpoint->mediaQuery = 'all';
+    $exception = FALSE;
+    try {
+      $breakpoint->save();
+    }
+    catch (Exception $e) {
+      $exception = TRUE;
+    }
+    $this->assertFalse($exception, t('breakpoint_config_name: No exception is thrown when a valid breakpoint is passed.'));
+    $this->assertEqual($breakpoint->id(), Breakpoint::SOURCE_TYPE_CUSTOM . '.custom_module.' . $breakpoint->name, t('breakpoint_config_name: A id is set when a valid breakpoint is passed.'));
+  }
+}
diff --git a/core/modules/breakpoint/lib/Drupal/breakpoint/Tests/BreakpointCrudTest.php b/core/modules/breakpoint/lib/Drupal/breakpoint/Tests/BreakpointCrudTest.php
new file mode 100644
index 0000000..ddfc683
--- /dev/null
+++ b/core/modules/breakpoint/lib/Drupal/breakpoint/Tests/BreakpointCrudTest.php
@@ -0,0 +1,63 @@
+<?php
+/**
+ * @file
+ * Definition of Drupal\breakpoint\Tests\BreakpointCrudTest.
+ */
+
+namespace Drupal\breakpoint\Tests;
+
+use Drupal\breakpoint\Tests\BreakpointTestBase;
+use Drupal\breakpoint\Breakpoint;
+
+/**
+ * Tests for breakpoint CRUD operations.
+ */
+class BreakpointCrudTest extends BreakpointTestBase {
+
+  /**
+   * Drupal\simpletest\WebTestBase\getInfo().
+   */
+  public static function getInfo() {
+    return array(
+      'name' => 'Breakpoint CRUD operations',
+      'description' => 'Test creation, loading, updating, deleting of breakpoints.',
+      'group' => 'Breakpoint',
+    );
+  }
+
+  /**
+   * Test CRUD operations for breakpoints.
+   */
+  public function testBreakpointCrud() {
+    // Add a breakpoint with minimum data only.
+    $values = array(
+      'label' => drupal_strtolower($this->randomName()),
+      'mediaQuery' => '(min-width: 600px)',
+    );
+
+    $breakpoint = new Breakpoint($values);
+    $breakpoint->save();
+
+    $this->verifyBreakpoint($breakpoint);
+
+    // Test breakpoint_load_all
+    $all_breakpoints = breakpoint_load_all();
+    $config_name = $breakpoint->getConfigName();
+    $this->assertTrue(isset($all_breakpoints[$config_name]), t('breakpoint_load_all: New breakpoint is present when loading all breakpoints.'));
+    $this->verifyBreakpoint($breakpoint, $all_breakpoints[$config_name]);
+
+    // Update the breakpoint.
+    $breakpoint->weight = 1;
+    $breakpoint->multipliers['2x'] = '2x';
+    $breakpoint->save();
+    $this->verifyBreakpoint($breakpoint);
+
+    // Disable the breakpoint.
+    $breakpoint->disable();
+    $this->verifyBreakpoint($breakpoint);
+
+    // Delete the breakpoint.
+    $breakpoint->delete();
+    $this->assertFalse(breakpoint_load($config_name), t('breakpoint_load: Loading a deleted breakpoint returns false.'), t('Breakpoints API'));
+  }
+}
diff --git a/core/modules/breakpoint/lib/Drupal/breakpoint/Tests/BreakpointGroupCrudTest.php b/core/modules/breakpoint/lib/Drupal/breakpoint/Tests/BreakpointGroupCrudTest.php
new file mode 100644
index 0000000..f5eb510
--- /dev/null
+++ b/core/modules/breakpoint/lib/Drupal/breakpoint/Tests/BreakpointGroupCrudTest.php
@@ -0,0 +1,75 @@
+<?php
+/**
+ * @file
+ * Definition of Drupal\breakpoint\Tests\BreakpointGroupCrudTest.
+ */
+
+namespace Drupal\breakpoint\Tests;
+
+use Drupal\breakpoint\Tests\BreakpointGroupTestBase;
+use Drupal\breakpoint\BreakpointGroup;
+use Drupal\breakpoint\Breakpoint;
+
+/**
+ * Tests for breakpoint set CRUD operations.
+ */
+class BreakpointGroupCrudTest extends BreakpointGroupTestBase {
+
+  /**
+   * Drupal\simpletest\WebTestBase\getInfo().
+   */
+  public static function getInfo() {
+    return array(
+      'name' => 'Breakpoint group CRUD operations',
+      'description' => 'Test creation, loading, updating, deleting of breakpoint sets.',
+      'group' => 'Breakpoint',
+    );
+  }
+
+  /**
+   * Test CRUD operations for breakpoint sets.
+   */
+  public function testBreakpointGroupCrud() {
+    // Add breakpoints.
+    $breakpoints = array();
+    for ($i = 0; $i <= 3; $i++) {
+      $width = ($i + 1) * 200;
+      $values = array(
+        'name' => drupal_strtolower($this->randomName()),
+        'weight' => $i,
+        'mediaQuery' => "(min-width: {$width}px)",
+      );
+      $breakpoint = new Breakpoint($values);
+      $breakpoint->save();
+      $breakpoints[$breakpoint->id()] = $breakpoint;
+    }
+    // Add a breakpoint set with minimum data only.
+    $label = $this->randomName();
+    $values = array(
+      'label' => $label,
+      'id' => drupal_strtolower($label),
+    );
+
+    $set = new BreakpointGroup($values);
+    $set->save();
+    $this->verifyBreakpointGroup($set);
+
+    // Update the breakpoint set.
+    $set->breakpoints = array_keys($breakpoints);
+    $set->save();
+    $this->verifyBreakpointGroup($set);
+
+    // Duplicate the breakpoint set.
+    $new_set = new BreakpointGroup();
+    $new_set->label = t('Clone of') . ' ' . $set->label();
+    $new_set->id = '';
+    $new_set->sourceType = Breakpoint::SOURCE_TYPE_CUSTOM;
+    $new_set->breakpoints = $set->breakpoints;
+    $duplicated_set = $set->createDuplicate();
+    $this->verifyBreakpointGroup($duplicated_set, $new_set);
+
+    // Delete the breakpoint set.
+    $set->delete();
+    $this->assertFalse(breakpoint_group_load($set->id), t('breakpoint_group_load: Loading a deleted breakpoint set returns false.'), t('Breakpoints API'));
+  }
+}
diff --git a/core/modules/breakpoint/lib/Drupal/breakpoint/Tests/BreakpointGroupTestBase.php b/core/modules/breakpoint/lib/Drupal/breakpoint/Tests/BreakpointGroupTestBase.php
new file mode 100644
index 0000000..ca45195
--- /dev/null
+++ b/core/modules/breakpoint/lib/Drupal/breakpoint/Tests/BreakpointGroupTestBase.php
@@ -0,0 +1,60 @@
+<?php
+/**
+ * @file
+ * Definition of Drupal\breakpoint\Tests\BreakpointGroupTestBase.
+ */
+
+namespace Drupal\breakpoint\Tests;
+
+use Drupal\simpletest\WebTestBase;
+use Drupal\breakpoint\BreakpointGroup;
+
+/**
+ * Base class for Breakpoint group tests.
+ */
+abstract class BreakpointGroupTestBase extends WebTestBase {
+
+  /**
+   * Modules to enable.
+   *
+   * @var array
+   */
+  public static $modules = array('breakpoint');
+
+  /**
+   * Drupal\simpletest\WebTestBase\setUp().
+   */
+  public function setUp() {
+    parent::setUp();
+  }
+
+  /**
+   * Verify that a breakpoint is properly stored.
+   */
+  public function verifyBreakpointGroup(BreakpointGroup $set, BreakpointGroup $compare_set = NULL) {
+    $properties = array(
+      'label',
+      'id',
+      'breakpoints',
+      'overridden',
+      'sourceType',
+    );
+    $assert_set = t('Breakpoints API');
+
+    // Verify breakpoint_group_load().
+    $compare_set = is_null($compare_set) ? breakpoint_group_load($set->id) : $compare_set;
+
+    foreach ($properties as $property) {
+      $t_args = array(
+        '%set' => $set->label(),
+        '%property' => $property,
+      );
+      if (is_array($compare_set->{$property})) {
+        $this->assertEqual(array_keys($compare_set->{$property}), array_keys($set->{$property}), t('breakpoint_group_load: Proper %property for breakpoint set %set.', $t_args), $assert_set);
+      }
+      else {
+        $this->assertEqual($compare_set->{$property}, $set->{$property}, t('breakpoint_group_load: Proper %property . for breakpoint set %set.', $t_args), $assert_set);
+      }
+    }
+  }
+}
diff --git a/core/modules/breakpoint/lib/Drupal/breakpoint/Tests/BreakpointMediaQueryTest.php b/core/modules/breakpoint/lib/Drupal/breakpoint/Tests/BreakpointMediaQueryTest.php
new file mode 100644
index 0000000..68438dc
--- /dev/null
+++ b/core/modules/breakpoint/lib/Drupal/breakpoint/Tests/BreakpointMediaQueryTest.php
@@ -0,0 +1,125 @@
+<?php
+/**
+ * @file
+ * Definition of Drupal\breakpoint\Tests\BreakpointMediaQueryTest.
+ */
+
+namespace Drupal\breakpoint\Tests;
+
+use Drupal\simpletest\UnitTestBase;
+use Drupal\breakpoint\Breakpoint;
+use Exception;
+
+/**
+ * Tests for media queries in a breakpoint.
+ */
+class BreakpointMediaQueryTest extends UnitTestBase {
+
+  /**
+   * Drupal\simpletest\WebTestBase\getInfo().
+   */
+  public static function getInfo() {
+    return array(
+      'name' => 'Breakpoint media query tests',
+      'description' => 'Test validation of media queries.',
+      'group' => 'Breakpoint',
+    );
+  }
+
+  /**
+   * Test valid media queries.
+   */
+  public function testValidMediaQueries() {
+    $media_queries = array(
+      // Bartik breakpoints.
+      '(min-width: 0px)',
+      'all and (min-width: 560px) and (max-width:850px)',
+      'all and (min-width: 851px)',
+      // Seven breakpoints.
+      '(min-width: 0em)',
+      'screen and (min-width: 40em)',
+      // Stark breakpoints.
+      '(min-width: 0px)',
+      'all and (min-width: 480px) and (max-width: 959px)',
+      'all and (min-width: 960px)',
+      '(orientation)',
+      'all and (orientation)',
+      'not all and (orientation)',
+      'only all and (orientation)',
+      'screen and (width)',
+      'screen and (width: 0)',
+      'screen and (width: 0px)',
+      'screen and (width: 0em)',
+      'screen and (min-width: -0)',
+      'screen and (max-width: 0)',
+      'screen and (min-width)',
+      // Multiline and comments.
+      'screen and /* this is a comment */ (min-width)',
+      "screen\nand /* this is a comment */ (min-width)",
+      "screen\n\nand /* this is\n a comment */ (min-width)",
+    );
+
+    foreach ($media_queries as $media_query) {
+      try {
+        $this->assertTrue(Breakpoint::isValidMediaQuery($media_query), $media_query . ' is valid.');
+      }
+      catch (Exception $e) {
+        $this->assertTrue(FALSE, $media_query . ' is valid.');
+      }
+    }
+  }
+
+  /**
+   * Test invalid media queries.
+   */
+  public function testInvalidMediaQueries() {
+    $media_queries = array(
+      'not (orientation)',
+      'only (orientation)',
+      'all and not all',
+      'screen and (width: 0xx)',
+      'screen and (width: -8xx)',
+      'screen and (width: -xx)',
+      'screen and (width: xx)',
+      'screen and (width: px)',
+      'screen and (width: -8px)',
+      'screen and (width: -0.8px)',
+      'screen and (height: 0xx)',
+      'screen and (height: -8xx)',
+      'screen and (height: -xx)',
+      'screen and (height: xx)',
+      'screen and (height: px)',
+      'screen and (height: -8px)',
+      'screen and (height: -0.8px)',
+      'screen and (device-width: 0xx)',
+      'screen and (device-width: -8xx)',
+      'screen and (device-width: -xx)',
+      'screen and (device-width: xx)',
+      'screen and (device-width: px)',
+      'screen and (device-width: -8px)',
+      'screen and (device-width: -0.8px)',
+      'screen and (device-height: 0xx)',
+      'screen and (device-height: -8xx)',
+      'screen and (device-height: -xx)',
+      'screen and (device-height: xx)',
+      'screen and (device-height: px)',
+      'screen and (device-height: -8px)',
+      'screen and (device-height: -0.8px)',
+      'screen and (min-orientation)',
+      'screen and (max-orientation)',
+      'screen and (min-orientation: landscape)',
+      'screen and (max-orientation: landscape)',
+      'screen and (orientation: bogus)',
+      '(orientation: bogus)',
+    );
+
+    foreach ($media_queries as $media_query) {
+      try {
+        $this->assertFalse(Breakpoint::isValidMediaQuery($media_query), $media_query . ' is not valid.');
+      }
+      catch (Exception $e) {
+        $this->assertTrue(TRUE, $media_query . ' is not valid.');
+      }
+    }
+  }
+}
diff --git a/core/modules/breakpoint/lib/Drupal/breakpoint/Tests/BreakpointTestBase.php b/core/modules/breakpoint/lib/Drupal/breakpoint/Tests/BreakpointTestBase.php
new file mode 100644
index 0000000..ba1ee07
--- /dev/null
+++ b/core/modules/breakpoint/lib/Drupal/breakpoint/Tests/BreakpointTestBase.php
@@ -0,0 +1,56 @@
+<?php
+/**
+ * @file
+ * Definition of Drupal\breakpoint\Tests\BreakpointTestBase.
+ */
+
+namespace Drupal\breakpoint\Tests;
+
+use Drupal\simpletest\WebTestBase;
+use Drupal\breakpoint\Breakpoint;
+
+/**
+ * Base class for Breakpoint tests.
+ */
+abstract class BreakpointTestBase extends WebTestBase {
+
+  /**
+   * Modules to enable.
+   *
+   * @var array
+   */
+  public static $modules = array('breakpoint');
+
+  /**
+   * Drupal\simpletest\WebTestBase\setUp().
+   */
+  public function setUp() {
+    parent::setUp();
+  }
+
+  /**
+   * Verify that a breakpoint is properly stored.
+   */
+  public function verifyBreakpoint(Breakpoint $breakpoint, Breakpoint $compare_breakpoint = NULL) {
+    $properties = array(
+      'label',
+      'mediaQuery',
+      'source',
+      'sourceType',
+      'status',
+      'weight',
+      'multipliers',
+    );
+    $assert_group = t('Breakpoints API');
+
+    // Verify breakpoint_load().
+    $compare_breakpoint = is_null($compare_breakpoint) ? breakpoint_load($breakpoint->getConfigName()) : $compare_breakpoint;
+    foreach ($properties as $property) {
+      $t_args = array(
+        '%breakpoint' => $breakpoint->label(),
+        '%property' => $property,
+      );
+      $this->assertEqual($compare_breakpoint->{$property}, $breakpoint->{$property}, t('breakpoint_load: Proper %property for breakpoint %breakpoint.', $t_args), $assert_group);
+    }
+  }
+}
diff --git a/core/modules/breakpoint/lib/Drupal/breakpoint/Tests/BreakpointThemeTest.php b/core/modules/breakpoint/lib/Drupal/breakpoint/Tests/BreakpointThemeTest.php
new file mode 100644
index 0000000..7df156b
--- /dev/null
+++ b/core/modules/breakpoint/lib/Drupal/breakpoint/Tests/BreakpointThemeTest.php
@@ -0,0 +1,90 @@
+<?php
+/**
+ * @file
+ * Definition of Drupal\breakpoint\Tests\BreakpointsThemeTest.
+ */
+
+namespace Drupal\breakpoint\Tests;
+
+use Drupal\breakpoint\Tests\BreakpointGroupTestBase;
+use Drupal\breakpoint\BreakpointGroup;
+use Drupal\breakpoint\Breakpoint;
+
+/**
+ * Test breakpoints provided by themes.
+ */
+class BreakpointThemeTest extends BreakpointGroupTestBase {
+
+  /**
+   * Modules to enable.
+   *
+   * @var array
+   */
+  public static $modules = array('breakpoint_theme_test');
+
+  /**
+   * Drupal\simpletest\WebTestBase\getInfo().
+   */
+  public static function getInfo() {
+    return array(
+      'name' => 'Breakpoint Theme functionality',
+      'description' => 'Thoroughly test the breakpoints provided by a theme.',
+      'group' => 'Breakpoint',
+    );
+  }
+
+  /**
+   * Drupal\simpletest\WebTestBase\setUp().
+   */
+  public function setUp() {
+    parent::setUp();
+    theme_enable(array('breakpoint_test_theme'));
+  }
+
+  /**
+   * Test the breakpoints provided by a theme.
+   */
+  public function testThemeBreakpoints() {
+    // Verify the breakpoint group for breakpoint_test_theme was created.
+    $breakpoint_group_obj = new BreakpointGroup();
+    $breakpoint_group_obj->label = 'Breakpoint test theme';
+    $breakpoint_group_obj->id = 'breakpoint_test_theme';
+    $breakpoint_group_obj->sourceType = Breakpoint::SOURCE_TYPE_THEME;
+    $breakpoint_group_obj->breakpoints = array(
+      'theme.breakpoint_test_theme.mobile' => array(),
+      'theme.breakpoint_test_theme.narrow' => array(),
+      'theme.breakpoint_test_theme.wide' => array(),
+      'theme.breakpoint_test_theme.tv' => array(),
+    );
+    $breakpoint_group_obj->overridden = 0;
+
+    // Verify we can load this breakpoint defined by the theme.
+    $this->verifyBreakpointGroup($breakpoint_group_obj);
+
+    // Override the breakpoints.
+    $overridden_set = clone $breakpoint_group_obj;
+    $breakpoint_group = breakpoint_group_load('breakpoint_test_theme');
+    $breakpoint_group = $breakpoint_group->override();
+
+    // Verify the group is overridden.
+    $overridden_set->breakpoints = array(
+      'custom.breakpoint_test_theme.mobile' => array(),
+      'custom.breakpoint_test_theme.narrow' => array(),
+      'custom.breakpoint_test_theme.wide' => array(),
+      'custom.breakpoint_test_theme.tv' => array(),
+    );
+    $overridden_set->overridden = 1;
+    $this->verifyBreakpointGroup($overridden_set);
+
+    // Revert the breakpoint set.
+    $breakpoint_group = breakpoint_group_load('breakpoint_test_theme');
+    $breakpoint_group = $breakpoint_group->revert();
+
+    // Verify the breakpoint group has its original values again when loaded.
+    $this->verifyBreakpointGroup($breakpoint_group_obj);
+
+    // Disable the test theme and verify the breakpoint group is deleted.
+    theme_disable(array('breakpoint_test_theme'));
+    $this->assertFalse(breakpoint_group_load('breakpoint_test_theme'), t('breakpoint_group_load: Loading a deleted breakpoint group returns false.'), t('Breakpoints API'));
+  }
+}
diff --git a/core/modules/breakpoint/tests/breakpoint_theme_test.info b/core/modules/breakpoint/tests/breakpoint_theme_test.info
new file mode 100644
index 0000000..f934419
--- /dev/null
+++ b/core/modules/breakpoint/tests/breakpoint_theme_test.info
@@ -0,0 +1,5 @@
+name = Breakpoint theme test
+description = Test breakpoints provided by themes
+package = Other
+core = 8.x
+hidden = TRUE
diff --git a/core/modules/breakpoint/tests/breakpoint_theme_test.module b/core/modules/breakpoint/tests/breakpoint_theme_test.module
new file mode 100644
index 0000000..50b5ff0
--- /dev/null
+++ b/core/modules/breakpoint/tests/breakpoint_theme_test.module
@@ -0,0 +1,13 @@
+<?php
+/**
+ * @file
+ * Test breakpoint functionality for breakpoints provided by themes.
+ */
+
+/**
+ * Implements hook_system_theme_info().
+ */
+function breakpoint_theme_test_system_theme_info() {
+  $themes['breakpoint_test_theme'] = drupal_get_path('module', 'breakpoint_theme_test') . '/themes/breakpoint_test_theme/breakpoint_test_theme.info';
+  return $themes;
+}
diff --git a/core/modules/breakpoint/tests/themes/breakpoint_test_theme/breakpoint_test_theme.info b/core/modules/breakpoint/tests/themes/breakpoint_test_theme/breakpoint_test_theme.info
new file mode 100644
index 0000000..b3ca39f
--- /dev/null
+++ b/core/modules/breakpoint/tests/themes/breakpoint_test_theme/breakpoint_test_theme.info
@@ -0,0 +1,5 @@
+name = Breakpoint test theme
+description = Test theme for breakpoint.
+core = 8.x
+base theme = bartik
+hidden = FALSE
diff --git a/core/modules/breakpoint/tests/themes/breakpoint_test_theme/config/breakpoint_test_theme.breakpoints.yml b/core/modules/breakpoint/tests/themes/breakpoint_test_theme/config/breakpoint_test_theme.breakpoints.yml
new file mode 100644
index 0000000..534fbf5
--- /dev/null
+++ b/core/modules/breakpoint/tests/themes/breakpoint_test_theme/config/breakpoint_test_theme.breakpoints.yml
@@ -0,0 +1,4 @@
+mobile: '(min-width: 0px)'
+narrow: '(min-width: 560px)'
+wide: '(min-width: 851px)'
+tv: 'only screen and (min-width: 3456px)'
diff --git a/core/modules/picture/lib/Drupal/picture/PictureMapping.php b/core/modules/picture/lib/Drupal/picture/PictureMapping.php
new file mode 100644
index 0000000..24d6338
--- /dev/null
+++ b/core/modules/picture/lib/Drupal/picture/PictureMapping.php
@@ -0,0 +1,123 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\picture\PictureMapping.
+ */
+
+namespace Drupal\picture;
+
+use Drupal\Core\Config\Entity\ConfigEntityBase;
+
+/**
+ * Defines the Picture entity.
+ */
+class PictureMapping extends ConfigEntityBase {
+
+  /**
+   * The Picture ID (machine name).
+   *
+   * @var string
+   */
+  public $id;
+
+  /**
+   * The Picture UUID.
+   *
+   * @var string
+   */
+  public $uuid;
+
+  /**
+   * The Picture label.
+   *
+   * @var string
+   */
+  public $label;
+
+  /**
+   * The Picture mappings.
+   *
+   * @var array
+   */
+  public $mappings = array();
+
+  /**
+   * The Picture BreakpointGroup.
+   *
+   * @var BreakpointGroup
+   */
+  public $breakpointSet = '';
+
+  /**
+   * Overrides Drupal\config\ConfigEntityBase::__construct().
+   */
+  public function __construct(array $values = array(), $entity_type = 'picture_mapping') {
+    parent::__construct($values, $entity_type);
+    $this->loadBreakpointGroup();
+    $this->loadAllMappings();
+  }
+
+  /**
+   * Overrides Drupal\Core\Entity::save().
+   */
+  public function save() {
+    // Only save the keys, but return the full objects.
+    if (isset($this->breakpointSet) && is_object($this->breakpointSet)) {
+      $this->breakpointSet = $this->breakpointSet->id();
+    }
+    parent::save();
+    $this->loadBreakpointGroup();
+    $this->loadAllMappings();
+  }
+
+  /**
+   * Implements EntityInterface::createDuplicate().
+   */
+  public function createDuplicate() {
+    $duplicate = new PictureMapping();
+    $duplicate->id = '';
+    $duplicate->label = t('Clone of') . ' ' . $this->label();
+    $duplicate->mappings = $this->mappings;
+    return $duplicate;
+  }
+
+  /**
+   * Load breakpointSet.
+   */
+  protected function loadBreakpointGroup() {
+    if ($this->breakpointSet) {
+      $breakpoint_group = breakpoint_group_load($this->breakpointSet);
+      $this->breakpointSet = $breakpoint_group;
+    }
+  }
+
+  /**
+   * Load all mappings, remove non-existing ones.
+   */
+  protected function loadAllMappings() {
+    $loaded_mappings = $this->mappings;
+    $this->mappings = array();
+    if ($this->breakpointSet) {
+      foreach ($this->breakpointSet->breakpoints as $breakpoint_id => $breakpoint) {
+        // Get the mapping for the default multiplier.
+        $this->mappings[$breakpoint_id]['1x'] = '';
+        if (isset($loaded_mappings[$breakpoint_id]['1x'])) {
+          $this->mappings[$breakpoint_id]['1x'] = $loaded_mappings[$breakpoint_id]['1x'];
+        }
+
+        // Get the mapping for the other multipliers.
+        if (isset($breakpoint->multipliers) && !empty($breakpoint->multipliers)) {
+          foreach ($breakpoint->multipliers as $multiplier => $status) {
+            if ($status) {
+              $this->mappings[$breakpoint_id][$multiplier] = '';
+              if (isset($loaded_mappings[$breakpoint_id][$multiplier])) {
+                $this->mappings[$breakpoint_id][$multiplier] = $loaded_mappings[$breakpoint_id][$multiplier];
+              }
+            }
+          }
+        }
+      }
+    }
+  }
+}
diff --git a/core/modules/picture/lib/Drupal/picture/PictureMappingFormController.php b/core/modules/picture/lib/Drupal/picture/PictureMappingFormController.php
new file mode 100644
index 0000000..e33784e
--- /dev/null
+++ b/core/modules/picture/lib/Drupal/picture/PictureMappingFormController.php
@@ -0,0 +1,119 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\picture\PictureFormController.
+ */
+
+namespace Drupal\picture;
+
+use Drupal\Core\Entity\EntityInterface;
+use Drupal\Core\Entity\EntityFormController;
+use Drupal\picture\PictureMapping;
+
+/**
+ * Form controller for the picture edit/add forms.
+ */
+class PictureMappingFormController extends EntityFormController {
+
+  /**
+   * Overrides Drupal\Core\Entity\EntityFormController::form().
+   *
+   * @param array $form
+   *   A nested array form elements comprising the form.
+   * @param array $form_state
+   *   An associative array containing the current state of the form.
+   * @param \Drupal\picture\PictureMapping $picture_mapping
+   *   The entity being edited.
+   *
+   * @return array
+   *   The array containing the complete form.
+   */
+  public function form(array $form, array &$form_state, EntityInterface $picture_mapping) {
+    // Check if we need to duplicate the picture.
+    if ($this->operation == 'duplicate') {
+      $picture_mapping = $picture_mapping->createDuplicate();
+      $this->setEntity($picture_mapping, $form_state);
+    }
+    $form['label'] = array(
+      '#type' => 'textfield',
+      '#title' => t('Label'),
+      '#maxlength' => 255,
+      '#default_value' => $picture_mapping->label(),
+      '#description' => t("Example: 'Main content' or 'Sidebar'."),
+      '#required' => TRUE,
+    );
+    $form['id'] = array(
+      '#type' => 'machine_name',
+      '#default_value' => $picture_mapping->id(),
+      '#machine_name' => array(
+        'exists' => 'picture_mapping_load',
+        'source' => array('label'),
+      ),
+      '#disabled' => (bool) $picture_mapping->id() && $this->operation != 'duplicate',
+    );
+    $form['breakpointSet'] = array(
+      '#type' => 'select',
+      '#title' => t('Breakpoint Set'),
+      '#default_value' => !empty($picture_mapping->breakpointSet) ? $picture_mapping->breakpointSet->id() : '',
+      '#options' => breakpoint_group_select_options(),
+      '#required' => TRUE,
+    );
+
+    $image_styles = image_style_options(TRUE);
+    foreach ($picture_mapping->mappings as $breakpoint_id => $mapping) {
+      foreach ($mapping as $multiplier => $image_style) {
+        $label = $multiplier . ' ' . $picture_mapping->breakpointSet->breakpoints[$breakpoint_id]->name . ' [' . $picture_mapping->breakpointSet->breakpoints[$breakpoint_id]->mediaQuery . ']';
+        $form['mappings'][$breakpoint_id][$multiplier] = array(
+          '#type' => 'select',
+          '#title' => check_plain($label),
+          '#options' => $image_styles,
+          '#default_value' => $image_style,
+        );
+      }
+    }
+
+    $form['#tree'] = TRUE;
+
+    return parent::form($form, $form_state, $picture_mapping);
+  }
+
+  /**
+   * Overrides Drupal\Core\Entity\EntityFormController::actions().
+   */
+  protected function actions(array $form, array &$form_state) {
+    // Only includes a Save action for the entity, no direct Delete button.
+    return array(
+      'submit' => array(
+        '#value' => t('Save'),
+        '#validate' => array(
+          array($this, 'validate'),
+        ),
+        '#submit' => array(
+          array($this, 'submit'),
+          array($this, 'save'),
+        ),
+      ),
+    );
+  }
+
+  /**
+   * Overrides Drupal\Core\Entity\EntityFormController::validate().
+   */
+  public function validate(array $form, array &$form_state) {
+  }
+
+  /**
+   * Overrides Drupal\Core\Entity\EntityFormController::save().
+   */
+  public function save(array $form, array &$form_state) {
+    $picture_mapping = $this->getEntity($form_state);
+    $picture_mapping->save();
+
+    watchdog('picture', 'Picture mapping @label saved.', array('@label' => $picture_mapping->label()), WATCHDOG_NOTICE);
+    drupal_set_message(t('Picture mapping %label saved.', array('%label' => $picture_mapping->label())));
+
+    $form_state['redirect'] = 'admin/config/media/picturemapping';
+  }
+
+}
diff --git a/core/modules/picture/lib/Drupal/picture/PictureMappingListController.php b/core/modules/picture/lib/Drupal/picture/PictureMappingListController.php
new file mode 100644
index 0000000..6329f92
--- /dev/null
+++ b/core/modules/picture/lib/Drupal/picture/PictureMappingListController.php
@@ -0,0 +1,35 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\picture\PictureListController.
+ */
+
+namespace Drupal\picture;
+
+use Drupal\Core\Config\Entity\ConfigEntityListController;
+use Drupal\Core\Entity\EntityInterface;
+use Drupal\picture\PictureMapping;
+
+/**
+ * Provides a listing of Pictures.
+ */
+class PictureMappingListController extends ConfigEntityListController {
+
+  /**
+   * Overrides Drupal\config\EntityListControllerBase::hookMenu().
+   */
+  public function hookMenu() {
+    $path = $this->entityInfo['list path'];
+    $items = parent::hookMenu();
+
+    // Override the access callback.
+    $items[$path]['title'] = 'Picture Mappings';
+    $items[$path]['description'] = 'Manage list of pictures.';
+    $items[$path]['access callback'] = 'user_access';
+    $items[$path]['access arguments'] = array('administer pictures');
+
+    return $items;
+  }
+
+}
diff --git a/core/modules/picture/lib/Drupal/picture/Tests/PictureAdminUITest.php b/core/modules/picture/lib/Drupal/picture/Tests/PictureAdminUITest.php
new file mode 100644
index 0000000..a70543d
--- /dev/null
+++ b/core/modules/picture/lib/Drupal/picture/Tests/PictureAdminUITest.php
@@ -0,0 +1,141 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\picture\Tests\PictureAdminUITest.
+ */
+
+namespace Drupal\picture\Tests;
+
+use Drupal\simpletest\WebTestBase;
+use Drupal\picture\PictureMapping;
+use Drupal\breakpoint\BreakpointGroup;
+use Drupal\breakpoint\Breakpoint;
+
+/**
+ * Tests for breakpoint sets admin interface.
+ */
+class PictureAdminUITest extends WebTestBase {
+
+  /**
+   * Modules to enable.
+   *
+   * @var array
+   */
+  public static $modules = array('picture');
+
+  /**
+   * Drupal\simpletest\WebTestBase\getInfo().
+   */
+  public static function getInfo() {
+    return array(
+      'name' => 'Picture administration functionality',
+      'description' => 'Thoroughly test the administrative interface of the picture module.',
+      'group' => 'Picture',
+    );
+  }
+
+  /**
+   * Drupal\simpletest\WebTestBase\setUp().
+   */
+  public function setUp() {
+    parent::setUp();
+
+    // Create user.
+    $this->admin_user = $this->drupalCreateUser(array(
+      'administer pictures',
+    ));
+
+    $this->drupalLogin($this->admin_user);
+
+    // Add breakpoint_group and breakpoints.
+    $breakpoint_group = new BreakpointGroup();
+    $breakpoint_group->id = 'atestset';
+    $breakpoint_group->label = 'A test set';
+    $breakpoint_group->sourceType = Breakpoint::SOURCE_TYPE_CUSTOM;
+
+    $breakpoints = array();
+    $breakpoint_names = array('small', 'medium', 'large');
+    for ($i = 0; $i < 3; $i++) {
+      $breakpoint = new Breakpoint;
+      $breakpoint->name = $breakpoint_names[$i];
+      $width = ($i + 1) * 200;
+      $breakpoint->mediaQuery = "(min-width: {$width}px)";
+      $breakpoint->source = 'user';
+      $breakpoint->sourceType = 'custom';
+      $breakpoint->multipliers = array(
+        '1.5x' => 0,
+        '2x' => '2x',
+      );
+      $breakpoint->save();
+      $breakpoint_group->breakpoints[$breakpoint->id()] = $breakpoint;
+    }
+    $breakpoint_group->save();
+
+  }
+
+  /**
+   * Test picture administration functionality.
+   */
+  public function testPictureAdmin() {
+    // We start without any default mappings.
+    $this->drupalGet('admin/config/media/picturemapping');
+    $this->assertText('There is no Picture mapping yet.');
+
+    // Add a new picture mapping, our breakpoint set should be selected.
+    $this->drupalGet('admin/config/media/picturemapping/add');
+    $this->assertFieldByName('breakpointSet', 'atestset');
+
+    // Create a new group.
+    $edit = array(
+      'label' => 'Mapping One',
+      'id' => 'mapping_one',
+      'breakpointSet' => 'atestset',
+    );
+    $this->drupalPost('admin/config/media/picturemapping/add', $edit, t('Save'));
+
+    // Check if the new group is created.
+    $this->assertResponse(200);
+    $this->drupalGet('admin/config/media/picturemapping');
+    $this->assertNoText('There is no Picture mapping yet.');
+    $this->assertText('Mapping One');
+    $this->assertText('mapping_one');
+
+    // Edit the group.
+    $this->drupalGet('admin/config/media/picturemapping/mapping_one/edit');
+    $this->assertFieldByName('label', 'Mapping One');
+    $this->assertFieldByName('breakpointSet', 'atestset');
+
+    // Check if the dropdows are present for the mappings.
+    $this->assertFieldByName('mappings[custom.user.small][1x]', '');
+    $this->assertFieldByName('mappings[custom.user.small][2x]', '');
+    $this->assertFieldByName('mappings[custom.user.medium][1x]', '');
+    $this->assertFieldByName('mappings[custom.user.medium][2x]', '');
+    $this->assertFieldByName('mappings[custom.user.large][1x]', '');
+    $this->assertFieldByName('mappings[custom.user.large][2x]', '');
+
+    // Save mappings for 1x variant only.
+    $edit = array(
+      'label' => 'Mapping One',
+      'breakpointSet' => 'atestset',
+      'mappings[custom.user.small][1x]' => 'thumbnail',
+      'mappings[custom.user.medium][1x]' => 'medium',
+      'mappings[custom.user.large][1x]' => 'large',
+    );
+    $this->drupalPost('admin/config/media/picturemapping/mapping_one/edit', $edit, t('Save'));
+    $this->drupalGet('admin/config/media/picturemapping/mapping_one/edit');
+    $this->assertFieldByName('mappings[custom.user.small][1x]', 'thumbnail');
+    $this->assertFieldByName('mappings[custom.user.small][2x]', '');
+    $this->assertFieldByName('mappings[custom.user.medium][1x]', 'medium');
+    $this->assertFieldByName('mappings[custom.user.medium][2x]', '');
+    $this->assertFieldByName('mappings[custom.user.large][1x]', 'large');
+    $this->assertFieldByName('mappings[custom.user.large][2x]', '');
+
+    // Delete the mapping.
+    $this->drupalGet('admin/config/media/picturemapping/mapping_one/delete');
+    $this->drupalPost(NULL, array(), t('Delete'));
+    $this->drupalGet('admin/config/media/picturemapping');
+    $this->assertText('There is no Picture mapping yet.');
+  }
+
+}
diff --git a/core/modules/picture/lib/Drupal/picture/Tests/PictureFieldDisplayTest.php b/core/modules/picture/lib/Drupal/picture/Tests/PictureFieldDisplayTest.php
new file mode 100644
index 0000000..f6f12d1
--- /dev/null
+++ b/core/modules/picture/lib/Drupal/picture/Tests/PictureFieldDisplayTest.php
@@ -0,0 +1,187 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\picture\Tests\PictureFieldDisplayTest.
+ */
+
+namespace Drupal\picture\Tests;
+
+use Drupal\picture\PictureMapping;
+use Drupal\breakpoint\BreakpointGroup;
+use Drupal\breakpoint\Breakpoint;
+use Drupal\image\Tests\ImageFieldTestBase;
+
+/**
+ * Test class to check that formatters and display settings are working.
+ */
+class PictureFieldDisplayTest extends ImageFieldTestBase {
+
+  /**
+   * Modules to enable.
+   *
+   * @var array
+   */
+  public static $modules = array('field_ui', 'picture');
+
+  /**
+   * Drupal\simpletest\WebTestBase\getInfo().
+   */
+  public static function getInfo() {
+    return array(
+      'name' => 'Picture field display tests',
+      'description' => 'Test picture display formatter.',
+      'group' => 'Picture',
+    );
+  }
+
+  /**
+   * Drupal\simpletest\WebTestBase\setUp().
+   */
+  public function setUp() {
+    parent::setUp();
+
+    // Create user.
+    $this->admin_user = $this->drupalCreateUser(array('administer pictures', 'access content', 'access administration pages', 'administer site configuration', 'administer content types', 'administer nodes', 'create article content', 'edit any article content', 'delete any article content', 'administer image styles'));
+    $this->drupalLogin($this->admin_user);
+
+    // Add breakpoint_group and breakpoints.
+    $breakpoint_group = new BreakpointGroup();
+    $breakpoint_group->id = 'atestset';
+    $breakpoint_group->label = 'A test set';
+    $breakpoint_group->sourceType = Breakpoint::SOURCE_TYPE_CUSTOM;
+
+    $breakpoints = array();
+    $breakpoint_names = array('small', 'medium', 'large');
+    for ($i = 0; $i < 3; $i++) {
+      $breakpoint = new Breakpoint;
+      $breakpoint->name = $breakpoint_names[$i];
+      $width = ($i + 1) * 200;
+      $breakpoint->mediaQuery = "(min-width: {$width}px)";
+      $breakpoint->source = 'user';
+      $breakpoint->sourceType = 'custom';
+      $breakpoint->multipliers = array(
+        '1.5x' => 0,
+        '2x' => '2x',
+      );
+      $breakpoint->save();
+      $breakpoint_group->breakpoints[$breakpoint->id()] = $breakpoint;
+    }
+    $breakpoint_group->save();
+
+    // Add picture mapping.
+    $picture_mapping = new PictureMapping();
+    $picture_mapping->id = 'mapping_one';
+    $picture_mapping->label = 'Mapping One';
+    $picture_mapping->breakpointSet = 'atestset';
+    $picture_mapping->save();
+    $picture_mapping->mappings['custom.user.small']['1x'] = 'thumbnail';
+    $picture_mapping->mappings['custom.user.medium']['1x'] = 'medium';
+    $picture_mapping->mappings['custom.user.large']['1x'] = 'large';
+    $picture_mapping->save();
+  }
+
+  /**
+   * Test picture formatters on node display for public files.
+   */
+  public function testPictureFieldFormattersPublic() {
+    $this->_testPictureFieldFormatters('public');
+  }
+
+  /**
+   * Test picture formatters on node display for private files.
+   */
+  public function testPictureFieldFormattersPrivate() {
+    // Remove access content permission from anonymous users.
+    user_role_change_permissions(DRUPAL_ANONYMOUS_RID, array('access content' => FALSE));
+    $this->_testPictureFieldFormatters('private');
+  }
+
+  /**
+   * Test picture formatters on node display.
+   */
+  public function _testPictureFieldFormatters($scheme) {
+    $field_name = strtolower($this->randomName());
+    $this->createImageField($field_name, 'article', array('uri_scheme' => $scheme));
+    // Create a new node with an image attached.
+    $test_image = current($this->drupalGetTestFiles('image'));
+    $nid = $this->uploadNodeImage($test_image, $field_name, 'article');
+    $node = node_load($nid, TRUE);
+
+    // Use the picture formatter.
+    $instance = field_info_instance('node', $field_name, 'article');
+    $instance['display']['default']['type'] = 'picture';
+    $instance['display']['default']['module'] = 'picture';
+
+    // Test that the default formatter is being used.
+    $image_uri = file_load($node->{$field_name}[LANGUAGE_NOT_SPECIFIED][0]['fid'])->uri;
+    $image_info = array(
+      'uri' => $image_uri,
+      'width' => 40,
+      'height' => 20,
+    );
+    $default_output = theme('image', $image_info);
+    $this->assertRaw($default_output, 'Default formatter displaying correctly on full node view.');
+
+    // Use the picture formatter linked to file formatter.
+    $instance = field_info_instance('node', $field_name, 'article');
+    $instance['display']['default']['type'] = 'picture';
+    $instance['display']['default']['module'] = 'picture';
+    $instance['display']['default']['settings']['image_link'] = 'file';
+    field_update_instance($instance);
+    $default_output = l(theme('image', $image_info), file_create_url($image_uri), array('html' => TRUE));
+    $this->drupalGet('node/' . $nid);
+    $this->assertRaw($default_output, 'Image linked to file formatter displaying correctly on full node view.');
+    // Verify that the image can be downloaded.
+    $this->assertEqual(file_get_contents($test_image->uri), $this->drupalGet(file_create_url($image_uri)), 'File was downloaded successfully.');
+    if ($scheme == 'private') {
+      // Only verify HTTP headers when using private scheme and the headers are
+      // sent by Drupal.
+      $this->assertEqual($this->drupalGetHeader('Content-Type'), 'image/png', 'Content-Type header was sent.');
+      $this->assertEqual($this->drupalGetHeader('Content-Disposition'), 'inline; filename="' . $test_image->filename . '"', 'Content-Disposition header was sent.');
+      $this->assertTrue(strstr($this->drupalGetHeader('Cache-Control'),'private') !== FALSE, 'Cache-Control header was sent.');
+
+      // Log out and try to access the file.
+      $this->drupalLogout();
+      $this->drupalGet(file_create_url($image_uri));
+      $this->assertResponse('403', 'Access denied to original image as anonymous user.');
+
+      // Log in again.
+      $this->drupalLogin($this->admin_user);
+    }
+
+    // Use the picture formatter with a picture mapping.
+    $instance['display']['default']['settings']['picture_mapping'] = 'mapping_one';
+    field_update_instance($instance);
+    // Output should contain all image styles and all breakpoints.
+    $this->drupalGet('node/' . $nid);
+    $this->assertRaw('/styles/thumbnail/');
+    $this->assertRaw('/styles/medium/');
+    $this->assertRaw('/styles/large/');
+    $this->assertRaw('media="(min-width: 200px)"');
+    $this->assertRaw('media="(min-width: 400px)"');
+    $this->assertRaw('media="(min-width: 600px)"');
+
+    // Test the fallback image style.
+    $instance['display']['default']['settings']['image_link'] = '';
+    $instance['display']['default']['settings']['fallback_image_style'] = 'large';
+    field_update_instance($instance);
+
+    $this->drupalGet(image_style_url('large', $image_uri));
+    $image_info['uri'] = $image_uri;
+    $image_info['width'] = 480;
+    $image_info['height'] = 240;
+    $image_info['style_name'] = 'large';
+    $default_output = '<noscript>' . theme('image_style', $image_info) . '</noscript>';
+    $this->drupalGet('node/' . $nid);
+    $this->assertRaw($default_output, 'Image style thumbnail formatter displaying correctly on full node view.');
+
+    if ($scheme == 'private') {
+      // Log out and try to access the file.
+      $this->drupalLogout();
+      $this->drupalGet(image_style_url('large', $image_uri));
+      $this->assertResponse('403', 'Access denied to image style thumbnail as anonymous user.');
+    }
+  }
+
+}
diff --git a/core/modules/picture/picture.info b/core/modules/picture/picture.info
new file mode 100644
index 0000000..0b7929b
--- /dev/null
+++ b/core/modules/picture/picture.info
@@ -0,0 +1,9 @@
+name = Picture
+description = Picture element
+package = Core
+version = VERSION
+core = 8.x
+dependencies[] = breakpoint
+dependencies[] = config
+dependencies[] = image
+configure = admin/config/media/picturemapping
\ No newline at end of file
diff --git a/core/modules/picture/picture.module b/core/modules/picture/picture.module
new file mode 100644
index 0000000..3b87455
--- /dev/null
+++ b/core/modules/picture/picture.module
@@ -0,0 +1,489 @@
+<?php
+
+use \Drupal\picture\PictureMapping;
+use \Drupal\Core\Template\Attribute;
+
+define('PICTURE_CLASS', 'resp-img-picture');
+
+/**
+ * Implements hook_permission().
+ */
+function picture_permission() {
+  return array(
+    'administer pictures' => array(
+      'title' => t('Administer Pictures'),
+      'description' => t('Administer Pictures'),
+    ),
+  );
+}
+
+/**
+ * Implements hook_menu().
+ */
+function picture_menu() {
+  $items = array();
+
+  $items['admin/config/media/picturemapping'] = array(
+    'title' => 'Picture Mappings',
+    'description' => 'Manage picture mappings',
+    'access arguments' => array('administer pictures'),
+    'weight' => 10,
+    'page callback' => 'picture_mapping_page',
+    'file' => 'picture_mapping.admin.inc',
+  );
+  $items['admin/config/media/picturemapping/add'] = array(
+    'title' => 'Add picture mapping',
+    'page callback' => 'picture_mapping_page_add',
+    'access callback' => 'user_access',
+    'access arguments' => array('administer pictures'),
+    'type' => MENU_LOCAL_ACTION,
+    'file' => 'picture_mapping.admin.inc',
+  );
+  $items['admin/config/media/picturemapping/%picture_mapping/edit'] = array(
+    'title' => 'Edit picture mapping',
+    'page callback' => 'picture_mapping_page_edit',
+    'page arguments' => array(4),
+    'access callback' => 'user_access',
+    'access arguments' => array('administer pictures'),
+    'file' => 'picture_mapping.admin.inc',
+  );
+  $items['admin/config/media/picturemapping/%picture_mapping/duplicate'] = array(
+    'title' => 'Duplicate picture mapping',
+    'page callback' => 'picture_mapping_page_duplicate',
+    'page arguments' => array(4),
+    'access callback' => 'user_access',
+    'access arguments' => array('administer pictures'),
+    'file' => 'picture_mapping.admin.inc',
+  );
+  $items['admin/config/media/picturemapping/%picture_mapping/delete'] = array(
+    'title' => 'Delete',
+    'page callback' => 'drupal_get_form',
+    'page arguments' => array('picture_mapping_action_confirm', 4, 5),
+    'access callback' => 'user_access',
+    'access arguments' => array('administer pictures'),
+    'file' => 'picture_mapping.admin.inc',
+  );
+
+  return $items;
+}
+
+/**
+ * Implements hook_entity_info().
+ */
+function picture_entity_info() {
+  $types['picture_mapping'] = array(
+    'label' => 'Picture mapping',
+    'entity class' => 'Drupal\picture\PictureMapping',
+    'controller class' => 'Drupal\Core\Config\Entity\ConfigStorageController',
+    'config prefix' => 'picture.mappings',
+    'entity keys' => array(
+      'id' => 'id',
+      'label' => 'label',
+      'uuid' => 'uuid',
+    ),
+    'form controller class' => array(
+      'default' => 'Drupal\picture\PictureMappingFormController',
+      'add' => 'Drupal\picture\PictureMappingFormController',
+      'duplicate' => 'Drupal\picture\PictureMappingFormController',
+    ),
+    'list controller class' => 'Drupal\picture\PictureMappingListController',
+    'list path' => 'admin/config/media/picturemapping',
+    'uri callback' => 'picture_mapping_uri',
+  );
+
+  return $types;
+}
+
+/**
+ * Implements hook_library_info().
+ */
+function picture_library_info() {
+  $libraries['matchmedia'] = array(
+    'title' => t('Matchmedia'),
+    'website' => 'https://github.com/attiks/picturefill-proposal',
+    'version' => '0.1',
+    'js' => array(
+      drupal_get_path('module', 'picture') . '/picturefill/matchmedia.js' => array('type' => 'file', 'weight' => -10, 'group' => JS_DEFAULT),
+    ),
+  );
+  $libraries['picturefill'] = array(
+    'title' => t('Picturefill'),
+    'website' => 'https://github.com/attiks/picturefill-proposal',
+    'version' => '0.1',
+    'js' => array(
+      drupal_get_path('module', 'picture') . '/picturefill/picturefill.js' => array('type' => 'file', 'weight' => -10, 'group' => JS_DEFAULT),
+    ),
+  );
+  return $libraries;
+}
+
+/**
+ * Load one picture by its identifier.
+ */
+function picture_mapping_load($id) {
+  return entity_load('picture_mapping', $id);
+}
+
+/**
+ * Load all pictures.
+ */
+function picture_mapping_load_all() {
+  $picture_mappings = entity_load_multiple('picture_mapping');
+  return $picture_mappings;
+}
+
+/**
+ * Picture uri callback.
+ */
+function picture_mapping_uri(PictureMapping $picture_mapping) {
+  return array(
+    'path' => 'admin/config/media/picturemapping/' . $picture_mapping->id(),
+  );
+}
+
+/**
+ * Picture uri callback.
+ */
+function picture_mapping_set_uri(PictureMapping $picture_mapping) {
+  return array(
+    'path' => 'admin/config/media/picturemapping/' . $picture_mapping->id(),
+  );
+}
+
+/**
+ * Implements hook_theme().
+ */
+function picture_theme() {
+  return array(
+    'picture' => array(
+      'variables' => array(
+        'style_name' => NULL,
+        'path' => NULL,
+        'width' => NULL,
+        'height' => NULL,
+        'alt' => '',
+        'title' => NULL,
+        'attributes' => array(),
+        'breakpoints' => array(),
+      ),
+    ),
+    'picture_formatter' => array(
+      'variables' => array(
+        'item' => NULL,
+        'path' => NULL,
+        'image_style' => NULL,
+        'breakpoints' => array(),
+      ),
+    ),
+    'picture_source' => array(
+      'variables' => array(
+        'src' => NULL,
+        'srcset' => NULL,
+        'dimension' => NULL,
+        'media' => NULL,
+      ),
+    ),
+  );
+}
+
+/**
+ * Implements hook_field_formatter_info().
+ */
+function picture_field_formatter_info() {
+  $formatters = array(
+    'picture' => array(
+      'label' => t('Picture'),
+      'field types' => array('image'),
+      'settings' => array('picture_mapping' => '', 'fallback_image_style' => '', 'image_link' => ''),
+    ),
+  );
+
+  return $formatters;
+}
+
+/**
+ * Implements hook_field_formatter_settings_form().
+ */
+function picture_field_formatter_settings_form($field, $instance, $view_mode, $form, &$form_state) {
+  $display = $instance['display'][$view_mode];
+  $settings = $display['settings'];
+
+  $picture_options = array();
+  $picture_mappings = picture_mapping_load_all();
+  if ($picture_mappings && !empty($picture_mappings)) {
+    foreach ($picture_mappings as $machine_name => $picture_mapping) {
+      $picture_options[$machine_name] = $picture_mapping->label();
+    }
+  }
+
+  $element['picture_mapping'] = array(
+    '#title' => t('Picture mapping'),
+    '#type' => 'select',
+    '#default_value' => $settings['picture_mapping'],
+    '#required' => TRUE,
+    '#options' => $picture_options,
+  );
+
+  $image_styles = image_style_options(FALSE);
+  $element['fallback_image_style'] = array(
+    '#title' => t('Fallback image style'),
+    '#type' => 'select',
+    '#default_value' => $settings['fallback_image_style'],
+    '#empty_option' => t('Automatic'),
+    '#options' => $image_styles,
+  );
+
+  $link_types = array(
+    'content' => t('Content'),
+    'file' => t('File'),
+  );
+  $element['image_link'] = array(
+    '#title' => t('Link image to'),
+    '#type' => 'select',
+    '#default_value' => $settings['image_link'],
+    '#empty_option' => t('Nothing'),
+    '#options' => $link_types,
+  );
+
+  return $element;
+}
+
+/**
+ * Implements hook_field_formatter_settings_summary().
+ */
+function picture_field_formatter_settings_summary($field, $instance, $view_mode) {
+  $display = $instance['display'][$view_mode];
+  $settings = $display['settings'];
+
+  $summary = array();
+
+  $picture_mapping = picture_mapping_load($settings['picture_mapping']);
+  if ($picture_mapping) {
+    $summary[] = t('Picture mapping: @picture_mapping', array('@picture_mapping' => $picture_mapping->label()));
+  }
+  else {
+    $summary[] = t("Picture mapping doesn't exists");
+  }
+
+  $image_styles = image_style_options(FALSE);
+  unset($image_styles['']);
+  if (isset($image_styles[$settings['fallback_image_style']])) {
+    $summary[] = t('Fallback Image style: @style', array('@style' => $image_styles[$settings['fallback_image_style']]));
+  }
+  else {
+    $summary[] = t('Automatic fallback');
+  }
+
+  $link_types = array(
+    'content' => t('Linked to content'),
+    'file' => t('Linked to file'),
+  );
+  // Display this setting only if image is linked.
+  if (isset($link_types[$settings['image_link']])) {
+    $summary[] = $link_types[$settings['image_link']];
+  }
+
+  return implode('<br />', $summary);
+}
+
+/**
+ * Implements hook_field_formatter_view().
+ */
+function picture_field_formatter_view($entity_type, $entity, $field, $instance, $langcode, $items, $display) {
+  $element = array();
+  // Check if the formatter involves a link.
+  if ($display['settings']['image_link'] == 'content') {
+    $uri = $entity->uri();
+  }
+  elseif ($display['settings']['image_link'] == 'file') {
+    $link_file = TRUE;
+  }
+
+  $breakpoint_styles = array();
+  $fallback_image_style = '';
+  $picture_mapping = picture_mapping_load($display['settings']['picture_mapping']);
+  if ($picture_mapping) {
+    foreach ($picture_mapping->mappings as $breakpoint_name => $multipliers) {
+      if (!empty($multipliers)) {
+        foreach ($multipliers as $multiplier => $image_style) {
+          if (!empty($image_style)) {
+            if (empty($fallback_image_style)) {
+              $fallback_image_style = $image_style;
+            }
+            if (!isset($breakpoint_styles[$breakpoint_name])) {
+              $breakpoint_styles[$breakpoint_name] = array();
+            }
+            $breakpoint_styles[$breakpoint_name][$multiplier] = $image_style;
+          }
+        }
+      }
+    }
+  }
+
+  if (isset($display['settings']['fallback_image_style']) && !empty($display['settings']['fallback_image_style'])) {
+    $fallback_image_style = $display['settings']['fallback_image_style'];
+  }
+
+  foreach ($items as $delta => $item) {
+    if (isset($link_file)) {
+      $uri = array(
+        'path' => file_create_url($item['uri']),
+        'options' => array(),
+      );
+    }
+    $element[$delta] = array(
+      '#theme' => 'picture_formatter',
+      '#attached' => array('library' => array(
+        array('picture', 'matchmedia'),
+        array('picture', 'picturefill'),
+      )),
+      '#item' => $item,
+      '#image_style' => $fallback_image_style,
+      '#breakpoints' => $breakpoint_styles,
+      '#path' => isset($uri) ? $uri : '',
+    );
+  }
+
+  return $element;
+}
+
+function theme_picture_formatter($variables) {
+  if (!isset($variables['breakpoints']) || empty($variables['breakpoints'])) {
+    return theme('image_formatter', $variables);
+  }
+
+  $item = $variables['item'];
+
+  // Do not output an empty 'title' attribute.
+  if (isset($item['title']) && drupal_strlen($item['title']) == 0) {
+    unset($item['title']);
+  }
+
+  $item['style_name'] = $variables['image_style'];
+  $item['breakpoints'] = $variables['breakpoints'];
+
+  if (!isset($item['path']) && isset($variables['uri'])) {
+    $item['path'] = $variables['uri'];
+  }
+  $output = theme('picture', $item);
+
+  if (isset($variables['path']['path'])) {
+    $path = $variables['path']['path'];
+    $options = isset($variables['path']['options']) ? $variables['path']['options'] : array();
+    $options['html'] = TRUE;
+    $output = l($output, $path, $options);
+  }
+  return $output;
+}
+
+/**
+ * Theme a picture element.
+ */
+function theme_picture($variables) {
+  // Make sure that width and height are proper values
+  // If they exists we'll output them
+  // @see http://www.w3.org/community/respimg/2012/06/18/florians-compromise/
+  if (isset($variables['width']) && empty($variables['width'])) {
+    unset($variables['width']);
+    unset($variables['height']);
+  }
+  elseif (isset($variables['height']) && empty($variables['height'])) {
+    unset($variables['width']);
+    unset($variables['height']);
+  }
+
+  $sources = array();
+  $output = array();
+
+  // Fallback image, output as source with media query.
+  $sources[] = array(
+    'src' => image_style_url($variables['style_name'], $variables['uri']),
+    'dimensions' => picture_get_image_dimensions($variables),
+  );
+
+  // All breakpoints and multipliers.
+  foreach ($variables['breakpoints'] as $breakpoint_name => $multipliers) {
+    $breakpoint = breakpoint_load($breakpoint_name);
+    if ($breakpoint) {
+      $new_sources = array();
+      foreach ($multipliers as $multiplier => $image_style) {
+        $new_source = $variables;
+        $new_source['style_name'] = $image_style;
+        $new_source['#multiplier'] = $multiplier;
+        $new_sources[] = $new_source;
+      }
+
+      // Only one image, use src.
+      if (count($new_sources) == 1) {
+        $sources[] = array(
+          'src' => image_style_url($new_sources[0]['style_name'], $new_sources[0]['uri']),
+          'dimensions' => picture_get_image_dimensions($new_sources[0]),
+          'media' => $breakpoint->mediaQuery,
+        );
+      }
+      else {
+        // Mutliple images, use srcset.
+        $srcset = array();
+        foreach ($new_sources as $new_source) {
+          $srcset[] = image_style_url($new_source['style_name'], $new_source['uri']) . ' ' . $new_source['#multiplier'];
+        }
+        $sources[] = array(
+          'srcset' => implode(', ', $srcset),
+          'dimensions' => picture_get_image_dimensions($new_sources[0]),
+          'media' => $breakpoint->mediaQuery,
+        );
+      }
+    }
+  }
+
+  if (!empty($sources)) {
+    $attributes = array();
+    foreach (array('alt', 'title') as $key) {
+      if (isset($variables[$key])) {
+        $attributes[$key] = $variables[$key];
+      }
+    }
+    $output[] = '<picture' . new Attribute($attributes) . '>';
+
+    // add source tags to the output.
+    foreach ($sources as $source) {
+      $output[] = theme('picture_source', $source);
+    }
+
+    // output the fallback image.
+    $output[] = '<noscript>' . theme('image_style', $variables) . '</noscript>';
+    $output[] = '</picture>';
+    return implode("\n", $output);
+  }
+}
+
+function theme_picture_source($variables) {
+  $output = array();
+  if (isset($variables['media']) && !empty($variables['media'])) {
+    if (!isset($variables['srcset'])) {
+      $output[] = '<!-- <source media="' . $variables['media'] . '" src="' . $variables['src'] . '" ' . new Attribute($variables['dimensions']) . ' /> -->';
+      $output[] = '<source media="' . $variables['media'] . '" src="' . $variables['src'] . '" ' . new Attribute($variables['dimensions']) . '/>';
+    }
+    elseif (!isset($variables['src'])) {
+      $output[] = '<!-- <source media="' . $variables['media'] . '" srcset="' . $variables['srcset'] . '" ' . new Attribute($variables['dimensions']) . ' /> -->';
+      $output[] = '<source media="' . $variables['media'] . '" srcset="' . $variables['srcset'] . '" ' . new Attribute($variables['dimensions']) . ' />';
+    }
+  }
+  else {
+    $output[] = '<!-- <source src="' . $variables['src'] . '" ' . new Attribute($variables['dimensions']) . ' /> -->';
+    $output[] = '<source src="' . $variables['src'] . '" ' . new Attribute($variables['dimensions']) . '/>';
+  }
+  return implode("\n", $output);
+}
+
+function picture_get_image_dimensions($variables) {
+  // Determine the dimensions of the styled image.
+  $dimensions = array(
+    'width' => $variables['width'],
+    'height' => $variables['height'],
+  );
+
+  image_style_transform_dimensions($variables['style_name'], $dimensions);
+
+  return $dimensions;
+}
diff --git a/core/modules/picture/picture_mapping.admin.inc b/core/modules/picture/picture_mapping.admin.inc
new file mode 100644
index 0000000..a035504
--- /dev/null
+++ b/core/modules/picture/picture_mapping.admin.inc
@@ -0,0 +1,79 @@
+<?php
+
+/**
+ * @file
+ * Administration functions to maintain picture.
+ */
+
+use \Drupal\picture\PictureMapping;
+
+/**
+ * Page callback: Presents the picture list page.
+ *
+ * @see picture_menu()
+ */
+function picture_mapping_page() {
+  $controller = entity_list_controller('picture_mapping');
+  return $controller->render();
+}
+
+/**
+ * Page callback: Presents the picture editing form.
+ *
+ * @see picture_menu()
+ */
+function picture_mapping_page_edit($picture_mapping) {
+  drupal_set_title(t('<em>Edit picture mapping</em> @label', array('@label' => $picture_mapping->label())), PASS_THROUGH);
+  return entity_get_form($picture_mapping);
+}
+
+/**
+ * Page callback: Provides the new picture addition form.
+ *
+ * @see picture_menu()
+ */
+function picture_mapping_page_add() {
+  $picture_mapping = entity_create('picture_mapping', array());
+  $form = entity_get_form($picture_mapping);
+  return $form;
+}
+
+/**
+ * Page callback: Form constructor for picture action confirmation form.
+ *
+ * @see picture_menu()
+ */
+function picture_mapping_action_confirm($form, &$form_state, $picture_mapping, $action) {
+  // Always provide entity id in the same form key as in the entity edit form.
+  if (in_array($action, array('delete'))) {
+    $form['id'] = array('#type' => 'value', '#value' => $picture_mapping->id());
+    $form['action'] = array('#type' => 'value', '#value' => $action);
+    $form_state['picture_mapping'] = $picture_mapping;
+    $form = confirm_form($form,
+      t('Are you sure you want to @action the picture_mapping %title?', array('@action' => $action, '%title' => $picture_mapping->label())),
+      'admin/config/media/picturemapping',
+      $action == 'delete' ? t('This action cannot be undone.') : '',
+      t(drupal_ucfirst($action)),
+      t('Cancel')
+    );
+  }
+  return $form;
+}
+
+/**
+ * Form submission handler for picture_action_confirm().
+ */
+function picture_mapping_action_confirm_submit($form, &$form_state) {
+  $picture_mapping = $form_state['picture_mapping'];
+  $action = $form_state['values']['action'];
+  $picture_mapping->{$action}();
+  $verb = '';
+  switch ($action) {
+    case 'delete':
+      $verb = 'deleted';
+      break;
+  }
+  drupal_set_message(t('Picture mapping %label has been @action.', array('%label' => $picture_mapping->label(), '@action' => $verb)));
+  watchdog('picture', 'Picture mapping %label has been @action.', array('%label' => $picture_mapping->label(), '@action' => $verb), WATCHDOG_NOTICE);
+  $form_state['redirect'] = 'admin/config/media/picturemapping';
+}
diff --git a/core/modules/picture/picturefill/matchmedia.js b/core/modules/picture/picturefill/matchmedia.js
new file mode 100644
index 0000000..adce5ab
--- /dev/null
+++ b/core/modules/picture/picturefill/matchmedia.js
@@ -0,0 +1,2 @@
+/*! matchMedia() polyfill - Test a CSS media type/query in JS. Authors & copyright (c) 2012: Scott Jehl, Paul Irish, Nicholas Zakas. Dual MIT/BSD license */
+window.matchMedia=window.matchMedia||(function(e,f){var c,a=e.documentElement,b=a.firstElementChild||a.firstChild,d=e.createElement("body"),g=e.createElement("div");g.id="mq-test-1";g.style.cssText="position:absolute;top:-100em";d.appendChild(g);return function(h){g.innerHTML='&shy;<style media="'+h+'"> #mq-test-1 { width: 42px; }</style>';a.insertBefore(d,b);c=g.offsetWidth==42;a.removeChild(d);return{matches:c,media:h}}})(document);
\ No newline at end of file
diff --git a/core/modules/picture/picturefill/picturefill.js b/core/modules/picture/picturefill/picturefill.js
new file mode 100644
index 0000000..db80012
--- /dev/null
+++ b/core/modules/picture/picturefill/picturefill.js
@@ -0,0 +1,125 @@
+/*! Picturefill - Author: Scott Jehl, 2012 | License: MIT/GPLv2 */
+/*
+  Picturefill: A polyfill for proposed behavior of the picture element, which does not yet exist, but should. :)
+  * Notes:
+    * For active discussion of the picture element, see http://www.w3.org/community/respimg/
+    * While this code does work, it is intended to be used only for example purposes until either:
+      A) A W3C Candidate Recommendation for <picture> is released
+      B) A major browser implements <picture>
+*/
+(function( w ){
+
+  // Enable strict mode
+  "use strict";
+
+  // User preference for HD content when available
+  var prefHD = false || w.localStorage && w.localStorage[ "picturefill-prefHD" ] === "true",
+    hasHD;
+
+  // Test if `<picture>` is supported natively, if so, exit - no polyfill needed.
+  if ( !!( w.document.createElement( "picture" ) && w.document.createElement( "source" ) && w.HTMLPictureElement ) ){
+    return;
+  }
+
+  w.picturefill = function() {
+    function _copyAttributes(src, tar) {
+      if (src.getAttribute( "width" ) && src.getAttribute( "height" )) {
+        tar.width = src.getAttribute( "width" );
+        tar.height = src.getAttribute( "height" );
+      }
+    }
+
+    var ps = w.document.getElementsByTagName( "picture" );
+
+    // Loop the pictures
+    for( var i = 0, il = ps.length; i < il; i++ ){
+      var sources = ps[ i ].getElementsByTagName( "source" ),
+        picImg = null,
+        matches = [];
+
+      // If no sources are found, they're likely erased from the DOM. Try finding them inside comments.
+      if( !sources.length ){
+        var picText =  ps[ i ].innerHTML,
+          frag = w.document.createElement( "div" ),
+          // For IE9, convert the source elements to divs
+          srcs = picText.replace( /(<)source([^>]+>)/gmi, "$1div$2" ).match( /<div[^>]+>/gmi );
+
+        frag.innerHTML = srcs.join( "" );
+        sources = frag.getElementsByTagName( "div" );
+      }
+
+      // See which sources match
+      for( var j = 0, jl = sources.length; j < jl; j++ ){
+        var media = sources[ j ].getAttribute( "media" );
+        // if there's no media specified, OR w.matchMedia is supported
+        if( !media || ( w.matchMedia && w.matchMedia( media ).matches ) ){
+          matches.push( sources[ j ] );
+        }
+      }
+
+      // Find any existing img element in the picture element
+      picImg = ps[ i ].getElementsByTagName( "img" )[ 0 ];
+
+      if( matches.length ){
+        // Grab the most appropriate (last) match.
+        var match = matches.pop(),
+          srcset = match.getAttribute( "srcset" );
+
+        if( !picImg ){
+          picImg = w.document.createElement( "img" );
+          picImg.alt = ps[ i ].getAttribute( "alt" );
+          ps[ i ].appendChild( picImg );
+        }
+
+        if( srcset ) {
+            var screenRes = ( prefHD && w.devicePixelRatio ) || 1, // Is it worth looping through reasonable matchMedia values here?
+              sources = srcset.split(","); // Split comma-separated `srcset` sources into an array.
+
+            hasHD = w.devicePixelRatio > 1;
+
+            for( var res = sources.length, r = res - 1; r >= 0; r-- ) { // Loop through each source/resolution in `srcset`.
+              var source = sources[ r ].replace(/^\s*/, '').replace(/\s*$/, '').split(" "), // Remove any leading whitespace, then split on spaces.
+                resMatch = parseFloat( source[1], 10 ); // Parse out the resolution for each source in `srcset`.
+
+              if( screenRes >= resMatch ) {
+                if( picImg.getAttribute( "src" ) !== source[0] ) {
+                  var newImg = document.createElement("img");
+
+                  newImg.src = source[0];
+                  // When the image is loaded, set a width equal to that of the original’s intrinsic width divided by the screen resolution:
+                  newImg.onload = function() {
+                    // Clone the original image into memory so the width is unaffected by page styles:
+                    var w = this.cloneNode( true ).width;
+                    if (w > 0) {
+                      this.width = ( w / resMatch );
+                    }
+                  }
+                  _copyAttributes(match, newImg);
+                  picImg.parentNode.replaceChild( newImg, picImg );
+                }
+                break; // We’ve matched, so bail out of the loop here.
+              }
+            }
+        } else {
+          // No `srcset` in play, so just use the `src` value:
+          picImg.src = match.getAttribute( "src" );
+          _copyAttributes(match, picImg);
+        }
+      }
+    }
+  };
+
+  // Run on resize and domready (w.load as a fallback)
+  if( w.addEventListener ){
+    w.addEventListener( "resize", w.picturefill, false );
+    w.addEventListener( "DOMContentLoaded", function(){
+      w.picturefill();
+      // Run once only
+      w.removeEventListener( "load", w.picturefill, false );
+    }, false );
+    w.addEventListener( "load", w.picturefill, false );
+  }
+  else if( w.attachEvent ){
+    w.attachEvent( "onload", w.picturefill );
+  }
+})( this );
\ No newline at end of file
diff --git a/core/themes/bartik/config/bartik.breakpoints.yml b/core/themes/bartik/config/bartik.breakpoints.yml
index 9eb9cb2..501f417 100644
--- a/core/themes/bartik/config/bartik.breakpoints.yml
+++ b/core/themes/bartik/config/bartik.breakpoints.yml
@@ -1,3 +1,3 @@
 mobile: '(min-width: 0px)'
-narrow: 'all and (min-width: 560px) and (max-width:850px)'
+narrow: 'all and (min-width: 560px) and (max-width: 850px)'
 wide: 'all and (min-width: 851px)'
diff --git a/core/themes/bartik/css/layout.css b/core/themes/bartik/css/layout.css
index fd69c49..4817eef 100644
--- a/core/themes/bartik/css/layout.css
+++ b/core/themes/bartik/css/layout.css
@@ -71,7 +71,7 @@ body,
   padding: 0 10px;
 }
 
-@media all and (min-width: 560px) and (max-width:850px) {
+@media all and (min-width: 560px) and (max-width: 850px) {
 
   #sidebar-first,
   .region-triptych-first,
