Get Ready for `display: contents;`

Last year I asked, “Will we be flattening our HTML for CSS Grids?”

The issue is that the only way for elements to participate in the same CSS grid together (or flexbox for that matter) is for them to be siblings. So, in some cases we might be incentivized to forego HTML semantics for the benefit of layout (not great).

One answer to this is display: contents;—a magical new display value that essentially makes the container disappear, making the child elements children of the element the next level up in the DOM.

Fast forward to today, Chrome is shipping it, WebKit is shipping it, and Firefox has shipped it. Vote for it in Edge here.

Wanna understand it better? Rachel Andrew wrote “Vanishing boxes with display contents” and clarifies how it all works:

This value becomes useful if you want to add some element because it makes sense in terms of document semantics, but doesn’t in terms of display. Perhaps you have some content that makes sense marked up as an article, that article is then a flex item in your layout BUT the elements you really would like to be flex items are nested inside that article. Rather than flattening your markup and remove the article element to enable these inner elements to be part of the flex layout, you could remove the boxes generated by article using display: contents. You then get the best of both worlds, semantic markup plus the visual display your design requires. That sounds good to me.

Manuel Rego takes a stab at explaining it as well:

display: contents makes that the div doesn’t generate any box, so its background, border and padding are not rendered. However the inherited properties like color and font have effect on the child (span element) as expected.

There is also a very related subject to all this: subgrids. Probably literally display: subgrid;. It’s probably less important in terms of maintaining semantics than display: contents; but also different.

Eric Meyer called subgrids essential:

Grid layout is the first serious candidate to fill that hole in the past two decades, and I don’t want to see them hamstrung from the outset. Subgrids are essential to the adoption of grids. I hope they’ll be implemented as soon as possible

And to understand the difference, Rachel Andrew also wrote “Why display: contents is not CSS Grid Layout subgrid”:

You won’t get far through a conversation about subgrid in CSS Grid Layout without someone suggesting that display: contents solves most of the problems anyway, so do we really need subgrid? This really isn’t the case, display: contents does indeed solve a class of problems, but these are different problems to those that subgrid would help us with.


Get Ready for `display: contents;` is a post from CSS-Tricks

Creating a Vue.js Serverless Checkout Form: Application and Checkout Component

This is the third post in a four-part series. In part one, we set up a serverless Stripe function on Azure. Part two covered how we hosted the function on Github. This post will focus on wiring everything up as a Vue.js application.

Article Series:

  1. Setup and Testing
  2. Stripe Function and Hosting
  3. Application and Checkout Component (This Post)
  4. Configure the Checkout Component

Stripe has a number of ways to build out a checkout form, the most basic being a single button on the page that you trigger to pull up their custom modal. There’s a repo and component for this, but as easy as that is to implement (it’s probably the most simple way to do it), I wanted a little more customization and wanted the checkout flow to be part of the page and application. This approach wouldn’t work for my needs.

Stripe Elements

Stripe also offers a thing called Elements. Elements allow you to integrate Stripe’s payment system into your own checkout form and style it like your own site for a cohesive experience. It won’t feel like you’re using a third party plugin. They do have some pre-styled examples if you prefer something you can use right out of the box.

Luckily for us, there’s a really nice repo with a Vue version of Stripe Elements called vue-stripe-elements. The repo’s documentation is really nice, so you could check that out. Here’s how I put it to use:

npm i vue-stripe-elements-plus --save

…or using Yarn:

yarn add vue-stripe-elements-plus

Now let’s talk about our cart and integrate it.

The Cart

Here’s what everything looks like as a birds eye view of the application. We’ve already addressed the function and stripe pieces, now let’s dig into the application itself.

birds eye view of the application structure

We’re not going to go through setting up the entire application in these posts, rather just the Cart and Checkout. I’d suggest checking out the following links before continuing if you need to catch up on the basics of Vue, Vuex, and Nuxt:

  • CSS-Tricks Vue Guide
  • Simple Server Side Rendering, Routing, and Page Transitions with Nuxt.js

In our general store set up with Vuex, we hold a manifest of all of our product data used to populate the pages with items. We’ll also use that information to populate a (currently empty) cart object where items can be added for purchase. We’ll use that data on a page called `Cart.vue` in the pages directory. If you’re unfamiliar with Nuxt.js, it allows us to use .vue components as pages by creating them in this pages directory. We can still populate these pages with components from the components directory to create a more modular application. Here are the parts we’re discussing now:

Showing we're discussing cart.vue and vuex in the application

We’ll need two pieces of information from that store in Vuex: the contents of the cart and the cartTotal.

