understanding-positioning-in-css

I was at JSConf China earlier this year, which happened in Shanghai from 19–20 Oct. There was fairly decent representation from Singapore I would say.

Wei (whom I probably mention every second blog post I write) was the opening keynote for Day 2, and it was one of the best if not THE best talk of the conference IMHO.

Wei on stage at JSConf China

We also had Yong Jun (another good friend of mine), who gave a workshop on the rather interesting topic of scrolly-telling. He works in the graphics department for Singapore Press Holdings and has lots of experience creating interactive graphics and visualisations.

Yong Jun running workshop at JSConf China

Yong Jun had developed an open-source scrolly-telling library that he used for a lot of his projects and it uses position: sticky for “snapping” content into place.

During the workshop, he posed the question of why just applying position: sticky alone on an element doesn’t work. And this gave me the idea to do another CSS property deep dive ?.

CSS is a team sport

CSS layout is not just one individual CSS property or even module. Everything on the web is a box. And the layout of these boxes are determined the following:

  • box dimensions and type
  • positioning scheme (normal flow, float, and absolute positioning)
  • relationships between elements in the document tree
  • external information (e.g., viewport size, intrinsic dimensions of images, etc.)

From this bit of information alone, we can pick out the following CSS modules (including those in draft status):

The point I’m making here is, if you’d like to be fully equipped to build any layout you can imagine with CSS, I suggest understanding these different CSS modules and how they interact with each other. It definitely helped me out when I did so.

In all fairness, I really dug into all of it when I was preparing for my CSS Day talk back in 2018, which was pretty much 45 minutes about boxes. All talk details on Notist.

Positioning schemes

After boxes are generated, the way they are laid out depends on which positioning scheme they fall into:

  • Normal flow
  • Floats
  • Absolute positioning

Boxies in normal flow

Normal flow is the default, where boxes are laid out one after another, and will not overlap or intrude into each others’ space. All boxes will be on the same stacking context as well.

Boxies with a floated friend

Boxes that are floated are considered out-of-flow. When a box is floated, it is first laid out according to normal flow, but then is taken out the flow and shifted as far to the left or right as possible (depending on the float value).

Content will then flow along the side of the floated box. But the floated boxes still remain on the same stacking context as the non-floated ones.

Boxies with an absolutely positioned friend

An absolutely positioned box is completely removed from normal flow altogether, and its position is assigned with respect to its containing block.

The positioning algorithms used to calculate the position of a box on the page is determined by the position and float properties. I’ll try my best to explain this in a way that makes sense. But first, let’s look at them individually.

The position property

There are several values for this property, with sticky being added in Level 3 of the Positioned Layout Module, and currently supported in most major browsers even though it is still in a working draft status.

position: static

The default position value of any box. This results in the box being laid out in normal flow and the properties of top, bottom, left and right (also known as box offset values) will have no effect because the box is considered not positioned.

Default layout of unpositioned boxes

position: relative

Applying a position of relative to a box makes it positioned, and now the top, bottom, left and right values will have some effect.

The box will initially be laid out according to normal flow, then depending on the aforementioned box offset values, will be offset relative to its normal position.

Relatively positioned box with top and left offsets

If there are other non-positioned boxes next to this positioned box, they will not be affected even if offset values are present. This means there may be the possibility of overlap.

Also, if the offset causes the box to have overflow, this may result in scrolling, which would affect layout. When a box becomes relatively positioned, it becomes the new containing block for its children.

position: sticky

A sticky positioned box works similarly to that of a relatively positioned box. The difference is that the offset is computed to the nearest ancestor with a scrolling box, or the viewport if none such ancestor exists.

This ties in to Yong Jun’s original question of why applying position: sticky alone on a box is insufficient to achieve the sticky effect. The box, without any offsets, behaves as though it was in normal flow, because it is.

Only when the offset values are something other than auto will the box start to behave differently from a relatively positioned box. The wording in the specification comes across slightly cryptic to me, but my understanding is that there is an intersection between the sticky box and its containing box when you apply a box offset value.

