Front End Web Development

How to Create a Component Library From SVG Illustrations

Css Tricks - Thu, 04/12/2018 - 4:01am

I’ve recently published my first ever open source npm package! It makes SVG illustrations from unDraw into customizable React components.

Here’s a GIF that shows what I mean:

What’s unDraw?

unDraw is a collection of MIT licensed illustrations for every project you can imagine and create. It’s a constantly updated collection of beautiful SVG images that you can use completely free and without attribution.

While unDraw is still fairly new, its open source nature means that it’s being used by a range of products already. Here it is on the newly launched design mentoring site called MentorOla, alongside a language site I’ve been working on myself called Little Lingua:

Left: MentorOla by Marc Andrew, Right: Little Lingua]

While using React to build the Little Lingua website, I discovered that converting SVGs into React components made them much more manageable and even more customizable as illustrations.

Because of this usefulness, I wanted to release a library of unDraw React components as an open source npm package to bring the beauty of unDraw to the largest of JavaScript audiences in a simple way:

Framework Nov. 2016 Oct. 2017 % Change React 2,564,601 7,040,410 174.5% Angular 1,289,953 2,168,899 68.1% Backbone 663,610 837,372 31.6% Angular.js 616,135 1,081,796 75.6% Vue 6,231 874,424 13,933.%

Source: JavaScript Frameworks by the Numbers

John Hannah, who authored the the JavaScript Frameworks by the Numbers report:

React absolutely dominates. It’s by far the most downloaded according to these numbers...

A lot of inspiration to put this package together came from Miuki Miu’s project React Kawaii, where she did largely the same thing, and a bit more:

Her article outlines the overall concept of SVGs as React Components, and here I will go into more of the details involved in my own process including putting together Styleguideist documentation. Here’s everything that will be covered:

  1. How to convert SVG illustrations into customisable React Components
  2. How to use Styleguideist to produce simple, interactive documentation
  3. How to release an npm package (since it was my first time doing this)
1. SVG Illustrations as React Components

When you visit unDraw, it’s currently possible to customize one primary color of each SVG illustration right from the website:

The SVG download you get when you grab the image is pretty tedious to customize any further, as there’s many color values to change. If that SVG is converted to a React component though, it becomes really easy! Compare the two:

unDraw designer SVG vs. React Component

Essentially, the SVG is held within a React component, which is very simple to interface with. You just pass properties to the Component (e.g. skinColor/hairColor/primaryColor), which then sprinkles them into the SVG.

You can do this in just three steps:

Convert the SVG to JSX

There are some awesome tools out there to convert SVGs into the JSX code that’s used in a React component’s render() method. The first one I used was the first one I came across: SVG to JSX—it was also the first Google search result 😉. As it says on the tin, any JSX code is generated from any SVG you paste in:

SVG to JSX converter by Balaj Marius

Once you’ve got your JSX, paste it into your React component like so:

import React from 'react'; import PropTypes from 'prop-types'; const UndrawDesigner = props => ( <svg id='780c6f38–12e9–4526–8343–95ef18389740' dataName='Layer 1' xmlns='http://www.w3.org/2000/svg'> // all your svg code </svg> ); export default UndrawDesigner;

That’s it! Now you can use this as a component by dropping this into your code:

<UndrawDesigner/>

Right now, you’ll be stuck with the default colors of your SVG. Let’s make those colors easy to change:

Make it Customizable With Props

We can use the benefits of React to make the illustration customisable by adding *props* as placeholders that are used to fill the *color attributes* of the SVG/JSX in your component:

<svg xmlns='http://www.w3.org/2000/svg'> <path fill={props.hairColor} d='...' /> <path fill={props.hairColor} d='...' /> <ellipse fill={props.skinColor} cx='...' cy='...' rx='...' ry='...' /> <ellipse fill={props.skinColor} cx='...' cy='...' rx='...' ry='...' /> <!-- etc --> </svg>

To make sure you’re replacing the right fill attributes, you can open the SVG in your browser, and identify colors using your browser’s inspector tools:

You can see the color here is rgb(226,189,149) . Convert that to a hex code. There’s many ways to do this, one is searching "colorpicker” in Google :

Since a single color is often used in numerous places in an SVG illustration (e.g. left hand, right hand, face will be the same), there will be many places a color needs replacing. To do it quickly, grab the HEX code, and do a find-and-replace in your component, replacing the color attribute with your prop name, e.g. {props.skinColor} .

Do this with as many colors/elements of your SVG as you’d like to make customizable, ensuring your props are named so that they’re easy for other people to understand and use.

Add PropType definitions and Default Colors

Once you’ve finished adding your props, it’s good practice to define them as propTypes. This will also help when we make awesome documentation for our components. Add them like so (make sure you’ve got prop-types installed in your project):

UndrawDesigner.propTypes = { /** * Hex color */ skinColor: PropTypes.string, /** * Hex color */ hairColor: PropTypes.string, /** * Hex color */ primaryColor: PropTypes.string, };

Finish up your component by defining some default colors, right before the export statement. This ensures a fallback color will be used if no props are passed to the component:

UndrawDesigner.defaultProps = { skinColor: '#e2bd95', primaryColor:'#6c68fb', hairColor:'#222' }; export default UndrawDesigner;

After doing this, your component will be ready to accept values for each of the attributes defined. For example, in UndrawDesigner, we can make a little gray human by passing in various types of gray for skin and hair. Nice and simple:

It’s that much simpler, really. If you want to go beyond changing colors, read Miuki Miu's article, where she cleverly adds smaller common components that are used as facial expressions across larger components:

2. Making the Style Guide

To make the React illustrations more useful to everyone, it’s possible to create a living style guide of the components using React Styleguidist. It’s not much extra work, either.

Because of how Stylguidist works with React, it’s really straightforward to create documentation from the components we have. Styleguidist requires two main things to generate documentation from our components:

  1. Clear PropType definitions
  2. Component examples

We’ve already taken care of the first one in the previous section. The comments above each PropType definition is also important, as it gets displayed in the end documentation:

Adding component examples is also straightforward—add a Readme.md to the folder of your component with an example of how it would be used. The contents may look something like this:

// UndrawResponsive example ```js <UndrawResponsive height='250px' primaryColor='#6c68fb' accentColor='#43d1a0' /> ```

You can find out more in the Styleguidist documentation.

Once you’ve got those two in place, installing and running Styleguidist will create the documentation like magic. Follow the instructions here to install and run it.

3. Releasing the npm package

At this stage, I had a folder of React components with unDraw illustrations, but it’s useless to any other project. Here are the steps I took to turn them into an npm module:

  1. Create a brand new React project using Facebook’s create-react-app
  2. Copy over the react components you’d like to release an npm module into src/node_modules/components of your creat-react-app project
  3. Follow these steps outlined by Pavel Lokhmakov

Finally, to publish your module, create an npm account and follow these two short videos of the npm documentation:

  1. How to create Node.js modules
  2. How to publish and update a package

That’s it! There are over 100 unDraw illustrations by Katerina Limpitsouni on unDraw. At the moment, I’ve only added a handful of those to the unDraw npm package, but will be adding more each week.

Check out the GitHub repository here. I’ll also be releasing the code for the LittleLingua soon, the website that makes use of this unDraw npm package. It’s built with unDraw’s production-ready MIT licensed theme, called evie, which I’ve also converted into React components.

To learn more about transforming SVG illustrations into components, check out Elizabet Oliveira’s talk about her side project, React Kawaii which was also nominated as “Fun Side Project of the Year” at the React Amsterdam Open Source Awards:

The post How to Create a Component Library From SVG Illustrations appeared first on CSS-Tricks.

List Rendering and Vue’s v-for Directive

Css Tricks - Wed, 04/11/2018 - 3:56am

List rendering is one of the most commonly used practices in front-end web development. Dynamic list rendering is often used to present a series of similarly grouped information in a concise and friendly format to the user. In almost every web application we use, we can see lists of content in numerous areas of the app.

In this article we'll gather an understanding of Vue’s v-for directive in generating dynamic lists, as well as go through some examples of why the key attribute should be used when doing so.

Since we'll be explaining things thoroughly as we start to write code, this article assumes you’ll have no or very little knowledge with Vue (and/or other JavaScript frameworks).

Case Study: Twitter

We’re going to use Twitter as the case study for this article.

When logged in and in the main index route of Twitter we’re presented with a view similar to this:

On the homepage, we’ve become accustomed to seeing a list of trends, a list of tweets, a list of potential followers, etc. The content displayed in these lists depends on a multitude of factors—our Twitter history, who we follow, our likes, etc. As a result, we can say all this data is dynamic.

Though this data is dynamically obtained, the way this data is shown remains the same. This is in part due to using reusable web components.

For example; we can see the list of tweets as a list of single tweet-component items. We can think of tweet-component as a shell that takes data of sorts, such as the username, handle, tweet and avatar, among other pieces, and that simply displays those pieces in a consistent markup.

Let’s say we wanted to render a list of components (e.g. a list of tweet-component items) based on a large data source obtained from a server. In Vue, the first thing that should come to mind to accomplish this is the v-for directive.

The v-for directive

The v-for directive is used to render a list of items based on a data source. The directive can be used on a template element and requires a specific syntax along the lines of:

Let’s see an example of this in practice. First, we’ll assume we’ve already obtained a collection of tweet data:

const tweets = [ { id: 1, name: 'James', handle: '@jokerjames', img: 'https://semantic-ui.com/images/avatar2/large/matthew.png', tweet: "If you don't succeed, dust yourself off and try again.", likes: 10, }, { id: 2, name: 'Fatima', handle: '@fantasticfatima', img: 'https://semantic-ui.com/images/avatar2/large/molly.png', tweet: 'Better late than never but never late is better.', likes: 12, }, { id: 3, name: 'Xin', handle: '@xeroxin', img: 'https://semantic-ui.com/images/avatar2/large/elyse.png', tweet: 'Beauty in the struggle, ugliness in the success.', likes: 18, } ]

tweets is a collection of tweet objects with each tweet containing details of that particular tweet—a unique identifier, the name/handle of the account, tweet message, etc. Let’s now attempt to use the v-for directive to render a list of tweet components based on this data.

First and foremost, we’ll create the Vue instance—the heart of the Vue application. We’ll mount/attach our instance to a DOM element of id app and assign the tweets collection as part of the instance’s data object.

new Vue({ el: '#app', data: { tweets } });

We’ll now go ahead and create a tweet-component that our v-for directive will use to render a list. We’ll use the global Vue.component constructor to create a component named tweet-component:

Vue.component('tweet-component', { template: ` <div class="tweet"> <div class="box"> <article class="media"> <div class="media-left"> <figure class="image is-64x64"> <img :src="tweet.img" alt="Image"> </figure> </div> <div class="media-content"> <div class="content"> <p> <strong>{{tweet.name}}</strong> <small>{{tweet.handle}}</small> <br> {{tweet.tweet}} </p> </div> <div class="level-left"> <a class="level-item"> <span class="icon is-small"><i class="fas fa-heart"></i></span> <span class="likes">{{tweet.likes}}</span> </a> </div> </div> </article> </div> </div> `, props: { tweet: Object } });

A few interesting things to note here.

  1. The tweet-component expects a tweet object prop as seen in the prop validation requirement (props: {tweet: Object}). If the component is rendered with a tweet prop that is not an object, Vue will emit warnings.
  2. We’re binding the properties of the tweet object prop on to the component template with the help of the Mustache syntax: {{ }}.
  3. The component markup adapts Bulma’s Box element as it represents a good resemblance to a tweet.

In the HTML template, we’ll need to create the markup where our Vue app will be mounted (i.e. the element with the id of app). Within this markup, we’ll use the v-for directive to render a list of tweets. Since tweets is the data collection we’ll be iterating over, tweet will be an appropriate alias to use in the directive. In each rendered tweet-component, we’ll also pass in the iterated tweet object as props for it to be accessed in the component.

<div id="app" class="columns"> <div class="column"> <tweet-component v-for="tweet in tweets" :tweet="tweet"/> </div> </div>

Regardless of how many more tweet objects would be introduced to the collection; or how they’ll change over time—our set up will always render all the tweets in the collection in the same markup we expect.

With the help of some custom CSS, our app will look something like this:

See the Pen Simple Twitter Feed #1 by Hassan Dj (@itslit) on CodePen.

Though everything works as expected, we may be prompted with a Vue tip in our browser console:

[Vue tip]: <tweet-component v-for="tweet in tweets">: component lists rendered with v-for should have explicit keys...

You may not be able to see the warning in the browser console when running the code through CodePen.

Why is Vue telling us to specify explicit keys in our list when everything works as expected?

key

It’s common practice to specify a key attribute for every iterated element within a rendered v-for list. This is because Vue uses the key attribute to create unique bindings for each node’s identity.

Let’s explain this some more—if there were any dynamic UI changes to our list (e.g. order of list items gets shuffled), Vue will opt towards changing data within each element instead of moving the DOM elements accordingly. This won’t be an issue in most cases. However, in certain instances where our v-for list depends on DOM state and/or child component state, this can cause some unintended behavior.

Let’s see an example of this. What if our simple tweet component now contained an input field that will allow the user to directly respond to the tweet message? We’ll ignore how this response could be submitted and simply address the new input field itself:

We’ll include this new input field on to the template of tweet-component:

Vue.component('tweet-component', { template: ` <div class="tweet"> <div class="box"> // ... </div> <div class="control has-icons-left has-icons-right"> <input class="input is-small" placeholder="Tweet your reply..." /> <span class="icon is-small is-left"> <i class="fas fa-envelope"></i> </span> </div> </div> `, props: { tweet: Object } });

Assume we wanted to introduce another new feature into our app. This feature would involve allowing the user to shuffle a list of tweets randomly.

To do this; we can first include a “Shuffle!” button in our HTML template:

<div id="app" class="columns"> <div class="column"> <button class="is-primary button" @click="shuffle">Shuffle!</button> <tweet-component v-for="tweet in tweets" :tweet="tweet"/> </div> </div>

We’ve attached a click event listener on the button element to call a shuffle method when triggered. In our Vue instance; we’ll create the shuffle method responsible in randomly shuffling the tweets collection in the instance. We’ll use lodash’s _shuffle method to achieve this:

new Vue({ el: '#app', data: { tweets }, methods: { shuffle() { this.tweets = _.shuffle(this.tweets) } } });

Let’s try it out! If we click shuffle a few times; we’ll notice our tweet elements get randomly assorted with each click.

See the Pen Simple Twitter Feed #2 by Hassan Dj (@itslit) on CodePen.

However, if we type some information in the input of each component then click shuffle; we’ll notice something peculiar happening:

Since we haven’t opted to using the key attribute, Vue has not created unique bindings to each tweet node. As a result, when we’re aiming to reorder the tweets, Vue takes the more performant saving approach to simply change (or patch) data in each element. Since the temporary DOM state (i.e. the inputted text) remains in place, we experience this unintended mismatch.

Here’s a diagram that shows us the data that gets patched on to each element and the DOM state that remains in place:

To avoid this; we’ll have to assign a unique key to every tweet-component rendered in the list. We’ll use the id of a tweet to be the unique identifier since we should safely say a tweet’s id shouldn’t be equal to that of another. Because we’re using dynamic values, we’ll use the v-bind directive to bind our key to the tweet.id:

<div id="app" class="columns"> <div class="column"> <button class="is-primary button" @click="shuffle">Shuffle!</button> <tweet-component v-for="tweet in tweets" :tweet="tweet" :key="tweet.id" /> </div> </div>

Now, Vue recognizes each tweet’s node’s identity; and thus will reorder the components when we intend on shuffling the list.

Since each tweet component is now being moved accordingly, we can take this a step further and use Vue’s transition-group to show how the elements are being reordered.

To do this, we’ll add the transition-group element as a wrapper to the v-for list. We'll specify a transition name of tweets and declare that the transition group should be rendered as a div element.

<div id="app" class="columns"> <div class="column"> <button class="is-primary button" @click="shuffle">Shuffle!</button> <transition-group name="tweets" tag="div"> <tweet-component v-for="tweet in tweets" :tweet="tweet" :key="tweet.id" /> </transition-group> </div> </div>

Based on the name of the transition, Vue will automatically recognize if any CSS transitions/animations have been specified. Since we aim to invoke a transition for the movement of items in the list; Vue will look for a specified CSS transition along the lines of tweets-move (where tweets is the name given to our transition group). As a result, we'll manually introduce a .tweets-move class that has a specified type and time of transition:

#app .tweets-move { transition: transform 1s; }

This is a very brief look into applying list transitions. Be sure to check out the Vue docs for detailed information on all the different types of transitions that can be applied!

Our tweet-component elements will now transition appropriately between locations when a shuffle is invoked. Give it a try! Type some information in the input fields and click “Shuffle!” a few times.

See the Pen Simple Twitter Feed #3 by Hassan Dj (@itslit) on CodePen.

Pretty cool, right? Without the key attribute, the transition-group element can’t be used to create list transitions since the elements are patched in place instead of being reordered.

Should the key attribute always be used? It’s recommended. The Vue docs specify that the key attribute should only be omitted if:

  • We intentionally want the default manner of patching elements in place for performance reasons.
  • The DOM content is simple enough.
Conclusion

And there we have it! Hopefully this short article portrayed how useful the v-for directive is as well as provided a little more context to why the key attribute is often used. Let me know if you may have any questions/thoughts whatsoever!

If you liked my style of writing and are potentially interested in learning how to build apps with Vue.js, you may like the book Fullstack Vue: The Complete Guide to Vue.js that I helped publish! The book covers numerous facets of Vue including but not restricted to routing, simple state management, form handling, Vuex, server persistence, and testing. If you're interested, you can get more information from our website, https://fullstack.io/vue.

The post List Rendering and Vue’s v-for Directive appeared first on CSS-Tricks.

Going Offline

Css Tricks - Wed, 04/11/2018 - 3:55am

Jeremy Keith has written a new book all about service workers and offline functionality that releases at the end of the month. The first chapter is posted on A List Apart. Now that the latest versions of iOS and macOS Safari support service workers, I can’t think of a better time to learn about how progressive web apps work under the hood. In fact, here's an example of a simple offline site and a short series on making web apps work offline.

News of Jeremy's book had me going back through his previous book, Resilient Web Design, where I half-remembered this super interesting quote from Chapter 4:

If you build something using web technologies, and someone visits with a web browser, you can’t be sure how many of the web technologies will be supported. It probably won’t be 100%. But it’s also unlikely to be 0%. Some people will visit with iOS devices. Others will visit with Android devices. Some people will get 80% or 90% of what you’ve designed. Others will get just 20%, 30%, or 50%. The web isn’t a platform. It’s a continuum.

I love this idea of the web as a continuum that’s constantly improving and growing over time and so I’m sure Jeremy’s latest book will be just as fun and interesting.

Direct Link to ArticlePermalink

The post Going Offline appeared first on CSS-Tricks.

Catch these Typekit speakers this spring

Nice Web Type - Tue, 04/10/2018 - 7:15am

It’s been a busy year for the Typekit team, with several folks giving presentations around the world — starting this very week.

Dan Rhatigan (Senior Manager, Adobe Type) is giving two workshops at the -ing Creative Festival in Dubai. Dan will teach Building Words Like Pictures on Wednesday, followed by Type Reconstruction on Thursday.

Hopping over to Japan, Taro Yamamoto (Senior Manager, Font Development) will participate in the Tokyo Cross Talk Show Panel on April 20, with several other typographic luminaries. At this event, Taro will accept an award from the Japan Typography Association.

Stateside, in May, head to Boston, where Dan Rhatigan presents Your Type; Your Text at HOW Design Live on May 2. Later in the month, Persa Zula (Computer Scientist) speaks at !!Con in New York telling Tales of ?! Can You Tell Your Story When Your Character Is Undefined?!

Wrapping up the spring, Dan heads over to the UK, where he’ll speak at the 2018 Ampersand Conference in Brighton.

Want a Typekit team member to speak at your event? Feel free to reach out in the comments!

Happy Font Day, Japan! Source Han Serif turns 1, and more

Nice Web Type - Tue, 04/10/2018 - 4:18am

When we released Source Han Serif this time last year, we knew the news would be of interest in Japan, but we never expected an official holiday would emerge. April 10 is now formally registered as Font Day in Japan, and our colleagues there haven’t missed the chance to celebrate its second year.

Celebrating one year of Source Han Serif

One year since its launch, Source Han Serif has proven to be hugely popular — a typeface suitable for editorial use, with seven different weights for variety, and with an immense number of glyphs to support Chinese Simplified and Traditional, Korean, and Japanese languages (including region-specific glyphs). Ryoko Nishizuka was the principal type designer for the project, and it was an enormous collaborative effort with designers from the other foundries involved as well.

Don’t miss our walkthrough of the Source Han Serif website itself, designed by Wenting Zhang — it’s full of neat details that illustrate the potential for a Pan-CJK typeface like this one.

Ten Mincho is in our free tier

Also from Ryoko, Adobe Originals released Ten Mincho last autumn, a delightful serif typeface for Japanese (and, with Ten Oldstyle from Robert Slimbach included in the typeface, also can be used for languages using Latin characters). We’ve just added it to the free tier in our library, so you can use it without a paid Creative Cloud subscription — though you’ll still need to log in with an Adobe ID to sync it to your desktop.

Font Day in Tokyo

Our colleagues in the Adobe Japan office organized Font Day events at the Yahoo! Lodge in Kioicho, Tokyo. Ryoko Nishizuka was among the participants for a “Meet the Typeface Designers” panel, which addressed the realities of type design and what the day-to-day of the job looks like.

A second panel comprising Japanese UX, web, and graphic designers discussed general themes around typography and using digital fonts; Taro Yamamoto joined this discussion. And for a lighthearted finale, Masataka Hattori from our Type team in Japan hosted a “Font Finding Quiz” along with a few type experts.

All of the “Meet the Typeface Designers” panel participants represented foundries with typefaces available on Typekit — a great chance to get to know them if you aren’t already familiar! Apart from Adobe, look for Japanese type from Morisawa, TypeBank, Dai Nippon Printing, Fontworks, Jiyukobo, and Visual Design Laboratory.

Further reading: Two different takes on Japanese type at Adobe

Bryan Lamkin (EVP and GM of Digital Media at Adobe) offers his reflection on the Japanese type offerings from Adobe in this blog post. It’s definitely an exciting time to be working with type, and we love his enthusiasm for it!

The people who have been in the type industry for 20 or 30 years have seen some remarkable transitions in design technology, and we’re fortunate enough to have several of those people on our team here — including Taro Yamamoto, Senior Manager of Type Development on the Adobe Type team in Japan. His newly-published article, “Thirty Years of Japanese font development at Adobe,” is featured on Adobe Japan’s Creative Station blog in two parts. (For the English translations, see Part I and Part II here.)

Maybe we should make Font Day an international holiday next year and expand it to the United States? Happy Font Day to you and yours!

Displaying the Weather With Serverless and Colors

Css Tricks - Tue, 04/10/2018 - 3:48am

I like to jog. Sometimes it’s cold out. Sometimes it’s cold out, but it looks like it isn’t. The sun is shining, the birds are chirping. Then you step outside in shorts and a t-shirt and realize you have roughly 2 minutes before exposure sets in.

I decided to solve this first world problem using a lightbulb to display a certain color based on what the temperature outside is. It works better than I expected, and that’s saying something because usually nothing works out like I want it to.

This was a fun project to build, and since it is essentially a hosted service running on a timer, it’s a perfect use case for Serverless.

Now you might be thinking, “um, wouldn’t it be easier to just check the weather?” Well, it would, but then I wouldn’t have an excuse to buy an expensive lightbulb or write an article with the word “Serverless.”

So let’s look at how you can build your own Weather Bulb. The final code is not complicated, but it does have some interesting pieces that are worth noting. By the way, did I mention that it’s Serverless?

Building the Weather Bulb

The first thing you are going to need is the bulb. You can’t have a Weather Bulb sans bulb. Say the word “bulb” out loud about 10 times and you’ll notice what a bizarre word it is. Bulb, bulb, bulb, bulb — see? Weird.

I am using the LIFX Mini Color. It’s not *too* expensive, but more importantly, it’s got an API that is wide open.

The API has two methods of authentication. The first contains the word “OAuth” and I’m already sorry that you had to read that. Don’t worry, there is an easier way that doesn’t involve OAu.... that which won’t be named.

The second way is to register an application with LIFX. You get back a key and all you have to do is pass that key with any HTTP request. That’s what I’m using for this demo.

For instance, if we wanted to change the bulb color to blue, we can just pass color: blue to the /state endpoint.

The API supports a few different color formats, including named colors (like red, blue), hex values, RBG, Kevlin, hue brightness and saturation. This is important because it factors into what proved to be the hardest part of this project: turning temperature into color.

Representing Temperature With Color

If you’ve ever watched a weather report, you’ll be familiar with the way that meteorology represents weather conditions with color on a map.

Usually, this is done to visualize precipitation. You have probably seen that ominous green strip of storms bearing down on you on a weather map while you try to figure out if you should get in the bathtub because you’re in the path of a tornado. Or maybe that’s just all of us unlucky souls here in America’s Tornado Alley.

Color is also used to represent temperature. This is precisely what I wanted to do with the bulb. The tough thing is that there doesn’t seem to be a standardized way to do this. Some maps show it as solid colors in bands. In this case, blue might represent the band from 0? - 32?.

Others have it as a gradient scale which is more precise. This is what I was after for the Weather Bulb.

My first stab at solving this was just to Google “temperature color scale” and other various iterations of that search term. I got back a lot of information about Kelvin.

Kelvin is a representation of the temperature of a color. Literally. For any light source (light bulb, the sun, ect) the actual temperature of that source will affect the color of the light it emits. A fire burns a yellowish red color. The hotter that fire gets, the more it moves towards white. Hence the saying, “white hot”. So if someone ever says “red hot,” you can correct them in front of everyone because who doesn’t love a pedantic jerk?

The LIFX bulb supports Kelvin, so you might think that this would work. After all, this is the Kelvin scale….

The problem is that there is simply not enough color variation because these are not actual colors, but rather the tinge of color that a light is emitting based on it’s “temperature.” Here is the Kelvin color wheel that comes with the LIFX app.

These colors are barely distinguishable from one another on the bulb. Not exactly what I was after.

That leaves me with trying to convert the color to either Hex, RGB or some other format. This is tough because where do you begin? I spent an embarrassing amount of time adjust RGB scale values between blue for cold (0, 0, 255) and red for hot (255, 0, 0). It was about this time that it dawned on me that maybe HSL would be a better way to go here. Why? Because hue is a whole lot easier to understand.

Hue

Hue is a representation of color on a scale between 0 and 360. This is why we often see color represented on a wheel (360°). That’s a vast oversimplification, but unless you want me to start talking about wavelengths, let’s go with that definition.

The hue color wheel looks like this….

If we flatten it out, it’s easier to reason about.

We’re ready to convert temperature to color. The first thing we need to do is figure out a set temperature range. I went with 0? to 100?. We can’t work with infinite temperature color combinations. Numbers go on forever, colors do not. It can only get so hot before our bulb is just bright red all the time, and that’s 100?. The same is true for cold.

If light blue represents 0?, I can start at about the 200 mark on the hue scale. Red will represent 100?. You can see that red is at both extremes, so I can move either left OR right, depending on what colors I want to use to represent the temperature. It’s not the same as the colors they use in actual weather programs, but who cares? Obviously not me.

I chose to go right because there is no pink on the left and pink is my favorite color. I also felt like pink represents warm a bit better than green. Green is rain and tornadoes.

Now we can back into a hue based on temperature. Ready? Here we go.

Let’s pretend it’s a brisk 50? outside.

If 100? is the hottest we go (360) and 0? is the coldest (200), then we have a color scale of 160 points. To figure out where in that 160 point range we need to be, we can divide the current temperature by the upper bound of 100? which will give us the exact percentage we need to move in our range, or 50%. If we move 50% of the way into a 160 point range, that leaves us at 80. Since we are starting at 200, that gives us a hue of 280.

That sounds complicated, but only because word problems in math SUCK. Here’s how the code looks when it’s all said and done…

let hue = 200 + (160 * ( temperature / 100 ));

OK! We’ve got a dynamic color scale based on hue, and wouldn’t you know it, we can just pass the hue to LIFX as simply as we pass a named color.

Now we just need to find out what the current temperature is, back into a hue and do that every few minutes. Serverless, here we come!

Serverless Timer Functions

Serverless is all the rage. It’s like HTML5 used to be: it doesn’t matter what it is, it only matters that you know the word and are not afraid to use it in a blog post.

For this example, we’ll use Azure Functions because there is support for timer triggers, and we can test those timer triggers locally before we deploy using VS Code. One of the things about Serverless that irritates me to no end is when I can’t debug it locally.

Using the Azure Functions Core Tools and the Azure Functions Extension for VS Code, I can create a new Serverless project and select a Timer Trigger.

Timer Triggers in Azure Functions are specified as Cron Expressions. Don’t worry, I didn’t know what that was either.

Cron Expressions allow you to get very specific with interval definition. Cron breaks things down into second, minute, hour, day, month, year. So if you wanted to run something every second of every minute of every hour of every day of every year, your expression would look like this…

* * * * * *

If you wanted to run it every day at 10:15, it would look like this…

* 15 10 * * *

If you wanted to run it every 5 minutes (which is what Azure defaults to), you specify that by saying “when minutes is divisible by 5."

0 */5 * * * *

For the purposes of this function, we set it to 2 minutes.

I am using a 2 minute interval because that’s how often we can call the weather API for free &#x1f4b0;.

Getting the Forecast From DarkSky

DarkSky has a wonderful weather API that you can call up to 1,000 times per day for free. If there are 1,440 minutes in a day (and there are), that means we can call DarkSky every 1.44 minutes per day and stay in the free zone. I just rounded up to 2 minutes because temperature doesn’t change that fast.

This is what our function looks like when we call the DarkSky API. All of my tokens, keys, latitude and longitude settings are in environment variables so they aren’t hardcoded. Those are set in the local.settings.json file. I used axios for my HTTP requests because it is a magical, magical package.

const axios = require('axios'); module.exports = function (context, myTimer) { // build up the DarkSky endpoint let endpoint = `${process.env.DS_API}/${process.env.DS_SECRET}/${process.env.LAT}, ${process.env.LNG}`; // use axios to call DarkSky for weather axios .get(endpoint) .then(response => { let temp = Math.round(response.data.currently.temperature); // TODO: Set the color of the LIFX bulb }) .catch(err => { context.log(err.message); }); };

Now that I have the temperature, I need to call the LIFX API. And wouldn’t you know it, someone has already created an npm package to do this called lifx-http-api. This is why you love JavaScript.

Setting the Bulb Hue

After the weather result comes back, I need to use the LIFX API instance and call the setState method. This method returns a promise which means that we need to nest promises. Nesting promises can get out of hand and could land us right back in callback hell, which is what we’re trying to avoid with promises in the first place.

Instead, we’ll handle the first promise and then return Promise.all which we can handle at another top-level then. This just prevents us from nesting then statements.

Remember kids, promises are just socially acceptable callbacks.

const axios = require('axios'); const LIFX = require('lifx-http-api'); let client = new LIFX({ bearerToken: process.env.LIFX_TOKEN }); module.exports = function (context, myTimer) { // build up the DarkSky endpoint let endpoint = <code>${process.env.DS_API}/${process.env.DS_SECRET}/${ process.env.LAT },${process.env.LNG}<code>; // use axios to call DarkSky for weather axios .get(endpoint) .then(response => { let temp = Math.round(response.data.currently.temperature); // make sure the temp isn't above 100 because that's as high as we can go temp = temp < 100 ? temp : 100; // determine the hue let hue = 200 + (160 * (temp / 100)); // return Promise.all so we can resolve at the top level return Promise.all([ data, client.setState('all', { color: <code>hue:${hue}<code> }) ]); }) .then(result => { // result[0] contains the darksky result // result[1] contains the LIFX result context.log(result[1]); }) .catch(err => { context.log(err.message); }); };

Now we can run this thing locally and watch our timer do it’s thang.

That’s it! Let’s deploy it.

Deploying Weather Bulb

I can create a new Functions project from the VS Code extension.

I can right-click that to “Open in portal” where I can define a deployment source so it sucks my code in from Github and deploys it. This is ideal because now whenever I push a change to Github, my application automatically gets redeployed.

All Hail the Weather Bulb

Now just sit back and behold the soft glow of the Weather Bulb! Why look at the actual temperature when you can look at this beautiful shade of hot pink instead?

Can you guess what the temperature is based on what you know from this article? The person who leaves a comment and gets the closest will get a free LIFX lightbulb from me (because I ❤️ all of you), or the cost of the bulb if you are outside the U.S. (~$40).

You can grab all of the code for this project from Github.

The post Displaying the Weather With Serverless and Colors appeared first on CSS-Tricks.

Simple Swipe With Vanilla JavaScript

Css Tricks - Mon, 04/09/2018 - 3:00am

I used to think implementing swipe gestures had to be very difficult, but I have recently found myself in a situation where I had to do it and discovered the reality is nowhere near as gloomy as I had imagined.

This article is going to take you, step by step, through the implementation with the least amount of code I could come up with. So, let's jump right into it!

The HTML Structure

We start off with a .container that has a bunch of images inside:

<div class='container'> <img src='img1.jpg' alt='image description'/> ... </div> Basic Styles

We use display: flex to make sure images go alongside each other with no spaces in between. align-items: center middle aligns them vertically. We make both the images and the container take the width of the container's parent (the body in our case).

.container { display: flex; align-items: center; width: 100%; img { min-width: 100%; /* needed so Firefox doesn't make img shrink to fit */ width: 100%; /* can't take this out either as it breaks Chrome */ } }

The fact that both the .container and its child images have the same width makes these images spill out on the right side (as highlighted by the red outline) creating a horizontal scrollbar, but this is precisely what we want:

The initial layout (see live demo).

Given that not all the images have the same dimensions and aspect ratio, we have a bit of white space above and below some of them. So, we're going to trim that by giving the .container an explicit height that should pretty much work for the average aspect ratio of these images and setting overflow-y to hidden:

.container { /* same as before */ overflow-y: hidden; height: 50vw; max-height: 100vh; }

The result can be seen below, with all the images trimmed to the same height and no empty spaces anymore:

The result after images are trimmed by overflow-y on the .container (see live demo).

Alright, but now we have a horizontal scrollbar on the .container itself. Well, that's actually a good thing for the no JavaScript case.

Otherwise, we create a CSS variable --n for the number of images and we use this to make .container wide enough to hold all its image children that still have the same width as its parent (the body in this case):

.container { --n: 1; width: 100%; width: calc(var(--n)*100%); img { min-width: 100%; width: 100%; width: calc(100%/var(--n)); } }

Note that we keep the previous width declarations as fallbacks. The calc() values won't change a thing until we set --n from the JavaScript after getting our .container and the number of child images it holds:

const _C = document.querySelector('.container'), N = _C.children.length; _C.style.setProperty('--n', N)

Now our .container has expanded to fit all the images inside:

Layout with expanded container (live demo). Switching Images

Next, we get rid of the horizontal scrollbar by setting overflow-x: hidden on our container's parent (the body in our case) and we create another CSS variable that holds the index of the currently selected image (--i). We use this to properly position the .container with respect to the viewport via a translation (remember that % values inside translate() functions are relative to the dimensions of the element we have set this transform on):

body { overflow-x: hidden } .container { /* same styles as before */ transform: translate(calc(var(--i, 0)/var(--n)*-100%)); }

Changing the --i to a different integer value greater or equal to zero, but smaller than --n, brings another image into view, as illustrated by the interactive demo below (where the value of --i is controlled by a range input):

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

Alright, but we don't want to use a slider to do this.

The basic idea is that we're going to detect the direction of the motion between the "touchstart" (or "mousedown") event and the "touchend" (or "mouseup") and then update --i accordingly to move the container such that the next image (if there is one) in the desired direction moves into the viewport.

function lock(e) {}; function move(e) {}; _C.addEventListener('mousedown', lock, false); _C.addEventListener('touchstart', lock, false); _C.addEventListener('mouseup', move, false); _C.addEventListener('touchend', move, false);

Note that this will only work for the mouse if we set pointer-events: none on the images.

.container { /* same styles as before */ img { /* same styles as before */ pointer-events: none; } }

Also, Edge needs to have touch events enabled from about:flags as this option is off by default:

Enabling touch events in Edge.

Before we populate the lock() and move() functions, we unify the touch and click cases:

function unify(e) { return e.changedTouches ? e.changedTouches[0] : e };

Locking on "touchstart" (or "mousedown") means getting and storing the x coordinate into an initial coordinate variable x0:

let x0 = null; function lock(e) { x0 = unify(e).clientX };

In order to see how to move our .container (or if we even do that because we don't want to move further when we have reached the end), we check if we have performed the lock() action, and if we have, we read the current x coordinate, compute the difference between it and x0 and resolve what to do out of its sign and the current index:

let i = 0; function move(e) { if(x0 || x0 === 0) { let dx = unify(e).clientX - x0, s = Math.sign(dx); if((i > 0 || s < 0) && (i < N - 1 || s > 0)) _C.style.setProperty('--i', i -= s); x0 = null } };

The result on dragging left/ right can be seen below:

Switching between images on swipe (live demo). Attempts to move to the right on the first image or left on the last one do nothing as we have no other image before or after, respectively.

The above is the expected result and the result we get in Chrome for a little bit of drag and Firefox. However, Edge navigates backward and forward when we drag left or right, which is something that Chrome also does on a bit more drag.

Edge navigating the pageview backward or forward on left or right swipe.

In order to override this, we need to add a "touchmove" event listener:

_C.addEventListener('touchmove', e => {e.preventDefault()}, false)

Alright, we now have something functional in all browsers, but it doesn't look like what we're really after... yet!

Smooth Motion

The easiest way to move towards getting what we want is by adding a transition:

.container { /* same styles as before */ transition: transform .5s ease-out; }

And here it is, a very basic swipe effect in about 25 lines of JavaScript and about 25 lines of CSS:

Working swipe effect (live demo).

Sadly, there's an Edge bug that makes any transition to a CSS variable-depending calc() translation fail. Ugh, I guess we have to forget about Edge for now.

Refining the Whole Thing

With all the cool swipe effects out there, what we have so far doesn't quite cut it, so let's see what improvements can be made.

Better Visual Cues While Dragging

First off, nothing happens while we drag, all the action follows the "touchend" (or "mouseup") event. So, while we drag, we have no indication of what's going to happen next. Is there a next image to switch to in the desired direction? Or have we reached the end of the line and nothing will happen?

To take care of that, we tweak the translation amount a bit by adding a CSS variable --tx that's originally 0px:

transform: translate(calc(var(--i, 0)/var(--n)*-100% + var(--tx, 0px)))

We use two more event listeners: one for "touchmove" and another for "mousemove". Note that we were already preventing backward and forward navigation in Chrome using the "touchmove" listener:

function drag(e) { e.preventDefault() }; _C.addEventListener('mousemove', drag, false); _C.addEventListener('touchmove', drag, false);

Now let's populate the drag() function! If we have performed the lock() action, we read the current x coordinate, compute the difference dx between this coordinate and the initial one x0 and set --tx to this value (which is a pixel value).

function drag(e) { e.preventDefault(); if(x0 || x0 === 0) _C.style.setProperty('--tx', `${Math.round(unify(e).clientX - x0)}px`) };

We also need to make sure to reset --tx to 0px at the end and remove the transition for the duration of the drag. In order to make this easier, we move the transition declaration on a .smooth class:

.smooth { transition: transform .5s ease-out; }

In the lock() function, we remove this class from the .container (we'll add it again at the end on "touchend" and "mouseup") and also set a locked boolean variable, so we don't have to keep performing the x0 || x0 === 0 check. We then use the locked variable for the checks instead:

let locked = false; function lock(e) { x0 = unify(e).clientX; _C.classList.toggle('smooth', !(locked = true)) }; function drag(e) { e.preventDefault(); if(locked) { /* same as before */ } }; function move(e) { if(locked) { let dx = unify(e).clientX - x0, s = Math.sign(dx); if((i > 0 || s < 0) && (i < N - 1 || s > 0)) _C.style.setProperty('--i', i -= s); _C.style.setProperty('--tx', '0px'); _C.classList.toggle('smooth', !(locked = false)); x0 = null } };

The result can be seen below. While we're still dragging, we now have a visual indication of what's going to happen next:

Swipe with visual cues while dragging (live demo). Fix the transition-duration

At this point, we're always using the same transition-duration no matter how much of an image's width we still have to translate after the drag. We can fix that in a pretty straightforward manner by introducing a factor f, which we also set as a CSS variable to help us compute the actual animation duration:

.smooth { transition: transform calc(var(--f, 1)*.5s) ease-out; }

In the JavaScript, we get an image's width (updated on "resize") and compute for what fraction of this we have dragged horizontally:

let w; function size() { w = window.innerWidth }; function move(e) { if(locked) { let dx = unify(e).clientX - x0, s = Math.sign(dx), f = +(s*dx/w).toFixed(2); if((i > 0 || s < 0) && (i < N - 1 || s > 0)) { _C.style.setProperty('--i', i -= s); f = 1 - f } _C.style.setProperty('--tx', '0px'); _C.style.setProperty('--f', f); _C.classList.toggle('smooth', !(locked = false)); x0 = null } }; size(); addEventListener('resize', size, false);

This now gives us a better result.

Go back if insufficient drag

Let's say that we don't want to move on to the next image if we only drag a little bit below a certain threshold. Because now, a 1px difference during the drag means we advance to the next image and that feels a bit unnatural.

To fix this, we set a threshold at let's say 20% of an image's width:

function move(e) { if(locked) { let dx = unify(e).clientX - x0, s = Math.sign(dx), f = +(s*dx/w).toFixed(2); if((i > 0 || s < 0) && (i < N - 1 || s > 0) && f > .2) { /* same as before */ } /* same as before */ } };

The result can be seen below:

We only advance to the next image if we drag enough (live demo). Maybe Add a Bounce?

This is something that I'm not sure was a good idea, but I was itching to try anyway: change the timing function so that we introduce a bounce. After a bit of dragging the handles on cubic-bezier.com, I came up with a result that seemed promising:

What our chosen cubic Bézier timing function looks like compared to a plain ease-out. transition: transform calc(var(--f)*.5s) cubic-bezier(1, 1.59, .61, .74); Using a custom CSS timing function to introduce a bounce (live demo). How About the JavaScript Way, Then?

We could achieve a better degree of control over more natural-feeling and more complex bounces by taking the JavaScript route for the transition. This would also give us Edge support.

We start by getting rid of the transition and the --tx and --f CSS variables. This reduces our transform to what it was initially:

transform: translate(calc(var(--i, 0)/var(--n)*-100%));

The above code also means --i won't necessarily be an integer anymore. While it remains an integer while we have a single image fully into view, that's not the case anymore while we drag or during the motion after triggering the "touchend" or "mouseup" events.

For example, while we have the first image fully in view, --i is 0. While we have the second one fully in view, --i is 1. When we're midway between the first and the second, --i is .5. When we have a quarter of the first one and three quarters of the second one in view, --i is .75.

We then update the JavaScript to replace the code parts where we were updating these CSS variables. First, we take care of the lock() function, where we ditch toggling the .smooth class and of the drag() function, where we replace updating the --tx variable we've ditched with updating --i, which, as mentioned before, doesn't need to be an integer anymore:

function lock(e) { x0 = unify(e).clientX; locked = true }; function drag(e) { e.preventDefault(); if(locked) { let dx = unify(e).clientX - x0, f = +(dx/w).toFixed(2); _C.style.setProperty('--i', i - f) } };

Before we also update the move() function, we introduce two new variables, ini and fin. These represent the initial value we set --i to at the beginning of the animation and the final value we set the same variable to at the end of the animation. We also create an animation function ani():

let ini, fin; function ani() {}; function move(e) { if(locked) { let dx = unify(e).clientX - x0, s = Math.sign(dx), f = +(s*dx/w).toFixed(2); ini = i - s*f; if((i > 0 || s < 0) && (i < N - 1 || s > 0) && f > .2) { i -= s; f = 1 - f } fin = i; ani(); x0 = null; locked = false; } };

This is not too different from the code we had before. What has changed is that we're not setting any CSS variables in this function anymore but instead set the ini and the fin JavaScript variables and call the animation ani() function.

ini is the initial value we set --i to at the beginning of the animation that the "touchend"/ "mouseup" event triggers. This is given by the current position we have when one of these two events fires.

fin is the final value we set --i to at the end of the same animation. This is always an integer value because we always end with one image fully into sight, so fin and --i are the index of that image. This is the next image in the desired direction if we dragged enough (f > .2) and if there is a next image in the desired direction ((i > 0 || s < 0) && (i < N - 1 || s > 0)). In this case, we also update the JavaScript variable storing the current image index (i) and the relative distance to it (f). Otherwise, it's the same image, so i and f don't need to get updated.

Now, let's move on to the ani() function. We start with a simplified linear version that leaves out a change of direction.

const NF = 30; let rID = null; function stopAni() { cancelAnimationFrame(rID); rID = null }; function ani(cf = 0) { _C.style.setProperty('--i', ini + (fin - ini)*cf/NF); if(cf === NF) { stopAni(); return } rID = requestAnimationFrame(ani.bind(this, ++cf)) };

The main idea here is that the transition between the initial value ini and the final one fin happens over a total number of frames NF. Every time we call the ani() function, we compute the progress as the ratio between the current frame index cf and the total number of frames NF. This is always a number between 0 and 1 (or you can take it as a percentage, going from 0% to 100%). We then use this progress value to get the current value of --i and set it in the style attribute of our container _C. If we got to the final state (the current frame index cf equals the total number of frames NF, we exit the animation loop). Otherwise, we just increment the current frame index cf and call ani() again.

At this point, we have a working demo with a linear JavaScript transition:

Version with linear JavaScript transition (live demo).

However, this has the problem we initially had in the CSS case: no matter the distance, we have to have to smoothly translate our element over on release ("touchend" / "mouseup") and the duration is always the same because we always animate over the same number of frames NF.

Let's fix that!

In order to do so, we introduce another variable anf where we store the actual number of frames we use and whose value we compute in the move() function, before calling the animation function ani():

function move(e) { if(locked) { let dx = unify(e).clientX - x0, s = Math.sign(dx), f = +(s*dx/w).toFixed(2); /* same as before */ anf = Math.round(f*NF); ani(); /* same as before */ } };

We also need to replace NF with anf in the animation function ani():

function ani(cf = 0) { _C.style.setProperty('--i', ini + (fin - ini)*cf/anf); if(cf === anf) { /* same as before */ } /* same as before */ };

With this, we have fixed the timing issue!

Version with linear JavaScript transition at constant speed (live demo).

Alright, but a linear timing function isn't too exciting.

We could try the JavaScript equivalents of CSS timing functions such as ease-in, ease-out or ease-in-out and see how they compare. I've already explained in a lot of detail how to get these in the previously linked article, so I'm not going to go through that again and just drop the object with all of them into the code:

const TFN = { 'linear': function(k) { return k }, 'ease-in': function(k, e = 1.675) { return Math.pow(k, e) }, 'ease-out': function(k, e = 1.675) { return 1 - Math.pow(1 - k, e) }, 'ease-in-out': function(k) { return .5*(Math.sin((k - .5)*Math.PI) + 1) } };

The k value is the progress, which is the ratio between the current frame index cf and the actual number of frames the transition happens over anf. This means we modify the ani() function a bit if we want to use the ease-out option for example:

function ani(cf = 0) { _C.style.setProperty('--i', ini + (fin - ini)*TFN['ease-out'](cf/anf)); /* same as before */ }; Version with ease-out JavaScript transition (live demo).

We could also make things more interesting by using the kind of bouncing timing function that CSS cannot give us. For example, something like the one illustrated by the demo below (click to trigger a transition):

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

The graphic for this would be somewhat similar to that of the easeOutBounce timing function from easings.net.

Graphical representation of the timing function.

The process for getting this kind of timing function is similar to that for getting the JavaScript version of the CSS ease-in-out (again, described in the previously linked article on emulating CSS timing functions with JavaScript).

We start with the cosine function on the [0, 90°] interval (or [0, ?/2] in radians) for no bounce, [0, 270°] ([0, 3·?/2]) for 1 bounce, [0, 450°] ([0, 5·?/2]) for 2 bounces and so on... in general it's the [0, (n + ½)·180°] interval ([0, (n + ½)·?]) for n bounces.

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

The input of this cos(k) function is in the [0, 450°] interval, while its output is in the [-1, 1] interval. But what we want is a function whose domain is the [0, 1] interval and whose codomain is also the [0, 1] interval.

We can restrict the codomain to the [0, 1] interval by only taking the absolute value |cos(k)|:

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

While we got the interval we wanted for the codomain, we want the value of this function at 0 to be 0 and its value at the other end of the interval to be 1. Currently, it's the other way around, but we can fix this if we change our function to 1 - |cos(k)|:

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

Now we can move on to restricting the domain from the [0, (n + ½)·180°] interval to the [0, 1] interval. In order to do this, we change our function to be 1 - |cos(k·(n + ½)·180°)|:

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

This gives us both the desired domain and codomain, but we still have some problems.

First of all, all our bounces have the same height, but we want their height to decrease as k increases from 0 to 1. Our fix in this case is to multiply the cosine with 1 - k (or with a power of 1 - k for a non-linear decrease in amplitude). The interactive demo below shows how this amplitude changes for various exponents a and how this influences the function we have so far:

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

Secondly, all the bounces take the same amount of time, even though their amplitudes keep decreasing. The first idea here is to use a power of k inside the cosine function instead of just k. This manages to make things weird as the cosine doesn't hit 0 at equal intervals anymore, meaning we don't always get that f(1) = 1 anymore which is what we'd always need from a timing function we're actually going to use. However, for something like a = 2.75, n = 3 and b = 1.5, we get a result that looks satisfying, so we'll leave it at that, even though it could be tweaked for better control:

The timing function we want to try.

This is the function we try out in the JavaScript if we want some bouncing to happen.

const TFN = { /* the other function we had before */ 'bounce-out': function(k, n = 3, a = 2.75, b = 1.5) { return 1 - Math.pow(1 - k, a)*Math.abs(Math.cos(Math.pow(k, b)*(n + .5)*Math.PI)) } };

Hmm, seems a bit too extreme in practice:

Version with a bouncing JavaScript transition (live demo).

Maybe we could make n depend on the amount of translation we still need to perform from the moment of the release. We make it into a variable which we then set in the move() function before calling the animation function ani():

const TFN = { /* the other function we had before */ 'bounce-out': function(k, a = 2.75, b = 1.5) { return 1 - Math.pow(1 - k, a)*Math.abs(Math.cos(Math.pow(k, b)*(n + .5)*Math.PI)) } }; var n; function move(e) { if(locked) { let dx = unify(e).clientX - x0, s = Math.sign(dx), f = +(s*dx/w).toFixed(2); /* same as before */ n = 2 + Math.round(f) ani(); /* same as before */ } };

This gives us our final result:

Version with the final bouncing JavaScript transition (live demo).

There's definitely still room for improvement, but I don't have a feel for what makes a good animation, so I'll just leave it at that. As it is, this is now functional cross-browser (without have any of the Edge issues that the version using a CSS transition has) and pretty flexible.

The post Simple Swipe With Vanilla JavaScript appeared first on CSS-Tricks.

`:focus-visible` and backwards compatibility

Css Tricks - Sun, 04/08/2018 - 2:56am

Patrick H. Lauke covers the future CSS pseudo class :focus-visible. We're in the early days of browser support, but it aims to solve an awkward situation:

... focus styles can often be undesirable when they are applied as a result of a mouse/pointer interaction. A classic example of this are buttons which trigger a particular action on a page, such as advancing a carousel. While it is important that a keyboard user is able to see when their focus is on the button, it can be confusing for a mouse user to find the look of the button change after they clicked it – making them wonder why the styles “stuck”, or if the state/functionality of the button has somehow changed.

If we use :focus-within instead of :focus, that gives the browser the freedom to not apply focus styles when it determines it's unnecessary, but still does when, for example, the element is tabbed to.

The scary part is "instead of". We can just up and switch with browser support as it is. Not even @supports can help us. But Patrick has some ideas.

button:focus { /* some exciting button focus styles */ } button:focus:not(:focus-visible) { /* undo all the above focused button styles if the button has focus but the browser wouldn't normally show default focus styles */ } button:focus-visible { /* some even *more* exciting button focus styles */ }

Direct Link to ArticlePermalink

The post `:focus-visible` and backwards compatibility appeared first on CSS-Tricks.

Keep Pixelated Images Pixelated as They Scale

Css Tricks - Sat, 04/07/2018 - 6:20am

This is a little reminder that there is a CSS property for helping control what happens to images as they scale up: image-rendering.

We're quite used to the idea that scaling an image larger than its natural size (upscaling) causes it to be blurry. As awful as that is, it's the browser doing the best it can to algorithmically smooth out an image over more pixels than it has data. But let's say you'd really rather not it do that. Say the image is already pixel-y (pixel art), or you prefer the look of a pixelated upscaling.

You can do it!

img { image-rendering: pixelated; image-rendering: -moz-crisp-edges; image-rendering: crisp-edges; }

It's a bit awkward in that the spec offers three values: auto, pixelated, and crisp-edges. Both pixelated and crisp-edges, for pixel art, appear to do the same thing to me, although the spec talks about them slightly differently (pixelated recommends the "nearest neighbor" or similar algorithm while crisp-edges isn't as specific).

Adding to the awkwardness, Chrome only supports pixelated and Firefox only supports crisp-edges, and for the deepest browser support, you gotta prefix it to -moz-crisp-edges. Fortunately, you can smash them all together and it seems fine.

Here's an example with and without, using an image from James T. I found on Tumblr:

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

The post Keep Pixelated Images Pixelated as They Scale appeared first on CSS-Tricks.

“Just”

Css Tricks - Fri, 04/06/2018 - 8:17am

Brad Frost's "Just" article from a few years ago has struck a fresh nerve with folks. It's a simple word that can slip out easily, that might be invoked to keep text casual-feeling, but the result can be damaging. Brad:

The amount of available knowledge in our field (or any field really) is growing larger, more complex, and more segmented all the time. That everyone has downloaded the same fundamental knowledge on any topic is becoming less and less probable. Because of this, we have to be careful not to make too many assumptions in our documentation, blog posts, tutorials, wikis, and communications.

Imagine yourself explaining a particular task to an earlier version of yourself. Once upon a time, you didn’t know what you know now. Provide context. The beauty of hypertext is that we’re able to quickly add much-needed context helpful for n00bs but easy enough for those already in-the-know to scan over. And making documentation more human-readable benefits everyone.

Ethan Marcotte takes this one step further:

I’ve noticed a rhetorical trope in our industry. It’s not, like, widespread, but I see it in enough blog entries and conference talks that I think it’s a pretty common pattern: namely, the author’s sharing some advice with the reader and, if the reader’s boss or stakeholders won’t support a given course of action, suggests the reader “just do the thing anyway.”

I think this is a bad, harmful trope. And I also think we should avoid using it.

"Just" is more insidious than the more overtly painful "Obviously" or "Simply". In fact, there is a whole list of words that could go. The result of not using words like this? Cleaner sentences and more inclusive writing. Wanna make a difference? Be like Jeremy Keith and submit Pull Requests when you see the opportunity.

The best teachers I’ve had were ones that were cautious not to make me feel dumb.

Direct Link to ArticlePermalink

The post “Just” appeared first on CSS-Tricks.

Methods, Computed, and Watchers in Vue.js

Css Tricks - Fri, 04/06/2018 - 4:11am

One of the reasons I love working with Vue is because of how useful methods, computed, and watchers are, and the legibility of their distinction. Until understanding all three, it’s difficult to leverage the functionality of Vue to its full potential. Still, the majority of people I see confused about this framework tend to also be confused about the differences here, so let’s dig in.

In case you need a quick answer and don’t have time to read through the entire article, here’s a small TL;DR:

  • Methods: These are exactly what they sound like they might be (yay, naming!). They’re functions that hang off of an object—typically the Vue instance itself or a Vue component.
  • Computed: These properties may at first look like they'd be used like a method, but are not. In Vue, we use data to track changes to a particular property that we’d like to be reactive. Computed properties allow us to define a property that is used the same way as data, but can also have some custom logic that is cached based on its dependencies. You can consider computed properties another view into your data.
  • Watchers: These are allowing you a peek into the reactivity system. We’re offered some hooks with which to observe any properties that are stored by Vue. If we want to add a bit of functionality each time something changes, or respond to a particular change, we could watch a property and apply some logic. This means that the name of the watcher has to match what we’re trying to observe.

If any of this sounds confusing, don’t worry! We’ll dive in further below and hopefully address any confusion. If you’re familiar with vanilla JavaScript already, methods may be pretty obvious to you, aside from one or two caveats. It might then behoove you (I love that phrase) to skip to the Computed and Watchers sections.

Methods

Methods are likely something you’re going to use a lot while working with Vue. They’re aptly named as, in essence, we’re hanging a function off of an object. They’re incredibly useful for connecting functionality to directives for events, or even just creating a small bit of logic to be reused like any other function. You can call a method within another method, for example. You can also call a method inside a lifecycle hook. They’re very versatile.

Here's a simple demo to demonstrate:

See the Pen Slim example of methods by Sarah Drasner (@sdras) on CodePen.

<code class="language-css"><div id="app"> <button @click="tryme">Try Me</button> <p>{{ message }}</p> </div> new Vue({ el: '#app', data() { return { message: null } }, methods: { tryme() { this.message = Date() } } })

We could have also executed the logic in the directive itself like <button @click="message = Date()">Try Me</button>, which works very well for this small example. However, as the complexity of our application grows, it's more common to do as we see above to break it out to keep it legible. There's also a limit to the logic that Vue will allow you to express in a directive—for instance, expressions are allowed but statements are not.

You may notice that we’re be able to access this method within that component or Vue instance, and we can call any piece of our data here, in this case, this.message. You don’t have to call a method like you'd call a function within a directive. For example, @click=”methodName()” is unnecessary. You can reference it with @click=”methodName”, unless you need to pass a parameter, such as @click=”methodName(param)”.

Using directives to call methods is also nice because we have some existing modifiers. One such example that's very useful is .prevent, which will keep a submit event from reloading the page, used like this:

<form v-on:submit.prevent="onSubmit"></form>

There are many more, here are just a few.

Computed

Computed properties are very valuable for manipulating data that already exists. Anytime you're building something where you need to sort through a large group of data and you don't want to rerun those calculations on every keystroke, think about using a computed value.

Some good candidates include, but are not limited to:

  • Updating a large amount of information while a user is typing, such as filtering a list
  • Gathering information from your Vuex store
  • Form validation
  • Data visualizations that change depending on what the user needs to see

Computed properties are a vital part of Vue to understand. They are calculations that will be cached based on their dependencies and will only update when needed. They're extremely performant when used well and extraordinarily useful. There are many large libraries that handle this kind of logic that you can now eliminate with only a few lines of code.

Computed properties aren't used like methods, though at first, they might look similar- you're stating some logic in a function and returning- but the name of that function becomes a property that you'd then use in your application like data.

If we needed to filter this big list of names of heroes based on what the user was typing, here’s how we would do it. We're keeping this really simple so you can get the base concepts down. Originally our list would output in our template using names, which we store in data:

new Vue({ el: '#app', data() { return { names: [ 'Evan You', 'John Lindquist', 'Jen Looper', 'Miriam Suzanne', ... ] } } }) <div id="app"> <h1>Heroes</h1> <ul> <li v-for="name in names"> {{ name }} </li> </ul> </div>

See the Pen Filter a list with Computed- start by Sarah Drasner (@sdras) on CodePen.

Now let's create a filter for those names. We'll start by creating an input with v-model that will originally be an empty string, but we'll eventually use to match and filter through our list. We'll call this property findName and you can see it referenced both on the input and in the data.

<label for="filtername">Find your hero:</label> <input v-model="findName" id="filtername" type="text" /> data() { return { findName: '', names: [ 'Evan You', 'John Lindquist', ... ] } }

Now, we can create the computed property that will filter all of the names based on what the user has typed into the input, so anything in our findName property. You'll note that I'm using regex here to make sure that mismatched capitalization doesn't matter, as users will typically not capitalize as they type.

computed: { filteredNames() { let filter = new RegExp(this.findName, 'i') return this.names.filter(el => el.match(filter)) } }

And now we'll update what we're using in the template to output from this:

<ul> <li v-for="name in names"> {{ name }} </li> </ul>

...to this:

<ul> <li v-for="name in filteredNames"> {{ name }} </li> </ul>

And it filters for us on every keystroke! We only had to add a couple of lines of code to make this work, and didn't have to load any additional libraries.

See the Pen Filter a list with Computed- end by Sarah Drasner (@sdras) on CodePen.

I can't tell you how much time I save by using them. If you're using Vue and haven't explored them yet, please do, you'll thank yourself.

Watchers

Vue has nice abstractions, and anyone who has been a programmer for a while will usually tell you that abstractions can be a pain because you'll eventually get to a use case they can't solve. However, this situation is accounted for, because Vue grants us some deeper access to into the reactivity system, which we can leverage as hooks to observe anything that’s changing. This can be incredibly useful because, as application developers, most of what we’re responsible for are things that change.

Watchers also allow us to write much more declarative code. You’re no longer tracking everything yourself. Vue is already doing it under the hood, so you can also have access to changes made to any properties it's tracking, in data, computed, or props, for example.

Watchers are incredibly good for executing logic that applies to something else when a change on a property occurs (I first heard this way of putting it from Chris Fritz, but he says he might have also heard it from someone else ☺️). This isn't a hard rule- you can absolutely use watchers for logic that refers to the property itself, but it's a nice way of looking at how watchers are immediately different from computed properties, where the change will be in reference to the property we intend to use.

Let’s run through the most simple example possible so you get a taste of what’s happening here.

Your browser does not support the video tag.

new Vue({ el: '#app', data() { return { counter: 0 } }, watch: { counter() { console.log('The counter has changed!') } } })

As you can see in the code above, we're storing counter in data, and by using the name of the property as the function name, we're able to watch it. When we reference that counter in watch, we can observe any change to that property.

Transitioning State With Watchers

If the state is similar enough, you can even simply transition the state with watchers. Here's an example I built from scratch of a chart with Vue. As the data changes, the watchers will update it and simply transition between them.

SVG is also good for a task like this because it's built with math.

See the Pen Chart made with Vue, Transitioning State by Sarah Drasner (@sdras) on CodePen.

watch: { selected: function(newValue, oldValue) { var tweenedData = {} var update = function () { let obj = Object.values(tweenedData); obj.pop(); this.targetVal = obj; } var tweenSourceData = { onUpdate: update, onUpdateScope: this } for (let i = 0; i < oldValue.length; i++) { let key = i.toString() tweenedData[key] = oldValue[i] tweenSourceData[key] = newValue[i] } TweenMax.to(tweenedData, 1, tweenSourceData) } }

What happened here?

  • First we created a dummy object that will get updated by our animation library.
  • Then we have an update function that is invoked on each tween step. We use this to push the data.
  • Then we create an object to hold the source data to be tweened and the function pointer for update events.
  • We create a for loop, and turn the current index into a string
  • Then we can tween over the our target dummy object, but we'll only do this for the specific key

We could also use animation in watchers to create something like this time difference dial. I travel a bit and all my coworkers are in different areas, so I wanted a way to track what local time we were all in, as well as some signification of the change from daytime/nighttime as well.

See the Pen Vue Time Comparison by Sarah Drasner (@sdras) on CodePen.

Here we're watching the checked property, and we'll fire different methods that contain timeline animations that change the hue and saturation and some other elements based on the relative association to the current time. As mentioned earlier- the change occurs on the dropdown, but what we're executing is logic that's applied elsewhere.

watch: { checked() { let period = this.timeVal.slice(-2), hr = this.timeVal.slice(0, this.timeVal.indexOf(':')); const dayhr = 12, rpos = 115, rneg = -118; if ((period === 'AM' && hr != 12) || (period === 'PM' && hr == 12)) { this.spin(`${rneg - (rneg / dayhr) * hr}`) this.animTime(1 - hr / dayhr, period) } else { this.spin(`${(rpos / dayhr) * hr}`) this.animTime(hr / dayhr, period) } } },

There are also a number of other interesting things about watchers, for instance: we're given access to both the new and old versions of the property as parameters, we can specify deep if we'd like to watch a nested object. For more detailed information, there's a lot of good information in the guide.

You can see how watchers can be incredibly useful for anything that’s updating—be it form inputs, asynchronous updates, or animations. If you’re curious how reactivity in Vue works, this part of the guide is really helpful. If you’d like to know more about reactivity in general, I really enjoyed Andre Staltz' post and the Reactivity section of Mike Bostock’s A Better Way to Code.

Wrapping Up

I hope this was a helpful breakdown on how to use each, and speeds up your application development process by using Vue efficiently. There's a stat out there that we spend 70% of our time as programmers reading code and 30% writing it. Personally, I love that, as a maintainer, I can look at a codebase I've never seen before and know immediately what the author has intended by the distinction made from methods, computed, and watchers.

The post Methods, Computed, and Watchers in Vue.js appeared first on CSS-Tricks.

Designing Button States

Css Tricks - Thu, 04/05/2018 - 12:44pm

Tyler Sticka on the complexity of designing buttons and making sure that we’ve taken into consideration focus, hover and active states during the design process:

In truth, mouse effects are probably the least important state to design for. By accounting for more functional states early, you can lower the need for costly redesigns as your pattern library matures. Here are the fundamental states you should address early on, in approximate order of importance.

I’ve been spending a lot more time lately thinking about focus styles as being a crucial challenge when building for the web and so I particularly take Tyler’s advice to heart. He argues that we should repeat this maxim throughout the button design process:

"I do solemnly swear never to disable browser focus styles without including a thoughtfully designed replacement."

The first step: focusing on focus styles.

On a related note, we recently did a series on CSS Basics that included a post dedicated to link styling for various link states. Also, there’s a pretty good post that’s related to this topic called Buttons in Design Systems that tackles a bunch of UX considerations for buttons, like how to write a good label.

Direct Link to ArticlePermalink

The post Designing Button States appeared first on CSS-Tricks.

Static File Hosting Doesn’t Have To Be So… Static

Css Tricks - Thu, 04/05/2018 - 12:42pm

A huge high-five and welcome to Netlify for the sponsorship this week.

If you haven't heard of Netlify, the big thing you should know is that it's web hosting, but more than that. It's web hosting with the developer workflow squarely at heart. You can spin up a site on Netlify in literally seconds. One way is through their robust CLI. Another way, that I find very comfortable (and just did the other day), is to log into the Netlify web interface, create a new site, and connect a Git repo to it. Plus I can give it a command that will run my site's build process when I push to master. Now anything I push up goes live on my website, which is HTTPS and on a CDN. Uh, wow. Of course, I can also point a custom domain name at Netlify and now we're cooking with gas.

The JAMstack is at the heart of Netlify. It's static file hosting, because static file hosting is super fast and secure. It means you can build your site with all kinds of fun, powerful, modern site generators like Hugo, Gatsby, Metalsmith, or 11ty. The site I spun up myself was my own custom thing with a Gulp build process that ran Sass and Nunjucks.

Try spinning up a Gatsby site right now!

Static sites aren't just HTML-only zero-interactivity stone statues.

In fact, I think static sites are one of the ingredients to the larger world of serverless technology, in which functionality is handled by services that are perfect for the job.

Netlify knows this, of course, so they've released has some brand spanking new features that allow you to add interactivity and functionality to your site:

Form Handling

Just add a netlify attribute to the <form>, configure where you want the redirection and email notifications to go, and you're set. You don't have to write any server-side code or JavaScript. Even blast that data over to Zapier to integrate with a million other web services. They don't inject JavaScript to make this work - it's handled at the CDN level.

You can also receive and manage submissions in your Netlify dashboard, so this can be yet another thing that brings together site management under one roof.

Built-in AWS Lambda Functions

JavaScript functions are designed to handle requests. Does your site need to trigger a Slack message? Send an SMS through Twilio? Process data? Now you can host your cloud functions right in the same repo as your site and Netlify will handle pushing them over to AWS Lambda for you. You don't have to configure anything or even bother setting up your own AWS account.

Plus, your functions benefit from the power of Deploy Previews and rollbacks. As in, your functions live in your version control along with the rest of your site, so they are easy to manage and come with all the comfort and advantages of working with Netlify. Wanna dig in? Here's a tutorial by Alex MacArthur that goes deep.

Identity

Do you need to log in to your website for admin purposes? Or have users log in? With Identity, Netlify gives you a really easy way to make that happen. Imagine a feature like a gym website offering a food log for members. The member could log in with Google/Twitter/etc and save/view/edit their food data (via cloud functions of course!).

Social login is a handy feature, but it's not required. You can manage and authenticate users that aren't Netlify users or users of any other service. You'll be able to handle log in, sign up, password recovery and all that. Very useful for gated content, site administration, and integrating with any service that understands JSON Web Tokens.

All on Netlify

All those things without having to go out, evaluate and purchase tools or customize open source tools, integrate them into your project, and then manage multiple disparate accounts/services.

How much does it all cost? There's a good chance it doesn't cost you anything. Small projects probably fit within Netlify's free tier. If you grow up and build something big, they you might get into a paid tier, but still good news, you only pay for what you use.

Go check out Netlify right now.

The post Static File Hosting Doesn’t Have To Be So… Static appeared first on CSS-Tricks.

Creating Themeable Design Systems

Css Tricks - Thu, 04/05/2018 - 12:41pm

Brad frost picks up the ongoing conversation about design systems. Where many posts seem to center on how to create one and how to enforce it, the big takeaway here is that design systems are not synonymous with constraints. They're only as strict as we make them and new CSS features like custom properties actually open up new creative possibilities—something Andres Galante and Cliff Pyles recently pitched right here on CSS-Tricks.

Brad:

The aesthetic layer is often the most malleable layer of the frontend stack, which means that we can create systems that allow for a lot of aesthetic flexibility while still retaining a solid underlying structural foundation.

This not only sounds right, but puts a strong punctuation on why we love CSS: it's a set of styles that can be applied an infinite number of ways to the same HTML markup. A new layer of paint can be slapped on at any time, but the beams, walls and ceiling of the building can remain constant. Dave Rupert's personal site is a prime example of this and he details his approach to theming.

Ah, CSS Zen Garden...

Direct Link to ArticlePermalink

The post Creating Themeable Design Systems appeared first on CSS-Tricks.

A Quick Way to Remember the Difference Between `justify-content` and `align-items`

Css Tricks - Thu, 04/05/2018 - 3:58am

I was talking with a pal the other day and moaning about flexbox for the millionth time because I had momentarily forgotten the difference between the justify-content and align-items properties.

"How do I center an element horizontally with flex again?” I wondered. Well, that was when she gave me what I think is the best shorthand way of remembering how the two work together.

She said that justify-content positions elements across the horizontal axis because the word itself is longer than align-items. At first I thought this was a really silly idea but now this is how I remember it. I even used it five minutes ago when I needed to make these two quick demos:

See the Pen justify-content: center by Robin Rendle (@robinrendle) on CodePen.

See the Pen align-items: center by Robin Rendle (@robinrendle) on CodePen.

So, to summarize:

  • justify-content: longer word: horizontal alignment
  • align-items: shorter word: vertical alignment

This had me thinking if there are there any other mnemonic devices or ways that to remember complex things in CSS? Are there any other tricks you’d recommend? It sort of reminds me of the way kids are taught to remember the names of planets with things like, “My Very Educated Mother Just Showed Us Nine” where the first letter in each word represents the first letter of each planet: Mercury, Venus, Earth, Mars, Jupiter, Saturn, Uranus and Neptune.

The post A Quick Way to Remember the Difference Between `justify-content` and `align-items` appeared first on CSS-Tricks.

?The future of data collection is here

Css Tricks - Thu, 04/05/2018 - 3:57am

(This is a sponsored post.)

Who said collecting data was easy? JotForm did. In today’s world, getting relevant data has never been more important for making informed business decisions. The thing is, companies still struggle with it because the forms they use for gathering information don’t typically resonate with their customers. Until now that is. With JotForm’s newest format, JotForm Cards, your online forms have all the same power of traditional forms, but with added benefits: they’re friendlier, sleeker, and not boring. Better yet, companies that use JotForm Cards see 36% higher conversions, which means more leads, more payments, more registrations, and more feedback. So, what are you waiting for? Get the data your company needs, and try out JotForm Cards for yourself today.

Direct Link to ArticlePermalink

The post ?The future of data collection is here appeared first on CSS-Tricks.

How to Write a Git Commit Message

Css Tricks - Wed, 04/04/2018 - 11:04am

An oldie but goodie, Chris Beams writes about the secret art of writing helpful Git commit messages. Here’s why he thinks it’s so important:

If you haven’t given much thought to what makes a great Git commit message, it may be the case that you haven’t spent much time using git log and related tools. There is a vicious cycle here: because the commit history is unstructured and inconsistent, one doesn’t spend much time using or taking care of it. And because it doesn’t get used or taken care of, it remains unstructured and inconsistent.

But a well-cared for log is a beautiful and useful thing. git blame, revert, rebase, log, shortlog and other subcommands come to life. Reviewing others’ commits and pull requests becomes something worth doing, and suddenly can be done independently. Understanding why something happened months or years ago becomes not only possible but efficient.

A project’s long-term success rests (among other things) on its maintainability, and a maintainer has few tools more powerful than his project’s log. It’s worth taking the time to learn how to care for one properly. What may be a hassle at first soon becomes habit, and eventually a source of pride and productivity for all involved.

This post pairs well with a more recent post on the topic. Where Chris provides a format for consistency, Stephen Amaza takes that same idea and expands it with suggestions for how to provide a commit message with better context.

I’ve always been pretty lazy with commit messages and there’s certainly been a few times when that’s come back to bite me in some terrible and unexpected way. One trick I’ll certainly be using from now on: using the first line of the message as a title and then having a much longer paragraph that follows it if the code doesn’t make sense at a quick glance.

Direct Link to ArticlePermalink

The post How to Write a Git Commit Message appeared first on CSS-Tricks.

Why would you do that in CSS?

Css Tricks - Wed, 04/04/2018 - 3:37am

And by that, it's usually some kind of CSS experiment, often an elaborate drawing or interaction.

For example, have you seen Lynn Fisher's extraordinary A Single Div project? Not only are all these graphics drawn in just HTML and CSS, they are all created with (you guessed it) a single <div></div>.

Why would she do that? Here's one pertinent possibility: it's none of our business. We're free to wonder, or even ask if it's done respectfully enough. But does it really matter? Let's stop short of assuming she doesn't know what's she's doing, assuming it's a twisted form of pain, or that she's unaware of other technologies. Check out the example where she drew the official SVG logo with CSS and a single div. Woke.

I even kinda get it. I wrote a whole book about SVG because I think it's underused. Are there "CSS drawings" that I think would be better as SVG for a production site meant to last? Sure.

How about this one?

See the Pen Pure CSS Biker by Julia Muzafarova (@miocene) on CodePen.

Jeepers creepers that's something else. I would have guessed GreenSock was behind this at a quick glance. Speaking of, GreenSock has advocated right here on CSS-Tricks some techniques for smart animations. Part of that is breaking animations into parts and stitching them together into larger timelines for maintenance ease and "without getting bogged down by the process."

So, did Julia Muzafarova do it wrong? Of course not. If there is a wrong way to animate a cartoon hipster on a bike in this world, I don't wanna live here anymore. It's 2,100 lines of meticulous positioning, coloring, and animating. Heck, it'll work in some email clients if you really need to latch onto something "practical."

Sasha Tran, a UI Developer at Level Studios, shared her story of creating drawings in CSS last year. She drew something new every day for 20 days during Codevember.

Even if I am not talented in hand illustrations, there is a way to express myself through other mediums. I found that medium to be in HTML and CSS. To level up and get to a point where I could create cute artwork, I focused on two things: the basics, and consistency. Working with basic CSS shapes like rectangles and basic properties like border-radius overtime gave me the muscle memory to progress into more intricate illustrations.

Significantly leveled up in a month and developed better CSS muscle memory. Huh. Speaking of shapes, there are plenty of ways to get tricky with those in CSS!

You could argue that pushing boundaries toward the impractical is what hones our skills for all that other work we have to do.

See the Pen CSS3 Thermostat by Daniel Stancu (@birkof) on CodePen.

You could argue that forcing yourself into restrictions is fuel for creativity.

See the Pen Pixel Hellboy by Servin (@servinnissen) on CodePen.

You could argue that stretching your brain creatively in these forced conditions helps build your confidence and widens your toolbox.

See the Pen Simple CSS Anchor by Joni Trythall (@jonitrythall) on CodePen.

You could argue that it's just kinda fun.

See the Pen CSS Cheese (or sponge) by Hugo Giraudel (@HugoGiraudel) on CodePen.

The post Why would you do that in CSS? appeared first on CSS-Tricks.

Animated SVG Radial Progress Bars

Css Tricks - Tue, 04/03/2018 - 11:20am

Dave Rupert shows us all how to animate radial progress bars in SVG with a tiny script alongside the stroke-dasharray and stroke-dashoffset properties:

For a client project we tasked ourselves with building out one of those cool radial progress bars. In the past, we’ve used entire Canvas-based charting libraries (156k/44k gzip), but that seemed like overkill. I looked at Airbnb’s Lottie project where you export After Effects animations as JSON. This is cool for complex animations, but the dependencies seemed heavy (248k/56k gzip) for one micro-animation.

Per the usual, I tried my hand at a minimal custom SVG with CSS animation and a small bit of JavaScript (~223b gzip). I’m pleased with the results.

Here's another example Jeremias Menichelli posted here on CSS-Tricks with the added twist of making them components in React and Vue.

Direct Link to ArticlePermalink

The post Animated SVG Radial Progress Bars appeared first on CSS-Tricks.

Scooped Corners in 2018

Css Tricks - Tue, 04/03/2018 - 4:00am

When I saw Chris' article on notched boxes, I remembered that I got a challenge a while ago to CSS a design like the one below in a cross-browser manner:

What the challenge looked like.

It looks pretty similar to the concept of notched boxes, except the corners are now scooped and we only have to worry about one corner per box. So let's see how we can do it, how we can expand the technique to multiple corners, what issues we run into and how we can get around them with or without making browser support compromises.

The initial idea: box-shadow!

We start with a box element:

<div class='box'></div>

We can give this some dimensions or let its dimensions be decided by the content—it doesn't really matter. For simplicity, we're just setting a max-width and a min-height on it. We're also giving it an outline so we can see its boundaries.

.box { outline: solid 2px; max-width: 15em; min-height: 10em; }

Next, we absolutely position a square ::before pseudo-element whose edge length is equal to the diameter (or twice the radius $r) of the scoop in the corner. We also give this pseudo-element a reddish box-shadow and a dummy background (that we'll remove later) just so that we can see it better:

$r: 2em; .box { position: relative; /* same styles as before */ &:before { position: absolute; padding: $r; box-shadow: 0 0 7px #b53; background: #95a; content: '' } }

And this is what we have so far:

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

Well, it doesn't look too exciting... yet! So let's move on and make this square a disc by setting border-radius: 50% on it and give it a negative margin equal to its radius $r, so that its central point coincides with the (0,0) point (top left corner) of its parent box. We also set overflow: hidden on the parent box, so that whatever of this pseudo-element is outside the .box gets cut out.

$r: 2em; .box { overflow: hidden; /* same styles as before */ &:before { /* same styles as before */ margin: -$r; border-radius: 50% } }

Now we're starting to see the shape we've been aiming for:

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

But it's still not quite what we want. In order to get there, we use the fourth length value for the box-shadow property: the spread radius. If you need a refresher on how box-shadow works with these four values, you can check out the interactive demo below:

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

You may have already guessed what we do next. We remove the dummy background, we zero the first three box-shadow values (the x and y offsets and the blur radius) and use a pretty big number for the last one (the spread radius):

box-shadow: 0 0 0 300px;

The interactive demo below shows how increasing the spread radius makes it cover up more and more of its parent .box:

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

So, the trick here is having a spread radius sufficiently large so that it covers the rest of the parent element. The cool thing about this is that we can make the box-shadow semi-transparent or have rounded corners on the parent .box:

.box { /* same styles as before */ border-radius: 1em; &:before { /* same styles as before */ box-shadow: 0 0 0 300px rgba(#95a, .75); } }

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

Of course, just like Chris pointed out in the article on notched boxes, we can make the scoop radius a CSS variable and then easily modify that from the JavaScript. Then everything updates nicely, even with text content in our box:

:root { --r: 50px } .box { /* same styles as before */ padding: var(--r); &:before { /* same styles as before */ margin: calc(-1*var(--r)); padding: inherit; }

Note that when we also have text content, we need to set a negative z-index on the ::before pseudo-element and explicitly position it in the corner as we now also have a padding on the .box to compensate for the scoop.

.box { /* same styles as before */ &:before { /* same styles as before */ z-index: -1; top: 0; left: 0 }

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

Applying this technique

Now, let's move further and see how we can apply this concept in order to reproduce the design I showed at the beginning. In this particular case, the central points of the pseudo-element discs don't coincide with box corners, but are outside, in the middle of the space in between boxes.

The structure used is pretty straightforward, just a <header> element followed by four <article> elements I've generated in a Pug loop:

while n-- article h3 #{data[n].name} section p #{data[n].quote} a(href='#') go

We use a wrapping flexbox layout on the <body> with the <header> really wide and with one or two <article> elements on each row, depending on how wide the viewport is.

Landscape (left) vs. portrait (right) mode.

If we have a single <article> on each row, we don't have scooped corners, so their radius is 0px. Otherwise, we give this radius --r a non-zero value.

$min-w: 15rem; /* min width of an article element */ $m: 1rem; /* margin of such an element */ html { --r: 0px; } article { margin: $m; min-width: $min-w; width: 21em; } @media (min-width: 2*($min-w + 2*$m) /* enough for 2 per row */) { html { --r: 4rem; } article { width: 40%; } }

Let's now consider just the situation when we have two <article> elements per row (and of course a scooped corner for each because that's what's of interest to us).

In the case of the first one, we start with the leftmost limit of the disc along the right edge of its parent. That's left: 100% so far. To move the x coordinate of the disc's central point on the right edge of its parent, we subtract the disc's radius, which brings us to left: calc(100% - var(--r)). But we don't want it on the right edge, we want it offset to the right by the <article> margin $m, which brings us to the final value:

left: calc(100% - var(--r) + #{$m});

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

Along the y axis, we start with the topmost limit of the disc along the bottom edge of its parent—that's top: 100%. To put the disc's central point on the bottom edge of the parent box, we move it up by one radius, which gives us top: calc(100% - var(--r)). Finally, we want this central point to be $m below the parent's bottom edge, which gives us the final vertical offset of:

top: calc(100% - var(--r) + #{$m});

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

For the second <article> (second on the same row), we have the same value in the case of the vertical offset.

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

Horizontally however, we start with the disc's left limit being along its parent's left edge—that's left: 0%. To put the disc's central point on its parent's left edge, we move it left by a radius --r, thus getting left: calc(0% - var(--r)). However, the final position is $m to the left of the parent's left edge:

left: calc(0% - var(--r) - #{$m});

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

For the third <article> (first on the last row), we have the same value for the offset along the x axis as in the case of the first one.

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

Vertically, we start with the disc's top limit along the top edge of its parent—that's top: 0%. To put the disc's central point on the parent's top edge, we move it up by a radius --r, thus getting top: calc(0% - var(--r)). But we want to have it $m above the parent's top edge, so the final top offset is:

top: calc(0% - var(--r) - #{$m});

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

For the final one (second on the last row), we have the same horizontal offset as in the case of the one above it and the same vertical offset as for the one to its left on the same row.

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

So, our offsets can be written:

article:nth-of-type(1) { /* 1st */ left: calc(100%/* 2*50% = (1 + 1)*50% = (1 + i)*50% */ - var(--r) + /* i=+1 */#{$m}); top: calc(100%/* 2*50% = (1 + 1)*50% = (1 + j)*50% */ - var(--r) + /* j=+1 */#{$m}); } article:nth-of-type(2) { /* 2nd */ left: calc( 0%/* 0*50% = (1 - 1)*50% = (1 + i)*50% */ - var(--r) - /* i=-1 */#{$m}); top: calc(100%/* 2*50% = (1 + 1)*50% = (1 + j)*50% */ - var(--r) + /* j=+1 */#{$m}); } article:nth-of-type(3) { /* 3rd */ left: calc(100%/* 2*50% = (1 + 1)*50% = (1 + i)*50% */ - var(--r) + /* i=+1 */#{$m}); top: calc( 0%/* 0*50% = (1 - 1)*50% = (1 + j)*50% */ - var(--r) - /* j=-1 */#{$m}); } article:nth-of-type(4) { /* 4th */ left: calc( 0%/* 0*50% = (1 - 1)*50% = (1 + i)*50% */ - var(--r) - /* i=-1 */#{$m}); top: calc( 0%/* 0*50% = (1 - 1)*50% = (1 + j)*50% */ - var(--r) - /* j=-1 */#{$m}); }

This means the positions of the central points of the discs depend on the gap in between our <article> elements (this gap is twice the margin: $m we set on them), on the disc radius r and on a couple of horizontal and vertical multipliers (--i and --j respectively). Both these multipliers are initially -1.

For the first two <article> elements (on the first row of the 2x2 grid), we change the vertical multiplier --j to 1 because we want the y coordinate of the discs' central points to be below the bottom edge, while for the odd ones (on the first column), we change the horizontal multiplier --i to 1 because we want the x coordinate to be to the right of the right edge.

html { --i: -1; --j: -1 } /* multipliers initially set to -1 */ h3, section { &:before { /* set generic offsets */ top: calc((1 + var(--j))*50% - var(--r) + var(--j)*#{$m}); left: calc((1 + var(--i))*50% - var(--r) + var(--i)*#{$m}); } } @media (min-width: 2*($min-w + 2*$m)) { article { /* change vertical multiplier for first two (on 1st row of 2x2 grid) */ &:nth-of-type(-n + 2) { --j: 1 } /* change horizontal multiplier for odd ones (on 1st column) */ &:nth-of-type(odd) { --i: 1 } }

Note that we only have visible disc cutouts on the <section> element for the first two <article> elements and only on the <h3> for the last two. So for the first two <article> elements, the radius --r on the heading's ::before pseudo-element is 0, while for the last two, this radius is 0 for the section's ::before pseudo:

@media (min-width: 2*($min-w + 2*$m)) { article { &:nth-of-type(-n + 2) h3, &:nth-of-type(n + 3) section { &:before { --r: 0 ; } } } }

In a similar manner, we add differentiated paddings to the children of the <article> elements:

$p: .5rem; h3, section { padding: $p; } @media (min-width: 2*($min-w + 2*$m)) { article { &:nth-of-type(-n + 2) section, &:nth-of-type(n + 3) h3 { padding-right: calc(.5*(1 + var(--i))*(var(--r) - #{$m}) + #{$p}); padding-left: calc(.5*(1 - var(--i))*(var(--r) - #{$m}) + #{$p}); } } }

This helps us get the result we're looking for:

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

The above demo works in current versions of all major browsers and, if we can do with some repetition instead of using CSS variables, we can extend support all the way back to IE9.

Potential issues with the above method

While this was a quick and easy cross-browser way to get the desired result in this particular case, we may not always be so lucky with this approach.

First off, we need a pseudo-element for each scooped corner, so if we want this effect for all corners, we need to bring in an extra element. Sad panda.

Secondly, we may not always want a solid background. We may want a semi-transparent one (which becomes a pain to get if we want to have more than one scooped corner), a gradient one (while we can emulate some radial gradients with box-shadow, it's a less than ideal solution) or even an image background (hardly doable with the only solution being to use mix-blend-mode which cuts out Edge support without an elegant fallback).

And how about really large boxes for which the spread we've set is not enough? Ugh.

So, let's explore other, more reliable approaches with various degrees of browser support.

Flexibility and good browser support? SVG it!

This is probably no surprise, but the full SVG solution fares best if we want something flexible and reliably cross-browser today. It's a solution that involves using an SVG element before the content of our box. This SVG contains a <circle> on which we've set a radius r attribute.

<div class='box'> <svg> <circle r='50'/> </svg> TEXT CONTENT OF BOX GOES HERE </div>

We absolutely position this SVG within the box and size it such that it fully covers its parent:

.box { position: relative; } svg { position: absolute; width: 100%; height: 100%; }

Nothing too interesting so far, so let's give the <circle> an id and clone it in the other corners:

<circle id='c' r='50'/> <use xlink:href='#c' x='100%'/> <use xlink:href='#c' y='100%'/> <use xlink:href='#c' x='100%' y='100%'/>

Note that if we want to exclude one corner or more, we just don't clone it there.

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

Alright, but what we've done here is create circles in the corners and what we actually want is... the exact opposite! What we do next is put these circles within a <mask>, on top of a white, full-size (covering the whole SVG) rectangle and then we use this mask on another full size rectangle:

<mask id='m' fill='#fff'> <rect id='r' width='100%' height='100%'/> <circle id='c' r='50' fill='#000'/> <use xlink:href='#c' x='100%'/> <use xlink:href='#c' y='100%'/> <use xlink:href='#c' x='100%' y='100%'/> </mask> <use xlink:href='#r' fill='#f90' mask='url(#m)'/>

The result can be seen below:

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

If we have text, we need to adapt the box padding to our corner radius, setting the it to the same value as we've set the radius of the SVG circle, using JavaScript to keep them in sync:

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

Of course, the fill of our background rectangle doesn't need to be a solid one. It may well be semi-transparent (as it is in the demo above), or we can use an SVG gradient or pattern for it. The latter would also allow us to use one or more background images.

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

But I came here for CSS candy!

Well, glad you asked! There are a number of things we can do here to shift the weight of the masking method from SVG to CSS.

Sadly, none of these is cross-browser, but they simplify things and they're definitely something to keep a watch for in the near or more distant future.

Use CSS masking on HTML elements instead

What we do here is remove everything outside the mask from the SVG. Then, from the CSS, we set a background (which can be semi-transparent, a CSS gradient, an image, a combination of multiple backgrounds... anything that CSS has to offer) and the mask property on the .box element:

.box { /* any kind of background we wish */ mask: url(#m); }

Note that setting an inline SVG mask on an HTML element only works in Firefox for now!

Version using CSS masking directly on our .box (live demo, Firefox only). Set the circle radius from the CSS

This means removing the r attribute from our <circle> and setting it in the CSS to the same variable as the box padding:

.box { padding: var(--r); } [id='c'] { r: var(--r); }

This way, when we change the value of --r, both the scoop radius and the padding around the .box content get updated!

Note that setting geometry properties for SVG elements from the CSS only works in Blink browsers for now!

Version using a CSS variable for the <circle> radius (live demo, Blink only). Combine the previous two methods

While this would be cool, it's sadly not possible in practice in any browser at the moment. But the good news is we can do even better than that!

Use CSS gradients for masking

Note that CSS masking on HTML elements doesn't work at all in Edge at this point, though it's listed as "In Development" and a flag for it (that doesn't do anything for now) has already shown up in about:flags.

We ditch the SVG part completely and start building our CSS gradient mask. We create the circles at the corners using radial gradients. The following CSS creates a circle of radius --r in the top left corner of a box:

.box { background: radial-gradient(circle at 0 0, #000 var(--r, 50px), transparent 0); }

It can be seen live in the demo below, where we've also given the box a red outline just so that we can see its boundaries:

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

We use the exact same gradient for our mask:

.box { /* same as before */ /* any CSS background we wish */ mask: radial-gradient(circle at 0 0, #000 var(--r, 50px), transparent 0); }

Note that WebKit browsers still need the -webkit- prefix for the mask properties.

We then add the circles at the other corners:

$grad-list: radial-gradient(circle at 0 0 , #000 var(--r, 50px), transparent 0), radial-gradient(circle at 100% 0 , #000 var(--r, 50px), transparent 0), radial-gradient(circle at 0 100%, #000 var(--r, 50px), transparent 0), radial-gradient(circle at 100% 100%, #000 var(--r, 50px), transparent 0); .box { /* same as before */ /* any CSS background we wish */ mask: $grad-list }

That's insanely repetitive, either a lot of writing or a lot of copy-pasting, so let's see what we can do about that.

First off, we use a CSS variable for the stop list. This eliminates repetition in the generated CSS.

$grad-list: radial-gradient(circle at 0 0 , var(--stop-list)), radial-gradient(circle at 100% 0 , var(--stop-list)), radial-gradient(circle at 0 100%, var(--stop-list)), radial-gradient(circle at 100% 100%, var(--stop-list)); .box { /* same as before */ /* any CSS background we wish */ --stop-list: #000 var(--r, 50px), transparent 0; mask: $grad-list; }

But it's still not much better, so let's generate the corners within a loop:

$grad-list: (); @for $i from 0 to 4 { $grad-list: $grad-list, radial-gradient(circle at ($i%2)*100% floor($i/2)*100%, var(--stop-list)); } .box { /* same as before */ /* any CSS background we wish */ --stop-list: #000 var(--r, 50px), transparent 0; mask: $grad-list; }

Much better as far as the code goes because now we don't have to write anything multiple times and run the risk of not updating everywhere later. But the result so far isn't what we were going for:

Result of the code above (live demo, no Edge support for now).

Here, we're cutting out everything but the corners, which is the opposite of what we want.

One thing we can do is reverse the gradients, make the corner circles transparent and the rest black with:

--stop-list: transparent var(--r, 50px), #000 0;

This does the trick when we use just one gradient for just one corner:

Result when using just one gradient (live demo, no Edge support for now).

However, when we stack up all four of them (or just even two), we get a black rectangle the size of our box for the mask, which means nothing actually gets masked out anymore.

Layering mask gradients (live demo, no Edge support for now).

So, we restrict each of these gradients to a quarter of our box - 50% of the width and 50% of the height, thus getting 25% (a quarter) of the area for each:

Our mask, split into four quarters (live).

This means we also need to set a mask-size of 50% 50%, a mask-repeat of no-repeat, and position each mask-image into the desired corner:

$grad-list: (); @for $i from 0 to 4 { $x: ($i%2)*100%; $y: floor($i/2)*100%; $grad-list: $grad-list radial-gradient(circle at $x $y, var(--stop-list)) /* mask image */ $x $y; /* mask position */ } .box { /* same as before */ /* any CSS background we wish */ --stop-list: transparent var(--r, 50px), #000 0; mask: $grad-list; mask-size: 50% 50%; mask-repeat: no-repeat; }

Note that WebKit browsers still need the -webkit- prefix for mask properties.

But the big problem here is... the problem with division and rounding in general—our four quarters put together don't always manage to make up a whole again, so we end up with gaps in between them.

Sadly, we may get gaps in between the four quarters (live demo).

Oh well, it's not like we can't cover up those gaps with thin linear-gradient() strips or increase the mask-size to let's say 51%:

Increasing the mask-size for each gradient layer fixes the problem of gaps (live demo).

But isn't there a more elegant way?

Well, there's a mask-composite property that can help us if we set it to intersect when reverting back to the full size gradient layers.

$grad-list: (); @for $i from 0 to 4 { $grad-list: $grad-list, radial-gradient(circle at ($i%2)*100% floor($i/2)*100%, var(--stop-list)); } .box { /* same as before */ /* any CSS background we wish */ --stop-list: transparent var(--r, 50px), #000 0; mask: $grad-list; mask-composite: exclude; }

This is extremely cool because it's a pure CSS, no SVG solution, but the not-so-good news is that support is limited to Firefox 53+ here.

Result using mask-composite: intersect (live demo).

However, it's still better than support for the final option we have when it comes to scooped corners.

The corner-shape option

Lea Verou came up with this idea some five years ago and even created a preview page for it. Sadly, not only is it not implemented by any browser yet, but the spec hasn't advanced much in the meanwhile. It's still something to keep in mind for the future, as it offers a lot of flexibility with very little code - recreating our effect would only require the following:

padding: var(--r); corner-shape: scoop; border-radius: var(--r);

No markup vomit, no long gradient lists, just this very simple piece of CSS. That is... when it finally gets supported by browsers!

The post Scooped Corners in 2018 appeared first on CSS-Tricks.

Syndicate content
©2003 - Present Akamai Design & Development.