19

Whilst creating a navigation bar for my site I decided to make the active page tab show up in bold for usability purposes, but when I change the font-weight on the element it only slightly makes the element wider, an example I made using hover effects instead demonstrates my issue and i've never known a way to solve it..

http://jsfiddle.net/amx7E/

HTML

<ul id="mainNav">
    <li class="navItem">
        <a class="navLink" id="activeLink">Link 1</a>
    </li>

    <li class="navItem">
        <a class="navLink">Link 2</a>
    </li>

    <li class="navItem">
        <a class="navLink">Link 3</a>
    </li>
</ul>

CSS

* {
    font-family: Arial;
    font-size: 14px;
    list-style: none;
    margin: 0;
    padding: 0;
    text-decoration: none;
}

#mainNav {
    background: RGB(200, 230, 240);
    border-bottom: 1px solid RGB(0, 0, 0);
    height: 30px;
    margin: 0 auto;
    position: relative;
    width: 100%;
}

.navItem {
    display: block;
    float: left;
    position: relative;
}

.navItem:last-child .navLink {
    border-right: 1px solid RGB(0, 0, 0);
}

.navLink {
    border-bottom: 1px solid RGB(0, 0, 0);
    border-left: 1px solid RGB(0, 0, 0);
    display: inline-block;
    height: 30px;
    line-height: 30px;
    padding: 0 15px;
    white-space: nowrap;
}

.navItem:hover .navLink {
    background: RGB(120, 200, 250);
    color: RGB(255, 255, 255);
    cursor: pointer;
    font-weight: bold;
}

#activeLink {
    background: RGB(90, 170, 220);
    color: RGB(255, 255, 255);
    font-weight: bold;
}

#activeLink:hover {
    background: RGB(110, 190, 240);
}

.navItem:hover .subNav {
    display: block;
}
Banny
  • 811
  • 4
  • 13
  • 36

9 Answers9

18

Other than the width route here are two other possibilities, it's down to personal preference as to whether you think they are suitable or not. Both these ideas work on the same principal, that you use a separate element to show the bold state, and this element either doesn't (idea one) or does (idea two) affect the UI with it's dimensions.

http://jsfiddle.net/3Jyge/2/

Idea one

Use pseudo selectors. This method relies on the browser supporting quite recent advances i.e. :before and content: attr() so probably isn't reliable just yet.

css:

ul {
  list-style: none;
}
ul li {
  float: left;
}
ul li:hover a {
  visibility: hidden;
}
ul li:hover:before {
  position: absolute;
  font-weight: bold;
  content: attr('data-text');
}

markup:

<ul>
  <li data-text="one"><a href="#">one</a></li>
  <li data-text="two"><a href="#">two</a></li>
  <li data-text="three"><a href="#">three</a></li>
</ul>


Idea two

The other is a bit more straight-forward, although it relies on preping your markup first — and those who use screen readers may understandably dislike your site; unless you can find a nice way to hide the duplicate text from them.

markup:

<ul>
  <li><a href="#">
    <span class="a">one</span>
    <span class="b">one</span>
  </a></li>
  <li><a href="#">
    <span class="a">two</span>
    <span class="b">two</span>
  </a></li>
  <li><a href="#">
    <span class="a">three</span>
    <span class="b">three</span>
  </a></li>
</ul>

css:

ul {
  margin: 0; padding: 0;
  list-style: none;
}
ul li {
  float: left;
}
ul li a span.b {
  visibility: hidden;
  font-weight: bold;
}
ul li a span.a {
  position: absolute;
}
ul li:hover a span.b {
  visibility: visible;
}
ul li:hover a span.a {
  visibility: hidden;
}

At the end of the day the better solutions would be:

  1. Set a width, although I can understand not wanting to do this.
  2. Use JavaScript to calculate dimensions.
  3. Choose a different highlight, one that doesn't alter the dimensions of the text.
Pebbl
  • 34,937
  • 6
  • 62
  • 64
  • Thank you for your suggestions. I am a huge fan of your first idea but as you said in terms of backwards compatibility you hit a stumbling block with it. I think the best things to be introduced to HTML are those custom `data-` attributes, very helpful for use in jQuery and CSS. – Banny Jul 15 '13 at 10:58
  • @LeeB ~ No worries, yeah anything involving `attr()` will have to wait for the moment, at least until one futher Internet Explorer version maybe :) Or at least until someone comes up with a speedy js shim to port compatibility backwards. – Pebbl Jul 16 '13 at 11:12
  • upvote for option 2. we can use aria-hidden at one of them for screen readers. – funky-nd Mar 15 '21 at 08:28
10

An alternative to font-weight for bolding text is to use text-shadow to set a horizontal shadow the same colour as the text. This code sets equal shadows to the left and the right. With only one shadow, the text would appear to shift in the direction of the shadow.

CSS:

    a:hover {
      text-shadow: .25px 0px .1px,
                  -.25px 0px .1px;
    }

Note that web browsers do not render text, let alone text-shadow effects, identically. You'll want to check how they look in multiple browsers before deciding on values.

Cowdozer
  • 117
  • 1
  • 7
8

