A complex, flexible table layout

Posted March 25th, 2009 by spanky

Greetings!

I was given the task of building what appeared like a relatively simple table for work, but in the end it turned out to have quite a few challenges, and I figured I’d document how I did it in case someone else wanted to give it a try (or I had to do it again.) NOTE: I originally did this with DIV’s and reverted to a table, and some of the CSS was more important then. First, let me show you what I was given:

The Photoshop file

The Photoshop file

OK, so you can see at first glance it looks pretty simple. Here are some of the tricky parts:

  1. The height must be flexible, since different browsers will have different font sizes
  2. The cell borders must overlay the shaded border
  3. There is a slight gradation in the background of the table
  4. Table has no outside border

So, here’s how (I think) I did it.

First the entire table is wrapped in a div with id “#left” for positioning and isolation purposes. (The page had another smaller box floated right.) The #left div has a set width and a background image. The background image is sliver of the gradation and is set to repeat-x and to sit at the bottom:


#left {
width: 651px;
background: transparent url('/graphics/pricing-box-background.png') repeat-x bottom;
}

Next we define a table inside the DIV with the same width, no cellpadding, border, or cellspacing. Yes, I’m still old-school like that.

Here’s where it gets tricky. All the rows (TR) get a class of “row”, but the first and last row get ID’s of “first” and “last”. Each table cell (TD) gets a class of “column” and a class of “column-1″ or “column-2″‘ depending. (The first column has to be wider than the rest, but the rest have to be equal.) Here’s the HTML:

OK, now that might look simple, but the CSS is, well, involved. Let’s start with the “row” class:


.row {
width: auto;
margin: 0 auto;
background-color: transparent;
background-image: url('/graphics/pricing-box-border.png') repeat-y;
position: relative;
}

Things of note here: we style the row with a background PNG that is 651px wide and consists only of the shaded left and right border of the table, transparent everywhere else. This gets the edges of each row. Originally I had done this to the entire #left div, but for some reason I forget now, that didn’t end up working out, so I decided to do it to the row. IN the end I had to add “position: relative” to solve an IE 6/7 problem where background images applied to a row are inherited by the table cells contained in it. The other half of this hack is setting the TD’s to have no background image. See below.

Now to style each cell. I called them columns. Here’s the column CSS:


.column {
border-left: 1px solid;
border-top: 1px solid;
border-color: #ccc;
border-width: 1px;
background-color: transparent;
padding: 4px;
vertical-align: top;
background-image: none;
}

Things of note: the order I’ve defined the border widths and colors seemed important at the time but now I don’t see why. I do know that applying the border to the top rather than the bottom was significant because if you apply the border to the bottom, you have the problem where not all the cells are the same length…at least when they were DIV’s. It still seemed prudent to keep the borders on the top. I also put a border on the left, rather than the right because we have that second unique class on the first column (.column-1) where we can shut it off. The background-image: none is the second half of the IE6/7 hack where the cells inherit the background image of the containing row.


.column-1 {
width: 30%;
border-left: none;
padding-left: 12px;
}

Things of note: This column is wider than the others. I used percentages because I absolutely hate trying to calculate margin/padding/width nightmares. Give it a percentage, make them add up to less than 100% and most padding shouldn’t self-destruct the table. As you can see we turn off the left border in the first column to achieve the empty outside border of the table on the left side. I also want the text to have some breathing room, so I added a little left padding.


.column-2 {
width: 20%;
}

The rest of the columns are all the same width, adding up to 60%. Add 30% and you get 90%. This swallows up the remainder added by padding/margins, which are all calculated differently under different circumstances/browsers, so I just fudge it and come under. Note that because we chose to use the left border before, we don’t have to deal with shutting off the border on the right side. Easy.

Now we start to pile some things up using precedence.


#first .column {
border-top: none;
padding-top: 6px;
}
#first.row {
background: transparent url('/graphics/pricing-box-top.png') no-repeat top left;
}

Here we simply turn off the top border for the first row and give some breathing room to the titles. Also we add the shaded rounded PNG to the top row to round out the shaded border of the table. Note how the interior table border sits on top of this background image seamlessly. Nice.


#last .column {
border-bottom: none;
padding: 9px;
}
#last.row {
background: transparent url('/graphics/pricing-box-bottom.png') no-repeat bottom left;
}

Not much special here (and some unnecessary code, oops) to add the shaded border to the bottom. One thing of note here. Because the DIV (#left) has a gradation for a background, is positioned to the bottom, and sits UNDERNEATH the rounded corners, which are transparent, the gradation pokes through on the bottom corners. If you look carefully, I’ve added white to the corners to prevent this from happening.

Right now, this page can’t be viewed live, but when it’s live, I’ll post the URL in all its glory.

All in all, it’s way more work than it should be. Now that I look back, it’s way easier to explain than it was to do, simply because I tried a few things here and there. I might have to go through and lint pick the CSS for leftover stuff from when I was doing it with DIVs. I hope you liked this, if so, click my Google Ads a few times.

~Spanky

Plans:

Free

Unlimited

On-Demand

Comments are closed.