Charlie Park

CSS-Only Sticky Headers for Tables, Using ‘position: sticky’

Two quick things to note:

  1. This only works (for now) on the latest versions of a WebKit-based browser, like Chrome Canary or the WebKit Nightly build. (I’m on Chrome 23.0.1263.1). More browsers will hopefully support this before too long.
  2. You probably want to see an example, yeah? Jump down to the example.

In the last few days, I’ve seen a few people talking about a new CSS property, position: sticky. The idea is straightforward, and neat: If an object has “position:sticky”, treat it as a normal position:relative block, as long as it’s on screen. If the user scrolls far enough that the object (let’s say it’s an h3) would be scrolled off the screen, but the h3’s parent div is still visible onscreen, treat the object as though it were position:fixed (at whatever top or left or right or bottom parameters you give it).

That explanation gets a little complicated, but it’s a principle you’ve seen before. Basically, if the parent div containing the headline (or whatever) is still on-screen, the headline should remain on-screen as well. Scroll down a bit to see an example (look for a table titled “The First 40 Elements”).

Why is this new implementation interesting?

What’s neat about this is that you get the effect of the static table headers by introducing a few lines of CSS. No jQuery, no weird CSS that breaks the semantic intent of the content, and no javascript handlers built off scroll events.

Lots of thanks to the developers at Apple for making this work.

So, tables?

One of the best use-cases for this is with really long tables. That is, tables where you want to see the different columns’ headers, as well as the data in the table. Tables like we sometimes have in PearBudget.

This is a use-case that, so far, has been served by javascript. A great front-end dev (and friend), Russell Heimlich, built a great implementation of it as a plugin for jQuery, Prototype, MooTools, and Dojo: Sticky Header. And his plugin is probably the best way to implement this effect for now, since browser support for this CSS property is currently almost non-existent.

But how do you make sticky headers with just CSS?

It’s super-easy. All you do is add

position: -webkit-sticky;
position: -moz-sticky;
position: -ms-sticky;
position: -o-sticky;
position: sticky;
top: 0;

to the CSS for whatever object you want to stay on-screen. In the “elements” example below, I’ve applied that CSS to the <thead>.

Can I see an example?

Sure thing. If you’re using the latest version of a WebKit-based browser (or, in the future, if other browsers are supporting this and you’re on one of those), the following table will show sticky headers. The thing to look for: the gray bar with the columns’ headers will remain visible, even when you’re scrolling far down the table.

Revisiting the description above of what’s going on, as long as the parent element (<table></table>) is visible on-screen, the <thead> (and everything in it) should be visible as well.

If you aren’t using a browser that can handle this, it’ll just look like a normal table, and the header row will scroll offscreen like any other position: relative element. Again, there’s no javascript in play here.

The First 40 Elements

Atomic no. Name Symbol Group Period Block State at STP Occurrence Description
1 Hydrogen H 1 1 s Gas Primordial Non-metal
2 Helium He 18 1 s Gas Primordial Noble gas
3 Lithium Li 1 2 s Solid Primordial Alkali metal
4 Beryllium Be 2 2 s Solid Primordial Alkaline earth metal
5 Boron B 13 2 p Solid Primordial Metalloid
6 Carbon C 14 2 p Solid Primordial Non-metal
7 Nitrogen N 15 2 p Gas Primordial Non-metal
8 Oxygen O 16 2 p Gas Primordial Non-metal
9 Fluorine F 17 2 p Gas Primordial Halogen
10 Neon Ne 18 2 p Gas Primordial Noble gas
11 Sodium Na 1 3 s Solid Primordial Alkali metal
12 Magnesium Mg 2 3 s Solid Primordial Alkaline earth metal
13 Aluminium Al 13 3 p Solid Primordial Metal
14 Silicon Si 14 3 p Solid Primordial Metalloid
15 Phosphorus P 15 3 p Solid Primordial Non-metal
16 Sulfur S 16 3 p Solid Primordial Non-metal
17 Chlorine Cl 17 3 p Gas Primordial Halogen
18 Argon Ar 18 3 p Gas Primordial Noble gas
19 Potassium K 1 4 s Solid Primordial Alkali metal
20 Calcium Ca 2 4 s Solid Primordial Alkaline earth metal
21 Scandium Sc 3 4 d Solid Primordial Transition metal
22 Titanium Ti 4 4 d Solid Primordial Transition metal
23 Vanadium V 5 4 d Solid Primordial Transition metal
24 Chromium Cr 6 4 d Solid Primordial Transition metal
25 Manganese Mn 7 4 d Solid Primordial Transition metal
26 Iron Fe 8 4 d Solid Primordial Transition metal
27 Cobalt Co 9 4 d Solid Primordial Transition metal
28 Nickel Ni 10 4 d Solid Primordial Transition metal
29 Copper Cu 11 4 d Solid Primordial Transition metal
30 Zinc Zn 12 4 d Solid Primordial Transition metal
31 Gallium Ga 13 4 p Solid Primordial Metal
32 Germanium Ge 14 4 p Solid Primordial Metalloid
33 Arsenic As 15 4 p Solid Primordial Metalloid
34 Selenium Se 16 4 p Solid Primordial Non-metal
35 Bromine Br 17 4 p Liquid Primordial Halogen
36 Krypton Kr 18 4 p Gas Primordial Noble gas
37 Rubidium Rb 1 5 s Solid Primordial Alkali metal
38 Strontium Sr 2 5 s Solid Primordial Alkaline earth metal
39 Yttrium Y 3 5 d Solid Primordial Transition metal
40 Zirconium Zr 4 5 d Solid Primordial Transition metal

You’ll notice that the effect degrades gracefully … if the user is not on a browser that supports position: sticky, the header just scrolls off-screen, like any other object on (read: off) the screen.

One thing to try doing (if you’re on the main charliepark.org page and not just this post’s page) is to scroll down a ways, and then scroll back up so you can see the table. You’ll notice the headers show up as soon as the table is in view.

Anything to be careful about?

First, this property is new. It’s not supported at all, apart from the beta builds of Webkit-based browsers. So caveat formator. Again, if you really want for your users to benefit from sticky headers, go with a javascript implementation.

Second, if you do use it, you’ll need to incorporate vendor prefixes. Perhaps position: sticky will work one day. For now, though, you need to use position:-webkit-sticky (and the others; check the block of CSS further up in this post).

Third, there aren’t any positioning defaults at the moment, so you need to at least include top: 0; in the same CSS declaration as the position:-webkit-sticky. Otherwise, it’ll just scroll off-screen.

Have fun, kids!