Thinking on ways to solve Centering in CSS

Adam Argyle looking at several techniques to center in CSS, and how they hold up in several conditions (narrow screen, rtl, etc)

In today’s challenge, we’re stress testing 5 different CSS centering techniques. See what techniques should earn a place in your tool belt by watching how they react to common layout stress. The contestants: content center, gentle flex, autobot, fluffy center, and pop & plop.

If you prefer a written format, a full write-up is also available on web.dev

Centering in CSS →

Native Aspect Ratio Boxes in CSS thanks to aspect-ratio


Old vs. New. Image by @una.

Back in May 2020 I was very delighted to read that the first Working Draft of the CSS Box Sizing Module Level 4 got published, as it featured an addition to CSS that I’ve been wanting for a long time now: native support for aspect ratio boxes through the new aspect-ratio CSS property.

With Chromium 89 (current Canary) and Firefox 85 (current Nightly) already supporting aspect-ratio unflagged, it’s time to start playing with this new addition and start thinking about dropping all those nasty hacks to mimic aspect ratios in CSS. Let’s take a look …

🤔 Working Draft (WD)?

The Working Draft (WD) phase is the first phase of the W3C Recommendation Track, and is considered the exploring phase of a W3C spec.

From thereon a spec can become a Candidate Recommendation (CR) to finally land on being a Recommendation (REC). In between those three stages there are two transition stages: Last Call Working Draft (LCWD) and Proposed Recommendation (PR)

In visual form, the Recommendation Track looks something like this:

See An Inside View of the CSS Working Group at W3C for more details on all phases.

⚠️ Note that the current implementations in both Firefox Nightly and Chrome Canary are not finalised yet. You can track these bugs to stay up-to-date:

~

~

# Welcome aspect-ratio

In short, the aspect-ratio property allows you to define a preferred aspect ratio on elements:

.box {
  width: 20vw;
  aspect-ratio: 16 / 9;
}

[CodePen Demo]

In the example above the .box will have a preferred aspect ratio of 16:9. Since its width is set to 20vw, the resulting height will be 20vw / 16 * 9 = 11.25vw. Easy, right?

~

# Allowed values for aspect-ratio

The value as set in the example above for aspect-ratio is of the <ratio> type:

  • It typically consists of two numbers separated by a /. The first parameter targets the width and the second one the height.
  • It’s also allowed to pass in just a single number. In that case the second number will be we considered to be 1. E.g. a <ratio> of 2 will translate to 2 / 1.
  • Passing in a 0 for either of the numbers is not allowed.
  • The spaces around the / are not required, so 2/1 is also a valid <ratio> value.

Another allowed value for the aspect-ratio property — which also is the default — is auto. This indicates that the box has no preferred aspect ratio and should size itself as it normally would.

🙋‍♂️ Hold up! How come images already behave correctly, without needing to define an aspect-ratio?

Images may be commonly used, but they are a quite uncommon type of HTML element:

  1. Images are replaced elements:

    A replaced element is an element whose content is outside the scope of the CSS formatting model, such as an image or embedded document. For example, the content of the HTML <img> element is often replaced by the image that its src attribute designates.

    Just check your DevTools: the browser will make an extra HTTP request for any image and fetch its contents separately. Once loaded, the browser will replace the original img tag with the actual image contents.

  2. Images have an intrinsic aspect ratio:

    The intrinsic dimensions represent a preferred or natural size of the object itself; that is, they are not a function of the context in which the object is used.

    Each photo that you take with your phone results in an image that has a certain width and height, which is referred to as the intrinsic or natural width/height. The intrinsic aspect ratio is the ratio between the intrinsic width and intrinsic height.

    When the browser has fetched the image and needs to draw it on screen it will take its intrinsic aspect ratio into account to know how big it should be drawn.

  3. When you define width and height attributes on an img, the browser will take those into account when drawing the image on screen. Nowadays browsers even internally map those properties to CSS sizing properties.

☝️ Do note that you can still set an aspect-ratio on an element that has an intrinsic aspect ratio. In that case your defined aspect-ratio will override the intrinsic aspect ratio.

~

# The fine print

# Aspect of what?

