November 26th, 2007 - by Paul OB

If you look at most restaurant menus (or recipes) you will see the dish described on the left hand side followed by a dotted line that continues to the right side of the menu where the price is situated. Have a look at Figure 1 to see what I mean.

Figure 1

Although this may look like an easy thing to replicate in HTML it’s really not as straight forward as it looks and there are a few obstacles to overcome if we want to reproduce this effect. So that’s exactly what we are going to learn how to do in this article!

Dotted Leaders

As this is clearly a list of sorts, CSS can handle this nicely (although an argument could be made for using tables also). As this column is about CSS we will continue with the CSS method. The most semantic element to use for this is obviously a list; so that’s what we will use.

There are two main issues to overcome:

First we must place the text on both the left and right sides of the same line.

The second problem we will need to overcome is how to make the dotted line continue from the text on the left and stop at the text on the right. This would be simple if the dotted line was underneath the text as we could simply use a border on the list. However, that’s not the way that these menus are usually presented so we are not going to cheat on this.

Left and Right

We’ll tackle the positioning of the text first. In order to have the price on the right hand side we will need to make use of the float property and either float the price to the right, or float the descriptive text to the left and let the price be aligned by text-align:right. Or alternatively float each part left and right. Any of those methods would work but to keep things simple we are going to float both elements; one left and one right.

In order to isolate each section of text we will need some hooks in our mark-up and for this purpose we are going to use an em for the descriptive text and a span for the price. This allows us to target them uniquely within the list item without needing to resort to adding any extra classes at all. You should always try to minimize your mark-up like this and when the chance arises target the element via its situation rather than just tagging another class to it.

Here is the basic HTML for the list element:

  1. <ul><li><em>Some menu text</em><span>£9.99</span></li></ul>

Assuming our list container has sufficient width for this to happen we will just float the em left and float the span to the right.

  1. #wrap li span{
  2. float:right;
  3. }
  4. #wrap li em{
  5. float:left;
  6. font-style:normal;
  7. }

As the contents of the list are floated they will float out of the list parent unless we stop them in some way. We can do this quite simply by floating the list also, which will make it contain its child floats (see the previous article on clearing if you are not sure about this topic. So we will float the ul thus containing all children.

  1. #wrap li{
  2. width:100%;
  3. float:left
  4. }

That will give the initial alignment that we wanted and with a little extra mark-up we soon have the effect shown in Figure 2.

Figure 2

I won’t bore you with the full code as you can grab that from the live demo in the link at the end of the article. It’s just basic CSS so far using an unordered list and then floating the elements as described above. As an aside (and as a matter of interest) I have been asked how I made the shadow effect around the element and this can be seen more clearly in Figure 3.

Figure 3

This is a simple technique and involves using a wrapper that has both a background color and a border in suitable colors. Nested inside this wrapper is our main element which has its own border and background color applied. We simply offset the inner element using relative positioning to reveal the background of the element underneath in the gap that we created by moving it. Relative positioning moves the element without affecting the flow of the document at all and leaves the parent in place creating an offset effect.

  1. #wrap{
  2. width:500px;
  3. border:1px solid #eff2df;
  4. margin:0 20px;
  5. float:left;
  6. background:#809900;
  7. }
  8. #wrap ul{
  9. padding:20px 40px;
  10. list-style:none;
  11. float:left;
  12. border:1px solid #4c7300;
  13. position:relative;
  14. left:-2px;
  15. top:-2px;
  16. background:#eff2df;
  17. color:#4c7300;
  18. }

Although the ul element doesn’t have a width it gets stretched to full width by its children; but normally you would keep the element static or give it a width. By using suitable colors you can get quite a good shadow effect with little effort. The border of the wrap should be a lighter color than the background and adds a nice finishing touch to the effect.

Join The Dots

Getting back on track — our next step is to create the dotted lines that join the left and right sections of text together. We can achieve a full length dotted underline by simply adding a dotted line to the bottom border of each list item.

  1. #wrap li{
  2. border-bottom:1px dotted #000;
  3. float:left
  4. }

Figure 4