We’ll use computed properties in pages/Cart.vue to fetch that information so that we can cache and use them in the cart.

computed: { cart() { return this.$store.state.cart; }, cartTotal() { return this.$store.state.cartTotal; }, ...
}

…and we’ll create a new computed property that will store the monetary total of the items in the cart as well:

computed: { ... total() { return Object.values(this.cart) .reduce((acc, el) => acc + (el.count * el.price), 0) .toFixed(2); }
}

The first thing that we’ll do is see if the cart has items in it. If it does, then we need to check that the payment hasn’t already been processed. We need to do this because there’s no need to display a checkout form if there are no items in the cart or if payment has already been processed for the items that were added.

<div v-if="cartTotal > 0"> <!--we'll add our checkout here-->
</div> <!--If the cart is empty, give them the ability to get back to the main page to add items-->
<div v-else-if="cartTotal === 0 && success === false" class="empty"> <!--we'll add our empty state here-->
</div> <!--If there's a success, let's let people know it's being processed, we'll add a success component later on-->
<div v-else> <!--we'll add success here-->
</div>

We’ll also create a success property in our data that we’ll initially set to false and use later to record whether or not a payment was successfully submitted.

data() { return { success: false };
},

We want to show cart items if they exist, their individual totals (as we can have multiple counts of the same item) and the final total.

<div v-if="cartTotal > 0"> <h1>Cart</h1> <div class="cartitems" v-for="item in cart" key="item"> <div class="carttext"> <h4>{{ item.name }}</h4> <p>{{ item.price | usdollar }} x {{ item.count }}</p> <p>Total for this item: <strong>{{ item.price * item.count }}</strong></p> </div> <img class="cartimg" :src="`/${item.img}`" :alt="`Image of ${item.name}`"> </div> <div class="total"> <h3>Total: {{ total | usdollar }}</h3> </div> <!--we're going to add our checkout here-->
</div>

We’re using a filter to format the prices in US dollars. I format them this way instead of hardcoding them in case I need to support other currencies in the future.

filters: { usdollar: function(value) { return `$${value}`; }
}

Setting up the Checkout Component

Now we’re going to create our checkout component, which will hold all of the Stripe checkout logic and connect to the serverless function we set up in Part Two. We’ll register the component in the Cart.vue file:

import AppCheckout from './../components/AppCheckout.vue'; export default { components: { AppCheckout }, ...
}

Here’s where we’re at now:

Showing the Cart.vue in pages, as well as the checkout component

And, in the checkout component itself, we’ll bring over the base for the file that we saw in the vue-stripe-elements repo documentation:

