# Position sticky in CSS, with examples

Before `position: sticky`, we had to finesse around with scroll listeners and
bounding box measurements, ugh. But nowadays `position: sticky` has gained
enough browser support to make it a viable option.

As a refresher, these are all the position values the CSS spec has:

```text
.foo {
  position: static|relative|absolute|fixed|sticky;
}
```

## Position sticky header

Basic layout with a **header**, **content**, and a **footer** at the bottom.

```html
<div class="wrap">
  <header class="sticky-header">Sticky header</header>
  <p>Position sticky demo...</p>
  <footer>Footer</footer>
</div>
```

And the CSS (mandatory lines highlighted):

```css {7-8}
.wrap {
  border: 1px solid #aaa;
  height: 400px;
}

.sticky-header {
  position: sticky;
  top: 20px;
}
```

It looks like this (scroll down to see the header sticking to the top):

Sticky header

Position sticky demo...

Footer

## Position sticky footer

More rarely used approach, but elements can also be **stuck to the bottom of the
page**.

The below layout also uses a "sticky footer" technique, which is a different
kind of sticky eh, it pushes the footer all the way down no matter how much
content there is, using some `flexbox` smartness.
[Read about the sticky footer technique](/sticky-footer-with-pure-css).

Here’s the HTML, pretty much the same as above:

```html
<div class="wrap">
  <header class="header">Sticky header</header>
  <p class="content">Position sticky demo...</p>
  <footer class="footer">Sticky footer</footer>
</div>
```

We can set the footer position to sticky and bottom to zero:

```css {13-14}
.wrap {
  display: flex;
  flex-direction: column;
  height: 400px;
}

.content {
  flex: 1;
  margin: 20px;
}

.footer {
  bottom: 0;
  position: sticky;
}
```

And the footer stays glued to the bottom:

Header

Position sticky demo...

Sticky footer

## Position sticky with tables

Table heads (`<th>`) can be sticky, too. It works exactly the same:

```css
th {
  background-color: rgba(255, 255, 255, 0.9);
  position: sticky;
  top: 0;
}
```

Position sticky compatibility table:

| Browser | Supports | Notes        |
| ------- | -------- | ------------ |
| Chrome  | ✅       | Full support |
| Opera   | ✅       | Full support |
| Edge    | ✅       | Full support |
| Firefox | ✅       | Full support |
| Safari  | ✅       | Full support |
| IE      | 🚫       | -            |

It’s important to set the background color to the sticky element, or it looks
ugly when content scrolls from under it.

## A few scenarios where sticky footer doesn’t work

### Extra wrapper

Adding an extra wrapping components breaks it, because the stickiness relies on
the height of its immediate parent component:

```html
<div class="wrap">
  <!-- This wrapper breaks it -->
  <div>
    <header class="sticky-header">Sticky header</header>
  </div>
  <p>Position sticky demo...</p>
  <footer>Footer</footer>
</div>
```

### Overflow hidden

Setting `overflow: hidden` to the parent element breaks the sticky.

> `overflow: hidden` is not preventing `position: sticky` from working. But if
> you set overflow to hidden on any ancestor of your sticky element, then this
> ancestor element will be the scrolling container for your sticky element. If
> you switch the overflow value on your ancestor from `hidden` to `scroll` and
> scroll this ancestor (not the window), then you can see that sticky is still
> working. —
> [StackOverflow answer.](https://stackoverflow.com/a/44929597/976972)

## Conclusions

You could think position sticky as a hybrid of relative and fixed position, at
at some point the component’s position changes to fixed, basically the same way
we had to do with JavaScript before. Now it’s just built-in.

Sticky has been around for a while now,
[I found it from a 2015 spec](https://www.w3.org/TR/2015/WD-css3-positioning-20150203/#sticky-pos).
I remember longingly looking up position sticky from
[caiuse.com](https://caniuse.com), but it was only supported in Firefox, and
maybe Chrome if a flag was set. I guess it just took a while for the browsers to
implement it, and now that Edge and Opera are based on Chromium, it’s all of a
sudden supported everywhere.