That’s looking pretty close but now we have to hide the dotted line at each end (left and right) and move the dotted line in a better horizontal position with the text. This can be accomplished relatively easily by using relative positioning (yet again) and moving the text down on top of the dotted line at each end. Because relative position will not affect the flow of the document, the dotted line will not move away from us as it would have done had we used a margin to move the text downwards. (For more information on relative positioning see our previous article “Relatives who Needs Them“.

You may be thinking here that a few pixels movement would be all that’s needed but that would be very short-sighted of us. If we move the text down with a pixel measurement then as soon as the user resizes the text from the browser controls, the dotted line would become visible again. We need to use an em measurement so that the distance moved also increases (or decreases) relative to the text size selected and ensuring our dotted line remains correctly hidden at each end.

Therefore we move the span and the em downwards resulting in covering the dotted line at each end, just as we wanted.

Well that’s not entirely true!

The text is now on top of the dotted line but is not actually hiding it. In order to hide the dotted line we need to give the em and the span a background color to hide the dots completely. This color needs to be the current background color in order to provide a seamless join. We will also take this opportunity to add a little padding to the elements to tidy the effect up resulting in the following code.

  1. * {margin:0;padding:0}
  2. h1,h2{padding:10px 20px 0}
  3. #wrap{
  4. width:500px;
  5. border:1px solid #eff2df;
  6. margin:0 20px;
  7. float:left;
  8. background:#809900;
  9. }
  10. #wrap ul{
  11. padding:20px 40px;
  12. list-style:none;
  13. float:left;
  14. border:1px solid #4c7300;
  15. position:relative;
  16. left:-2px;
  17. top:-2px;
  18. background:#eff2df;
  19. color:#4c7300;
  20. }
  21. #wrap li{
  22. border-bottom:1px dotted #000;
  23. line-height:1.0;
  24. margin:0 0 .5em 0;
  25. position:relative;
  26. width:100%;
  27. float:left
  28. }
  29. #wrap li span{
  30. background:#eff2df;
  31. padding:1px 0 1px 5px;
  32. float:right;
  33. color:#000;
  34. position:relative;
  35. top:.2em;
  36. }
  37. #wrap li em{
  38. float:left;
  39. margin:0;
  40. position:relative;
  41. top:.2em;
  42. padding:0 5px 0 0;
  43. background:#eff2df;
  44. font-style:normal;
  45. }
  1. <div id="wrap">
  2. <ul>
  3. <li><em>Some text</em><span>£9.99</span></li>
  4. <li><em>Some text a bit longer</em><span>£9.99</span></li>
  5. <li><em>More text</em><span>£10.00</span></li>
  6. <li><em>Other text a bit longer</em><span>£11.00</span></li>
  7. <li><em>text</em><span>£12.00</span></li>
  8. <li><em>Some text a bit longer</em><span>£9.99</span></li>
  9. <li><em>More text</em><span>£10.00</span></li>
  10. <li><em>Other text a bit longer</em><span>£11.00</span></li>
  11. <li><em>text</em><span>£12.00</span></li>
  12. </ul>
  13. </div>

Figure 5 shows the result of the above code:

Figure 5

You can see a live demo here.

I have used the universal global reset method for this demo (*{margin:0;padding:0}) but I would advise using a more thorough reset method so that form elements are not affected. See a previous article for more information on this topic.

Recipe for Disaster

That’s quite a good little effect for relatively simple code. You can resize the text up and down and the layout still holds together quite nicely. If we were feeling lazy we could call it a day here as we seem to have created the effect we wanted. However, looking further ahead we need to think about what happens when the descriptive text is much longer or even runs to two or three lines. What is going to happen to our layout then?

We can soon test that out by adding more text to one of the lines. You can see the result in Figure 6 below.

Figure 6

Although our example is still readable it’s not a very good effect and ruins our nicely presented list. What we really want to achieve here is for the text to wrap to a new line and the dotted line to stay in position on the bottom line of text. We also want the price on the right hand side to stay at the right end of the dotted line. The effect we are looking for can be seen in Figure 7 below.

Figure 7

The descriptive text wraps to a new line and the dotted line continues directly to the price on the right. Although this looks as though it will be quite simple, it in fact turns up that there are a number of new obstacles to overcome. Firstly, look back at Figure 6 and see if you can understand why the dotted line has moved away from the text and not stayed in line with it? You may have thought that the text would just wrap at the end of the line and everything would be fine.

