flexbox – CSS-Tricks https://css-tricks.com Tips, Tricks, and Techniques on using Cascading Style Sheets. Tue, 21 May 2024 09:32:45 +0000 en-US hourly 1 https://wordpress.org/?v=6.5.5 https://i0.wp.com/css-tricks.com/wp-content/uploads/2021/07/star.png?fit=32%2C32&ssl=1 flexbox – CSS-Tricks https://css-tricks.com 32 32 45537868 Responsive Layouts, Fewer Media Queries https://css-tricks.com/responsive-layouts-fewer-media-queries/ https://css-tricks.com/responsive-layouts-fewer-media-queries/#comments Mon, 22 Nov 2021 15:29:03 +0000 https://css-tricks.com/?p=357080 We cannot talk about web development without talking about Responsive Design. It’s just a given these days and has been for many years. Media queries are a part of Responsive Design and they aren’t going anywhere. Since the introduction of …


Responsive Layouts, Fewer Media Queries originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
We cannot talk about web development without talking about Responsive Design. It’s just a given these days and has been for many years. Media queries are a part of Responsive Design and they aren’t going anywhere. Since the introduction of media queries (literally decades ago), CSS has evolved to the points that there are a lot of tricks that can help us drastically reduce the usage of media queries we use. In some cases, I will show you how to replace multiple media queries with only one CSS declaration. These approaches can result in less code, be easier to maintain, and be more tied to the content at hand.

Let’s first take a look at some widely used methods to build responsive layouts without media queries. No surprises here — these methods are related to flexbox and grid.

Using flex and flex-wrap

Live Demo

In the above demo, flex: 400px sets a base width for each element in the grid that is equal to 400px. Each element wraps to a new line if there isn’t enough room on the currently line to hold it. Meanwhile, the elements on each line grow/stretch to fill any remaining space in the container that’s leftover if the line cannot fit another 400px element, and they shrink back down as far as 400px if another 400px element can indeed squeeze in there.

Let’s also remember that flex: 400px is a shorthand equivalent to flex: 1 1 400px (flex-grow: 1, flex-shrink: 1, flex-basis: 400px).

What we have so far:

  • ✔️ Only two lines of code
  • ❌ Consistent element widths in the footer
  • ❌ Control the number of items per row
  • ❌ Control when the items wrap

Using auto-fit and minmax

Live Demo

Similar to the previous method, we are setting a base width—thanks to repeat(auto-fit, minmax(400px, 1fr))—and our items wrap if there’s enough space for them. This time, though we’re reaching for CSS Grid. That means the elements on each line also grow to fill any remaining space, but unlike the flexbox configuration, the last row maintains the same width as the rest of the elements.

So, we improved one of requirements and solved another, but also introduced a new issue since our items cannot shrink below 400px which may lead to some overflow.

  • ✔️ Only one line of code
  • ✔️ Consistent element widths in the footer
  • ❌ Control the number of items per row
  • ❌ Items grow, but do not shrink
  • ❌ Control when the items wrap

Both of the techniques we just looked at are good, but we also now see they come with a few drawbacks. But we can overcome those with some CSS trickery.

Control the number of items per row

Let’s take our first example and change flex: 400px to flex: max(400px, (100% - 20px)/3).

Resize the screen and notice that each row never has more than three items, even on a super wide screen. We have limited each line to a maximum of three elements, meaning each line only contains between one and three items at any given time.

Let’s dissect the code:

  • When the screen width increases, the width of our container also increases, meaning that 100%/3 gets bigger than 400px at some point.
  • Since we are using the max() function as the width and are dividing 100% by 3 in it, the largest any single element can be is just one-third of the overall container width. So, we get a maximum of three elements per row.
  • When the screen width is small, 400px takes the lead and we get our initial behavior.

You might also be asking: What the heck is that 20px value in the formula?

It’s twice the grid template’s gap value, which is 10px times two. When we have three items on a row, there are two gaps between elements (one on each on the left and right sides of the middle element), so for N items we should use max(400px, (100% - (N - 1) * gap)/N). Yes, we need to account for the gap when defining the width, but don’t worry, we can still optimize the formula to remove it!

We can use max(400px, 100%/(N + 1) + 0.1%). The logic is: we tell the browser that each item has a width equal to 100%/(N + 1) so N + 1 items per row, but we add a tiny percentage ( 0.1%)—thus one of the items wraps and we end with only N items per row. Clever, right? No more worrying about the gap!

Now we can control the maximum number of items per row which give us a partial control over the number of items per row.

The same can also be applied to the CSS Grid method as well:

Note that here I have introduced custom properties to control the different values.

We’re getting closer!

  • ✔️ Only one line of code
  • ✔️ Consistent element widths in the footer
  • ⚠️ Partial control of the number of items per row
  • ❌ Items grow, but do not shrink
  • ❌ Control when the items wrap

Items grow, but do not shrink

We noted earlier that using the grid method could lead to overflow if the base width is bigger than the container width. To overcome this we change:

max(400px, 100%/(N + 1) + 0.1%)

…to:

clamp(100%/(N + 1) + 0.1%, 400px, 100%)

Breaking this down:

  • When the screen width is big, 400px is clamped to 100%/(N + 1) + 0.1%, maintaining our control of the maximum number of items per row.
  • When the screen width is small, 400px is clamped to 100% so our items never exceed the container width.

We’re getting even closer!

  • ✔️ Only one line of code
  • ✔️ Consistent element widths in the footer
  • ⚠️ Partial control of the number of items per row
  • ✔️ Items grow and shrink
  • ❌ Control when the items wrap

Control when the items wrap

So far, we’ve had no control over when elements wrap from one line to another. We don’t really know when it happens because it depends a number of things, like the base width, the gap, the container width, etc. To control this, we are going to change our last clamp() formula, from this:

clamp(100%/(N + 1) + 0.1%, 400px, 100%)

…to:

clamp(100%/(N + 1) + 0.1%, (400px - 100vw)*1000, 100%)

I can hear you screaming about that crazy-looking math, but bear with me. It’s easier than you might think. Here’s what’s happening:

  • When the screen width (100vw) is greater than 400px, (400px - 100vw) results in a negative value, and it gets clamped down to 100%/(N + 1) + 0.1%, which is a positive value. This gives us N items per row.
  • When the screen width (100vw) is less than 400px, (400px - 100vw) is a positive value and multiplied by a big value that’s clamped to the 100%. This results in one full-width element per row.
Live Demo

Hey, we made our first media query without a real media query! We are updating the number of items per row from N to 1 thanks to our clamp() formula. It should be noted that 400px behave as a breakpoint in this case.

What about: from N items per row to M items per row?

We can totally do that by updating our container’s clamped width:

clamp(100%/(N + 1) + 0.1%, (400px - 100vw)*1000, 100%/(M + 1) + 0.1%)

I think you probably get the trick by now. When the screen width is bigger than 400px we fall into the first rule (N items per row). When the screen width is smaller than 400px, we fall into the second rule (M items per row).

Live Demo

There we go! We can now control the number of items per row and when that number should change—using only CSS custom properties and one CSS declaration.

  • ✔️ Only one line of code
  • ✔️ Consistent element widths in the footer
  • ✔️ Full control of the number of items per row
  • ✔️ Items grow and shrink
  • ✔️ Control when the items wrap

More examples!

Controlling the number of items between two values is good, but doing it for multiple values is even better! Let’s try going from N items per row to M items per row, down to one item pre row.

Our formula becomes:

clamp(clamp(100%/(N + 1) + 0.1%, (W1 - 100vw)*1000,100%/(M + 1) + 0.1%), (W2 - 100vw)*1000, 100%)

A clamp() within a clamp()? Yes, it starts to get a big lengthy and confusing but still easy to understand. Notice the W1 and W2 variables in there. Since we are changing the number of items per rows between three values, we need two “breakpoints” (from N to M, and from M to 1).

Here’s what’s happening:

  • When the screen width is smaller than W2, we clamp to 100%, or one item per row.
  • When the screen width is larger than W2, we clamp to the first clamp().
  • In that first clamp, when the screen width is smaller than W1, we clamp to 100%/(M + 1) + 0.1%), or M items per row.
  • In that first clamp, when the screen width is bigger than W1, we clamp to 100%/(N + 1) + 0.1%), or N items per row.

We made two media queries using only one CSS declaration! Not only this, but we can adjust that declaration thanks to the CSS custom properties, which means we can have different breakpoints and a different number of columns for different containers.

How many media queries do we have in the above example? Too many to count but we will not stop there. We can have even more by nesting another clamp() to get from N columns to M columns to P columns to one column. (😱)

clamp(
  clamp(
    clamp(100%/(var(--n) + 1) + 0.1%, (var(--w1) - 100vw)*1000,
          100%/(var(--m) + 1) + 0.1%),(var(--w2) - 100vw)*1000,
          100%/(var(--p) + 1) + 0.1%),(var(--w3) - 100vw)*1000,
          100%), 1fr))
from N columns to M columns to P columns to 1 column

As I mentioned at the very beginning of this article, we have a responsive layout without any single media queries while using just one CSS declaration—sure, it’s a lengthy declaration, but still counts as one.

A small summary of what we have:

  • ✔️ Only one line of code
  • ✔️ Consistent element widths in the footer
  • ✔️ Full control of the number of items per row
  • ✔️ Items grow and shrink
  • ✔️ Control when the items wrap
  • ✔️ Easy to update using CSS custom properties

Let’s simulate container queries

Everyone is excited about container queries! What makes them neat is they consider the width of the element instead of the viewport/screen. The idea is that an element can adapt based on the width of its parent container for more fine-grain control over how elements respond to different contexts.

Container queries aren’t officially supported anywhere at the time of this writing, but we can certainly mimic them with our strategy. If we change 100vw with 100% throughout the code, things are based on the .container element’s width instead of the viewport width. As simple as that!

Resize the below containers and see the magic in play:

The number of columns change based on the container width which means we are simulating container queries! We’re basically doing that just by changing viewport units for a relative percentage value.

More tricks!

Now that we can control the number of columns, let’s explore more tricks that allow us to create conditional CSS based on either the screen size (or the element size).

Conditional background color

A while ago someone on StackOverflow asked if it is possible to change the color of an element based on its width or height. Many said that it’s impossible or that it would require a media query.

But I have found a trick to do it without a media query:

div {
  background:
   linear-gradient(green 0 0) 0 / max(0px,100px - 100%) 1px,
   red;
}
  • We have a linear gradient layer with a width equal to max(0px,100px - 100%) and a height equal to 1px. The height doesn’t really matter since the gradient repeats by default. Plus, it’s a one color gradient, so any height will do the job.
  • 100% refers to the element’s width. If 100% computes to a value bigger than 100px, the max() gives us 0px, which means that the gradient does not show, but the comma-separated red background does.
  • If 100% computes to a value smaller than 100px, the gradient does show and we get a green background instead.

In other words, we made a condition based on the width of the element compared to 100px!

This demo supports Chrome, Edge, and Firefox at the time of writing.

The same logic can be based on an element’s height instead by rearranging where that 1px value goes: 1px max(0px,100px - 100%). We can also consider the screen dimension by using vh or vw instead of %. We can even have more than two colors by adding more gradient layers.

div {
  background:
   linear-gradient(purple 0 0) 0 /max(0px,100px - 100%) 1px,
   linear-gradient(blue   0 0) 0 /max(0px,300px - 100%) 1px,
   linear-gradient(green  0 0) 0 /max(0px,500px - 100%) 1px,
   red;
}

Toggling an element’s visibility

To show/hide an element based on the screen size, we generally reach for a media query and plop a classic display: none in there. Here is another idea that simulates the same behavior, only without a media query:

div {
  max-width: clamp(0px, (100vw - 500px) * 1000, 100%);
  max-height: clamp(0px, (100vw - 500px) * 1000, 1000px);
  overflow: hidden;
}

