Frames or Iframes with CSS

A frequent request from people new to CSS is trying to get a behaviour similar to frames while only using CSS. Many have read that frames have a lot of problems but do not know much more than that. This article will try to explain why frames are bad and what to do about it.

Update: I’ve updated this technique using position: fixed in a new article, have a look at those templates instead! (Although this article is still good for background information).

Why are traditional frames such a bad thing?

Let me start by just listing a few bad things about frames.

  • Prevents users from bookmarking the exact page they’re on. Instead the bookmark will lead to the frameset. This is clearly not a good choice for larger sites where finding a certain page might be a problem, but even users on smaller sites might find it annoying. I do.
  • Poor support by some parsers leads to problems. Many screen readers, mobile phones, and search engines have problems parsing frames. This means some of your users might miss out on your content.
  • Content order problems. Just like with tables, frames give screen readers problems when deciding which order to read the frames. If it reads a 2×2 frameset will not know whether to read the content like two rows or like two columns. The order might be important for understanding the content and frames has nothing built-in to help with that.
  • Search engines might split the ranking up on the different pages. I just discovered this by looking at one of my old pages (no, I won’t show it, it’s terrible :). There, the PageRank has been split up on the framed pages and the frameset has no rank whatsoever.
  • Invalid code. If you’re using separate frames for navigation and content your links will need the target attribute to make links from the navigation frame open in the content frame. That attribute is not supported when using a strict doctype so you will have to use transitional. The doctype won’t matter if you want to get rid of borders cross-browser though, to do that you need invalid markup.
  • Printing is a problem. How should the different frames be handled while printing? There’s no good solution to this.

People that are good with javascript sometimes say they like frames because they make it possible to save information about the user even though the user clicks links. A variable can be saved in a frame and be accessible to the other frames even after they are reloaded. Another pro-frames reason frequently heard is that it saves overhead since not the entire page gets reloaded.

Personally I think that the first is a bad reason to use frames. Using server side scripting to save session info is much more robust since that will mean the applications still work if the user reloads the page. The other reason of only reloading a part of the page is nowadays done by using javascript together with XMLHttp (some like to call it AJAX). To me, the bad parts of frames greatly weigh over, even for applications.

Getting a scrollbar on any element using CSS

Aside from all of the above frames are not structure. Think about it. When working with frames you define that you want a couple of rows and columns, which should scroll and which shouldn’t, and which should have borders. These are things that many of us would never dream of doing with tables but some still use frames like that.

Instead we should define that kind of design in our CSS files. How? By using a few lines of code you can easily limit any element to a certain width/height and let it scroll if the content overflows that size.