The problem is that we have floated both elements which means that should our content stretch, the element will eventually fill all the available 100% width thus pushing the other float out of the way and onto a new line. That’s exactly what has happened in Figure 6 where the descriptive text has stretched all the way along the line pushing the price out of the way. If you are wondering why the price seems to have dropped down two lines that is because a float is a rectangular box and therefore the float on the right (the price) must clear the whole rectangular block that the other left float creates.

If we outline the float in Figure 6 using a red border it soon becomes clear and easier to understand (Figure 8).

Figure 8

When the width of the left float becomes 100% it shoves the right float down below it thus also pushing the dotted line down and away. Again, it might be thought that an easy solution would be to give the descriptive text a width that stops it from reaching the float on the right. We could do this quite easily and allow enough room (to cover normal situations) for the text on the right. However, as can be seen in Figure 9 things start to get worse instead of better when a width is added!

Figure 9

The first thing to notice is that the dotted line is now missing because we have set the width and therefore the background rubs out the dotted line all along that width. Also the price is now too high on the line with the longer text. Obviously we can’t use this method – so what can be done? Ahaa… what if we don’t float the left side but let it remain static instead and also do away with the width?

If we just float the right side and let the left side be static we would first need to change the HTML order so that the floated content comes first in the HTML (before the content that we want to wrap around it). (As an aside it should have been possible to leave the HTML alone in this case as floats should stay on the same line as inline content alongside it in the HTML. Floats should only move below block level content. However only Opera gets this right so we have to change the HTML around anyway.)

  1. <li><span>£11.00</span><em>Other text a bit longer</em></li>

So now we have moved the span containing the price text to the front of the HTML for each line — that should allow the text to wrap around as we hoped for. Figure 10 shows the result of these changes.

Figure 10

That’s got our layout looking closer to what we want but now the price is misplaced on the line that is wrapping. The price on the right is floated and when the text wraps to another line the right float just stays where it was. Why should it automatically move down anyway?

It seems as though at every step a new problem occurs and we are getting no nearer to achieving the effect we wanted (which is seen in Figure 7). It turns out that our current plan of action will fail to achieve the results we are looking for and we need to approach this from a different angle altogether.

Using our current design there is no way to make both the descriptive text and the floated right text both move down at the same time when the text wraps. Therefore we are going to change the design and move the right float onto the next line to start with. The descriptive text will be put in a block element (not floated) and that will ensure that the right float is always one line below the text even when the line wraps. This will give us a consistent measurement to work on and we can offset this known distance by using relative positioning once again.

The descriptive text and the right float now start on their own lines as shown in Figure 11.

Figure 11

This ensures that the price is always one line below the descriptive text. Then all we need to do is to drag the descriptive text downwards by one line height (more or less ) and it will always be in the correct position. If we control the line height and use ems for our measurements we can make sure that the whole thing always scales together. This may take a little trial and error to get things exact but it will provide us with the effect we have been striving for. We also need to push the price downwards to cover the dotted line as in our first example. This downward movement is done with relative positioning so that the lists’ bottom border doesn’t move.

This brings everything nicely into position with one slight drawback in that we have a one line height gap above the descriptive text which spaces the lists out a bit too much. This can be overcome by applying a negative top margin to the list to offset this gap. Once again we use ems so that the layout remains solid and will scale well. The exact dimensions can be tweaked depending on your situation and a little trial and error goes a long way.

There is one last issue to deal with — we would like the descriptive text to wrap before it meets the price to keep everything looking neat. I have added a 5em right padding to the p element that is now holding the em and the descriptive text. I have just guessed that 5em will be enough but we should be safe anyway because the line-height is such that the rows will not clash should the text from both sides meet.

We can now put our HTML back into a more logical order resulting in the list item looking like this.

  1. <li>
  2. <p><em>Some text a bit longer</em></p>
  3. <span>£9.99</span>
  4. </li>

There is an extra p element inserted (to satisfy Opera’s behavior as already mentioned previously) and the span once again moved after the text but also after the p element. (I have left the span as a span because we have been using it throughout the demo but it would be more semantic to use a block element now such as a p element. That would of course mean using a class to differentiate the styles or perhaps using a div instead of a p element to keep it unique. For the sake of clarity I have left it as a span for now.)

