diff --git a/core/includes/theme.inc b/core/includes/theme.inc
index 8d527df..c57c388 100644
--- a/core/includes/theme.inc
+++ b/core/includes/theme.inc
@@ -1703,9 +1703,11 @@ function theme_link($variables) {
 }
 
 /**
- * Returns HTML for a set of links.
+ * Prepares variables for links templates.
  *
- * @param $variables
+ * Default template: links.html.twig.
+ *
+ * @param array $variables
  *   An associative array containing:
  *   - links: An associative array of links to be themed. The key for each link
  *     is used as its CSS class. Each link should be itself an array, with the
@@ -1738,38 +1740,32 @@ function theme_link($variables) {
  *     http://juicystudio.com/article/screen-readers-display-none.php and
  *     http://www.w3.org/TR/WCAG-TECHS/H42.html for more information.
  */
-function theme_links($variables) {
+function template_preprocess_links(&$variables) {
   $language_url = language(LANGUAGE_TYPE_URL);
 
   $links = $variables['links'];
-  $attributes = $variables['attributes'];
-  $heading = $variables['heading'];
-  $output = '';
+  $heading = &$variables['heading'];
+  $variables['attributes'] = new Attribute($variables['attributes']);
 
   if (!empty($links)) {
     // Prepend the heading to the list, if any.
     if (!empty($heading)) {
-      // Convert a string heading into an array, using a H2 tag by default.
+      // Convert a string heading into an array.
       if (is_string($heading)) {
         $heading = array('text' => $heading);
+      } else {
+        // @todo Remove backwards compatibility for $heading['class'].
+        if (isset($heading['class'])) {
+          $heading['attributes']['class'] = $heading['class'];
+          unset($heading['class']);
+        }
       }
-      // Merge in default array properties into $heading.
-      $heading += array(
-        'level' => 'h2',
-        'attributes' => array(),
-      );
-      // @todo Remove backwards compatibility for $heading['class'].
-      if (isset($heading['class'])) {
-        $heading['attributes']['class'] = $heading['class'];
+      // Convert the attributes array into an attributes object.
+      if (isset($heading['attributes'])) {
+        $heading['attributes'] = new Attribute($heading['attributes']);
       }
-
-      $output .= '<' . $heading['level'] . new Attribute($heading['attributes']) . '>';
-      $output .= check_plain($heading['text']);
-      $output .= '</' . $heading['level'] . '>';
     }
 
-    $output .= '<ul' . new Attribute($attributes) . '>';
-
     $num_links = count($links);
     $i = 0;
     foreach ($links as $key => $link) {
@@ -1793,47 +1789,43 @@ function theme_links($variables) {
         $is_current_language = (empty($link['language']) || $link['language']->langcode == $language_url->langcode);
         if ($is_current_path && $is_current_language) {
           $class[] = 'active';
+          $link['attributes']['class'][] = 'active';
         }
+        // debug($is_current_path);
+        // debug($link);
+        // debug(current_path());
+        // debug(drupal_is_front_page());
         // @todo Reconcile Views usage of 'ajax' as a boolean with the rest of
         //   core's usage of it as an array.
+        $item = array(
+          '#type' => 'link',
+          '#title' => $link['title'],
+          '#href' => $link['href'],
+        );
         if (isset($link['ajax']) && is_array($link['ajax'])) {
           // To attach Ajax behavior, render a link element, rather than just
           // call l().
-          $link_element = array(
-            '#type' => 'link',
-            '#title' => $link['title'],
-            '#href' => $link['href'],
+          $item += array(
             '#ajax' => $link['ajax'],
             '#options' => array_diff_key($link, drupal_map_assoc(array('title', 'href', 'ajax'))),
           );
-          $item = drupal_render($link_element);
         }
         else {
-          // Pass in $link as $options, they share the same keys.
-          $item = l($link['title'], $link['href'], $link);
-        }
-      }
-      // Handle title-only text items.
-      else {
-        // Merge in default array properties into $link.
-        $link += array(
-          'html' => FALSE,
-        );
-        $item = ($link['html'] ? $link['title'] : check_plain($link['title']));
-        if (isset($link['attributes'])) {
-          $item = '<span' . new Attribute($link['attributes']) . '>' . $item . '</span>';
+          $item += array(
+            '#options' => array_diff_key($link, drupal_map_assoc(array('title', 'href'))),
+          );
         }
+        $variables['links'][$key]['link'] = $item;
       }
 
-      $output .= '<li' . new Attribute(array('class' => $class)) . '>';
-      $output .= $item;
-      $output .= '</li>';
-    }
+      // Handle text.
+      $text = (!empty($link['html']) ? $link['title'] : check_plain($link['title']));
+      $variables['links'][$key]['text'] = $text;
 
-    $output .= '</ul>';
+      // Handle list item attributes.
+      $variables['links'][$key]['attributes'] = new Attribute(array('class' => $class));
+    }
   }
-
-  return $output;
 }
 
 /**
@@ -3155,6 +3147,7 @@ function drupal_common_theme() {
     ),
     'links' => array(
       'variables' => array('links' => array(), 'attributes' => array('class' => array('links')), 'heading' => array()),
+      'template' => 'links',
     ),
     'dropbutton_wrapper' => array(
       'render element' => 'element',