Depending upon which of width or height you set, the box dimensions will be calculated against that.

.box {
  width: 20vw;
  aspect-ratio: 16 / 9; /* Dimensions will be calculated against the width,
                           yielding a height of 11.25vw (20vw / 16 * 9) */
}
.box {
  height: 20vw;
  aspect-ratio: 16 / 9; /* Dimensions will be calculated against the height,
                           yielding a width of 35.55vw (20vw / 9 * 16) */
}

# aspect-ratio+width+height = 🚫

Setting an aspect-ratio won’t have effect on elements that have both a CSS width and CSS height set to a value other than auto. Only one of width or height can be explicitly set, and the other should remain set to auto.

.box {
  width: 20vw;
  height: 20vw;
  aspect-ratio: 16 / 9; /* won't have any effect! */
}

# aspect-ratio + percentage based width/height

In case one of the width and height should be set to a percentage based value such as 100%, the targeted box will take a look at the direct parent element’s dimensions to define its value upon that.

.parent {
  height: 100px;
}
.parent .box {
  height: 100%;
  aspect-ratio: 1 / 1; /* .box will be 100px by 100px */
}

There’s some more edge cases here too, but let’s not get too deep into the spec 😉

# aspect-ratio sets a preferred aspect ratio

Setting an aspect-ratio will tell the browser that this is a preferred aspect ratio. Should the content of the box be larger, then the box will simply grow.

div {
  aspect-ratio: 1/1;
  /* 'width' and 'height' both default to 'auto' */
}
+----------+  +----------+  +----------+
| ~~~~~~~~ |  | ~~~~~~~~ |  | ~~~~~~~~ |
| ~~~~~~~~ |  | ~~~~~~~~ |  | ~~~~~~~~ |
| ~~~~~~~  |  | ~~~~~~~~ |  | ~~~~~~~~ |
|          |  | ~~~      |  | ~~~~~~~~ |
+----------+  +----------+  | ~~~~~~~~ |
                            | ~~~~~~   |
                            +----------+

To maintain the aspect-ratio, you can set overflow to auto so that a scrollbar will be shown should the contents be larger:

div {
  overflow: auto;
  aspect-ratio: 1/1;
}
+----------+  +----------+  +----------+
| ~~~~~~~~ |  | ~~~~~~~~ |  | ~~~~~~~~^|
| ~~~~~~~~ |  | ~~~~~~~~ |  | ~~~~~~~~ |
| ~~~~~~~  |  | ~~~~~~~~ |  | ~~~~~~~~ |
|          |  | ~~~      |  | ~~~~~~~~v|
+----------+  +----------+  +----------+

What also works, is setting min-height

Overriding the min-height property also maintains the 1:1 aspect ratio, but will result in content overflowing the box if it is not otherwise handled.

div {
  aspect-ratio: 1/1;
  min-height: 0;
}
+----------+  +----------+  +----------+
| ~~~~~~~~ |  | ~~~~~~~~ |  | ~~~~~~~~ |
| ~~~~~~~~ |  | ~~~~~~~~ |  | ~~~~~~~~ |
| ~~~~~~~  |  | ~~~~~~~~ |  | ~~~~~~~~ |
|          |  | ~~~      |  | ~~~~~~~~ |
+----------+  +----------+  +-~~~~~~~~-+
                              ~~~~~~    

~

# Demos

# Using aspect-ratio with a fallback for older browsers

Thanks to the powerful @supports it’s possible to add a fallback for browsers that don’t support aspect-ratio. In the demo below (based upon this demo by Una) a fallback using the padding-top hack is applied:

[CodePen Demo]

# Using aspect-ratio with CSS Variables

By introducing a CSS Variable CSS Custom Property it’s possible to make your code more generic and extract away a .aspect-ratio class.

[CodePen Demo]

To use it, add apply the .aspect-ratio on the element you want, and pass in a --aspect-ratio CSS Custom Property:

<div
  class="aspect-ratio"
  style="--aspect-ratio: 16/9;"
>I am an aspect ratio box</div>

The code is written so that it will use the value for --aspect-ratio in both the fallback and the modern version.

# Automatically setting aspect-ratio on iframes and the like

