5 hot new CSS features and how to use them

null

In this article we'll explore five new properties coming to CSS, take a look at what they do and how to put them to practical use in your projects. We're going to be creating a website layout for a page that includes a newsfeed and a little chat box in the corner. For more CSS tricks, take a look at our article exploring different CSS animation examples.

For this tutorial you'll need Chrome 65+ or Firefox 59+. Take a look at the accompanying GitHub repo for the the step-by-step code. We'll leverage the following features to create a better experience and work around some issues. 

CSS Display Module (Level 3)

CSS Conditional Rules Module (Level 3)

CSS Overscroll Behavior Module (Level 1)

CSS Selectors Module (Level 4)

The CSS Containment Module (Level 1) 

01. Set up the HTML for the newsfeed

First we need to set up some super simple, repeating markup for our newsfeed. Let's create a .container div with an unordered list inside. Give the <ul> the class of .feed, then create 10 list-items each containing a div with the .card class and the text Card 1, Card 2 etc. 

Finally create another list-item in-between 5 and 6 with a class of .nested – this will be helpful later – and add a <ul> inside with three list items using the text Card A, Card B etc.

<body>
 <div class="container">
 <ul class="feed">
 <li><div class="card">Card 1</div></li>
 <li><div class="card">Card 2</div></li>
 <li><div class="card">Card 3</div></li>
 <li><div class="card">Card 4</div></li>
 <li><div class="card">Card 5</div></li>
    <li class="nested">
 <ul>
 <li><div class="card">Card A</div></li>
 <li><div class="card">Card B</div></li>
 <li><div class="card">Card C</div></li>
 </ul>
 </li>
 <li><div class="card">Card 6</div></li>
 <li><div class="card">Card 7</div></li>
 <li><div class="card">Card 8</div></li>
 <li><div class="card">Card 9</div></li>
 <li><div class="card">Card 10</div></li>
 </ul>
</div>
</body>

02. Style the newsfeed

Now we need to add some quick styles so that this starts to look more like a newsfeed. First we can give <body> a subtle grey background colour. Then give .container a max-width of 800px and use margin: 0 auto; to centre align it. 

Let's also give .card a white background, 10px of padding and margin and finally a min-height of 300px – this should give us enough to make the page scrollable. Lastly we'll sprinkle some Flexbox magic on the .feed to make the items flow nicely, with two cards per row.

.feed {
display: flex;
flex-wrap: wrap;
}
.feed li {
flex: 1 0 50%;
}

03. Fix layout problems

If you scroll down the list you'll notice that our cards in the nested list, Card A - C, are causing some layout problems. Ideally we'd like them to flow in with the rest of the cards but they are all stuck together in one block. The reason for this is that a flex container – which is created using display: flex – only makes its immediate children (i.e. the list items) into flex items. 

Now, normally the only way of fixing this is to change the markup, but let's pretend that you don't have that luxury. Perhaps the newsfeed markup is generated by a third-party script or it's legacy code that you're only trying to reskin. So how can we fix this?

Meet display: contents. This little one-liner essentially makes an element behave as if it isn't there. You still see the descendants of the element but the element itself doesn't affect the layout.

Because we're pretending we can't change the markup (just for this step) we can be a bit smart about this and make the .card elements the flex items and almost entirely ignore the list markup. 

First remove the existing .feed li class and then use display: contents for both <ul> and <li> elements:

.feed ul,
.feed li {
display: contents;
}

Now you should see the cards flowing in order, but we've lost the sizing. Fix that by adding the flex property to the .card instead:

.card {
flex: 1 0 40%;
}

Ta-da! Our cards are now using the wonders of Flexbox as if their structural unordered list markup doesn't exist.

As a side note you might be wondering why the flex-basis value is set to 40%. This is because the .card class has a margin, which isn't included in the width calculation as you would expect when using box-sizing: border-box. To work around this we just need to set the flex-basis high enough to trigger the wrapping at the necessary point and Flexbox will fill the remaining space automatically.

04. Explore feature queries

Although display: contents does exactly what we need, it is still only at Working Draft status with W3C. And in Chrome support only arrived in version 65 released in March 2018. Incredibly Firefox has had support since April 2015!

If you disable the style in DevTools you'll see that our changes have made a bit of a mess with the layout when display: contents isn't applied. So what can we do about this? Time for our next new feature – feature queries. 

These work just like media queries but they allow you to ask the browser – using CSS alone – if a property and value expression is supported. If they are, the styles contained inside the query will be applied. Now, let's move our display: contents styles into a feature query.

@supports (display: contents) {
.feed ul,
.feed li {
  display: contents;
}
.card {
  flex: 1 0 40%;
}
}

05. Use 'not' for a cleaner outcome

