diff --git a/core/modules/rdf/rdf.module b/core/modules/rdf/rdf.module
index dc97773..6ec4fff 100644
--- a/core/modules/rdf/rdf.module
+++ b/core/modules/rdf/rdf.module
@@ -375,14 +375,13 @@ function rdf_preprocess_user(&$variables) {
  * Implements hook_preprocess_HOOK() for theme_username().
  */
 function rdf_preprocess_username(&$variables) {
-  $attributes = array();
   // Because lang is set on the HTML element that wraps the page, the
   // username inherits this language attribute. However, since the username
   // might not be transliterated to the same language that the content is in,
   // we do not want it to inherit the language attribute, so we set the
   // attribute to an empty string.
   if (empty($variables['attributes']['lang'])) {
-    $attributes['lang'] = '';
+    $variables['attributes']['lang'] = '';
   }
 
   // The profile URI is used to identify the user account. The about attribute
@@ -393,44 +392,32 @@ function rdf_preprocess_username(&$variables) {
   // a user profile URI for it (only a homepage which cannot be used as user
   // profile in RDF.)
   if ($variables['uid'] > 0) {
-    $attributes['about'] = url('user/' . $variables['uid']);
+    $variables['attributes']['about'] = url('user/' . $variables['uid']);
   }
 
   // Add RDF type of user.
   $mapping = rdf_get_mapping('user', 'user');
   $bundle_mapping = $mapping->getPreparedBundleMapping();
   if (!empty($bundle_mapping['types'])) {
-    $attributes['typeof'] = $bundle_mapping['types'];
+    $variables['attributes']['typeof'] = $bundle_mapping['types'];
   }
   // Annotate the username in RDFa. A property attribute is used with an empty
   // datatype attribute to ensure the username is parsed as a plain literal
   // in RDFa 1.0 and 1.1.
   $name_mapping = $mapping->getPreparedFieldMapping('name');
   if (!empty($name_mapping)) {
-    $attributes['property'] = $name_mapping['properties'];
-    $attributes['datatype'] = '';
+    $variables['attributes']['property'] = $name_mapping['properties'];
+    $variables['attributes']['datatype'] = '';
   }
   // Add the homepage RDFa markup if present.
   $homepage_mapping = $mapping->getPreparedFieldMapping('homepage');
   if (!empty($variables['homepage']) && !empty($homepage_mapping)) {
-    $attributes['rel'] = $homepage_mapping['properties'];
+    $variables['attributes']['rel'] = $homepage_mapping['properties'];
   }
   // Long usernames are truncated by template_preprocess_username(). Store the
   // full name in the content attribute so it can be extracted in RDFa.
   if ($variables['truncated']) {
-    $attributes['content'] = check_plain($variables['name_raw']);
-  }
-  // The remaining attributes can have multiple values listed, with whitespace
-  // separating the values in the RDFa attributes
-  // (see http://www.w3.org/TR/rdfa-syntax/#rdfa-attributes).
-  // Therefore, merge rather than override so as not to clobber values set by
-  // earlier preprocess functions. These attributes will be placed in the a
-  // element if a link is rendered, or on a span element otherwise.
-  if (isset($variables['link_path'])) {
-    $variables['link_options']['attributes'] = array_merge_recursive($variables['link_options']['attributes'], $attributes);
-  }
-  else {
-    $variables['attributes'] = array_merge_recursive($variables['attributes'], $attributes);
+    $variables['attributes']['content'] = check_plain($variables['name_raw']);
   }
 }
 
diff --git a/core/modules/system/system.api.php b/core/modules/system/system.api.php
index f842fc0..efbe956 100644
--- a/core/modules/system/system.api.php
+++ b/core/modules/system/system.api.php
@@ -956,10 +956,9 @@ function hook_system_info_alter(array &$info, \Drupal\Core\Extension\Extension $
  *     have inherent security risks across a variety of potential use cases
  *     (for example, the "administer filters" and "bypass node access"
  *     permissions provided by Drupal core). When set to TRUE, a standard
- *     warning message defined in user_admin_permissions() and output via
- *     theme_user_permission_description() will be associated with the
- *     permission and displayed with it on the permission administration page.
- *     Defaults to FALSE.
+ *     warning message defined in user_admin_permissions() will be displayed
+ *     with the permission on the permission administration page. Defaults
+ *     to FALSE.
  *   - warning: (optional) A translated warning message to display for this
  *     permission on the permission administration page. This warning overrides
  *     the automatic warning generated by 'restrict access' being set to TRUE.
@@ -967,8 +966,6 @@ function hook_system_info_alter(array &$info, \Drupal\Core\Extension\Extension $
  *     have a clear, consistent security warning that is the same across the
  *     site. Use the 'description' key instead to provide any information that
  *     is specific to the permission you are defining.
- *
- * @see theme_user_permission_description()
  */
 function hook_permission() {
   return array(
diff --git a/core/modules/user/src/Form/UserPermissionsForm.php b/core/modules/user/src/Form/UserPermissionsForm.php
index 7eee62d..6851727 100644
--- a/core/modules/user/src/Form/UserPermissionsForm.php
+++ b/core/modules/user/src/Form/UserPermissionsForm.php
@@ -140,18 +140,21 @@ public function buildForm(array $form, array &$form_state) {
             'warning' => !empty($perm_item['restrict access']) ? $this->t('Warning: Give to trusted roles only; this permission has security implications.') : '',
           );
           $options[$perm] = $perm_item['title'];
-          $user_permission_description = array(
-            '#theme' => 'user_permission_description',
-            '#permission_item' => $perm_item,
-            '#hide' => $hide_descriptions,
-          );
+          // Show the permission description.
+          if (!$hide_descriptions) {
+            $user_permission_description = $perm_item['description'];
+            // Append warning message.
+            if (!empty($perm_item['warning'])) {
+              $user_permission_description .= ' <em class="permission-warning">' . $perm_item['warning'] . '</em>';
+            }
+          }
           $form['permissions'][$perm]['description'] = array(
             '#wrapper_attributes' => array(
               'class' => array('permission'),
             ),
             '#type' => 'item',
             '#markup' => $perm_item['title'],
-            '#description' => drupal_render($user_permission_description),
+            '#description' => $user_permission_description,
           );
           $options[$perm] = '';
           foreach ($role_names as $rid => $name) {
diff --git a/core/modules/user/templates/username.html.twig b/core/modules/user/templates/username.html.twig
new file mode 100644
index 0000000..5d4f2f7
--- /dev/null
+++ b/core/modules/user/templates/username.html.twig
@@ -0,0 +1,25 @@
+{#
+/**
+ * @file
+ * Default theme implementation for displaying a username.
+ *
+ * Available variables:
+ * - account: The full account information for the user.
+ * - name: The user's name, sanitized.
+ * - extra: Additional text to append to the user's name, sanitized.
+ * - link_path: The path or URL of the user's profile page, home page,
+ *   or other desired page to link to for more information about the user.
+ * - link_options: Options to pass to the url() function's $options parameter if
+ *   linking the user's name to the user's page.
+ * - attributes: HTML attributes for the containing element.
+ *
+ * @see template_preprocess_username()
+ *
+ * @ingroup themeable
+ */
+#}
+{% if link_path -%}
+  <a{{ attributes }}>{{ name }}{{ extra }}</a>
+{%- else -%}
+  <span{{ attributes }}>{{ name }}{{ extra }}</span>
+{%- endif -%}
diff --git a/core/modules/user/user.admin.inc b/core/modules/user/user.admin.inc
deleted file mode 100644
index 9a6ca2a..0000000
--- a/core/modules/user/user.admin.inc
+++ /dev/null
@@ -1,37 +0,0 @@
-<?php
-
-/**
- * @file
- * Admin page callback file for the user module.
- */
-
-/**
- * Returns HTML for an individual permission description.
- *
- * @param $variables
- *   An associative array containing:
- *   - permission_item: An associative array representing the permission whose
- *     description is being themed. Useful keys include:
- *     - description: The text of the permission description.
- *     - warning: A security-related warning message about the permission (if
- *       there is one).
- *   - hide: A boolean indicating whether or not the permission description was
- *     requested to be hidden rather than shown.
- *
- * @ingroup themeable
- */
-function theme_user_permission_description($variables) {
-  if (!$variables['hide']) {
-    $description = array();
-    $permission_item = $variables['permission_item'];
-    if (!empty($permission_item['description'])) {
-      $description[] = $permission_item['description'];
-    }
-    if (!empty($permission_item['warning'])) {
-      $description[] = '<em class="permission-warning">' . $permission_item['warning'] . '</em>';
-    }
-    if (!empty($description)) {
-      return implode(' ', $description);
-    }
-  }
-}
diff --git a/core/modules/user/user.module b/core/modules/user/user.module
index 6473625..a4cfa29 100644
--- a/core/modules/user/user.module
+++ b/core/modules/user/user.module
@@ -107,15 +107,9 @@ function user_theme() {
       'file' => 'user.pages.inc',
       'template' => 'user',
     ),
-    'user_permission_description' => array(
-      'variables' => array('permission_item' => NULL, 'hide' => NULL),
-      'file' => 'user.admin.inc',
-    ),
-    'user_signature' => array(
-      'variables' => array('signature' => NULL),
-    ),
     'username' => array(
       'variables' => array('account' => NULL, 'attributes' => array()),
+      'template' => 'username',
     ),
   );
 }
@@ -617,7 +611,14 @@ function user_template_preprocess_default_variables_alter(&$variables) {
 }
 
 /**
- * Preprocesses variables for theme_username().
+ * Prepares variables for username templates.
+ *
+ * Default template: username.html.twig.
+ *
+ * @param array $variables
+ *   An associative array containing:
+ *   - account: The user account to check access for
+ *     (Drupal\user\Plugin\Core\Entity\User).
  *
  * Modules that make any changes to variables like 'name' or 'extra' must ensure
  * that the final string is safe to include directly in the output by using
@@ -653,55 +654,26 @@ function template_preprocess_username(&$variables) {
   // Populate link path and attributes if appropriate.
   if ($variables['uid'] && $variables['profile_access']) {
     // We are linking to a local user.
-    $variables['link_options']['attributes']['title'] = t('View user profile.');
+    $variables['attributes']['title'] = t('View user profile.');
     $variables['link_path'] = 'user/' . $variables['uid'];
   }
   elseif (!empty($account->homepage)) {
     // Like the 'class' attribute, the 'rel' attribute can hold a
     // space-separated set of values, so initialize it as an array to make it
     // easier for other preprocess functions to append to it.
-    $variables['link_options']['attributes']['rel'] = 'nofollow';
+    $variables['attributes']['rel'] = 'nofollow';
     $variables['link_path'] = $account->homepage;
     $variables['homepage'] = $account->homepage;
   }
-  // We do not want the l() function to check_plain() a second time.
-  $variables['link_options']['html'] = TRUE;
   // Set a default class.
-  $variables['link_options']['attributes']['class'] = array('username');
-}
-
-/**
- * Returns HTML for a username, potentially linked to the user's page.
- *
- * @param $variables
- *   An associative array containing:
- *   - account: The user object to format.
- *   - name: The user's name, sanitized.
- *   - truncated: A boolean indicating if $variables['name'] has been shortened.
- *   - extra: Additional text to append to the user's name, sanitized.
- *   - link_path: The path or URL of the user's profile page, home page, or
- *     other desired page to link to for more information about the user.
- *   - link_options: An array of options to pass to the l() function's $options
- *     parameter if linking the user's name to the user's page.
- *   - attributes: An array of attributes to instantiate the
- *     Drupal\Core\Template\Attribute class if not linking to the user's page.
- *
- * @see template_preprocess_username()
- */
-function theme_username($variables) {
+  $variables['attributes']['class'] = array('username');
+  // We have a link path, so we should generate a link using url().
+  // Additional classes may be added as array elements like
+  // $variables['attributes']['class'][] = 'myclass';
   if (isset($variables['link_path'])) {
-    // We have a link path, so we should generate a link using l().
-    // Additional classes may be added as array elements like
-    // $variables['link_options']['attributes']['class'][] = 'myclass';
-    $output = l($variables['name'] . $variables['extra'], $variables['link_path'], $variables['link_options']);
-  }
-  else {
-    // Modules may have added important attributes so they must be included
-    // in the output. Additional classes may be added as array elements like
-    // $variables['attributes']['class'][] = 'myclass';
-    $output = '<span' . new Attribute($variables['attributes']) . '>' . $variables['name'] . $variables['extra'] . '</span>';
+    $link_options = isset($variables['link_options']) ? $variables['link_options'] : array();
+    $variables['attributes']['href'] = url($variables['link_path'], $link_options);
   }
-  return $output;
 }
 
 /**
@@ -1415,29 +1387,6 @@ function user_role_revoke_permissions($rid, array $permissions = array()) {
 }
 
 /**
- * Returns HTML for a user signature.
- *
- * @param $variables
- *   An associative array containing:
- *   - signature: The user's signature.
- *
- * @ingroup themeable
- */
-function theme_user_signature($variables) {
-  $signature = $variables['signature'];
-  $output = '';
-
-  if ($signature) {
-    $output .= '<div class="clear">';
-    $output .= '<div>—</div>';
-    $output .= $signature;
-    $output .= '</div>';
-  }
-
-  return $output;
-}
-
-/**
  * Conditionally create and send a notification email when a certain
  * operation happens on the given user account.
  *