Based on the screen width (100vw), we either get clamped to a 0px value for the max-height and max-width (meaning the element is hidden) or get clamped to 100% (meaning the element is visible and never greater than full width). We’re avoiding using a percentage for the max-height since it fails. That’s why we’re using a big pixel value (1000px).

Notice how the green elements disappear on small screens:

It should be noted that this method is not equivalent to the toggle of the display value. It’s more of a trick to give the element 0×0 dimensions, making it invisible. It may not be suitable for all cases, so use it with caution! It’s more a trick to be used with decorative elements where we won’t have accessibility issues. Chris wrote about how to hide content responsibly.

It’s important to note that I am using 0px and not 0 inside clamp() and max(). The latter makes invalidates property. I won’t dig into this but I have answered a Stack Overflow question related to this quirk if you want more detail.

Changing the position of an element

The following trick is useful when we deal with a fixed or absolutely positioned element. The difference here is that we need to update the position based on the screen width. Like the previous trick, we still rely on clamp() and a formula that looks like this: clamp(X1,(100vw - W)*1000, X2).

Basically, we are going to switch between the X1 and X2 values based on the difference, 100vw - W, where W is the width that simulates our breakpoint.

Let’s take an example. We want a div placed on the left edge (top: 50%; left:0) when the screen size is smaller than 400px, and place it somewhere else (say top: 10%; left: 40%) otherwise.

div {
  --c:(100vw - 400px); /* we define our condition */
  top: clamp(10%, var(--c) * -1000, 50%);
  left: clamp(0px, var(--c) * 1000, 40%);
}
Live Demo

First, I have defined the condition with a CSS custom property to avoid the repetition. Note that I also used it with the background color switching trick we saw earlier—we can either use (100vw - 400px) or (400px - 100vw), but pay attention to the calculation later as both don’t have the same sign.

Then, within each clamp(), we always start with the smallest value for each property. Don’t incorrectly assume that we need to put the small screen value first!

Finally, we define the sign for each condition. I picked (100vw - 400px), which means that this value will be negative when the screen width is smaller than 400px, and positive when the screen width is bigger than 400px. If I need the smallest value of clamp() to be considered below 400px then I do nothing to the sign of the condition (I keep it positive) but if I need the smallest value to be considered above 400px I need to invert the sign of the condition. That’s why you see (100vw - 400px)*-1000 with the top property.

OK, I get it. This isn’t the more straightforward concept, so let’s do the opposite reasoning and trace our steps to get a better idea of what we’re doing.

For top, we have clamp(10%,(100vw - 400px)*-1000,50%) so…

  • if the screen width (100vw) is smaller than 400px, then the difference (100vw - 400px) is a negative value. We multiply it with another big negative value (-1000 in this case) to get a big positive value that gets clamped to 50%: That means we’re left with top: 50% when the screen size is smaller than 400px.
  • if the screen width (100vw) is bigger than 400px, we end with: top: 10% instead.

The same logic applies to what we’re declaring on the left property. The only difference is that we multiply with 1000 instead of -1000 .

Here’s a secret: You don’t really need all that math. You can experiment until you get it perfect values, but for the sake of the article, I need to explain things in a way that leads to consistent behavior.

It should be noted that a trick like this works with any property that accepts length values (padding, margin, border-width, translate, etc.). We are not limited to changing the position, but other properties as well.

Demos!

Most of you are probably wondering if any of these concepts are at all practical to use in a real-world use case. Let me show you a few examples that will (hopefully) convince you that they are.

Progress bar

The background color changing trick makes for a great progress bar or any similar element where we need to show a different color based on progression.

This demo supports Chrome, Edge, and Firefox at the time of writing.

That demo is a pretty simple example where I define three ranges:

  • Red: [0% 30%]
  • Orange: [30% 60%]
  • Green: [60% 100%]

There’s no wild CSS or JavaScript to update the color. A “magic” background property allows us to have a dynamic color that changes based on computed values.

Editable content

It’s common to give users a way to edit content. We can update color based on what’s entered.

In the following example, we get a yellow “warning” when entering more than three lines of text, and a red “warning” if we go above six lines. This can a way to reduce JavaScript that needs to detect the height, then add/remove a particular class.

This demo supports Chrome, Edge, and Firefox at the time of writing.

Timeline layout

Timelines are great patterns for visualizing key moments in time. This implementation uses three tricks to get one without any media queries. One trick is updating the number of columns, another is hiding some elements on small screens, and the last one is updating the background color. Again, no media queries!

When the screen width is below 600px, all of the pseudo elements are removed, changing the layout from two columns to one column. Then the color updates from a blue/green/green/blue pattern to a blue/green/blue/green one.

Responsive card

Here’s a responsive card approach where CSS properties update based on the viewport size. Normally, we might expect the layout to transition from two columns on large screens to one column on small screens, where the card image is stacked either above or below the content. In this example, however, we change the position, width, height, padding, and border radius of the image to get a totally different layout where the image sits beside the card title.

Speech bubbles

Need some nice-looking testimonials for your product or service? These responsive speech bubbles work just about anywhere, even without media queries.

Fixed button

You know those buttons that are sometimes fixed to the left or right edge of the screen, usually for used to link up a contact for or survey? We can have one of those on large screens, then transform it into a persistent circular button fixed to the bottom-right corner on small screens for more convenient taps.

Fixed alert

One more demo, this time for something that could work for those GDPR cookie notices:

Conclusion

Media queries have been a core ingredient for responsive designs since the term responsive design was coined years ago. While they certainly aren’t going anywhere, we covered a bunch of newer CSS features and concepts that allow us to rely less often on media queries for creating responsive layouts.

We looked at flexbox and grid, clamp(), relative units, and combined them together to do all kinds of things, from changing the background of an element based on its container width, moving positions at certain screen sizes, and even mimicking as-of-yet-unreleased container queries. Exciting stuff! And all without one @media in the CSS.

The goal here is not to get rid or replace media queries but it’s more to optimize and reduce the amount of code especially that CSS has evolved a lot and now we have some powerful tool to create conditional styles. In other words, it’s awesome to see the CSS feature set grow in ways that make our lives as front-enders easier while granting us superpowers to control how our designs behave like we have never had before.


Responsive Layouts, Fewer Media Queries originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
https://css-tricks.com/responsive-layouts-fewer-media-queries/feed/ 20 357080
Minding the “gap” https://css-tricks.com/minding-the-gap/ https://css-tricks.com/minding-the-gap/#comments Thu, 16 Sep 2021 14:34:46 +0000 https://css-tricks.com/?p=351481 You might already know about the CSS gap property. It isn’t exactly new, but it did gain an important new ability last year: it now works in Flexbox in addition to CSS Grid. That, and the fact that I …


Minding the “gap” originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
You might already know about the CSS gap property. It isn’t exactly new, but it did gain an important new ability last year: it now works in Flexbox in addition to CSS Grid. That, and the fact that I believe the property is more complicated than it appears, made me want to go back and explain exactly how it works.

Let’s take a proper look at gap and its associated properties, and understand how and where they work.

All the gap properties

To start us off, let’s review all of the CSS properties related to gap. There are six in total:

  • grid-row-gap
  • grid-column-gap
  • grid-gap
  • row-gap
  • column-gap
  • gap

From this list, we can already ignore the first three properties. The grid-* properties were added early when CSS Grid’s specifications were being drafted, and later deprecated when gap became more generalized. Browsers still support these deprecated grid-* properties (as of this writing) and merely treat them as if the grid- prefix is not present. Hence, grid-gap is the same as gap, grid-column-gap is the same as column-gap and grid-row-gap is the same as row-gap.

As for the other three properties, knowing that gap is a shorthand that lets you specify the other two properties, we really only need to know what row-gap and column-gap do.

Our understanding of these properties depends on the type of CSS layout we’re using. Let’s look at those options first.

Where can gaps be used?

If you’re like me, you’ve used gaps in grid layouts, but they can now be used in Flexbox, as well as multi-column layouts. Let’s go over each case.

Grid gaps

All browsers support gaps in grid layouts, and they’re quite simple to understand in this context.

  • row-gap introduces space between row tracks
  • column-gap introduces space between column tracks

Let’s create a grid with three columns and two rows:

.container {
  display: grid;
  grid-template-columns: 200px 100px 300px;
  grid-template-rows: 100px 100px;
}

This gives us the following grid:

A 3 by 2 grid of yellow boxes, with fix-sized tracks displayed in dashed purple lines.

The lines in the picture above are called grid lines, and they separate the tracks (rows and columns) of the grid. These lines don’t really exist in the grid — they’re invisible, have no thickness, and are typically what DevTools displays when we enable the grid inspector (in Safari, Firefox, Edge or Chrome).

The CSS-Tricks site with DevTools open and docked to the left of the viewport in Firefox. DevTools displays Grid Inspector options and the page contains borders around elements in blue and green to indicate grid track lines.

If we do, however, start adding gaps to our grid, it will work as though these lines start acquiring thickness.

Let’s add a 20px gap:

.container {
  display: grid;
  grid-template-columns: 200px 100px 300px;
  grid-template-rows: 100px 100px;
  gap: 20px;
}

The lines between our tracks are now 20px thick and therefore push grid items further apart.

A 3 by 2 grid of yellow boxes with 20px gaps between the column and row tracks.

It’s worth noting that the tracks still have the same sizes (defined by the grid-template-* properties); therefore, the grid is wider and taller than it was without gaps.

In grid, row-gap always applies between row tracks. So, if we replace gap with row-gap in the above example, we get this:

The same 3 by 2 grid with a gap only between the two rows.

And column-gap always applies between column tracks, so replacing gap with column-gap produces the following result:

The same 3 by 2 grid with a gap only between the three columns.

Grid is simple because, by default, columns are vertical, and rows are horizontal, just like in a table. So it’s easy to remember where column-gap and row-gap apply.

Now, things do get a little more complicated when writing-mode is used. The default writing mode on the web is horizontal, from left to right, but there are vertical writing modes as well, and when that happens, columns become horizontal and rows are vertical. Always pay attention to writing-mode as it can make it less intuitive than it usually is.

This is a good transition into the next section as columns and rows get new meanings within Flexbox.

Flexbox gaps

Let’s talk about gaps in Flexbox layouts, where things get a little more complicated. We’ll use the following example:

.container {
  display: flex;
}

By default, this gives us a row flex container, which means items within the container are stacked from left to right on the same horizontal line.

A default flex container with six yellow boxes stacked horizontally, from left to right. Each one says flex item in it. A purple border is drawn around each item.

In this case, column-gap is applied between items and row-gap does nothing. That’s because there is only one line (or row). But now let’s add some gap between items:

.container {
  display: flex;
  column-gap: 10px;
}
The same six yellow flex items on a single line with 10 pixels between them.

Now let’s switch the flex-direction of our container to column, which stacks items vertically, from top to bottom, with the following code:

.container {
  display: flex;
  flex-direction: column;
  column-gap: 10px;
}

Here is what happens:

Six yellow rectangles stacked from top to bottom with no gap between them.

The gap disappeared. Even if column-gap did add space between items when the container was in a row direction, it does not work anymore in the column direction.

We need to use row-gap to get it back. Alternatively, we could use the gap shorthand with one value, which would apply the same gap in both directions and, therefore, work in both cases.

.container {
  display: flex;
  flex-direction: column;
  gap: 10px;
}
The same six yellow rectangles stacked vertically, but with 10 pixels of space between them.

So, to summarize, colum-gap always works vertically (assuming the default writing-mode), and row-gap always works horizontally. This does not depend on the direction of the flex container.

But now take a look at an example where line wrapping is involved:

.container {
  display: flex;
  flex-wrap: wrap;
  column-gap: 40px;
  row-gap: 10px;
  justify-content: center;
}