Here's some sample content that I would like to scroll
p {
   width: 200px;
   height: 80px;
   overflow: auto;

It’s easy, you’re saying: “limit the paragraph to this size and give the box a scrollbar if the content gets too big” (Other valid values for overflow are “hidden” and “scroll”. The former just cuts everything that sticks out, that latter adds the scrollbars even if they’re not needed).

The above method works when you want something that’s similar to iframes but it doesn’t work right away for traditional frames. Those usually span the whole width or height of the page and leave the rest of the page scrolling. But what stops us from taking any element and stretching and placing it like a frame? Nothing. Let’s do it.

Mimicking traditional frames

The idea here is to use divisions to group our content. Then position those divisions as if they where frames and limit their site and set overflow: auto like we did above. Frames use the syntax cols="200,*" to say that the first column should be 200px wide and the second one “cover what’s left”. The second part is a bit harder to do with CSS.

There’s no way to say 100%-200px but there’s a trick we can use: floats push the content away. So if we set a width on the left block to 200px and float it left, the other block will be as wide as possible (default for block level elements) but it’s content will be pushed 200px to the right. Here’s an example of two columns with a fixed left in action. By just changing float: left; on navigation to float: right; we get the other example: Two columns with the right one fixed.

(Technical note: we don’t need to add any margin-left since overflow: auto; clears too).

The above works well for columns but what if we need rows instead? When using frames we specify rows="100,*" and as I said this is not possible directly with CSS. We can’t use the above trick either since we’re dealing with height here. Setting height: 100% will just make it as high as the window, leaving no space for the other row. The solution here is to cheat a bit. If we make the first column 100px high and the lower one 100% high but then remove the first one from the flow we will be pretty close. The first row will be on top of the other one but that can easily be solved by adding a padding-top the same height as the first row.

We’re almost there now, one last thing: the latter row scrolls on top of the first one because of the order in the source (latter elements stack on top of earlier ones). So what we do is force the first one on top with position: relative. This solves the problem except that it also covers the scrollbar now. An easy (and ugly fix) is to add a margin-left of 16px to the top column making the scrollbar visible again. Browsers seem to use the same scrollbar width so that’s it, an example of frame rows using CSS.

I’ve tested this in IE 6, FF 1.5 and Opera 8.5 on WinXP and it seems to work fine. Broken in any other browsers? Let me know through the comments.

When looking at this I see that this is also a good way to mimick blocks that are position: fixed, something that doesn’t work in IE. This method might be good in a situation where you need that one.

31 responses to “Frames or Iframes with CSS

  1. Pingback: kbglob
  2. My problem (and you probably explained it- please excuse my ignorance)…but In the Html editor I put the code for the html and on the topstyle file I list the css content I can not see how they converge. I opened a pratice page on Geocities and have added both saved files in the file manager. but it does not come up as one page. to give me the green back ground with the the title that I have placed on it my pratice link as follows can u see what I have done wrong…I would really like to know how to improve my skills so that when I open my real web site things will flow more easily.Thanks for your time

  3. I like your article. However I encountered a problems with the frame columns sample:If a picture (in my case 640×480 of size) is embedded into the text and IE6 is reduced to a size where a horizontal scroll bar would be needed the complete right side vanishes.
    Whith all that nice specifications that are meant to replace the evil we used before we seam again to get stuck in what should work but doesn’t.
    Anyway thanks for this and your other articles. They are very helpful.

  4. @Michael: I see the problem. IE has strange ideas of how overflow works and I think the problem is that it expands the column even though it shouldn’t. Since overflow: auto; is set it should instead display a scrollbar.

    In your case I think your should just let that be. I have 1% users with 800×600 resultion on this site, does your bug affect many enough so that it warrants a hack?

    [Update: Setting a width on that columns stops it from happening, is that a solution in your case? (might make linelengths a little nicer too…]

  5. Hej! Letade efter svar på frågan om man verkligen kan använda css som frames och fann din sida.

    Har sett en del ha en sida med div-element vars placering styrs med css. I varje sådan lägger de sedan ett html-dokument.

    Det fungerar men verkar inte riktigt rätt. Vad är det som gäller?

    Hälsningar // Jonas

  6. @Jonas Ahlberg: Hejsan. Tanken med artikeln till höger är att förklara precis det.

    Men låt mig ta det igen lite kort: du använder först CSS för att få frames-liknande beteende. Sedan använder du något server-side språk (PHP, ASP eller liknande) för att lägga in t.ex. navigationen på alla sidor.

  7. I love the way you are developing this site, great information.

    However, this page is not cross compatible. The scrolling paragraph does not work in IE6.

  8. @Jonathan: That’s just me being a bit unclear. If you cody the code shown here to a page and try it out it will work. I have not set any width on that exact paragraph in my own code so it doesn’t scroll in IE.

  9. @Emil Stenström
    your post 04 in reply to 03
    Thanks for your suggestion. However it doesn’t really solve the topic. As on larger window size the scrollbar is not on the right any more and the content still vanishes when the window width is smaller than width of navigation + width of content.
    The image size was just a sample.
    If you assume an image of 1280×1024 the percentage of visitors where this fits on the screen would be lower.
    However I have no concrete problem. I just discovered this effect when I was playing around with your (great!) samples.
    My comments may have been capable of being misunderstood as I think that CSS is a step in the right direction and using it has many advantages compared to coding the layout directly in HTML.
    The problem that persists is that (browser) developers interpret and implement the specifications differently, forcing us to use workarounds or browser specific techniques.
    Your site is a great help to achieve this. I was just moaning about the imperfect world…

  10. The notes on Cascading style sheets was excellent.

    I liked it very much and i used it in my personal website.

    Thank you,

  11. How would you get these to work if you wanted both a side bar for navigation and a titlebar at the top. then the frame for content?

  12. “When looking at this I see that this is also a good way to mimick blocks that are position: static, something that doesn’t work in IE. This method might be good in a situation where you need that one.”
    I suppose you meant position:fixed



  13. Hello Emil,

    Excuse my ignorance here, I stumbled upon your site looking for some CSS help. I’m trying to use a 1024X768 jpeg as a background for my site. Simple but I would like to fix the size so the browser reflects the size of the background. I’m trying to prevent tiling when it’s too small and underscan when the image is too big like it is now at 1024X768. I’m looking at it on a 14 inch iBook. Hope this makes sense.

    Thanks for any help.

  14. @William: try just making a div with the size you want and setting a height and width of the same size as the image. Then add the image as a background.

    I’m not sure if that will work in your case, what will you do if the content overflows that area? how will you handle people will a smaller resolution? Designing for the web is quite different from print design.

  15. But how do you encorporate links with this? like with I-frame.
    Example: the top row would be stationary and have the links and the bottom row would display the pages of the links.

    is this possible to do with only CSS?

    please help.

  16. @Alina: You use server-side scripting for that. If you use PHP you can use something like

    if ($_GET["page"] == "home")
    else if ($_GET["page"] == "info")

    . Then you use template.php?page=[page name here] as your url.

  17. Thank you Emil. This is just brilliant.
    I have spent some hours finding much more difficult and useless solutions to the “position:fixed;” problem in IE. Yours is simple, elegant… and works!! I have to test it in some more browsers but it looks great.
    And easy to understand for an spaniard like me ;-)

  18. Hi. I really like this solution, but I just thought I’d let you know that none of the examples even appear in IE Mac! Maybe you don’t worry about these users, and I’m not an IE Mac user (just use it for testing) but I do know people that still browse with it!

  19. @tom: Thanks for letter me know. It was pretty expected though. IE Mac is no longer supported by Microsoft and they themselves recommend people to upgrade to Safari or Firefox. Mind recommending those (excellent) browsers to your friends?

  20. how is the compatibility of this scrolling thing with ie? heard that its not so good…?

  21. The article about using css to replace frames was extremely useful. I modified the two parts so that I have a header at the top and a navigation on the left which are fixed, whilst the content scrolls. The only problem I have found is the content of the paragraph “makescroll”. It can include pictures, tables etc but if a “center” instruction or H1 H2 etc is used, the layout goes haywire. Very helpful article – thanks.

  22. I have studied some of your information here, and I always learn something each time. Thank you for this site.

    I am attempting to get away from using frames. I have only one page using frames with selection buttons on the left selecting what is placed in the right frame.

    I read your post above about using PHP, and will have to study that some to see if I can make it work.

    My PHP knowledge is limited, but in your post, I did not see a closing ?> and wonder if it is required? Currently, all I am doing with PHP is making sure any changes to my menu is applied to every page, and every page uses the same footer.

    Again thanks for the PHP code that I am now off to study.


Comments are closed.