loading...
Cover image for Understanding positioning in CSS

Understanding positioning in CSS

huijing profile image Chen Hui Jing Originally published at chenhuijing.com on ・7 min read

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.

So a little while ago, my friend, Yong Jun asked the 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.

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):

Mapping of physical and logical box offset values

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

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

Logical box offset values for horizontal-tb with ltr

Logical box offset values for horizontal-tb with rtl

The following is for vertical-rl:

Logical box offset values for vertical-rl with ltr

Logical box offset values for vertical-rl with ltr

The following is for vertical-lr:

Logical box offset values for vertical-lr with ltr

Logical 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).

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.

Discussion

pic
Editor guide
Collapse
facundocorradini profile image
Facundo Corradini

Great article! An important clarification would be that the containing block for a fixed positioned element is not always the viewport: if they have a parent with a transform applied, it will behave pretty much as if it was absolute positioned to it.

Collapse
klaudiomilankovic profile image
Klaudio Milankovic

Hi,

Can you clarify this because it's the first time I'm hearing about it.

Are you saying parent with "transform" property will act as a base for a fixed positioned item inside it?

Collapse
facundocorradini profile image
Facundo Corradini

Exactly. Same thing happens with filter and perspective, but browsers are inconsistent on those.

Here's a quick demo with transform

jsfiddle.net/to4g607c

Collapse
ziizium profile image
Habdul Hazeez

This is great and thank you for the effort in putting this together.

I wrote a post about CSS positioning few months back here on DEV and i want to ask one question:

Can i update the post with a link to this post as an additional learning resource?

Collapse
victorquanlam profile image
Victor Quan Lam

This is great!

Collapse
zechtyounes profile image
zechtyounes

Hi, great post thank you !

Add the #beginners you will reach and help more people 👌🏻😉

Collapse
huijing profile image
Chen Hui Jing Author

That's an excellent suggestion, thank you! Updated the tag.

Collapse
karataev profile image
Eugene Karataev

Why is it static for default block position? For me static is like well.. static. Something that doesn't move.
I'd prefer to name default positioning like normal or dynamic or something like that to highlight that it's able to move if neighbours are around.
Am I missing something?

Collapse
demaine profile image
Colin Demaine

I think I understand what you're saying and naming things is always hard, but I think you probably said it best yourself; Static means something that doesn't move. The blocks in their default state don't actually move from the position that they are rendered in. Plus they can't be manipulated by directional properties (left, right, top, bottom, z-index) so the blocks do therefore remain static on the page.
Whereas a term like dynamic feels better suited to describing the way that Flexbox works to display blocks.
Hope this helps!

Collapse
ben profile image
Ben Halpern

This is really fabulous

Collapse
fly profile image
joon

Exactly the post I needed. Thank you immensely Chen :)

Collapse
lisasy profile image
Lisa Sy (she/her)

This is really great, thank you for sharing.

Collapse
nguyenquangtin profile image
Tony Tin Nguyen

Great article, thanks Chen Hui Jing.

Collapse
calvinmills profile image
Calvin Mills

Great article, thank you for this!

Collapse
zainwah24 profile image
zainwah24

Detailed explanation. Awesome.

Collapse
synchromatik profile image
synchromatik

Ok, this is the type of articles you read to actually become "full stack ninajs superwomans supermans by end of 2020", and not the shitty ones with 1 video tutorial on the subject ;)