Here, we’re allowing items to wrap on multiple lines with flex-wrap: wrap if there isn’t enough space to fit everything in a single line.

Five yellow boxes that wrap into two lines, where three are on the first line and two are on the bottom line. There are differently sized gaps between them based on the space around them.

In this case, the column-gap is still applied vertically between items, and row-gap is applied horizontally between the two flex lines.

There’s one interesting difference between this and grid. The column gaps don’t necessarily align across flex lines. That’s because of justify-content: center, which centers items within their flex lines. This way, we can see that each flex line is a separate layout where gaps apply independently of other lines.

Multi-column gaps

Multi-column is a type of layout that makes it very easy to automatically flow content between several columns, like what you might expect in a traditional newspaper article. We set a number of columns and set the size for each column.

A three-column layout of plain text with a 1 em gap between columns

Gaps in multi-column layouts don’t quite work the same as grid or Flexbox. There are three notable differences:

  • row-gap has no effect,
  • column-gap has a non-0 default value,
  • and gaps can be styled.

Let’s break those down. First of all, row-gap has no effect. In multi-column layouts, there aren’t any rows to separate. That means only column-gap is relevant (as is the gap shorthand).

Secondly, unlike in grid and Flexbox, column-gap has a default value of 1em in multi-column layouts (as opposed to 0). So, even when no gap is specified at all, the columns of content are still visually separated. The default gap can, of course, be overridden but it’s a good default to have.

Here is the code that the example is based on:

.container {
  column-count: 3;
  padding: 1em;
}

Finally, we can style the empty gap between columns in a multi-column layout. We use the column-rule property which works like border:

.container {
  column-count: 3;
  column-gap: 12px;
  column-rule: 4px solid red;
  padding: 12px;
}
The same three columns of plain text, but with a red border between the columns.
The column-rule property gives us some styling affordance in a multi-column layout.

Browser support

gap is really well-supported across the board. There’s more information over at caniuse, but to summarize:

  • Flexbox: gap is supported everywhere except for Internet Explorer (which is on its way out), Opera Mini and UC Browser for Android. caniuse has global support at 87.31%.
  • Grid: Same thing, but we’re looking at 93.79% global support.
  • Multi-column: Same thing, too, but it’s unsupported in Safari and has 75.59% global support.

So, overall, the gap property is well supported and, in most cases, workarounds are unnecessary.

Styling the gap in flex and grid

Styling gap in Flexbox and CSS Grid would be really useful. The sad news is that it isn’t supported anywhere today. But the good news is that it could be in the near future. This has been discussed over at the CSS working group and is in the works in Firefox. Once we have a working implementation in Firefox along with the spec proposal, perhaps it will drive implementation in other browsers.

In the meantime, there are ways around this.

One is to give the grid container a background color, then a different color for the items, and finally a gap to make the container color show through.

While this works, it means we’re unable to use gaps to introduce space between items. The gap here acts as a border width instead. So, to visually separate the items out a bit more, we need to use padding or margin on the items, which isn’t as great… as we’ll see in the next section.

Can’t I just use margin or padding?

Yes, in most cases we can also use margin (and/or padding) to add visual space between elements of a layout. But gap comes with multiple advantages.

First, gaps are defined at the container level. This means we define them once for the entire layout and they are applied consistently within it. Using margins would require a declaration on each and every item. This can get complicated when items are different in nature, or come from different reusable components.

On top of this, gaps do the right thing by default with just one line of code. For example, if we’re trying to introduce some space in between flex items, not around them, margin would require special cases to remove extra margins before the first item and after the last one. With gaps, we don’t need to do this.

With a margin: 0 20px on each flex item, we’d end up with:

However with a gap: 40px on the container, we’d get this:

Similarly in grid layout, defining gap at the container level is much simpler and provides better results than having to define a margin on each item and accounting for the margin that applies on the edge of the grid.

With margin: 20px on each grid item:

And with gap: 40px on the grid container instead:

Empty space adds up

With everything said up to this point, margin and gap don’t have to be exclusive. In fact, there are many ways to spread items of a layout further apart, and they all can work together very well.

The gap property is just one part of the empty space created between boxes in a layout container. margin, padding, and alignment all may increase the empty space on top of what gap already defines.

Let’s consider an example where we build a simple flex layout with a given width, some gap, some distribution of content (using justify-content), and some margin and padding:

.container {
  display: flex;
  gap: 40px;
  width: 900px;
  justify-content: space-around;
}
.item {
  padding: 20px;
  margin: 0 20px;
}

Let’s assume this code produces the following result:

Now, let’s see exactly how the empty space between items got created:

As we see, there are four different types of empty space between two consecutive flex items:

  • Between two consecutive items, the gap defines the minimum space between these items. There can be more, like in this case, but there can never be less space.
  • Margin pushes items even further apart, but unlike gap, it adds space both sides of all items.
  • Padding provides some space inside each item.
  • Finally, and only because there is enough remaining space, content distribution kicks in and distributes the items evenly within the flex line, according to the space-around value.

Debugging gaps

Let’s conclude with a topic that’s very near and dear to my heart: DevTools support for debugging gaps. There can always be cases where things go wrong, and knowing that DevTools has got our backs is very comforting, but we do need to know which tools can help us in this case.

For gap, I can think of two very specific features that might become useful.

Is my gap active?

Unless we misspelled gap or provided an invalid value, the property is always going to apply to the page. For example, this is correct:

.some-class {
  display: block;
  gap: 3em;
}

It won’t do anything, but it is valid CSS and the browser doesn’t mind that gap doesn’t apply to block layouts. However, Firefox has a feature called Inactive CSS that does just this: it cares about valid CSS that’s applied to things that make sense. In this case, Firefox DevTools displays a warning in the Inspector.

Where is my gap?

Chrome and Microsoft Edge also have a very useful feature for debugging gaps. It was added through a collaboration between Microsoft and Google that was aimed at building layout debugging tools in Chromium (the open source project that powers both of the browsers, as well as others). In these browsers, you can hover over individual properties in the Styles panel, and see their effect on the page.

The cursor is hovering over the gap and the justify-content properties in the Styles panel, and the corresponding areas of the page light up to indicate where these properties have effect.
The cursor is hovering over the margin and padding properties, which highlights the corresponding box model areas of the page.

And that’s it. I hope this article was useful in helping understand some of the details of how gaps work in CSS.


Minding the “gap” originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
https://css-tricks.com/minding-the-gap/feed/ 2 351481
Equal Columns With Flexbox: It’s More Complicated Than You Might Think https://css-tricks.com/equal-columns-with-flexbox-its-more-complicated-than-you-might-think/ https://css-tricks.com/equal-columns-with-flexbox-its-more-complicated-than-you-might-think/#comments Thu, 10 Jun 2021 14:34:22 +0000 https://css-tricks.com/?p=341894 As awesome as flexbox is, what it’s doing under the hood is actually a little strange because, by default, it is doing two things at once. It first looks at the content size which is what we would get if by declaring width: max-content on an element. But on top of that, flex-shrink is also doing some work allowing the items to be smaller, but only if needed.

Let’s break those two down and see how they work together.


Equal Columns With Flexbox: It’s More Complicated Than You Might Think originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
You get a nice-looking design handed to you and it has this nice big hero section, followed by one of those three-up columns right beneath it. You know, like almost every other website you’ve ever worked on.

You bang through the hero section and get to work on the three-column section. It’s time to pull out our trusty friend flexbox! Except, you write display: flex and you get this mess instead of getting the three equal columns you’d expect.

This happens because of how flexbox calculates the base size of an element. You’ve probably read lots of flexbox tutorials, and many of them (including my own) are an overly simplistic example of what flexbox does without really digging into the complex things that flexbox does for us that we take for granted.

I’m positive you’ve seen examples and tutorials that look at something like this, where three divs shrink down around the content that’s inside them:

In other words, we get some block-level elements shrinking down and slotting next to one another. It feels like flex wants things to be as small as possible. But in reality, flexbox actually wants things to be as big as possible.

Wait, what? Flex shrinks things by default — that can’t be right! Right?

As awesome as flexbox is, what it’s doing under the hood is actually a little strange because, by default, it is doing two things at once. It first looks at the content size which is what we would get if by declaring width: max-content on an element. But on top of that, flex-shrink is also doing some work allowing the items to be smaller, but only if needed.

To really understand what’s going on, let’s break those two down and see how they work together.

Diving into max-content

max-content is a pretty handy property value in certain situations, but we want to understand how it works in this particular situation where we are trying to get three seemingly simple equal columns. So let’s strip away flexbox for a moment and look at what max-content does on its own.

MDN does a good job of explaining it:

The max-content sizing keyword represents the intrinsic maximum width of the content. For text content this means that the content will not wrap at all even if it causes overflows.

Intrinsic might throw you off here, but it basically means we’re letting the content decide on the width, rather than us explicitly setting a set width. Uri Shaked aptly describes the behavior by saying “the browser pretends it has infinite space, and lays all the text in a single line while measuring its width.”

So, bottom line, max-content allows the content itself to define the width of the element. If you have a paragraph, the width of that paragraph will be the text inside it without any line breaks. If it’s a long enough paragraph, then you end up with some horizontal scrolling.

Let’s revisit that overly-simplistic example of three block-level elements that shrink down and slot next to one another. That isn’t happening because of flex-shrink; it’s happening because that’s the size of those elements when their declared width is max-content. That’s literally as wide as they go because that’s as wide as the combined content inside each element.

Here, take a look at those elements without flexbox doing it’s flexbox stuff, but with a width: max-content on there instead:

So, when there’s just a small amount of text, the intrinsic max-content shrinks things down instead of flex-shrink. Of course, flexbox also comes in with it’s default flex-direction: row behavior which turns the flex items into columns, putting them right next to one another. Here’s another look but with the free space highlighted.

Adding flex-shrink to the equation

So we see that declaring display: flex pushes that max-content intrinsic size on flex items. Those items want to be as big as their content. But there is another default that comes in here as well, which is flex-shrink.

flex-shrink is basically looking at all the flex items in a flexible container to make sure they don’t overflow the parent. If the flex items can all fit next to each other without overflowing the flexible container, then flex-shrink won’t do anything at all… it’s job is already done.

But if the flex items do overflow the container (thanks to that max-content intrinsic width thing), the flex items are allowed to shrink to prevent that overflow because flex-shrink is looking out for that.

This is why flex-shrink has a default value of 1. Without it, things would get messy pretty quickly.

Here’s why the columns aren’t equal

Going back to our design scenario where we need three equal columns beneath a hero, we saw that the columns aren’t equal widths. That’s because flexbox starts by looking at the content size of each flex item before even thinking about shrinking them.

Using Firefox’s DevTools (because it’s got some unique flexbox visualizations that others don’t), we can actually see how flexbox is calculating everything.

For simplicity’s sake, as we dive deeper into this, let’s work with some nice round numbers. We can do this by declaring widths on our flex items. When we declare a width on a flex item, we throw that intrinsic size out the window, as we’ve now declared an explicit value instead. This makes figuring out what’s really going on a lot easier.

In the Pen below, we have a parent that’s a 600px wide flexible container (display: flex). I’ve removed anything that might influence the numbers, so no gap or padding. I’ve also switched out the border for an outline so we can still visualize everything easily.

The first and third flex items have a width: 300px and the middle one a width: 600px. If we add that all up, it’s a total of 1200px. That’s bigger than the the 600px available within the parent, so flex-shrink kicks in.

flex-shrink is a ratio. If everything has the same flex-shrink (which is 1 by default), they all shrink at the same rate. That doesn’t mean they all shrink to the same size or by the same amount, but they all shrink at the same rate.

If we jump back into Firefox DevTools, we can see the base size, the flex-shrink and the final size. In this case, the two 300px elements are now 150px, and the 600px one is now 300px.

