Symphonious

Living in a state of accord.

Alternating Table Row Colours Filling All Available Space

CSS3 makes it trivial to have alternating row colours for tables, but when the table is in a fixed height scroll panel, it’s much more difficult to have those alternating row colours extend beyond the bottom of the table content to fill the available space. Here’s the approach I use.

First let’s start with a simple table with alternating colours in a scroll panel:

<!DOCTYPE html>
<html>
<head>
<meta charset=UTF-8>
<title>Alternating Rows Example</title>
<style>
* { margin: 0; padding: 0; }
table {
  border-collapse: collapse;
  width: 100%;
  height: 100%;
}
.scroll {
  position: absolute;
  top: 0;
  bottom: 0;
  left: 0;
  right: 0;
  overflow-y: scroll;
  overflow-x: auto;
}
.scroll tr {
  height: 20px;
}
.striped tr:nth-child(odd) {
  background-color: #EAEAEA;
}
</style>
</head>
<body>
<div class="scroll">
<table class="striped">
<tbody>
<tr><td>Row 1</td></tr>
<tr><td>Row 2</td></tr>
<tr><td>Row 3</td></tr>
<tr><td>Row 4<br>With an extra line</td></tr>
<tr><td>Row 5</td></tr>
</tbody>
</table>
</div>
</body>
</html>

That gets us alternating row colours where there is table content. To get it to extend beyond the table content we need to add a filler row:

<div class="scroll">
<table class="striped">
<tbody>
<tr><td>Row 1</td></tr>
<tr><td>Row 2</td></tr>
<tr><td>Row 3</td></tr>
<tr><td>Row 4<br>With an extra line</td></tr>
<tr><td>Row 5</td></tr>
<tr class="filler"><td></td></tr>
</tbody>
</table>
</div>

This gives us an extra row in the table that we know will always start immediately after the last row of content in the table and line up. We can now set a prepared image as the background of that row that preserves the alternating colours and stretch it out to take up the remaining space:

.striped .filler {
  line-height: 0;
  background: url(stripes.png) repeat top left;
  height: 100%;
  padding: 0;
}

.striped .filler td {
 padding: 0;
}

.striped .filler:nth-child(even) {
  background-position: 0 20px;
}

Apart from the background image, we set the height to 100% so that our filler row fills the remaining space. If the table is empty, the filler row will get 100% height and fill all available space, if however the table has other rows in it, the table rendering algorithm will shrink our filler row down so that it can fit within the bounds of the table – preventing the filler row from overflowing the scroll container and activating the scroll bar.

Since our background image starts with an odd row colour, if the filler row is an even row, we use background-position to shift the background image down by the height of a stripe so it effectively starts with an odd row colour.

We also add line-height: 0 and padding: 0 so that when the table content fills all available space (or starts scrolling), the filler row has no minimum height and disappears entirely.

Note that this approach will adjust perfectly well even if the content rows vary in height, because the filler row will always start immediately after the last content row and fill the available space. The height of the alternating colours in our image will just provide the default row height in the filler space.

If we’re only targeting modern browsers anyway, we can go ahead and replace the background image with a dynamically generated gradient:

.striped .filler {
  line-height: 0;
  height: 100%;
  padding: 0;
  background-image: linear-gradient(bottom, rgb(255,255,255) 50%, rgb(234,234,234) 50%);
  background-image: -o-linear-gradient(bottom, rgb(255,255,255) 50%, rgb(234,234,234) 50%);
  background-image: -moz-linear-gradient(bottom, rgb(255,255,255) 50%, rgb(234,234,234) 50%);
  background-image: -webkit-linear-gradient(bottom, rgb(255,255,255) 50%, rgb(234,234,234) 50%);
  background-image: -ms-linear-gradient(bottom, rgb(255,255,255) 50%, rgb(234,234,234) 50%);

  background-image: -webkit-gradient(
linear,
left bottom,
left top,
color-stop(0.5, rgb(255,255,255)),
color-stop(0.5, rgb(234,234,234))
  );

  -webkit-background-size: 40px 40px;
  -moz-background-size: 40px 40px;
  -ms-background-size: 40px 40px;
  -o-background-size: 40px 40px;
  background-size: 40px 40px;
}

The final version of our page is:

<!DOCTYPE html>
<html>
<head>
<meta charset=UTF-8>
<title>Alternating Rows Example</title>
<style>
* { margin: 0; padding: 0; }
table {
  border-collapse: collapse;
  width: 100%;
  height: 100%;
}
.scroll {
  position: absolute;
  top: 0;
  bottom: 0;
  left: 0;
  right: 0;
  overflow-y: scroll;
  overflow-x: auto;
}
.scroll tr {
  height: 20px;
}
.striped tr:nth-child(odd) {
  background-color: #EAEAEA;
}
.striped .filler {
  line-height: 0;
  height: 100%;
  padding: 0;
  background-image: linear-gradient(bottom, rgb(255,255,255) 50%, rgb(234,234,234) 50%);
  background-image: -o-linear-gradient(bottom, rgb(255,255,255) 50%, rgb(234,234,234) 50%);
  background-image: -moz-linear-gradient(bottom, rgb(255,255,255) 50%, rgb(234,234,234) 50%);
  background-image: -webkit-linear-gradient(bottom, rgb(255,255,255) 50%, rgb(234,234,234) 50%);
  background-image: -ms-linear-gradient(bottom, rgb(255,255,255) 50%, rgb(234,234,234) 50%);

  background-image: -webkit-gradient(
linear,
left bottom,
left top,
color-stop(0.5, rgb(255,255,255)),
color-stop(0.5, rgb(234,234,234))
  );

  -webkit-background-size: 40px 40px;
  -moz-background-size: 40px 40px;
  -ms-background-size: 40px 40px;
  -o-background-size: 40px 40px;
  background-size: 40px 40px;
}