Anyway back to the example — we can see what effect the following CSS will have on our design. I have only shown the relevant parts so refer to the live demo at the end of the article for the full code.

  1. #wrap li{
  2. line-height:1.2;
  3. margin:-.9em 0 0 0;
  4. position:relative;
  5. float:left;
  6. width:100%;
  7. text-align:left;
  8. border-bottom:1px dotted #000;
  9. clear:both;
  10. }
  11. * html #wrap li{
  12. border:none;
  13. background: url(images/dotted-leader.gif) repeat-x left bottom;
  14. }
  15. #wrap li span{
  16. background:#eff2df;
  17. padding:1px 0 1px 5px;
  18. color:#000;
  19. position:relative;
  20. top:.4em;
  21. float:right;
  22. }
  23. #wrap li em{
  24. margin:0 ;
  25. position:relative;
  26. top:1.6em;
  27. padding:0 5px 0 0;
  28. background:#eff2df;
  29. }
  30. #wrap p{padding:0 5em 0 0}

The result of this can be seen in Figure 12 below.

Figure 12

Hmm…. looking good!

The above screenshot is from Firefox 2.0, I wonder what IE6 looks like?

Here we go…

Figure 13

Looks pretty good but what’s happening with the nice dotted border? They look like dashes and are a bit chunky for the design. This is an old IE problem where IE6 (and under) will display dashes instead of dotted borders when the border size is 1 pixel. There is nothing we can do about this. Or is there?

In fact we can make IE6 look much better by doing away with the bottom dotted border and using a background image instead. We can just give it to IE6 and under like so.

  1. * html #wrap li{
  2. border:none;
  3. background: url(images/dotted-leader.gif) repeat-x left bottom;
  4. }

The border is negated from the list and then a background image is used instead. The star selector hack ensures only IE6 and under gets that style rule.

That just about wraps it up and you can see the full demo in this live example. View sources to grab the full code as the CSS has been left in the head for you to get easily.

Try scaling the text size up and down and you can see that the layout is very solid and the effect is maintained. To finish we can see what the design looks like across a range of browsers.

108 Responses to “CSS – A Recipe for Success”

[…] 33. CSS Recipe for Success […]

2 Domki pod sosnami

thank you for sharing

3 Domki pod sosnami

Very good article. Thanks.

4 Sukienki

Wery nice 🙂

5 Sukienki

Very nice 🙂

[…] » CSS – A recipe for Success […]


Greate article, thanks.

Going to make some changes on my site with your tips. :))

8 Sklep Calivita

very interesting and instructive.
Such CSS is a sure success

9 panele

Thank you information was useful!

10 twojeanonse

very interesting article 🙂

11 twojarandka

Very interesting article – thanks. I really enjoyed reading all of your articles. Keep up the good work.

12 O ciuchach

Very good article. Will help me a lot. Hope to take advantage of it on my site soon.

13 omega2

Excellent article. Have been looking for something similar for a while. I need to do a menu with the item name and a description then the leader and the price. I will post when I have it resolved.

14 BenSky

Interesting, bookmarked this for a restauraunt website i will be doing soon, you should try a 3 column one now! 🙂

15 garmin1370t

Thank you, useful article.

16 JohnX

Very cool! I only found one small error, you forgot to close the last p-tag (right before the last div).

17 Paul OB

@JohnX – thanks – fixed 🙂

[…] 33. CSS Recipe for Success […]

19 bigyaz

Very clever, but it’s sad that something as simple as dot leaders — which have been a breeze since the early days of computerized typesetting — require such contortions in CSS.

20 Walkman69

I’ve been looking for this solution for days.. It’s wonderful.. Thanks!

21 kaos mercury

nice info… thanks

22 CSS – A Recipe for Success « FlagMag

[…] CSS – A Recipe for Success Categories: CSS Tags:CSS, CSS tricks, Menu, restaurant Kommentarer (0) Trackbacks (0) Skriv en kommentar Trackback […]

23 Sylwester w Górach

At least goog article about CSS thanks a lot, i try to use it now.

24 Sukienki

Very useful article. Thanks!

25 Hooman

