A Tough Mixture — Smashing Journal

0
12
A Tricky Combination — Smashing Magazine


I used to be not too long ago requested by a pupil to assist with a seemingly easy downside. She’d been engaged on an internet site for a espresso store that sports activities a sticky header, and he or she needed the hero part proper beneath that header to span the remainder of the accessible vertical area within the viewport.

Right here’s a visible demo of the specified impact for readability.

Seems to be prefer it must be simple sufficient, proper? I used to be certain (learn: overconfident) that the issue would solely take a few minutes to unravel, solely to seek out it was a a lot deeper properly than I’d assumed.

Earlier than we dive in, let’s take a fast take a look at the preliminary markup and CSS to see what we’re working with:


  
  
Hero Content material
Important Content material
.header {
  place: sticky;
  high: 0; /* Offset, in any other case it will not stick! */
}

/* and so on. */

With these declarations, the .header will keep on with the highest of the web page. And but the .hero factor under it stays intrinsically sized. That is what we wish to change.

The Low-Hanging Fruit

The primary impulse you might need, as I did, is to surround the header and hero in some form of dad or mum container and provides that container 100vh to make it span the viewport. After that, we might use Flexbox to distribute the kids and make the hero develop to fill the remaining area.


  

Hero Content material

Important Content material
.container {
  peak: 100vh;
  show: flex;
  flex-direction: column;
}

.hero {
  flex-grow: 1;
}

/* and so on. */

This appears to be like right at first look, however watch what occurs when scrolling previous the hero.

