diff --git a/core/includes/theme.inc b/core/includes/theme.inc
index 03d0df0..8507cc5 100644
--- a/core/includes/theme.inc
+++ b/core/includes/theme.inc
@@ -1283,8 +1283,17 @@ function template_preprocess_image(&$variables) {
   $variables['attributes']['src'] = file_create_url($variables['uri']);
 
   foreach (array('width', 'height', 'alt', 'title') as $key) {
-    if (isset($variables[$key])) {
-      $variables['attributes'][$key] = $variables[$key];
+    // At least 'alt' defaults to an empty string; allow to omit an attribute by
+    // overriding the variable to NULL.
+    if (array_key_exists($key, $variables)) {
+      // If there is a value for the variable, it overrides the attribute.
+      if (isset($variables[$key])) {
+        $attributes[$key] = $variables[$key];
+      }
+      // Otherwise, remove the attribute.
+      else {
+        unset($attributes[$key]);
+      }
     }
   }
 }
@@ -1772,9 +1781,37 @@ function template_preprocess_feed_icon(&$variables) {
  *   An associative array containing:
  *   - url: The URL of the main page.
  *   - title: A descriptive verb for the link, like 'Read more'.
+  *   - options: A l()-compatible options array. Extra subkey:
+ *     - visually-hidden: boolean for screen readers. Will set HTML option.
+ *
+ * The 'title' value, if not empty, overrides any 'title' attribute in the
+ * 'options' array.
  */
 function theme_more_link($variables) {
-  return '<div class="more-link">' . l(t('More'), $variables['url'], array('attributes' => array('title' => $variables['title']))) . '</div>';
+  $options = (isset($variables['options']) && !empty($variables['options'])) ? $variables['options'] : array();
+
+  // Option visually-hidden needs to force HTML mode.
+  if (!empty($variables['visually-hidden'])) {
+    // If HTML option was not set by caller,
+    // title can not be assumed to be safe.
+    if (empty($options['html'])) {
+      $options['html'] = TRUE;
+      $variables['title'] = check_plain($variables['title']);
+    }
+    $invisible = '<span class="visually-hidden">' . $variables['title']  . '</span>';
+  }
+  else {
+    $invisible = NULL;
+  }
+
+  if (!empty($variables['title'])) {
+    $options['attributes']['title'] = $variables['title'];
+  }
+  else {
+    $variables['title'] = t('More');
+  }
+
+  return '<div class="more-link">' . l($variables['title'] . $invisible, $variables['url'], $options) . '</div>';
 }
 
 /**
diff --git a/core/modules/forum/src/Tests/ForumBlockTest.php b/core/modules/forum/src/Tests/ForumBlockTest.php
index 1295c82..99497a7 100644
--- a/core/modules/forum/src/Tests/ForumBlockTest.php
+++ b/core/modules/forum/src/Tests/ForumBlockTest.php
@@ -63,7 +63,7 @@ public function testNewForumTopicsBlock() {
     $topics = $this->createForumTopics();
 
 
-    $this->assertLink(t('More'), 0, 'New forum topics block has a "more"-link.');
+    $this->assertLink(t('Read the latest forum topics.'), 0, 'New forum topics block has a "more"-link.');
     $this->assertLinkByHref('forum', 0, 'New forum topics block has a "more"-link.');
 
     // We expect all 5 forum topics to appear in the "New forum topics" block.
@@ -118,7 +118,8 @@ public function testActiveForumTopicsBlock() {
     // Enable the block.
     $block = $this->drupalPlaceBlock('forum_active_block');
     $this->drupalGet('');
-    $this->assertLink(t('More'), 0, 'Active forum topics block has a "more"-link.');
+    $this->assertLink(t('Read the latest forum topics.'), 0, 'Active forum topics block has a "more"-link.');
+
     $this->assertLinkByHref('forum', 0, 'Active forum topics block has a "more"-link.');
 
     // We expect the first 5 forum topics to appear in the "Active forum topics"