Wow, that was such a great article thank you

26 grinding mill

Nice article. I just stumbled upon your blog and wanted to say that I have really enjoyed reading your articles. Anyway I’ll be subscribing to your feed and I hope you post again soon. thanks for share!

27 mill

You are sick and twisted madman to come up with that example!! It’s freaking brilliant. Most of us would have just said to hell with it and left the dotted line under the text, but you my friend, well that was crazy thinking outside-a-the-box is what that was.

28 portable crusher

Great article Paul, I would have just stayed with a less robust solution – but you take it above and beyond – Great work.

29 crusher machine

Very interesting article – thanks. I really enjoyed reading all of your articles. Keep up the good work.

30 vertical roller mill

At least goog article about CSS thanks a lot, i try to use it now. thanks for share

31 baterie słoneczne

Thanks for really good article. It seems that someone did a great work for all of us. What else I could say? Keep up the good work and don’t be shy 🙂

32 Rob

Very useful and and comprehensive article thanks for sharing Rob

33 adam

Nice Example 🙂 I wonder if dl dt && dd tags would have been a better element to use, considering your defining something with a cost. just a thought 😉

34 Paul OB

Hi Adam,

Thanks for your comments 🙂

Yes a DL would be a good choice and in fact in post #36 I have linked to a dl version and a table version just for completeness.

A good case can be made for either/all depending on circumstance.

35 Sukienki

Very helpful. Thanks.

37 Smykker Online

I like this website, you have many great articles 🙂

38 Omega2

I found a similar way to do this using CSS definition lists. You can see it at

After a bit of CSS, the menu items are entered as:

Item Name
£ x.xx
decription of food

It is easy to use and update. The method has proved very robust and can cope with multiple prices per item etc very well. Also remains readable without the CSS.

Great site and keep up the good work!

39 Omega2

just to clarify my last post:

items go in as:

2. Item Name
3. £ x.xx
4. Item Description

40 Omega2

sorry – that keeps stripping the DL, DT and DD code

41 Paul OB

@Omega2: Thanks for showing your method it looks fine for single line items. Well done.:)

It doesn’t work if the first line wraps to the next line though so couldn’t be used in a fluid layout of for menus with more text on that first line (unlike the one in the article).

However it works well for you so that’s fine and looks good.

42 Audrius

Hi Paul,

Thanks for sharing such a nice method. Nice work really!

While trying it on my website (which is not publicly available yet), I noticed strange behavior. When you put links on list items or spans it is very difficult to activate them with a mouse! I mean clickable area (where the mouse pointer changes to hand) is very small and is located just above the linked text. And I noticed that only on Firefox v3.6. On IE8 it behaves as expected. I did not tested it on Opera yet. I am using your example No 7 (list-with-dotted-leader7). Is it possible to fix that? It is a bit annoying.

43 Paul OB

Hi Audrius,

Did you see my comments at the bottom of this demo:

If none of those work for you I’ll take another look 🙂

44 Smykker Online

Another great article 🙂

45 Audrius

I just tried it with an overlapping fix from example8 and it works perfect now! Thanks for such a fast reply!

46 omega2

Used this a few times now with some minor tweaks and it works really well.

You can see an example at

Thanks again.

47 Paul OB

Looks good Smykker 🙂

48 Jeanne Chin

This is great! Thank you for sharing! I learned so much by following your well explained process. I was almost successful using the version to create this page, but, alas, it doesn’t work in ie7 – the absolute position of the prevents it from scrolling. Although, I may just have an extra div… I don’t know. Any suggestions?

49 Paul OB

@Jeanne: Try adding position:relative to .frameSand and to .wrap.

Positioned elements become fixed when they are children of parents with overflow set in IE7 and under unless you position the parent as well.

50 Jeanne Chin

Wow! Child is the father to the man! Thank you SO much!

mulberry sale spyder womens jacket cheap new balance 574 mulberry outlet cheap new balance 574 arcteryx outlet mulberry sale spyder womens jacket mulberry sale spyder womens jacket mulberry outlet mulberry outlet new balance 574

Popular Articles

Top 10 Commentators

Subscribe to this feed! Subscribe by Email!

Random Bits Podcast

You need to download the Flash player from Adobe

Blogs Worth Reading