Initial position of sticky positioned box

This intersection is called a sticky-constraint rectangle and is used to contain the location of the sticky box. The specification uses the term “projects above/below/outside” but I’m not sure if my understanding is the same as what the specification authors intended.

Position of sticky positioned box after scrolling takes place

What I do know is that when you define an offset value, the offset will never push the sticky box outside its containing block, but the sticky box is free to move within the containing block as the page is scrolled, hence the perception of it being pinned to the relevant edges.

position: absolute

An absolutely positioned box is explicitly offset with respect to its containing block, and the box does not participate in the normal flow at all. Its later siblings will not know about its existence in the greater layout.

Position of absolutely positioned box

Contents of an absolutely positioned box do not flow around any other boxes and may overlap other boxes, and depending on the stack level of the boxes, content may be obscured by the overlap.

position: fixed

A fixed position box behaves similarly to an absolutely positioned box. The key distinction is that the containing block is always the viewport.

Position of fixed positioned box

Box offset values

Traditionally, the box offset values most developers are familiar with are top, bottom, left and right. And I don’t think it’s a generalisation to say that most developers from the West don’t give a second thought to writing modes that are anything other than horizontal top-to-bottom.

But when you use a writing mode that is right-to-left, for example, or vertical writing, then these physical values of top, bottom, left and right make less sense. For example, the top of your content may not be the physical top of the browser viewport.

As someone who grew up exposed to writing systems in different directions from the Western default, this was not hard for me to wrap my head around, but that may not be the case for many folks from the West.

Naming is always hard, especially when it does not exactly correlate with real life / usage expectations / metaphors. At least the terms used are consistent with other CSS properties. You need to know how block and inline behave and have some imagination.

— Ecaterina Moraru (@evalica) November 7, 2019

This is why I appreciate it when influential folks in our industry start talking about these lesser known aspects of web development, especially on the internationalisation side of things, because they have a wider reach and can help something considered obscure become more mainstream.

Logical box offset values

Because the specification is still in Editor’s Draft status, the syntax may change moving forward. Even now, the current browser implementation is different from what is in the specification, so be sure to double-check with MDN: CSS Logical Properties and Values on the most updated syntax.

The matrix of writing directions and their corresponding values for a box’s physical sides and logical sides are as follows (the table has been lifted from the specification as of time of writing):

writing-mode / direction
horizontal-tb vertical-rl vertical-lr
ltr rtl ltr rtl ltr rtl

Edge

top

inset-before inset-before inset-start inset-end inset-start inset-end

right

inset-end inset-start inset-before inset-before inset-after inset-after

bottom

inset-after inset-after inset-end inset-start inset-end inset-start

left

inset-start inset-end inset-after inset-after inset-before inset-before

The logical top of a container uses inset-before, while the logical bottom of a container uses inset-after. The logical left of a container uses inset-start, while the logical right of a container uses inset-end.

This is probably easier to visualise with a diagram (or live code if your browser supports it). The following is for horizontal-tb:

Mapping for horizontal-tb

Logical box offset values for horizontal-tb with ltrLogical box offset values for horizontal-tb with rtl

The following is for vertical-rl:

Mapping for vertical-rl

Logical box offset values for vertical-rl with ltrLogical box offset values for vertical-rl with ltr

The following is for vertical-lr:

Mapping for vertical-lr

Logical box offset values for vertical-lr with ltrLogical box offset values for vertical-lr with ltr

Wrapping up

I suggest opening this CodePen in a full browser window and playing around with the examples and CSS values to get a feel of how everything fits together (or you could select the 0.5x option in the embed).

See the Pen
Understanding CSS positioning
by Chen Hui Jing (@huijing)
on CodePen.

There are plenty of useful resources that cover CSS positioning, so if my explanation doesn’t work for you, try someone else’s article or the specification directly. It is definitely worth the effort in the long run to figure out this important aspect of CSS layout for yourself.



CSS