The two elements that have a base width of 300px become 150px.
The larger element with a base width of 600px becomes 300px.

If we add up all the base sizes of all three flex items (the actual widths we declared on them), the total comes out to 1200px. Our flex container is 600px wide. If we divide everything by 2, it fits! They are all shrinking by the same rate, dividing their own widths by 2.

It’s not often that we have nice round numbers like that in the real world, but I think this does a nice job illustrating how flexbox does what it does when figuring out how big to make things.

Getting the columns to be equal

There are a few different ways to get the three columns we want to be equal in width, but some are better than others. For all the approaches, the basic idea is that we want to get all the columns base size to be the same. If they have an equal base size, then they will shrink (or grow, if we use flex-grow) at an equal rate when flexbox does it’s flex things, and in theory, that should make them the same size.

There are a few common ways to do this, but as I discovered while diving into all of this, I have come to believe those approaches are flawed. Let’s look at two of the most common solutions that I see used in the wild, and I’ll explain why they don’t work.

Method 1: Using flex: 1

One way we can try to get all the flex items to have the same base size is by declaring flex: 1 on all of them:

.flex-parent { display: flex; }
.flex-parent > * { flex: 1; }

In a tutorial I made once, I used a different approach, and I must have had 100 people asking why I wasn’t using flex: 1 instead. I replied by saying I didn’t want to dive into the flex shorthand property. But then I used flex: 1 in a new demo, and to my surprise, it didn’t work.

The columns weren’t equal.

The middle column here is larger than the other two. It’s not by a ton, but the whole design pattern I’m creating is just so you have perfectly equal columns every single time, regardless of the content.

So why didn’t it work in this situation? The culprit here is the padding on the component in the middle column.

And maybe you’ll say it’s silly to add padding to one of the items and not the others, or that we can nest things (we’ll get to that). In my opinion, though, I should be able to have a layout that works regardless of the content that we’re putting in it. The web is all about components that we can plug and play these days, after all.

When I first set this up, I was sure it would work, and seeing this issue pop up made me want to learn what was really going on here.

The flex shorthand does more than just set the flex-grow: 1. If you don’t already know, flex is shorthand for flex-grow, flex-shrink, and flex-basis.

The default values for those constituent properties are:

.selector {
  flex-grow: 0;
  flex-shrink: 1;
  flex-basis: auto;
}

I’m not going to deep dive flex-basis in this article, as that’s something Robin has already done well. For now, we can think of it like width to keep things simple since we aren’t playing with flex-direction.

We’ve already seen how flex-shrink works. flex-grow is the opposite. If the size of all the flex items along the main axis is smaller than the parent, they can grow to fill that space.

So by default, flex items:

  • don’t grow;
  • if they would otherwise overflow the parent, they are allowed to shrink;
  • their width acts like max-content.

Putting that all together, the flex shorthand defaults to:

.selector {
  flex: 0 1 auto;
}

The fun thing with the flex shorthand is you can omit values. So, when we declare flex: 1, it’s setting the first value, flex-grow, to 1, which basically turns on flex-grow.

The strange thing here is what happens to the values that you omit. You’d expect them to stay at their defaults, but they don’t. Before we get to what happens, though, let’s first dive into what flex-grow even does.

As we’ve seen, flex-shrink allows elements to shrink if their base sizes add up to a computed value that’s bigger than the available width of the parent container. flex-grow is the opposite. If the grand total of the element base sizes is smaller than the value of the parent container’s width, then they will grow to fill the available space.

If we take that super basic example where we have three small divs next to one another and add flex-grow: 1, they grow to fill that leftover space.

But if we have three divs with unequal widths — like those ones we started with — adding flex-grow to them won’t do anything at all. They won’t grow because they’re already taking up all the available space —so much space, in fact, that flex-shrink needs to kick in and shrink them down to fit!

But, as folks have pointed out to me, setting flex: 1 can work to create equal columns. Well, sort of, as we saw above! In simple situations it does work though, as you can see below.

When we declare flex: 1 it works because, not only does this set the flex-grow to 1, but it also changes the flex-basis!

.selector {
  flex: 1;
  /* flex-grow: 1; */
  /* flex-shrink: 1; */
  /* flex-basis: 0%; Wait what? */
}

Yup, setting flex: 1 sets the flex-basis to 0%. This overwrites that intrinsic sizing we had before that behaved like max-content. All of our flex-items now want to have a base size of 0!

Looking at the expanded view of the flex: 1 shorthand in DevTools shows us that the flex-basis has changed to 0

So their base sizes are 0 now, but because of the flex-grow, they can all grow to fill up the empty space. And really, in this case, flex-shrink is no longer doing anything, as all the flex items now have a width of 0, and are growing to fill the available space.

FireFox’s DevTools showing an element with flex: 1 has a content size of 0 and is growing to fill the available space.

Just like the shrink example before, we’re taking the space that’s available, and letting all the flex items grow at an equal rate. Since they are all a base width of 0, growing at an equal rate means the available space is equally divided between them and they all have the same final size!

Except, as we saw, that’s not always the case…

The reason for this is because, when flexbox does all this stuff and distributes the space, whether it’s shrinking or growing a flex item, it’s looking at the content size of the element. If you remember back to the box model, we have the content size itself, then the padding, border, and margin outside of that.

And no, I didn’t forget * { box-sizing: border-box; }.

This is one of those strange quirks of CSS but it does make sense. If the browser looked at the width of those elements and included their padding and borders in the calculations, how could it shrink things down? Either the padding would also have to shrink or things are going to overflow the space. Both of those situations are terrible, so instead of looking at the box-size of elements when calculating the base size of them, it only looks at the content-box!

So, setting flex: 1 causes a problem in cases where you have borders or padding on some of your elements. I now have three elements that have a content-size of 0, but my middle one has padding on it. If we didn’t have that padding, we’d have the same math we did when we looked at how flex-shrink works.

A parent that is 600px and three flex items with a width of 0px. They all have a flex-grow: 1 so they grow at an equal rate, and they each end up 200px wide. But the padding mucks it all up. Instead, I end up with three divs with a content size of 0, but the middle one has padding: 1rem. That means it has a content size of 0, plus 32px padding as well.

We have 600 - 32 = 568px to divide equally, instead of 600px. All the divs want to grow at an equal rate, so 568 / 3 = 189.3333px.

And that’s what happens!

But… remember, that’s their content size, not the total width! That leaves us with two divs with a width of 189.333px, and another with a which of 189.333px + 32 = 221.333px. That’s a pretty substantial difference!

Method 2: flex-basis: 100%

I have always handled this like this:

.flex-parent {
  display: flex;
}

.flex-parent > * {
  flex-basis: 100%;
}

I thought this worked for the longest time. Actually, this was supposed to be my final solution after showing that flex: 1 doesn’t work. But while I was writing this article, I realized it also falls into the same problem, but it’s a lot less obvious. Enough so that I didn’t notice it with my eyes.

The elements are all trying to be 100% width, meaning they all want to match the width of the parent, which, again, is 600px (and in normal circumstances, is often much bigger, which further reduces the perceivable difference).

The thing is that 100% includes the padding in the computed values (because of * { box-size: border-box; }, which for the sake of this article, I’m assuming everyone is using). So, the outer divs end up with a content size of 600px, whereas the middle div ends up with a content size of 600 - 32 = 568px.

When the browser is working out how to evenly divide the space, it isn’t looking at how to evenly squish 1800px into a 600px space, but rather it’s looking at how to squish 1768px. Plus, as we saw earlier, flex items don’t shrink by the same amount, but at an equal pace! So, the element with padding shrinks slightly less in total than the others do.

This results in the .card having a final width of 214.483px while the others clock in at 192.75px. Again, this leads to unequal width values, though the difference is smaller than we saw with the flex: 1 solution.

Why CSS Grid is the better choice here

While all this is a little frustrating (and even a little confusing), it all happens for a good reason. If margins, padding, or borders changed sizes when flex gets involved, it would be a nightmare.

And maybe this means that CSS Grid might be a better solution to this really common design pattern.

I’ve long thought that flexbox was easier to pick up and start using than grid, but grid gives you more ultimate control in the long run, but that it’s a lot harder to figure out. I’ve changed my mind on that recently though, and I think not only does grid give us better control in this type of situation, but it’s actually more intuitive as well.

Normally, when we use grid, we explicitly declare our columns using grid-template-columns. We don’t have to do that though. We can make it behave a lot like flexbox does by using grid-auto-flow: column.

.grid-container {
  display: grid;
  grid-auto-flow: column;
}

Just like that, we end up with the same type of behavior as throwing display: flex on there. Like flex, the columns can potentially be unbalanced, but the advantage with grid is that the parent has total control over everything. So, rather than the content of the items having an impact like they would in flexbox, we only need one more line of code and we can solve the problem:

.grid-container {
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: 1fr;
}

I love that this is all on the parent selector, and that we don’t have to select the children to help get the layout that we are after!

The interesting thing here is how fr units work. They are literally called “flex units” in the spec, and they work just like flexbox does in dividing up space;. The big difference: they’re looking at the other tracks to determine how much space they have, paying no attention to the content inside those tracks.

That’s the real magic here. By declaring grid-auto-columns: 1fr, we are in essence saying, “by default, all my columns should have an equal width,” which is what we’ve been after from the start!

But what about at small screens?

What I love with this approach is we can keep it super simple:

.grid-container {
  display: grid;
  gap: 1em;
}

@media (min-width: 35em) {
  grid-auto-flow: column;
  grid-auto-columns: 1fr;
}

And just like that, it works perfectly. And by declaring display: grid from the start, we can include the gap to maintain equal spacing, whether the children are rows or columns.

I also find this to be a lot more intuitive than changing the flex-direction within a media query to get a similar result. As much as I love flexbox (and I really do still think it has great use cases), the fact that declaring flex-direction: column creates rows, and vice versa, is a little counter-intuitive at first glance.

And of course, if you prefer rolling without media queries, there is nothing stopping you from taking this to the next level with the help of auto-fit, which would be similar to setting something up with flex-wrap (though not exactly the same):

.grid-container {
  display: grid;
  gap: 1em;
  grid-template-columns: repeat(auto-fit, minmax(10em, 25em));
}

Making it work with flexbox

I realize that we can get around this with flexbox by nesting the element with the padding on it. We’ve done this since we started making layouts using floats, and Bootstrap really hammered home this type of design pattern.

<div class="container">
  <div class="row">
    <div class="col"> <div class="">... </div>
    <div class="col"> <div class="element-with-padding">...</div> </div>
    <div class="col"> ... </div>
  </div>
</div>

And there is nothing wrong with that. It works! But floats work too, and we’ve stopped using them for layouts as better solutions have been released in recent years.

One of the reasons that I love grid is because we can simplify our markup quite a bit. Just like we ditched floats for a better solution, I’d at least like to people to keep an open mind that maybe, just maybe, grid could be a better, and more intuitive solution for this type of design pattern.

Flexbox still has it’s place, of course

I still love flexbox, but I think its real power comes from times that we want to rely on the intrinsic width of the flex items, such as with navigations, or groups of elements, such as buttons or other items of varying width that you want to go next to one another.

In those situations, that behavior is an asset that makes things like even spacing between unequal items such a breeze! Flexbox is wonderful, and I have no plans to stop using.

In other situations though, when you find yourself fighting with how flexbox is trying to work, maybe you could turn to grid instead, even if it’s not a typical “2d” grid where you’re told you should be using it for.

People often tell me that they struggle to figure out grid because it’s too complicated, and while it can be, as we saw here, it doesn’t have to be.