<template> <div id='app'> <h1>Please give us your payment details:</h1> <card class='stripe-card' :class='{ complete }' stripe='pk_test_XXXXXXXXXXXXXXXXXXXXXXXX' :options='stripeOptions' @change='complete = $event.complete' /> <button class='pay-with-stripe' @click='pay' :disabled='!complete'>Pay with credit card</button> </div>
</template>
<script>
import { stripeKey, stripeOptions } from './stripeConfig.json'
import { Card, createToken } from 'vue-stripe-elements-plus' export default { data () { return { complete: false, stripeOptions: { // see https://stripe.com/docs/stripe.js#element-options for details } } }, components: { Card }, methods: { pay () { // createToken returns a Promise which resolves in a result object with // either a token or an error key. // See https://stripe.com/docs/api#tokens for the token object. // See https://stripe.com/docs/api#errors for the error object. // More general https://stripe.com/docs/stripe.js#stripe-create-token. createToken().then(data => console.log(data.token)) } }
}
</script>

Next Up…

So far, this is what the component looks like out of the box. We’re going to have to update this component a bit to fit our needs, but not too much. Stay tuned tomorrow for the final installment when we connect our component to our serverless function and finish up the checkout!

Article Series:

  1. Setup and Testing
  2. Stripe Function and Hosting
  3. Application and Checkout Component (This Post)
  4. Configure the Checkout Component

Creating a Vue.js Serverless Checkout Form: Application and Checkout Component is a post from CSS-Tricks

Creating a Vue.js Serverless Checkout Form: Stripe Function and Hosting

We’re now in the second post of a four-part series where we’re creating a checkout form application in Vue.js that can accept payments via the Stripe API. In part one, we looked at the concept of serverless functions, set one up in Azure, and connected it to a Stripe account. In this post, we’ll focus on setting up Stripe as a serverless function and hosting it all on Github.

Article Series:

  1. Setup and Testing
  2. Stripe Function and Hosting (This Post)
  3. Application and Checkout Component
  4. Configure the Checkout Component

First, we’re going write our function and test it out in the portal, but eventually we’re going to move it over to Github and have Azure pull in the code. I’ll explain why we do this in a moment.

For now, in order to get it working and testable, we’re going to write it in the portal and fill in the request body to perform the test. But we need to know what Stripe will expect from us first.

Dun dun dun…

Working With Stripe as a Serverless Function

If you check out Stripe’s documentation, you can see that we’ll need to grab the Stripe token in the dashboard. This will eventually mirror the POST parameters submitted by our form. Stripe makes it easy, so it’s fairly straightforward to use their library for the server-side function with Express:

app.get('/', (req, res) => res.render('index.pug', { keyPublishable })); app.post('/charge', (req, res) => { let amount = 500; stripe.customers .create({ email: req.body.stripeEmail, source: req.body.stripeToken }) .then(customer => stripe.charges.create({ amount, description: 'Sample Charge', currency: 'usd', customer: customer.id }) ) .then(charge => res.render('charge.pug'));
}); app.listen(4567);

We won’t need to set up all of Node and Express for this, though, as what we really need is the amount, the currency, the description, and the token, which we can integrate with the testing code we were provided earlier in the portal’s view of our function. So, let’s head over to the Azure portal where our function lives and update that default testing code to accept the parameters we need for Stripe, and also populate the request.body in the test panel.

We’ll add our Stripe testing key and kick everything off. To be totally sure, we’re going to log what we’ve gotten started:

var stripe = require('stripe')('sk_test_whateveryourtestingkeyisgoeshere');
// ^ this is a stripe testing key module.exports = function(context, req) { context.log('starting to get down');

If we have a request body, an email, and a token, then let’s get started. We’ll create a customer from the email and then use that customer to create the Stripe charges, passing in the amount of the charge as we do so.

if ( req.body && req.body.stripeEmail && req.body.stripeToken && req.body.stripeAmt
){ stripe.customers .create({ email: req.body.stripeEmail, source: req.body.stripeToken }) .then(customer => { context.log('starting the stripe charges'); stripe.charges.create({ amount: req.body.stripeAmt, description: 'Sample Charge', currency: 'usd', customer: customer.id }); }) ...

We also want to test if this all completed successfully, or if it errored out. If it did error, we need to log what that error is. We’ll also see if the whole thing errored entirely, making sure we’re logging everything appropriately along the way.

You’ll note that I log a lot. I think it’s not enough to know that something has errored. I want to know when the error happened and why so that I can track it down. This makes it much easier to debug if something were to go wrong.

 ... .then(charge => { context.log('finished the stripe charges'); context.res = { // status: 200 body: 'This has been completed' }; context.done(); }) .catch(err => { context.log(err); context.done(); }); } else { context.log(req.body); context.res = { status: 400, body: "We're missing something" }; context.done(); }
};

In the testing area on the right side of the portal, we’ll fill the request.body with the stripeEmail, stripeToken (a testing token in this case), and some random amount for the charge. When we run this, we can see that it works! We get a 200 OK Status, and we’ve logged This has been completed in the output.

actual function with testing request body params
Testing the request body parameters with the actual function in Azure.

Github-Hosted Serverless Function

Let’s put everything in Github now that it’s working. One big reason we want to do this is because our function will have a dependency on Stripe’s library. If you head over to the sample-stripe-handler repo I’ve created for this tutorial, you’ll see a package.json file. The most important lines in that file are these:

"dependencies": { "stripe": "^5.3.0"
}

This tells the function to pull in the correct version of the Stripe API that we need to use in order for our app to properly function. As a note, you could also use this method to write other kinds of functions using other libraries. This means the possibilities for what to create are endless!

We’ll pull everything from our function into this repo. This includes the function itself, the package.json file, as well as the contents of the function.json file that you’ll see in the “View Files” tab on the right in the Azure portal.

Once we have that all in ready to go in a Github repo, we’ll head back over to the Azure portal, because now we have to let Azure know that we’d like to use this repo to host our function instead of our test. We can still test our function inside the portal—we just won’t be able to edit it via the GUI anymore.

Click on the “Platform Features” tab and select the “Deployment Options” item.

Azure Portal Deployment Options

From here, click “Settings” then “Choose source” and a number of options will be provided. I’m going to choose Github because that’s where I want to host mine, but you can see that there are a lot of other ways we could have done this.

Choose github
Deployment settings source options, including Github.

Once Github has been selected, you will be able to configure which repo you would like to use as your deployment source. I chose the sample-stripe-handler repo that we created earlier.

Choose github repo for deployment option
Configuring Github as the deployment source.

After we’ve done this and it’s loaded, you’ll be taken to a “Deployments” screen that shows the last commit that you made to the repo. That means everything’s working correctly!

first deploy

Let’s test this a little further. My function didn’t work properly the first time because I was using ES6. I could have added in Babel, but I just converted it back to ES5 and pushed to the master branch. You can see the function.json becomes inactive as the last deployment, and my latest commit message—which is mostly me grumbling—is now the latest deploy! Awesome.

New deployment working

We can’t be too careful so, to check that these tests did indeed work, I’m going to head over to the Stripe dashboard. Sure enough, there are testing charges showing up in our dashboard 😀

Stripe dashboard with a little activity

One last thing!

We would be remiss to exclude our good friend CORS, which we need to properly enable for everything to communicate as it should. Let’s go to our function in the dashboard, and select CORS:

select cors

In the prompt that appears, we’ll whitelist our localhost dev server, as well as our final URL for the site. Voila! We’re all set.

whitelist localhost

Next Up…

We got a lot done in this post! Next, we’ll want to learn how to move away from testing only within the function and get this sucker communicating freely with a checkout experience that we’ll build within a Vue.js application. Stay tuned!

Article Series:

  1. Setup and Testing
  2. Stripe Function and Hosting (This Post)
  3. Application and Checkout Component
  4. Configure the Checkout Component

Creating a Vue.js Serverless Checkout Form: Stripe Function and Hosting is a post from CSS-Tricks

“Stop Using CSS Selectors for Non-CSS”

I saw Nicole Dominguez tweet this the other day:

I wasn’t at this conference, so I have very little context. Normally, I’d consider it a sin to weigh in on a subject brought up by looking at two out-of-context slides, but I’m only weighing in out of interest and to continue the conversation.

The idea seems to be that if you need to select an element in the DOM with JavaScript, don’t use the same selector as you would in CSS.

So if you have…

<article class="article">
</article>

…and you need to apply an event listener to that article for some reason, then don’t use…

$(".article")

(or querySelector or whatever, I assume.)

Instead, apply an attribute intended just for the JavaScript to target, like…

<article class="article" data-hoverable>
</article>

…and target that like…

$("[data-hoverable]")

The idea is that you can separate jobs. The class has the job of styling, and the data attribute has the job of JavaScripting. Both can change without affecting each other.

Seems reasonable to me.

Also seems like there is plenty to talk about here. Performance, I suppose, but that’s probably the least-interesting thing since selectors are generally pretty damn fast these days. We could continue the conversation by talking about:

  • What naming convention?
  • Should you be naming events?
  • What if it needs to be selected for different reasons multiple times?
  • Can you or should you use IDs?
  • Is it worth avoiding DOM selection at all if you can?
  • What other nuances are part of this discussion?

I saw Michael Scharnagl had some thoughts on his own usage of ID’s, classes, and data-attributes that could help frame things a bit.


“Stop Using CSS Selectors for Non-CSS” is a post from CSS-Tricks

Creating a Vue.js Serverless Checkout Form: Setup and Testing

There comes a time in any young app’s life when it will have to monetize. There are a number of ways to become profitable, but accepting cash is a surefire way to make this more direct. In this four-part tutorial, we’ll go over how to set up a serverless function, make it talk to the Stripe API, and connect it to a checkout form that is setup as a Vue application. This may sound daunting, but it’s actually pretty straightforward! Let’s dig in.

Vue shop

Article Series:

  1. Setup and Testing (This Post)
  2. Stripe Function and Hosting
  3. Application and Checkout Component
  4. Configure the Checkout Component

What is Serverless?

We’ve covered serverless concepts before but, in case you haven’t read that article, let’s talk for a minute about what we mean by “serverless” because it’s a bit of a misnomer.

The promise of serverless is to spend less time setting up and maintaining a server. You’re essentially letting the service handle maintenance and scaling for you, and you boil what you need down to functions that run certain code when a request is made. For this reason, people may refer to this as Azure to help us with this. The portal and Github integration are pretty quick to manipulate, as long as you know where to go. So by all means, let’s make it happen!

Sign up for Stripe

First, we’ll create a Stripe account. We verify our new account via email and then we’ll head over to the API section, where we can retrieve two keys. You’ll note that we’re in test mode right now, which is good! We’ll keep it like that for testing, and unveil the testing key token to use while we set up the application.

Once you’re signed in, go to the API section of your dashboard to retrieve your key.

Stripe testing dashboard
The Stripe API screen

You may also want to add a phone number to your account for 2 factor auth as well.

Setting up Our Serverless Function in the Azure Portal

First, we’ll head over to the portal, (or if you don’t already have an account, you can sign up for a free trial here) and select New > Serverless Function

New Function
Setting up a new Servless Function in Azure

When we click on the Serverless Function app, we’ll be taken to a panel that asks for details to help with the setup. As you can see in the screenshot above, it will autofill most of the fields just from the app name, but let’s go over some of these options quickly:

  • Add in a unique name
  • A Resource Group (if you don’t already have one, create one)
  • I use the Windows OS because the Linux is still in preview, so Windows will be more stable
  • I use the Consumption Plan because this is the one that will have payments that scale with the use, and it will also scale automatically. The other option, App Service Plan, is good for people who prefer everything to be a bit more manual.
  • Choose a location that is close to your customer base, or a midpoint between two customer bases
  • Choose a storage, or create one as I’ve done
  • I’ll also check Pin to Dashboard because I want to be able to retrieve my function quickly later
New function settings

This will bring you back to the main portal dashboard and let you know that your function is deploying. Once it’s done, it take you to a main screen that has all of your options. From here, we’ll want to create our function, and it will be an HTTP trigger.

We’ll select Functions under our function name, and you’ll see a little table with a plus that says “New Function”:

New Function in the Portal

Once we click here, we have a few options on what we can create. We’ll pick HTTP Trigger:

Choose HTTP Trigger

We’ll be able to select the language (pick “JavaScript”) and then “Create”:

Pick the Language and Create

The Default Testing Function

From here, we’re given a default testing function which helps us see how this all works. If we open all of these panels and hit the Run button, we’ll see the output in logs.

The initial testing output
Running the function in a test environment

Here’s the code we were given:

module.exports = function(context, req) { context.log('JavaScript HTTP trigger function processed a request.'); if (req.query.name || (req.body && req.body.name)) { context.res = { // status: 200, /* Defaults to 200 */ body: 'Hello ' + (req.query.name || req.body.name) }; } else { context.res = { status: 400, body: 'Please pass a name on the query string or in the request body' }; } context.done();
};

You’ll see here that we’re passing in the context. That allows us to log, which will be shown in the lowest panel below. In the Test panel on the right, we can pass in a request body that can be used to test our application. When it runs, we see the output with a 200 status and know that everything is working. We also have a context.log for the case that it gives us a 400 error. If you’d like to play around with a serverless function and see it in action for yourself, you can create one with a free trial account.

Next Up…

Now that we have the base of our serverless function, let’s set up what we’ll need to communicate with Stripe! More to come in the next post in this series.

Article Series:

  1. Setup and Testing (This Post)
  2. Stripe Function and Hosting
  3. Application and Checkout Component
  4. Configure the Checkout Component

Creating a Vue.js Serverless Checkout Form: Setup and Testing is a post from CSS-Tricks

Meet the New Dialog Element

Keith Grant discusses how HTML 5.2 has introduced a peculiar new element: <dialog>. This is an absolutely positioned and horizontally centered modal that appears on top of other content on a page. Keith looks at how to style this new element, the basic opening/closing functionality in JavaScript and, of course, the polyfills that we’ll need to get cross-browser support right.

Also, I had never heard of the ::backdrop pseudo element before. Thankfully the MDN documentation for this pseudo element digs into it a little bit more.

Direct Link to Article — Permalink


Meet the New Dialog Element is a post from CSS-Tricks

The latest ways to deal with the cascade, inheritance and specificity

The cascade is such an intrinsic part of CSS that they put it right there in the name. If you’ve ever needed to use !important to affect specificity in the cascade, you’ll know that it can be a tricky thing to deal with. In the early days of CSS, it was common to see highly specific selectors like this:

#sidebar ul li {}

We’re all much better at managing specificity nowadays. It’s a widely accepted best practice to keep specificity low and flat—to shun ID selectors, to make liberal use of classes, and to avoid unnecessary nesting. But there are still plenty of situations where a more specific selector will be useful. With the introduction of a newly proposed pseudo-class, more support of the shadow DOM, and the use of the all property, we will soon be able to handle inheritance and specificity in new and exciting ways.

The :is Pseudo-Class

Lea Verou recently proposed this new pseudo-class specifically designed to control specificity. It’s already made its way to the CSS Level 4 Selectors spec. Lea has a write up of why it’s useful and there’s some coverage of it in the CSS-Tricks almanac.

Let’s take :not as an example. The specificity of :not is equal to the specificity of its argument. This makes using :not rather painful. Take the following as an example:

See the Pen :not and specificity by CSS-Tricks (@css-tricks) on CodePen.

We might expect that the .red class would have higher specificity because it is lower in the cascade. However, for any styles to override div:not(.foobar) they would need to at least match the specificity of a combined element selector (div) and class selector (.foobar). Another approach would be div.red, but there is a better way. This is where :is can help.

div:is(:not(.foobar)) { background-color: black;
}

The :not selector no longer adds any specificity, so the total specificity of the above selector is simply that of one element selector (div). The .red class would now be able to override it in the cascade. Once implemented, specificity hacks will be a thing of the past.

Shadow DOM

Today, many people are using classes in HTML like this:

<form class="site-search site-search--full"> <input type="text" class="site-search__field"> <button type="Submit" class="site-search__button">search</button>
</form>

When using shadow DOM, rather than following a verbose naming convention, we’ll be able to omit classes altogether. Styles defined within the shadow DOM are scoped to apply only within the component. Styling can be achieved with simple element selectors without worrying about whether the selectors will interfere with elements elsewhere on the page.

See the Pen shadow dom by CSS GRID (@cssgrid) on CodePen.

It’s liberating to write such easy CSS. No more effort spent naming things. Shadow DOM looks like it is finally making its way to full browser support. It’s likely to make it into the next release of Firefox while Edge have implementation as a high priority.

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

Chrome Opera Firefox IE Edge Safari
53 40 No No No TP

Mobile / Tablet

iOS Safari Opera Mobile Opera Mini Android Android Chrome Android Firefox
11.0-11.2 No No 62 62 No

The all Property

The all property is a way of setting all CSS properties at once—everything from align-content to z-index. What values does it accept? I can’t think of any use case when I’d want all properties to inherit, but that’s an option. Then there’s initial which is more like applying a CSS reset where all the styles are gone. No padding. No margin. The initial value is set per property, regardless of the element it is applied to. The initial value of display is inline, even if you apply it to a div. The font-style of an em tag is normal, as is the font-weight of a strong tag. Link text will be black. You get the idea. (You can find the initial value of any CSS property on MDN.) This does perhaps limit its usefulness, going further than we might like by removing all styles, regardless of context.

See the Pen all: initial by CSS GRID (@cssgrid) on CodePen.

Sadly, the most useful value for all is also the least widely implemented: revert. It can remove the styles that you as a developer have applied, while leaving the default user-agent styles intact. We’ve all seen a page of HTML without a stylesheet—black Times New Roman on a white (transparent) background with blue underlined links. If you really want to avoid inheritance, then all: revert has you covered. All divs will be display: block and spans will be inline. All em tags will be italic and strong tags will be bold. Links will be blue and underlined.

See the Pen all: revert by CSS GRID (@cssgrid) on CodePen.

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

Chrome Opera Firefox IE Edge Safari
No No No No No 9.1

Mobile / Tablet

iOS Safari Opera Mobile Opera Mini Android Android Chrome Android Firefox
9.3 No No No No No

The future?


The miscellany of rival unstandardized methods for writing CSS-in-JS was an attempt to sidestep these same issues. That approach has gained popularity over the last several years. Some of its proponents have deemed inheritance, the cascade and specificity as fundamentally flawed design decisions of the language. The CSS Working Group at the W3C is responding by improving the power of CSS and the native web platform. It will be interesting to see the outcome…


The latest ways to deal with the cascade, inheritance and specificity is a post from CSS-Tricks

Making CSS Animations Feel More Natural

It used to be that designers designed and coders coded. There was no crossover, and that’s the way it was. But with the advent of CSS transitions and animations, those lines are blurring a bit. It’s no longer as simple as the designer dictating the design and the coder transcribing—designers must now know something about code, and coders must know something about design in order to effectively collaborate.

As an example, let’s say a designer asks a developer to make a box bounce. That’s it—no additional instruction. Without some cross-knowledge and a common vocabulary, both sides are a little lost in this communication: the developer doesn’t have enough information to fully realize the designer’s vision, but the designer doesn’t really know what the options are and how to communicate them. With a very basic interpretation, you might end up with something that looks like this:

See the Pen Bouncing Box 1 by Brandon Gregory (@pulpexploder) on CodePen.

Not very exciting. Although, to be fair, this does meet all of the criteria given. We can definitely do better than this, though.

The first thing to look at is the timing function. In the above example, we’re using a linear timing function, which means that the box is constantly moving at the same speed. In some cases, this is desirable; however, in the real world, motion usually doesn’t work like that.

An easy fix is to simply change the timing function to ease. This makes the beginning and ending of each animation a little slower than the middle part, which adds a more natural look to some animations. Here’s the box with the easing function turned on:

See the Pen Bouncing Box 2 by Brandon Gregory (@pulpexploder) on CodePen.

This is a slight improvement, but there’s still a lot of work to be done. The box still looks mechanical and stiff, with the same animation occurring in the same timeframe over and over. Adding a slight delay between bounces adds some visual contrast that seems a little more natural:

See the Pen Bouncing Box 3 by Brandon Gregory (@pulpexploder) on CodePen.

The box now looks like it’s jumping rather than simply moving up and down. There’s a little wind-up and cool-down between jumps that mimics what a live creature might do if given the same instruction. Even though we have no reference for what a jumping box would look like, we all have a pretty good idea of what a jumping creature would look like. Because we know what would happen in nature, by mimicking that, the animation feels more natural. But we can do more to make that wind-up feel a little more weighty.

If you watch cartoons, you’ll notice that natural movements are often exaggerated, creating a caricature of real life. When done well, this can feel just as natural as something in the real world, with the added bonus of infusing a little charm and character into the animation.

At this stage, collaboration between the designer and developer is crucial — but many designers may not even be aware that these options exist. It may be up to the developer to pitch this possibility to the designer.

By adding some subtle distortion to the scale of the box, we can add a lot to the animation:

See the Pen Bouncing Box 4 by Brandon Gregory (@pulpexploder) on CodePen.

Now, the box has character. It feels alive. There are many things to tweak, but this is already moving much farther than the original instruction — in a very good way!

We’re going to go a step further and add a little rebound at the end of the jump:

See the Pen Bouncing Box 5 by Brandon Gregory (@pulpexploder) on CodePen.

The second bounce is making this feel more alive, but something still seems off. The bounce looks stiff compared to the rest of the animation. We need to add another bit of distortion like we did for the wind-up:

See the Pen Bouncing Box 6 by Brandon Gregory (@pulpexploder) on CodePen.

That subtle distortion at the end makes the rebound seem much more natural. Overall, a huge improvement from our basic linear bounce in the first example.

That right there may be exactly what we’re looking for, but further tweaks to the rate of movement can be made with a custom cubic Bézier curve:

See the Pen Bouncing Box 7 by Brandon Gregory (@pulpexploder) on CodePen.

Without both the designer and the developer aware of basic animation principles and controls, this level of customization is impossible. Really, this article just scratches the surface of both fields. If you’re a web designer or a web developer who works with designers, I’d strongly urge you to read up on both.

For animation principles, The Illusion of Life: Disney Animation by Ollie Johnston and Frank Thomas is a great primer on how to make that caricature of real life seem alive and real. With that common language in place, communication and collaboration between designers and developers becomes much easier.

For the technical controls and variations of CSS animation, the possibilities are nearly endless. Delay and timing are simple to adjust. As mentioned, if you don’t like the out-of-the-box ease timing function, it’s very possible to create your own using a cubic-bezier(). You can also adjust the level of distortion you want to bring the animation closer to or further from reality. The important thing is that both the designer and developer are thinking about these variations rather than blindly taking everything without customization. Shared knowledge and collaboration can make even simple animations into great ones.

More Resources

  • 12 basic principles of animation – Wikipedia post outlining the concepts introduced in The Illusion of Life.
  • The Guide to CSS Animation: Principles and Examples – Smashing Magazine article providing a comprehensive guide to CSS animations.
  • Animation in Design Systems – 24 Ways article by Sarah Drasner
  • Animation property – CSS-Tricks almanac entry covering the property and its values
  • Transition property – CSS-Tricks almanac entry covering the property and its values
  • CubicBezier.com – Resource to create custom animation curves with a user interface.

Making CSS Animations Feel More Natural is a post from CSS-Tricks

WordPress User Survey Data for 2015-2017

A grand total of 77,609 responses from WordPress users and professionals collected by Automattic between 2015 and 2017. The stats for 2015 and 2016 have been shared at the annual State of the Word address and 2017 marks the first time they have been published on WordPress News.

A few items that caught my attention at first glance:

  • Between 66% and 75% of WordPress users installed WordPress on their own. In other words, they were savvy enough to do it without the help of a developer. Hosting providers were next up and clocked in at 13-14% of installs.
  • WordPress professionals described their clients as large and enterprise companies only 6-7% of the time. I guess this makes sense if those companies are relying on in-house resourcing, but I still would have pegged this higher.
  • What do users love most about WordPress? It’s simple and user-friendly (49-52%). What frustrates them most about it? Plugins and themes (19-28%). Seems like those two would go hand-in-hand to some degree.

I’m not a statistician and have no idea how much the results of these surveys accurately reflect the 26% of all sites on the internet that are powered by WordPress, but it sure is interesting.

Direct Link to Article — Permalink


WordPress User Survey Data for 2015-2017 is a post from CSS-Tricks

Careful Now

Tom Warren’s “Chrome is turning into the new Internet Explorer 6” for The Verge has a title that, to us front-end web developers, suggests that Chrome is turning into a browser far behind in technology and replete with tricky bugs. Aside from the occasional offhand generic, “Chrome is getting so bad lately,” comments you hear, we know that’s not true. Chrome often leads the pack for good web tech.

Instead, it’s about another equally concerning danger: developers building sites specifically for Chrome. In theory, that’s not really a thing, because if you build a website with web standards (of which there isn’t really much of an alternative) it’ll work in Chrome like any other modern web browser. But it is a thing if you build the site to somehow block other browsers and only allow Chrome. Warren:

Google has been at the center of a lot of “works best with Chrome” messages we’re starting to see appear on the web. Google Meet, Allo, YouTube TV, Google Earth, and YouTube Studio Beta all block Windows 10’s default browser, Microsoft Edge, from accessing them and they all point users to download Chrome instead. Google Meet, Google Earth, and YouTube TV are also not supported on Firefox with messages to download Chrome.

I wouldn’t call it an epidemic but it’s not a good trend. Best I can tell, it’s server-side UA sniffing that entirely blocks the sites:

Sheesh.

If anything, I’d think you’d just let people use the site and display a warning if you’re really worried some particular feature might not work. Or even better, fix it. I have no behind-the-scenes knowledge of why they made the choice to block certain browsers, but it’s hard to imagine a technical limitation that would force it. And if it is, I’d suggest letting it be very publicly known to incentivize the other browsers to support what is needed, assuming it’s an established standard.

Even more concerning than browser-specific websites is seeing browsers ship non-standardized features just because they want them, not behind any vendor prefix or flag. There was a time when web developers would have got out the pitchforks if a browser was doing this, but I sense some complacency seeping in.

These days, the vibe is more centered around complaining about other browsers lack of support for things. For example, one browser ships something, we see one green dot in caniuse, and we lambast the other browsers to catch up. Instead, we might ask, was it a good idea to ship that feature yet?

No modern browser is shipping vendor prefixes anymore since we all ultimately decided that was a bad idea. A side effect of that is that shipping a new feature in CSS or JavaScript is all the riskier. I would think shipping an unprefixed feature to a stable version of the browser would mean the feature is standardized and not going to significantly change. Yet, it’s been happening.

In CSS, Chrome shipped motion-* properties, but then that all changed to offset-*, and the old motion-* properties just stopped working. That’s more than just annoying, that kind of thing helps developers justify saying, “I just build this site for Chrome, if you wanna use it, use Chrome.” Fine for a demo, perhaps, but bad for the web industry as a whole. Again, I have no special insight into why this happens, I’m just a developer looking in from the outside.

Here’s another CSS one I just saw the other day. People are excited about text-decoration-skip: ink; because it looks great and helps people. They are using it a lot. But apparently, that’s not the correct name for it? It’s been changed to text-decoration-skip-ink: auto; and so Chrome 64 is canning text-decoration-skip: ink;. This stuff is hard to keep up with even while actively trying.

Chris Krycho had a take on it recently as well:

Over the past few years, I’ve increasingly seen articles with headlines that run something like, “New Feature Coming To the Web” — followed by content which described how Chrome had implemented an experimental new feature. “You’ll be able to use this soon!” has been the promise.

The reality is a bit more complicated. Sometimes, ideas the Chrome team pioneers make their way out to the rest of the browsers and become tools we can all use. Sometimes… they get shelved because none of the other browsers decide to implement them.

Many times, when this latter tack happens, developers grouse about the other browser makers who are “holding the web back.” But there is a fundamental problem in this way of looking at things: Chrome isn’t the standard. The fact that Chrome proposes something, and even the fact that a bunch of developers like it, does not a standard make. Nor does it impose an obligation to other browsers to prioritize it, or even to ship it.

This isn’t all to throw Chrome under the bus. I’m a Chrome fan. I’m sure there are examples from all the major vendors in the same vein. I’d just like my two cents to be careful now. The web is the best platform to build for and generally heading in a direction that makes that even truer. The easiest way to screw that up is not being careful with standards.


Careful Now is a post from CSS-Tricks