The way I handle this is to use JQuery to 'fix' the width so that it does not change on hover even though the bold does change. This basically takes the css width answer here and automates the process.

  (function($) {
    $(function() {
      $('#menu > li').each(function(){
        wid = $(this).width();
        $(this).css('width', wid+'px');
      });
    });
  })(jQuery);
Nate Bunney
  • 2,418
  • 2
  • 22
  • 31
  • 1
    This was very helpful, although I had to add some extra pixels to allow the `bold` enough room, i.e. `var wid = $(this).width() + 10`. – Nick Sep 06 '14 at 10:59
  • Yup, that is likely a good idea @Nick. – Nate Bunney Jun 22 '16 at 17:02
  • I like the simplicity of this one and since I can bury this type of code once in an angular directive its clean too. – flyer Aug 30 '17 at 18:59
4

Interesting that no-one mentions letter-spacing.

It is possible to add letter spacing to the normal font and remove it for the bold version.

Getting it right is not always possible and depends a lot on the font and the font-size. But with more browsers starting to accept subpixel values for letter-spacing it starts to be more practical.

Example:

.nav-link.active {
    font-weight: bold;

    /* Adjust for font-weight bold so that the text will still align */
    letter-spacing: -1px;
}
Martin Tournoij
  • 26,737
  • 24
  • 105
  • 146
niccol
  • 129
  • 4
  • Very good point. I've experimented with letter-spacing on a few occassions now and found in some instances it can be great but in others it just doesn't look the best, perhaps not helped by font kerning issues with some fonts. – Banny Jan 14 '15 at 10:23
  • 3
    ...probably because it doesn't work. It might accidentally work on certain systems with some fonts at some font sizes (probably mostly on Windows where the stupid font rendering engine probably thickens each letter by a whole pixel) but definitely not on Linux or Mac, since letters are only some fractions of a pixel wider when bold. – Stein G. Strindhaug Sep 16 '15 at 13:53
3

As you are not specifying any width for the elements, they are sized according to their content. As such, when the font-weight is normal, the element takes up less space than when it is bold. As the content changes size, the size of the box also changes.

You can specify the width of the element so that it slightly bigger than it's content or alternatively use a different method for identifying the currently active tab.

Kami
  • 19,134
  • 4
  • 51
  • 63
  • Exactly. As far as I can tell, you need to specify a width (regardless of it being in percent or pixels). Since width wasn't set, it's default value "auto" is in effect, hence the element becomes larger if the content takes more space, as a bold font does. – Anpan Jul 15 '13 at 09:13
  • It makes sense, but I was curious as to whether there were any simple hacks to get around the fluid width issue but I guess there isn't. – Banny Jul 15 '13 at 09:16
  • Marked as correct as in some cases this would be the solution, but for my issue this was not the solution. – Banny Jul 15 '13 at 09:24
0

I think there is no CSS based solution for this, unless you can add a width, maybe a different width to each li item. You already have a change in bg color on hover and selected states, I suggest you drop the font-weight change. Visually it's not a big change, and in terms of usability you already have the bg color change that I think is enough.

Mihai Alex
  • 678
  • 6
  • 13
  • I've decided to do that now, it's useless me wasting time thinking of another solution when I already have a way to identify it. Thanks for the advice. – Banny Jul 15 '13 at 09:24
0

No. It will change the size of the parent div.

I would like to quote Quentin's answer.

The font is a proportional font, so it gets thicker (and takes up more horizontal space) when it is made bold.

Find more information here.

Community
  • 1
  • 1
Praveen
  • 55,303
  • 33
  • 133
  • 164
0

Here how I have done it:

<ul id="header_menu">
  <?php
  $menu_items = array(array("Home", "/"),
                      array("About", "/about.html"),
                      array("Consumer Info", "consumer-info.html"),
                      array("News & Events", "news-events.html"),
                      array("Contact", "contact.html")
                      );
  foreach($menu_items as $value)
  {
    // Find box width of text and add padding
    $padding = 10;
    $font-size = 12; // pt
    list($left,, $right) = imageftbbox($font-size, 0, "fonts/OpenSans-Bold.ttf", $value[0]);
    $width = ($right - $left) + ($padding * 2);

    echo "<li><a style=\"width:" . ($width) . "px;\" href=\"" . $value[1] . "\">" . $value[0] . "</a></li>";
  }

  ?>
</ul>
AndrewT
  • 11
  • 2
-1

If you put the width attribute in your .navItem class, then the width wouldn't be changed on hovering.

.navItem {
    display: block;
    float: left;
    position: relative;
    **width:70px;**  // this
}

I have udpated the fiddle. Please check.

Khadim Ali
  • 2,548
  • 3
  • 33
  • 61
  • The issue with assigning a width is that i've made it so that there is no width otherwise i have to have the tab for `Mens` being unnecessarily wide as the tab for `Accessories` – Banny Jul 15 '13 at 09:10
  • Then you may assign width to
  • elements individually.
  • – Khadim Ali Jul 15 '13 at 09:12
  • I think i'll find a different way of identifying the active tab, if there is no way to stop the width fluctuating based on font-weight without a set width then i'll do something colour based instead i think – Banny Jul 15 '13 at 09:15