Equal Columns With Flexbox: It’s More Complicated Than You Might Think originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
https://css-tricks.com/equal-columns-with-flexbox-its-more-complicated-than-you-might-think/feed/ 25 https://www.youtube.com/embed/fm3dSg4cxRI Flexbox is more complicated than you thought nonadult 341894
Float an Element to the Bottom Corner https://css-tricks.com/float-an-element-to-the-bottom-corner/ https://css-tricks.com/float-an-element-to-the-bottom-corner/#comments Mon, 19 Apr 2021 14:17:49 +0000 https://css-tricks.com/?p=338290 Need to lay out an element to the right or the left, such that text wraps around it? That’s an easy task for the float property. But what about if you also want to push that element (let’s call it …


Float an Element to the Bottom Corner originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
Need to lay out an element to the right or the left, such that text wraps around it? That’s an easy task for the float property. But what about if you also want to push that element (let’s call it an image) to one of the bottom corners while we’re at it? Sounds a bit tricky, right? We probably need JavaScript?

Nope, few lines of (tricky) CSS can do it! Here’s the CSS-only solution that will make your image to stick to the bottom corner, regardless of the size and content.

Resize the wrapper element and see the magic at work:

Let’s dissect the code.

Markup and layout

We’ll need a wrapper element to contain everything, and we’ll be using flexbox on it. Flexbox allows us to rely on the default stretch alignment to be able to later use height: 100%.

<div class="wrapper">
  <div class="box">
    <div class="float"><img></div>
    Lorem ipsum dolor ...
  </div>
</div>
.wrapper {
  display: flex;
}

.float {
  float: right;
  height: 100%;
  display: flex;
  align-items: flex-end;
  shape-outside: inset(calc(100% - 100px) 0 0);
}

The .box within the .wrapper is our flex item. We don’t need any particular CSS applied to the box. It defines the height of the wrapper and, at the same time, is stretched to the same height. This behavior will give us a “reference height” that can be used by the child elements.

From the specification:

If the flex item has align-self: stretch, redo layout for its contents, treating this used size as its definite cross size so that percentage-sized children can be resolved.

The keyword is the definite which allows us to safely use a percentage (%) height inside the box element.

Now for the floated element

Our .float element will take the entire height next to the text content, thanks to the height calculation we detailed above. Inside this element we push the image to the bottom using flexbox alignment.

A paragraph of lorem i-sum text with a small square image to the right of the text. The image is located at the bottom right of the text and the space above the image is empty.
The image is floated to the right but the free space above it prevents the content from wrapping around it.

Now for the real trickery, using the shape-outside property. Here’s how MDN defines it:

The shape-outside CSS property defines a shape—which may be non-rectangular—around which adjacent inline content should wrap. By default, inline content wraps around its margin box; shape-outside provides a way to customize this wrapping, making it possible to wrap text around complex objects rather than simple boxes.

In other words, shape-outside sets the way content flows around an element’s bounding box.

It takes a number of values. One of those is the inset() function which, again, according to MDN:

Defines an inset rectangle. When all of the first four arguments are supplied they represent the top, right, bottom and left offsets from the reference box inward that define the positions of the edges of the inset rectangle.

So, with shape-outside: inset(calc(100% - X) 0 0) we can create an inset rectangle that starts exactly at the top of the image. And the top is equal to 100% - X, where X is the image height and 100% is the height of the .float element. This allows the text to wrap within the free space on the top of the image. This is responsive, plus we can easily switch between left and right (by adjusting the float property)

That’s it! The only major caveat is that you need to know the image height.

Let’s Optimize The Markup

We can still optimize our code a little and remove the extra wrapper around the image.

<div class="wrapper">
  <div class="box">
    <img class="float">
    Lorem ipsum dolor ...
  </div>
</div>

Then our CSS will become

.wrapper {
  display: flex;
}

.float {
  float: right;
  height: 100%;
  width: 100px;
  shape-outside: inset(calc(100% - 100px /*height */) 0 0);
  object-fit: contain;
  object-position: bottom;
}

Since we removed the extra wrapper we need another technique to place the image at the bottom corner. For this, we are using object-fit and object-position but we have to explicitly specify the width of the image.

Using this method we need to know both the width and the height of the image unlike the previous code where only the height is needed.

Want more?

We can extend this concept a little further to account for fancier situations. For example, we can float the image to the right, but pin it to the middle of the box with justify-content: center: and also adjust our inset rectangle to the middle by changing the shape-outside from inset(calc(100% - X) 0 0) to inset(calc(50% - X/2) 0 0)

We can also float two images at both bottom corners:

Nothing complex here. I am simply using the same floating element twice, once on the right, and again on the left. And why stop at two corners when we can place images at all four corners:

The same basic idea is at play here, but we’re are also relying on the common float feature for the top images. However, you’ll notice that this is where the concept starts to break down a bit, and we get some unwanted overflow depending on the size of the containing box. We can make the height of the .float element greater than 100% and apply somewhat “magic numbers” that smooth things out by adjusting the padding and margin of the images.

Did you know that shape-outside accepts radial-gradient() as a value? We can use that to place rounded images like below:

The transparent part of the gradient is the free space where the text can go. You may have noticed that we applied a border-radius to the image as well. The shape-outside property will simply affect the .float element and we need to manually adjust the shape of the image to follow the shape defined by shape-outside.

While we’re at it, let’s combine this with our earlier example that pins the image to the vertical center of the box using justify-content: center:

Another radial-gradient() and also another border-radius configuration.

We could have used a linear-gradient() instead to make a triangular shape for the wrapping area:

This is the same idea that we used for the radial-gradient(). The big difference is that we’re using clip-path instead of border-radius to cut our image.

And, since we did it for the others, let’s use the justify-content: center idea to pin the image to the vertical center of the box’s right edge:

We used a conic-gradient() in the above demo with shape-outside to define the triangular shape and clip-path to get a similar shape on the image

All of these examples can still be optimized using less of code in the case that the image is decorative (when it’s not needed inside the HTML for SEO purposes). Let’s replace the .float element with a pseudo-element and apply the image as background instead:

We’re using mask to show just the portion of the image that we need and, guess what, it uses the same value as shape-outside! So, all we had to do is define one value for the shape.

That’s it!

There are a lot of possibilities here to place not just rectangles in corners, but any kind of shape at any position, using largely the same code structure. We only need to:

  • Adjust the shape-outside property to define the shape
  • Apply some styling to the image to follow the previously defined shape or apply the same value to mask in case we are using the pseudo element version

Then everything holds it place, even in responsive designs.


Float an Element to the Bottom Corner originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
https://css-tricks.com/float-an-element-to-the-bottom-corner/feed/ 17 338290
How to Make an Area Chart With CSS https://css-tricks.com/how-to-make-an-area-chart-with-css/ https://css-tricks.com/how-to-make-an-area-chart-with-css/#comments Wed, 02 Dec 2020 15:28:56 +0000 https://css-tricks.com/?p=326391 You might know a few ways to create charts with pure CSS. Some of them are covered here on CSS-Tricks, and many others can be found on CodePen, but I haven’t seen many examples of “area charts” (imagine …


How to Make an Area Chart With CSS originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
You might know a few ways to create charts with pure CSS. Some of them are covered here on CSS-Tricks, and many others can be found on CodePen, but I haven’t seen many examples of “area charts” (imagine a line chart with the bottom area filled in), particularly any in HTML and CSS alone. In this article, we’ll do just that, using a semantic and accessible HTML foundation.

A red area chart against a dark gray background.

Let’s start with the HTML

To simplify things, we will be using <ul> tags as wrappers and <li> elements for individual data items. You can use any other HTML tag in your project, depending on your needs.

<ul class="area-chart">
  <li> 40% </li>
  <li> 80% </li>
  <li> 60% </li>
  <li> 100% </li>
  <li> 30% </li>
</ul>

CSS can’t retrieve the inner HTML text, that is why we will be using CSS custom properties to pass data to our CSS. Each data item will have a --start and an --end custom properties.

<ul class="area-chart">
  <li style="--start: 0.1; --end: 0.4;"> 40% </li>
  <li style="--start: 0.4; --end: 0.8;"> 80% </li>
  <li style="--start: 0.8; --end: 0.6;"> 60% </li>
  <li style="--start: 0.6; --end: 1.0;"> 100% </li>
  <li style="--start: 1.0; --end: 0.3;"> 30% </li>
</ul>

Here’s what we need to consider…

There are several design principles we ought to consider before moving into styling:

  • Units data: We will be using unit-less data in our HTML (i.e. no px, em , rem , % or any other unit). The --start and --end custom properties will be numbers between 0 and 1.
  • Columns width: We won’t set a fixed width for each <li> element. We won’t be using % either, as we don’t know how many items are there. Each column width will be based on the main wrapper width, divided by the total number of data items. In our case, that’s the width of the <ul> element divided by the number of <li> elements.
  • Accessibility: The values inside each <li> is optional and only the --start and --end custom properties are required. Still, it’s best to include some sort of text or value for screen readers and other assistive technologies to describe the content.

Now, let’s start styling!

Let’s start with general layout styling first. The chart wrapper element is a flex container, displaying items in a row, stretching each child element so the entire area is filled.

.area-chart {
  /* Reset */
  margin: 0;
  padding: 0;
  border: 0;

  /* Dimensions */
  width: 100%;
  max-width: var(--chart-width, 100%);
  height: var(--chart-height, 300px);

  /* Layout */
  display: flex;
  justify-content: stretch;
  align-items: stretch;
  flex-direction: row;
}

If the area chart wrapper is a list, we should remove the list style to give us more styling flexibility.

ul.area-chart,
ol.area-chart {
  list-style: none;
}

This code styles all of the columns in the entire chart. With bar charts it’s simple: we use background-color and height for each column. With area charts we are going to use the clip-path property to set the region that should be shown.

First we set up each column:

.area-chart > * {
  /* Even size items */
  flex-grow: 1;
  flex-shrink: 1;
  flex-basis: 0;

  /* Color */
  background: var(--color, rgba(240, 50, 50, .75));
}

To create a rectangle covering the entire area, we will reach for the clip-path property and use its polygon() function containing the coordinates of the area. This basically doesn’t do anything at the moment because the polygon covers everything:

.area-chart > * {
  clip-path: polygon(
    0% 0%,     /* top left */
    100% 0%,   /* top right */
    100% 100%, /* bottom right */
    0% 100%    /* bottom left */
  );
}

Now for the best part!

To show just part of the column, we clip it to create that area chart-like effect. To show just the area we want, we use the --start and --end custom properties inside the clip-path polygon:

.area-chart > * {
  clip-path: polygon(
    0% calc(100% * (1 - var(--start))),
    100% calc(100% * (1 - var(--end))),
    100% 100%,
    0% 100%
  );
}

Seriously, this one bit of CSS does all of the work. Here’s what we get:

Working with multiple datasets

Now that we know the basics, let’s create an area chart with multiple datasets. Area charts often measure more than one set of data and the effect is a layered comparison of the data.

This kind of chart requires several child elements, so we are going to replace our <ul> approach with a <table>.

<table class="area-chart">
  <tbody>
    <tr>
      <td> 40% </td>
      <td> 80% </td>
    </tr>
    <tr>
      <td> 60% </td>
      <td> 100% </td>
    </tr>
  </tbody>
</table>

Tables are accessible and search engine friendly. And if the stylesheet doesn’t load for some reason, all the data is still visible in the markup.

Again, we will use the --start and --end custom properties with numbers between 0 and 1.

<table class="area-chart">
  <tbody>
    <tr>
      <td style="--start: 0; --end: 0.4;"> 40% </td>
      <td style="--start: 0; --end: 0.8;"> 80% </td>
    </tr>
    <tr>
      <td style="--start: 0.4; --end: 0.6;"> 60% </td>
      <td style="--start: 0.8; --end: 1.0;"> 100% </td>
    </tr>
  </tbody>
</table>

So, first we will style the general layout for the wrapping element, our table, which we’ve given an .area-chart class:

.area-chart {
  /* Reset */
  margin: 0;
  padding: 0;
  border: 0;

  /* Dimensions */
  width: 100%;
  max-width: var(--chart-width, 600px);
  height: var(--chart-height, 300px);
}

Next, we will make the <tbody> element a flex container, displaying the <tr> items in a row and evenly sized:

.area-chart tbody {
  width: 100%;
  height: var(--chart-height, 300px);

  /* Layout */
  display: flex;
  justify-content: stretch;
  align-items: stretch;
  flex-direction: row;
}
.area-chart tr {
  /* Even size items */
  flex-grow: 1;
  flex-shrink: 1;
  flex-basis: 0;
}

Now we need to make the <td> elements cover each other, one element on top of each other so we get that layered effect. Each <td> covers the entire area of the <tr> element that contains it.

.area-chart tr {
  position: relative;
}
.area-chart td {
  position: absolute;
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
}

Let’s put the magical powers of clip-path: polygon() to use! We’re only displaying the area between the --start and --end custom properties which, again, are values between 0 and 1:

.area-chart td {
  clip-path: polygon(
    0% calc(100% * (1 - var(--start))),
    100% calc(100% * (1 - var(--end))),
    100% 100%,
    0% 100%
  );
}

Now let’s add color to each one:

.area-chart td {
  background: var(--color);
}
.area-chart td:nth-of-type(1) {
  --color: rgba(240, 50, 50, 0.75);
}
.area-chart td:nth-of-type(2) {
  --color: rgba(255, 180, 50, 0.75);
}
.area-chart td:nth-of-type(3) {
  --color: rgba(255, 220, 90, 0.75);
}

It’s important to use colors with opacity to get a nicer effect, which is why we’re using rgba() values. You could use hsla() here instead, if that’s how you roll.

And, just like that:

Wrapping up

It doesn’t matter how many HTML elements we add to our chart, the flex-based layout makes sure all the items are equally sized. This way, we only need to set the width of the wrapping chart element and the items will adjust accordingly for a responsive layout.

We have covered one technique to create area charts using pure CSS. For advanced use cases, you can check out my new open source data visualization framework, ChartsCSS.org. See the Area Chart section to see how area charts can be customized with things like different orientations, axes, and even a reversed order without changing the HTML markup, and much more!


How to Make an Area Chart With CSS originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
https://css-tricks.com/how-to-make-an-area-chart-with-css/feed/ 7 326391
Understanding flex-grow, flex-shrink, and flex-basis https://css-tricks.com/understanding-flex-grow-flex-shrink-and-flex-basis/ https://css-tricks.com/understanding-flex-grow-flex-shrink-and-flex-basis/#comments Tue, 10 Nov 2020 23:47:52 +0000 https://css-tricks.com/?p=325027 When you apply a CSS property to an element, there’s lots of things going on under the hood. For example, let’s say we have some HTML like this:

<div class="parent"<div class="child"Child</div<div class="child"Child</div<div class="child"Child</div</div


Understanding flex-grow, flex-shrink, and flex-basis originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
When you apply a CSS property to an element, there’s lots of things going on under the hood. For example, let’s say we have some HTML like this:

<div class="parent">
  <div class="child">Child</div>
  <div class="child">Child</div>
  <div class="child">Child</div>
</div>

And then we write some CSS…

.parent {
  display: flex;
}

These are technically not the only styles we’re applying when we write that one line of CSS above. In fact, a whole bunch of properties will be applied to the .child elements here, as if we wrote these styles ourselves:

.child {
  flex: 0 1 auto; /* Default flex value */
}

That’s weird! Why do these elements have these extra styles applied to them even though we didn’t write that code? Well, that’s because some properties have defaults that are then intended to be overridden by us. And if we don’t happen to know these styles are being applied when we’re writing CSS, then our layouts can get pretty darn confusing and tough to manage.

That flex property above is what’s known as a shorthand CSS property. And really what this is doing is setting three separate CSS properties at the same time. So what we wrote above is the same as writing this:

.child {
  flex-grow: 0;
  flex-shrink: 1;
  flex-basis: auto;
}

So, a shorthand property bundles up a bunch of different CSS properties to make it easier to write multiple properties at once, precisely like the background property where we can write something like this:

body {
  background: url(sweettexture.jpg) top center no-repeat fixed padding-box content-box red;                   
}

I try to avoid shorthand properties because they can get pretty confusing and I often tend to write the long hand versions just because my brain fails to parse long lines of property values. But it’s recommended to use the shorthand when it comes to flexbox, which is…weird… that is, until you understand that the flex property is doing a lot of work and each of its sub-properties interact with the others.

Also, the default styles are a good thing because we don’t need to know what these flexbox properties are doing 90% of the time. For example, when I use flexbox, I tend to write something like this:

.parent {
  display: flex;
  justify-content: space-between;
}

I don’t even need to care about the child elements or what styles have been applied to them, and that’s great! In this case, we’re aligning the child items side-by-side and then spacing them equally between each other. Two lines of CSS gives you a lot of power here and that’s the neatest thing about flexbox and these inherited styles — you don’t have to understand all the complexity under the hood if you just want to do the same thing 90% of the time. It’s remarkably smart because all of that complexity is hidden out of view.

But what if we want to understand how flexbox — including the flex-grow, flex-shrink, and flex-basis properties — actually work? And what cool things can we do with them?

Just go to the CSS-Tricks Almanac. Done!

Just kidding. Let’s start with a quick overview that’s a little bit simplified, and return to the default flex properties that are applied to child elements:

.child {
  flex: 0 1 auto;
}

These default styles are telling that child element how to stretch and expand. But whenever I see it being used or overridden, I find it helpful to think of these shorthand properties like this:

/* This is just how I think about the rule above in my head */

.child {
  flex: [flex-grow] [flex-shrink] [flex-basis];
}

/* or... */

.child {
  flex: [max] [min] [ideal size];
}

That first value is flex-grow and it’s set to 0 because, by default, we don’t want our elements to expand at all (most of the time). Instead, we want every element to be dependent on the size of the content within it. Here’s an example:

.parent { 
  display: flex; 
}

I’ve added the contenteditable property to each .child element above so you can click into it and type even more content. See how it responds? That’s the default behavior of a flexbox item: flex-grow is set to 0 because we want the element to grow based on the content inside it.

But! If we were to change the default of the flex-grow property from 0 to 1, like this…

.child {
  flex: 1 1 auto;
}

Then all the elements will take an equal portion of the .parent element, but only if the lengths of their contents are the same.

This is exactly the same as writing…

.child {
  flex-grow: 1;
}

…and ignoring the other values because those have been set by default anyway. I think this confused me for such a long time when I started working with flexible layouts. I would see code that would add just flex-grow and wonder where the other styles are coming from. It was like an infuriating murder mystery that I just couldn’t figure out.

Now, if we wanted to make just one of these elements grow more than the others we’d just need to do the following:

.child-three {
  flex: 3 1 auto;
}

/* or we could just write... */

.child-three {
  flex-grow: 3;
}

Is this weird code to look at even a decade after flexbox landed in browsers? It certainly is for me. I need extra brain power to say, “Ah, max, min, ideal size,” when I’m reading the shorthand, but it does get easier over time. Anyway, in the example above, the first two child elements will take up proportionally the same amount of space but that third element will try to grow up to three times the space as the others.

Now this is where things get weird because this is all dependent on the content of the child elements. Even if we set flex-grow to 3, like we did in the example above and then add more content, the layout will do something odd and peculiar like this:

That second column is now taking up too much darn space! We’ll come back to this later, but for now, it’s just important to remember that the content of a flex item has an impact on how flex-grow, flex-shrink, and flex-basis work together.

OK so now for flex-shrink. Remember that’s the second value in the shorthand:

.child {
  flex: 0 1 auto; /* flex-shrink = 1 */
}

flex-shrink tells the browser what the minimum size of an element should be. The default value is 1, which is saying, “Take up the same amount of space at all times.” However! If we were to set that value to 0 like this:

.child {
  flex: 0 0 auto;
}

…then we’re telling this element not to shrink at all now. Stay the same size, you blasted element! is essentially what this CSS says, and that’s precisely what it’ll do. We’ll come back to this property in a bit once we look at the final value in this shorthand.

flex-basis is the last value that’s added by default in the flex shorthand, and it’s how we tell an element to stick to an ideal size. By default, it’s set to auto which means, “Use my height or width.” So, when we set a parent element to display: flex

.parent {
  display: flex;
}

.child {
  flex: 0 1 auto;
}

We’ll get this by default in the browser:

Notice how all the elements are the width of their content by default? That’s because auto is saying that the ideal size of our element is defined by its content. To make all the elements take up the full space of the parent we can set the child elements to width: 100%, or we can set the flex-basis to 100%, or we can set flex-grow to 1.

Does that make sense? It’s weird, huh! It does when you think about it. Each of these shorthand values impact the other and that’s why it is recommended to write this shorthand in the first place rather than setting these values independently of one another.

OK, moving on. When we write something like this…

.child-three {
  flex: 0 1 1000px;
}

What we’re telling the browser here is to set the flex-basis to 1000px or, “please, please, please just try and take up 1000px of space.” If that’s not possible, then the element will take up that much space proportionally to the other elements.

You might notice that on smaller screens this third element is not actually a 1000px! That’s because it’s really a suggestion. We still have flex-shrink applied which is telling the element to shrink to the same size as the other elements.

Also, adding more content to the other children will still have an impact here:

Now, if we wanted to prevent this element from shrinking at all we could write something like this:

.child-three {
  flex: 0 0 1000px;
}

Remember, flex-shrink is the second value here and by setting it to 0 we’re saying, “Don’t shrink ever, you jerk.” And so it won’t. The element will even break out of the parent element because it’ll never get shorter than 1000px wide:

Now all of this changes if we set flex-wrap to the parent element:

.parent {
  display: flex;
  flex-wrap: wrap;
}

.child-three {
  flex: 0 0 1000px;
}

We’ll see something like this:

This is because, by default, flex items will try to fit into one line but flex-wrap: wrap will ignore that entirely. Now, if those flex items can’t fit in the same space, they’ll break onto a new line.


Anyway, this is just some of the ways in which flex properties bump into each other and why it’s so gosh darn valuable to understand how these properties work under the hood. Each of these properties can affect the other, and if you don’t understand how one property works, then you sort of don’t understand how any of it works at all — which certainly confused me before I started digging into this!

But to summarize:

  • Try to use the flex shorthand
  • Remember max, min and ideal size when doing so
  • Remember that the content of an element can impact how these values work together, too.

Understanding flex-grow, flex-shrink, and flex-basis originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
https://css-tricks.com/understanding-flex-grow-flex-shrink-and-flex-basis/feed/ 32 325027
#196: Learning Grid & Flexbox with Kyle Simpson https://css-tricks.com/videos/196-learning-grid-flexbox-with-kyle-simpson/ Fri, 30 Oct 2020 18:58:54 +0000 https://css-tricks.com/?page_id=324816 Kyle is a JavaScript guy. He knows a ton about the web, and plenty about CSS too, but isn’t as up-to-date on modern CSS layout tools like flexbox and grid. We didn’t go from 0 to 100 here, but …


#196: Learning Grid & Flexbox with Kyle Simpson originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
Kyle is a JavaScript guy. He knows a ton about the web, and plenty about CSS too, but isn’t as up-to-date on modern CSS layout tools like flexbox and grid. We didn’t go from 0 to 100 here, but we did have a great chat about modern layout while live coding and we chatted through layout situations.

Along the way, we let the VS Code extension TabNine see what it could do to help us write CSS.