.striped .filler td {
 padding: 0;
}

.striped .filler:nth-child(even) {
  background-position: 0 20px;
}
</style>
</head>
<body>
<div class="scroll">
<table class="striped">
<tbody>
<tr><td>Row 1</td></tr>
<tr><td>Row 2</td></tr>
<tr><td>Row 3</td></tr>
<tr><td>Row 4<br>With an extra line</td></tr>
<tr><td>Row 5</td></tr>
<tr class="filler"><td></td></tr>
</tbody>
</table>
</div>
</body>
</html>
view raw final.html This Gist brought to you by GitHub.

Or try the demo in your browser.

How To Round-Trip Data Via SSH

I keep forgetting this so for the record, you can use SSH to round trip data out to a remote server and back to your own box.  This is most useful for adding latency and bandwidth limitations to a connection when doing website testing.  The trick is to use both local and remote port forwards:

ssh -L 9090:localhost:9091 -R 9091:localhost:80 remote.server.com

The -L argument starts listening on port 9090 locally and sends all that traffic to port 9091 on the remote server (the domain name, localhost, is resolved by the remote server so it refers to the remote server, not your local machine).  Then the -R argument listens on port 9091 of the remote server and forwards all that traffic back to your machine’s port 80 (here localhost is resolved on your local machine).

You don’t have to use localhost as the domain name. For example, if the site you want to test is deployed on your intranet at testmachine.intranet which remote.server.com doesn’t have access to, you could use:

ssh -L 9090:localhost:9091 -R 9091:testmachine.intranet:80 remote.server.com

Or if the test site is publicly available you can do it all without the -R argument:

ssh -L 9090:testmachine.com:80 remote.server.com

In all these cases, you connect to localhost:9090 to utilise the tunnel.

The Business of Standards

Recently I’ve been getting spam from the “standards organisation” OASIS inviting a company that I don’t work for1 to join a new standards initiative. There’s no pretense that I’m being invited because of my clearly superior knowledge of the area involved, merely that the company could get great advertising exposure by participating – including being listed on a press release! Naturally we’d need to become OASIS members and pay the appropriate fee, and to be in the press release you’d have to be at minimum a “sponsor level member”.

On the one hand it’s nice that they aren’t pretending to be anything other than a for-profit marketing company, but isn’t it a bit sad that we think standards are just a marketing tool?

1 – yes, they’ve done so much research into how much I have to contribute that they’ve got the company wrong

Development Mode – Concatenating Scripts and CSS

HTML 5 Boilerplate reminded me of an old-school tool which is extremely useful for concatenating JS and CSS files on the fly – server side includes:

<FilesMatch ".*\.combined\.(js|css)$">
  Options +Includes
  SetOutputFilter INCLUDES
</FilesMatch>

Then you have a main scripts.combined.js (or css) which contains:

<!--#include file="libs/backbone-min.js" -->
<!--#include file="libs/underscore-min.js" -->
<!--#include file="libs/jquery-1.7.1.min.js" -->

Plus any other files you need, in the order you specify. This works great for development mode so you can change a file on the fly and just refresh the browser without running any kind of build script. When it comes time to push to production, it’s easy for a build script to process the file ahead of time and be confident that you’ll get exactly the same result.

Cross Pairing

This evening I stumbled across two interesting posts about alternate layouts for pairing rather than sitting side by side. Firstly Tom Dale talking about Tilde’s corner desk pairing setup (and some of the software setup they use) and that was inspired by Josh Susser’s face to face pairing setup at Pivotal Labs.

Both approaches require more floor space which makes them difficult to setup but I would expect the face to face pairing to be a heck of a lot better if done well. I’ve always preferred having separate screens in mirror configuration as well as separate keyboards and mice to allow the developers to sit a little further apart to be comfortable and to be able to look straight ahead at the screen. That said, I quite like having a second monitor for spreading out windows as we have at LMAX so it’s not clear cut which is better.

It’s also interesting to note the popularity of the flat screen iMacs as opposed to Mac Pros or laptops. The former being too expensive for extra power and extensibility that generally isn’t required and the latter often being a bit too individualised to be good as pairing machines. Plus laptops, while amazingly powerful these days still have less bang for the buck and the reduction in performance is just enough to matter for development.