Normally in this kind of progressive enhancement scenario we'd use the query to add the new styles, but it would also have to disable some of the original styles necessary for the fallback layout.

However you might decide that because support for feature queries is pretty good (if you ignore IE) you actually want to use the feature query not operator. It works just like you'd expect, so we can re-apply our original flex property to the list-items when display: contents is not supported:

@supports not (display: contents) {
.feed li {
  flex: 1 0 50%;
}
}

Inside the not query we can add some styles so that the .nested items basically re-apply what was being inherited by using display: contents.

feed li.nested {
flex-basis: 100%;
}
.feed li.nested ul {
 display: flex;
 flex-wrap: wrap;
}

06. Taking it one step further

You can already see the potential of feature queries, but the really cool thing is that you can combine expressions using the three available operators: and, or and not. Perhaps we could also check for display: flex support and then add a float-based fallback. 

We're not going to do that here, but if we were we'd first modify the two feature queries like so:

@supports (display: flex) and (display: contents) {
...
}
@supports (display: flex) and (not (display: contents)) {
...
}

As a bonus you can also test for custom properties support like this:

@supports (--foo: green) {
...
}

07. Add a chat box

Now we have a beautiful newsfeed in place, let's add a little chat box that is fixed to the bottom right of the screen. We'll need a list of messages and a text field for the user to enter their message. Add this block just after the opening <body> tag:

<div class="chat">
 <div class="messages">
 <ul>
<li><div class="message">Message 1</div></li>
<li><div class="message">Message 2</div></li>
<li><div class="message">Message 3</div></li>
<li><div class="message">Message 4</div></li>
<li><div class="message">Message 5</div></li>
<li><div class="message">Message 6</div></li>
<li><div class="message">Message 7</div></li>
<li><div class="message">Message 8</div></li>
<li><div class="message">Message 9</div></li>
<li><div class="message">Message 10</div></li>
 </ul>
 </div>
 <input type="text" class="input">
</div>

08. Style the chat box

Time to quickly add some styling so it looks half decent.

.chat {
 background: #fff;
 border: 10px solid #000;
 bottom: 0;
 font-size: 10px;
 position: fixed;
 right: 0;
 width: 300px;
 z-index: 10;
}
.messages {
 border-bottom: 5px solid #000;
 overflow: auto;
 padding: 10px;
 max-height: 300px;
}
.message {
 background: #000;
 border-radius: 5px;
 color: #fff;
 margin: 0 20% 10px 0;
 padding: 10px;
}
.messages li:last-child .message {
 margin-bottom: 0;
}
.input {
 border: none;
 display: block;
 padding: 10px;
 width: 100%;
}

09. Scroll chaining

Hopefully now you'll have a little chat box with a scrollable list of messages sitting on top of your newsfeed. Great. But have you noticed what happens when you scroll inside a nested area and you reach the end of its scrollable range? Try it. Scroll all the way to the end of the messages and you'll see the page itself will start scrolling instead. This is called scroll chaining.

It's not a big deal in our example but in some cases it might be. We've had to work around this feature before when creating scrollable areas inside modals or context menus. 

The dirty fix is to set the <body> to overflow: hidden, but there is a nice, shiny new CSS property that fixes all of this and it's a simple one-liner. Say hello to overscroll-behavior. There are three possible values: 

  • auto – The default, which allows scroll chaining
  • contain – Disables scroll chaining
  • none – Disables scroll chaining and other overscroll effects (e.g. rubberbanding)

We can use the shorthand overscroll-behavior or we can target a specific direction with overscroll-behavior-x (or -y). Let's add it to our .messages class:

.messages {
...
overscroll-behavior-y: contain;
... }

Now try the scrolling again and you'll see that it no longer affects the page's scroll when you reach the end of the content.

This property is also pretty handy if you wanted to implement a pull-to-refresh feature in your PWA, say to refresh the newsfeed. Some browsers, such as Chrome for Android, automatically add their own and until now there has been no easy way to disable it without using JS to cancel events using performance-impacting non-passive handlers.

Now you just need to add overscroll-behavior: contain to the viewport-containing element, probably <body> or <html>.

It's worth noting that this property is not a W3C Standard, rather a proposal by the Web Incubator Community Group (WICG). Popular, stable and mature WICG proposals are considered for migration to a W3C Working Group at a later stage.

10. Collapse the chat box when not in use

At the moment the chat box takes up quite a bit of space, which unless we're interacting with it is a bit distracting. Fortunately we can do something about this with a little CSS magic.

But first of all we need to modify our existing styles slightly. By default we want the chat box to be collapsed, so we need to reduce the max-height value in the .messages class. While we're there we can also add a transition to the max-height property:

.messages {
 ...
 max-height: 25px;
transition: max-height 500ms; }

Next page: Continue exploring new CSS features in steps 11-20