Front End Web Development

Using Mixins in Vue.js

Css Tricks - Wed, 06/14/2017 - 8:48pm

It's a common situation: you have two components that are pretty similar, they share the same basic functionality, but there's enough that's different about each of them that you come to a crossroads: do I split this component into two different components? Or do I keep one component, but create enough variance with props that I can alter each one?

Neither of these solutions is perfect: if you split it into two components, you run the risk of having to update it in two places if the functionality ever changes, defeating DRY premises. On the other hand, too many props can get really messy very quickly, and force the maintainer, even if it's yourself, to understand a lot of context in order to use it, which can slow you down.

Enter mixins. Mixins in Vue are useful for writing in a functional style because ultimately, functional programming is about making code understandable by reducing moving parts. (There's a great quote by Michael Feathers about this). A mixin allows you to encapsulate one piece of functionality so that you can use it in different components throughout the application. If written correctly, they are pure- they don't modify or change things outside of the function's scope, so you will reliably always receive the same value with the same inputs on multiple executions. This can be really powerful.

Basic example

Let's say we have a couple of different components whose job it is to toggle a state boolean, a modal and a tooltip. These tooltips and modals don't have a lot in common except for that functionality: they don't look the same, they're not used the same, but the logic is similar.

//modal const Modal = { template: '#modal', data() { return { isShowing: false } }, methods: { toggleShow() { this.isShowing = !this.isShowing; } }, components: { appChild: Child } } //tooltip const Tooltip = { template: '#tooltip', data() { return { isShowing: false } }, methods: { toggleShow() { this.isShowing = !this.isShowing; } }, components: { appChild: Child } }

We could extract the logic here and create something that can be reused:

const toggle = { data() { return { isShowing: false } }, methods: { toggleShow() { this.isShowing = !this.isShowing; } } } const Modal = { template: '#modal', mixins: [toggle], components: { appChild: Child } }; const Tooltip = { template: '#tooltip', mixins: [toggle], components: { appChild: Child } };

See the Pen Mixin by Sarah Drasner (@sdras) on CodePen.

This example was intentionally kept small and simple for purposes of legibility- examples of mixins I've found useful in real life applications are included but not limited to: getting dimensions of the viewport and component, gathering specific mousemove events, and base elements of charts. Paul Pflugradt has a nice repo of Vue Mixins, but it's worth mentioning that they're written in coffeescript.

Usage

This Pen doesn't really show how we would set this up in a real application, so let's look at that next.

You can set up your directory structure any way that you like, but I like to create a mixin directory in order to stay organized. The file we'd create would have a .js extension (as opposed to .vue, like our other files), and we'd export an object for the mixin:

And then in Modal.vue we would now have access to it by importing the toggle like this:

import Child from './Child' import { toggle } from './mixins/toggle' export default { name: 'modal', mixins: [toggle], components: { appChild: Child } }

It's important to understand that even though we're using an object and not a component, lifecycle methods are still available to us. We could hook into mounted() here and it would be applied to the component's lifecycle, which makes this way of working really flexible and powerful.

Merging

Looking at the last example, we can see that not only do we have our functionality, but also lifecycle hooks available to us from the mixin, so when applying it to a component with overlapping processes, ordering matters. By default, mixins will be applied first, and the component will be applied second so that we can override it as necessary. The component has the last say. This only really becomes important when there is a conflict and the component has to "decide" which one wins out, otherwise everything will be placed in an array to execute and the mixin will be pushed first, the component second.

//mixin const hi = { mounted() { console.log('hello from mixin!') } } //vue instance or component new Vue({ el: '#app', mixins: [hi], mounted() { console.log('hello from Vue instance!') } }); //Output in console > hello from mixin! > hello from Vue instance!

If the two conflict, we can see how the Vue instance or component will win:

//mixin const hi = { methods: { sayHello: function() { console.log('hello from mixin!') } }, mounted() { this.sayHello() } } //vue instance or component new Vue({ el: '#app', mixins: [hi], methods: { sayHello: function() { console.log('hello from Vue instance!') } }, mounted() { this.sayHello() } }) // Output in console > hello from Vue instance! > hello from Vue instance!

You may notice that we have two console.logs for the Vue instance string instead of one here- that's because the first function that was called wasn't destroyed, it was overridden. We're still calling both of the sayHello() functions here.

Global Mixins

When we use the term global in reference to mixins, we are not referring to being able to access them on every component, like we are with something like filters. We can already access our mixins in a component with mixins: [toggle].

Global mixins are literally applied to every single component. For this reason, the use case for them is extremely limited and they should be considered with great caution. One use I can think of that makes sense is something like a plugin, where you may need to gain access to everything. But again, even in this instance, I would be wary about what you're applying, especially when you're extending functionality to applications that might be a black box for you.

To create a global instance, we would place it above the Vue instance. In a typical Vue-cli build, this would go in your main.js file.

Vue.mixin({ mounted() { console.log('hello from mixin!') } }) new Vue({ ... })

Again, use this with caution! That console.log would now appear in every single component. This isn't so bad in this case (aside from all the noise in the console) but you can see how potentially harmful that could be if used incorrectly.

Conclusion

Mixins can be useful to encapsulate a small piece of functionality that you'd like to reuse. They are certainly not the only option available to you: higher order components, for example, allow you to compose similar functionality, this is just one way of working. I like mixins because we're not having to pass state around, but this pattern can certainly be abused as well, so take care to think through which option makes the most sense for your application.

Using Mixins in Vue.js is a post from CSS-Tricks

Introduction to Webpack: Entry, Output, Loaders, and Plugins

Css Tricks - Wed, 06/14/2017 - 12:36am

Front-end development has shifted to a modular approach, improving the encapsulation and structure of codebases. Tooling became a critical part of any project, and right now there are a lot of possible choices.

Webpack has gained popularity in the last years because of its power and scalability, but some developers found its configuration process confusing and hard to adopt.

We'll go step by step from an empty configuration file to a simple but complete setup to bundle a project. This article assumes basic understanding of CommonJS notation and how modules work.

Concepts

Unlike most bundlers out there, the motivation behind Webpack is to gather all your dependencies (not just code, but other assets as well) and generate a dependency graph.

At first, it might look strange to see a `.js` file require a stylesheet, or a stylesheet retrieving an image modified as it was a module, but these allow Webpack to understand what is included in your bundle and helps you transform and optimize them.

Install

Let's first add the initial packages we are going to use:

npm install webpack webpack-dev-server --save-dev

Next we create a `webpack.config.js` file in the root of our project and add two scripts to our `package.json` files for both local development and production release.

"scripts": { "start": "webpack-dev-server", "build": "webpack" }

Webpack commands will pick up the config file we've just created unless we indicate other action.

Entry

There are many ways to specify our "entry point", which will be the root of our dependencies graph.

The easiest one is to pass a string:

var baseConfig = { entry: './src/index.js' };

We could also pass an object in case we need more than one entry in the future.

var baseConfig = { entry: { main: './src/index.js' } };

I recommend the last one since it will scale better as your project grows.

Output

The output in Webpack is an object holding the path where our bundles and assets will go, as well as the name the entries will adopt.

var path = require('path'); var baseConfig = { entry: { main: './src/index.js' }, output: { filename: 'main.js', path: path.resolve('./build') } }; // export configuration module.exports = baseConfig;

If you're defining the entry with an object, rather than hardcoding the output filename with a string, you can do:

output: { filename: '[name].js', path: path.resolve('./build') }

This way when new entries are added Webpack will pick up their key to form the file name.

With just this small set of configurations, we are already able to run a server and develop locally with npm start or npm run build to bundle our code for release. By knowing the dependencies of the project, webpack-dev-server will watch them and reload the site when it detects one of them has changed.

Loaders

The goal of Webpack is to handle all our dependencies.

// index.js file import helpers from '/helpers/main.js'; // Hey Webpack! I will need these styles: import 'main.css';

What's that? Requiring a stylesheet in JavaScript? Yes! But bundlers are only prepared to handle JavaScript dependencies out-of-the-box. This is where "loaders" make their entrance.

Loaders provide an easy way to intercept our dependencies and preprocess them before they get bundled.

var baseConfig = { // ... module: { rules: [ { test: /* RegEx */, use: [ { loader: /* loader name */, query: /* optional config object */ } ] } ] } };

For loaders to work, we need a regular expression to identify the files we want to modify and a string or an array with the loaders we want to use.

Styles

To allow Webpack to process our styles when required we are going to install css and style loaders.

npm install --save-dev css-loader style-loader

The css-loader will interpret styles as dependencies and the style-loader will automatically include a <style> tag with them on the page when the bundle loads.

var baseConfig = { entry: { main: './src/index.js' }, output: { filename: '[name].js', path: path.resolve('./build') }, module: { rules: [ { test: /\.css$/, use: [ { loader: 'style-loader' }, { loader: 'css-loader' } ] } ] } };

In this example, main.css will go first through css-loader and then style-loader.

Preprocessors

Adding support for LESS or any other preprocessor is as simple as installing the corresponding loader and adding it to the rule.

rules: [ { test: /\.less$/, use: [ { loader: 'style-loader' }, { loader: 'css-loader' }, { loader: 'less-loader' } ] } ] Transpiling

JavaScript can be transformed by loaders too. One example would be using a Babel loader to transpile our scripts.

rules: [ { test: /\.js$/, use: [ { loader: 'babel-loader' } ] } ] Images

Webpack has a great feature where it can detect url() statements inside stylesheets and let loaders apply changes to the image file and the url itself.

// index.less file @import 'less/vars'; body { background-color: @background-color; color: @text-color; } .logo { background-image: url('./images/logo.svg'); }

By adding one rule, we could apply the file-loader to just copy the file or use the url-loader, the latest inlines the image as a base64 string unless it exceeds a byte limit, in which case it will replace the url statement with a relative path and copy the file to the output location for us.

{ test: /\.svg$/, use: [ { loader: 'url-loader', query: { limit : 10000 } } ] }

Loaders can be configurable by passing a query object with options, like here where we are configuring the loader to inline the file unless it exceeds 10Kb in size.

Managing our build process this way, we will only include the necessary resources instead of moving a hypothetical assets folder with tons of files that might or might be not used in our project.

If you use React or a similar library you can require the .svg file in your component with the svg-inline-loader.

Plugins

Webpack contains default behaviors to bundle most type of resources. When loaders are not enough, we can use plugins to modify or add capabilities to Webpack.

For example, Webpack by default includes our styles inside our bundle, but we can alter this by introducing a plugin.

Extracting Assets

A common use for a plugin is to extract the generated stylesheet and load it as we normally do using a <link> tag.

var ExtractTextPlugin = require('extract-text-webpack-plugin'); var lessRules = { test: /\.less$/, use: [ { loader: 'style-loader' }, { loader: 'css-loader' }, { loader: 'less-loader' } ] }; var baseConfig = { // ... module: { rules: [ // ... { test: /\.less$/, use: ExtractTextPlugin.extract(lessRules) } ] }, plugins: [ new ExtractTextPlugin('main.css') ] }; Generate an `index.html` file

When building single-page applications we usually need one .html file to serve it.

The HtmlWebpackPlugin automatically creates an `index.html` file and add script tags for each resulting bundle. It also supports templating syntax and is highly configurable.

var HTMLWebpackPlugin = require('html-webpack-plugin'); var baseConfig = { // ... plugins: [ new HTMLWebpackPlugin() ] }; Building for Production Define the Environment

A lot of libraries introduce warnings that are useful during development time but have no use in our production bundle and increase its size.

Webpack comes with a built-in plugin to set global constants inside your bundle.

var ENV = process.env.NODE_ENV; var baseConfig = { // ... plugins: [ new webpack.DefinePlugin({ 'process.env.NODE_ENV': JSON.stringify(ENV) }) ] };

We now need to specify the environment on our commands:

"scripts": { "start": "NODE_ENV=development webpack-dev-server", "build": "NODE_ENV=production webpack" }

process.env.NODE_ENV will be replaced by a string, allowing compressors to eliminate unreachable development code branches.

This is really useful to introduce warnings in your codebase for your team and they won't get to production.

if (process.env.NODE_ENV === 'development') { console.warn('This warning will dissapear on production build!'); } Compressing

On production, we need to give users the fastest possible product. By minifying our code with remove unnecessary characters, this reduces the size of our bundle and improves loading times.

One of the most popular tools to do this is UglifyJS, and Webpack comes with a built-in plugin to pass our code through it.

// webpack.config.js file var ENV = process.env.NODE_ENV; var baseConfig = { // ... plugins: [] }; if (ENV === 'production') { baseConfig.plugins.push(new webpack.optimize.UglifyJsPlugin()); } Wrap Up

Webpack config files are incredibly useful, and the complexity of the file will depend on your needs. Take care to organize them well as they can become harder to tame as your project grows.

In this article, we started with a blank config file and ended up with a base setup that would allow you to develop locally and release production code. There's more to explore in Webpack, but these key parts and concepts can help you become more familiar with it.

If you want to go deeper, I recommend Webpack official documentation which has been updated and improved for its second big release.

Introduction to Webpack: Entry, Output, Loaders, and Plugins is a post from CSS-Tricks

New fonts from Type Network in Typekit Marketplace

Nice Web Type - Tue, 06/13/2017 - 11:09am

We are pleased to welcome new fonts from Type Network, a growing alliance of independent type designers from around the world. Since they joined Typekit in November, they have expanded their collection with more partners and font releases. Let’s take a closer look!

CSTM Fonts, a foundry led by Ilya Ruderman and Yury Ostromentsky, is adding some stellar Cyrillic options with four new families.

Kazimir is based on late 19th-century book typography, and its popularity spurred the creation of Kazimir Text, which includes more weights than its display counterpart. Cyrillic letters are more symmetrical in nature than those comprising the Latin alphabet, so Kazimir’s static contrast (where the thins and thicks are somewhat symmetrical) works well to create consistency between the two writing systems. Big City Grotesque, by Olga Pankova and Ilya Ruderman, is a sans serif with humanist flair — and don’t miss the awesome ligatures! Have a look at all of their fonts on Typekit Marketplace.

Laura Meseguer and Jose Manuel Uros bring us a fantastic variety of type through their foundry, Type-Ø-Tones. We love the natural rhythm of Rumba, which gets even more dynamic if you experiment with its three different styles; Laura calls it the “axis of expressiveness”. Rumba was Laura’s final project at the Type and Media program at the Royal Academy of Art The Hague, and the subject of her dissertation, which gets into much more detail about its development.

Cyrus Highsmith doesn’t disappoint with his new sans serif, Allium. Cyrus emphasized a warm, balanced aesthetic in this design, and he recommends, “Try it if you need a clear, calm tone of voice but don’t want to put your readers to sleep”. His introduction to Allium on the Type Network site is a great study of thoughtful type design in action. Allium includes support for Greek and Cyrillic.

Bennet from Richard Lipton shows more than just his calligraphic expertise. The typeface’s support of widths, weights, and even grades — variations which affect the way a typeface looks when printed on paper — make the design exceptionally high-functioning without compromising its playful nature. Read the Type Network overview for even more detail about Bennet’s skillful construction.

Sibylle Hagmann has created a portfolio of interesting and original typefaces, and we’re delighted to add more from her to Typekit Marketplace. Kopius is loosely based on Liberta, a serif family designed by Herbert Thannhaeuser in 1956 in East Germany (see original images for reference). Both regular and condensed widths are available.

New Zen, by Miles Newlyn and Elana Schneider, is a rounded typeface with just a little edge to it. One of Miles’s goals with New Zen was to create a rounded sans-serif that felt less “childish” in character. The subtle edges in the counters create a broad nib pen look — and definitely detract from any elementary-school vibe. Read more from Type Network about the design. We love the detail about designing for accessibility!

In designing charming script face Gautreaux, Victoria Rushton was inspired by her grandmother’s handwriting, and her own treasured copy of The Script Letter by Tommy Thompson. With only four contextual alternates, Gautreaux connects and flows with a wonderful simplicity that makes it adaptable to many different editorial situations.

It’s great to see the Type Network library expand with such a varied selection. All these new fonts are available for purchase on Typekit Marketplace, which you do not need a paid Creative Cloud subscription to use. All you need is an Adobe ID so that you can sync the fonts you’ve purchased via the Creative Cloud desktop app. The fonts are then yours to use for as long as you keep the CC app running.

We would love to see how you use these typefaces in your work — let us know what you think on Twitter and Instagram!


CSS Animations vs Web Animations API

Css Tricks - Tue, 06/13/2017 - 1:57am

There is a native API for animation in JavaScript known as the Web Animations API. We'll call it WAAPI in this post. MDN has good documentation on it, and Dan Wilson has a great article series.

In this article, we'll compare WAAPI and animations done in CSS.

A note on browser support

WAAPI has a comprehensive and robust polyfill, making it usable in production today, even while browser support is limited.

As ever, you can check Can I Use for browser support data. However, that doesn't provide very good info on support of all the sub features of WAAPI. Here's a checker for that:

See the Pen WAAPI Browser Support Test by Dan Wilson (@danwilson) on CodePen.

To experiment with all features without a polyfill, use Firefox Nightly.

The basics of WAAPI

If you've ever used jQuery's .animate(), the basic syntax of WAAPI should look pretty familiar. 

var element = document.querySelector('.animate-me'); element.animate(keyframes, 1000);

The animate method accepts two parameters: keyframes and duration. In contrast to jQuery, not only does it have the benefit of being built into the browser, it's also more performant.

The first argument, the keyframes, should be an array of objects. Each object is a keyframe in our animation. Here's a simple example:

var keyframes = [ { opacity: 0 }, { opacity: 1 } ];

The second argument, the duration, is how long we want the animation to last?. In the example above it is 1000 milliseconds. Let's look at a more exciting example.

Recreating an animista CSS animation with WAAPI

Here's some CSS code I yanked from the awesome animista for something calling itself the "slide-in-blurred-top" entrance animation. It looks pretty sweet. 

The actual perf is much better than this GIF.

Here's those keyframes in CSS:

0% { transform: translateY(-1000px) scaleY(2.5) scaleX(.2); transform-origin: 50% 0; filter: blur(40px); opacity: 0; } 100% { transform: translateY(0) scaleY(1) scaleX(1); transform-origin: 50% 50%; filter: blur(0); opacity: 1; }

Here's the same code in WAAPI:

var keyframes = [ { transform: 'translateY(-1000px) scaleY(2.5) scaleX(.2)', transformOrigin: '50% 0', filter: 'blur(40px)', opacity: 0 }, { transform: 'translateY(0) scaleY(1) scaleX(1)', transformOrigin: '50% 50%', filter: 'blur(0)', opacity: 1 } ];

We've already seen how easy it is to apply the keyframes to whichever element we want to animate:

element.animate(keyframes, 700);

To keep the example simple, I've only specified the duration. However, we can use this second parameter to pass in far more options. At the very least, we should also specify an easing. Here's the full list of available options with some example values:

var options = { iterations: Infinity, iterationStart: 0, delay: 0, endDelay: 0, direction: 'alternate', duration: 700, fill: 'forwards', easing: 'ease-out', } element.animate(keyframes, options);

With these options, our animation will start at the beginning with no delay and loop forever alternating between playing forwards and in reverse.

See the Pen motion blur waapi circle by CSS GRID (@cssgrid) on CodePen.

Annoyingly, for those of us familiar with CSS animations, some of the terminologies varies from what we're used to. Although on the plus side, things are a lot quicker to type!

  • It's easing rather than animation-timing-function
  • Rather than animation-iteration-count it's iterations. If we want the animation to repeat forever it's Infinity rather than infinite. Somewhat confusingly, Infinity isn't in quotes. Infinity is a JavaScript keyword, whereas the other values are strings.
  • We use milliseconds instead of seconds, which should be familiar to anyone who's written much JavaScript before. (You can use milliseconds in CSS animations as well, but few people do.)

Let's take a closer look at one of the options: iterationStart. 

I was stumped when I first came across iterationStart. Why would you want to start on a specified iteration rather than just decreasing the number of iterations? This option is mostly useful when you use a decimal number. For example, you could set it to .5, and the animation would start half way through. It takes two halves to make a whole, so if your iteration count is set to one and your iterationStart is set to .5, the animation will play from halfway through until the end of the animation, then start at the beginning of the animation and end in the middle! 

It is worth noting that you can also set the total number of iterations to less than one. For example:

var option = { iterations: .5, iterationStart: .5 }

This would play the animation from the middle until the end. 
endDelay: endDelay is useful if you want to string multiple animations after each other, but want there to be a gap between the end of one animation and the start of any subsequent ones. Here's a useful video to explain from Patrick Brosset.

Easing

Easing is one of the most important elements in any animation. WAAPI offers us two different ways to set easing?—?within our keyframes array or within our options object.
In CSS, if you applied animation-timing-function: ease-in-out you might assume that the start of your animation would ease in, and the end of your animation would ease out. In fact, the easing applies between keyframes, not over the entire animation. This can give fine-grained control over the feel of an animation. WAAPI also offers this ability.

var keyframes = [ { opacity: 0, easing: 'ease-in' }, { opacity: 0.5, easing: 'ease-out' }, { opacity: 1 } ]

It's worth noting that in both CSS and WAAPI, you shouldn't pass in an easing value for the last frame, as this will have no effect. This is a mistake a lot of people make.
Sometimes it's a lot more intuitive to add easing over an entire animation. This is not possible with CSS, but can now be achieved with WAAPI.

var options = { duration: 1000, easing: 'ease-in-out', }

You can see the difference between these two kinds of easing in this Pen:

See the Pen Same animation, different easing by CSS GRID (@cssgrid) on CodePen.

Ease vs Linear

It's worth noting another difference between CSS animation and WAAPI: the default of CSS is ease, while the default of WAAPI is linear. Ease is actually a version of ease-in-out and is a pretty nice option if you're feeling lazy. Meanwhile, linear is deadly dull and lifeless?—?a consistent speed that looks mechanical and unnatural. It was probably chosen as the default as it is the most neutral option. However, it makes it even more important to apply an easing when working with WAAPI than when working with CSS, lest your animation look tedious and robotic.

Performance

WAAPI provides the same performance improvements as CSS animations, although that doesn't mean a smooth animation is inevitable. 

I had hoped that the performance optimizations of this API would mean we could escape the use of will-change and the totally hacky translateZ? —? and eventually, it might. However, at least in the current browser implementations, these properties can still be helpful and necessary in dealing with jank issues.

However, at least if you have a delay on your animation, you don't need to worry about using will-change. The primary author of the web animations spec had some interesting advice over on the Animation for Work Slack community, which hopefully he won't mind me repeating here: 

If you have a positive delay, you don't need will-change since the browser will layerize at the start of the delay and when the animation starts it will be ready to go.

WAAPI Versus CSS Animations?

WAAPI gives us a syntax to do in JavaScript what we could already achieve in a stylesheet. Yet, they shouldn't be seen as rivals. If we decide to stick to CSS for our animations and transitions, we can interact with those animations with WAAPI.
 

Animation Object

The .animate() method doesn't just animate our element,? it also returns something. 

var myAnimation = element.animate(keyframes, options); Animation object viewed in a console

If we take a look at the return value in the console, we'll see its an animation object. This offers us all sorts of functionality, some of which is pretty self-explanatory, like myAnimation.pause(). We could already achieve a similar result with CSS animations by changing the animation-play-state property, but the WAAPI syntax is somewhat terser than element.style.animationPlayState = "paused". We also have the power to easily reverse our animation with myAnimation.reverse(), which again, is only a slight improvement over changing the animation-direction CSS property with our script.

However, up until now, manipulating @keyframes with JavaScript hasn't been the easiest thing in the world. Even something as simple as restarting an animation takes a bit of know-how, as Chris Coyier has previously written about. Using WAAPI we can simply use myAnimation.play() to replay the animation from the beginning if it had previously completed, or to play it from mid-iteration if we had paused it.

We can even change the speed of an animation with complete ease.

myAnimation.playbackRate = 2; // speed it up myAnimation.playbackRate = .4; // use a number less than one to slow it down getAnimations()

This method will return an array of any animation objects for any animations we've defined with WAAPI, as well as for any CSS transitions or animations.

element.getAnimations() // returns any animations or transitions applied to our element using CSS or WAAPI

If you feel comfortable and content using CSS for defining and applying your animations, getAnimations() allows you to use the API in conjunction with @keyframes. It's possible to continue to use CSS for the bulk of your animation work and still get the benefit of the API when you need it. Let's see how easy that is.

Even if a DOM element only has one animation applied to it, getAnimations() will always return an array. Let's grab that single animation object to work with.

var h2 = document.querySelector("h2"); var myCSSAnimation = h2.getAnimations()[0];

Now we can use the web animation API on our CSS animation :)

myCSSAnimation.playbackRate = 4; myCSSAnimation.reverse(); Promises and Events

We already have a variety of events triggered by CSS that we can utilise in our JavaScript code?: ?animationstart, animationend, animationiteration and transitionend. I often need to listen for the end of an animation or transition in order to then remove the element it was applied to from the DOM.

The equivalent of using animationend or transitionend for such a purpose in WAAPI would again make use of the animation object:

myAnimation.onfinish = function() { element.remove(); }

WAAPI offers us the choice of working with both events and promises. The .finished property of our animation object will return a promise that will resolve at the end of the animation. Here's what the example above would look like using a promise:

myAnimation.finished.then(() => element.remove())

Let's look at a slightly more involved example yanked from the Mozilla Developer Network. Promise.all expects an array of promises and will only run our callback function once all of those promises have resolved. As we've already seen, element.getAnimations() returns an array of animation objects. We can map over all the animation objects in the array calling .finished on each of them, giving us the needed array of promises.

In this example, it's only after all the animations on the page have finished that our function will run.

Promise.all(document.getAnimations().map(animation => animation.finished)).then(function() { // do something cool }) The Future

The features mentioned in this article are just the beginning. The current spec and implementation look to be the start of something great.

CSS Animations vs Web Animations API is a post from CSS-Tricks

An Introduction to the `fr` CSS unit

Css Tricks - Mon, 06/12/2017 - 2:23am

With all the excitement around CSS Grid, I haven't seen as much talk about the new fr CSS length unit (here's the spec). And now that browser support is rapidly improving for this feature, I think this is the time to explore how it can be used in conjunction with our fancy new layout engine because there are a number of benefits when using it; more legible and maintainable code being the primary reasons for making the switch.

To get started, let's take a look at how we'd typically think of building a grid in CSS. In the example below, we're creating a four column grid where each column has an equal width:

<div class="grid"> <div class="column"></div> <div class="column"></div> <div class="column"></div> <div class="column"></div> </div> .grid { display: grid; grid-template-columns: repeat(4, 25%); grid-column-gap: 10px; }

See the Pen CSS-Tricks: Grid Example 1 by Robin Rendle (@robinrendle) on CodePen.

If you've never seen that repeat() function after the grid-template-columns property then let me introduce you to one of the neatest features of CSS Grid! It's a shorthand, essentially, allow us to more succinctly describe repeating values. We could have written grid-template-columns: 25% 25% 25% 25%; instead, but it's cleaner using repeat(), particularly when you have more verbose widths (like a minmax() expression).

The syntax is essentially this:

repeat(number of columns/rows, the column width we want);

There are actually a couple of issues with what we've done so far, though.

First, in order to use this neat CSS function, we had to do a tiny bit of math. We had to think to ourselves what is the total width of the grid (100%) divided by the number of columns we want (4), which brings us to 25%. In this instance, the math is pretty darn easy so we don't have to worry about it but in more complex examples we can completely avoid doing the math and let the browser figure that out for us. We do have calc() available to us, so we could have done repeat(4, calc(100% / 4), but even that's a little weird, and there is another problem anyway...

The second issue is a problem with overflow. Because we've set each column to 25% and a grid-column-gap to 10px then that pushes grid element wider than 100%. It isn't how you'd expect things to work from just looking at the code above but that's how percentages work. What we're really saying with the code above is "set each column to 25% the width of the viewport and have a 10px gap between them." It's a subtle difference, but it causes a big issue with layout.

We've inadvertently caused some horizontal scrolling here:

See the Pen CSS-Tricks: Grid Example 1 by Robin Rendle (@robinrendle) on CodePen.

This is where the fr unit can help us.

The fr unit (a "fraction") can be used when defining grids like any other CSS length such as %, px or em. Let's quickly refactor the code above to use this peculiar new value:

.grid { display: grid; grid-template-columns: repeat(4, 1fr); grid-column-gap: 10px; }

See the Pen CSS-Tricks: Grid Example 1b by Robin Rendle (@robinrendle) on CodePen.

That will look just the same as the example above because in this instance we're setting each of our four columns to one fraction (which happens to be 1/4 or 25%). But! There's no overflow on the x-axis anymore because setting each column to 1fr takes that 10px into account automatically and subtracts it from the total width available for each column.

Why the heck should I learn how to use this fancy new CSS length if I can mostly stick to the units like percent or pixels, you wonder? Well, let's dig into a more complex CSS Grid example to explain why fr is a better alternative. In a new example, let's say we want our navigation on the left followed by a twelve column grid which should look like this:

This is a pretty typical scenario for a lot of UIs and so using the fr unit prevents us from either making a separate grid div or fumbling about with calc. Because if we didn't use fr in the example above then we'd somehow have to figure out the following:

the width of each column = ((width of viewport - width of nav) / number of columns) * 1%

That's possible for sure, it's just awfully painful to read, and if we changed the width of the nav then we'd have to do that dumb calculation all over again. Instead, the fr unit tidies all of that up into a super readable line of code:

.grid { display: grid; grid-template-columns: 250px repeat(12, 1fr); grid-column-gap: 10px; }

See the Pen CSS-Tricks: Grid Example 2 by Robin Rendle (@robinrendle) on CodePen.

What we're doing here is setting a fixed width in pixels for the first column and then creating twelve separate columns which are set at one "fraction of the free space" (literally how the spec phrases it). But there's no crazy calculations or anything! It's super readable and if the width of that left nav changes then the width of our columns on the right will adjust themselves automatically.

With just a little bit of legwork we've made our interface more maintainable for the future and we've ensured that our code is more legible for the next developers that are coming up behind us.

Information from other folks

Some of the fun and power of the fr unit comes from mixing it with other units. Imagine a fixed sidebar and main content area that takes up the rest of the space: grid-template-columns: 200px 1fr; easy!

Here's an example from Alligator.io showing mixed units nicely:

Rachel Andrew has a video specifically about fr:

Anna Monus has a very good article on fr.

Yay for the fr unit!

An Introduction to the `fr` CSS unit is a post from CSS-Tricks

A Little Example of Data Massaging

Css Tricks - Sun, 06/11/2017 - 9:15am

I'm not sure if "data massaging" is a real thing, but that's how I think of what I'm about to describe.

Dave and I were thinking about a bit of a redesign for ShopTalk Show. Fresh coat of paint kinda thing. Always nice to do that from time to time. But we wanted to start from the inside out this time. It didn't sound very appealing to design around the data that we had. We wanted to work with cleaner data. We needed to massage the data that we had, so that it would open up more design possibilities.

We had fallen into the classic WordPress trap

Which is... just dumping everything into the default content area:

We used Markdown, which I think is smart, but still was a pile of rather unstructured content. An example:

If that content was structured entirely differently every time (like a blog post probably would be), that would be fine. But it wasn't. Each show has that same structure.

It's not WordPress' fault

We just didn't structure the data correctly. You can mess that up in any CMS.

To be fair, it probably took quite a while to fall into a steady structure. It's hard to set up data from day one when you don't know what that structure is going to be. Speaking of which...

The structure we needed

This is what one podcast episode needs as far as structured data:

  • Title of episode
  • Description of episode
  • Featured image of episode
  • MP3
    • URL
    • Running Time
    • Size in Bytes
  • A list of topics in the show with time stamps
  • A list of links
  • Optional: Guest(s)
    • Guest Name
    • Guest URL
    • Guest Twitter
    • Guest Bio
    • Guest Photo
  • Optional: Advertiser(s)
    • Advertiser Name
    • Advertiser URL
    • Advertiser Text
    • Advertiser Timestamp
  • Optional: Job Mention(s)
    • Job Company
    • Job Title
    • Job URL
    • Job Description
  • Optional: Transcript
Even that's not perfect

For example: we hand-number the episodes as part of the title, which means when we need that number individually we're doing string manipulation in the templates, which feels a bit janky.

Another example: guests aren't a programmatic construct to themselves. A guest isn't its own database record with an ID. Which means if a guest appears on multiple shows, that's duplicated data. Plus, it doesn't give us the ability to "display all shows with Rebecca Murphey" very easily, which is something we discussed wanting. There is probably some way to program out way out of this in the future, we're thinking.

Fortunately, that structure is easy to express in Advanced Custom Fields

Once you know what you need, ACF makes it pretty easy to build that out and apply it to whatever kind of page type you need to.

I'm aware that other CMS's encourage this kind of structuring by default. Cool. I think that's smart. You should be very proud of yourself for choosing YourFavoriteCMS.

In ACF, our "Field Group" ended up like this:

We needed "Repeater" fields for data like guests, where there is a structure that needs to repeat any number of times. That's a PRO feature of ACF, which seems like a genius move on their part.

Let the data massaging begin

Unfortunately, now that we had the correct structure, it doesn't mean that all the old data just instantly popped into place. There are a couple of ways we could have gone about this...

We could have split the design of show pages by date. If it was an old show, dump out the content like we always have. If it's a new show, use the nice data format. That feels like an even bigger mess than what we had, though.

We could have tried to program our way out of it. Perhaps some scripts we could run that would parse the old data, make intelligent guesses about what content should be ported to the new structure, and run it. Definitely, a non-trivial thing to write. Even if we could have written it, it may have taken more time than just moving the data by hand.

Or... we could move the data by hand. So that's what we ended up doing. Or rather, we hired someone to move the data for us. Thanks Max! Max Kohler was our data massager.

Hand moving really seemed like the way to go. It's essentially data entry work, but required a little thought and decision making (hence "massaging"), so it's the perfect sort of job to either do yourself or find someone who could use some extra hours.

Design is a lot easier with clean and structured data

With all the data nicely cleaned up, I was able to spit it out in a much more consistent and structured way in the design itself:

This latest design of ShopTalk Show is no masterpiece, but now that all this structural work is done, the next design we should be able to focus more on aesthetics and, perhaps, the more fun parts of visual design.

A Little Example of Data Massaging is a post from CSS-Tricks

CSS-Tricks Chronicle XXXI

Css Tricks - Sat, 06/10/2017 - 6:52am

All the latest happenings! As I like to do, I round up a bunch of things that have happened in the past few months around here on this site, over at CodePen and ShopTalk, and other sites where I got to be a guest or was involved somehow. There has been some big releases, some redesigns, and a bunch of guest podcasts.

I got to be a guest on Relative Paths with Mark Phoenix and Ben Hutchings. It was episode 47 and the topic was dogmatism, a topic I weighed in on earlier with my post My Increasing Wariness of Dogmatism.

The biggest release ever on CodePen is CodePen Projects. It hasn't even been out three months yet! As opposed to Pens on CodePen, Projects gives you an editor that is more of a full-on IDE with your own file system.

I was a guest on Eric Siu's podcast Growth Everywhere, Episode 196 where we talk numbers and growth stuff. (Fair warning on the link: it's pretty pop-up heavy.)

I also had a lot of fun on the Email Design Podcast, Episode 60, where I got to chat with Kevin Mandeville and Jason Rodriguez specifically about email stuff. That's not something I get to talk about much, but I actually find myself doing quite a bit lately with email, and it's a very weird world that somehow feels completely different than "normal" front-end development.

I have moved back home to Milwaukee, after spending the last 7 months in Miami. Bittersweet! Farewell, friends old and new in Miami. Hello, friends old and new in Milwaukee.

We're less than a month away from the 10 year anniversary of CSS-Tricks! We'll definitely do something. No rooftop party or anything, but definitely come see what we got on July 4th.

I redesigned my personal site. It's nothing special to look at, but I think it's going to serve my needs very well. The new site needed to clearly show: this is who I am, this is what I do, this is where I exist other places on the web, and most importantly, these are the things I want you to do.

The most fun little bit is the radio buttons by the bio area, which allow you to customize the length, first person or third, and what format it's in.

ShopTalk also has a brand new website. Also designed and implemented by me, so, brace yourselves for utilitarian. This one was driven by backend data. I think I'll write about that soon.

We recently did a podcast called On Podcasting where I got to chat with Chris Enns about podcasting equipment. I figured it was about time to get some advice and update my gear. I bet if you factor in all the ShopTalk, CodePen Radio, guest appearances, and videos I've recorded, I'm around 1,000 episodes of stuff. Probably about time I have some decent gear. I pulled the trigger on the major upgrade. I'll have to post about that soon as well.

My public speaking schedule for the rest of the year is:

CSS-Tricks Chronicle XXXI is a post from CSS-Tricks

Sites We Like: The Morning News & iGNANT

Nice Web Type - Fri, 06/09/2017 - 3:36pm

Whether the content is long-form or in brief, or a mix of both, it takes a talented designer to pull together a page that invites readers to stay a while — and to check back often.

Both of these online publications do a great job with this, keeping the type in perfect balance with the overall aesthetic.

The Morning News

The Morning News features articles and essays by its own staff and contributors along a regular stream of headlines from publications across the web, with an understated page design that puts the content (and its readers) first.

The sans-serif face used on the homepage and in navigation throughout is JAF Facit, with different weights in play to keep the page feeling vibrant. Compare block quotes to the headlines to see how the lighter weight has a voice completely distinct from the mid-range weights. Body text for the articles is set in Minion, a classic from Adobe for long-form text, and frequently spotted in book typesetting.

iGNANT

Devoted to art, fashion, design, and all things creative, iGNANT is a source of inspiration for people across virtually all artistic disciplines.

Navigation and subheads are set in Futura PT, which not only reads well at those small sizes but provides a lovely sense of balance on the page with its geometric forms. Shown large and proud in headlines and body text is Adobe Garamond, with generous line spacing giving an especially graceful feel to those tall ascenders.

That’s it for sites this time. Share sites you like in the comments!


The Equilateral Triangle of a Perfect Paragraph

Css Tricks - Fri, 06/09/2017 - 2:11am

Still, too many web designers neglect the importance of typography on the web. So far, I've only met a few that really understand typography and know how to apply that knowledge to their work. And the lack of knowledge about typography doesn't come from ignorance. I learned that web designers are commonly either self-taught and haven't grasped the importance of typography yet, or they actually studied design but typography was just one of the classes they had to attend.

I created the Better Web Type course to help raise awareness of the important role typography plays on the web. In my opinion, both web designers and web developers should learn the basics—if a designer uses ligatures in her designs but the developer doesn't even know what ligatures are, how can we expect him to correctly transform the most beautifully designed typography into code? With both roles knowing the basics, we'll be able to start contributing to a better web by producing better web typography. Together.

One of the most important things in typography is to shape a seamless reading experience that invites the reader and presents the content in an objective way. To do that, we need to be able to shape perfect paragraphs. There are three keys to doing that, as Josef Mueller-Brockmann—a renowned 20th-century typographer and visual communicator, put it:

The reader should be able to read the message of a text easily and comfortably. This depends to a not inconsiderable extent on the size of the type, the length of the lines and the leading (line-height).

—Josef Mueller–Brockmann

When I was writing the lessons for the Better Web Type course, I was trying to think of a simple concept that would illustrate exactly that: font-size, line-height, and length of lines need to be in perfect balance. I struggled for a while but then, out of nothing, it hit me—the equilateral triangle. All three sides of an equilateral triangle are equal and so are all the three angles. A great representation of three things in perfect balance. It turned out that it was easy to apply this idea to the three keys that shape a perfect paragraph. But first, let's take a look at each of them in isolation.

Type size & color

One of the mistakes that most websites make is that they make the main body text too small. Back in the early 2000s it was common practice to set the body text to a size of around 11px. But back then the screens were smaller with lower screen resolutions. 11px then was larger than it is now.

A common rule for setting the body text is to set it to a size that would match the size of the text in a book at an arm's length distance.

Matching the font size with the text size from a book at an arm's length. (Source: iA)

The recommended size for today's screens is 16px for mobile and from 18 to 22px for desktop. This also depends on the typeface. Some typefaces set at 16px may seem larger than others, as it's clear in the picture below.

The same text set in Merriweather seems larger and heavier than when set in Georgia.

"Type color" in typography doesn’t mean actual color like red, blue or green. Type color means how heavy black type on a light background looks like. A typeface of the same size and same weight may seem heavier than another one. The contrast of the Merriweather typeface in the example above is lower (comparing the serifs and different stems) than the one of Georgia on the right. That makes it look heavier and the color of the type is darker.

Length of lines

Reading very long lines of text is tiring for our eyes. Our eyes need constant pauses which are provided by line breaks. In that instance of switching to the next line, they get that short pause. Short, but long enough to keep them going for longer. It's like an engine that doesn't run on full power all the time so it keeps going longer without overheating.

The ideal length of a line of text is from 45 to 75 characters—including spaces. Anything that reaches far from that range becomes hard to read. Too much line switching when lines are too short and too few breaks for the eyes when they're too long.

Recommended line length in Google's Material Design Guidelines (Source: material.io) Line-height (leading)

Line-height, or leading as it's usually referred to in print, is the pillar of vertical rhythm in typography. Understanding how it works is key to producing better typography for the web.

Line-height (leading) works differently on the web. It's evenly distributed below and above the line of text.

Too many designers or web developers that I've met think of line-height as an isolated text feature. So they tend to set it based on what seems right. Consequently, line-height is something that gets set without too much consideration. But line-height is too important to be set so mindlessly. The length of the lines affects the line-height. The longer the lines, the more space between them is required. Type size affects the line-height. The larger it is, the larger the line-height should be as well.

Type color affects line-height too. Darker and heavier type requires more space between the lines. And in the end, the typeface itself may affect it as well. As we've seen earlier, some typefaces seem larger than others. Some, mostly serifed typefaces, will seem heavier. Those will require more space between the lines too.

Now that you know that line-height is very important and that it should never be considered as an isolated feature, let's look at general best practices. For paragraphs, ideal line-height is usually between 1.3 and 1.7. So a body text set at 16px should have a line-height from 21 to 26px. This will depend on the things mentioned earlier: typeface design, type size, weight etc.

Same typeface, same font-size, different color. Darker text should have larger line-height. Same typeface, same font size, same color, different line length. The longer the line of text, the larger the line-height required. Different typefaces, same font size, same color. Typefaces that seem larger and heavier need more line-height. The equilateral triangle

There are some general best practices in typography but they're never definite. As we saw with the line-height, we're commonly presented with ranges that we should use. What to choose from that range takes time to learn. The eye needs time to get sharper in noticing these differences.

And so we come to what I call The Equilateral Triangle of a Perfect Paragraph. We've looked at type size, measure and line-height in isolation (as much as we could). By doing so, we already learned that these properties are interconnected. They can't be considered in isolation and they never should be. That's why an equilateral triangle is a perfect representation of a well-designed paragraph of text. For it, we need a good type size that matches the measure, which matches the line height. Get one of them wrong and your triangle will get skewed.

Unfortunately, I can't give you definite numbers of a perfect paragraph as there are millions of combinations out there. But I can give you a few examples that will help you train your eye. Try to pay attention to the details and compare the type size, the lengths of the lines and the spacing between them.

A perfectly balanced paragraph represented by an equilateral triangle.

In the example above, the text is set in Merriweather — an uncommonly large and heavy typeface. That's why it's set to 14px. The line-height is, therefore, closer to the upper edge of the recommended range of 1.3 to 1.7. The paragraph above neatly fits 55 characters per line (also in the recommended range).

Lines of text in this paragraph are too long. The triangle isn't equilateral. To fix this we'd need to either make the type size and line-height larger or decrease the length of the line.

In the example above, the length of the lines is outside the recommended range of 55–75. It's 84 on average, meaning that the bottom side of the triangle expands on both sides. The triangle is not equilateral anymore.

The line-height of this paragraph is too large. The lines of text are starting to drift apart. This example could be improved if we increased the length of the lines. To properly fix it we'd also need to increase the font size to balance it out. A simpler way to fix it would be to decrease the line-height.

The line-height in the example above is obviously too large. It's set to 2 and very much outside the recommended range. The lines of text are too far apart. Unfortunately, this happens too often on the web. It seems like there's a trend of setting type to a light gray color and applying a large line-height on top of it. It's an attempt to get that clean, minimalistic style but it's wrong. Texts like that, especially if long, are hard to read.

The font-size is way too large in this paragraph. It forces the length of the lines to 30 characters and completely skews the triangle. The lines of text are too close together. To fix this we'd either have to decrease the size of the text or increase both line-height and the length of the lines.

Fortunately, I don't encounter this on the web too often. I still wanted to include it, so we cover all the possible options that can come out of this concept. The font size in the example above is way too large for the given line-height and length of the lines. Texts set like this are extremely hard to read.

The learning game

Many of the students of the Better Web Type course have asked me to elaborate on this concept. And what's the best way to illustrate how this concept works in action? A learning game that will help you sharpen your eyes. Presenting "The Equilateral Triangle of a Perfect Paragraph — A Web Typography Learning Game".

The game consists of ten levels, each asking you to set one of the three properties: font size, line-height or the length of the lines. At the end, you get a score out of 100 and a chance to participate in the giveaway of the upcoming book Better Web Typography for a Better Web. Four, randomly chosen, highest scores will get a free book, which is based on the popular Better Web Type course.

I hope that as many people as possible come across this game and learn that line-height, font-size, and length of lines always need to be considered together. Try to get the best result and challenge your friends. The more people know about this, the better the web typography will get.

Check out the game

The Equilateral Triangle of a Perfect Paragraph is a post from CSS-Tricks

An intro to web components with otters

Css Tricks - Fri, 06/09/2017 - 2:10am

Monica Dinculescu on web components and why we might care:

... before web components came around, you had to wait on all browsers to agree on a new element (like, a date picker). And even after they agreed on a new element, it took them yeaaaaars to implement it... With web components, web developers get to write such elements, so that you don't have to wait for 10 years before all browsers agree that they should implement a date picker.

I struggle with trying to figure out if web components (with Polymer or not) are really "the future" or not. People definitely haven't adopted them with the same ferocious appetite as New JavaScript, which also tackles componentization. But they are a web standard with growing native support, so... probably?

I'm probably wrong in conflating the two, though. Even the React Docs say:

React and Web Components are built to solve different problems. Web Components provide strong encapsulation for reusable components, while React provides a declarative library that keeps the DOM in sync with your data. The two goals are complementary.

Direct Link to ArticlePermalink

An intro to web components with otters is a post from CSS-Tricks

Women speakers and attendees at Amsterdam web conferences

QuirksBlog - Fri, 06/09/2017 - 1:59am

As a slight contribution to the diversity in web development discussion, here are the ratios of female attendees and speakers from the Amsterdam web conferences Krijn and I organised or are close to. I’m not sure what these numbers mean, but someone will surely have a bright idea after staring at them for long enough.

function init() { var trs = document.querySelectorAll('#women tbody tr'); for (var i=0,tr;tr=trs[i];i+=1) { var att = parseInt(tr.querySelectorAll('td')[2].textContent); var sp = parseInt(tr.querySelectorAll('td')[3].textContent); tr.appendChild(document.createElement('td')).textContent = (sp/att).toFixed(1); } }

Krijn gathered the crucial attendee numbers, while I added the speakers, a calculation, and some general remarks.

Before we show the numbers, here are some caveats:

  1. Attendee numbers include all speakers.
  2. We only counted an attendee as a woman if we’re 100% certain. Some names are inconclusive, and we counted those as men.
  3. Ratio is the ratio of female speakers and female attendees, both in percentages. Thus, a high ratio means there are relatively more women on stage than in the audience.
  4. Fronteers 2017 ticket sales have not yet concluded; the number of female attendees may yet go up.
  5. My apologies for Fronteers 2008. In my defence: diversity was not in fashion yet back then, and I had no clue what I was doing anyway. The miracle was that the conference took place at all.
Women at Amsterdam web development conferences Date Name Attendees Speakers Ratio 0809 Fronteers 2008 6% 0% 0910 Fronteers 2009 6% 13% 1010 Fronteers 2010 10% 12% 1110 Fronteers 2011 11% 21% 1210 Fronteers 2012 11% 11% 1310 Fronteers 2013 12% 22% 1410 Fronteers 2014 9% 17% 1510 Fronteers 2015 10% 33% 1610 Fronteers 2016 10% 38% 1710 Fronteers 2017* 12% 50% 1306 CSS Day 2013 10% 25% 1406 CSS Day 2014 10% 13% 1506 CSS Day 2015 15% 50% 1606 CSS Day 2016 18% 31% 1706 CSS Day 2017 16% 31% 1411 dsgnday 2014 19% 50% 1511 dsgnday 2015 24% 50% 1105 Mobilism 2011 11% 12% 1205 Mobilism 2012 7% 5% 1305 Mobilism 2013 11% 12% 1503 Mobilism 2015 11% 25%

Now what can we glean from these numbers? The number of female attendees varies between 6 and 24%, and they form a higher percentage in later years, although it partly depends on the conference topic. dsgnday scored really well, while Mobilism fell somewhat short.

The ratio column really asks more questions than it answers; specifically: what SHOULD the share of female speakers be? If the share of female attendees is the benchmark, then all but three of these conferences overshot their mark, sometimes considerably.

The other view would be that there should be 50% woman speakers. Although that sounds great I personally never believed in this argument. It’s based on the general population instead of the population of web developers, and if we’d extend that argument to its logical conclusion then 99.9% of the web development conference speakers should know nothing about web development, since that’s the rough ratio in the general population.

Make of that what you wish. Your mileage (kilometrage?) will vary.

Historical female ratios

This raises the question: what is the share of women in web development anyway? Right now my answer is 15-20%.

The table above is one of my sources. Another is my recollection of my first job, when between 1999 and 2002 I was lead front-end engineer of a team of, to the best of my memory, 13 front-end developers, of which four were women.

Since I had no clue if my experiences were extraordinary or pretty mainstream, I recently asked on Twitter. Based on the replies I conclude that my 30% score was on the high end, and that the average back then was slightly lower. 20-25%?

For many years A List Apart ran an annual survey. I was able to find gender data for three years: 16% women in 2007, 17% in 2009, and 18% in 2011. This sketches a somewhat darker picture, but we can conclude there have always been women in web development — though not as many as we’d like.

So after squinting at these bits of data and doing heuristics (also called AI nowadays), I arrived at my guesstimate of 15-20%. If anyone has more data to share, especially data that does not conform to this picture, now would be a great time to publish it.

World Wide Web, Not Wealthy Western Web

Css Tricks - Fri, 06/09/2017 - 1:58am

Bruce Lawson explores many of the misconceptions that web designers might have when building websites. The crux of his argument is that we should be focusing on designing for users that are just getting online and for those that have frustratingly slow internet connection speeds.

He even makes a bold prediction:

Many of your next customers will come from the area circled below, if only because there are more human beings alive in this circle than in the world outside the circle.

Direct Link to ArticlePermalink

World Wide Web, Not Wealthy Western Web is a post from CSS-Tricks

Building a Directory with the Twitter API

Css Tricks - Thu, 06/08/2017 - 6:11am

Last month, designer Helen Tran asked people to name five women designers that they thought were valuable to the industry:

I need you to help me out.
Name five female Designers you think are really valuable + important to our industry.

— Helen Tran (@tranhelen) April 26, 2017

In just a few days, the tweet generated over 373 replies that named 636 women by their Twitter usernames specifically. A few people were joking around that the thread was a great place to find potential recruits and I realized I had a different need: I'm not trying to recruit anyone but I'd love to find women to follow who are excelling at things that I'm interested in or trying to learn more about. Since many people put their job title or area of expertise in their Twitter descriptions, I realized I could create a self-reported filtering system by searching each description for keywords and then display the profiles in a sortable directory.

I decided to use the thread as a jumping off point and create a free-standing website: Women Who Design. Here's how I built it.

Getting Started

To start, I needed to:

  1. Record each person's Twitter username, also known as their "handle," from the original thread on a list.
  2. Set up the project's file structure.
  3. Get the Twitter REST API to return profile information from a given handle.
  4. Choose a database to store each handle and its corresponding profile information.

The first step, getting the handles, was the easiest. With a good playlist on in the background, I spent about an hour combing the Twitter thread and entered each handle into a spreadsheet, which I then exported as a JSON file called `designers.json`.

At this point, I initialized my git repo and set up a basic file structure:

  • index.html
  • app.js
  • styles.css
  • designers.json

At the top of my `app.js` file, I imported all the designers from the original Twitter thread.

var designers = require('./designers.json');

Next up, I registered my app with Twitter to start working with the REST API.

I chose to configure the project as a read-only application since I was only planning to make use of the GET users/show endpoint, which supplies user profile information.

Then I installed an asynchronous client library for Twitter (also called Twitter) through the command line to enable me to make calls to the API.

npm install twitter

To use the library in my project, I also had to require it at the top of my `app.js` file.

var twitter = require('twitter');

According to the client library documentation, I would need to enter my app's consumer key, consumer secret and a bearer token in my `.js` file after requiring "twitter".

var client = new Twitter({ consumer_key: '', consumer_secret: '', bearer_token: '' });

The key and secret were easily found in my Twitter app dashboard, but the bearer token required an extra step. I ran the following command in the command line to get the bearer token, filling in the variables with my credentials from the dashboard, then I added the result to the client variable above.

curl -u "$CONSUMER_KEY:$CONSUMER_SECRET" \ --data 'grant_type=client_credentials' \ 'https://api.twitter.com/oauth2/token'

The client library also provided a handy convenience method for making requests, so I added it to my app.js file with a note to fill it in later. According to the GET users/show endpoint documentation, I would need to pass each handle from my list to the "screen_name" parameter to get the profile information I was looking for.

client.get('users/show', {'screen_name': handle}, function(error, response) { if (!error) { console.log(response); // do stuff here later! } });

If done correctly, I could expect the response to look something like this:

{ "id": 2244994945, "id_str": "2244994945", "name": "TwitterDev", "screen_name": "TwitterDev", "location": "Internet", "profile_location": null, "description": "...",

Finally, I had to choose a database to store the profiles. I landed on the Realtime Database from Firebase, because it's a NoSQL database that uses JSON as its storage format. I installed firebase and firebase-admin on npm, then required them at the top of my `app.js` file alongside everything else.

var firebase = require('firebase'); var admin = require('firebase-admin');

To get writing to and reading from the database working, I had to authenticate Firebase using a specially generated "Service Account" private key. I generated the key in the Service Accounts tab of my Firebase settings, and dropped the corresponding code below the rest of my configurations.

var serviceAccount = { "type": "service_account", "project_id": process.env.WWD_FIREBASE_PROJECT_ID, "private_key_id": process.env.WWD_FIREBASE_PRIVATE_KEY_ID, "private_key": process.env.WWD_FIREBASE_PRIVATE_KEY, "client_email": process.env.WWD_FIREBASE_CLIENT_EMAIL, "client_id": process.env.WWD_FIREBASE_CLIENT_ID, "auth_uri": "https://accounts.google.com/o/oauth2/auth", "token_uri": "https://accounts.google.com/o/oauth2/token", "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs", "client_x509_cert_url": process.env.WWD_FIREBASE_CLIENT_CERT_URL }; Working with the data

Phew! List recorded: check. App registered: check. File structure set up: check. Database setup: check. Time to actually start using the API.

I took a look at the sample response in the GET users/show endpoint documentation and I determined I’d need to store the user’s name, handle (referred to in the docs as screen_name), location, description, and profile image URL. I also wanted to save the profile color each user set on Twitter to use as a highlight color for buttons, links, and other accents, so I put the following code inside the client library convenience method I had saved in my `app.js` file.

var name = response.name; var handle = response.screen_name; var description = response.description; var imageUrl = response.profile_image_url_https; var location = response.location; var profileColor = response.profileColor;

To prevent situations where differences in capitalization would result in two instances of the same profile (like @julesforrest vs @JulesForrest), I set the handle variable to lowercase.

handle = handle.toLowerCase();

Then I noticed the profile images being returned were way too small to be useful on a desktop display. By tinkering with the image URL, I found a larger image path that worked better for my needs.

imageUrl = response.profile_image_url_https.replace("_normal", "_400x400"); Linking the Links

People often have URLs, @handles, #hashtags and email addresses in their profile descriptions, but unfortunately, the Twitter API was returning each description as a simple string. Luckily, I found a tool called Autolinker that would search each string and build out anchor tags in the appropriate places.

To get it working, I installed it via npm, then required it at the top of my app.js file:

npm install autolinker --save var Autolinker = require( 'autolinker' );

Basic usage looked pretty straightforward and it came with a few options out of the box that could be passed as an object to the second parameter, like opening each link in a new window or adding a class to each anchor tag.

var linkedText = Autolinker.link( textToAutolink[, options] );

However, I wanted to add each user’s custom profile color (saved earlier from the API response) as an inline style on that user’s links, which required writing a custom replacement function. While custom replacement functions can handle some pretty complex configurations, I only tweaked the example from the documentation slightly to add the inline color styles and open each link in a new window. It’s important to note the social network for mentions and hashtags needs to be specified at the top of object parameter to link them properly, which wasn’t immediately clear from the docs.

description = Autolinker.link( description, { mention: 'twitter', hashtag: 'twitter', replaceFn : function( match ) { switch( match.getType() ) { case 'url' : var tag = match.buildTag(); tag.setAttr( 'style', 'color: #' + profileColor ); return tag; case 'mention' : var mention = match.getMention(); return `<a href="https://twitter.com/${mention}" target="blank" style="color: #${profileColor}">@${mention}</a>`; case 'email' : var email = match.getEmail(); return `<a href="mailto:"${email}" target="blank" style="color: #${profileColor}">${email}</a>`; case 'hashtag' : var hashtag = match.getHashtag(); return `<a href="https://twitter.com/hashtag/${hashtag}" target="blank" style="color: #${profileColor}">#${hashtag}</a>`; } } });

Frustratingly, though, Twitter's link-shortened t.co URLs were showing up as the text of the anchor tag instead of the descriptive URL.

Several hours of debugging later, I finally noticed that the t.co URLs and not the descriptive URLs were in the original strings returned by the API all along. After re-examining the sample response, I found a description.urls object that I had missed earlier and logged that as well, replacing the t.co URL text with the appropriate descriptive URL text.

var descriptionUrls = response.entities.description.urls; if (descriptionUrls.length != 0) { for (var i = 0; i < descriptionUrls.length; ++i) { description = description.replace(descriptionUrls[i].url, `${descriptionUrls[i].display_url}`); } } Searching for Filters

People get the most of Twitter when they're following people who are relevant to them in some way, so it had to be easy for directory users to sort the profiles by position or area of expertise. For example, as someone who’s interested in front-end development, I'd like to find which women in the directory identified themselves as developers.

Adding those sortable filters was the last and most important step to complete before writing each profile to Firebase and I spent a lot of time thinking about the right way to handle the problem. I knew I could create a self-reported filtering system by searching each profile description for keywords, but many of the terms people had in their descriptions overlapped with one another (product designer and UX designer, developer, and engineer). Ultimately, I decided that it was important to use the exact terms people used to describe themselves, even if it meant more clicks for the directory user.

To choose the filter categories, I looked for terms that showed up most frequently across the descriptions as a whole and wrote a function to search for terms and push appropriate tags to an array. I planned to later use the array on the front end to make the filtering work.

var designerTagsArray = []; function addDesignerTags(handle, searchTerm, tag) { if ((description.toUpperCase()).includes(searchTerm) === true) { designerTagsArray.push(tag); }; }; addDesignerTags(handle, "PRODUCT DESIGN", "product "); addDesignerTags(handle, "LEAD ", "lead "); addDesignerTags(handle, "MANAGER", "manager "); // etc, etc

For certain terms like "director", I had to do a custom search to weed out similar phrases with significantly different meanings like art director or creative director.

if ((description.toUpperCase()).includes("DIRECTOR") === true) { if ((description.toUpperCase()).includes("ART DIRECTOR") === true) { // do nothing } else if ((description.toUpperCase()).includes("CREATIVE DIRECTOR") === true) { // do nothing } else { designerTagsArray.push("director"); }; };

When the filtering was done, I stringified the array and removed any extra characters:

designerTagsArray = JSON.stringify(designerTagsArray); designerTagsArray = designerTagsArray.replace(/[^\w\s]/gi, ''); Writing to Firebase

It was time to upload all the data from the original list. First, I created a new object to hold all the information I needed to write to Firebase:

var designerProfile = new Object();

Assigned all items:

designerProfile.name = name; designerProfile.handle = handle; designerProfile.description = description; designerProfile.imageUrl = imageUrl; designerProfile.imageUrlMobile = imageUrlMobile; designerProfile.profileColor = profileColor; designerProfile.designerTags = designerTagsArray;

And wrote a function to add them to an object I named display on Firebase:

function writeToFirebase(handle, designerProfile) { firebase.database().ref('display/' + handle).set({ designerProfile }); console.log(handle + " has been written to Firebase!"); }; writeToFirebase(handle, designerProfile);

Up until this point, all the code I had written was contained inside the original convenience method from the Twitter client library:

client.get('users/show', {'screen_name': handle}, function(error, response) { if (!error) { console.log(response); // log relevant data // lowercase handle // adjust image URL // add links to descriptions // search and add tags // write to Firebase } });

I wrapped the convenience method inside a function called getProfileInfo, which accepted a Twitter handle as a parameter.

var getProfileInfo = function(handle) { client.get('users/show', {'screen_name': handle}, function(error, response) { if (!error) { console.log(response); // log relevant data // lowercase handle // adjust image URL // add links to descriptions // search for tags // write to Firebase } }); };

Then I wrote a loop to cycle through each handle from the JSON file of the original list, which I'd imported earlier to the top of my app.js file.

for (var i = 0; i < designers.length; ++i) { getProfileInfo(designers[i].handle); };

Finally, I ran the script in the command line using Node and got all the profile data showing up in Firebase.

node app.js The Front End

While ironing out the data kinks, I was also working on a simple front-end that read profiles from the database and built them out in HTML using jQuery. I created about and nomination pages as well, with a form on the nomination page to capture new submissions.

To get the form working, I grabbed the inputted text from each of the input fields and added them to a new Firebase object I named submit for later review.

var designerDatabase = firebase.database(); $('#designer-submission').submit(function(event){ event.preventDefault(); var handle = $('#handle').val(); var reason = $('#reason').val(); designerDatabase.ref('submissions/' + handle).set({ handle: handle, reason: reason }); $('#handle').val(''); $('#reason').val(''); });

All in all, I ended up with a client-side `.js` file, three `.html` files, a logo `.svg` file and a `.css` file.

Getting it Live

When the basic interactions were all coded, I decided it was time to try getting the project up on Heroku. Since most of the app was built on server-side Node, I needed a tool called Express.js to publish it as an actual site. To do that, I had to set up my `package.json` file.

npm init

After asking a bunch of questions about the name and version number of my app, it prompted me to specify an entry point, which I left as the default: index.js.

entry point: (index.js)

Then I installed Express:

npm install express --save

After that, I set up my index.js file, which looked like this:

var express = require('express'); var app = express(); app.use(express.static('public')); app.get('/', function (req, res) { res.sendFile('index.html'); }); app.listen(process.env.PORT || 3000, function () { console.log('Example app listening on port 3000!'); });

To serve all my client-side files properly, I moved them all to a folder called public. I set up a remote Heroku repo and pushed the code.

git push heroku master Adjusting the back-end structure

Once everything else was up and running, I had to change my setup a bit because I wanted to use the getProfileInfo function to both refresh existing profiles and write entirely new profiles for designers submitted through the site.

I nixed my app.js file and saved the getProfileInfo function as a module.export called getprofileinfo.js<code> to use in two newly created scripts: display.js and submit.js. Then I required the module at the top of the display script and used the Heroku scheduler add-on to run it every 24 hours to refresh the data of existing profiles on Firebase.

var getProfileInfo = require('./getprofileinfo.js'); function getDisplayedDesigners() { firebase.database().ref('display/').on('value', function (results) { var allDisplayedDesigners = results.val(); for (var designer in allDisplayedDesigners) { getProfileInfo(designer); }; }); } getDisplayedDesigners();

The submit script was a little different. I wanted to browse the submissions manually to remove any troll responses or ineligible people, then automatically add the remaining submissions to the display object and remove them from the submit object. I also had to account for people who might include the @ symbol in the handle field of the form when submitting.

var getProfileInfo = require('./getprofileinfo.js'); function getSubmittedDesigners() { firebase.database().ref('submissions/').on('value', function (results) { var allSubmittedDesigners = results.val(); for (var designer in allSubmittedDesigners) { var handle = designer; if (handle.includes("@") === true) { handle = handle.replace("@", ""); getProfileInfo(handle); firebase.database().ref('submissions/' + "@" + handle).remove(); } else { getProfileInfo(handle); firebase.database().ref('submissions/' + handle).remove(); }; }; }); } getSubmittedDesigners();

With this setup, I could run the submit.js in the command line and process all the eligible submissions at once.

node submit.js Launch!

On May 15, I officially launched my first-ever app: Women Who Design. In the first 24 hours, it got 15,000 visitors and 1,000 new nominations, which was pretty exciting to see. I hadn't anticipated that kind of response, though, so I’m now working on some major front-end and performance upgrades to support the site traffic and volume of profiles. In the meantime, I'm glad that people are using the site to find and nominate incredibly talented women in the design industry. Stay tuned!

Building a Directory with the Twitter API is a post from CSS-Tricks

How to improve your site’s UX

Css Tricks - Thu, 06/08/2017 - 6:08am

Let's face it, it's tough to be a web designer today. You have to stand up in front of the class and let people pick apart the thing you spent 10 hours on the night before.

But what if you can really support your designs by actual data and feedback, directly from your users and visitors, ultimately improving the overall experience.

By combining both Analytics and Feedback tools, Hotjar gives you the 'big picture' of how to improve your site's user experience and performance. The Analytics tools allow you to measure and observe user behavior (what users do) while the Feedback tools allow you to hear what your users have to say (Voice of User), all in one central interface.

Try it for free today.

Direct Link to ArticlePermalink

How to improve your site’s UX is a post from CSS-Tricks

Aspect Ratio Boxes

Css Tricks - Thu, 06/08/2017 - 5:47am

I had a little situation the other day where I needed to make one of those aspect-ratio friendly boxes. This isn't particularly new stuff. I think the original credit goes as far back as 2009 and Thierry Koblentz's Intrinsic Ratios and maintained popularity even for other kinds of content with articles like Uncle Dave's Ol' Padded Box.

Let's go on a little journey through this concept, as there is plenty to talk about.

The Core Concept: padding in percentages is based on width

Even when that is a little unintuitive, like for vertical padding. This isn't a hack, but it is weird: padding-top and padding-bottom is based on an element's width. So if you had an element that is 500px wide, and padding-top of 100%, the padding-top would be 500px.

Isn't that a perfect square, 500px × 500px? Yes, it is! An aspect ratio!

If we force the height of the element to zero (height: 0;) and don't have any borders. Then padding will be the only part of the box model affecting the height, and we'll have our square.

Now imagine instead of 100% top padding, we used 56.25%. That happens to be a perfect 16:9 ratio! (9 / 16 = 0.5625).

Now we have a friendly aspect ratio box, that works well in fluid width environments. If the width changes, so does the height, and the element keeps that aspect ratio.

Use case: a background-image

Perhaps we've made a typographic lockup. It's for the title of an article, so it makes sense to use an <h1> tag.

<h1> Happy Birthday </h1>

We can make that <h1> tag the aspect ratio box and apply the lockup as a background image.

h1 { overflow: hidden; height: 0; padding-top: 56.25%; background: url(/images/happy-birthday.svg); }

But I lied about that aspect ratio up there. It's not actually a 16:9 image. I just downloaded that graphic off a stock photography site. It happens to be an SVG with a viewBox="0 0 1127.34 591.44" which means it's essentially an 1127.34 × 591.44 image in terms of aspect ratio. Or it could have been a 328 × 791 image.

I'd say it's very common for any random image not to fit into a very specific pre-defined aspect ratio...

The Math of Any Possible Aspect Ratio

Perfect squares and 16:9 stuff is great, but the values used for those are just simple math. An aspect ratio can be anything, and they commonly are completely arbitrary. A video or image can be cropped to any size.

So how do we figure out the padding-top for our 1127.34 × 591.44 SVG above?

One way is using calc(), like this:

padding-top: calc(591.44 / 1127.34 * 100%);

It was expressed to me not long ago that using calc() here may be "slower", but I've never seen any evidence of that. I imagine that yes, the computer does need to calculate something, so in a head-to-head battle against a situation where it doesn't, calculating is slower. But a math problem doesn't seem like too much work for a computer. For example, the popular Intel Pentium III (released in 1999) could do 2,054 MIPS or "Millions of instructions per second", so it would make imperceptively quick work of a division problem. And now chips are 50× faster.

If we were using a preprocessor like Sass, we could do the calculation ahead of time:

padding-top: 591.44px / 1127.34px * 100%;

Either way, I'm a fan of leaving the math in the authored code.

How do you put content inside if padding is pushing everything down?

We hid the content in the demo above, by letting the content get pushed out and hiding the overflow. But what if you need an aspect ratio box while keeping content inside of it? That's slightly trickier. We'll need to position it back up into place. Absolute positioning can be up for that job.

Say we're just using text alone now, but still want the aspect ratio box. We'll need an inside wrapper for the absolute positioning. Let's get specific with our classnames:

<h1 class="aspect-ratio-box"> <div class="aspect-ratio-box-inside"> Happy Birthday </div> </h1>

Then do the positioning:

.aspect-ratio-box { height: 0; overflow: hidden; padding-top: 591.44px / 1127.34px * 100%; background: white; position: relative; } .aspect-ratio-box-inside { position: absolute; top: 0; left: 0; width: 100%; height: 100%; }

Just for fun, let's go full blast here and center that text and size it so it scales with the box:

<h1 class="aspect-ratio-box"> <div class="aspect-ratio-box-inside"> <div class="flexbox-centering"> <div class="viewport-sizing"> Happy Birthday </div> </div> </div> </h1>

Few more classes to style:

.flexbox-centering { height: 100%; display: flex; justify-content: center; align-items: center; } .viewport-sizing { font-size: 5vw; } Use Case: Video

This is probably the #1 most common and practical use case for all this aspect ratio box stuff. HTML5 <video> isn't an issue, as it behaves as if it has an aspect ratio just like <img>s do. But a lot of video on the web arrives in <iframe>s, which do not scale with an aspect ratio.

This is exactly what FitVids is all about. It finds videos on the page, figures out their unique aspect ratios, then applies these same CSS concepts to them to make them fluid width while maintaining their unique aspect ratios.

FitVids is jQuery based, but there are vanilla JavaScript options, like this one by Ross Zurowski.

But... what if there is too much content?

Back to arbitrary (probably textual) content for a bit.

We're essentially setting heights here, which should always flash a blinking red light when working with CSS. The web likes to grow downward, and setting fixed heights is an enemy to that natural movement.

If the content becomes too much for the space, we're in Bad Design territory:

Bad Design Territory

Maybe we could do something like overflow: auto; but that might be Awkward Design Territory.

The Psuedo Element Tactic

This is what has become, I think, the best way to handle an aspect ratio box that has completely arbitrary content in it. Keith Grant has a good writeup on it. Marc Hinse had an explanation and demos back in 2013.

With this technique, you get the aspect ratio box with less markup, and it's still safe if the content exceeds the height.

The trick is to apply the % padding to a pseudo element instead of the box itself. You float the pseudo-element, so the content inside can be inside nicely, then clear the float.

.aspect-ratio-box { background: white; } .aspect-ratio-box::before { content: ""; width: 1px; margin-left: -1px; float: left; height: 0; padding-top: 591.44px / 1127.34px * 100%; } .aspect-ratio-box::after { /* to clear float */ content: ""; display: table; clear: both; }

See how it's safer:

And a video:

Using Custom Properties

This is perhaps the coolest idea of all!

Thierry Koblentz recently wrote this up, crediting Sérgio Gomes for the idea.

To use it, you set a custom property scoped right to the element you need it on:

<div style="--aspect-ratio:815/419;"> </div> <div style="--aspect-ratio:16:9;"> </div> <!-- even single value --> <div style="--aspect-ratio:1.4;"> </div>

The CSS that styles this is gosh-danged genius:

[style*="--aspect-ratio"] > :first-child { width: 100%; } [style*="--aspect-ratio"] > img { height: auto; } @supports (--custom:property) { [style*="--aspect-ratio"] { position: relative; } [style*="--aspect-ratio"]::before { content: ""; display: block; padding-bottom: calc(100% / (var(--aspect-ratio))); } [style*="--aspect-ratio"] > :first-child { position: absolute; top: 0; left: 0; height: 100%; } }

Allow me to quote Thierry's step-by-step explanation:

  • We use [style*="--aspect-ratio"] as a hook to target the appropriate boxes
  • We stretch the inner box regardless of support for custom property
  • We make sure the height of images comes from their intrinsic ratio rather than their height attribute
  • We style the container as a containing block (so the inner box references that ancestor for its positioning)
  • We create a pseudo-element to be used with the “padding hack” (it is that element that creates the aspect ratio)
  • We use calc() and var() to calculate padding based on the value of the custom property
  • We style the inner box so it matches the dimensions of its containing block
Other Ideas & Tools

Lisi Linhart tipped me off to Ratio Buddy, which is super cool:

Notice that it uses a pseudo element, but then still absolutely positions the inside container. That's kinda weird. You'd probably either skip the pseudo element and put the padding right on the container, or float the pseudo-element so you don't need that inner container. Still, I like the idea of a little generator for this.

Tommy Hodgins has CSSplus which features Aspecty which is just for this, assuming you're cool with a JavaScript parsing and changing your CSS kinda thing:

See the Pen Aspect Ratio for Chris by Chris Coyier (@chriscoyier) on CodePen.

I've actually seen quite a bit of real world usage of aspect ratio boxes over the years. Feel free to share if you've had some experience!

Aspect Ratio Boxes is a post from CSS-Tricks

New on Typekit: Sync multiple fonts more easily

Nice Web Type - Thu, 06/08/2017 - 4:40am

Starting today, “multi-sync” (as we call it) is back and better than ever—including the much-requested ability to sync entire font families with just one click.

Since we launched Typekit’s new user interface last year—including our new, bigger type families—one of your most popular requests has been to bring back the ability to sync several fonts from a family at the same time.

The new Sync All button, as shown on Halyard‘s family page

On most family pages, you’ll now see a Sync All button that does exactly what it sounds like—click it and all the fonts in the family will be added to your sync selections right away, and synced to your computer within seconds. This makes it a lot easier to play around with the whole range of weights, widths, and other variations within families, not to mention a lot simpler to set paragraph text with the standard “RIBBI” (regular, italic, bold, bold italic) styles.

A small number of our families are very, very big: Acumin, for example, has 90 fonts, while Kepler has a whopping 132 fonts. On these families’ pages, you’ll see a drop-down menu, allowing you to sync all the fonts within a subset of the family, such as Acumin’s widths or Kepler’s optical sizes. This gives you more control over how many fonts you’re syncing at once, to help you avoid syncing more fonts than you need.

Of course, if you really do want all 132 fonts, there’s a Sync All button at the bottom of the menu.

Managing your synced fonts

All Typekit plans have a limit on the number of fonts that can be synced at one time. These “sync limits” are similar to the pageview limits that apply to each plan for web fonts. Today’s updates will make it easier to understand what your plan’s limits are, and to figure out what to do if you’ve gone over them.

If you sync a font beyond your plan’s limit, we’ll display a message letting you know. You can visit the Synced Fonts page to manage your previous sync selections and unsync fonts you’re not using anymore, or see if it’s time to invest in a plan with a higher sync limit.

We’ve also added a usage meter to the Synced Fonts page, along with some labels indicating how many fonts are synced from each family, so you can better manage your usage.

Customers who stay over their sync limit for a prolonged amount of time may get an email from us asking them to unsync some fonts or upgrade. We’ll never charge your credit card without contacting you first.

We’ve been busily adding a lot of new fonts to our library for sync, so we expect this is good timing to introduce an easier way to sync a bunch of them at once. Keep up with us on Twitter and Instagram to hear about new library additions, and don’t hesitate to send an email our way if you have questions about anything.


Masking vs. Clipping: When to Use Each

Css Tricks - Wed, 06/07/2017 - 3:20am

I was recently doing some client work where I used both <clipPath>s and <mask>s in SVG to hide and show content for animation. When I started this project, I thought I knew all of the reasons to use one over the other. But, as tends to happen, working closely with something reveals idiosyncrasies. In this post, we'll go over some of these details so that you can get productive as soon as possible.

What is clipping and masking and why should we care about it?

Both clipping and masking obscure areas where elements in an SVG or HTML element can be visually applied. You use a bit of SVG designated as a <clipPath> or <mask> by wrapping the element and applying an id, like so:

See the Pen d2a9315e310901a3d43ba3bdf8413c65 by Sarah Drasner (@sdras) on CodePen.

Though no demo of these techniques will rival Yoksel's version, which is comprehensive not just to SVG but to CSS versions of clipping and masking as well:

See the Pen CSS and SVG Masks by yoksel (@yoksel) on CodePen.

These techniques are so useful because we can show and hide bits of content, which we can use for unique layouts, realism in animation, and performant alternatives to height transitions.

Clipping

Think about <clipPath> in SVG (or clip-path in CSS) as a way to cut a shape out of another shape. There's no concept of opacity, or alpha channel, to gray area here. Parts of the element with a clipping path applied are literally visible or not visible. Clipping just uses the geometry of the shape. Because of this, certain visual elements won't be applied. This includes, but is not limited to: stroke and stroke styles, gradients, and fill colors.

You can still, however, animate these visual elements, change their attributes, and apply transforms to them, these will still be honored. Remember that when you animate them you need to target the elements inside the <clipPath>. Another thing to keep in mind is that the pieces that are clipped away won't accept pointer events, so events can only be captured on the parts that you can visually see.

Check out the Wall-e demo below. Do you see how the arms are being obscured as they reach out and then tuck back into the body? In order to realistically hide and show that arm, we will need to obscure it as it moves. In this case, we made a clipPath out of the elbow curve and clipped pieces of the arm as it extended.

See the Pen Vue-controlled Wall-E by Sarah Drasner (@sdras) on CodePen.

It doesn't end there, of course, we can apply this technique to more sophisticated effects, such as this amazing x-ray machine from Noel Delgado:

See the Pen X-ray me (SVG Experiment) by Noel Delgado (@noeldelgado) on CodePen.

Masking

Think about masking as a way to apply complex, detailed, and shapes with varying opacity over another element. This can lead to really beautiful visual effects and performant alternatives to other techniques. For instance, animating gradients can be really CPU-intensive. But in the graphic below, we’re animating the mask instead of the gradient to the same visual effect, and it's a lot less heavy.

See the Pen Animating transparent mask by Sarah Drasner (@sdras) on CodePen.

Because SVG supports text and each piece is editable, we can also create interesting effects to show and hide pieces of data that are changing. Check out this demo from Craig Roblewsky:

See the Pen Quick Tip: Invert SVG text fill color with masks by Craig Roblewsky (@PointC) on CodePen.


Unlike clipping, masking does respect stroke and stroke effects, but keep in mind that in a mask, it will treat white as an area that will be shown and black as an area to be masked, with the greyscale exposing each along that scale.

This means, though, that you can use gifs with white and transparency to create masks that expose really cool effects:

See the Pen Gif masking in SVG by Sarah Drasner (@sdras) on CodePen.

I learned everything I needed to learn to make the above demo from Yoksel, who has a vast collection of pens with this technique, here's one I particularly like with a bird and a gradient, and a subtle texture in the background.

See the Pen Rainbow bird by yoksel (@yoksel) on CodePen.

Masking vs. Clipping: When to Use Each is a post from CSS-Tricks

Oh wait, I was looking at production

Css Tricks - Wed, 06/07/2017 - 3:10am

Oh so true:

Why won't this CSS change?!

Restart server

>Nope

rm -rf everything

>Nope

rebuild dev env

>Nope

Oh wait, I was looking at production

— Mike Coutermarsh (@mscccc) June 5, 2017

Not to ruin the joke, but I find it tremendously helpful to Give Your Development Domain a Different Favicon Than Production.

Read the comment thread on that post too, there are some other very clever ideas.

Direct Link to ArticlePermalink

Oh wait, I was looking at production is a post from CSS-Tricks

Ricardo: A Tale of Two Worlds

Typography - Tue, 06/06/2017 - 8:13am

David Ricardo (1772–1823) was a versatile man. A stock trader, politician and, most importantly, an economist. He is considered the first of the classical economists, thus symbolizing the transition from mercantilism to capitalism. While his theories are mostly derived from mathematical abstractions, he communicated his ideas in understandable, down-to-earth language. Besides a versatile man, Ricardo is also a versatile typeface that bridges the gap between geometrical and humanist typeface design.

In broad terms, geometric typefaces express a certain conceptual clarity, a set of a few simple rules that seem to govern everything. In reality, even geometric typefaces deviate from strict geometry all the time, to account for small optical illusions in the way we perceive the shapes of letters. So, geometric typefaces may not be bound by true geometry, but they are bound by visual geometry. This kind of geometry, though very popular in today’s world, also creates limitations to the freedom of the designer, and generally results in suboptimal legibility. Humanist designs, on the other hand, find their origins in broad-nib pen calligraphy and Roman inscriptions. This reduces the visual simplicity, but provides opportunity to increase legibility, because both the pen and the chisel are flexible tools. Counters are given room to breathe, joints are made as thin as possible, and moderate contrast helps to guide the eye along the line. Ricardo is an attempt to combine the best of both worlds: the conceptual clarity of a geometric design, with the more airy character and legibility of a humanist design.

Design features

The most obvious humanist aspect of Ricardo’s design is its vertically cut terminals and open counters in letters such as a, c, and e. Cutting the curves vertically makes it easier to keep the counters wide open, and prevents the whitespace from clogging. While the terminals all appear to be of the same thickness, this is not the case. The influence of the broad-nib pen means that the top terminal of the c, for example, needs to be thicker than the bottom. Secondly, monolinearity (a constant thickness of strokes all around) is sacrificed for a more crisp appearance. Strongly thinned joints between bowls and stems, and thin strokes in the middle of the a and e create more whitespace in places that are otherwise very dense with ink or pixels. Thirdly, capitals are kept fairly small, which allows for easy distinction between the lowercase l and the uppercase I, and ensures that abbreviations in capital letters don’t stand out too much from the rest of the text. These humanist additions to an essentially geometric skeleton not only help to shape Ricardo’s friendly character, but also make it very suitable for use at text-size, in both printed materials and on screens (websites/applications).

When used in larger sizes, Ricardo’s idiosyncrasies come into play. The diagonal terminals on horizontal strokes such as in the f, z, T, and E are a hint to the broad-nib pen, infusing some warmth into an otherwise fairly rigid texture. The terminals in the j, comma, ampersand, and parentheses happily divert from the vertical expectation, and instead follow the angle of the curve. The currency symbols, which are usually very compact due to their complexity, are kept airy by simplifying their shapes, and in some cases opening up the area between the horizontal bars, creating a stencil-like effect. These slightly unusual characteristics become most pronounced in the heavier weights.

Family structure

The Ricardo family exists of three subfamilies: Ricardo, Ricardo ALT, and Ricardo ITA. The ‘normal’ Ricardo contains the most conventional forms, and is the most suitable option for long-form text. Ricardo ALT, which is also accessible through Stylistic Set 2 of Ricardo, has a slightly more geometric flavour, as the a, j, u, and t have been replaced by simplified forms. It can also be used in small text, but is probably most useful at larger sizes. Of course, both Ricardo and Ricardo ALT also come with matching italics. A third, more cursive set of italics can be found in Ricardo ITA. For all of these sub-families, be it upright or italic, there are also stylistic alternates for the A, M, N, V, W, v, w, and y which are accessible through the OpenType panel in opentype-savvy applications. These alternates switch the blunt apexes (where two diagonals meet) for sharper ones. Other OpenType goodness includes: (discretionary) ligatures, smallcaps, case-sensitive forms, fractions, nine sets of numerals, and more.

Ricardo contains seven carefully chosen weights. The ExtraLight and ExtraBold can give some extra elegance or punch to your headlines and advertisements. The Medium weight is intended as a slightly darker alternative for the Regular weight. In recent years, type has become lighter and lighter, but some of us still like text to have a little more body, and for those people, among whom I count myself, I created the Medium. For fanatics on the lighter side of the spectrum, the Light may be used for long-form text, and the SemiBold can then function to highlight.

Italics

To provide significant typographic flexibility, Ricardo has not one but two sets of italics. The ‘normal’ italics (in Ricardo and Ricardo ALT) were created by first slanting the upright glyphs 10 degrees, and then painstakingly adjusting each glyph by hand. Some curved areas were even completely redrawn from scratch. Such measures may seem extreme, but are necessary to retain the geometric feel, keep the stroke modulation the same as in the Roman styles, and ensure a constant visual slant. A second set of italics, which can be found in the Ricardo ITA subfamily, is of a more eccentric nature. The R, K, and almost all lowercase letters were given tails. The a became single-story, the f borrowed a descending tail from the j, and some letters, like the v and x were completely redrawn. Together, these changes give Ricardo ITA a more cursive kind of feel, which not only looks nice, but also helps italicized words to stand out more strongly within a piece of text, or in an advertisement. It is in these funky italics that the influence of humanist design features is most pronounced. Cursive elements from the humanist realm, and consistent simplicity from the geometric side.

Marketing

A few years ago, I started a discussion on typophile.com about the emerging phenomenon of typefamilies that are discounted by eighty, or sometimes even ninety percent when they are released. I did not like this, and to be honest I still don’t like it. It seems to me to be a cause and a symptom of an overall devaluation of typefaces, and an increasingly short-lived significance of new releases. However, Bureau Roffa is only a small one-man foundry that needs exposure and revenue from its resellers to exist. To be able to find a spot among competitors, I saw myself forced to discount Ricardo by a whopping 75%, which is the most my heart can bare at the moment. I hope that this will not cause you, dear reader, to lose respect for my work and the work of other typedesigners. Many of us still put great care, a lot of time, and attention to the smallest of details into our work. In an attempt to establish my hopes on this matter, I have written this article, and created a small clip that functions as a sort of trailer to Ricardo and gives some insight into what it can do, and how it was made.



Sponsored by Hoefler & Co.

Ricardo: A Tale of Two Worlds

Creating Yin and Yang Loaders On the Web

Css Tricks - Tue, 06/06/2017 - 2:30am

I came across a couple such animations a while ago and this gave me the idea of creating my own versions with as little code as possible, no external libraries, using various methods, some of which take advantage of more recent features we can use these days, such as CSS variables. This article is going to guide you through the process of building these demos.

Before anything else, this is the animation we're trying to achieve here:

The desired result: a rotating ? symbol, with its two lobes increasing and decreasing in size.

No matter what method we choose to use to recreate the above animation, we always start from the static yin and yang shape which looks as illustrated below:

The static yin and yang symbol (live demo).

The structure of this starting shape is described by the following illustration:

The structure of the static symbol (live demo).

First off, we have a big circle of diameter d. Inside this circle, we tightly fit two smaller circles, each one of them having a diameter that's half the diameter of our initial big circle. This means that the diameter for each of these two smaller circles is equal to the big circle's radius r (or .5*d). Inside each of these circles of diameter r we have an even smaller concentric circle. If we are to draw a diameter for the big circle that passes through all the central points of all these circles - the line segment AB in the illustration above, the intersections between it and the inner circles split it into 6 equal smaller segments. This means that the diameter of one of the smallest circles is r/3 (or d/6) and its radius is r/6.

Knowing all of this, let's get started with the first method!

Plain old HTML + CSS

In this case, we can do it with one element and its two pseudo-elements. The how behind building the symbol is illustrated by the following animation (since the whole thing is going to rotate, it doesn't matter if we switch axes):

See the Pen by thebabydino (@thebabydino) on CodePen.

The actual element is the big circle and it has a top to bottom gradient with a sharp transition right in the middle. The pseudo-elements are the smaller circles we place over it. The diameter of one of the smaller circles is half the diameter of the big circle. Both smaller circles are vertically middle-aligned with the big circle.

So let's start writing the code that can achieve this!

First of all, we decide upon a diameter $d for the big circle. We use viewport units so that everything scales nicely on resize. We set this diameter value as its width and height, we make the element round with border-radius and we give it a top to bottom gradient background with a sharp transition from black to white in the middle.

$d: 80vmin; .? { width: $d; height: $d; border-radius: 50%; background: linear-gradient(black 50%, white 0); }

So far, so good:

See the Pen by thebabydino (@thebabydino) on CodePen.

Now let's move on to the smaller circles which we create with pseudo-elements. We give our element display: flex and make its children (or pseudo-elements in our case) middle aligned with it vertically by setting align-items: center. We make these pseudo-elements have half the height (50%) of their parent element and make sure that, horizontally, they each cover half of the big circle. Finally, we make them round with border-radius, give them a dummy background and set the content property just so that we can see them:

.? { display: flex; align-items: center; /* same styles as before */ &:before, &:after { flex: 1; height: 50%; border-radius: 50%; background: #f90; content: ''; } }

See the Pen by thebabydino (@thebabydino) on CodePen.

Next, we need to give them different backgrounds:

.? { /* same styles as before */ &:before, &:after { /* same styles as before */ background: black; } &:after { background: white } }

Now we're getting somewhere!

See the Pen by thebabydino (@thebabydino) on CodePen.

All that's left to do before we get the static symbol is to give these two pseudo-elements borders. The black one should get a white border, while the white one should get a black border. These borders should be a third of the pseudo-element's diameter, which is a third of half the diameter of the big circle - that gives us $d/6.

.? { /* same styles as before */ &:before, &:after { /* same styles as before */ border: solid $d/6 white; } &:after { /* same styles as before */ border-color: black; } }

However, the result doesn't look quite right:

See the Pen by thebabydino (@thebabydino) on CodePen.

This is because, vertically, the border adds up to the height instead of being subtracted out of it. Horizontally, we haven't set a width, so it gets subracted from the available space. We have two fixes possible here. One would be to set box-sizing: border-box on the pseudo-elements. The second one would be to change the height of the pseudo-elements to $d/6 - we'll go with this one:

See the Pen by thebabydino (@thebabydino) on CodePen.

We now have the basic shape, so let's move on to the animation! This animation involves going from the state where the first pseudo-element has shrunk to let's say half its original size (which would mean a scaling factor $f of .5) while the second pseudo-element has expanded to take up all available space left - meaning to the diameter of the big circle (which is twice its original size) minus the diameter of the first circle (which is $f of its original size) to the state where the second pseudo-element has shrunk to $f of its original size and the first pseudo-element has expanded to 2 - $f of its original size. The first pseudo-element circle scales relative to its leftmost point (so we need to set a transform-origin of 0 50%), while the second one scales relative to its rightmost point (100% 50%).

$f: .5; $t: 1s; .? { /* same styles as before */ &:before, &:after { /* same styles as before */ transform-origin: 0 50%; transform: scale($f); animation: s $t ease-in-out infinite alternate; } &:after { /* same styles as before */ transform-origin: 100% 50%; animation-delay: -$t; } } @keyframes s { to { transform: scale(2 - $f) } }

We now have the shape changing animation we've been after:

See the Pen by thebabydino (@thebabydino) on CodePen.

The last step is to make the whole symbol rotate:

$t: 1s; .? { /* same styles as before */ animation: r 2*$t linear infinite; } @keyframes r { to { transform: rotate(1turn) } }

And we got the final result!

However, there's still one more thing we can do to make the compiled CSS more efficient: eliminate redundancy with CSS variables!

white can be written in HSL format as hsl(0, 0%, 100%). The hue and the saturation don't matter, any value that has the lightness 100% is white, so we just set them both to 0 to make our life easier. Similarly, black can be written as hsl(0, 0%, 0%). Again, the hue and saturation don't matter, any value that has the lightness 0% is black. Given this, our code becomes:

.? { /* same styles as before */ &:before, &:after { /* same styles as before */ border: solid $d/6 hsl(0, 0%, 100% /* = 1*100% = (1 - 0)*100% */); transform-origin: 0 /* = 0*100% */ 50%; background: hsl(0, 0%, 0% /* 0*100% */); animation: s $t ease-in-out infinite alternate; animation-delay: 0 /* = 0*-$t */; } &:after { /* same styles as before */ border-color: hsl(0, 0%, 0% /* = 0*100% = (1 - 1)*100% */); transform-origin: 100% /* = 1*100% */ 50%; background: hsl(0, 0%, 100% /* = 1*100% */); animation-delay: -$t /* = 1*-$t */; } }

From the above, it results that:

  • the x component of our transform-origin is calc(0*100%) for the first pseudo-element and calc(1*100%) for the second one
  • our border-color is hsl(0, 0%, calc((1 - 0)*100%)) for the first pseudo-element and hsl(0, 0%, calc((1 - 1)*100%)) for the second one
  • our background is hsl(0, 0%, calc(0*100%)) for the first pseudo-element and hsl(0, 0%, calc(1*100%)) for the second one
  • our animation-delay is calc(0*#{-$t}) for the first pseudo-element and calc(1*#{-$t}) for the second one

This means we can use a custom property that acts as a switch and is 0 for the first pseudo-element and 1 for the second:

.? { /* same styles as before */ &:before, &:after { /* same styles as before */ --i: 0; border: solid $d/6 hsl(0, 0%, calc((1 - var(--i))*100%)); transform-origin: calc(var(--i)*100%) 50%; background: hsl(0, 0%, calc(var(--i)*100%)); animation: s $t ease-in-out calc(var(--i)*#{-$t}) infinite alternate; } &:after { --i: 1 } }

This eliminates the need for witing all these rules twice: all we need to do now is flip the switch! Sadly, this only works in WebKit browsers for now because Firefox and Edge don't support using calc() as an animation-delay value and Firefox doesn't support using it inside hsl() either.

Canvas + JavaScript

While some people might think this method is overkill, I really like it because it requires about the same amount of code as the CSS one, it has good support and good performance.

We start with a canvas element and some basic styles just to put it in the middle of its container (which is the body element in our case) and make it visible. We also make it circular with border-radius so that we simplify our job when drawing on the canvas.

$d: 80vmin; body { display: flex; justify-content: center; align-items: center; height: 100vh; background: lightslategray; } canvas { width: $d; height: $d; border-radius: 50%; background: white; }

So far, so good - we have a white disc:

See the Pen by thebabydino (@thebabydino) on CodePen.

Alright, now let's move on to the JavaScript part! Before anything else, we need to get the canvas element, the 2D context and set the canvas element's width and height attributes (things we draw on the canvas would appear stretched otherwise). Then, we're going to need to have a radius for our big circle. We get this radius to be half the computed size of the canvas element and, after we do that, we translate our context such that we bring the 0,0 point of our canvas dead in the middle (it's originally in the top left corner). We make sure we recompute the radius and the width and height attributes on each resize because, in the CSS, we made the canvas dimensions depend on the viewport.

const _C = document.querySelector('canvas'), CT = _C.getContext('2d'); let r; function size() { _C.width = _C.height = Math.round(_C.getBoundingClientRect().width); r = .5*_C.width; CT.translate(r, r); }; size(); addEventListener('resize', size, false);

After we've done this, we can move on to drawing on the canvas. Draw what? Well, a shape made out of three arcs, as shown in the illustration below:

The structure of the three arc shape (live demo).

In order to draw an arc on a 2D canvas, we need to know a few things. First off, it's the coordinates of the central point of the circle this arc belongs to. Then we need to know the radius of this circle and the angles (relative to the x axis of the local coordinate system of the circle) at which the start and end points of the arc are located. Finally, we need to know if we go from the start point to the end point clockwise or not (if we don't specify this, the default is clockwise).

The first arc is on the big circle whose diameter is equal to the canvas dimensions and, since we've placed the 0,0 point of the canvas right in the middle of this circle, this means we know both the first set of coordinates (it's 0,0) and the circle radius (it's r). The start point of this arc is the leftmost point of this circle - this point is at -180° (or -?). The end point is the rightmost point of the circle, which is at 0° (also 0 in radians). If you need a refresher of angles on a circle, check out this helper demo.

This means we can create a path and add this arc to it and, in order to see what we have so far, we can close this path (which in this case means connecting the end point of our arc to the start point with a straight line) and fill it (using the default fill, which is black):

CT.beginPath(); CT.arc(0, 0, r, -Math.PI, 0); CT.closePath(); CT.fill();

The result can be seen in the following Pen:

See the Pen by thebabydino (@thebabydino) on CodePen.

Now let's move on to the second arc. The coordinates of the central point of the circle it's on are .5*r,0 and its radius is .5*r (half the radius of the big circle). It goes from 0 to ?, moving clockwise in doing so. So the arc we add to out path before closing it is:

CT.arc(.5*r, 0, .5*r, 0, Math.PI);

After adding this arc, our shape becomes:

See the Pen by thebabydino (@thebabydino) on CodePen.

Now we have one more arc left to add. The radius is the same as for the previous one (.5*r) and the first set of coordinates is -.5*r,0. This arc goes from 0 to -? and it's the first arc not to go clockwise, so we need to change that flag:

CT.arc(-.5*r, 0, .5*r, 0, -Math.PI, true);

We now have the shape we wanted:

See the Pen by thebabydino (@thebabydino) on CodePen.

Next, we're going to add the black circle to this path. We're not going to create another path because the aim is to group all shapes with the same fill into the same path for better performance. Calling fill() is expensive, so we don't want to do it more often than we really need to.

A circle is just an arc from 0° to 360° (or from 0 to 2*?). The central point of this circle coincides to that for the last arc we've drawn (-.5*r, 0) and its radius is a third of that of the previous two arcs.

CT.arc(-.5*r, 0, .5*r/3, 0, 2*Math.PI);

Now we're getting really close to having the full symbol:

See the Pen by thebabydino (@thebabydino) on CodePen.

All that's left to do is create a white circle, symmetrical to the black one with respect to the y axis. This means need to switch to a white fill, start a new path and then add an arc to it using almost the same command we used to add the black circle shape - the only difference is that we reverse the sign of the x coordinate (this time, it's +, not -). After that, we close that path and fill it.

CT.fillStyle = 'white'; CT.beginPath(); CT.arc(.5*r, 0, .5*r/3, 0, 2*Math.PI); CT.closePath(); CT.fill();

We now have the static symbol!

See the Pen by thebabydino (@thebabydino) on CodePen.

For the animation, we want to go from the state where the first of the smaller arcs has shrunk to half is original radius (so we use a scaling factor F of .5) and the other one has expanded accordingly to the state where these initial radii are reversed.

In the initial state, given that the radius of the smaller arcs is initially .5*r, then the radius of the first of them after being scaled down by a factor F is r1 = F*.5*r. Since the radii of the smaller circles need to add up to the radius of the big circle r, we have that the radius of the second one of the smaller circles is r2 = r - r1 = r - F*.5*r.

In order to get the x coordinate of the origin of the first smaller arc for the initial state, we need to subtract its radius from the x coordinate of the point it starts at. This way, we get that this coordinate is r - r1 = r2. Similarly, in order to get the x coordinate of the origin of the second smaller arc, we need to add up its radius to the coordinate of the point it ends at. This way, we get that this coordinate is -r + r2 = -(r - r2) = -r1.

The initial vs. the final state of the animation (live demo).

For the final state, the values of the two radii are reversed. The second one is F*.5*r, while the first one is r - F*.5*r.

With every frame of our animation, we increase the current radius of the first smaller arc from the minimum value (F*.5*r) to the maximum value (r - F*.5*r) and then we start decreasing it to the minimum value and then the cycle repeats while also scaling the radius of the other smaller arc accordingly.

In order to do this, we first set the minimum and maximum radius in the size() function:

const F = .5; let rmin, rmax; function size() { /* same as before */ rmin = F*.5*r; rmax = r - rmin; };

At any moment in time, the current radius of the first of the smaller arcs is k*rmin + (1 - k)*rmax, where this k factor keeps going from 1 to 0 and then back up to 1. This sounds similar to the cosine function on the [0, 360°] interval. At 0°, the value of the cosine is 1. Then it starts decreasing and it keeps doing so until it gets to 180°, when it reaches its minimum value of -1, after which the value of the function starts increasing again until it gets to 360°, where it's again 1:

See the Pen by thebabydino (@thebabydino) on CodePen.

Alright, but the values of the cosine function are in the [-1, 1] interval and we need a function that gives us values in the [0, 1] interval. Well, if we add 1 to the cosine, then we shift the whole graph up and the values are now in the [0, 2] interval:

See the Pen by thebabydino (@thebabydino) on CodePen.

[0, 2] isn't [0, 1], so what we still need to do here is divide the whole thing by 2 (or multiply it with .5, same thing). This squishes our graph to the desired interval.

See the Pen by thebabydino (@thebabydino) on CodePen.

Good, but what's up with that angle? We don't have an angle going from 0° to 360°. If we're going to use requestAnimationFrame, we just have the number of the current frame, which starts at 0 and then keeps going up. Well, at the beginning, we set a total number of frames T for one animation cycle (the first arc going from the minimum radius value to the maximum radius value and then back again).

For every frame, we compute the ratio between the number of the current frame (t) and the total numeber of frames. For one cycle, this ratio goes from 0 to 1. If we multiply this ratio with 2*Math.PI (which is the same as 360°), then the result goes from 0 to 2*Math.PI over the course of a cycle. So this is going to be our angle.

const T = 120; (function ani(t = 0) { let k = .5*(1 + Math.cos(t/T*2*Math.PI)), cr1 = k*rmin + (1 - k)*rmax, cr2 = r - cr1; })();

The next step is to put inside this function the code that actually draws our symbol. The code for beginning, closing, filling paths, changing fills stays the same, as does the code needed for creating the big arc. The things that change are:

  • the radii of the smaller arcs - they're cr1 and cr2 respectively
  • the x coordinates of the central points for the smaller arcs - they're at cr2 and -cr1 respectively
  • the radii of the black and white circles - they're cr2/3 and cr1/3 respectively
  • the x coordinates of the central points of these circles - they're at -cr1 and cr2 respectively

So our animation function becomes:

const T = 120; (function ani(t = 0) { let k = .5*(1 + Math.cos(t/T*2*Math.PI)), cr1 = k*rmin + (1 - k)*rmax, cr2 = r - cr1; CT.beginPath(); CT.arc(0, 0, r, -Math.PI, 0); CT.arc(cr2, 0, cr1, 0, Math.PI); CT.arc(-cr1, 0, cr2, 0, -Math.PI, true); CT.arc(-cr1, 0, cr2/3, 0, 2*Math.PI); CT.closePath(); CT.fill(); CT.fillStyle = 'white'; CT.beginPath(); CT.arc(cr2, 0, cr1/3, 0, 2*Math.PI); CT.closePath(); CT.fill(); })();

This gives us the initial state of the animation:

See the Pen by thebabydino (@thebabydino) on CodePen.

Before we actually start animating the radii of the arcs, we still need to take care of a couple more things. First of all, if we start the animation right now, we're just going to be drawing how the shape looks for each frame over what we've drawn for the previous frames, which is going to create one big mess. In order to avoid this, we need to clear the canvas for each frame, before drawing anything on it. What we clear is the visible part, which is inside the rectangle of canvas dimensions whose top left corner is at -r,-r:

CT.clearRect(-r, -r, _C.width, _C.width);

The second little problem we need to fix is that we're switching to a white fill, but at the start of the next frame, we need a black one. So we need to make this switch for each frame before the beginning of the first path:

CT.fillStyle = 'black';

Now we can actually start the animation:

requestAnimationFrame(ani.bind(this, ++t));

This gives us the morphing animation, but we still need to rotate the whole thing. Before tackling that, let's look at the formula for k once more:

let k = .5*(1 + Math.cos(t/T*2*Math.PI))

T and 2*Math.PI are constant throughout the animation, so we can just take that part out and store it as a constant angle A:

const T = 120, A = 2*Math.PI/T; (function ani(t = 0) { let k = .5*(1 + Math.cos(t*A)); /* same as before */ })();

Now for every frame, we can also rotate the context by A after clearing the canvas.

CT.rotate(A);

This rotation keeps adding up with every frame and we now have the rotating and morphing animation we've been after.

SVG + JavaScript

We start with an SVG element and pretty much the same CSS as in the canvas case:

$d: 80vmin; body { display: flex; justify-content: center; align-items: center; height: 100vh; background: lightslategray; } svg { width: $d; height: $d; border-radius: 50%; background: white; }

This gives us a white disc:

See the Pen by thebabydino (@thebabydino) on CodePen.

Not too exciting, so let's move on to drawing something on the SVG canvas. Just like in the canvas case, we'll be drawing a path made up of the same three arcs (the big one with a radius that's half the size of the SVG viewBox and the two smaller ones with a radius that's half of that of the big arc in the static case) and two small circles (with a radius that's a third of that of the smaller arc they share their central point with).

So we start by picking a radius r value and using it to set the viewBox on the svg element:

- var r = 1500; svg(viewBox=[-r, -r, 2*r, 2*r].join(' '))

The next step is to add the path made up of the three arcs. Creating a path in SVG is a bit different from canvas. Here, the shape is described by the path data d attribute, which, in our case, is made up of:

  • a "move to" (M) command after which we specify the coordinates of the start point of our path (also the start point of the big arc in this case)
  • an "arc to" (A) command for each of our arcs after which we describe our arcs; each of these arcs starts from the end point of the previous arc or, in the case of the first arc, from the start point of our path

Let's take a closer look at the components of an "arc to" (A) command:

  • the radius of our arc along the x axis of its system of coordinates - this is equal to r in the case of the big arc and to .5*r in the case of the two smaller ones
  • the radius of our arc along the y axis of its system of coordinates - this is equal to the one along the x axis in the case of circular arcs as we have here (it's only different for elliptical arcs, but that's beyond the scope of this article)
  • the rotation of our arc's system of coordinates - this only influences the arc's shape in the case of elliptical arcs, so we can safely always take it 0 to simplify things for circular arcs
  • the large arc flag - this is 1 if our arc is greater than half a circle and 0 otherwise; since our arcs are exactly half a circle, they're not greater than haf a circle, so this is always 0 in our case
  • the sweep flag - this is 1 if the arc goes clockwise between its start and its end point and 0 otherwise; in our case, the first two arcs go clockwise, while the third doesn't, so the values we use for the three arcs are 1, 1 and 0
  • the x coordinate of the arc's end point - this is something we need to determine for each arc
  • the y coordinate of the arc's end point - also something we need to determine for each arc

At this point, we already know most of what we need. All we still have to figure out are the coordinates of the arcs' endpoints. So let's consider the following illustration:

The structure of the three arc shape with coordinates of arc endpoints (live demo).

From the illustration above we can see that the first arc (the big one) starts at (-r,0) and ends at (r,0), the second one ends at 0,0 and the third one ends at (-r,0) (also the start point of our path). Note that the y coordinates of all these points remain 0 even if the smaller arcs' radii change, but the x coordinate of the second arc's endpoint only happens to be 0 in this case when the radii of the smaller arcs are exactly half of the big one. In the general case, it's r - 2*r1, where r1 is the radius of the second arc (the first of the smaller ones). This means we can now create our path:

- var r1 = .5*r, r2 = r - r1; path(d=`M${-r} 0 A${r} ${r} 0 0 1 ${r} 0 A${r1} ${r1} 0 0 1 ${r - 2*r1} 0 A${r2} ${r2} 0 0 0 ${-r} 0`)

This gives us the three arc shape we've been after:

See the Pen by thebabydino (@thebabydino) on CodePen.

Now let's move on to the small circles. We already know the coordinates of their central points and their radii from the canvas method.

circle(r=r1/3 cx=r2) circle(r=r2/3 cx=-r1)

By default, all these shapes have a black fill so we need to explicitly set a white one on the circle at (r2,0):

circle:nth-child(2) { fill: white }

We now have the static shape!

See the Pen by thebabydino (@thebabydino) on CodePen.

Next, we're going to animate the shape of our path and the size and position of our two small circles using JavaScript. This means that the first thing we do is get these elements, get the radius R of the big circle and set a scaling factor F that gives us the minimum radius (RMIN) down to which the arcs can be scaled. We also set a total number of frames (T) and a unit angle (A).

const _P = document.querySelector('path'), _C = document.querySelectorAll('circle'), _SVG = document.querySelector('svg'), R = -1*_SVG.getAttribute('viewBox').split(' ')[0], F = .25, RMIN = F*R, RMAX = R - RMIN, T = 120, A = 2*Math.PI/T;

The animation function is pretty much the same as in the canvas case. The only thing that's different is the fact that now, in order to change the path shape, we change its d attribute and, in order to change the small circles' radii and positions, we change their r and cx attributes. But everything else works exactly the same way:

(function ani(t = 0) { let k = .5*(1 + Math.cos(t*A)), cr1 = k*RMIN + (1 - k)*RMAX, cr2 = R - cr1; _P.setAttribute('d', `M${-R} 0 A${R} ${R} 0 0 1 ${R} 0 A${cr1} ${cr1} 0 0 1 ${R - 2*cr1} 0 A${cr2} ${cr2} 0 0 0 ${-R} 0`); _C[0].setAttribute('r', cr1/3); _C[0].setAttribute('cx', cr2); _C[1].setAttribute('r', cr2/3); _C[1].setAttribute('cx', -cr1); requestAnimationFrame(ani.bind(this, ++t)); })();

This gives us the morphing shape:

See the Pen by thebabydino (@thebabydino) on CodePen.

There's just one more thing to take care of and that's the rotation of the whole symbol, which we set on the _SVG element:

let ca = t*A; _SVG.style.transform = `rotate(${+ca.toFixed(2)}rad)`;

And we now have the desired result with SVG and JavaScript as well!

SVG + CSS

There's one more method of doing this, although it involves changing things like the path data from CSS, which is something only Blink browsers support at this point (and they're not even matching the latest spec).

It's also a bit breakable because we need to have the same radius value both in the SVG viewBox attribute and as a Sass variable.

- var r = 1500; svg(viewBox=[-r, -r, 2*r, 2*r].join(' ')) path circle circle $d: 65vmin; $r: 1500; $r1: .5*$r; $r2: $r - $r1; $rmin: .25*$r; $rmax: $r - $rmax;

We could access the value of this radius from the CSS, but only as a custom property, if we were to do something like this:

- var r = 1500; svg(viewBox=[-r, -r, 2*r, 2*r].join(' ')) style :root { --r: #{r} }

However, while this may be very helpful in some cases, it is useless here, as we currently have no way of putting CSS variables into the path data string we build with Sass. So we're stuck with having to set the same value both in the viewBox attribute and in the Sass code.

The basic styles are the same and we can create the path data with Sass in a way that's similar to the Pug method:

$r: 1500; $r1: .5*$r; $r2: $r - $r1; path { $data: 'M#{-$r} 0' + 'A#{$r} #{$r} 0 0 1 #{$r} 0' + 'A#{$r1} #{$r1} 0 0 1 #{$r - 2*$r1} 0' + 'A#{$r2} #{$r2} 0 0 0 #{-$r} 0'; d: path($data); }

This gives us our three arcs shape:

The three arcs shape (live demo, Blink only).

For the two small circles, we set their radii and positions along the x axis. We also need to make sure one of them is white:

circle { r: $r1/3; cx: $r2; &:nth-child(2) { fill: white } &:nth-child(3) { r: $r2/3; cx: -$r1 } }

We now have the static shape:

The static yin and yang shape (live demo, Blink only).

In order to get the effect we're after, we need the following animations:

  • a morphing animation of the path shape, where the radius of the first of the smaller arcs goes from the minimum possible radius ($rmin: .25*$r) to the maximum possible one ($rmax: $r - $rmin) and then back, while the radius of the last arc goes from $rmax to $rmin and back again; this can be done with a keyframe animation from one extreme to the other and then using the alternate value for animation-direction
  • another alternating animation that scales the radius of the first small circle from $rmin/3 up to $rmax/3 and then back down to $rmin/3 again; the second small circle uses the same animation only delayed by the value of a normal animation-duration
  • a third alternating animation that moves the central points of the two small circles back and forth; in the case of the first (white) small circle, it moves from $rmax down to $rmin; in the case of the second (black) circle, it goes from -$rmin down to -$rmax; what we can do here to unify them is use a CSS variable as a switch (it only works in WebKit browsers, but setting the path data or the circle radii or offsets from the CSS doesn't have better support either)

So let's first see the morphing @keyframes. These are created by setting pretty much the same path data as before, only replacing $r1 with $rmin and $r2 with $rmax for the 0% keyframe and the other way around for the 100% one:

@keyframes m { 0% { $data: 'M#{-$r} 0' + 'A#{$r} #{$r} 0 0 1 #{$r} 0' + 'A#{$rmin} #{$rmin} 0 0 1 #{$r - 2*$rmin} 0' + 'A#{$rmax} #{$rmax} 0 0 0 #{-$r} 0'; d: path($data); } 100% { $data: 'M#{-$r} 0' + 'A#{$r} #{$r} 0 0 1 #{$r} 0' + 'A#{$rmax} #{$rmax} 0 0 1 #{$r - 2*$rmax} 0' + 'A#{$rmin} #{$rmin} 0 0 0 #{-$r} 0'; d: path($data); } }

Now we just need to set this animation on the path element:

$t: 1s; path { animation: m $t ease-in-out infinite alternate }

And the shape morphing part works!

The three arcs shape morphing (live demo, Blink only).

Next step is to move on to scaling and moving the two small circles. The scaling @keyframes follow the same pattern as the morphing ones. The radius value is $rmin/3 at 0% and $rmax/3 at 100%:

@keyframes s { 0% { r: $rmin/3 } 100% { r: $rmax/3 } }

We set this animation on the circle elements:

circle { animation: s $t ease-in-out infinite alternate }

And now the radii of the two small circles are animated:

The three arcs shape morphing and the small circles scaling (live demo, Blink only).

It's a start, but we have a number of problems here. First of all, the second small circle should decrease in size when the first one is growing bigger and the other way around. We fix this by setting an animation-delay that depends on a CSS variable we initially set to 0 and then switch to 1 on the second small circle:

circle { --i: 0; animation: s $t ease-in-out calc(var(--i)*#{$t}) infinite alternate &:nth-child(3) { --i: 1 } }

As mentioned before, using calc() as an animation-delay value only works in WebKit browsers, but setting r from the CSS has even poorer support, so the animation-delay is not the biggest problem we have here. The result can be seen below:

The three arcs shape morphing and the small circles scaling (live demo, Blink only).

This is much better, but we still nedd to animate the positions of the small circles along the x axis. The way we do this is with a set of @keyframes that make cx go from $rmax to $rmin and back again for the first small circle and from -$rmin to -$rmax and back for the second one. In these two cases, we have both a different order and a different sign, so we need to come up with a keyframe animation that satisfies both.

Getting around the order problem is the easy part - we use the same animation-delay as we did for the scaling radii animation.

But what about the sign? Well, we use our custom property --i again. This is 0 for the first small circle and 1 for the second one, so we need a function that takes in --i and gives us 1 when this variable's value is 0 and -1 for a value of 1. The simplest one that comes to mind is raising -1 to the power --i. Sadly, that's not possible with CSS - we can only have arithmetic operations inside calc(). However, calc(1 - 2*var(--i)) is another solution that works and it's not much more complicated. Using this, our code becomes:

circle { --i: 0; --j: calc(1 - 2*var(--i)); animation: s $t ease-in-out calc(var(--i)*#{-$t}) infinite alternate; animation-name: s, x; &:nth-child(3) { --i: 1 } } @keyframes x { 0% { cx: calc(var(--j)*#{$rmax}) } 100% { cx: calc(var(--j)*#{$rmin}) } }

The result can be seen below... and it's not quite as expected:

Result (live demo, Blink only).

What we have looks like a sudden flip at 50% in between the two end values, not a smooth animation. This is not what we wanted, so it looks like we need to abandon this tactic.

We have another option here though: combining cx with transform. The two small circles are always positioned such that the distance between their central points is $r. So what we can do is position the second of the small circles at -$r, then translate them both by a distance that's between $rmax and $rmin:

circle { transform: translate($r2*1px); animation: s $t ease-in-out infinite alternate; animation-name: s, x; &:nth-child(3) { cx: -$r; animation-delay: -$t, 0s } } @keyframes x { 0% { transform: translate($rmax*1px) } 100% { transform: translate($rmin*1px) } }

This finally behaves as we wanted it to!

Correct animation (live demo, Blink only)

One more thing we can do here to simplify the code is get rid of the initial $r1 and $r2 values and replace them with those in the 0% keyframe of each animation:

path { d: path('M#{-$r} 0A#{$r} #{$r} 0 0 1 #{$r} 0' + 'A#{$rmin} #{$rmin} 0 0 1 #{$r - 2*$rmin} 0' + 'A#{$rmax} #{$rmax} 0 0 0 #{-$r} 0'); animation: m $t ease-in-out infinite alternate } circle { r: $rmin/3; transform: translate($rmax*1px); animation: s $t ease-in-out infinite alternate; animation-name: s, x; &:nth-child(3) { cx: -$r; animation-delay: -$t, 0s } } @keyframes m { to { d: path('M#{-$r} 0A#{$r} #{$r} 0 0 1 #{$r} 0' + 'A#{$rmax} #{$rmax} 0 0 1 #{$r - 2*$rmax} 0' + 'A#{$rmin} #{$rmin} 0 0 0 #{-$r} 0'); } } @keyframes s { to { r: $rmax/3 } } @keyframes x { to { transform: translate($rmin*1px) } }

The visual result is exactly the same, we just have less code.

The final step is to make the SVG element itself rotate infinitely:

svg { animation: r 2*$t linear infinite } @keyframes r { to { transform: rotate(1turn) } }

The finished loading animation can be seen in this Pen.

So there you have it - one loading animation, four different methods of recreating it from scratch for the web. Not everything we've explored in here is usable in practice today. For example, the support for the last method is really poor and the performance is awful. However, exploring the limits of what's becoming possible these days was a fun exercise and a great learning opportunity.

Creating Yin and Yang Loaders On the Web is a post from CSS-Tricks

Syndicate content
©2003 - Present Akamai Design & Development.