diff --git a/core/modules/block/block.links.action.yml b/core/modules/block/block.links.action.yml
new file mode 100644
index 0000000..0f9821d
--- /dev/null
+++ b/core/modules/block/block.links.action.yml
@@ -0,0 +1,7 @@
+block_region_demo:
+  route_name: block.admin_demo
+  title: 'Demonstrate block regions'
+  appears_on:
+    - block.admin_display
+    - block.admin_display_theme
+  class: \Drupal\block\Plugin\Menu\LocalAction\BlockDemoLocalAction
diff --git a/core/modules/block/block.module b/core/modules/block/block.module
index 7862e66..5fa58f4 100644
--- a/core/modules/block/block.module
+++ b/core/modules/block/block.module
@@ -39,13 +39,11 @@ function block_help($route_name, RouteMatchInterface $route_match) {
       $output .= '<dd>' . t('You can add custom blocks, if the <em>Custom Block</em> module is installed. For more information, see the <a href="!blockcontent-help">Custom Block help page</a>.', array('!blockcontent-help' => $block_content)) . '</dd>';
       $output .= '</dl>';
       return $output;
-  }
-  if ($route_name == 'block.admin_display' || $route_name == 'block.admin_display_theme') {
-    $demo_theme = $route_match->getParameter('theme') ?: \Drupal::config('system.theme')->get('default');
-    $themes = \Drupal::service('theme_handler')->listInfo();
-    $output = '<p>' . t('This page provides a drag-and-drop interface for adding a block to a region, and for controlling the order of blocks within regions. To add a block to a region, or to configure its specific title and visibility settings, click the block title under <em>Place blocks</em>. Since not all themes implement the same regions, or display regions in the same way, blocks are positioned on a per-theme basis. Remember that your changes will not be saved until you click the <em>Save blocks</em> button at the bottom of the page.') . '</p>';
-    $output .= '<p>' . \Drupal::l(t('Demonstrate block regions (@theme)', array('@theme' => $themes[$demo_theme]->info['name'])), new Url('block.admin_demo', array('theme' => $demo_theme))) . '</p>';
-    return $output;
+
+    case 'block.admin_display':
+    case 'block.admin_display_theme':
+      $output = '<p>' . t('This page provides a drag-and-drop interface for adding a block to a region, and for controlling the order of blocks within regions. To add a block to a region, or to configure its specific title and visibility settings, click the block title under <em>Place blocks</em>. Since not all themes implement the same regions, or display regions in the same way, blocks are positioned on a per-theme basis. Remember that your changes will not be saved until you click the <em>Save blocks</em> button at the bottom of the page.') . '</p>';
+      return $output;
   }
 }
 
diff --git a/core/modules/block/src/Plugin/Menu/LocalAction/BlockDemoLocalAction.php b/core/modules/block/src/Plugin/Menu/LocalAction/BlockDemoLocalAction.php
new file mode 100644
index 0000000..81fd1c8
--- /dev/null
+++ b/core/modules/block/src/Plugin/Menu/LocalAction/BlockDemoLocalAction.php
@@ -0,0 +1,107 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\block\Plugin\Menu\LocalAction\BlockDemoLocalAction.
+ */
+
+namespace Drupal\block\Plugin\Menu\LocalAction;
+
+use Drupal\Core\Config\ConfigFactoryInterface;
+use Drupal\Core\Extension\ThemeHandlerInterface;
+use Drupal\Core\Menu\LocalActionDefault;
+use Drupal\Core\Routing\RouteMatchInterface;
+use Drupal\Core\Routing\RouteProviderInterface;
+use Drupal\Core\StringTranslation\StringTranslationTrait;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+
+/**
+ * Modifies the "Demonstrate block regions" local action.
+ */
+class BlockDemoLocalAction extends LocalActionDefault {
+
+  use StringTranslationTrait;
+
+  /**
+   * The system theme configuration.
+   *
+   * @var \Drupal\Core\Config\ImmutableConfig
+   */
+  protected $config;
+
+  /**
+   * The theme handler.
+   *
+   * @var \Drupal\Core\Extension\ThemeHandlerInterface
+   */
+  protected $themeHandler;
+
+  /**
+   * Constructs a BlockDemoLocalAction object.
+   *
+   * @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\Routing\RouteProviderInterface $route_provider
+   *   The route provider to load routes by name.
+   * @param \Drupal\Core\Extension\ThemeHandlerInterface $theme_handler
+   *   The theme handler used to get a list of available themes.
+   * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
+   *   The configuration object factory used to get configuration data.
+   */
+  public function __construct(array $configuration, $plugin_id, $plugin_definition, RouteProviderInterface $route_provider, ThemeHandlerInterface $theme_handler, ConfigFactoryInterface $config_factory) {
+    parent::__construct($configuration, $plugin_id, $plugin_definition, $route_provider);
+    $this->themeHandler = $theme_handler;
+    $this->config = $config_factory->get('system.theme');
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
+    return new static(
+      $configuration,
+      $plugin_id,
+      $plugin_definition,
+      $container->get('router.route_provider'),
+      $container->get('theme_handler'),
+      $container->get('config.factory')
+    );
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getRouteParameters(RouteMatchInterface $route_match) {
+    $parameters = parent::getRouteParameters($route_match);
+    if (!isset($parameters['theme'])) {
+      $parameters['theme'] = $this->config->get('default');
+    }
+    return $parameters;
+  }
+
+  /**
+   * Adds the theme name to the action title.
+   *
+   * The ControllerResolver provides this method with arguments from the route.
+   *
+   * @param string $theme
+   *   (optional) The theme parameter from the route.
+   *
+   * @return string
+   *   The title to be shown for this action.
+   *
+   * @see \Drupal\Core\Menu\LocalActionManager::getTitle()
+   */
+  public function getTitle($theme = NULL) {
+    if (empty($theme)) {
+      $theme = $this->config->get('default');
+    }
+    $themes = $this->themeHandler->listInfo();
+    return $this->t('Demonstrate block regions (@theme)', ['@theme' => $themes[$theme]->info['name']]);
+  }
+
+}
diff --git a/core/modules/block/src/Tests/BlockUiTest.php b/core/modules/block/src/Tests/BlockUiTest.php
index 37fce6e..6140330 100644
--- a/core/modules/block/src/Tests/BlockUiTest.php
+++ b/core/modules/block/src/Tests/BlockUiTest.php
@@ -81,9 +81,11 @@ protected function setUp() {
    * Test block demo page exists and functions correctly.
    */
   public function testBlockDemoUiPage() {
-    $this->drupalPlaceBlock('help_block', array('region' => 'help'));
     $this->drupalGet('admin/structure/block');
+    // The demo link should be available even if the help module is disabled.
+    $this->assertFalse($this->xpath('//div[contains(@class, "region-help")]'), 'Help block is not shown.');
     $this->clickLink(t('Demonstrate block regions (@theme)', array('@theme' => 'Classy')));
+    $this->assertText(t('Classy'), 'Followed link to correct demonstration page.');
     $elements = $this->xpath('//div[contains(@class, "region-highlighted")]/div[contains(@class, "block-region") and contains(text(), :title)]', array(':title' => 'Highlighted'));
     $this->assertTrue(!empty($elements), 'Block demo regions are shown.');
   }
