Adding a Full-Width and Full-Height Fixed Border Around the Page

January 23, 2020 Updated: 1/28/2020

I wanted to add a thick orange border around this site when I working on a redesign of it. The idea is a that the border would act like a picture frame around the content that stays in place no matter where you scroll to.

“Easy,” I thought. But I actually struggled with it to the point of questioning my entire life’s work.

Turns out I was merely overlooking what was right under my nose.

So, say you wanted to do this yourself. We have two HTML elements we can use to select the entire document. (Well, there are technically more ways, but there’s no need to get super tricky here.)

html {}
body {}

Either of these are just fine — at least as far as I’m aware. Let’s go with the HTML element. It selects the entire document by default, whereas the body element relies on the amount of content in the document. That distinction doesn’t really matter but that makes it feel more “correct” to me. Whatever “correct” means in development these days.

The thing is that we can’t add the border directly to the element.

/* Don’t do this */
html {
  border: 8px solid #fa8000;
}

That does indeed put a border around the page like we want. However, any content that requires scrolling to will overlap the bottom border.

Oh! We have pseudo-elements we can use. Let’s put the border on the :after pseudo-element instead. That was, we can use z-index to place the border over the content, right?

html:after {
  content: “”; /* Required for pseudos */
  border: 8px solid #fa8000;
  height: 100vh;
  width: 100vw;
  z-index: 1; /* Should place this over the body content. */
}

Great, that works! But whoa, nothing on the page is selectable anymore. And hey, I can’t click any links! Gah.

I reverted my changes and walked away for a couple of days because I didn’t feel like thinking about it anymore., I mean, it’s such a small design enhancement that it didn’t feel worthwhile to spend time on.

It was during one of those times when I was walking down the street to get coffee that I started thinking about the pointer-events CSS property. I knew what it did, but never really never really reach for it because the use cases are so few and far between, at least in my experience.

But I knew it would work in this case. Using the none value removes all cursor and clicking functionality on an element. That would make the :after pseudo to stay on top of the content, but without hogging all the cursor and clicking stuff it was blocking before. In other words, it’s like punching a giant hole in the element as it covers the entire screen so that you can interact with elements beneath it.

html:after {
  content: “”; /* Required for pseudos */
  border: 8px solid #fa8000;
  height: 100vh;
  pointer-events: none;
  width: 100vw;
  z-index: 1;
}

Ah, much better. Now I can shove my imposter syndrome back in the closet… for a little while at least.