CSS position: fixed is terrible, here’s why

JavaScriptUI — DevBlog #4

Bence Meszaros
4 min readMay 27, 2024
Oh what fun CSS is

Intro

HTML and CSS have a tendency to introduce a ton of exceptions instead of solving underlying issues. The position property and especially position: fixed is a testament to that. I cannot wrap my head around why these languages keep building concepts, rules and conventions only to bring them down with more quirks and inconsistencies than I can count.

What do I mean by that?

The rules

You don’t have to be a rocket scientist to intuitively understand the most basic rules of a view tree:

  1. the tree in our code should look the same as our view tree (sigh)
  2. each view is unique and can appear in a tree only once
  3. each view has exactly one parent, except the root which has zero
  4. a view can have zero or more children
  5. views defined later appear on top of views defined earlier

This is straightforward, clear and simple. Enter position: fixed.

The terrible design of position: fixed

HTML has a certain rigidity in how our hierarchy can and cannot be defined which pretty much undermines proper layout/user interface concepts. To solve this, CSS had to step in and patch HTML which in turn caused even more problems, especially with regard to layout building. Take a look at this example, how will this snippet appear on screen?

<body>
<div id="fixed_1" style="position: fixed">
<div id="fixed_2" style="position: fixed"></div>
</div>
<div id="regular"></div>
</body>

fixed_1 is ripped out of the normal flow and positioned relative to the viewport, effectively changing its parent. fixed_2 is the same, but visually it is not even the child of fixed_1 anymore, they became siblings (fixed_1 cannot crop or scroll fixed_2). On top of that (pun intended), they appear on top of regular, even though regular was defined later than any of the two fixed elements. And this is what web gurus consider semantic.

You can write hundreds of pages of specifications, introduce endless number of exceptions, weird new concepts, stacking contexts, containing blocks, and tell everyone that CSS has a steep learning curve, but the result will still be a trainwreck. Once we add position: fixed to even a single element, we violate basically all five rules that I defined earlier (better, these aren’t even my rules but the core of HTML and CSS). All this, because HTML doesn’t allow adding views directly to the viewport.

The solution

If you can set aside your HTML indoctrination for a moment, please take a look at this snippet:

<viewport>
<html>
<body>
<div id="regular">this is the main body of the website</div>
</body>
</html>
<div id="fixed_1">this is 'fixed'</div>
<div id="fixed_2">this is also 'fixed'</div>
</viewport>

This is obviously not valid HTML, but it is very close, it honors all five rules that I have defined earlier and it doesn’t even need fixed positioning at all. In fact, there is no need for a position property whatsoever, we put our views where they belong, the view hierarchy is crystal clear. We achieve more with less, simply by understanding our own rules instead of mindlessly generating ad hoc exceptions everywhere.

Sadly, the root of the view tree in browsers are much more complicated than this, the actual viewport is not directly accessible,<html> is not the viewport, not even the first child of the viewport and the actual tree looks more like this:

window //object
screen //object, should be the parent of window
viewport //object, inaccessible, should be the root view
#document //node, actual root view
#doctype //node
<html> //"root" element
<head> //non-visible element (metadata)
<body> //"root" element
<div> //element (fixed), should be the direct child of viewport
<div> //element (regular)

What an absolute beauty.

How does JavaScriptUI work

In JavaScriptUI there is no need to use position: fixed, in fact, the whole position property is unnecessary. Instead, it has a proper view hierarchy and intuitive containers (essentially removing the display property as well, but we’ll get to that in another installment). What you write is what you get.

This is the exact same example but without the mess and confusion:

App(
Stack(
Text("this is the main body of the website")
),
Text("this is 'fixed'"),
Text("this is also 'fixed'")
);

Again, this is valid JavaScript code, the only dependency is a single library. Do you still think that the legacy mess is justified?

But man, why bother?

I get it, this is just a single property, who cares right? The point I am making is that HTML and CSS are flooded with these annoyances, and as your project scales, you won’t even notice how much strain these issues put on it. If you need an overlay, a popup, different layers in your document, you will encounter this issue, it will bloat your CSS code, mess up your HTML hierarchy, increase confusion, degrade accessibility and negatively impact responsiveness.

Outro

This was a short post but I hope this was still helpful. In the coming weeks I will dive into further CSS properties and values and build an abstraction that is much better to use.

If you like my project please consider clapping, commenting and sharing this blog with others.

Thanks, have fun!

⬅️ DevBlog #3 — Designing a new style system to replace CSS

--

--