Do I miss something or is it impossible to theme the link anchor HTML element <ul class="main-menu"><li><a href="" class="put something in here"></li></ul> for a targeted menu on the theming level (theme functions)?

We can theme the <li> elements and the menu block elements with HOOK_menu_link_MENU_NAME(). Thanks to David Thomas for pointing that out to me. But I had no luck with targeting the nested anchors itself. The Devel array output and the drupal.org docs only brought me to theme_menu_item_link(), theme_links, theme_link(), and others, but I can't see an opportunity here to elegantly incept dynamic values for css classes on the anchor element?

I would like to add style class attributes to the <a> element in ul > li of a given menu (not of all menus, thats what theme_links() does)...


  • nested css targeting by altering the upper element is not an option here, since the required css class is level sensitive and should NOT be applied to the upper html element.

  • And yes, I know the menu_attributes module. It's an awesome module I use very often. But the approach is different. I don't want to change the menu link data in the DB, but the output only.

  • There is an issue for the theme_links() function on drupal.org mentioning some trouble with its scope and how it works: https://drupal.org/node/588148

    theme_links(): Returns HTML for a set of links. => theme_links($variables) https://drupal.org/node/588148 => theme_links() is not really themeable [#588148] => Drupal core, theme system, normal, needs work, 49 comments, 1 IRC mention

  • Another option would be to add <span> elements inside the anchor elements. I already did this for node objects but I don't know if this is possible here and if this is the right way to do it.

  • Edit: Here are two questions - 1 - 2 - on stackexchange I read thru' atm, since there are near issues discussed. Maybe it will enlighten me soon ... (Thanks to Clive, who has pointed me to the second one.)

  • Have you come across/tried overriding theme_menu_link()? This question might help
    – Clive
    Commented Jul 2, 2013 at 17:32
  • Yes Clive, I already use it to add the link id as a class attribute to the li-element, but I don't see a way to get deeper into the a-element level inside of the li subarray in theme_menu_link()? Do I have overlooked something here? Thank you for chiming in, Clive!
    – nilsun
    Commented Jul 2, 2013 at 17:37
  • I think the link HTML comes from the call to l() in theme_menu_link(), you can pass in an attributes array as one of the options (details on the l() docs page) which should solve the problem...in theory!
    – Clive
    Commented Jul 2, 2013 at 17:50
  • Thank you Clive for taking one more time to explain you thoughts on theme_menu_link. I think I have to read the docs first before I can say anything more to your suggestions ...
    – nilsun
    Commented Jul 2, 2013 at 19:15
  • Thank you Clive. Again. I overlooked or forgot to dip deeper into the issue you have linked in your first comment. It was deep in the night. I took a short look on it right now and it seems to me now like I should look deeper into this issue for my question. Especially some comments below. I willl go thru it now. I also tried to tighten up my question a littel bit. I hope it is better now. I have problems to come to the point sometimes.
    – nilsun
    Commented Jul 3, 2013 at 10:23

2 Answers 2


Based on this it should be pretty simple:

function THEME_menu_link($vars) {
  $element  = $vars['element'];
  $menuname = $element['#original_link']['menu_name'];

  if ($element['#below']) {
    $sub_menu = drupal_render($element['#below']);

  // This is where you check the menu name and assign the class.
  if($menuname == 'main-menu') {
    $element['#localized_options']['attributes']['class'][] = 'main-menu-class-whatever';
    $element['#localized_options']['attributes']['class'][] = 'another-class';

  $output = l($element['#title'], $element['#href'], $element['#localized_options']);

  return '<li' . drupal_attributes($element['#attributes']) . '>' . $output . $sub_menu . '</li>';
  • Thanks a million for chiming in Beebee! This is what I already do here for another scope. But maybe I am too weared down to see where I can elegantly incept dynamic css classes here in the <a href="our menu link" class="our dynamic class inception">title</a> element? If I mix your answer and jimajammas a little bit, I start to think you both mean that I should add $element['#mywhateverdata'] to $output = l($element['#title'], $element['#href'], $element['#localized_options']);. Yes/no?
    – nilsun
    Commented Jul 3, 2013 at 11:58
  • oh wait. no. I should read before I write. Your example already shows how you pass attributes to the output, I think... facepalm I should try this and come back to report ...
    – nilsun
    Commented Jul 3, 2013 at 12:04
  • Jesus. It works! Thanks for clearifying it again. Since Jimajamma already mentioned it but I didn't understood what he suggested I am not sure which answer I should mark as solution now? Thanks a million for chiming in again, Beebee!
    – nilsun
    Commented Jul 3, 2013 at 12:15
  • I vote for @Beebee as an answer, I just commented on it.
    – Jimajamma
    Commented Jul 3, 2013 at 13:29
  • Thank you, Jimajamma for making things uncomplicated. Greetings from Berlin
    – nilsun
    Commented Jul 3, 2013 at 20:25

I am probably looking at this upside down or backwards but, are you rendering this menu yourself, eg, you are calling theme('links', $variables) somewhere and can easily modify $variables?

If so, a quick look at the theme_links() function itself reveals the following comment...

 * Returns HTML for a set of links.
 * @param $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
 *     following elements:
 *     - title: The link text.
 *     - href: The link URL. If omitted, the 'title' is shown as a plain text
 *       item in the links list.
 *     - html: (optional) Whether or not 'title' is HTML. If set, the title
 *       will not be passed through check_plain().
 *     - attributes: (optional) Attributes for the anchor, or for the <span>
 *       tag used in its place if no 'href' is supplied. If element 'class' is
 *       included, it must be an array of one or more class names.
 *     If the 'href' element is supplied, the entire link array is passed to
 *     l() as its $options parameter.
 *   - attributes: A keyed array of attributes for the UL containing the
 *     list of links.
 *  (rest of comment deleted)

showing that what you need to do is put in an attributes key with each link, as, further on down in the code, when it is creating the link itself, it does, as the comment above also mentions, adds in the full $link as the $options parameter, eg...

$output .= l($link['title'], $link['href'], $link);

So, if you create an attributes key that points to an array with a key of class for whatever link needs a special class, and that key itself points to an array containing that name, this class will be added to the anchor.

  • Jimajamma: thanks for chiming in. The menu link anchors I try to target are from menus like main-menu, user-menu or any menu created from the user via admin UI. In the moment I try to follow Clives suggestion, which is close to yours if I understood correctly. But it needs further more docs reading of mine atm. I have problems to understand how to alter the l() in theme_menu_link for my needs. But that's maybe because I am very tired (2h sleep only) ... I will try to follow yours and Clives idea, but need to understand l() before better.
    – nilsun
    Commented Jul 2, 2013 at 19:16
  • gotcha. there it is doing it via $output = l($element['#title'], $element['#href'], $element['#localized_options']); so you need to look at the #localized_options key. You might be able to just copy this to YOURTHEME_menu_link__MENU_NAME() and do it manually inside based upon whatever logic you need.
    – Jimajamma
    Commented Jul 2, 2013 at 19:37
  • Thank you Jimajamma. I think I need more time to get into it. Somehow the way it needs to get altered don't raise in my picture very well. I will follow yours and Clives advices, but for some unexplainable reason I have a hard time to get into the way in this special case here ...
    – nilsun
    Commented Jul 3, 2013 at 10:25

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.