sticky-positioning-with-nothing-but-css-(thanks-to-css-position:-sticky)

by

Difficulty:IntermediateLength:ShortLanguages:

Sticking elements when the user scrolls to a certain point is a common pattern in modern web design. You’ll see it applied to top navigation, the sidebar, share links, or perhaps ad blocks; retaining visibility within the viewport as the user scrolls. 

Historically we’ve needed JavaScript to do this. However, sticky behaviour has become a new standard (CSS position: sticky), allowing us to achieve the effect with pure CSS. Let’s take a look at how it works!

Sticky Position

sticky is a new(ish) value for the position property, added as part of CSS3 Layout Module Spec. It acts similarly to relative positioning, in that it doesn’t remove anything from the document flow. In other words, a sticky element has no effect on the position of adjacent elements and doesn’t collapse its parent element.

Given the following example, we set the #sidebar position to sticky along with top: 10px. The top value is required and specifies the distance from the top edge of the viewport where the element will stick

#sidebar {
	position: -webkit-sticky;  // required for Safari
	position: sticky;
	top: 0; // required as well.
}	

Now, as we scroll the page, when the sidebar’s distance from the top of the viewport reaches 0, the sidebar should stick, effectively giving us a fixed position. In other words, the sticky is kind of a hybrid between relative and fixed position.

Nesting

Additionally, CSS position: sticky will work within a scrolling box or an overflowing element. This time we’ll set the top to 15px to give some more space above the sidebar when it’s stickily positioned (yes, that’s a word).

The sidebar will remain sticky all along the parent’s height (ie: when the bottom of the parent reaches the bottom of the sidebar, it will “push” it off the page once more.)

Easy, isn’t it?

Support

In the last couple of years browser support for CSS position: sticky has taken huge leaps. From the chart you’ll see it enjoys excellent coverage, although many supporting browsers (such as Chrome and Edge, both of which use the Blink rendering engine) still struggle when CSS sticky is applied to

or

elements.

Additionally, as mentioned earlier, Safari offers full support but requires use of the -webkit- prefix.

Browser support for CSS position sticky
Browser support for CSS position: sticky

Using the Polyfill

To help out our non-supporting browsers (not that there are many nowadays) we can use stickyfill developed by Oleg Korsunsky. The polyfill works nicely in various circumstances. Whether the designated element has padding, margins, borders, been floated, or formed with relative units like em and percentage, the polyfill will accurately mimic the CSS sticky position behavior. And using stickyfill is equally intuitive.

To begin, grab stickyfill.js (optionally with jQuery, if you are more familiar with and prefer using jQuery for selecting elements). Link these libraries within your HTML document. Then initiate stickyfill with the designated element, as follows:

var sidebar = document.getElementById('sidebar');
Stickyfill.add(sidebar);

If you are using jQuery, you could write the following instead:

$('#sidebar').Stickyfill();

Now, our sticky sidebar should work across browsers including Chrome and Opera. The polyfill is smart enough that it will only run in non-supporting browsers, otherwise it will be completely disabled, stepping aside for the browser’s native implementation.

Caveats

There are a couple of other things to note before you take the plunge and use sticky position on your websites:

  • First, the height of the parent container should be greater than the sticky element.
  • The polyfill has its own shortcomings, in that it won’t work within an overflowed box.
  • At the time of writing, there is no specific JavaScript event handler for sticky to identify when the element is stuck. Such an event could be useful, for example, to add additional classes when the element is stickified (that might not be a word). There is however a slightly hacky way to achieve this using the Intersection Observer API. Read this post to find out more.

Closing Thoughts

CSS sticky position is a brilliant tool if you simply need a plain sticky element. If your need grows beyond that though—say you want to add some fancy effects upon the sticky element—you’ll still be better off opting for a JavaScript solution, be that self-written, or a library like Waypoints.js with its sticky module.

Further References

Learn CSS: The Complete Guide

We’ve built a complete guide to help you learn CSS, whether you’re just getting started with the basics or you want to explore more advanced CSS.