Icon Fonts and Drupal7

Feb 3, 2014

In my previous post Icon Fonts I described the use of icon fonts instead of images. This technique has many advantages but what about using icon fonts with Drupal were most links are generated by the theming system. We have full control over the html in the body element of a node, but how to use icon fonts with menu links or links created with the link field is not that obvious.

Turns out that modifying the link html is not that hard. All we have to do is bend the theme_link function to our will.

Before we modify the theme function we have to agree on a set of assumptions that must be met so this scheme works:

  • Icon links will have a class of “icon-<icon name>”
  • Icons will be added before the link text
  • If the link has a class of “icon-<icon name>” and a class of “after”, the icon will be placed after the link text
  • The text link will be placed in <span> tags so we can hide the text and show the icon only if required

So with this settled we can start creating our own theme function. Our starting point is a copy of the original theme function from the Drupal7 API documentation

The theme link function looks like this:

function theme_link($variables) {
  return 
    '<a href="' . check_plain(url($variables['path'], $variables['options'])) . '"' . drupal_attributes($variables['options']['attributes']) . '>' . ($variables['options']['html'] ? $variables['text'] : check_plain($variables['text'])) . '</a>';
}

Let’s consider a link that is processed through the theme layer. The link without our intervention would look like this:

<a href=“path/to/whatever” id=“myFolder” class=“icon-folder iconOnly after” target=“_blank”>More Documents</a>

Looking at the array $variables that is passed into the theme function we would see something like this:

$variables = array(
    ‘text’ => ‘More Documents’,
    ‘path’ => ‘path/to/whatever’,
    ‘options’ => array(
        ‘attributes’  => array(
            ‘title’ => ‘More Documents’,
            ‘id’ => ‘myFolder’,
            ‘class’ => array(
                0 => ‘icon-folder’,
                1 => ‘iconOnly’,
                2 => ‘after’,
            ),
            target => ‘_blank’,
        ),
        html => FALSE
    ),
);

For the ‘iconized’ theme function we are using the presence of a class icon-* to build the new link html. The code below show all modifications:

/**
 * Implements THEME_link
 */
function t47theme_link($variables) {

  $icon_class = '';
  $icon_after = false;

  // check if there is a link with attribute class
  // a class of "icon-......" must be present for the link html to be modified for an icon
  if (isset($variables['options']['attributes']['class'])) {

    dsm($variables);
    foreach($variables['options']['attributes']['class'] as $key => $value) {
      // check if there is an array element with a value that starts with "icon-"
      if (strpos($value, 'icon-') !== false) {
        // we found our icon class
        $icon_class = $value;
        // remove the array element
        unset($variables['options']['attributes']['class'][$key]);
      }

      // the icon will be placed before the link text by default
      // if a class after is found we'll put the icon after the link text
      if (strpos($value, 'after') !== false) {
        // the icon will be placed after the link text
        $icon_after = true;
        // remove the after class, we don't need it anymore
        unset($variables['options']['attributes']['class'][$key]);
      }
    }
  }

  // assemble the link html
  if ($icon_class) {
    // we are injecting html into the link text so we must set html to TRUE
    // and perform our own check_plain when assembling the link html
    $variables['options']['html'] = true;

    // build the link html
    if($icon_after) {
      $link_html = "<span>" . check_plain($variables['text']) . "</span><i class='" . $icon_class . "'></i>";
    } else {
      $link_html = "<i class='" . $icon_class . "'></i><span>" . check_plain($variables['text']) . "</span>";
    }

    // overwrite the current link text with the 'iconized' link html
    $variables['text'] = $link_html;
  }

  return '<a href="' . check_plain(url($variables['path'], $variables['options'])) . '"' . drupal_attributes($variables['options']['attributes']) . '>' . ($variables['options']['html'] ? $variables['text'] : check_plain($variables['text'])) . '</a>';
}

Here is what we did:

  • We first checked if the link has any classes
  • If yes, we check if the array class has a member with a value that contains “icon-“
  • If yes we check for the presence of class ‘after”.
  • If we find a value that contains ‘icon-‘ we store the value in a temporary variable and remove the element from the classes array
  • We do the same for the ‘after’ class
  • We now assemble the new link text. Because the new link text will contain html, we have to set html to TRUE in the variables array and perform our own check_plain on the text.
  • We then overwrite the link text

The resulting variables array will look like this:


$variables = array(
    ‘text’ => ‘<span>More Documents</span><i class=“icon-folder”></i>’,
    ‘path’ => ‘path/to/whatever’,
    ‘options’ => array(
        ‘attributes’  => array(
            ‘title’ => ‘More Documents’,
            ‘id’ => ‘myFolder’,
            ‘class’ => array(
                0 => ‘iconOnly’,	
            ),
            target => ‘_blank’,
        ),
        html => TRUE
    ),
);

And the final link will look like this:


<a href=“path/to/whatever” id=“myFolder” class=“iconOnly” target=“_blank”><span>More Documents</span><i class=“icon-folder”></i></a>

There it is. We now have all hooks in place to style the link anyway we want.

Add new comment