When you embed an iframe you most likely set its width and height HTML attribute.

<iframe
  src="https://www.youtube.com/embed/e7BkmF8CJpQ"
  width="560"
  height="315"></iframe>

It’s possible to use the values of these attributes to automatically set the aspect-ratio.

iframe[width][height] {
  aspect-ratio: attr(width) / attr(height);
}

Heck, you could even target [width][height] if you’d want!

💁‍♂️ FYI: This is also what browsers nowadays do for images: they map the values from the width and height HTML attributes from images to a aspect-ratio in order to prevent Cumulative Layout Shift.

Firefox’s internal stylesheet for example looks like this:

img, input[type="image"], video, embed, iframe, marquee, object, table {
  aspect-ratio: attr(width) / attr(height);
}

marquee, lol 😆

[CodePen Demo]

🐛 I’ve noticed that reading the width/height attribute values using attr() to pass them into aspect-ratio doesn’t seem to work in current Chromium. To cater for that I’m also passing their values by means of a CSS Custom Property …

<iframe
  src="https://www.youtube.com/embed/e7BkmF8CJpQ"
  width="560"
  height="315"
  style="--aspect-ratio: 560 / 315"
></iframe>
🙋‍♂️ Why doesn’t this iframe demo have a padding-top fallback injected using :after?

Just like images, iframes also are replaced elements. It’s not possible to inject contents using :before/:after on replaced elements.

If you really need to have a fallback, you need to wrap the iframe in a container and apply the aspect-ratio on the container. See Embed Responsively and adjust were needed.

~

# In Closing

After 8 years of wanting this feature to land in CSS (ref) I’m very happy to see this addition make it into the spec. It’s still a Working Draft right now, but that doesn’t stop me from being excited about it already. I hope you are too 🙂

🔥 Like what you see? Want to stay in the loop? Here's how:

A Lightweight Masonry Solution

With Masonry being specced in Grid Layout Module Level 2 – and already being implemented in Firefox – Ana Tudor looked into whipping up a lightweight fallback for browsers that don’t support it.

🤔 Masonry Layout?

Masonry is a grid layout based on columns, as popularized by Pinterest. Unlike other grid layouts, it doesn’t have fixed height rows. It works by placing elements in optimal position based on available vertical space, sort of like a mason fitting stones in a wall.

It became easy to implement thanks to the Masonry JavaScript Library.

After first laying out all images in columns, she then calculates the vertical gaps between all items. Each gap is adjusted – using a negative top margin – so that the items shift upwards, after which them end up at their wanted positions.

A Lightweight Masonry Solution →

The Just in Case Mindset in CSS

Some nice examples of defensive programming in CSS by Ahmad Shadeed.

The just in case mindset aims to educate designers and developers to think ahead of time of some possible failure scenarios for a component. I called it just in case because that’s literally what it means. Just in case the title got longer, do this. Just in case this element has a sibling next to it, do that. It’s all about accounting for some edge cases.

It’s this “cover all the scenarios” that I find very interesting and fun to tackle when working on a project 🙂

The Just in Case Mindset in CSS →

Modern CSS grid solutions to common layout problems

Kevin Pennekamp (Vycke):

With the addition of grids, we can overcome media-query fatigue. Not only make our CSS more maintainable, but they also improve the user experience. We can let CSS handle the available space. In this article, I will describe three layout implementations that can improve your (personal) website.

The addition of Grid Layout to CSS indeed has changed the way I lay out web pages quite a lot. I don’t event want to go back to using floats (or tables, before that) 😅

Modern CSS grid solutions to common layout problems →

10 Modern Layouts in 1 Line of CSS

Already making rounds on Twitter last week, but now the video’s been released too:

In this dynamic talk, Una goes over the power of modern CSS layout techniques by highlighting a few key terms and how much detail can be described in a single line of code. Learn a few layout tricks you can implement in your codebase today, and be able to write entire swaths of layout with just a few lines of code.

Ten modern layouts in one line of CSS →
1-Line Layouts Demo Site →

CSS Grid Layout Module Level 2: Masonry Layout

UPDATE 2020.10.22: Masonry Layout has officially become part of the CSS Grid Module Level 3 spec!