#196: Learning Grid & Flexbox with Kyle Simpson originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
324816
Balancing on a Pivot with Flexbox https://css-tricks.com/balancing-on-a-pivot-with-flexbox/ https://css-tricks.com/balancing-on-a-pivot-with-flexbox/#comments Thu, 08 Oct 2020 14:44:22 +0000 https://css-tricks.com/?p=322369 Let me show you a way I recently discovered to center a bunch of elements around what I call the pivot. I promise you that funky HTML is out of the question and you won’t need to know any …


Balancing on a Pivot with Flexbox originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
Let me show you a way I recently discovered to center a bunch of elements around what I call the pivot. I promise you that funky HTML is out of the question and you won’t need to know any bleeding-edge CSS to get the job done.

I’m big on word games, so I recently re-imagined the main menu of my website as a nod to crossword puzzles, with my name as the vertical word, and the main sections of my website across the horizontals.

Here’s how the design looks with the names of some colors instead:

And here’s a sample of the HTML that drives this puzzle:

<div class="puzzle">
  <div class="word">
    <span class="letter">i</span>
    <span class="letter">n</span>
    <span class="letter">d</span>
    <span class="letter">i</span>
    <span class="letter pivot">g</span>
    <span class="letter">o</span>
  </div>
  <!-- MORE WORDS -->
</div>

In this example, the letter g is the pivot. See how it’s not at the halfway mark? That’s the beauty of this challenge.

We could apply an offset to each word using hard-coded CSS or inline custom properties and walk away. It certainly gets an award for being the most obvious way to solve the problem, but there’s a downside — in addition to the .pivot class, we’d have to specify an offset for every word. The voice in my head tells me that’s adding unnecessary redundancy, is less flexible, and requires extra baggage we don’t need every time we add or change a word.

Let’s take a step back instead and see how the puzzle looks without any balancing:

Imagine for a moment that we use display: none to hide all of the letters before the pivot; now all we can see are the pivots and everything after them:

With no further changes, our pivots would already be aligned. But we’ve lost the start of our words, and when we reintroduce the hidden parts, each word gets pushed out to the right and everything is out of whack again.

If we were to hide the trailing letters instead, we’d still be left with misaligned pivots:

All of this back-and-forth seems a bit pointless, but it reveals a symmetry to my problem. If we were to use a right-to-left (RTL) reading scheme, we’d have the opposite problem — we’d be able to solve the right side but the left would be all wrong.

Wouldn’t it be great if there was a way to have both sides line up at the same time?

As a matter of fact, there is.

Given we already have half a solution, let’s borrow a concept from algorithmics called divide and conquer. The general idea is that we can break a problem down into parts, and that by finding a solution for the parts, we’ll find a solution for the whole.

In that case, let’s break our problem down into the positioning of two parts. First is the “head” or everything before the pivot.

Next is the “tail” which is the pivot plus everything after it.

The flex display type will help us here; if you’re not familiar with it, flex is a framework for positioning elements in one-dimension. The trick here is to take advantage of the left and right ends of our container to enforce alignment. To make it work, we’ll swap the head and tail parts by using a smaller order  property value on the tail than the head. The order property is used by flex to determine the sequence of elements in a flexible layout. Smaller numbers are placed earlier in the flow.

To distinguish the head and tail elements without any extra HTML, we can apply styles to the head part to all of the letters, after which we’ll make use of the cascading nature of CSS to override the pivot and everything after it using the subsequent-sibling selector .pivot ~ .letter.

Here’s how things look now:

Okay, so now the head is sitting flush up against the end of the tail. Hang on, don’t go kicking up a stink about it! We can fix this by applying margin: auto to the right of the last element in the tail. That just so happens to also be the last letter in the word which is now sitting somewhere in the middle. The addition of an auto margin serves to push the head away from the tail and all the way over to the right-hand side of our container.

Now we have something that looks like this:

The only thing left is stitch our pieces back together in the right order. This is easy enough to do if we apply position: relative to all of our letters and then chuck a left: 50% on the tail and a right: 50% on our head items.

Here’s a generalized version of the code we just used. As you can see, it’s just 15 lines of simple CSS:

.container {
  display: flex;
}
.item:last-child {
  margin-right: auto;
}
.item {
  order: 2;
  position: relative;
  right: 50%;
}
.pivot, .pivot ~ .item {
  order: 1;
  left: 50%;
}

It’s also feasible to use this approach for vertical layouts by setting the flex-direction to a column value. It should also be said that the same can be achieved by sticking the head and tail elements in their own wrappers — but that would require more markup and verbose CSS while being a lot less flexible. What if, for example, our back-end is already generating an unwrapped list of elements with dynamically generated classes?

Quite serendipitously, this solution also plays well with screen readers. Although we’re ordering the two sections backwards, we’re then shifting them back into place via relative positioning, so the final ordering of elements matches our markup, albeit nicely centered.

Screen readers preserve the element ordering as per the original markup.

Here’s the final example on CodePen:

Conclusion

Developers are better at balancing than acrobats. Don’t believe me? Think about it: many of the common challenges we face require finding a sweet spot between competing requirements ranging from performance and readability, to style and function, and even scalability and simplicity. A balancing act, no doubt.

But the point at which we find balance isn’t always midway between one thing and another. Balance is often found at some inexplicable point in between; or, as we’ve just seen, around an arbitrary HTML element.

So there you have it! Go and tell your friends that you’re the greatest acrobat around.


Balancing on a Pivot with Flexbox originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
https://css-tricks.com/balancing-on-a-pivot-with-flexbox/feed/ 18 322369
Achieving Vertical Alignment (Thanks, Subgrid!) https://css-tricks.com/achieving-vertical-alignment-thanks-subgrid/ https://css-tricks.com/achieving-vertical-alignment-thanks-subgrid/#comments Wed, 30 Sep 2020 14:46:39 +0000 https://css-tricks.com/?p=321721 Our tools for vertical alignment have gotten a lot better as of late. My early days as a website designer involved laying out 960px wide homepage designs and aligning things horizontally across a page using a 12-column grid. Media queries …


Achieving Vertical Alignment (Thanks, Subgrid!) originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
Our tools for vertical alignment have gotten a lot better as of late. My early days as a website designer involved laying out 960px wide homepage designs and aligning things horizontally across a page using a 12-column grid. Media queries came along which required a serious mental shift. It solved some big problems, of course, but introduced new ones, like dealing with alignment when elements wrap or are otherwise moved around in the layout. 

Let’s take a look at just one particular scenario: a “bar” with some buttons in it. There are two groups of these buttons, each contained within a <fieldset> with a <legend>

On a large screen, we’re all set:

Two groups of two bright pink buttons set on a dark background.

And here’s a very basic CSS method that accomplishes that layout, and also breaks down onto two “rows” at a mobile breakpoint:

.accessibility-tools fieldset {
  width: 48%;
  float: left;
  margin-right: 1%;
}


/* Mobile */
@media only screen and (max-width: 480px) {
  .accessibility-tools fieldset {
    width: 100%;
  }
}

On a small screen, we end up with this:

The same two groups of two pink buttons, with one group of buttons stacked on top of the other, showing the buttons are uneven in width.

This is the problem: lack of vertical alignment. Let’s say we want to align those buttons into a more pleasing arrangement where the button edges align with each other nicely.

To begin, we could go for fixed-width, pixel-based CSS solutions to force elements to line up nicely at various breakpoints, using magic numbers like this:

/* Mobile */
@media only screen and (max-width: 480px) {
  legend {
    width: 160px;
  }
  button {
    width: 130px;
  }
}
the same two groups of pink buttons but where the buttons all have the same consistent width and line up evenly.

That does the trick.

But… this is not exactly a flexible solution to the problem. Aside from the magic numbers (fixed-pixel values based on specific content), it also relied on the use of media queries which I am trying to move away from when I can. I discussed this in a post called “Stepping away from Sass” on my blog.

As I moved towards some of the more modern features of CSS the need to target specific screen sizes with unique code was removed.

What I need is each button and label to respond to:

  1. the space available
  2. their content 

and!

  1. other elements around them.

Available space

The problem with using media queries is that they don’t take into account the space around the elements that are being realigned — a point perfectly demonstrated in this image from “The Flexbox holy albatross” by Heydon Pickering:

Three browsers side-by-side. The first shows three black rectangles in a single row, the second shows three black rectangles stacked vertically, and the third shows three black rectangles set to the right hand of the screen.

What I really want is for the second <fieldset> to wrap under the first only when they can no longer fit neatly on one row.

Can we get this done with flexbox?

A key selling point for flexbox is its ability to create elements that respond to the space around them. Components can “flex” to fill additional space and shrink to fit into smaller spaces. 

For this situation, the flex-wrap property is set to wrap. This means as soon as both <fieldset> elements no longer fit on one line, they will wrap onto a second line.

.wrapper--accessibility-tools {
  display: flex;
  flex-wrap: wrap;
} 

The flex-wrap property has three available values. The default value is nowrap, leaving items on one line. The wrap value allows elements to flow onto multiple lines. Then there’s wrap-reverse, which allows items to wrap but — wait for it — in reverse (it is weird to see: when elements wrap, they go above the previous row in left-to-right situations).

Using flexbox stops the layout from being quite as rigid, but a min-width value is still needed to remove the vertical alignment problem. So: close but no cigar.

Can grid help us?

CSS Grid is the very first CSS module created specifically to solve the ongoing layout problems faced by web designers and developers. It is not a direct replacement for flexbox; rather the two modules usually work pretty well together.

Like flexbox, grid can be used to allow each <fieldset> to occupy as much or as little space as they need. Getting right to it, we can leverage the auto-fill and auto-fit keywords (within a repeat() function) to allow grid items to flow onto multiple lines without the need for media queries. The difference is a bit subtle, but well-explained in “Auto-Sizing Columns in CSS Grid: auto-fill vs auto-fit” by Sara Soueidan. Let’s use auto-fit:

.wrapper--accessibility-tools {
  display: grid;
  grid-template-columns: repeat(auto-fit, 450px);
  grid-gap: 10px;
}

Like the flexbox example, I still need to set an absolute value for the width of the label to align the <fieldset> elements as they stack.

Another approach with grid

CSS Grid also allows elements to respond based on their content using flexible grid tracks. In addition to other length values like percentages, relative units, or pixels, CSS Grid accepts a Fractional Unit (fr), where 1fr will take up one part of the available space, 2fr will take up two parts of the available space, and so on. Let’s set up two equal columns here:

.wrapper--accessibility-tools {
  display: grid;
  grid-template-columns: 1fr 1fr;
  grid-gap: 10px;
}

There’s also a minmax() function which creates grid tracks that flex to the available space, but also don’t shrink narrower than a specified size.

.wrapper--accessibility-tools {
  display: grid;
  grid-template-columns: minmax(auto, max-content) minmax(auto, max-content);
  grid-gap: 10px;
}

Both of these demos work, and are free from any absolute values or device specific CSS. The results are far from ideal though, each grid now responds at different points. Maybe not a huge problem, but certainly not great.

This happens because when adding display: grid to a container, only the direct children of that container become grid items. This means the intrinsic sizing units we used only relate to elements in the same grid. 

Using subgrid

To really achieve my goal, I need the buttons and labels to react to elements in sibling grid containers. CSS Grid Level 2 includes the subgrid feature. Although we have always been able to nest grids, the elements within each grid container have been independent. With subgrid, we get to set up nested (child) grids that use parent grids tracks.

This makes a number patterns that were previously difficult much easier, in particular the “card” pattern which seems to be the most popular example to show the benefits of subgrid. Without subgrid, each card is defined as an independent grid, meaning track sizing in the first card cannot respond to a change of height in the second. Pulling from an example Rachel Andrew used, here’s a simple group of cards:

Showing three columns of cards where each card has a headers with a dark blue background and white text, content with a white background, and a footer with a dark blue background. The cards are uneven because some of them have more content that others.
Credit: Rachel Andrew