See the Pen [Attempt #1: Container + Flexbox [forked]](https://codepen.io/smashingmag/pen/yLdQgQo) by Philip.

See the Pen Try #1: Container + Flexbox [forked] by Philip.

The sticky header will get trapped in its dad or mum container! However.. why?

In case you’re something like me, this habits is unintuitive, no less than initially. You could have heard that sticky is a mixture of relative and mounted positioning, which means it participates within the regular circulate of the doc however solely till it hits the sides of its scrolling container, at which level it turns into mounted. Whereas viewing sticky as a mixture of different values generally is a helpful mnemonic, it fails to seize one vital distinction between sticky and mounted parts:

A place: mounted factor doesn’t care concerning the dad or mum it’s nested in or any of its ancestors. It would escape of the traditional circulate of the doc and place itself instantly offset from the viewport, as if glued in place a sure distance from the sting of the display screen.

Conversely, a place: sticky factor might be pushed together with the sides of the viewport (or subsequent closest scrolling container), however it is going to by no means escape the boundaries of its direct dad or mum. Effectively, no less than should you don’t depend visually remodel-ing it. So a greater means to consider it could be, to steal from Chris Coyier, that “place: sticky is, in a way, a regionally scoped place: mounted.” That is an intentional design resolution, one that permits for section-specific sticky headers like those made well-known by alphabetical lists in cellular interfaces.

See the Pen [Sticky Section Headers [forked]](https://codepen.io/smashingmag/pen/OJeaWrM) by Philip.

See the Pen Sticky Part Headers [forked] by Philip.

Okay, so this strategy is a no-go for our predicament. We have to discover a resolution that doesn’t contain a container across the header.

Mounted, However Not Solved

Possibly we are able to make our lives a bit easier. As a substitute of a container, what if we gave the .header factor a mounted peak of, say, 150px? Then, all we have now to do is outline the .hero factor’s peak as peak: calc(100vh - 150px).

See the Pen [Attempt #2: Fixed Height + Calc() [forked]](https://codepen.io/smashingmag/pen/yLdQgGz) by Philip.

See the Pen Try #2: Mounted Peak + Calc() [forked] by Philip.

This strategy kinda works, however the downsides are extra insidious than our final try as a result of they will not be instantly obvious. You in all probability observed that the header is just too tall, and we’d wanna do some math to resolve on a greater peak.

Pondering forward a bit,

  • What if the .header’s kids must wrap or rearrange themselves at totally different display screen sizes or develop to keep up legibility on cellular?
  • What if JavaScript is manipulating the contents?

All of these items might subtly change the .header’s best measurement, and chasing the fitting peak values for every situation has the potential to spiral right into a upkeep nightmare of unmanageable breakpoints and magic numbers — particularly if we contemplate this must be completed not just for the .header but additionally the .hero factor that is determined by it.

I might argue that this workaround additionally simply feels incorrect. Mounted heights break one of many fundamental affordances of CSS structure — the way in which parts mechanically develop and shrink to adapt to their contents — and never counting on this normally makes our lives tougher, not easier.

So, we’re left with…

A Novel Strategy

Now that we’ve discovered the constraints we’re working with, one other approach to phrase the issue is that we wish the .header and .hero to collectively span 100vh with out sizing the weather explicitly or wrapping them in a container. Ideally, we’d discover one thing that already is 100vh and align them to that. That is the place it dawned on me that show: grid might present simply what we’d like!

Let’s do that: We declare show: grid on the physique factor and add one other factor earlier than the .header that we’ll name .above-the-fold-spacer. This new factor will get a peak of 100vh and spans the grid’s complete width. Subsequent, we’ll inform our spacer that it ought to take up two grid rows and we’ll anchor it to the highest of the web page.

This factor should be completely empty as a result of we don’t ever need it to be seen or to register to display screen readers. We’re merely utilizing it as a crutch to inform the grid how you can behave.


  
  

  
  
  
Hero Content material
Important Content material
physique {
  show: grid;
}

.above-the-fold-spacer {
  peak: 100vh;
  /* Span from the primary to the final grid column line */
  /* (Unfavourable numbers depend from the tip of the grid) */
  grid-column: 1 / -1;
  /* Begin on the first grid row line, and take up 2 rows */
  grid-row: 1 / span 2; 
}

/* and so on. */

This is the magic ingredient.

By including the spacer, we’ve created two grid rows that collectively take up precisely 100vh. Now, all that’s left to do, in essence, is to inform the .header and .hero parts to align themselves to these current rows. We do have to inform them to start out on the similar grid column line because the .above-the-fold-spacer factor in order that they received’t attempt to sit subsequent to it. However with that completed… ta-da!

See the Pen [The Solution: Grid Alignment [forked]](https://codepen.io/smashingmag/pen/YzoRNdo) by Philip.

See the Pen The Resolution: Grid Alignment [forked] by Philip.

The explanation this works is that a grid container can have a number of kids occupying the identical cell overlaid on high of one another. In a state of affairs like that, the tallest youngster factor defines the grid row’s total peak — or, on this case, the mixed peak of the 2 rows (100vh).

To regulate how precisely the 2 seen parts divvy up the accessible area between themselves, we are able to use the grid-template-rows property. I made it in order that the primary row makes use of min-content quite than 1fr. That is crucial in order that the .header doesn’t take up the identical quantity of area because the .hero however as a substitute solely takes what it wants and lets the hero have the remainder.

Right here’s our full resolution:


physique {
  show: grid;
  grid-template-rows: min-content 1fr;
}

.above-the-fold-spacer {
  peak: 100vh;
  grid-column: 1 / -1;
  grid-row: 1 / span 2;
}

.header {
  place: sticky;
  high: 0;
  grid-column-start: 1;
  grid-row-start: 1;
}

.hero {
  grid-column-start: 1;
  grid-row-start: 2;
}

And voila: A sticky header of arbitrary measurement above a hero that grows to fill the remaining seen area!

Caveats and Closing Ideas

It’s value noting that the HTML order of the weather issues right here. If we outline .above-the-fold-spacer after our .hero part, it is going to overlay and block entry to the weather beneath. We are able to work round this by declaring both order: -1, z-index: -1, or visibility: hidden.

Needless to say this can be a easy instance. In case you had been so as to add a sidebar to the left of your web page, for instance, you’d want to regulate at which column the weather begin. Nonetheless, within the majority of instances, utilizing a CSS Grid strategy is more likely to be much less troublesome than the Sisyphean process of manually managing and coordinating the peak values of a number of parts.

One other upside of this strategy is that it’s adaptable. In case you resolve you desire a group of three parts to take up the display screen’s peak quite than two, then you definitely’d make the invisible spacer span three rows and assign the seen parts to the suitable one. Even when the hero factor’s content material causes its peak to exceed 100vh, the grid adapts with out breaking something. It’s even well-supported in all fashionable browsers.

The extra I take into consideration this method, the extra I’m persuaded that it’s really fairly clear. Then once more, you understand how attorneys can speak themselves into their very own arguments? In case you can consider an excellent easier resolution I’ve neglected, be at liberty to achieve out and let me know!

Smashing Editorial
(gg, yk)



Supply hyperlink

LEAVE A REPLY

Please enter your comment!
Please enter your name here