A long requested CSS feature is to be able to create a “Masonry Layout” using pure CSS. Today we can already create a Masonry-like layout using grid-auto-flow: dense;, but unfortunately that’s not the real deal.

🤔 Masonry Layout?

Masonry is a grid layout based on columns, as popularized by Pinterest. Unlike other grid layouts, it doesn’t have fixed height rows. It works by placing elements in optimal position based on available vertical space, sort of like a mason fitting stones in a wall.

It became easy to implement thanks to the Masonry JavaScript Library.

~

Thankfully discussions to natively implement Masonry in CSS haven’t stopped and back in January it kinda settled on this syntax:

.masonry {
  display: grid;
  grid-gap: 1em;
  grid: masonry / repeat(auto-fit, minmax(20em, 1fr));
}

☝️ That’s grid-template-rows being set to masonry there, and grid-template-columns to the already familiar repeat(auto-fit, minmax(20em, 1fr))

Knowing that Tab Atkins – author of CSS Grid Layout Level 1 – responded with I’m liking this quite a bit! was a good sign. What’s even better is that Firefox Nightly (version 77a1) has implemented it by now, behind a flag.

👨‍🔬 Do note that this Masonry addition is still a proposal and is considered to be highly experimental at the time of writing. The syntax is still being discussed upon and is NOT final at all. Heck, you can’t even find a mention of Masonry in the CSS Grid Module Level 2 Draft!

~

To enable the experimental Masonry implementation in Firefox Nightly go to about:config and set layout.css.grid-template-masonry-value.enabled to true.

Once enabled the following pen should show a nice Masonry Layout:

See the Pen CSS Masonry Layout (FF Nightly – Feature Flag) by Miriam Suzanne (@mirisuzanne) on CodePen.

Yes, I’m really excited about this one … let’s hope it sticks!

~

Did this help you out? Like what you see?
Thank me with a coffee.

I don't do this for profit but a small one-time donation would surely put a smile on my face. Thanks!

☕️ Buy me a Coffee (€3)

To stay in the loop you can follow @bramus or follow @bramusblog on Twitter.

Margin considered harmful

Max Stoiber:

We should ban margin from our components. Hear me out.

By banning margin from all components you have to build more reusable and encapsulated components.

I think the message is a bit more nuanced though: margins of course still are allowed and used throughout your CSS, but if you want to allow a developer to tweak the spacing used inside your component provide a prop to let them do so.

However, don’t let them set a direct size (e.g. 1em), but give them several options to choose from. Think of Tailwind’s .m-0, .m-1, .m-2, etc. to tweak the margin itself, or Bootstrap’s .btn-sm and .btn-lg to make buttons small or big.

Margin considered harmful →

Crafting a Cutout Collage Layout with CSS Grid and clip-path

On Codrops there’s a tutorial on how to create a Cutout Collage Layout using CSS Grid and Clip Path. The result looks quite nice I must say:

What I’m not too excited about in this implementation is the fact that it requires you to manually cut up the image into parts beforehand, even though clip-path is used later on. I’ve quickly made a pen that uses the one single image, yielding a similar cutout effect.

See the Pen Magazine Cutout by Bramus (@bramus) on CodePen.

I’ve only cut out a few parts, but you get the idea. The parallax part as seen in the original is left as an exercise to you, dear reader 😛

🚧 A big help in creating these cutouts was the Shape Path Editor which you can find in Firefox’ DevTools. Without it such a task would have been really difficult.

“Designing Intrinsic Layouts” by Jen Simmons

Twenty-five years after the web began, we finally have a real toolkit for creating layouts. Combining CSS Grid, Flexbox, Multicolumn, Flow layout and Writing Modes gives us the technical ability to build layouts today without the horrible hacks and compromises of the past.

In this hour-long talk captured live at An Event Apart DC 2019, Jen Simmons walks you through the thinking process of creating accessible & reusable page and component layouts.

Her YouTube Channel Layout Land, which is mentioned at the start, is worth a closer look if you’re looking for more CSS Grid/Flexbox/etc. related videos.

“Designing Intrinsic Layouts” by Jen Simmons – An Event Apart →