diff --git a/src/Form/AjaxFormTrait.php b/src/Form/AjaxFormTrait.php
new file mode 100644
index 0000000000000000000000000000000000000000..4c322eeec5ebb20ad1fddf8718a6de1d8e086058
--- /dev/null
+++ b/src/Form/AjaxFormTrait.php
@@ -0,0 +1,48 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\ctools\Form\AjaxFormTrait.
+ */
+
+namespace Drupal\ctools\Form;
+
+use Drupal\Component\Serialization\Json;
+use Drupal\Component\Utility\NestedArray;
+
+/**
+ * Provides helper methods for using an AJAX modal.
+ */
+trait AjaxFormTrait {
+
+  /**
+   * Gets attributes for use with an AJAX modal.
+   *
+   * @return array
+   */
+  protected function getAjaxAttributes() {
+    return [
+      'class' => ['use-ajax'],
+      'data-dialog-type' => 'modal',
+      'data-dialog-options' => Json::encode([
+        'width' => 'auto',
+      ]),
+    ];
+  }
+
+  /**
+   * Gets attributes for use with an add button AJAX modal.
+   *
+   * @return array
+   */
+  protected function getAjaxButtonAttributes() {
+    return NestedArray::mergeDeep($this->getAjaxAttributes(), [
+      'class' => [
+        'button',
+        'button--small',
+        'button-action',
+      ],
+    ]);
+  }
+
+}
diff --git a/src/Plugin/BlockPluginCollection.php b/src/Plugin/BlockPluginCollection.php
new file mode 100644
index 0000000000000000000000000000000000000000..1d3dd0f6f672cb55fa077d27de388093d79b70e3
--- /dev/null
+++ b/src/Plugin/BlockPluginCollection.php
@@ -0,0 +1,58 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\ctools\Plugin\BlockPluginCollection.
+ */
+
+namespace Drupal\ctools\Plugin;
+
+use Drupal\Core\Block\BlockPluginInterface;
+use Drupal\Core\Plugin\DefaultLazyPluginCollection;
+
+/**
+ * Provides a collection of block plugins.
+ */
+class BlockPluginCollection extends DefaultLazyPluginCollection {
+
+  /**
+   * {@inheritdoc}
+   *
+   * @return \Drupal\Core\Block\BlockPluginInterface
+   */
+  public function &get($instance_id) {
+    return parent::get($instance_id);
+  }
+
+  /**
+   * Returns all blocks keyed by their region.
+   *
+   * @return array
+   *   An associative array keyed by region, containing an associative array of
+   *   block plugins.
+   */
+  public function getAllByRegion() {
+    $region_assignments = [];
+    foreach ($this as $block_id => $block) {
+      $configuration = $block->getConfiguration();
+      $region = isset($configuration['region']) ? $configuration['region'] : NULL;
+      $region_assignments[$region][$block_id] = $block;
+    }
+    foreach ($region_assignments as $region => $region_assignment) {
+      // @todo Determine the reason this needs error suppression.
+      @uasort($region_assignment, function (BlockPluginInterface $a, BlockPluginInterface $b) {
+        $a_config = $a->getConfiguration();
+        $a_weight = isset($a_config['weight']) ? $a_config['weight'] : 0;
+        $b_config = $b->getConfiguration();
+        $b_weight = isset($b_config['weight']) ? $b_config['weight'] : 0;
+        if ($a_weight == $b_weight) {
+          return strcmp($a->label(), $b->label());
+        }
+        return $a_weight > $b_weight ? 1 : -1;
+      });
+      $region_assignments[$region] = $region_assignment;
+    }
+    return $region_assignments;
+  }
+
+}
diff --git a/src/Plugin/BlockVariantInterface.php b/src/Plugin/BlockVariantInterface.php
new file mode 100644
index 0000000000000000000000000000000000000000..bb745ee32b5e3dbbc01bf0cd52a324abb806d9f5
--- /dev/null
+++ b/src/Plugin/BlockVariantInterface.php
@@ -0,0 +1,101 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\ctools\Plugin\BlockVariantInterface.
+ */
+
+namespace Drupal\ctools\Plugin;
+
+use Drupal\Core\Display\VariantInterface;
+
+/**
+ * Provides an interface for variant plugins that use block plugins.
+ */
+interface BlockVariantInterface extends VariantInterface {
+
+  /**
+   * Returns the human-readable list of regions keyed by machine name.
+   *
+   * @return array
+   *   An array of human-readable region names keyed by machine name.
+   */
+  public function getRegionNames();
+
+  /**
+   * Returns the human-readable name of a specific region.
+   *
+   * @param string $region
+   *   The machine name of a region.
+   *
+   * @return string
+   *   The human-readable name of a region.
+   */
+  public function getRegionName($region);
+
+  /**
+   * Adds a block to this display variant.
+   *
+   * @param array $configuration
+   *   An array of block configuration.
+   *
+   * @return string
+   *   The block ID.
+   */
+  public function addBlock(array $configuration);
+
+  /**
+   * Returns the region a specific block is assigned to.
+   *
+   * @param string $block_id
+   *   The block ID.
+   *
+   * @return string
+   *   The machine name of the region this block is assigned to.
+   */
+  public function getRegionAssignment($block_id);
+
+  /**
+   * Returns an array of regions and their block plugins.
+   *
+   * @return array
+   *   The array is first keyed by region machine name, with the values
+   *   containing an array keyed by block ID, with block plugin instances as the
+   *   values.
+   */
+  public function getRegionAssignments();
+
+  /**
+   * Returns a specific block plugin.
+   *
+   * @param string $block_id
+   *   The block ID.
+   *
+   * @return \Drupal\Core\Block\BlockPluginInterface
+   *   The block plugin.
+   */
+  public function getBlock($block_id);
+
+  /**
+   * Updates the configuration of a specific block plugin.
+   *
+   * @param string $block_id
+   *   The block ID.
+   * @param array $configuration
+   *   The array of configuration to set.
+   *
+   * @return $this
+   */
+  public function updateBlock($block_id, array $configuration);
+
+  /**
+   * Removes a specific block from this display variant.
+   *
+   * @param string $block_id
+   *   The block ID.
+   *
+   * @return $this
+   */
+  public function removeBlock($block_id);
+
+}
diff --git a/src/Plugin/BlockVariantTrait.php b/src/Plugin/BlockVariantTrait.php
new file mode 100644
index 0000000000000000000000000000000000000000..70dc32902afa893b1477bc63064afe724fed610f
--- /dev/null
+++ b/src/Plugin/BlockVariantTrait.php
@@ -0,0 +1,120 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\ctools\Plugin\BlockVariantTrait.
+ */
+
+namespace Drupal\ctools\Plugin;
+
+/**
+ * Provides methods for \Drupal\ctools\Plugin\BlockVariantInterface.
+ */
+trait BlockVariantTrait {
+
+  /**
+   * The plugin collection that holds the block plugins.
+   *
+   * @var \Drupal\ctools\Plugin\BlockPluginCollection
+   */
+  protected $blockPluginCollection;
+
+  /**
+   * @see \Drupal\ctools\Plugin\BlockVariantInterface::getRegionNames()
+   */
+  public function getRegionNames() {
+    return [
+      'top' => 'Top',
+      'bottom' => 'Bottom',
+    ];
+  }
+
+  /**
+   * @see \Drupal\ctools\Plugin\BlockVariantInterface::getBlock()
+   */
+  public function getBlock($block_id) {
+    return $this->getBlockCollection()->get($block_id);
+  }
+
+  /**
+   * @see \Drupal\ctools\Plugin\BlockVariantInterface::addBlock()
+   */
+  public function addBlock(array $configuration) {
+    $configuration['uuid'] = $this->uuidGenerator()->generate();
+    $this->getBlockCollection()->addInstanceId($configuration['uuid'], $configuration);
+    return $configuration['uuid'];
+  }
+
+  /**
+   * @see \Drupal\ctools\Plugin\BlockVariantInterface::removeBlock()
+   */
+  public function removeBlock($block_id) {
+    $this->getBlockCollection()->removeInstanceId($block_id);
+    return $this;
+  }
+
+  /**
+   * @see \Drupal\ctools\Plugin\BlockVariantInterface::updateBlock()
+   */
+  public function updateBlock($block_id, array $configuration) {
+    $existing_configuration = $this->getBlock($block_id)->getConfiguration();
+    $this->getBlockCollection()->setInstanceConfiguration($block_id, $configuration + $existing_configuration);
+    return $this;
+  }
+
+  /**
+   * @see \Drupal\ctools\Plugin\BlockVariantInterface::getRegionAssignment()
+   */
+  public function getRegionAssignment($block_id) {
+    $configuration = $this->getBlock($block_id)->getConfiguration();
+    return isset($configuration['region']) ? $configuration['region'] : NULL;
+  }
+
+  /**
+   * @see \Drupal\ctools\Plugin\BlockVariantInterface::getRegionAssignments()
+   */
+  public function getRegionAssignments() {
+    // Build an array of the region names in the right order.
+    $empty = array_fill_keys(array_keys($this->getRegionNames()), []);
+    $full = $this->getBlockCollection()->getAllByRegion();
+    // Merge it with the actual values to maintain the ordering.
+    return array_intersect_key(array_merge($empty, $full), $empty);
+  }
+
+  /**
+   * @see \Drupal\ctools\Plugin\BlockVariantInterface::getRegionName()
+   */
+  public function getRegionName($region) {
+    $regions = $this->getRegionNames();
+    return isset($regions[$region]) ? $regions[$region] : '';
+  }
+
+  /**
+   * Returns the block plugins used for this display variant.
+   *
+   * @return \Drupal\Core\Block\BlockPluginInterface[]|\Drupal\ctools\Plugin\BlockPluginCollection
+   *   An array or collection of configured block plugins.
+   */
+  protected function getBlockCollection() {
+    if (!$this->blockPluginCollection) {
+      $this->blockPluginCollection = new BlockPluginCollection(\Drupal::service('plugin.manager.block'), $this->getBlockConfig());
+    }
+    return $this->blockPluginCollection;
+  }
+
+  /**
+   * Returns the UUID generator.
+   *
+   * @return \Drupal\Component\Uuid\UuidInterface
+   */
+  abstract protected function uuidGenerator();
+
+  /**
+   * Returns the configuration for stored blocks.
+   *
+   * @return array
+   *   An array of block configuration, keyed by the unique block ID.
+   */
+  abstract protected function getBlockConfig();
+
+}
diff --git a/src/Plugin/ConditionVariantInterface.php b/src/Plugin/ConditionVariantInterface.php
new file mode 100644
index 0000000000000000000000000000000000000000..2d592013d04129753f87279ce01d0cd5bac271da
--- /dev/null
+++ b/src/Plugin/ConditionVariantInterface.php
@@ -0,0 +1,92 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\ctools\Plugin\ConditionVariantInterface.
+ */
+
+namespace Drupal\ctools\Plugin;
+
+use Drupal\Core\Display\VariantInterface;
+
+/**
+ * Provides an interface for variant plugins that use condition plugins.
+ */
+interface ConditionVariantInterface extends VariantInterface {
+
+  /**
+   * Gets the values for all defined contexts.
+   *
+   * @return \Drupal\Component\Plugin\Context\ContextInterface[]
+   *   An array of set contexts, keyed by context name.
+   */
+  public function getContexts();
+
+  /**
+   * Returns the conditions used for determining if this variant is selected.
+   *
+   * @return \Drupal\Core\Condition\ConditionInterface[]|\Drupal\Core\Condition\ConditionPluginCollection
+   *   An array of configured condition plugins.
+   */
+  public function getSelectionConditions();
+
+  /**
+   * Removes a specific selection condition.
+   *
+   * @param string $condition_id
+   *   The selection condition ID.
+   *
+   * @return $this
+   */
+  public function removeSelectionCondition($condition_id);
+
+  /**
+   * Adds a new selection condition to the page entity.
+   *
+   * @param array $configuration
+   *   An array of configuration for the new selection condition.
+   *
+   * @return string
+   *   The selection condition ID.
+   */
+  public function addSelectionCondition(array $configuration);
+
+  /**
+   * Returns the logic used to compute selections, either 'and' or 'or'.
+   *
+   * @return string
+   *   The string 'and', or the string 'or'.
+   */
+  public function getSelectionLogic();
+
+  /**
+   * Returns the definition of the plugin implementation.
+   *
+   * @return array
+   *   The plugin definition, as returned by the discovery object used by the
+   *   plugin manager.
+   */
+  public function getPluginDefinition();
+
+  /**
+   * Sets the context values for this display variant.
+   *
+   * @param \Drupal\Component\Plugin\Context\ContextInterface[] $contexts
+   *   An array of contexts, keyed by context name.
+   *
+   * @return $this
+   */
+  public function setContexts(array $contexts);
+
+  /**
+   * Retrieves a specific selection condition.
+   *
+   * @param string $condition_id
+   *   The selection condition ID.
+   *
+   * @return \Drupal\Core\Condition\ConditionInterface
+   *   The selection condition object.
+   */
+  public function getSelectionCondition($condition_id);
+
+}
diff --git a/src/Plugin/ConditionVariantTrait.php b/src/Plugin/ConditionVariantTrait.php
new file mode 100644
index 0000000000000000000000000000000000000000..ba1be95de3b7fc4e0c705eac0d09ad5f78a97346
--- /dev/null
+++ b/src/Plugin/ConditionVariantTrait.php
@@ -0,0 +1,108 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\ctools\Plugin\ConditionVariantTrait.
+ */
+
+namespace Drupal\ctools\Plugin;
+
+use Drupal\Core\Condition\ConditionAccessResolverTrait;
+use Drupal\Core\Condition\ConditionPluginCollection;
+use Drupal\Core\Plugin\ContextAwarePluginInterface;
+
+/**
+ * Provides methods for \Drupal\ctools\Plugin\ConditionVariantInterface.
+ */
+trait ConditionVariantTrait {
+
+  use ConditionAccessResolverTrait;
+
+  /**
+   * The plugin collection that holds the selection condition plugins.
+   *
+   * @var \Drupal\Component\Plugin\LazyPluginCollection
+   */
+  protected $selectionConditionCollection;
+
+  /**
+   * @see \Drupal\ctools\Plugin\ConditionVariantInterface::getSelectionConditions()
+   */
+  public function getSelectionConditions() {
+    if (!$this->selectionConditionCollection) {
+      $this->selectionConditionCollection = new ConditionPluginCollection(\Drupal::service('plugin.manager.condition'), $this->getSelectionConfiguration());
+    }
+    return $this->selectionConditionCollection;
+  }
+
+  /**
+   * @see \Drupal\ctools\Plugin\ConditionVariantInterface::addSelectionCondition()
+   */
+  public function addSelectionCondition(array $configuration) {
+    $configuration['uuid'] = $this->uuidGenerator()->generate();
+    $this->getSelectionConditions()->addInstanceId($configuration['uuid'], $configuration);
+    return $configuration['uuid'];
+  }
+
+  /**
+   * @see \Drupal\ctools\Plugin\ConditionVariantInterface::getSelectionCondition()
+   */
+  public function getSelectionCondition($condition_id) {
+    return $this->getSelectionConditions()->get($condition_id);
+  }
+
+  /**
+   * @see \Drupal\ctools\Plugin\ConditionVariantInterface::removeSelectionCondition()
+   */
+  public function removeSelectionCondition($condition_id) {
+    $this->getSelectionConditions()->removeInstanceId($condition_id);
+    return $this;
+  }
+
+  /**
+   * Determines if the selection conditions will pass given a set of contexts.
+   *
+   * @param \Drupal\Component\Plugin\Context\ContextInterface[] $contexts
+   *   An array of set contexts, keyed by context name.
+   *
+   * @return bool
+   *   TRUE if access is granted, FALSE otherwise.
+   */
+  protected function determineSelectionAccess(array $contexts) {
+    $conditions = $this->getSelectionConditions();
+    foreach ($conditions as $condition) {
+      if ($condition instanceof ContextAwarePluginInterface) {
+        $this->contextHandler()->applyContextMapping($condition, $contexts);
+      }
+    }
+    return $this->resolveConditions($conditions, $this->getSelectionLogic());
+  }
+
+  /**
+   * @see \Drupal\ctools\Plugin\ConditionVariantInterface::getSelectionLogic()
+   */
+  abstract public function getSelectionLogic();
+
+  /**
+   * Returns the configuration for stored selection conditions.
+   *
+   * @return array
+   *   An array of condition configuration, keyed by the unique condition ID.
+   */
+  abstract protected function getSelectionConfiguration();
+
+  /**
+   * Returns the UUID generator.
+   *
+   * @return \Drupal\Component\Uuid\UuidInterface
+   */
+  abstract protected function uuidGenerator();
+
+  /**
+   * Returns the context handler.
+   *
+   * @return \Drupal\Core\Plugin\Context\ContextHandlerInterface
+   */
+  abstract protected function contextHandler();
+
+}
diff --git a/src/Plugin/ContextAwareVariantInterface.php b/src/Plugin/ContextAwareVariantInterface.php
new file mode 100644
index 0000000000000000000000000000000000000000..951dceaa300614a2f478ebbfc1760ebcf8e45bde
--- /dev/null
+++ b/src/Plugin/ContextAwareVariantInterface.php
@@ -0,0 +1,33 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\ctools\Plugin\ContextAwareVariantInterface.
+ */
+
+namespace Drupal\ctools\Plugin;
+
+/**
+ * Provides an interface for variant plugins that are context-aware.
+ */
+interface ContextAwareVariantInterface {
+
+  /**
+   * Gets the values for all defined contexts.
+   *
+   * @return \Drupal\Component\Plugin\Context\ContextInterface[]
+   *   An array of set contexts, keyed by context name.
+   */
+  public function getContexts();
+
+  /**
+   * Sets the context values for this display variant.
+   *
+   * @param \Drupal\Component\Plugin\Context\ContextInterface[] $contexts
+   *   An array of contexts, keyed by context name.
+   *
+   * @return $this
+   */
+  public function setContexts(array $contexts);
+
+}
diff --git a/src/Plugin/ContextAwareVariantTrait.php b/src/Plugin/ContextAwareVariantTrait.php
new file mode 100644
index 0000000000000000000000000000000000000000..f2578de961d9ef215b72bd7bf93e30da0e59e600
--- /dev/null
+++ b/src/Plugin/ContextAwareVariantTrait.php
@@ -0,0 +1,41 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\ctools\Plugin\ContextAwareVariantTrait.
+ */
+
+namespace Drupal\ctools\Plugin;
+
+/**
+ * Provides methods for \Drupal\ctools\Plugin\ContextAwareVariantInterface.
+ */
+trait ContextAwareVariantTrait {
+
+  /**
+   * An array of collected contexts.
+   *
+   * This is only used on runtime, and is not stored.
+   *
+   * @var \Drupal\Component\Plugin\Context\ContextInterface[]
+   */
+  protected $contexts = [];
+
+  /**
+   * {@inheritdoc}
+   *
+   * @see \Drupal\ctools\Plugin\ContextAwareVariantInterface
+   */
+  public function getContexts() {
+    return $this->contexts;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function setContexts(array $contexts) {
+    $this->contexts = $contexts;
+    return $this;
+  }
+
+}
diff --git a/src/Plugin/DisplayVariant/BlockDisplayVariant.php b/src/Plugin/DisplayVariant/BlockDisplayVariant.php
new file mode 100644
index 0000000000000000000000000000000000000000..e0fd3d31f33ea5b915a755c3b718713317f3d0cf
--- /dev/null
+++ b/src/Plugin/DisplayVariant/BlockDisplayVariant.php
@@ -0,0 +1,217 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\ctools\Plugin\DisplayVariant\BlockDisplayVariant.
+ */
+
+namespace Drupal\ctools\Plugin\DisplayVariant;
+
+use Drupal\Component\Utility\Html;
+use Drupal\Component\Uuid\UuidInterface;
+use Drupal\Core\Cache\Cache;
+use Drupal\Core\Cache\CacheableMetadata;
+use Drupal\Core\Cache\RefinableCacheableDependencyInterface;
+use Drupal\Core\Cache\RefinableCacheableDependencyTrait;
+use Drupal\Core\Display\VariantBase;
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
+use Drupal\Core\Plugin\ContextAwarePluginInterface;
+use Drupal\Core\Plugin\Context\ContextHandlerInterface;
+use Drupal\Core\Render\Element;
+use Drupal\Core\Session\AccountInterface;
+use Drupal\Core\Utility\Token;
+use Drupal\ctools\Form\AjaxFormTrait;
+use Drupal\ctools\Plugin\BlockVariantInterface;
+use Drupal\ctools\Plugin\BlockVariantTrait;
+use Drupal\ctools\Plugin\ConditionVariantInterface;
+use Drupal\ctools\Plugin\ConditionVariantTrait;
+use Drupal\ctools\Plugin\ContextAwareVariantInterface;
+use Drupal\ctools\Plugin\ContextAwareVariantTrait;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+
+/**
+ * Provides a display variant that simply contains blocks.
+ *
+ * @DisplayVariant(
+ *   id = "block_display",
+ *   admin_label = @Translation("Block page")
+ * )
+ */
+abstract class BlockDisplayVariant extends VariantBase implements ContextAwareVariantInterface, ConditionVariantInterface, ContainerFactoryPluginInterface, BlockVariantInterface, RefinableCacheableDependencyInterface {
+
+  use AjaxFormTrait;
+  use BlockVariantTrait;
+  use ContextAwareVariantTrait;
+  use ConditionVariantTrait;
+  use RefinableCacheableDependencyTrait;
+
+  /**
+   * The context handler.
+   *
+   * @var \Drupal\Core\Plugin\Context\ContextHandlerInterface
+   */
+  protected $contextHandler;
+
+  /**
+   * The UUID generator.
+   *
+   * @var \Drupal\Component\Uuid\UuidInterface
+   */
+  protected $uuidGenerator;
+
+  /**
+   * The current user.
+   *
+   * @var \Drupal\Core\Session\AccountInterface
+   */
+  protected $account;
+
+  /**
+   * The token service.
+   *
+   * @var \Drupal\Core\Utility\Token
+   */
+  protected $token;
+
+  /**
+   * Constructs a new BlockDisplayVariant.
+   *
+   * @param array $configuration
+   *   A configuration array containing information about the plugin instance.
+   * @param string $plugin_id
+   *   The plugin ID for the plugin instance.
+   * @param mixed $plugin_definition
+   *   The plugin implementation definition.
+   * @param \Drupal\Core\Plugin\Context\ContextHandlerInterface $context_handler
+   *   The context handler.
+   * @param \Drupal\Core\Session\AccountInterface $account
+   *   The current user.
+   * @param \Drupal\Component\Uuid\UuidInterface $uuid_generator
+   *   The UUID generator.
+   * @param \Drupal\Core\Utility\Token $token
+   *   The token service.
+   */
+  public function __construct(array $configuration, $plugin_id, $plugin_definition, ContextHandlerInterface $context_handler, AccountInterface $account, UuidInterface $uuid_generator, Token $token) {
+    parent::__construct($configuration, $plugin_id, $plugin_definition);
+
+    $this->contextHandler = $context_handler;
+    $this->account = $account;
+    $this->uuidGenerator = $uuid_generator;
+    $this->token = $token;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
+    return new static(
+      $configuration,
+      $plugin_id,
+      $plugin_definition,
+      $container->get('context.handler'),
+      $container->get('current_user'),
+      $container->get('uuid'),
+      $container->get('token')
+    );
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function build() {
+    // Set default page cache keys that include the display.
+    $build['#cache']['keys'] = [
+      'ctools_block_display',
+      // The UUID of this display.
+      // @todo should have an API for this?
+      $this->configuration['uuid'],
+    ];
+    $build['#pre_render'][] = [$this, 'buildRegions'];
+    return $build;
+  }
+
+  /**
+   * #pre_render callback for building the regions.
+   */
+  abstract public function buildRegions(array $build);
+
+  /**
+   * {@inheritdoc}
+   */
+  public function access(AccountInterface $account = NULL) {
+    // Delegate to the conditions.
+    return $this->determineSelectionAccess($this->getContexts());
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function defaultConfiguration() {
+    return parent::defaultConfiguration() + [
+      'blocks' => [],
+      'selection_conditions' => [],
+      'selection_logic' => 'and',
+      'page_title' => '',
+    ];
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function calculateDependencies() {
+    foreach ($this->getBlockCollection() as $instance) {
+      $this->calculatePluginDependencies($instance);
+    }
+    foreach ($this->getSelectionConditions() as $instance) {
+      $this->calculatePluginDependencies($instance);
+    }
+    return $this->dependencies;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getConfiguration() {
+    return [
+      'selection_conditions' => $this->getSelectionConditions()->getConfiguration(),
+      'blocks' => $this->getBlockCollection()->getConfiguration(),
+    ] + parent::getConfiguration();
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getSelectionLogic() {
+    return $this->configuration['selection_logic'];
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function contextHandler() {
+    return $this->contextHandler;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function getSelectionConfiguration() {
+    return $this->configuration['selection_conditions'];
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function getBlockConfig() {
+    return $this->configuration['blocks'];
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function uuidGenerator() {
+    return $this->uuidGenerator;
+  }
+
+}