Subgrid allows the cards to use the rows defined in the parent grid, meaning they can react to content in surrounding cards.

The same three columns of cards, but with each card perfectly aligned with the others.
Credit: Rachel Andrew

Each card in this example still spans three row tracks, but those rows are now defined on the parent grid, allowing each card to occupy the same amount of vertical space.

For the example we’ve been working with, we do not need to use rows. Instead, we need to size columns based on content from sibling grids. First, let’s set the parent grid to contain the two <fieldset> elements. This is similar to the code we previously look at in the auto-fit demo.

.wrapper--accessibility-tools {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
  grid-gap: 10px;
}

Then we position each subgrid onto the parent grid.

.sub-grid {
  display: grid;
  grid-column: span 3;
  grid-template-columns: subgrid;
  align-items: center;
}
Showing the two groups of bright pink buttons with both groups on the same row and all the buttons have a consistent width.

All of the labels and buttons are now aligned to the tracks of their parent grid, keeping them consistent. They will each have an equal width based on the space that is available. If there is not enough space for each nested grid on one line, the second will wrap onto a new line.

The two groups of buttons vertically stacked and the buttons are all consistently aligned with the same width.

This time, the two nested grid items align perfectly. The grid is also flexible if we introduce a longer title on a one of the buttons, the other elements will respond accordingly.

Browser compatibility

Support for subgrid is not great at the time of writing. It is only supported in Firefox 71+, although there are positive signals from other browsers. CSS feature queries can be used to provide alternative styling to Chrome and Edge.

This browser support data is from Caniuse, which has more detail. A number indicates that browser supports the feature at that version and up.

Desktop

ChromeFirefoxIEEdgeSafari
11771No11716.0

Mobile / Tablet

Android ChromeAndroid FirefoxAndroidiOS Safari
12612712616.0

Note that I am using an extra wrapper around the fieldsets in these demos. This is to combat a bug with form elements and grid and flexbox.

<fieldset class="accessibility-tools__colour-theme">
  <div class="wrapper"></div>
</fieldset>

The layout CSS is applied to the wrapper with the fieldset being set to display: contents.

.accessibility-tools fieldset {
  display: contents;
  border: 0;
} 

Other writing on the subject


Achieving Vertical Alignment (Thanks, Subgrid!) originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
https://css-tricks.com/achieving-vertical-alignment-thanks-subgrid/feed/ 2 321721
Holy Albatross with Widths https://css-tricks.com/holy-albatross-with-widths/ Fri, 24 Jul 2020 14:07:09 +0000 https://css-tricks.com/?p=317139 Heydon’s Holy Albatross is a technique to have a row of elements break into a column of elements at a specific width. A specified parent width, not a screen width like a media query would have. So, like a container …


Holy Albatross with Widths originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
Heydon’s Holy Albatross is a technique to have a row of elements break into a column of elements at a specific width. A specified parent width, not a screen width like a media query would have. So, like a container query (ya know, those things that don’t exist yet that we all want).

I’ve used it before, although it was pointed out to me that using it only on two elements isn’t really necessary, and that the Holy Albatross is most useful when working with three or more elements.

The original article kind didn’t get into setting the widths on the horizontal row of elements (say one of them needs to be larger than the others), but the follow-up article has a demo in it showing that flex-grow can be used to do exactly that. But Xiao Zhuo Jia notes that it’s not exactly a great system:

Problem is, it’s very difficult to set width to your non-stacked column, because the width is taken over by the hack.

One suggestion by Heydon is to use flex-grow, the problems are:

1. It is a very unintuitive way to set width – for a 3 column layout, you want 1 column to be 50% wide, you have to set flex-grow to 2.333
2. You have to know the number of total columns and set other columns’ flex-grow value accordingly

The other method is to use min-width and max-width, as shown by this codepen. I don’t believe max-width: 100% is needed as anything larger than 100% will be changed to 100% due to flex-shrink, so really we’re dealing with min-width.

The problem with this method is that we have to set min-width for all columns, or else flex-grow will take over and expand the column beyond the min-width we’ve set.

None of this is fun.

I poked around a bit and I found that you can have your cake and eat it too…

Xiao Zhuo Jia calls the Unholy Albatross. Check out the How does it work? part of the article to see the great CSS trickery. It has to do with using the max() function and CSS custom properties with fullbacks. It still feels very in the spirit of the Holy Albatross and allows you to set the width (via --width) on any given element with a pixel value or ratio. Plus, it supports gaps.

To Shared LinkPermalink on CSS-Tricks


Holy Albatross with Widths originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
317139
Grid for layout, flexbox for components https://css-tricks.com/grid-for-layout-flexbox-for-components/ https://css-tricks.com/grid-for-layout-flexbox-for-components/#comments Fri, 26 Jun 2020 02:24:12 +0000 https://css-tricks.com/?p=314446 When should we reach for CSS grid and when should we use flexbox? Rachel Andrew wrote about this very conundrum way back in 2016:

Flexbox is essentially for laying out items in a single dimension – in a row OR


Grid for layout, flexbox for components originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
When should we reach for CSS grid and when should we use flexbox? Rachel Andrew wrote about this very conundrum way back in 2016:

Flexbox is essentially for laying out items in a single dimension – in a row OR a column. Grid is for layout of items in two dimensions – rows AND columns.

Ahmad Shadeed wrote a post where he gives the same advice, but from a different angle. He argues we should use grid for layout and flexbox for components:

Remember that old layout method might be perfect for the job. Overusing flexbox or grid can increase the complexity of your CSS by time. I don’t mean they are complex, but using them correctly and in the right context as explained from the examples in this article is much better.

Speaking of which, there’s so many great layout examples in this post, too.

To Shared LinkPermalink on CSS-Tricks


Grid for layout, flexbox for components originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
https://css-tricks.com/grid-for-layout-flexbox-for-components/feed/ 1 314446
Chromium lands Flexbox gap https://css-tricks.com/chromium-lands-flexbox-gap/ Fri, 08 May 2020 23:22:40 +0000 https://css-tricks.com/?p=310085 I mentioned this the other day via Michelle Barker’s coverage, but here I’ll link to the official announcement. The main thing is that we’ll be getting gap with flexbox, which means:

.flex-parent {
  display: flex;
  gap: 1rem;
}
.flex-child 


Chromium lands Flexbox gap originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
I mentioned this the other day via Michelle Barker’s coverage, but here I’ll link to the official announcement. The main thing is that we’ll be getting gap with flexbox, which means:

.flex-parent {
  display: flex;
  gap: 1rem;
}
.flex-child {
  flex: 1;
}

That’s excellent, as putting space in between flex items has been tough in the past. We have justify-content: space-between, which is nice sometimes, but that doesn’t allow you to explicitly tell the flex container how much space you want. For that, we’d typically use margin, but that means avoiding setting the margin on the first or last element depending on the direction of the margin — which is annoying gets complicated.

We have gap in CSS Grid and it’s way better. It’s great to have it in flexbox.

But it’s going to get weird for a minute. Safari doesn’t support it yet (nor stable Chrome) so we can’t just plop it out there and expect it to work with flexbox. But shouldn’t we be able to do an @supports query these days?

/* Nope, sorry. This "works" but it doesn't 
   actually tell you if it works in *flexbox* or not.
   This works in grid in most browsers now, so it will pass. */
@supports (gap: 1rem) {
  .flex-parent {
     gap: 1rem;
  }
}

That got weird because grid-gap was dropped in favor of just gap. I’m sure grid-gap will be supported forever because that’s generally how these things go, but we’re encouraged to use gap instead. So, you might say gap is a little overloaded, but that should shake out over time (a year?). It’s complicated a smidge more by the fact that column-gap is now going to gap as well. gap has a whole bunch of jobs.

I’d say I’m still in favor of the change, despite the overloading. Simpler mental modals are important for the long-term, and there isn’t exactly anything coming up to challenge CSS for styling in the browser. I’d bet my 2-year old daughter writes some CSS in her lifetime.

To Shared LinkPermalink on CSS-Tricks


Chromium lands Flexbox gap originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
310085
Exciting Things on the Horizon For CSS Layout https://css-tricks.com/exciting-things-on-the-horizon-for-css-layout/ Fri, 08 May 2020 00:51:37 +0000 https://css-tricks.com/?p=308403 Michelle Barker notes that it’s been a heck of a week for us CSS layout nerds.

  1. Firefox has long had the best DevTools for CSS Grid, but Chrome is about to catch up and go one bit better by visualizing


Exciting Things on the Horizon For CSS Layout originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
Michelle Barker notes that it’s been a heck of a week for us CSS layout nerds.

  1. Firefox has long had the best DevTools for CSS Grid, but Chrome is about to catch up and go one bit better by visualizing grid line numbers and names.
  2. Firefox supports gap for display: flex, which is great, and now Chrome is getting that too.
  3. Firefox is trying out an idea for masonry layout.

To Shared LinkPermalink on CSS-Tricks


Exciting Things on the Horizon For CSS Layout originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
308403
Flexbox and absolute positioning https://css-tricks.com/flexbox-and-absolute-positioning/ https://css-tricks.com/flexbox-and-absolute-positioning/#comments Wed, 18 Mar 2020 14:13:38 +0000 https://css-tricks.com/?p=305346 Chen Hui Jing notes that when you absolutely position a flex item, it’s no longer part of the flex layout. Except… it kinda is a little bit. If you make the child position: absolute; but don’t apply any top/right/bottom/left properties, …


Flexbox and absolute positioning originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
Chen Hui Jing notes that when you absolutely position a flex item, it’s no longer part of the flex layout. Except… it kinda is a little bit. If you make the child position: absolute; but don’t apply any top/right/bottom/left properties, then flexbox alignment will still apply to it.

It’s odd to see, but it makes a certain sort of sense as well. When you apply position: absolute; to things (and nothing else), they kinda just stay where they are until you apply other positioning. Check out how this SVG icon just sits in the middle of this paragraph, and even flows with it on resize, because it doesn’t have any specific positioning instructions other than to not affect anything else.

To Shared LinkPermalink on CSS-Tricks


Flexbox and absolute positioning originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
https://css-tricks.com/flexbox-and-absolute-positioning/feed/ 4 305346
How Auto Margins Work in Flexbox https://css-tricks.com/how-auto-margins-work-in-flexbox/ https://css-tricks.com/how-auto-margins-work-in-flexbox/#comments Mon, 06 Jan 2020 22:56:11 +0000 https://css-tricks.com/?p=300845 Robin has covered this before, but I’ve heard some confusion about it in the past few weeks and saw another person take a stab at explaining it, and I wanted to join the party.

Say you have a …


How Auto Margins Work in Flexbox originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
Robin has covered this before, but I’ve heard some confusion about it in the past few weeks and saw another person take a stab at explaining it, and I wanted to join the party.

Say you have a flex container with some flex items inside that don’t fill the whole area.

See the Pen
ZEYLVEX
by Chris Coyier (@chriscoyier)
on CodePen.

Now I want to push that “Menu” item to the far right. That’s where auto margins come in. If I put a margin-left: auto; on it, it’ll push as far away as it possibly can on that row.

See the Pen
WNbRLbG
by Chris Coyier (@chriscoyier)
on CodePen.

Actually, you might consider margin-inline-start: auto; instead and start using logical properties everywhere so that you’re all set should you need to change direction.

See the Pen
gObgZpb
by Chris Coyier (@chriscoyier)
on CodePen.

Also, note that auto margins work in both directions as long as there is room to push. In this example, it’s not alignment that is moving the menu down, it’s an auto margin.

See the Pen
XWJpobE
by Chris Coyier (@chriscoyier)
on CodePen.


How Auto Margins Work in Flexbox originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
https://css-tricks.com/how-auto-margins-work-in-flexbox/feed/ 7 300845